diff --git a/target/linux/x-burst/Makefile b/target/linux/x-burst/Makefile new file mode 100644 index 000000000..3103a2e41 --- /dev/null +++ b/target/linux/x-burst/Makefile @@ -0,0 +1,24 @@ +# +# Copyright (C) 2009 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# +include $(TOPDIR)/rules.mk + +ARCH:=mipsel +BOARD:=jz47x0 +BOARDNAME:=Ingenic JZ47x0 +FEATURES:=squashfs jffs2 + +LINUX_VERSION:=2.6.24.7 + +include $(INCLUDE_DIR)/target.mk + +DEFAULT_PACKAGES += hostapd-mini kmod-madwifi gpioctl + +define Target/Description + Build firmware images for Ingenic JZ47x0 based boards. +endef + +$(eval $(call BuildTarget)) diff --git a/target/linux/x-burst/config-2.6.24 b/target/linux/x-burst/config-2.6.24 new file mode 100644 index 000000000..b7809a36d --- /dev/null +++ b/target/linux/x-burst/config-2.6.24 @@ -0,0 +1,453 @@ +CONFIG_32BIT=y +# CONFIG_5x5_KEYBOARD_JZ is not set +# CONFIG_64BIT is not set +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_ARCH_POPULATES_NODE_MAP=y +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_ARCH_SUPPORTS_OPROFILE=y +# CONFIG_ARPD is not set +CONFIG_BASE_SMALL=0 +# CONFIG_BCM47XX is not set +CONFIG_BITREVERSE=y +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_SD=y +# CONFIG_BRIDGE is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +# CONFIG_CONFIGFS_FS is not set +# CONFIG_CPU_BIG_ENDIAN is not set +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_DEBUG is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_JZ=y +CONFIG_CPU_FREQ_STAT=y +# CONFIG_CPU_FREQ_STAT_DETAILS is not set +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_HAS_LLSC=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_LITTLE_ENDIAN=y +# CONFIG_CPU_LOONGSON2 is not set +CONFIG_CPU_MIPS32=y +CONFIG_CPU_MIPS32_R1=y +# CONFIG_CPU_MIPS32_R2 is not set +# CONFIG_CPU_MIPS64_R1 is not set +# CONFIG_CPU_MIPS64_R2 is not set +CONFIG_CPU_MIPSR1=y +# CONFIG_CPU_NEVADA is not set +# CONFIG_CPU_R10000 is not set +# CONFIG_CPU_R3000 is not set +# CONFIG_CPU_R4300 is not set +# CONFIG_CPU_R4X00 is not set +# CONFIG_CPU_R5000 is not set +# CONFIG_CPU_R5432 is not set +# CONFIG_CPU_R6000 is not set +# CONFIG_CPU_R8000 is not set +# CONFIG_CPU_RM7000 is not set +# CONFIG_CPU_RM9000 is not set +# CONFIG_CPU_SB1 is not set +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +# CONFIG_CPU_TX39XX is not set +# CONFIG_CPU_TX49XX is not set +# CONFIG_CPU_VR41XX is not set +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_HW=y +# CONFIG_CRYPTO_LZO is not set +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MD5=y +CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_IOSCHED="anticipatory" +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_DM9000 is not set +CONFIG_DMA_NEED_PCI_MAP_STATE=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DNOTIFY=y +CONFIG_DUMMY_CONSOLE=y +CONFIG_ELF_CORE=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_SECURITY is not set +CONFIG_EXT2_FS_XATTR=y +# CONFIG_FAIR_CGROUP_SCHED is not set +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FAIR_USER_SCHED=y +CONFIG_FAT_FS=y +CONFIG_FB=y +# CONFIG_FB_BACKLIGHT is not set +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_DDC is not set +CONFIG_FB_DEFERRED_IO=y +# CONFIG_FB_JZ4740_SLCD is not set +# CONFIG_FB_JZLCD_4730_4740 is not set +CONFIG_FB_JZSOC=y +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FIXED_PHY is not set +CONFIG_FONTS=y +# CONFIG_FONT_10x18 is not set +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_7x14 is not set +CONFIG_FONT_8x16=y +CONFIG_FONT_8x8=y +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_SUN8x16 is not set +CONFIG_FORCE_MAX_ZONEORDER=12 +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_CURSOR_FLASH is not set +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +CONFIG_FS_MBCACHE=y +CONFIG_FS_POSIX_ACL=y +# CONFIG_FW_LOADER is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_FIND_NEXT_BIT=y +# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set +# CONFIG_HAMRADIO is not set +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HID=y +CONFIG_HID_SUPPORT=y +# CONFIG_HIGH_RES_TIMERS is not set +CONFIG_HW_CONSOLE=y +# CONFIG_HW_RANDOM is not set +# CONFIG_I2C is not set +# CONFIG_I2S_AK4642EN is not set +# CONFIG_I2S_DLV is not set +CONFIG_I2S_ICODEC=y +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IDE is not set +CONFIG_INET_DIAG=m +CONFIG_INET_TCP_DIAG=m +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_INPUT=y +CONFIG_INPUT_KEYBOARD=y +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +CONFIG_INSTRUMENTATION=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_MULTICAST is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_JZ4720_VIRGO is not set +# CONFIG_JZ4725_DIPPER is not set +# CONFIG_JZ4730_PMP is not set +# CONFIG_JZ4740_LEO is not set +# CONFIG_JZ4740_LYRA is not set +CONFIG_JZ4740_PAVO=y +# CONFIG_JZ4750D_FUWA1 is not set +# CONFIG_JZ4750_APUS is not set +# CONFIG_JZ4750_FUWA is not set +CONFIG_JZCHAR=y +CONFIG_JZCS8900=y +CONFIG_JZRISC=y +CONFIG_JZSOC=y +# CONFIG_JZ_CIM is not set +# CONFIG_JZ_OW is not set +CONFIG_JZ_POWEROFF=y +CONFIG_JZ_SADC=y +CONFIG_JZ_TCSM=y +CONFIG_JZ_TPANEL=y +# CONFIG_JZ_TPANEL_AK4182 is not set +# CONFIG_JZ_TPANEL_ATA2508 is not set +# CONFIG_JZ_TPANEL_UCB1400 is not set +# CONFIG_JZ_TPANEL_WM9712 is not set +# CONFIG_JZ_UDC_HOTPLUG is not set +CONFIG_KALLSYMS=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_JZ is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KMOD=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=2 +# CONFIG_LEMOTE_FULONG is not set +CONFIG_LOCALVERSION_AUTO=y +CONFIG_LOCKD=y +CONFIG_LOCK_KERNEL=y +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +# CONFIG_MACH_ALCHEMY is not set +# CONFIG_MACH_DECSTATION is not set +# CONFIG_MACH_JAZZ is not set +# CONFIG_MACH_VR41XX is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MAGIC_SYSRQ=y +# CONFIG_MARKERS is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_MINIX_FS=y +CONFIG_MIPS=y +# CONFIG_MIPS_ATLAS is not set +# CONFIG_MIPS_COBALT is not set +CONFIG_MIPS_L1_CACHE_SHIFT=5 +# CONFIG_MIPS_MALTA is not set +CONFIG_MIPS_MT_DISABLED=y +# CONFIG_MIPS_MT_SMP is not set +# CONFIG_MIPS_MT_SMTC is not set +# CONFIG_MIPS_SEAD is not set +# CONFIG_MIPS_SIM is not set +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_JZ is not set +# CONFIG_MMC_UNSAFE_RESUME is not set +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_MODVERSIONS=y +CONFIG_MSDOS_FS=y +CONFIG_MTD=y +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_ALAUDA is not set +CONFIG_MTD_BADBLOCK_FLAG_PAGE=127 +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_MTD_BLOCK2MTD is not set +# CONFIG_MTD_CFI is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CHAR=y +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_CONCAT is not set +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +# CONFIG_MTD_HW_BCH_ECC is not set +CONFIG_MTD_HW_HM_ECC=y +# CONFIG_MTD_HW_RS_ECC is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +CONFIG_MTD_MAP_BANK_WIDTH_2=y +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE is not set +# CONFIG_MTD_MTDRAM is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_CS2 is not set +# CONFIG_MTD_NAND_CS3 is not set +# CONFIG_MTD_NAND_CS4 is not set +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +CONFIG_MTD_NAND_IDS=y +CONFIG_MTD_NAND_JZ4740=y +CONFIG_MTD_NAND_MULTI_PLANE=y +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_ONENAND is not set +CONFIG_MTD_OOB_COPIES=3 +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_PLATRAM is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_SW_HM_ECC is not set +# CONFIG_MTD_UBI_BLKDEVS is not set +# CONFIG_NETFILTER is not set +# CONFIG_NET_SCHED is not set +# CONFIG_NEW_LEDS is not set +CONFIG_NFS_ACL_SUPPORT=y +CONFIG_NFS_DIRECTIO=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NLS=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_936=y +CONFIG_NLS_ISO8859_1=y +# CONFIG_NO_IOPORT is not set +CONFIG_OSS_OBSOLETE=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_PAGE_SIZE_16KB is not set +CONFIG_PAGE_SIZE_4KB=y +# CONFIG_PAGE_SIZE_64KB is not set +# CONFIG_PAGE_SIZE_8KB is not set +CONFIG_PHYLIB=y +CONFIG_PM=y +# CONFIG_PMC_MSP is not set +# CONFIG_PMC_YOSEMITE is not set +# CONFIG_PM_DEBUG is not set +CONFIG_PM_LEGACY=y +# CONFIG_PNP is not set +# CONFIG_PNX8550_JBS is not set +# CONFIG_PNX8550_STB810 is not set +CONFIG_PREEMPT=y +CONFIG_PREEMPT_BKL=y +# CONFIG_PREEMPT_NONE is not set +# CONFIG_QSEMI_PHY is not set +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_RELAY=y +CONFIG_ROOT_NFS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RTC is not set +# CONFIG_RTC_JZ is not set +CONFIG_RTC_LIB=y +# CONFIG_RTC_PCF8563 is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y +CONFIG_SCSI=y +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_WAIT_SCAN=m +# CONFIG_SDIO_UART is not set +CONFIG_SECCOMP=y +# CONFIG_SERIAL_8250_EXTENDED is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SGI_IP22 is not set +# CONFIG_SGI_IP27 is not set +# CONFIG_SGI_IP32 is not set +# CONFIG_SIBYTE_BIGSUR is not set +# CONFIG_SIBYTE_CARMEL is not set +# CONFIG_SIBYTE_CRHINE is not set +# CONFIG_SIBYTE_CRHONE is not set +# CONFIG_SIBYTE_LITTLESUR is not set +# CONFIG_SIBYTE_PTSWARM is not set +# CONFIG_SIBYTE_RHONE is not set +# CONFIG_SIBYTE_SENTOSA is not set +# CONFIG_SIBYTE_SWARM is not set +CONFIG_SLABINFO=y +# CONFIG_SMSC_PHY is not set +CONFIG_SOC_JZ4740=y +CONFIG_SOUND=y +# CONFIG_SOUND_JZ_AC97 is not set +CONFIG_SOUND_JZ_I2S=y +# CONFIG_SOUND_MSNDCLAS is not set +# CONFIG_SOUND_MSNDPIN is not set +CONFIG_SOUND_PRIME=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SSB_POSSIBLE=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +# CONFIG_SUSPEND is not set +# CONFIG_SYN_COOKIES is not set +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +# CONFIG_TICK_ONESHOT is not set +# CONFIG_TOSHIBA_JMR3927 is not set +# CONFIG_TOSHIBA_RBTX4927 is not set +# CONFIG_TOSHIBA_RBTX4938 is not set +CONFIG_TRAD_SIGNALS=y +CONFIG_USB=y +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_DEVICEFS is not set +CONFIG_USB_DEVICE_CLASS=y +CONFIG_USB_HID=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_MON=y +# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set +# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_PERSIST is not set +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USER_NS is not set +CONFIG_VFAT_FS=y +# CONFIG_VGASTATE is not set +# CONFIG_VGA_CONSOLE is not set +# CONFIG_VLAN_8021Q is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_WATCHDOG is not set +# CONFIG_WLAN_80211 is not set +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS=10 +CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK=y +# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set +# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set +# CONFIG_YAFFS_DOES_ECC is not set +# CONFIG_YAFFS_ECC_HAMMING is not set +CONFIG_YAFFS_ECC_RS=y +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y +CONFIG_YAFFS_YAFFS1=y +CONFIG_YAFFS_YAFFS2=y +CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/x-burst/image/Makefile b/target/linux/x-burst/image/Makefile new file mode 100644 index 000000000..b3b9a3dc3 --- /dev/null +++ b/target/linux/x-burst/image/Makefile @@ -0,0 +1,27 @@ +# +# Copyright (C) 2009 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/image.mk + +define Image/BuildKernel + cp $(KDIR)/vmlinux.elf $(BIN_DIR)/openwrt-$(BOARD)-vmlinux.elf + gzip -9 -c $(KDIR)/vmlinux > $(KDIR)/vmlinux.bin.gz + $(STAGING_DIR_HOST)/bin/lzma e $(KDIR)/vmlinux $(KDIR)/vmlinux.bin.l7 + dd if=$(KDIR)/vmlinux.bin.l7 of=$(BIN_DIR)/openwrt-$(BOARD)-vmlinux.lzma bs=65536 conv=sync + dd if=$(KDIR)/vmlinux.bin.gz of=$(BIN_DIR)/openwrt-$(BOARD)-vmlinux.gz bs=65536 conv=sync +endef + +define Image/Build/squashfs + $(call prepare_generic_squashfs,$(KDIR)/root.squashfs) +endef + +define Image/Build + $(call Image/Build/$(1)) + dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/openwrt-$(BOARD)-root.$(1) bs=128k conv=sync +endef + +$(eval $(call BuildImage)) diff --git a/target/linux/x-burst/patches-2.6.24/001-board.patch b/target/linux/x-burst/patches-2.6.24/001-board.patch new file mode 100644 index 000000000..289fcb938 --- /dev/null +++ b/target/linux/x-burst/patches-2.6.24/001-board.patch @@ -0,0 +1,233914 @@ +diff -urN linux-2.6.24.7.old/Changelog linux-2.6.24.7/Changelog +--- linux-2.6.24.7.old/Changelog 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/Changelog 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,364 @@ ++2009.02.17 ++* Modify oss driver to support jz4750 i2s codec. ++ Update Files: ++ sound/oss/Kconfig ++ sound/oss/Makefile ++ sound/oss/jz_i2s.c ++ Add Files: ++ sound/oss/jzdlv.h ++ sound/oss/jzdlv.c ++ ++2008.12.08 ++* Power management is supported for jz4750. ++ Update Files: ++ arch/mips/jz4750/pm.c ++ drivers/char/jzchar/poweroff.c ++ ++2008.12.04 ++* Whether NAND multiple planes operation for one partition is used or not could be ++ determined by the value of use_planes in partition_info[] in drivers/mtd/nand/jz47xx_nand.c ++ Updated files: ++ include/linux/mtd/partitions.h ++ drivers/mtd/nand/nand_base.c ++ drivers/mtd/nand/jz4740_nand.c ++ drivers/mtd/mtdpart.c ++* Supported 4KB page size nand with 2 planes ++ Update Files: ++ include/linux/mtd/nand.h ++ drivers/mtd/nand/nand_base.c ++ fs/yaffs2/utils/mkyaffs2image.c ++ drivers/mtd/mtd-utils/nandwrite_mlc.c ++ ++ ++2008.11.07 ++* The ubi was modified to support MTD of 64bit. ++ Updated file: ++ drivers/mtd/ubi/io.c ++* The ubi and ubifs were modified by changing vmalloc and vfree to kmalloc and kfree ++ to provide DMA buffer for NAND driver. But the NAND driver will use DMA buffer in ++ itself instead of in ubi and ubifs when CONFIG_MTD_NAND_DMABUF is defined. ++ Updated files: ++ drivers/mtd/ubi/build.c ++ drivers/mtd/ubi/cdev.c ++ drivers/mtd/ubi/gluebi.c ++ drivers/mtd/ubi/misc.c ++ drivers/mtd/ubi/scan.c ++ drivers/mtd/ubi/ubiblk.c ++ drivers/mtd/ubi/upd.c ++ drivers/mtd/ubi/vtbl.c ++ fs/ubifs/build.c ++ fs/ubifs/log.c ++ fs/ubifs/lpt.c ++ fs/ubifs/lpt_commit.c ++ fs/ubifs/orphan.c ++ fs/ubifs/recovery.c ++ fs/ubifs/replay.c ++ fs/ubifs/super.c ++ ++ ++2008.10.31 ++* Converted MTD from 32bit to 64bit to support the NAND larger than 4GB, and yaffs2 was ++ modified accordingly. ++ Updated file: ++ include/mtd/mtd-abi.h ++ include/linux/mtd/mtd.h ++ include/linux/mtd/partitions.h ++ include/linux/mtd/nand.h ++ drivers/mtd/mtdcore.c ++ drivers/mtd/mtdchar.c ++ drivers/mtd/mtdpart.c ++ drivers/mtd/mtdblock-jz.c ++ drivers/mtd/nand/nand_base.c ++ drivers/mtd/nand/nand_bbt.c ++ drivers/mtd/mtd-utils/include/mtd/mtd-abi.h ++ drivers/mtd/mtd-utils/flash_eraseall.c ++ drivers/mtd/mtd-utils/nandwrite_mlc.c ++ drivers/mtd/mtd-utils/nandwrite.c ++ fs/yaffs2/yaffs_fs.c ++ fs/yaffs2/yaffs_mtdif.c ++ fs/yaffs2/yaffs_mtdif2.c ++ ++2008.10.29 ++* Modified yaffs2 utils mkyaffs2image to support writting 4KB pagesize NAND. NAND layout ++ is (0 - raw(512B pagesize), 1 - nand_oob_64(2KB pagesize), 2 - nand_oob_128(4KB pagesize)). ++ ++ Updated file: ++ fs/yaffs2/utils/mkyaffs2image.c ++ ++2008.10.27 ++* Supported multiply chip selecting for NAND flash. ++ Updated files: ++ include/linux/mtd/nand.h ++ drivers/mtd/nand/nand_base.c ++ drivers/mtd/nand/jz4750_nand.c ++ drivers/mtd/nand/jz4740_nand.c ++ ++2008.10.23 ++* Modified yaffs2 utils mkyaffs2image to enable writing soft reed-solomn ECC for ++ yaffs2 file system information in oob area of MLC nand, getting CONFIG_YAFFS_ECC_RS ++ from .config, so when CONFIG_YAFFS_ECC_RS is changed, mkyaffs2image should be built ++ again. ++ Updated files: ++ fs/yaffs2/yaffs_ecc.c ++ fs/yaffs2/utils/Makefile ++ Added files: ++ fs/yaffs2/utils/ssfdc_rs_ecc.c ++ fs/yaffs2/utils/ssfdc_rs_ecc.h ++ ++2008.09.26 ++ ++* Fixed a fatal bug for mplayer, which may cause some files cannot be played and ++ the system is crashed. ++ Updated file: arch/mips/jz4740/proc.c ++ ++2008.08.30 ++* For jz4750, DMA clock for each channel should be enabled before using the channel. ++ So REG_DMAC_DMACKE and __dmac_channel_enable_clk(n) were added. ++ Updated files: ++ include/asm-mips/mach-jz4750/regs.h ++ include/asm-mips/mach-jz4750/ops.h ++ Regen, ++ ++2008.08.19 ++* Modify jzfb_mmap() for cacheable framebuffer access. ++ Updated files: ++ drivers/video/jzlcd.c ++ drivers/video/jz4740_slcd.c ++ drivers/video/jz4750_lcd.c ++ ++2008.08.15 ++* Modify ipu interface to toggle PID of MPlayer in TLB. ++* Modify OSS ioctl function to play movie with mono channel better. ++ Update Files: ++ arch/mips/jz4740/proc.c ++ sound/oss/jz_i2s.c ++ Richard Feng, ++ ++2008.08.04 ++* Check whether the free block is erased before erasing it, 'unsigned int' instead of ++ 'unsigned short' was used to store block number, and heap sort for lifetime after ++ erasing a block was replaced by another faster method. ++ Update File: ++ drivers/mtd/mtdblock-jz.uu ++ ++* Cache read was used in nand_read_page_hwecc_rs() for Jz4740. ++ Update File: ++ drivers/mtd/nand/nand_base.c ++ ++* Faster timing in REG_EMC_SMCR1 whose value is 0x09221200 was used. ++ Update File: ++ drivers/mtd/nand/jz4740_nand.c ++ ++ Regen, ++ ++2008.07.21 ++* Supported 4KB page size nand ++ Update File: ++ drivers/mtd/nand/nand_base.c ++ include/linux/mtd/nand.h ++ include/mtd/mtd-abi.h ++ drivers/mtd/mtd-utils/include/mtd/mtd-abi.h ++ Regen, ++ ++2008.07.18 ++* Soft reed solomon ECC was supported for yaffs2 information which is 16 bytes in nand ++ oob, and it should be used for MLC nand. ++ Update File: ++ fs/yaffs2/yaffs_ecc.c ++ fs/yaffs2/yaffs_ecc.h ++ fs/yaffs2/yaffs_fs.c ++ fs/yaffs2/yaffs_packedtags2.c ++ Regen, ++ ++2008.07.10 ++* Added support to generate any frequency baud rate of uart for both Jz4740 and Jz4750. ++ Update File: ++ drivers/serial/8250.c ++ Regen, ++ ++2008.07.03 ++* Modified Jz4750's INTC, CIM, TSSI, macros. ++ Update Files: ++ include/asm-mips/mach-jz4750/regs.h ++ include/asm-mips/mach-jz4750/ops.h ++ ++2008.06.24 ++* Combined Jz4750 SLCD Controller support into drivers/video/jz4750_lcd.c. ++* drivers/video/jz4750_lcd.c, now support: LCD Controller, Slcd Controller, TVE. ++* And add smart lcd panel TRULY_TFT_GG1P0319LTSW_W support. ++ Update Files: ++ drivers/video/Kconfig ++ drivers/video/jz4750_lcd.h ++ drivers/video/jz4750_lcd.c ++ Wolfgang Wang, ++ ++2008.06.20 ++* Add Jz4750 LCDC and TVE driver. ++ Update Files: ++ arch/asm-mips/mach-jz4750/regs.h ++ arch/asm-mips/mach-jz4750/ops.h ++ drivers/video/Kconfig ++ drivers/video/Makefile ++ Add Files: ++ drivers/video/jz4750_lcd.h ++ drivers/video/jz4750_lcd.c ++ drivers/video/jz4750_tve.h ++ drivers/video/jz4750_tve.c ++ ++2008.06.12 ++* Modified CONFIG_FB_JZXXX macros, rename drivers/video/jzslcd.x to drivers/video/jz4740_slcd.x ++ Update Files: ++ drivers/video/Kconfig ++ drivers/video/Makefile ++ arch/mips/configs/dipper_defconfig ++ arch/mips/configs/leo_defconfig ++ arch/mips/configs/lyra_defconfig ++ arch/mips/configs/pavo_defconfig ++ arch/mips/configs/virgo_defconfig ++ ++ ++2008.06.10 ++* Add jz_clocksource, upgrade the system time's accuracy from 10ms to about 1(or 2) us. ++ but the system timer remained 10ms. ++ Files modified: ++ arch/mips/jz4730/time.c ++ arch/mips/jz4740/time.c ++ arch/mips/jz4750/time.c ++ ++ ++2008.05.31 ++ ++* Updated UBIFS. ++ ++2008.05.30 ++ ++* Added JZ4720 virgo board support. ++ ++2008.05.29 ++ ++* Added definition of CONFIG_SOC_JZ4725 and CONFIG_SOC_JZ4720. ++* Added selection of 4-bit/1-bit data bus for MMC/SD card driver. ++* Added dipper_defconfig for JZ4725 DIPPER board. ++ ++2008.05.29: ++ ++* Modified sound/oss/jz_i2s.c to increase the sound buffer. ++* Modified pavo_defconfig to select the oss sound driver by default. ++* Fixed jzlcd.h for jz4730 pmp. ++* Modified jzcs8900a.c to not test the chip ID. ++ ++2008.05.22: ++ ++* jzcs8900a.c: fixed the bug of "No network devices available". ++ ++2008.05.13: ++ ++* Rewrote all of the UBI and UBIFS codes. ++ ++2008.05.07: ++* Add GPIO group E group F irq, DMAC1 irq. Add SSI1 macros. ++ ++ ++2008.05.06: ++ ++* Modified MMC/SD driver jz_mmc.c to support PM callback. ++ ++2008.05.04: ++ ++* Fixed a bug of mtdblock-jz.uu of using the badblock_table. ++ ++2008.04.26: ++ ++* Patch jz4740_nand.c to optimize the RS correction algorithm. ++ ++2008.04.24 ++* Jzlcd driver add Framebuffer Rotate support. ++ Update files: ++ drivers/video/Kconfig ++ drivers/video/jzlcd.h ++ drivers/video/jzlcd.c ++ ++ ++2008.04.21: ++* Modified LCD_CFG_MODE_INTER_CCIR656 define ++ #define LCD_CFG_MODE_INTER_CCIR656 (5 << LCD_CFG_MODE_BIT) ++ should be ==>> ++ #define LCD_CFG_MODE_INTER_CCIR656 (6 << LCD_CFG_MODE_BIT) ++ Update files: ++ include/asm-mips/mach-jz4730/regs.h ++ include/asm-mips/mach-jz4740/regs.h ++ include/asm-mips/mach-jz4750/regs.h ++ ++2008.04.14: ++ ++* Modify drivers/video/jzslcd.c to suport Smart LCD switches between ++ always refresh and event-driven refresh . ++ ++2008.04.01: ++ ++* Support multi-framebuffers, update files: ++ drivers/video/Kconfig, add: CONFIG_JZLCD_FRAMEBUFFER_MAX=1 ++ drivers/video/jzlcd.h ++ drivers/video/jzlcd.c ++ arch/mips/configs/pavo_defconfig, add: CONFIG_JZLCD_FRAMEBUFFER_MAX=1 ++ ++ ++2008.03.29: ++ ++* Modified sound/soc/jz4740/jz4740-i2s.c to support 32KHz PCM sample. ++ ++2008.03.28 ++ ++* Ported new mtd-utils and mkfs.ubifs. ++ ++2008.03.27 ++ ++* board_fuwa.h, change GPIO_DISP_OFF_N to GPD25. ++* Added lyra_defconfig for JZ4740 LYRA (MP4) board. ++ ++2008.03.24: ++ ++* Added jzslcd.c for Smart LCD framebuffer driver. ++* Modified rtc_jz.c to use some functions in rtc library instead of our function. ++ ++* Added jz_keypad.c and gpio_keys.c for scan keypad drivers. ++ ++2008.03.19: ++ ++* Added block-jz.c to support block device layer on top of ubi. ++ ++ ++2008.03.17: ++ ++* Modified jz4740_udc.c to enable the suspend irq when host unloads us. ++ ++* pavo_defconfig: select CONFIG_WIRELESS_EXT, CONFIG_PNP and CONFIG_SERIAL_8250_PNP. ++ ++ ++2008.03.14: ++ ++* Modified jz_ts.c jz_ts.h and sadc.c to release CPU by interrupt mode instead of pio mode. ++ ++ ++2008.03.13: ++ ++* Fixed a bug in jz4740_udc.c and jz4730_udc.c during rmmod the driver. ++ ++ ++2008.03.10: ++ ++* Modified jz_i2s.c to fix the jz_audio_release(). ++ ++2008.03.08: ++ ++* Fixed udc_hotplug.c to avoid the "unexpected IRQ". ++* Fixed jz4740/cpufreq.c of calculating the new_mclk. ++ ++2008.03.05: ++ ++* Modified drivers/video/console/fbcon.c by adding fb_flashcursor selection. ++ ++ ++2008.03.04: ++ ++* Initial release. +diff -urN linux-2.6.24.7.old/README-JZ linux-2.6.24.7/README-JZ +--- linux-2.6.24.7.old/README-JZ 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/README-JZ 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1181 @@ ++ ++ Linux 2.6 Kernel Release for Ingenic ++ ++ (Updated: 2008-12-29) ++ ++------------- ++** Content ** ++------------- ++ ++** Quick start ** ++** Supported SOC and Platforms ** ++** Overview of source tree ** ++** NAND Flash Filesystem ** ++** UBI, UBIFS and UBI Block Layer ** ++** UBI and UBIFS images ** ++** YAFFS2 ** ++** MTD Block Layer ** ++** About Bad Blocks of NAND ** ++** SLC and MLC NAND Flash ** ++** Initramfs ** ++** Audio full duplex mode ** ++** Support ** ++ ++ ++----------------- ++** Quick Start ** ++----------------- ++ ++To build linux 2.6, you needs a mipsel-linux-gcc version 4. Please ++download it from Ingenic website http://www.ingenic.cn. ++ ++You should have downloaded the linux-2.6.24.3.tar.bz2 and the latest kernel ++patch. The patch file was named as "linux-2.6.24.3-jz-yyyymmdd.patch.gz". ++ ++Follow next steps to install the full kernel source: ++ ++ $ tar -xjf linux-2.6.24.3.tar.bz2 ++ $ cd linux-2.6.24.3 ++ $ gzip -cd ../linux-2.6.24.3-jz-yyyymmdd.patch.gz | patch -p1 ++ ++Now you can configure and build the kernel. ++ ++First, you need to do a 'make board_defconfig' to select a board. ++ ++For example: ++ ++ - make pavo_defconfig # JZ4740 PAVO board default configuration ++ - make pmp_defconfig # JZ4730 PMP ver 2.x board default configuration ++ - make dipper_defconfig # JZ4725 PMP ver 1.x board default configuration ++ ++Then, configure and compile the kernel: ++ ++ - make xconfig or make menuconfig, if you want to change the configuration. ++ - make, make uImage, or make zImage, to build the kernel. ++ ++The ELF format kernel image is linux-2.6.24.3/vmlinux. ++The U-Boot format kernel image is linux-2.6.24.3/arch/mips/boot/uImage. ++The compressed raw kernel image is linux-2.6.24.3/arch/mips/boot/compressed/zImage. ++ ++ ++--------------------------------- ++** Supported SOC and Platforms ** ++--------------------------------- ++ ++This release supports several platforms based on JZ4730, JZ4740 and JZ4750. ++ ++JZ4750 based platforms: ++ ++ - apus: JZ4750 development board ++ ++JZ4740 based platforms: ++ ++ - pavo: JZ4740 reference board ++ - leo: JZ4740 development board ++ ++JZ4730 based platforms: ++ ++ - pmp: JZ4730 reference board version 2.x ++ ++JZ4725 based platforms: ++ ++ - dipper: JZ4725 reference board version 1.x ++ ++ ++----------------------------- ++** Overview of source tree ** ++----------------------------- ++ ++ - Changelog : Revision history ++ - README-JZ : This file ++ - arch/mips/ ++ - kernel/ : MIPS kernel common code ++ - mm/ : MIPS memory common code ++ - jz4730/ : JZ4730 code ++ - jz4740/ : JZ4740 JZ4725 JZ4720 code ++ - jz4750/ : JZ4750 code ++ - configs/ ++ - apus_defconfig : jz4750 based apus default configuration ++ - pavo_defconfig : jz4740 based pavo default configuration ++ - pmp_defconfig : jz4730 based pmp default configuration ++ - dipper_defconfig : jz4725 based dipper default configuration ++ - include/asm-mips/ : MIPS asm common include ++ - jzsoc.h : JZ SoC common include ++ - mach-jz4730/ : JZ4730 SoC headers ++ - mach-jz4740/ : JZ4740 JZ4725 JZ4720 SoC headers ++ - mach-jz4750/ : JZ4750 SoC headers ++ - fs/ ++ - jffs2/ : JFFS2 file system ++ - yaffs2/ : YAFFS2 file system ++ - utils/ : YAFFS2 utilities, like mkyaffs2image ++ - ubifs/ : ubifs file system ++ - mkfs.ubifs/ : mkfs.ubifs util to create UBIFS ++ - sound/ ++ oss/ : OSS audio driver ++ soc/jz4740/ : JZ4740 ALSA audio driver ++ - drivers/ ++ - char/ ++ - serial.c : serial port driver ++ - rtc_pcf8563.c : PCF8563 RTC driver ++ - rtc_jz.c : JZSOC On-Chip RTC driver ++ - jzchar/ : jzchar devices ++ - jz_ts.c : generic touch screen driver ++ - sadc.c : JZ4740 SADC driver ++ - ak4182.c : AK4182 touch driver ++ - udc_hotplug.c : UDC hotplug management ++ - poweroff.c : suspend/poweroff management ++ - input/keyboard/ ++ - jz_keypad.c : scan keypad driver ++ - gpio_keys.c : gpio keypad driver ++ - media/video/ ++ - jz_cim.c : generic camera driver ++ - jz_sensor.c : generic sensor driver ++ - mmc/host/ ++ - jz_mmc.c : jz mmc/sd card driver ++ - mtd/ ++ - mtdblock-jz.c : NAND Flash translation layer driver ++ - nand/ ++ - nand_base.c : NAND flash interface to MTD ++ - jz4730_nand.c : NAND flash definition on JZ4730 boards ++ - jz4740_nand.c : NAND flash definition on JZ4740 boards ++ - jz4750_nand.c : NAND flash definition on JZ4750 boards ++ - ubi/ : MTD utilities like flash_eraseall, nandwrite etc. ++ - ubiblk.c : UBI block layer driver on top of UBI ++ - mtd-utils/ : MTD and UBI utilities, like flash_eraseall, nandwrite and ubimkvol etc. ++ - ubi-utils : UBI utils like ubimkvol/ubirmvol/ubinize etc. ++ - net/ ++ - jz_eth.c : JZ4730 On-Chip ethernet driver ++ - jzcs8900a.c : cs8900a ethernet driver ++ - serial/ ++ - 8250.c : standard 16550A serial driver ++ - usb/ : USB OHCI host driver ++ - usb/host/ ++ ohci-jz.c : JZ OHCI driver ++ - usb/gadget/ ++ - jz4730_udc.c : JZ4730 UDC low-level driver ++ - jz4740_udc.c : JZ4740 and JZ4750 UDC low-level driver ++ - file_storage.c : USB mass storage class driver ++ - serial.c : USB serial class driver ++ - video/ ++ - jzlcd.c : JZ LCD controller framebuffer driver for JZ4730 and JZ4740 ++ - jz4740_slcd.c : JZ Smart LCD controller framebuffer driver for JZ4740 ++ - jz4750_lcd.c : JZ LCD and Smart LCD controller driver for JZ4750 ++ - jz4750_tve.c : JZ TV encoder controller driver for JZ4750 ++ - watchdog/ ++ - jz_wdt.c : JZ On-Chip watchdog driver ++ ++ ++--------------------------- ++** NAND Flash Filesystem ** ++--------------------------- ++ ++NAND Flash is the main non-volatile storage for most embedded devices. ++So, it's very important to implement a stable and reasonable filesystem on ++NAND flash. ++ ++In Linux, the MTD subsystem provides a common interface for operating with ++many flash devices, such as NOR, NAND etc. And the MTD subsystem was modified ++by Ingenic to support the NAND larger than 2GB. ++ ++Above MTD layer, we can implement the YAFFS2 filesystem. Or we can implement ++a MTD block device, on top of it we can implement the general filesystem ++such as FAT and EXT2. ++ ++The Linux 2.6 kernel also implements the UBI (Unsorted Block Images). UBI ++is a software layer above MTD layer which admits of LVM-like logical volumes ++on top of MTD devices, hides some complexities of flash chips like wear ++and bad blocks and provides some other useful capabilities. Please, consult ++the MTD web site for more details (www.linux-mtd.infradead.org). ++ ++On top of UBI, we can implement the UBIFS filesystem. We can also emulate ++block devices above UBI, such that we can use the general filesystem such as ++FAT and EXT2 on it. ++ ++The architecture of the NAND flash filesystem is illustrated as below: ++ ++ ++ ++ +-----------+ +-------------+ +-------------+ ++ | YAFFS2 | | UBIFS | | FAT or EXT2 | Filesystems ++ +-----------+ +-------------+ +-------------+ ++ \ | / \ ++ \ | / \ ++ \ | / \ ++ \ | +-----------------+ +-----------------+ ++ \ | | UBI Block Layer | | MTD Block Layer | ++ \ | +-----------------+ +-----------------+ ++ \ | / / ++ \ | / / ++ \ +-------------+ / ++ \ | UBI | / ++ \ +-------------+ / ++ \ | / ++ +-------------------------------------------+ ++ | MTD | ++ +-------------------------------------------+ ++ | ++ +--------------------+ ++ | nand_base.c | ++ +--------------------+ ++ | ++ +--------------------+ ++ | jz4740_nand.c | ++ +--------------------+ ++ ++ ++The related source codes are listed below: ++ ++fs/yaffs2: YAFFS2 ++fs/ubifs: UBIFS ++fs/fat: FAT ++fs/ext2: EXT2 ++drivers/mtd: MTD ++drivers/mtd/ubi: UBI ++drivers/mtd/ubi/ubiblk.c: UBI Block Layer ++drivers/mtd/mtdblock-jz.c: MTD Block Layer ++drivers/mtd/mtd-utils: MTD and UBI utils (flash_eraseall/ubimkvol/ubinfo/ubinize etc.) ++fs/ubifs/mkfs.ubifs: UBIFS util to create ubifs image (mkfs.ubifs) ++fs/yaffs2/util: YAFFS2 util (mkyaffs2image) ++ ++To build mtd utils, go to drivers/mtd/mtd-utils, type 'make' and ++'make install DESTDIR=/nfsroot/root26'. ++ ++To build yaffs2 util, go to fs/yaffs2/utils and type 'make'. ++ ++To build ubifs util, go to fs/ubifs/mkfs.ubifs and type 'make'. ++ ++Except 'UBI Block Layer' and 'MTD Block Layer', which are implement by Ingenic ++ourself, the others are general in the linux kernel tree. ++ ++User can select any one of these drivers to implement the filesystem. It all ++depends on yourself. ++ ++Following sections will describe how to use these drivers in details. ++ ++ ++------------------------------------ ++** UBI, UBIFS and UBI Block Layer ** ++------------------------------------ ++ ++UBIFS is a new flash file system which is designed to work on top of UBI. ++ ++Here is a short and unsorted list of some of UBIFS features: ++ ++* write-back support - This dramatically improves the throughput of the ++file-system comparing to JFFS2, which is write-through; ++ ++* fast mount time ++ ++* tolerance to unclean reboots - UBIFS is a journaling file system and it ++tolerates sudden crashes and unclean reboots; ++ ++* fast I/O - even with write-back disabled; ++ ++* on-the-flight compression - the data is stored in compressed form on ++the flash media, which makes it possible to put considerably more data to ++the flash as if the data would not be compressed; ++ ++Please, consult the MTD web site for more details (www.linux-mtd.infradead.org). ++ ++The UBI and UBIFS can be compiled as modules or built into the kernel. ++ ++To enable UBI, you need to select following configurations: ++ ++CONFIG_MTD_UBI: Enable UBI ++CONFIG_MTD_UBI_WL_THRESHOLD: UBI wear-leveling threshold ++CONFIG_MTD_UBI_BEB_RESERVE: Percentage of reserved eraseblocks for bad eraseblocks handling ++ ++To enable 'UBI Block Layer', you need to select following configurations: ++ ++CONFIG_MTD_UBI_BLKDEVS: Common interface to block layer for UBI ++CONFIG_MTD_UBI_BLOCK: Emulate block devices ++ ++To enable UBIFS, you need to select following configurations: ++ ++CONFIG_UBIFS_FS: UBIFS file system support ++CONFIG_UBIFS_COMPRESSION_OPTIONS: Advanced compression options for UBIFS ++CONFIG_UBIFS_LZO: UBIFS LZO compression support ++CONFIG_UBIFS_ZLIB: UBIFS ZLIB compression support ++CONFIG_UBIFS_FS_DEBUG: UBIFS debugging ++ ++If you want to compile as modules, take next steps: ++ ++Type 'make modules' to compile the modules. ++ ++Type 'make modules_install INSTALL_MOD_PATH=/nfsroot/root26' to install the ++modules to the target root. ++ ++You also need to compile the MTD and UBIFS utilities and install them to the ++target root. ++ ++ ++Now boot your board and mounted root FS with these modules, and below is a ++simple guide to test and use the UBIFS and 'UBI Block Layer': ++ ++Here we will create UBI volumes on mtd5. ++ ++First, format mtd5: ++ ++# flash_eraseall /dev/mtd5 ++Erasing 256 Kibyte @ 1ffc0000 -- 99 % complete. ++ ++Install UBI module, attached it to mtd5: ++ ++# modprobe ubi mtd=5 ++UBI: empty MTD device detected ++UBI: create volume table (copy #1) ++UBI: create volume table (copy #2) ++UBI: attached mtd5 to ubi0 ++UBI: MTD device name: "NAND VFAT partition" ++UBI: MTD device size: 512 MiB ++UBI: physical eraseblock size: 262144 bytes (256 KiB) ++UBI: logical eraseblock size: 258048 bytes ++UBI: number of good PEBs: 2048 ++UBI: number of bad PEBs: 0 ++UBI: smallest flash I/O unit: 2048 ++UBI: VID header offset: 2048 (aligned 2048) ++UBI: data offset: 4096 ++UBI: max. allowed volumes: 128 ++UBI: wear-leveling threshold: 4096 ++UBI: number of internal volumes: 1 ++UBI: number of user volumes: 0 ++UBI: available PEBs: 2024 ++UBI: total number of reserved PEBs: 24 ++UBI: number of PEBs reserved for bad PEB handling: 20 ++UBI: max/mean erase counter: 0/0 ++UBI: background thread "ubi_bgt0d" started, PID 241 ++ ++Now, create two UBI volumes, one is called ubifs and size is 200MB, the ++other is called vfat and size is 298MB. ++ ++# ubimkvol /dev/ubi0 -s 200MiB -N ubifs ++Volume ID 0, size 813 LEBs (209793024 bytes, 200.1 MiB), LEB size 258048 bytes (252.0 KiB), dynamic, name "ubifs", alignment 1 ++# ubimkvol /dev/ubi0 -s 298MiB -N vfat ++Volume ID 1, size 1211 LEBs (312496128 bytes, 298.0 MiB), LEB size 258048 bytes (252.0 KiB), dynamic, name "vfat", alignment 1 ++ ++Then you can use 'ubinfo' to query the UBI volume info: ++ ++# ubinfo -a ++UBI version: 1 ++Count of UBI devices: 1 ++UBI control device major/minor: 10:63 ++Present UBI devices: ubi0 ++ ++ubi0: ++Volumes count: 2 ++Logical eraseblock size: 258048 ++Total amount of logical eraseblocks: 2048 (528482304 bytes, 504.0 MiB) ++Amount of available logical eraseblocks: 0 (0 bytes) ++Maximum count of volumes 128 ++Count of bad physical eraseblocks: 0 ++Count of reserved physical eraseblocks: 20 ++Current maximum erase counter value: 3 ++Minimum input/output unit size: 2048 bytes ++Character device major/minor: 252:0 ++Present volumes: 0, 1 ++ ++Volume ID: 0 (on ubi0) ++Type: dynamic ++Alignment: 1 ++Size: 813 LEBs (209793024 bytes, 200.1 MiB) ++State: OK ++Name: ubifs ++Character device major/minor: 252:1 ++----------------------------------- ++Volume ID: 1 (on ubi0) ++Type: dynamic ++Alignment: 1 ++Size: 1211 LEBs (312496128 bytes, 298.0 MiB) ++State: OK ++Name: vfat ++Character device major/minor: 252:2 ++ ++ ++It shows that we have successfully created two UBI volumes (Volume ID 0 and 1) ++on ubi0. ++ ++Now you can install the UBIFS and 'UBI Block Layer' modules and create ++UBIFS and FAT on UBI volume 0 and 1 respectively. ++ ++# modprobe ubifs ++# modprobe ubiblk ++ ++# lsmod ++Module Size Used by Not tainted ++ubiblk 7696 0 ++bdev 10016 1 ubiblk ++deflate 4256 1 ++zlib_deflate 22256 1 deflate ++zlib_inflate 16992 1 deflate ++lzo 2400 1 ++lzo_decompress 2816 1 lzo ++lzo_compress 2848 1 lzo ++ubifs 208560 0 ++crc16 2048 1 ubifs ++ubi 103664 4 ubiblk,bdev,ubifs ++ ++Mount UBI volume 0 (the name is "ubifs") on ubi0 with type ubifs: ++ ++# mount -t ubifs ubi0:ubifs /mnt/ubifs/ ++UBIFS: mounted UBI device 0, volume 0 ++UBIFS: minimal I/O unit size: 2048 bytes ++UBIFS: logical eraseblock size: 258048 bytes (252 KiB) ++UBIFS: file system size: 207212544 bytes (202356 KiB, 197 MiB, 803 LEBs) ++UBIFS: journal size: 9420800 bytes (9200 KiB, 8 MiB, 37 LEBs) ++UBIFS: data journal heads: 1 ++UBIFS: default compressor: LZO ++ ++It shows that we have mounted it sucessfully. ++ ++Format /dev/ubiblock1 (the block device for UBI volume 1) and mount it with ++type vfat: ++ ++# mkfs.vfat /dev/ubiblock1 ++# mount -t vfat /dev/ubiblock1 /mnt/ubiblock1 ++ ++Please refer to the linux26_developer_guide.pdf for more details about ++the UBI and UBIFS. ++ ++ ++-------------------------- ++** UBI and UBIFS images ** ++-------------------------- ++ ++Generally, you want to create UBIFS and VFAT images respectively and combine ++these two images into one single image, and then use the nandwrite command ++to write this image to the MTD partition. ++ ++First of all, you need to compile the mkfs.ubifs utility. We use mkfs.ubifs ++to create the UBIFS image, like this: ++ ++Note: download the linux-nand-utils.tar.gz package from www.ingenic.cn and ++follows the guide to compile and run the mkfs.ubifs. ++ ++On the PC host: ++ ++$ mkfs.ubifs -h ++Usage: mkfs.ubifs [OPTIONS] ++Make a UBIFS file system image from an existing directory tree ++ ++Options: ++ -r, -d, --root=DIR Build file system from directory DIR ++ -m, --min-io-size=SIZE Minimum I/O size SIZE ++ -e, --leb-size=SIZE Use logical erase block size SIZE ++ -c, --max-leb-cnt=COUNT Use maximum logical erase block count COUNT ++ -o, --output=FILE Output to FILE ++ -j, --jrn-size=SIZE Use journal size SIZE bytes ++ -x, --compr=TYPE Use compression type TYPE (lzo, zlib or none) (default: lzo) ++ -f, --fanout=NUM Use fanout NUM (default: 8) ++ -k, --keyhash=TYPE Use key hash type TYPE (r5 or test) (default: r5) ++ -l, --log-lebs=COUNT Use COUNT erase blocks for the log ++ -p, --orph-lebs=COUNT Use COUNT erase blocks for orphans (default: 1) ++ -v, --verbose Verbose operation ++ -V, --version Display version information ++ -g, --debug=LEVEL Display debug information ++ -h, --help Display this help text ++ ++$ mkfs.ubifs -r /nfsroot/root26 -m 2048 -e 258048 -c 813 -o ubifs.img ++ ++This will create an UBIFS image called ubifs.img. The argument values ++can be obtained from the 'ubinfo -a' command. ++ ++Follow next steps to create a 30MB FAT32 image called vfat.img: ++ ++# dd if=/dev/zero of=vfat.img bs=1M count=30 ++# losetup /dev/loop0 vfat.img ++# mkfs.vfat /dev/loop0 ++# mount -t vfat /dev/loop0 /mnt/vfat ++# cp * /mnt/vfat ++# umount /mnt/vfat ++# losetup -d /dev/loop0 ++ ++Now the two images ubifs.img and vfat.img are ready, and we want to ++create two UBI volumes and write these two images to the two UBI volumes ++respectively. We can do like this: ++ ++First, use 'ubinize' command to combine ubifs.img and vfat.img into one ++UBI image called ubi.img. ++ ++Second, use 'nandwrite_mlc_ubi' command to write the ubi.img to the ++MTD partition. ++ ++To use 'ubinize' command, you should prepare an INI file for it. The ++content of the INI file are as below: ++ ++# cat ubinize.cfg ++[ubifs] ++mode=ubi ++image=ubifs.img ++vol_id=0 ++vol_size=200MiB ++vol_type=dynamic ++vol_name=ubifs ++vol_alignment=1 ++vol_flag=autoresize ++ ++[vfat] ++mode=ubi ++image=vfat.img ++vol_id=1 ++vol_size=298MiB ++vol_type=dynamic ++vol_name=vfat ++vol_alignment=1 ++vol_flag=autoresize ++ ++ ++Now you can boot your board and follow next guides to create the UBI ++image and burn the UBI image. ++ ++On the target board side: ++ ++# ubinize -o ubi.img ubinize.cfg -p 262144 -m 2048 ++ ++If things go well, you can get the UBI image ubi.img. ++ ++Then use 'nandwrite_ubi' command to write it to the MTD partition. ++ ++# flash_eraseall /dev/mtd5 ++# nandwrite_ubi -a -q -m /dev/mtd5 ubi.img ++ ++Now the UBI image has been written to the NAND mtd5 partition. ++ ++Use next commands to test it. ++ ++# modprobe ubi mtd=5 ++ ++# ubinfo -a ++UBI version: 1 ++Count of UBI devices: 1 ++UBI control device major/minor: 10:63 ++Present UBI devices: ubi0 ++ ++ubi0: ++Volumes count: 2 ++Logical eraseblock size: 258048 ++Total amount of logical eraseblocks: 2048 (528482304 bytes, 504.0 MiB) ++Amount of available logical eraseblocks: 0 (0 bytes) ++Maximum count of volumes 128 ++Count of bad physical eraseblocks: 0 ++Count of reserved physical eraseblocks: 20 ++Current maximum erase counter value: 1 ++Minimum input/output unit size: 2048 bytes ++Character device major/minor: 252:0 ++Present volumes: 0, 1 ++ ++Volume ID: 0 (on ubi0) ++Type: dynamic ++Alignment: 1 ++Size: 813 LEBs (209793024 bytes, 200.1 MiB) ++State: OK ++Name: ubifs ++Character device major/minor: 252:1 ++----------------------------------- ++Volume ID: 1 (on ubi0) ++Type: dynamic ++Alignment: 1 ++Size: 1211 LEBs (312496128 bytes, 298.0 MiB) ++State: OK ++Name: vfat ++Character device major/minor: 252:2 ++ ++ ++This shows that two UBI volumes are present on ubi0. ++ ++# modprobe ubifs ++# mount -t ubifs ubi0:ubifs /mnt/ubifs/ ++UBIFS: mounted UBI device 0, volume 0 ++UBIFS: minimal I/O unit size: 2048 bytes ++UBIFS: logical eraseblock size: 258048 bytes (252 KiB) ++UBIFS: file system size: 207212544 bytes (202356 KiB, 197 MiB, 803 LEBs) ++UBIFS: journal size: 9420800 bytes (9200 KiB, 8 MiB, 37 LEBs) ++UBIFS: data journal heads: 1 ++UBIFS: default compressor: LZO ++ ++# modprobe ubiblk ++# mount -t vfat /dev/ubiblock1 /mnt/ubiblock1 ++ ++# df ++Filesystem 1k-blocks Used Available Use% Mounted on ++tmpfs 30196 56 30140 0% /dev ++ubi0:ubifs 197536 48228 149308 24% /mnt/ubifs ++/dev/ubiblock1 30642 5762 24880 19% /mnt/ubiblock1 ++ ++It shows that the UBIFS and VFAT are all mounted successfully. ++ ++ ++------------ ++** YAFFS2 ** ++------------ ++ ++YAFFS (Yet Another Flash File System) was written to satisfy the ++special needs of NAND flash. The second release of YAFFS especially ++points to supporting newer NAND flash chips with 2k page size and ++up to 128MB capacity. ++ ++YAFFS2 is supported in this kernel. It's built on top of MTD directly. ++Go to fs/yaffs2 for more details. ++ ++To build the utility of YAFFS2, change to directory fs/yaffs2/utils/ ++and type 'make'. ++ ++To create a YAFFS2 image, mkyaffs2image command is used On PC host: ++ ++ usage: mkyaffs2image layout# dir image_file [convert] ++ ++ layout# NAND OOB layout: ++ 0 - nand_oob_raw, no used, ++ 1 - nand_oob_64, for 2KB pagesize, ++ 2 - nand_oob_128, for 2KB pagesize using multiple planes or 4KB pagesize, ++ 3 - nand_oob_256, for 4KB pagesize using multiple planes ++ dir the directory tree to be converted ++ image_file the output file to hold the image ++ ++ e.g., for 2KB page size NAND not using multi-plane: ++ ++ $ mkyaffs2image 1 /nfsroot/root26 root26.yaffs2 ++ ++To burn the yaffs2 image to the NAND, use next command (On target board): ++ ++ # nandwrite -a -o /dev/mtd2 root26.yaffs2 ++ ++To format and mount YAFFS2 (On target board): ++ ++ # flash_eraseall /dev/mtd2 ++ # mount -t yaffs2 /dev/mtdblock2 /mnt/mtdblock2 ++ ++ ++--------------------- ++** MTD Block Layer ** ++--------------------- ++ ++Ingenic implement the 'MTD Block Layer' by ourself. This gives you a choice ++to implement a general filesystem such as FAT and ext2 on NAND flash. ++ ++Which partition used for VFAT should be set in bootargs of u-boot for kernel cmdline, ++so the kernel kowns other partitions aren't base on MTD Block Layer, and their mounting ++speed will be faster. For example, if mtdblock5 is used for VFAT, you can ++ ++set bootargs mem=64M console=ttyS1,57600n8 ip=dhcp root=/dev/mtdblock2 rw mtdblk=5 ++ ++and the mounting speed of mtdblock2 which is not based on MTD Block Layer will be faster. ++ ++ ++Features of the 'MTD Block Layer' include: ++ ++1. Block unit management (address mapping & block cache operations) ++2. Wear-leveling ++3. Bad block management ++4. Write verify enable ++5. mutiple choice of ECC algorithms (hardware Hamming ECC & Reed Solomon ECC, ++software Hamming ECC) ++ ++Kernel configurations related to the 'MTD Block Layer': ++ ++* CONFIG_MTD_OOB_COPIES: defines how many copies of the critical oob data for ++ each block. Since the page data can be corrected by the ECC algorithm but ++ the oob data can't, we want to ensure the correction of the oob data by this ++ way. The mtdblock-jz translation layer driver uses block mode to manipulate ++ the NAND flash. It makes several copies of the oobinfo data for each block, ++ so that it can get a correct copy even there is an error in one of them. ++ ++* CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE: defines this to enable the write ++ verification function, which will read back data to verify during a write ++ operation. ++ ++Take following steps to use the 'MTD Block Layer': ++ ++ # flash_eraseall /dev/mtd3 ++ # mkfs.vfat /dev/mtdblock3 ++ # mount -t vfat /dev/mtdblock3 /mnt/vfat ++ ++ ++NOTICE: ++ ++You can define mutiple VFAT partitions, all the VFAT partitions share ++the same above configurations. ++ ++Each VFAT partition have its own block cache which resides only in RAM. ++Generally, the block cache flush operation is triggered when the access ++address exceeds block boundary. The last block cache usually will be ++flushed to NAND device when the device is closed (eg: umount /mnt/vfat; ++use system call close(fd)). ++ ++Abrupt poweroff without flushing the last block cache will cause the ++VFAT partition to lose the most significant data which records the ++information of the file system management such as FAT table, inodes ... ++ ++To avoid this bad thing, you have to flush block cache as soon as possible. ++Please do remember to flush block cache manually when you finish ++a write operation. ++ ++The MTD block layer driver supplys an ioctl for triggering flush block cache ++operation. The code attached behind is a reference for you to use. ++ ++eg: ++ ++ # cp * /mnt/vfat ++ # sync ; this step is necessary to flush the FS cache ++ # flushcache /dev/mtdblock3 ; this step is necessary to flush the NFTL block cache ++ ++ ++/* flushcache.c */ ++#include ++#include ++#include ++#include ++ ++int main(int argc,char **argv) ++{ ++ int fd; ++ ++ if( argc != 2 ){ ++ printf( "Usage:%s device name(full path)\n", argv[0] ); ++ return -1; ++ } ++ ++ if( (fd = open( argv[1], O_RDONLY ) ) == -1) { ++ printf( "Open %s failed\n", argv[1] ); ++ return -1; ++ } ++ ++ if( ioctl( fd, BLKFLSBUF) == -1) ++ printf("flush catche failed\n"); ++ ++ close(fd); ++ return 0; ++} ++ ++ ++------------------------------ ++** About Bad Blocks of NAND ** ++------------------------------ ++ ++NAND is a special flash type which there are new bad blocks generated during ++the whole period of using it. So the NAND driver should know how to detect ++a bad block and how to mark a new block bad. ++ ++Some types of NAND flash mark the bad block in the spare area of the first ++page but others in the last page. So we define a kernel configuration called ++CONFIG_MTD_BADBLOCK_FLAG_PAGE and use it to decide the bad block. ++ ++CONFIG_MTD_BADBLOCK_FLAG_PAGE: page in a block to store the badblock mark ++ ++Following functions should be cared: ++ ++nand_base.c: ++ ++ - nand_block_bad() ++ - nand_default_block_markbad() ++ ++nand_bbt.c: ++ ++ - create_bbt() ++ ++--------------------------------------- ++** Multiple plane operation for NAND ** ++--------------------------------------- ++ ++NAND multiple plane is a feature enabling support of simultaneous write/erase ++operations for NAND devices with multiplane architecture. This feature ++significantly increases write performance. It could give performance benefits ++if NAND device supports ultiple plane architecture only. If NAND device does ++not support multiple plane and CONFIG_MTD_NAND_MULTI_PLANE was set to yes when ++compiling kernel, code will parse device capabilities and NAND device will work ++in single plane mode. For safe, you'd better set CONFIG_MTD_NAND_MULTI_PLANE to ++no if NAND device does not support multiple plane. ++ ++If the NAND device support multiple plane, you could determine which partitions ++use multiple plane and which use single plane by setting the value of ++partition_info[] in driver/mtd/nand/jz47xx_nand.c, if the value of use_planes ++for a partition is 0, then the partition uses single plane, or else it uses ++multiple plane. e.g. ++ ++static struct mtd_partition partition_info[] = { ++ {name:"NAND BOOT partition", ++ offset:0 * 0x100000, ++ size:4 * 0x100000, ++ use_planes: 0}, ++ {name:"NAND KERNEL partition", ++ offset:4 * 0x100000, ++ size:4 * 0x100000, ++ use_planes: 0}, ++ {name:"NAND ROOTFS partition", ++ offset:8 * 0x100000, ++ size:120 * 0x100000, ++ use_planes: 1}, ++ {name:"NAND DATA1 partition", ++ offset:128 * 0x100000, ++ size:128 * 0x100000, ++ use_planes: 1}, ++ {name:"NAND DATA2 partition", ++ offset:256 * 0x100000, ++ size:256 * 0x100000, ++ use_planes: 1}, ++ {name:"NAND VFAT partition", ++ offset:512 * 0x100000, ++ size:512 * 0x100000, ++ use_planes: 1}, ++}; ++ ++--------------------------------------- ++** Multiple chip selecting for NAND ** ++--------------------------------------- ++ ++CS1_N pin on Jz4740 or Jz4750 will be used for NAND defaultly; besides CS2_N, ++CS3_N, and CS4_N pins could be used for NAND, too. You can configure the kernel ++and select the configuration at: ++ ++ [Memory Technology Devices (MTD)] --> [NAND Device Support] ++ ++ --> [ECC Type] --> [Use NAND on CS2_N of JZSOC] ++ [Use NAND on CS3_N of JZSOC] ++ [Use NAND on CS4_N of JZSOC] ++ ++ ++---------------------------- ++** SLC and MLC NAND Flash ** ++---------------------------- ++ ++Single-Level Cell (SLC) and Multi-Level Cell (MLC) are both NAND-based ++non-volatile memory technologies. MLC NAND Flash allows each memory cell ++to store two bits of information, compared to the one bit-per-cell SLC ++NAND Flash allows. As a result, 90 nanometer (nm) MLC NAND offers a ++larger capacity (typically twice the density of SLC) and at a cost point ++appropriate for consumer products. ++ ++Though SLC NAND offers a lower density, it also provides an enhanced ++level of performance in the form of faster write speeds. Because SLC ++stores only one bit per cell, the likelihood for error is reduced. ++At 90 nanometer process, it is recommended to implement a 1 to 2-bit ++ECC for SLC, whereas 4-bit ECC is recommended on the MLC architecture. ++ ++The linux kernel from ingenic provides MLC NAND support through Hardware ++ECC algorithm. Jz4740 supports Reed-Solomon (RS) ECC algorithm, which can ++detect and correct 4-bit errors per 512 bytes at least. Jz4750 supports ++4-bit and 8-bit BCH ECC algorithm, 4-bit BCH ECC can detect and correct ++4 bits for up to 1010 bytes and 8-bit BCH ECC can detect and correct ++8 bits errors for up to 1016 bytes. ++ ++To include MLC NAND support, you are required to configure the kernel ++and select the configuration CONFIG_MTD_HW_RS_ECC for Jz4740, and ++CONFIG_MTD_HW_BCH_ECC for Jz4750, which can be found at: ++ ++ [Memory Technology Devices (MTD)] --> [NAND Device Support] ++ ++ --> [ECC Type] --> [Select hardware RS ECC] ++ [Select hardware BCH ECC] ++ ++ ++--------------- ++** Initramfs ** ++--------------- ++ ++Please read Documentation/filesystems/ramfs-rootfs-initramfs.txt for ++more information about how to use initramfs. ++ ++Following are some steps to help you to create root fs by using initramfs: ++ ++# cd /rootfs/ ++# find . | cpio -c -o | gzip -9 > ../rootfs.cpio.gz ++ ++Reconfigure the Linux kernel and select next configurations: ++ ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="/rootfs.cpio.gz" ++CONFIG_INITRAMFS_ROOT_UID=0 ++CONFIG_INITRAMFS_ROOT_GID=0 ++ ++Rebuild the kernel and boot the kernel with next command lines: ++ ++"root=/dev/ram0 rw rdinit=/sbin/init" ++ ++ ++---------------------------- ++** Audio full duplex mode ** ++---------------------------- ++ ++OSS audio driver supports full duplex mode, that is to say, you can ++record while replaying. ++ ++The user application want to do some jobs to enable the full duplex mode ++of the OSS driver. The following are two examples: ++ ++ ++One is: ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AUDIO_FILE "/dev/dsp" ++#define FREQS 48000 ++#define CHANNELS 2 ++#define TEST_TIME 10 ++#define SAMPLES 16 ++#define BUF_SIZE 8192 ++#define MAX_LEN (FREQS * CHANNELS * TEST_TIME * SAMPLES / 8) ++ ++int main(int argc, char *argv[]) ++{ ++ FILE *fp; ++ pid_t pid; ++ int audio_fd, i; ++ char *play_name; ++ char *rec_name; ++ ++ int play_count, play_cnt, played; ++ int rec_count, rec_cnt, recorded; ++ ++ u_char play_arr[MAX_LEN]; ++ u_char rec_arr[MAX_LEN]; ++ int err = 0; ++ ++ play_name = argv[1]; ++ rec_name = argv[2]; ++ ++ if ((audio_fd = open("/dev/dsp", O_RDWR)) < 0) { ++ printf(" Can't open sound device!\n"); ++ exit(-1); ++ } ++ ++ i = BUF_SIZE; ++ ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &i); ++ ioctl(audio_fd, SNDCTL_DSP_SYNC, NULL); ++ ++ i = CHANNELS; ++ ioctl(audio_fd, SNDCTL_DSP_STEREO, (u_char *) &i); ++ ++ i = SAMPLES; ++ ioctl(audio_fd, SNDCTL_DSP_SAMPLESIZE, (u_char *) &i); ++ ++ i = FREQS; ++ ioctl(audio_fd, SNDCTL_DSP_SPEED, (u_char *) &i); ++ ++ fp=fopen(play_name, "r"); ++ fread(play_arr, 1, MAX_LEN, fp); ++ fclose(fp); ++ ++ ++ play_count = MAX_LEN; ++ played = 0; ++ ++ rec_count = MAX_LEN; ++ recorded = 0; ++ ++ switch(pid = fork()) { ++ case -1: ++ printf("error\n"); ++ break; ++ case 0: ++ printf(" playing\n"); ++ while (play_count) { ++ play_cnt = play_count; ++ if (play_cnt > BUF_SIZE) ++ play_cnt = BUF_SIZE; ++ ++ write (audio_fd, (char *)play_arr+played, play_cnt); ++ played += play_cnt; ++ play_count -= play_cnt; ++ }/* while (count) */ ++ exit(0); ++ break; ++ default: ++ printf(" recording\n"); ++ while (rec_count) { ++ rec_cnt = rec_count; ++ if (rec_cnt > BUF_SIZE) ++ rec_cnt = BUF_SIZE; ++ ++ read (audio_fd, (char *)rec_arr+recorded, rec_cnt); ++ recorded += rec_cnt; ++ rec_count -= rec_cnt; ++ }/* while (count) */ ++ break; ++ } ++ close(audio_fd); ++ ++ printf("write data to file, waiting for a while\n"); ++ fp=fopen(rec_name, "w"); ++ fwrite(rec_arr, 1, MAX_LEN, fp); ++ fclose(fp); ++ ++ return err; ++} ++ ++The other is : ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AUDIO_FILE "/dev/dsp" ++#define FREQS_PLAY 22050 ++#define FREQS_REC 8000 ++#define FREQS 22050 ++#define CHANNELS 1 ++#define TEST_TIME 10 ++#define SAMPLES 16 ++#define BUF_SIZE 8192 ++#define MAX_LEN (FREQS * CHANNELS * TEST_TIME * SAMPLES / 8) ++ ++int main(int argc, char *argv[]) ++{ ++ FILE *fp; ++ pid_t pid; ++ int audio_fd, i; ++ char *play_name; ++ char *rec_name1, *rec_name2, *rec_name3; ++ int mixerfd, vol, savevol; ++ ++ int count; ++ int play_count, play_cnt, played; ++ int rec_count, rec_cnt, recorded; ++ ++ u_char play_arr[MAX_LEN]; ++ u_char rec_arr[MAX_LEN]; ++ int err = 0; ++ ++ play_name = argv[1]; ++ rec_name1 = argv[2]; ++ rec_name2 = argv[3]; ++ rec_name3 = argv[4]; ++ ++ if ((mixerfd = open("/dev/mixer", O_RDWR)) < 0) { ++ perror("/dev/mixer"); ++ exit(1); ++ } ++ ++ if ((audio_fd = open("/dev/dsp", O_RDWR)) < 0) { ++ printf(" Can't open sound device!\n"); ++ exit(-1); ++ } ++ ++ i = BUF_SIZE; ++ ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &i);// midify buffer lenth ++ ioctl(audio_fd, SNDCTL_DSP_SYNC, NULL); ++ ++ i = CHANNELS; ++ ioctl(audio_fd, SNDCTL_DSP_CHANNELS, (u_char *) &i); ++ ++ i = SAMPLES; ++ ioctl(audio_fd, SNDCTL_DSP_SAMPLESIZE, (u_char *) &i); ++ ++ fp=fopen(play_name, "r"); ++ fread(play_arr, 1, MAX_LEN, fp); ++ fclose(fp); ++ ++ if (ioctl(mixerfd, SOUND_MIXER_READ_MIC, &savevol) == -1) { ++ perror("SOUND_MIXER_READ_DEVMASK mic"); ++ exit(-1); ++ } ++ ++ savevol = savevol & 0xff; ++ ++ for (count = 1; count <= 3; count++) { ++ ++ play_count = MAX_LEN; ++ played = 0; ++ ++ rec_count = MAX_LEN; ++ recorded = 0; ++ ++ ++ i = FREQS_PLAY; ++ ioctl(audio_fd, SNDCTL_DSP_SPEED, (u_char *) &i); ++ ++ printf(" playing %d\n", i); ++ while (play_count) { ++ play_cnt = play_count; ++ if (play_cnt > BUF_SIZE) ++ play_cnt = BUF_SIZE; ++ ++ write (audio_fd, (char *)play_arr+played, play_cnt); ++ played += play_cnt; ++ play_count -= play_cnt; ++ }/* while (count) */ ++ ++ /* wait for sync */ ++ ioctl(audio_fd, SNDCTL_DSP_SYNC, NULL); ++ ++ /* set mic gain 0 */ ++ vol = 0; ++ if (ioctl(mixerfd, SOUND_MIXER_WRITE_MIC, &vol) == -1) { ++ perror("SOUND_MIXER_WRITE_DEVMASK mic"); ++ exit(-1); ++ } ++ i = FREQS_REC; ++ ioctl(audio_fd, SNDCTL_DSP_SPEED, (u_char *) &i); ++ /* set mic gain orgin */ ++ vol = savevol; ++ if (ioctl(mixerfd, SOUND_MIXER_WRITE_MIC, &vol) == -1) { ++ perror("SOUND_MIXER_READ_DEVMASK mic"); ++ exit(-1); ++ } ++ ++ printf(" recording %d\n", i); ++ while (rec_count) { ++ rec_cnt = rec_count; ++ if (rec_cnt > BUF_SIZE) ++ rec_cnt = BUF_SIZE; ++ ++ read (audio_fd, (char *)rec_arr+recorded, rec_cnt); ++ recorded += rec_cnt; ++ rec_count -= rec_cnt; ++ }/* while (count) */ ++ ++ switch (count) { ++ case 1: ++ printf(" write to %s\n",rec_name1); ++ fp=fopen(rec_name1, "w"); ++ fwrite(rec_arr, 1, MAX_LEN, fp); ++ fclose(fp); ++ break; ++ case 2: ++ printf(" write to %s\n",rec_name2); ++ fp=fopen(rec_name2, "w"); ++ fwrite(rec_arr, 1, MAX_LEN, fp); ++ fclose(fp); ++ break; ++ case 3: ++ printf(" write to %s\n",rec_name3); ++ fp=fopen(rec_name3, "w"); ++ fwrite(rec_arr, 1, MAX_LEN, fp); ++ fclose(fp); ++ break; ++ } ++ sleep(1); ++ } ++ ++ close(audio_fd); ++ ++ return err; ++} ++ ++------------- ++** Support ** ++------------- ++ ++Welcome to Ingenic website: ++ ++More details, please refer to linux26_developer_guide.pdf. +diff -urN linux-2.6.24.7.old/arch/mips/Kconfig linux-2.6.24.7/arch/mips/Kconfig +--- linux-2.6.24.7.old/arch/mips/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/arch/mips/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -16,6 +16,82 @@ + prompt "System type" + default SGI_IP22 + ++config JZ4730_PMP ++ bool "Ingenic JZ4730 PMP board" ++ select DMA_NONCOHERENT ++ select SYS_HAS_CPU_MIPS32_R1 ++ select SYS_SUPPORTS_32BIT_KERNEL ++ select SYS_SUPPORTS_LITTLE_ENDIAN ++ select SOC_JZ4730 ++ ++config JZ4740_PAVO ++ bool "Ingenic JZ4740 PAVO board" ++ select DMA_NONCOHERENT ++ select SYS_HAS_CPU_MIPS32_R1 ++ select SYS_SUPPORTS_32BIT_KERNEL ++ select SYS_SUPPORTS_LITTLE_ENDIAN ++ select SOC_JZ4740 ++ ++config JZ4740_LEO ++ bool "Ingenic JZ4740 LEO board" ++ select DMA_NONCOHERENT ++ select SYS_HAS_CPU_MIPS32_R1 ++ select SYS_SUPPORTS_32BIT_KERNEL ++ select SYS_SUPPORTS_LITTLE_ENDIAN ++ select SOC_JZ4740 ++ ++config JZ4740_LYRA ++ bool "Ingenic JZ4740 LYRA board" ++ select DMA_NONCOHERENT ++ select SYS_HAS_CPU_MIPS32_R1 ++ select SYS_SUPPORTS_32BIT_KERNEL ++ select SYS_SUPPORTS_LITTLE_ENDIAN ++ select SOC_JZ4740 ++ ++config JZ4725_DIPPER ++ bool "Ingenic JZ4725 DIPPER board" ++ select DMA_NONCOHERENT ++ select SYS_HAS_CPU_MIPS32_R1 ++ select SYS_SUPPORTS_32BIT_KERNEL ++ select SYS_SUPPORTS_LITTLE_ENDIAN ++ select SOC_JZ4740 ++ select SOC_JZ4725 ++ ++config JZ4720_VIRGO ++ bool "Ingenic JZ4720 VIRGO board" ++ select DMA_NONCOHERENT ++ select SYS_HAS_CPU_MIPS32_R1 ++ select SYS_SUPPORTS_32BIT_KERNEL ++ select SYS_SUPPORTS_LITTLE_ENDIAN ++ select SOC_JZ4740 ++ select SOC_JZ4720 ++ ++config JZ4750_FUWA ++ bool "Ingenic JZ4750 FUWA board" ++ select DMA_NONCOHERENT ++ select SYS_HAS_CPU_MIPS32_R1 ++ select SYS_SUPPORTS_32BIT_KERNEL ++ select SYS_SUPPORTS_LITTLE_ENDIAN ++ select SOC_JZ4750 ++ select JZ_FPGA ++ ++config JZ4750D_FUWA1 ++ bool "Ingenic JZ4750d FUWA1 board" ++ select DMA_NONCOHERENT ++ select SYS_HAS_CPU_MIPS32_R1 ++ select SYS_SUPPORTS_32BIT_KERNEL ++ select SYS_SUPPORTS_LITTLE_ENDIAN ++ select SOC_JZ4750D ++ select JZ_FPGA ++ ++config JZ4750_APUS ++ bool "Ingenic JZ4750 APUS board" ++ select DMA_NONCOHERENT ++ select SYS_HAS_CPU_MIPS32_R1 ++ select SYS_SUPPORTS_32BIT_KERNEL ++ select SYS_SUPPORTS_LITTLE_ENDIAN ++ select SOC_JZ4750 ++ + config MACH_ALCHEMY + bool "Alchemy processor based machines" + +@@ -701,6 +777,48 @@ + + endmenu + ++##################################################### ++# Ingenic SOC series ++##################################################### ++ ++config SOC_JZ4730 ++ bool ++ select JZSOC ++ ++config SOC_JZ4740 ++ bool ++ select JZSOC ++ ++config SOC_JZ4725 ++ bool ++ select JZSOC ++ ++config SOC_JZ4720 ++ bool ++ select JZSOC ++ ++config SOC_JZ4750 ++ bool ++ select JZSOC ++ ++config SOC_JZ4750D ++ bool ++ select JZSOC ++ ++config JZ_FPGA ++ bool ++ ++config JZSOC ++ bool ++ select JZRISC ++ select SYS_HAS_CPU_MIPS32_R1 ++ select SYS_SUPPORTS_32BIT_KERNEL ++ ++config JZRISC ++ bool ++ ++#################################################### ++ + config RWSEM_GENERIC_SPINLOCK + bool + default y +@@ -1770,6 +1888,14 @@ + + source "kernel/time/Kconfig" + ++# the value of (max order + 1) ++config FORCE_MAX_ZONEORDER ++ prompt "MAX_ZONEORDER" ++ int ++ default "12" ++ help ++ The max memory that can be allocated = 4KB * 2^(CONFIG_FORCE_MAX_ZONEORDER - 1) ++ + # + # Timer Interrupt Frequency Configuration + # +@@ -2046,6 +2172,23 @@ + + endmenu + ++menu "CPU Frequency scaling" ++ ++config CPU_FREQ_JZ ++ tristate "CPUfreq driver for JZ CPUs" ++ depends on JZSOC ++ default n ++ help ++ This enables the CPUfreq driver for JZ CPUs. ++ ++ If in doubt, say N. ++ ++if (CPU_FREQ_JZ) ++source "drivers/cpufreq/Kconfig" ++endif ++ ++endmenu ++ + menu "Power management options" + + source "kernel/power/Kconfig" +diff -urN linux-2.6.24.7.old/arch/mips/Makefile linux-2.6.24.7/arch/mips/Makefile +--- linux-2.6.24.7.old/arch/mips/Makefile 2009-04-12 18:05:07.000000000 +0200 ++++ linux-2.6.24.7/arch/mips/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -159,6 +159,37 @@ + # + + # ++# Commond Ingenic JZ4730 series ++# ++core-$(CONFIG_SOC_JZ4730) += arch/mips/jz4730/ ++cflags-$(CONFIG_SOC_JZ4730) += -Iinclude/asm-mips/mach-jz4730 ++load-$(CONFIG_SOC_JZ4730) += 0xffffffff80010000 ++ ++# ++# Commond Ingenic JZ4740 series ++# ++ ++core-$(CONFIG_SOC_JZ4740) += arch/mips/jz4740/ ++cflags-$(CONFIG_SOC_JZ4740) += -Iinclude/asm-mips/mach-jz4740 ++load-$(CONFIG_SOC_JZ4740) += 0xffffffff80010000 ++ ++# ++# Commond Ingenic JZ4750 series ++# ++ ++core-$(CONFIG_SOC_JZ4750) += arch/mips/jz4750/ ++cflags-$(CONFIG_SOC_JZ4750) += -Iinclude/asm-mips/mach-jz4750 ++load-$(CONFIG_SOC_JZ4750) += 0xffffffff80010000 ++ ++# ++# Commond Ingenic JZ4750d series ++# ++ ++core-$(CONFIG_SOC_JZ4750D) += arch/mips/jz4750d/ ++cflags-$(CONFIG_SOC_JZ4750D) += -Iinclude/asm-mips/mach-jz4750d ++load-$(CONFIG_SOC_JZ4750D) += 0xffffffff80010000 ++ ++# + # Acer PICA 61, Mips Magnum 4000 and Olivetti M700. + # + core-$(CONFIG_MACH_JAZZ) += arch/mips/jazz/ +@@ -673,6 +704,12 @@ + + all: $(all-y) + ++uImage: $(vmlinux-32) ++ +@$(call makeboot,$@) ++ ++zImage: $(vmlinux-32) ++ +@$(call makeboot,$@) ++ + vmlinux.bin: $(vmlinux-32) + +@$(call makeboot,$@) + +@@ -697,12 +734,13 @@ + + archclean: + @$(MAKE) $(clean)=arch/mips/boot ++ @$(MAKE) $(clean)=arch/mips/boot/compressed + @$(MAKE) $(clean)=arch/mips/lasat + + define archhelp +- echo ' vmlinux.ecoff - ECOFF boot image' +- echo ' vmlinux.bin - Raw binary boot image' +- echo ' vmlinux.srec - SREC boot image' ++ echo ' uImage - u-boot format image (arch/$(ARCH)/boot/uImage)' ++ echo ' zImage - Compressed binary image (arch/$(ARCH)/boot/compressed/zImage)' ++ echo ' vmlinux.bin - Uncompressed binary image (arch/$(ARCH)/boot/vmlinux.bin)' + echo + echo ' These will be default as apropriate for a configured platform.' + endef +diff -urN linux-2.6.24.7.old/arch/mips/Makefile.orig linux-2.6.24.7/arch/mips/Makefile.orig +--- linux-2.6.24.7.old/arch/mips/Makefile.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/Makefile.orig 2009-04-12 18:01:55.000000000 +0200 +@@ -0,0 +1,712 @@ ++# ++# 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) 1994, 95, 96, 2003 by Ralf Baechle ++# DECStation modifications by Paul M. Antoine, 1996 ++# Copyright (C) 2002, 2003, 2004 Maciej W. Rozycki ++# ++# This file is included by the global makefile so that you can add your own ++# architecture-specific flags and dependencies. Remember to do have actions ++# for "archclean" cleaning up for this architecture. ++# ++ ++cflags-y := ++ ++# ++# Select the object file format to substitute into the linker script. ++# ++ifdef CONFIG_CPU_LITTLE_ENDIAN ++32bit-tool-archpref = mipsel ++64bit-tool-archpref = mips64el ++32bit-bfd = elf32-tradlittlemips ++64bit-bfd = elf64-tradlittlemips ++32bit-emul = elf32ltsmip ++64bit-emul = elf64ltsmip ++else ++32bit-tool-archpref = mips ++64bit-tool-archpref = mips64 ++32bit-bfd = elf32-tradbigmips ++64bit-bfd = elf64-tradbigmips ++32bit-emul = elf32btsmip ++64bit-emul = elf64btsmip ++endif ++ ++ifdef CONFIG_32BIT ++tool-archpref = $(32bit-tool-archpref) ++UTS_MACHINE := mips ++endif ++ifdef CONFIG_64BIT ++tool-archpref = $(64bit-tool-archpref) ++UTS_MACHINE := mips64 ++endif ++ ++ifneq ($(SUBARCH),$(ARCH)) ++ ifeq ($(CROSS_COMPILE),) ++ CROSS_COMPILE := $(call cc-cross-prefix, $(tool-archpref)-linux- $(tool-archpref)-linux-gnu- $(tool-archpref)-unknown-linux-gnu-) ++ endif ++endif ++ ++ifdef CONFIG_32BIT ++ld-emul = $(32bit-emul) ++vmlinux-32 = vmlinux ++vmlinux-64 = vmlinux.64 ++ ++cflags-y += -mabi=32 ++endif ++ ++ifdef CONFIG_64BIT ++ld-emul = $(64bit-emul) ++vmlinux-32 = vmlinux.32 ++vmlinux-64 = vmlinux ++ ++cflags-y += -mabi=64 ++endif ++ ++all-$(CONFIG_BOOT_ELF32) := $(vmlinux-32) ++all-$(CONFIG_BOOT_ELF64) := $(vmlinux-64) ++ ++# ++# GCC uses -G 0 -mabicalls -fpic as default. We don't want PIC in the kernel ++# code since it only slows down the whole thing. At some point we might make ++# use of global pointer optimizations but their use of $28 conflicts with ++# the current pointer optimization. ++# ++# The DECStation requires an ECOFF kernel for remote booting, other MIPS ++# machines may also. Since BFD is incredibly buggy with respect to ++# crossformat linking we rely on the elf2ecoff tool for format conversion. ++# ++cflags-y += -G 0 -mno-abicalls -fno-pic -pipe ++cflags-y += -msoft-float ++LDFLAGS_vmlinux += -G 0 -static -n -nostdlib ++MODFLAGS += -mlong-calls ++ ++cflags-y += -ffreestanding ++ ++# ++# We explicitly add the endianness specifier if needed, this allows ++# to compile kernels with a toolchain for the other endianness. We ++# carefully avoid to add it redundantly because gcc 3.3/3.4 complains ++# when fed the toolchain default! ++# ++# Certain gcc versions upto gcc 4.1.1 (probably 4.2-subversion as of ++# 2006-10-10 don't properly change the predefined symbols if -EB / -EL ++# are used, so we kludge that here. A bug has been filed at ++# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29413. ++# ++undef-all += -UMIPSEB -U_MIPSEB -U__MIPSEB -U__MIPSEB__ ++undef-all += -UMIPSEL -U_MIPSEL -U__MIPSEL -U__MIPSEL__ ++predef-be += -DMIPSEB -D_MIPSEB -D__MIPSEB -D__MIPSEB__ ++predef-le += -DMIPSEL -D_MIPSEL -D__MIPSEL -D__MIPSEL__ ++cflags-$(CONFIG_CPU_BIG_ENDIAN) += $(shell $(CC) -dumpmachine |grep -q 'mips.*el-.*' && echo -EB $(undef-all) $(predef-be)) ++cflags-$(CONFIG_CPU_LITTLE_ENDIAN) += $(shell $(CC) -dumpmachine |grep -q 'mips.*el-.*' || echo -EL $(undef-all) $(predef-le)) ++ ++cflags-$(CONFIG_CPU_HAS_SMARTMIPS) += $(call cc-option,-msmartmips) ++ ++cflags-$(CONFIG_SB1XXX_CORELIS) += $(call cc-option,-mno-sched-prolog) \ ++ -fno-omit-frame-pointer ++ ++# ++# CPU-dependent compiler/assembler options for optimization. ++# ++cflags-$(CONFIG_CPU_R3000) += -march=r3000 ++cflags-$(CONFIG_CPU_TX39XX) += -march=r3900 ++cflags-$(CONFIG_CPU_R6000) += -march=r6000 -Wa,--trap ++cflags-$(CONFIG_CPU_R4300) += -march=r4300 -Wa,--trap ++cflags-$(CONFIG_CPU_VR41XX) += -march=r4100 -Wa,--trap ++cflags-$(CONFIG_CPU_R4X00) += -march=r4600 -Wa,--trap ++cflags-$(CONFIG_CPU_TX49XX) += -march=r4600 -Wa,--trap ++cflags-$(CONFIG_CPU_LOONGSON2) += -march=r4600 -Wa,--trap ++cflags-$(CONFIG_CPU_MIPS32_R1) += $(call cc-option,-march=mips32,-mips32 -U_MIPS_ISA -D_MIPS_ISA=_MIPS_ISA_MIPS32) \ ++ -Wa,-mips32 -Wa,--trap ++cflags-$(CONFIG_CPU_MIPS32_R2) += $(call cc-option,-march=mips32r2,-mips32r2 -U_MIPS_ISA -D_MIPS_ISA=_MIPS_ISA_MIPS32) \ ++ -Wa,-mips32r2 -Wa,--trap ++cflags-$(CONFIG_CPU_MIPS64_R1) += $(call cc-option,-march=mips64,-mips64 -U_MIPS_ISA -D_MIPS_ISA=_MIPS_ISA_MIPS64) \ ++ -Wa,-mips64 -Wa,--trap ++cflags-$(CONFIG_CPU_MIPS64_R2) += $(call cc-option,-march=mips64r2,-mips64r2 -U_MIPS_ISA -D_MIPS_ISA=_MIPS_ISA_MIPS64) \ ++ -Wa,-mips64r2 -Wa,--trap ++cflags-$(CONFIG_CPU_R5000) += -march=r5000 -Wa,--trap ++cflags-$(CONFIG_CPU_R5432) += $(call cc-option,-march=r5400,-march=r5000) \ ++ -Wa,--trap ++cflags-$(CONFIG_CPU_NEVADA) += $(call cc-option,-march=rm5200,-march=r5000) \ ++ -Wa,--trap ++cflags-$(CONFIG_CPU_RM7000) += $(call cc-option,-march=rm7000,-march=r5000) \ ++ -Wa,--trap ++cflags-$(CONFIG_CPU_RM9000) += $(call cc-option,-march=rm9000,-march=r5000) \ ++ -Wa,--trap ++cflags-$(CONFIG_CPU_SB1) += $(call cc-option,-march=sb1,-march=r5000) \ ++ -Wa,--trap ++cflags-$(CONFIG_CPU_R8000) += -march=r8000 -Wa,--trap ++cflags-$(CONFIG_CPU_R10000) += $(call cc-option,-march=r10000,-march=r8000) \ ++ -Wa,--trap ++ ++ifdef CONFIG_CPU_SB1 ++ifdef CONFIG_SB1_PASS_1_WORKAROUNDS ++MODFLAGS += -msb1-pass1-workarounds ++endif ++endif ++ ++# ++# Firmware support ++# ++libs-$(CONFIG_ARC) += arch/mips/fw/arc/ ++libs-$(CONFIG_CFE) += arch/mips/fw/cfe/ ++libs-$(CONFIG_SIBYTE_CFE) += arch/mips/sibyte/cfe/ ++ ++# ++# Board-dependent options and extra files ++# ++ ++# ++# Acer PICA 61, Mips Magnum 4000 and Olivetti M700. ++# ++core-$(CONFIG_MACH_JAZZ) += arch/mips/jazz/ ++cflags-$(CONFIG_MACH_JAZZ) += -Iinclude/asm-mips/mach-jazz ++load-$(CONFIG_MACH_JAZZ) += 0xffffffff80080000 ++ ++# ++# Common Alchemy Au1x00 stuff ++# ++core-$(CONFIG_SOC_AU1X00) += arch/mips/au1000/common/ ++cflags-$(CONFIG_SOC_AU1X00) += -Iinclude/asm-mips/mach-au1x00 ++ ++# ++# AMD Alchemy Pb1000 eval board ++# ++libs-$(CONFIG_MIPS_PB1000) += arch/mips/au1000/pb1000/ ++cflags-$(CONFIG_MIPS_PB1000) += -Iinclude/asm-mips/mach-pb1x00 ++load-$(CONFIG_MIPS_PB1000) += 0xffffffff80100000 ++ ++# ++# AMD Alchemy Pb1100 eval board ++# ++libs-$(CONFIG_MIPS_PB1100) += arch/mips/au1000/pb1100/ ++cflags-$(CONFIG_MIPS_PB1100) += -Iinclude/asm-mips/mach-pb1x00 ++load-$(CONFIG_MIPS_PB1100) += 0xffffffff80100000 ++ ++# ++# AMD Alchemy Pb1500 eval board ++# ++libs-$(CONFIG_MIPS_PB1500) += arch/mips/au1000/pb1500/ ++cflags-$(CONFIG_MIPS_PB1500) += -Iinclude/asm-mips/mach-pb1x00 ++load-$(CONFIG_MIPS_PB1500) += 0xffffffff80100000 ++ ++# ++# AMD Alchemy Pb1550 eval board ++# ++libs-$(CONFIG_MIPS_PB1550) += arch/mips/au1000/pb1550/ ++cflags-$(CONFIG_MIPS_PB1550) += -Iinclude/asm-mips/mach-pb1x00 ++load-$(CONFIG_MIPS_PB1550) += 0xffffffff80100000 ++ ++# ++# AMD Alchemy Pb1200 eval board ++# ++libs-$(CONFIG_MIPS_PB1200) += arch/mips/au1000/pb1200/ ++cflags-$(CONFIG_MIPS_PB1200) += -Iinclude/asm-mips/mach-pb1x00 ++load-$(CONFIG_MIPS_PB1200) += 0xffffffff80100000 ++ ++# ++# AMD Alchemy Db1000 eval board ++# ++libs-$(CONFIG_MIPS_DB1000) += arch/mips/au1000/db1x00/ ++cflags-$(CONFIG_MIPS_DB1000) += -Iinclude/asm-mips/mach-db1x00 ++load-$(CONFIG_MIPS_DB1000) += 0xffffffff80100000 ++ ++# ++# AMD Alchemy Db1100 eval board ++# ++libs-$(CONFIG_MIPS_DB1100) += arch/mips/au1000/db1x00/ ++cflags-$(CONFIG_MIPS_DB1100) += -Iinclude/asm-mips/mach-db1x00 ++load-$(CONFIG_MIPS_DB1100) += 0xffffffff80100000 ++ ++# ++# AMD Alchemy Db1500 eval board ++# ++libs-$(CONFIG_MIPS_DB1500) += arch/mips/au1000/db1x00/ ++cflags-$(CONFIG_MIPS_DB1500) += -Iinclude/asm-mips/mach-db1x00 ++load-$(CONFIG_MIPS_DB1500) += 0xffffffff80100000 ++ ++# ++# AMD Alchemy Db1550 eval board ++# ++libs-$(CONFIG_MIPS_DB1550) += arch/mips/au1000/db1x00/ ++cflags-$(CONFIG_MIPS_DB1550) += -Iinclude/asm-mips/mach-db1x00 ++load-$(CONFIG_MIPS_DB1550) += 0xffffffff80100000 ++ ++# ++# AMD Alchemy Db1200 eval board ++# ++libs-$(CONFIG_MIPS_DB1200) += arch/mips/au1000/pb1200/ ++cflags-$(CONFIG_MIPS_DB1200) += -Iinclude/asm-mips/mach-db1x00 ++load-$(CONFIG_MIPS_DB1200) += 0xffffffff80100000 ++ ++# ++# AMD Alchemy Bosporus eval board ++# ++libs-$(CONFIG_MIPS_BOSPORUS) += arch/mips/au1000/db1x00/ ++cflags-$(CONFIG_MIPS_BOSPORUS) += -Iinclude/asm-mips/mach-db1x00 ++load-$(CONFIG_MIPS_BOSPORUS) += 0xffffffff80100000 ++ ++# ++# AMD Alchemy Mirage eval board ++# ++libs-$(CONFIG_MIPS_MIRAGE) += arch/mips/au1000/db1x00/ ++cflags-$(CONFIG_MIPS_MIRAGE) += -Iinclude/asm-mips/mach-db1x00 ++load-$(CONFIG_MIPS_MIRAGE) += 0xffffffff80100000 ++ ++# ++# 4G-Systems eval board ++# ++libs-$(CONFIG_MIPS_MTX1) += arch/mips/au1000/mtx-1/ ++load-$(CONFIG_MIPS_MTX1) += 0xffffffff80100000 ++ ++# ++# MyCable eval board ++# ++libs-$(CONFIG_MIPS_XXS1500) += arch/mips/au1000/xxs1500/ ++load-$(CONFIG_MIPS_XXS1500) += 0xffffffff80100000 ++ ++# ++# Cobalt Server ++# ++core-$(CONFIG_MIPS_COBALT) += arch/mips/cobalt/ ++cflags-$(CONFIG_MIPS_COBALT) += -Iinclude/asm-mips/mach-cobalt ++load-$(CONFIG_MIPS_COBALT) += 0xffffffff80080000 ++ ++# ++# DECstation family ++# ++core-$(CONFIG_MACH_DECSTATION) += arch/mips/dec/ ++cflags-$(CONFIG_MACH_DECSTATION)+= -Iinclude/asm-mips/mach-dec ++libs-$(CONFIG_MACH_DECSTATION) += arch/mips/dec/prom/ ++load-$(CONFIG_MACH_DECSTATION) += 0xffffffff80040000 ++ ++# ++# Wind River PPMC Board (4KC + GT64120) ++# ++core-$(CONFIG_WR_PPMC) += arch/mips/gt64120/wrppmc/ ++cflags-$(CONFIG_WR_PPMC) += -Iinclude/asm-mips/mach-wrppmc ++load-$(CONFIG_WR_PPMC) += 0xffffffff80100000 ++ ++# ++# lemote fulong mini-PC board ++# ++core-$(CONFIG_LEMOTE_FULONG) +=arch/mips/lemote/lm2e/ ++load-$(CONFIG_LEMOTE_FULONG) +=0xffffffff80100000 ++cflags-$(CONFIG_LEMOTE_FULONG) += -Iinclude/asm-mips/mach-lemote ++ ++# ++# For all MIPS, Inc. eval boards ++# ++core-$(CONFIG_MIPS_BOARDS_GEN) += arch/mips/mips-boards/generic/ ++ ++# ++# MIPS Atlas board ++# ++core-$(CONFIG_MIPS_ATLAS) += arch/mips/mips-boards/atlas/ ++cflags-$(CONFIG_MIPS_ATLAS) += -Iinclude/asm-mips/mach-atlas ++cflags-$(CONFIG_MIPS_ATLAS) += -Iinclude/asm-mips/mach-mips ++load-$(CONFIG_MIPS_ATLAS) += 0xffffffff80100000 ++all-$(CONFIG_MIPS_ATLAS) := vmlinux.srec ++ ++# ++# MIPS Malta board ++# ++core-$(CONFIG_MIPS_MALTA) += arch/mips/mips-boards/malta/ ++cflags-$(CONFIG_MIPS_MALTA) += -Iinclude/asm-mips/mach-mips ++load-$(CONFIG_MIPS_MALTA) += 0xffffffff80100000 ++all-$(CONFIG_MIPS_MALTA) := vmlinux.srec ++ ++# ++# MIPS SEAD board ++# ++core-$(CONFIG_MIPS_SEAD) += arch/mips/mips-boards/sead/ ++cflags-$(CONFIG_MIPS_SEAD) += -Iinclude/asm-mips/mach-mips ++load-$(CONFIG_MIPS_SEAD) += 0xffffffff80100000 ++all-$(CONFIG_MIPS_SEAD) := vmlinux.srec ++ ++# ++# MIPS SIM ++# ++core-$(CONFIG_MIPS_SIM) += arch/mips/mipssim/ ++cflags-$(CONFIG_MIPS_SIM) += -Iinclude/asm-mips/mach-mipssim ++load-$(CONFIG_MIPS_SIM) += 0x80100000 ++ ++# ++# PMC-Sierra MSP SOCs ++# ++core-$(CONFIG_PMC_MSP) += arch/mips/pmc-sierra/msp71xx/ ++cflags-$(CONFIG_PMC_MSP) += -Iinclude/asm-mips/pmc-sierra/msp71xx \ ++ -mno-branch-likely ++load-$(CONFIG_PMC_MSP) += 0xffffffff80100000 ++ ++# ++# PMC-Sierra Yosemite ++# ++core-$(CONFIG_PMC_YOSEMITE) += arch/mips/pmc-sierra/yosemite/ ++cflags-$(CONFIG_PMC_YOSEMITE) += -Iinclude/asm-mips/mach-yosemite ++load-$(CONFIG_PMC_YOSEMITE) += 0xffffffff80100000 ++ ++# ++# Qemu simulating MIPS32 4Kc ++# ++core-$(CONFIG_QEMU) += arch/mips/qemu/ ++cflags-$(CONFIG_QEMU) += -Iinclude/asm-mips/mach-qemu ++load-$(CONFIG_QEMU) += 0xffffffff80010000 ++all-$(CONFIG_QEMU) := vmlinux.bin ++ ++# ++# Basler eXcite ++# ++core-$(CONFIG_BASLER_EXCITE) += arch/mips/basler/excite/ ++cflags-$(CONFIG_BASLER_EXCITE) += -Iinclude/asm-mips/mach-excite ++load-$(CONFIG_BASLER_EXCITE) += 0x80100000 ++ ++# ++# LASAT platforms ++# ++core-$(CONFIG_LASAT) += arch/mips/lasat/ ++cflags-$(CONFIG_LASAT) += -Iinclude/asm-mips/mach-lasat ++load-$(CONFIG_LASAT) += 0xffffffff80000000 ++ ++# ++# Common VR41xx ++# ++core-$(CONFIG_MACH_VR41XX) += arch/mips/vr41xx/common/ ++cflags-$(CONFIG_MACH_VR41XX) += -Iinclude/asm-mips/mach-vr41xx ++ ++# ++# NEC VR4133 ++# ++core-$(CONFIG_NEC_CMBVR4133) += arch/mips/vr41xx/nec-cmbvr4133/ ++load-$(CONFIG_NEC_CMBVR4133) += 0xffffffff80100000 ++ ++# ++# ZAO Networks Capcella (VR4131) ++# ++load-$(CONFIG_ZAO_CAPCELLA) += 0xffffffff80000000 ++ ++# ++# Victor MP-C303/304 (VR4122) ++# ++load-$(CONFIG_VICTOR_MPC30X) += 0xffffffff80001000 ++ ++# ++# IBM WorkPad z50 (VR4121) ++# ++core-$(CONFIG_IBM_WORKPAD) += arch/mips/vr41xx/ibm-workpad/ ++load-$(CONFIG_IBM_WORKPAD) += 0xffffffff80004000 ++ ++# ++# CASIO CASSIPEIA E-55/65 (VR4111) ++# ++core-$(CONFIG_CASIO_E55) += arch/mips/vr41xx/casio-e55/ ++load-$(CONFIG_CASIO_E55) += 0xffffffff80004000 ++ ++# ++# TANBAC VR4131 multichip module(TB0225) and TANBAC VR4131DIMM(TB0229) (VR4131) ++# ++load-$(CONFIG_TANBAC_TB022X) += 0xffffffff80000000 ++ ++# ++# Common Philips PNX8550 ++# ++core-$(CONFIG_SOC_PNX8550) += arch/mips/philips/pnx8550/common/ ++cflags-$(CONFIG_SOC_PNX8550) += -Iinclude/asm-mips/mach-pnx8550 ++ ++# ++# Philips PNX8550 JBS board ++# ++libs-$(CONFIG_PNX8550_JBS) += arch/mips/philips/pnx8550/jbs/ ++#cflags-$(CONFIG_PNX8550_JBS) += -Iinclude/asm-mips/mach-pnx8550 ++load-$(CONFIG_PNX8550_JBS) += 0xffffffff80060000 ++ ++# Philips PNX8550 STB810 board ++# ++libs-$(CONFIG_PNX8550_STB810) += arch/mips/philips/pnx8550/stb810/ ++load-$(CONFIG_PNX8550_STB810) += 0xffffffff80060000 ++ ++# NEC EMMA2RH boards ++# ++core-$(CONFIG_EMMA2RH) += arch/mips/emma2rh/common/ ++cflags-$(CONFIG_EMMA2RH) += -Iinclude/asm-mips/mach-emma2rh ++ ++# NEC EMMA2RH Mark-eins ++core-$(CONFIG_MARKEINS) += arch/mips/emma2rh/markeins/ ++load-$(CONFIG_MARKEINS) += 0xffffffff88100000 ++ ++# ++# SGI IP22 (Indy/Indigo2) ++# ++# Set the load address to >= 0xffffffff88069000 if you want to leave space for ++# symmon, 0xffffffff80002000 for production kernels. Note that the value must ++# be aligned to a multiple of the kernel stack size or the handling of the ++# current variable will break so for 64-bit kernels we have to raise the start ++# address by 8kb. ++# ++core-$(CONFIG_SGI_IP22) += arch/mips/sgi-ip22/ ++cflags-$(CONFIG_SGI_IP22) += -Iinclude/asm-mips/mach-ip22 ++ifdef CONFIG_32BIT ++load-$(CONFIG_SGI_IP22) += 0xffffffff88002000 ++endif ++ifdef CONFIG_64BIT ++load-$(CONFIG_SGI_IP22) += 0xffffffff88004000 ++endif ++ ++# ++# SGI-IP27 (Origin200/2000) ++# ++# Set the load address to >= 0xc000000000300000 if you want to leave space for ++# symmon, 0xc00000000001c000 for production kernels. Note that the value must ++# be 16kb aligned or the handling of the current variable will break. ++# ++ifdef CONFIG_SGI_IP27 ++core-$(CONFIG_SGI_IP27) += arch/mips/sgi-ip27/ ++cflags-$(CONFIG_SGI_IP27) += -Iinclude/asm-mips/mach-ip27 ++ifdef CONFIG_MAPPED_KERNEL ++load-$(CONFIG_SGI_IP27) += 0xc00000004001c000 ++OBJCOPYFLAGS := --change-addresses=0x3fffffff80000000 ++dataoffset-$(CONFIG_SGI_IP27) += 0x01000000 ++else ++load-$(CONFIG_SGI_IP27) += 0xa80000000001c000 ++OBJCOPYFLAGS := --change-addresses=0x57ffffff80000000 ++endif ++endif ++ ++# ++# SGI-IP32 (O2) ++# ++# Set the load address to >= 80069000 if you want to leave space for symmon, ++# 0xffffffff80004000 for production kernels. Note that the value must be aligned to ++# a multiple of the kernel stack size or the handling of the current variable ++# will break. ++# ++core-$(CONFIG_SGI_IP32) += arch/mips/sgi-ip32/ ++cflags-$(CONFIG_SGI_IP32) += -Iinclude/asm-mips/mach-ip32 ++load-$(CONFIG_SGI_IP32) += 0xffffffff80004000 ++ ++# ++# Sibyte SB1250/BCM1480 SOC ++# ++# This is a LIB so that it links at the end, and initcalls are later ++# the sequence; but it is built as an object so that modules don't get ++# removed (as happens, even if they have __initcall/module_init) ++# ++core-$(CONFIG_SIBYTE_BCM112X) += arch/mips/sibyte/sb1250/ ++core-$(CONFIG_SIBYTE_BCM112X) += arch/mips/sibyte/common/ ++cflags-$(CONFIG_SIBYTE_BCM112X) += -Iinclude/asm-mips/mach-sibyte \ ++ -DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1250_112x_ALL ++ ++core-$(CONFIG_SIBYTE_SB1250) += arch/mips/sibyte/sb1250/ ++core-$(CONFIG_SIBYTE_SB1250) += arch/mips/sibyte/common/ ++cflags-$(CONFIG_SIBYTE_SB1250) += -Iinclude/asm-mips/mach-sibyte \ ++ -DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1250_112x_ALL ++ ++core-$(CONFIG_SIBYTE_BCM1x55) += arch/mips/sibyte/bcm1480/ ++core-$(CONFIG_SIBYTE_BCM1x55) += arch/mips/sibyte/common/ ++cflags-$(CONFIG_SIBYTE_BCM1x55) += -Iinclude/asm-mips/mach-sibyte \ ++ -DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1480_ALL ++ ++core-$(CONFIG_SIBYTE_BCM1x80) += arch/mips/sibyte/bcm1480/ ++core-$(CONFIG_SIBYTE_BCM1x80) += arch/mips/sibyte/common/ ++cflags-$(CONFIG_SIBYTE_BCM1x80) += -Iinclude/asm-mips/mach-sibyte \ ++ -DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1480_ALL ++ ++# ++# Sibyte BCM91120x (Carmel) board ++# Sibyte BCM91120C (CRhine) board ++# Sibyte BCM91125C (CRhone) board ++# Sibyte BCM91125E (Rhone) board ++# Sibyte SWARM board ++# Sibyte BCM91x80 (BigSur) board ++# ++libs-$(CONFIG_SIBYTE_CARMEL) += arch/mips/sibyte/swarm/ ++load-$(CONFIG_SIBYTE_CARMEL) := 0xffffffff80100000 ++libs-$(CONFIG_SIBYTE_CRHINE) += arch/mips/sibyte/swarm/ ++load-$(CONFIG_SIBYTE_CRHINE) := 0xffffffff80100000 ++libs-$(CONFIG_SIBYTE_CRHONE) += arch/mips/sibyte/swarm/ ++load-$(CONFIG_SIBYTE_CRHONE) := 0xffffffff80100000 ++libs-$(CONFIG_SIBYTE_RHONE) += arch/mips/sibyte/swarm/ ++load-$(CONFIG_SIBYTE_RHONE) := 0xffffffff80100000 ++libs-$(CONFIG_SIBYTE_SENTOSA) += arch/mips/sibyte/swarm/ ++load-$(CONFIG_SIBYTE_SENTOSA) := 0xffffffff80100000 ++libs-$(CONFIG_SIBYTE_SWARM) += arch/mips/sibyte/swarm/ ++load-$(CONFIG_SIBYTE_SWARM) := 0xffffffff80100000 ++libs-$(CONFIG_SIBYTE_BIGSUR) += arch/mips/sibyte/swarm/ ++load-$(CONFIG_SIBYTE_BIGSUR) := 0xffffffff80100000 ++ ++# ++# Broadcom BCM47XX boards ++# ++core-$(CONFIG_BCM47XX) += arch/mips/bcm47xx/ ++cflags-$(CONFIG_BCM47XX) += -Iinclude/asm-mips/mach-bcm47xx ++load-$(CONFIG_BCM47XX) := 0xffffffff80001000 ++ ++# ++# SNI RM ++# ++core-$(CONFIG_SNI_RM) += arch/mips/sni/ ++cflags-$(CONFIG_SNI_RM) += -Iinclude/asm-mips/mach-rm ++load-$(CONFIG_SNI_RM) += 0xffffffff80600000 ++all-$(CONFIG_SNI_RM) := vmlinux.ecoff ++ ++# ++# Toshiba JMR-TX3927 board ++# ++core-$(CONFIG_TOSHIBA_JMR3927) += arch/mips/jmr3927/rbhma3100/ \ ++ arch/mips/jmr3927/common/ ++cflags-$(CONFIG_TOSHIBA_JMR3927) += -Iinclude/asm-mips/mach-jmr3927 ++load-$(CONFIG_TOSHIBA_JMR3927) += 0xffffffff80050000 ++ ++# ++# Toshiba RBTX4927 board or ++# Toshiba RBTX4937 board ++# ++core-$(CONFIG_TOSHIBA_RBTX4927) += arch/mips/tx4927/toshiba_rbtx4927/ ++core-$(CONFIG_TOSHIBA_RBTX4927) += arch/mips/tx4927/common/ ++cflags-$(CONFIG_TOSHIBA_RBTX4927) += -Iinclude/asm-mips/mach-tx49xx ++load-$(CONFIG_TOSHIBA_RBTX4927) += 0xffffffff80020000 ++ ++# ++# Toshiba RBTX4938 board ++# ++core-$(CONFIG_TOSHIBA_RBTX4938) += arch/mips/tx4938/toshiba_rbtx4938/ ++core-$(CONFIG_TOSHIBA_RBTX4938) += arch/mips/tx4938/common/ ++cflags-$(CONFIG_TOSHIBA_RBTX4938) += -Iinclude/asm-mips/mach-tx49xx ++load-$(CONFIG_TOSHIBA_RBTX4938) += 0xffffffff80100000 ++ ++# temporary until string.h is fixed ++cflags-y += -ffreestanding ++ ++cflags-y += -Iinclude/asm-mips/mach-generic ++drivers-$(CONFIG_PCI) += arch/mips/pci/ ++ ++ifdef CONFIG_32BIT ++ifdef CONFIG_CPU_LITTLE_ENDIAN ++JIFFIES = jiffies_64 ++else ++JIFFIES = jiffies_64 + 4 ++endif ++else ++JIFFIES = jiffies_64 ++endif ++ ++# ++# Automatically detect the build format. By default we choose ++# the elf format according to the load address. ++# We can always force a build with a 64-bits symbol format by ++# passing 'KBUILD_SYM32=no' option to the make's command line. ++# ++ifdef CONFIG_64BIT ++ ifndef KBUILD_SYM32 ++ ifeq ($(shell expr $(load-y) \< 0xffffffff80000000), 0) ++ KBUILD_SYM32 = y ++ endif ++ endif ++ ++ ifeq ($(KBUILD_SYM32), y) ++ ifeq ($(call cc-option-yn,-msym32), y) ++ cflags-y += -msym32 -DKBUILD_64BIT_SYM32 ++ endif ++ endif ++endif ++ ++KBUILD_AFLAGS += $(cflags-y) ++KBUILD_CFLAGS += $(cflags-y) \ ++ -D"VMLINUX_LOAD_ADDRESS=$(load-y)" ++ ++LDFLAGS += -m $(ld-emul) ++ ++ifdef CONFIG_MIPS ++CHECKFLAGS += $(shell $(CC) $(KBUILD_CFLAGS) -dM -E -xc /dev/null | \ ++ egrep -vw '__GNUC_(|MINOR_|PATCHLEVEL_)_' | \ ++ sed -e 's/^\#define /-D/' -e "s/ /='/" -e "s/$$/'/") ++ifdef CONFIG_64BIT ++CHECKFLAGS += -m64 ++endif ++endif ++ ++OBJCOPYFLAGS += --remove-section=.reginfo ++ ++# ++# Choosing incompatible machines durings configuration will result in ++# error messages during linking. Select a default linkscript if ++# none has been choosen above. ++# ++ ++CPPFLAGS_vmlinux.lds := \ ++ $(KBUILD_CFLAGS) \ ++ -D"LOADADDR=$(load-y)" \ ++ -D"JIFFIES=$(JIFFIES)" \ ++ -D"DATAOFFSET=$(if $(dataoffset-y),$(dataoffset-y),0)" ++ ++head-y := arch/mips/kernel/head.o arch/mips/kernel/init_task.o ++ ++libs-y += arch/mips/lib/ ++ ++core-y += arch/mips/kernel/ arch/mips/mm/ arch/mips/math-emu/ ++ ++drivers-$(CONFIG_OPROFILE) += arch/mips/oprofile/ ++ ++ifdef CONFIG_LASAT ++rom.bin rom.sw: vmlinux ++ $(Q)$(MAKE) $(build)=arch/mips/lasat/image $@ ++endif ++ ++# ++# Some machines like the Indy need 32-bit ELF binaries for booting purposes. ++# Other need ECOFF, so we build a 32-bit ELF binary for them which we then ++# convert to ECOFF using elf2ecoff. ++# ++vmlinux.32: vmlinux ++ $(OBJCOPY) -O $(32bit-bfd) $(OBJCOPYFLAGS) $< $@ ++ ++# ++# The 64-bit ELF tools are pretty broken so at this time we generate 64-bit ++# ELF files from 32-bit files by conversion. ++# ++vmlinux.64: vmlinux ++ $(OBJCOPY) -O $(64bit-bfd) $(OBJCOPYFLAGS) $< $@ ++ ++makeboot =$(Q)$(MAKE) $(build)=arch/mips/boot VMLINUX=$(vmlinux-32) $(1) ++ ++all: $(all-y) ++ ++vmlinux.bin: $(vmlinux-32) ++ +@$(call makeboot,$@) ++ ++vmlinux.ecoff: $(vmlinux-32) ++ +@$(call makeboot,$@) ++ ++vmlinux.srec: $(vmlinux-32) ++ +@$(call makeboot,$@) ++ ++CLEAN_FILES += vmlinux.ecoff \ ++ vmlinux.srec ++ ++archprepare: ++ifdef CONFIG_MIPS32_N32 ++ @echo ' Checking missing-syscalls for N32' ++ $(Q)$(MAKE) $(build)=. missing-syscalls EXTRA_CFLAGS="-mabi=n32" ++endif ++ifdef CONFIG_MIPS32_O32 ++ @echo ' Checking missing-syscalls for O32' ++ $(Q)$(MAKE) $(build)=. missing-syscalls EXTRA_CFLAGS="-mabi=32" ++endif ++ ++archclean: ++ @$(MAKE) $(clean)=arch/mips/boot ++ @$(MAKE) $(clean)=arch/mips/lasat ++ ++define archhelp ++ echo ' vmlinux.ecoff - ECOFF boot image' ++ echo ' vmlinux.bin - Raw binary boot image' ++ echo ' vmlinux.srec - SREC boot image' ++ echo ++ echo ' These will be default as apropriate for a configured platform.' ++endef ++ ++CLEAN_FILES += vmlinux.32 \ ++ vmlinux.64 \ ++ vmlinux.ecoff +diff -urN linux-2.6.24.7.old/arch/mips/boot/Makefile linux-2.6.24.7/arch/mips/boot/Makefile +--- linux-2.6.24.7.old/arch/mips/boot/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/arch/mips/boot/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -7,6 +7,9 @@ + # Copyright (C) 2004 Maciej W. Rozycki + # + ++# This one must match the LOADADDR in arch/mips/Makefile! ++LOADADDR=0x80010000 ++ + # + # Some DECstations need all possible sections of an ECOFF executable + # +@@ -25,7 +28,7 @@ + + VMLINUX = vmlinux + +-all: vmlinux.ecoff vmlinux.srec addinitrd ++all: vmlinux.ecoff vmlinux.srec addinitrd uImage zImage + + vmlinux.ecoff: $(obj)/elf2ecoff $(VMLINUX) + $(obj)/elf2ecoff $(VMLINUX) vmlinux.ecoff $(E2EFLAGS) +@@ -42,8 +45,24 @@ + $(obj)/addinitrd: $(obj)/addinitrd.c + $(HOSTCC) -o $@ $^ + ++uImage: $(VMLINUX) vmlinux.bin ++ rm -f $(obj)/vmlinux.bin.gz ++ gzip -9 $(obj)/vmlinux.bin ++ mkimage -A mips -O linux -T kernel -C gzip \ ++ -a $(LOADADDR) -e $(shell sh ./$(obj)/tools/entry $(NM) $(VMLINUX) ) \ ++ -n 'Linux-$(KERNELRELEASE)' \ ++ -d $(obj)/vmlinux.bin.gz $(obj)/uImage ++ @echo ' Kernel: arch/mips/boot/$@ is ready' ++ ++zImage: ++ $(Q)$(MAKE) $(build)=$(obj)/compressed loadaddr=$(LOADADDR) $@ ++ @echo ' Kernel: arch/mips/boot/compressed/$@ is ready' ++ + clean-files += addinitrd \ + elf2ecoff \ + vmlinux.bin \ + vmlinux.ecoff \ +- vmlinux.srec ++ vmlinux.srec \ ++ vmlinux.bin.gz \ ++ uImage \ ++ zImage +diff -urN linux-2.6.24.7.old/arch/mips/boot/compressed/Makefile linux-2.6.24.7/arch/mips/boot/compressed/Makefile +--- linux-2.6.24.7.old/arch/mips/boot/compressed/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/boot/compressed/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,42 @@ ++# ++# linux/arch/mips/boot/compressed/Makefile ++# ++# create a compressed zImage from the original vmlinux ++# ++ ++targets := zImage vmlinuz vmlinux.bin.gz head.o misc.o piggy.o dummy.o ++ ++OBJS := $(obj)/head.o $(obj)/misc.o ++ ++LD_ARGS := -T $(obj)/ld.script -Ttext 0x80600000 -Bstatic ++OBJCOPY_ARGS := -O elf32-tradlittlemips ++ ++ENTRY := $(obj)/../tools/entry ++FILESIZE := $(obj)/../tools/filesize ++ ++drop-sections = .reginfo .mdebug .comment .note .pdr .options .MIPS.options ++strip-flags = $(addprefix --remove-section=,$(drop-sections)) ++ ++ ++$(obj)/vmlinux.bin.gz: vmlinux ++ rm -f $(obj)/vmlinux.bin.gz ++ $(OBJCOPY) -O binary $(strip-flags) vmlinux $(obj)/vmlinux.bin ++ gzip -v9f $(obj)/vmlinux.bin ++ ++$(obj)/head.o: $(obj)/head.S $(obj)/vmlinux.bin.gz vmlinux ++ $(CC) $(KBUILD_AFLAGS) \ ++ -DIMAGESIZE=$(shell sh $(FILESIZE) $(obj)/vmlinux.bin.gz) \ ++ -DKERNEL_ENTRY=$(shell sh $(ENTRY) $(NM) vmlinux ) \ ++ -DLOADADDR=$(loadaddr) \ ++ -c -o $(obj)/head.o $< ++ ++$(obj)/vmlinuz: $(OBJS) $(obj)/ld.script $(obj)/vmlinux.bin.gz $(obj)/dummy.o ++ $(OBJCOPY) \ ++ --add-section=.image=$(obj)/vmlinux.bin.gz \ ++ --set-section-flags=.image=contents,alloc,load,readonly,data \ ++ $(obj)/dummy.o $(obj)/piggy.o ++ $(LD) $(LD_ARGS) -o $@ $(OBJS) $(obj)/piggy.o ++ $(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab -R .stabstr -R .initrd -R .sysmap ++ ++zImage: $(obj)/vmlinuz ++ $(OBJCOPY) -O binary $(obj)/vmlinuz $(obj)/zImage +diff -urN linux-2.6.24.7.old/arch/mips/boot/compressed/dummy.c linux-2.6.24.7/arch/mips/boot/compressed/dummy.c +--- linux-2.6.24.7.old/arch/mips/boot/compressed/dummy.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/boot/compressed/dummy.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,4 @@ ++int main(void) ++{ ++ return 0; ++} +diff -urN linux-2.6.24.7.old/arch/mips/boot/compressed/head.S linux-2.6.24.7/arch/mips/boot/compressed/head.S +--- linux-2.6.24.7.old/arch/mips/boot/compressed/head.S 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/boot/compressed/head.S 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,85 @@ ++/* ++ * linux/arch/mips/boot/compressed/head.S ++ * ++ * Copyright (C) 2005-2008 Ingenic Semiconductor Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define IndexInvalidate_I 0x00 ++#define IndexWriteBack_D 0x01 ++ ++ .set noreorder ++ LEAF(startup) ++startup: ++ move s0, a0 /* Save the boot loader transfered args */ ++ move s1, a1 ++ move s2, a2 ++ move s3, a3 ++ ++ la a0, _edata ++ la a1, _end ++1: sw zero, 0(a0) /* Clear BSS section */ ++ bne a1, a0, 1b ++ addu a0, 4 ++ ++ la sp, (.stack + 8192) ++ ++ la a0, __image_begin ++ la a1, IMAGESIZE ++ la a2, LOADADDR ++ la ra, 1f ++ la k0, decompress_kernel ++ jr k0 ++ nop ++1: ++ ++ move a0, s0 ++ move a1, s1 ++ move a2, s2 ++ move a3, s3 ++ li k0, KERNEL_ENTRY ++ jr k0 ++ nop ++2: ++ b 32 ++ END(startup) ++ ++ ++ LEAF(flushcaches) ++ la t0, 1f ++ la t1, 0xa0000000 ++ or t0, t0, t1 ++ jr t0 ++ nop ++1: ++ li k0, 0x80000000 # start address ++ li k1, 0x80004000 # end address (16KB I-Cache) ++ subu k1, 128 ++ ++2: ++ .set mips3 ++ cache IndexWriteBack_D, 0(k0) ++ cache IndexWriteBack_D, 32(k0) ++ cache IndexWriteBack_D, 64(k0) ++ cache IndexWriteBack_D, 96(k0) ++ cache IndexInvalidate_I, 0(k0) ++ cache IndexInvalidate_I, 32(k0) ++ cache IndexInvalidate_I, 64(k0) ++ cache IndexInvalidate_I, 96(k0) ++ .set mips0 ++ ++ bne k0, k1, 2b ++ addu k0, k0, 128 ++ la t0, 3f ++ jr t0 ++ nop ++3: ++ jr ra ++ nop ++ END(flushcaches) ++ ++ .comm .stack,4096*2,4 +diff -urN linux-2.6.24.7.old/arch/mips/boot/compressed/ld.script linux-2.6.24.7/arch/mips/boot/compressed/ld.script +--- linux-2.6.24.7.old/arch/mips/boot/compressed/ld.script 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/boot/compressed/ld.script 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,151 @@ ++OUTPUT_ARCH(mips) ++ENTRY(startup) ++SECTIONS ++{ ++ /* Read-only sections, merged into text segment: */ ++ ++ .init : { *(.init) } =0 ++ .text : ++ { ++ _ftext = . ; ++ *(.text) ++ *(.rodata) ++ *(.rodata1) ++ /* .gnu.warning sections are handled specially by elf32.em. */ ++ *(.gnu.warning) ++ } =0 ++ .kstrtab : { *(.kstrtab) } ++ ++ . = ALIGN(16); /* Exception table */ ++ __start___ex_table = .; ++ __ex_table : { *(__ex_table) } ++ __stop___ex_table = .; ++ ++ __start___dbe_table = .; /* Exception table for data bus errors */ ++ __dbe_table : { *(__dbe_table) } ++ __stop___dbe_table = .; ++ ++ __start___ksymtab = .; /* Kernel symbol table */ ++ __ksymtab : { *(__ksymtab) } ++ __stop___ksymtab = .; ++ ++ _etext = .; ++ ++ . = ALIGN(8192); ++ .data.init_task : { *(.data.init_task) } ++ ++ /* Startup code */ ++ . = ALIGN(4096); ++ __init_begin = .; ++ .text.init : { *(.text.init) } ++ .data.init : { *(.data.init) } ++ . = ALIGN(16); ++ __setup_start = .; ++ .setup.init : { *(.setup.init) } ++ __setup_end = .; ++ __initcall_start = .; ++ .initcall.init : { *(.initcall.init) } ++ __initcall_end = .; ++ . = ALIGN(4096); /* Align double page for init_task_union */ ++ __init_end = .; ++ ++ . = ALIGN(4096); ++ .data.page_aligned : { *(.data.idt) } ++ ++ . = ALIGN(32); ++ .data.cacheline_aligned : { *(.data.cacheline_aligned) } ++ ++ .fini : { *(.fini) } =0 ++ .reginfo : { *(.reginfo) } ++ /* Adjust the address for the data segment. We want to adjust up to ++ the same address within the page on the next page up. It would ++ be more correct to do this: ++ . = .; ++ The current expression does not correctly handle the case of a ++ text segment ending precisely at the end of a page; it causes the ++ data segment to skip a page. The above expression does not have ++ this problem, but it will currently (2/95) cause BFD to allocate ++ a single segment, combining both text and data, for this case. ++ This will prevent the text segment from being shared among ++ multiple executions of the program; I think that is more ++ important than losing a page of the virtual address space (note ++ that no actual memory is lost; the page which is skipped can not ++ be referenced). */ ++ . = .; ++ .data : ++ { ++ _fdata = . ; ++ *(.data) ++ ++ /* Put the compressed image here, so bss is on the end. */ ++ __image_begin = .; ++ *(.image) ++ __image_end = .; ++ /* Align the initial ramdisk image (INITRD) on page boundaries. */ ++ . = ALIGN(4096); ++ __ramdisk_begin = .; ++ *(.initrd) ++ __ramdisk_end = .; ++ . = ALIGN(4096); ++ ++ CONSTRUCTORS ++ } ++ .data1 : { *(.data1) } ++ _gp = . + 0x8000; ++ .lit8 : { *(.lit8) } ++ .lit4 : { *(.lit4) } ++ .ctors : { *(.ctors) } ++ .dtors : { *(.dtors) } ++ .got : { *(.got.plt) *(.got) } ++ .dynamic : { *(.dynamic) } ++ /* We want the small data sections together, so single-instruction offsets ++ can access them all, and initialized data all before uninitialized, so ++ we can shorten the on-disk segment size. */ ++ .sdata : { *(.sdata) } ++ . = ALIGN(4); ++ _edata = .; ++ PROVIDE (edata = .); ++ ++ __bss_start = .; ++ _fbss = .; ++ .sbss : { *(.sbss) *(.scommon) } ++ .bss : ++ { ++ *(.dynbss) ++ *(.bss) ++ *(COMMON) ++ . = ALIGN(4); ++ _end = . ; ++ PROVIDE (end = .); ++ } ++ ++ /* Sections to be discarded */ ++ /DISCARD/ : ++ { ++ *(.text.exit) ++ *(.data.exit) ++ *(.exitcall.exit) ++ } ++ ++ /* This is the MIPS specific mdebug section. */ ++ .mdebug : { *(.mdebug) } ++ /* These are needed for ELF backends which have not yet been ++ converted to the new style linker. */ ++ .stab 0 : { *(.stab) } ++ .stabstr 0 : { *(.stabstr) } ++ /* DWARF debug sections. ++ Symbols in the .debug DWARF section are relative to the beginning of the ++ section so we begin .debug at 0. It's not clear yet what needs to happen ++ for the others. */ ++ .debug 0 : { *(.debug) } ++ .debug_srcinfo 0 : { *(.debug_srcinfo) } ++ .debug_aranges 0 : { *(.debug_aranges) } ++ .debug_pubnames 0 : { *(.debug_pubnames) } ++ .debug_sfnames 0 : { *(.debug_sfnames) } ++ .line 0 : { *(.line) } ++ /* These must appear regardless of . */ ++ .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) } ++ .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) } ++ .comment : { *(.comment) } ++ .note : { *(.note) } ++} +diff -urN linux-2.6.24.7.old/arch/mips/boot/compressed/misc.c linux-2.6.24.7/arch/mips/boot/compressed/misc.c +--- linux-2.6.24.7.old/arch/mips/boot/compressed/misc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/boot/compressed/misc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,242 @@ ++/* ++ * linux/arch/mips/boot/compressed/misc.c ++ * ++ * This is a collection of several routines from gzip-1.0.3 ++ * adapted for Linux. ++ * ++ * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 ++ * ++ * Adapted for JZSOC by Peter Wei, 2008 ++ * ++ */ ++ ++#define size_t int ++#define NULL 0 ++ ++/* ++ * gzip declarations ++ */ ++ ++#define OF(args) args ++#define STATIC static ++ ++#undef memset ++#undef memcpy ++#define memzero(s, n) memset ((s), 0, (n)) ++ ++typedef unsigned char uch; ++typedef unsigned short ush; ++typedef unsigned long ulg; ++ ++#define WSIZE 0x8000 /* Window size must be at least 32k, */ ++ /* and a power of two */ ++ ++static uch *inbuf; /* input buffer */ ++static uch window[WSIZE]; /* Sliding window buffer */ ++ ++static unsigned insize = 0; /* valid bytes in inbuf */ ++static unsigned inptr = 0; /* index of next byte to be processed in inbuf */ ++static unsigned outcnt = 0; /* bytes in output buffer */ ++ ++/* gzip flag byte */ ++#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ ++#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ ++#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ ++#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ ++#define COMMENT 0x10 /* bit 4 set: file comment present */ ++#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ ++#define RESERVED 0xC0 /* bit 6,7: reserved */ ++ ++#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) ++ ++/* Diagnostic functions */ ++#ifdef DEBUG ++# define Assert(cond,msg) {if(!(cond)) error(msg);} ++# define Trace(x) fprintf x ++# define Tracev(x) {if (verbose) fprintf x ;} ++# define Tracevv(x) {if (verbose>1) fprintf x ;} ++# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} ++# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} ++#else ++# define Assert(cond,msg) ++# define Trace(x) ++# define Tracev(x) ++# define Tracevv(x) ++# define Tracec(c,x) ++# define Tracecv(c,x) ++#endif ++ ++static int fill_inbuf(void); ++static void flush_window(void); ++static void error(char *m); ++static void gzip_mark(void **); ++static void gzip_release(void **); ++ ++void* memset(void* s, int c, size_t n); ++void* memcpy(void* __dest, __const void* __src, size_t __n); ++ ++extern void flushcaches(void); /* defined in head.S */ ++ ++char *input_data; ++int input_len; ++ ++static long bytes_out = 0; ++static uch *output_data; ++static unsigned long output_ptr = 0; ++ ++ ++static void *malloc(int size); ++static void free(void *where); ++static void error(char *m); ++static void gzip_mark(void **); ++static void gzip_release(void **); ++ ++static void puts(const char *str) ++{ ++} ++ ++extern unsigned char _end[]; ++static unsigned long free_mem_ptr; ++static unsigned long free_mem_end_ptr; ++ ++#define HEAP_SIZE 0x10000 ++ ++#include "../../../../lib/inflate.c" ++ ++static void *malloc(int size) ++{ ++ void *p; ++ ++ if (size <0) error("Malloc error\n"); ++ if (free_mem_ptr == 0) error("Memory error\n"); ++ ++ free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ ++ ++ p = (void *)free_mem_ptr; ++ free_mem_ptr += size; ++ ++ if (free_mem_ptr >= free_mem_end_ptr) ++ error("\nOut of memory\n"); ++ ++ return p; ++} ++ ++static void free(void *where) ++{ /* Don't care */ ++} ++ ++static void gzip_mark(void **ptr) ++{ ++ *ptr = (void *) free_mem_ptr; ++} ++ ++static void gzip_release(void **ptr) ++{ ++ free_mem_ptr = (long) *ptr; ++} ++ ++void* memset(void* s, int c, size_t n) ++{ ++ int i; ++ char *ss = (char*)s; ++ ++ for (i=0;i> 3; i > 0; i--) { ++ *d++ = *s++; ++ *d++ = *s++; ++ *d++ = *s++; ++ *d++ = *s++; ++ *d++ = *s++; ++ *d++ = *s++; ++ *d++ = *s++; ++ *d++ = *s++; ++ } ++ ++ if (__n & 1 << 2) { ++ *d++ = *s++; ++ *d++ = *s++; ++ *d++ = *s++; ++ *d++ = *s++; ++ } ++ ++ if (__n & 1 << 1) { ++ *d++ = *s++; ++ *d++ = *s++; ++ } ++ ++ if (__n & 1) ++ *d++ = *s++; ++ ++ return __dest; ++} ++ ++/* =========================================================================== ++ * Fill the input buffer. This is called only when the buffer is empty ++ * and at least one byte is really needed. ++ */ ++static int fill_inbuf(void) ++{ ++ if (insize != 0) { ++ error("ran out of input data\n"); ++ } ++ ++ inbuf = input_data; ++ insize = input_len; ++ inptr = 1; ++ return inbuf[0]; ++} ++ ++/* =========================================================================== ++ * Write the output window window[0..outcnt-1] and update crc and bytes_out. ++ * (Used for the decompressed data only.) ++ */ ++static void flush_window(void) ++{ ++ ulg c = crc; /* temporary variable */ ++ unsigned n; ++ uch *in, *out, ch; ++ ++ in = window; ++ out = &output_data[output_ptr]; ++ for (n = 0; n < outcnt; n++) { ++ ch = *out++ = *in++; ++ c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); ++ } ++ crc = c; ++ bytes_out += (ulg)outcnt; ++ output_ptr += (ulg)outcnt; ++ outcnt = 0; ++} ++ ++static void error(char *x) ++{ ++ puts("\n\n"); ++ puts(x); ++ puts("\n\n -- System halted"); ++ ++ while(1); /* Halt */ ++} ++ ++void decompress_kernel(unsigned int imageaddr, unsigned int imagesize, unsigned int loadaddr) ++{ ++ input_data = (char *)imageaddr; ++ input_len = imagesize; ++ output_ptr = 0; ++ output_data = (uch *)loadaddr; ++ free_mem_ptr = (unsigned long)_end; ++ free_mem_end_ptr = free_mem_ptr + HEAP_SIZE; ++ ++ makecrc(); ++ puts("Uncompressing Linux..."); ++ gunzip(); ++ flushcaches(); ++ puts("Ok, booting the kernel."); ++} +diff -urN linux-2.6.24.7.old/arch/mips/boot/tools/entry linux-2.6.24.7/arch/mips/boot/tools/entry +--- linux-2.6.24.7.old/arch/mips/boot/tools/entry 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/boot/tools/entry 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,12 @@ ++#!/bin/sh ++ ++# grab the kernel_entry address from the vmlinux elf image ++entry=`$1 $2 | grep kernel_entry` ++ ++fs=`echo $entry | grep ffffffff` # check toolchain output ++ ++if [ -n "$fs" ]; then ++ echo "0x"`$1 $2 | grep kernel_entry | cut -c9- | awk '{print $1}'` ++else ++ echo "0x"`$1 $2 | grep kernel_entry | cut -c1- | awk '{print $1}'` ++fi +diff -urN linux-2.6.24.7.old/arch/mips/boot/tools/filesize linux-2.6.24.7/arch/mips/boot/tools/filesize +--- linux-2.6.24.7.old/arch/mips/boot/tools/filesize 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/boot/tools/filesize 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,7 @@ ++#!/bin/sh ++HOSTNAME=`uname` ++if [ "$HOSTNAME" = "Linux" ]; then ++echo `ls -l $1 | awk '{print $5}'` ++else ++echo `ls -l $1 | awk '{print $6}'` ++fi +diff -urN linux-2.6.24.7.old/arch/mips/configs/apus_defconfig linux-2.6.24.7/arch/mips/configs/apus_defconfig +--- linux-2.6.24.7.old/arch/mips/configs/apus_defconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/configs/apus_defconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1205 @@ ++# ++# Automatically generated make config: don't edit ++# Linux kernel version: 2.6.24.3 ++# Fri Dec 12 17:16:48 2008 ++# ++CONFIG_MIPS=y ++ ++# ++# Machine selection ++# ++# CONFIG_JZ4730_PMP is not set ++# CONFIG_JZ4740_PAVO is not set ++# CONFIG_JZ4740_LEO is not set ++# CONFIG_JZ4740_LYRA is not set ++# CONFIG_JZ4725_DIPPER is not set ++# CONFIG_JZ4720_VIRGO is not set ++# CONFIG_JZ4750_FUWA is not set ++CONFIG_JZ4750_APUS=y ++# CONFIG_MACH_ALCHEMY is not set ++# CONFIG_BASLER_EXCITE is not set ++# CONFIG_BCM47XX is not set ++# CONFIG_MIPS_COBALT is not set ++# CONFIG_MACH_DECSTATION is not set ++# CONFIG_MACH_JAZZ is not set ++# CONFIG_LASAT is not set ++# CONFIG_LEMOTE_FULONG is not set ++# CONFIG_MIPS_ATLAS is not set ++# CONFIG_MIPS_MALTA is not set ++# CONFIG_MIPS_SEAD is not set ++# CONFIG_MIPS_SIM is not set ++# CONFIG_MARKEINS is not set ++# CONFIG_MACH_VR41XX is not set ++# CONFIG_PNX8550_JBS is not set ++# CONFIG_PNX8550_STB810 is not set ++# CONFIG_PMC_MSP is not set ++# CONFIG_PMC_YOSEMITE is not set ++# CONFIG_QEMU is not set ++# CONFIG_SGI_IP22 is not set ++# CONFIG_SGI_IP27 is not set ++# CONFIG_SGI_IP32 is not set ++# CONFIG_SIBYTE_CRHINE is not set ++# CONFIG_SIBYTE_CARMEL is not set ++# CONFIG_SIBYTE_CRHONE is not set ++# CONFIG_SIBYTE_RHONE is not set ++# CONFIG_SIBYTE_SWARM is not set ++# CONFIG_SIBYTE_LITTLESUR is not set ++# CONFIG_SIBYTE_SENTOSA is not set ++# CONFIG_SIBYTE_PTSWARM is not set ++# CONFIG_SIBYTE_BIGSUR is not set ++# CONFIG_SNI_RM is not set ++# CONFIG_TOSHIBA_JMR3927 is not set ++# CONFIG_TOSHIBA_RBTX4927 is not set ++# CONFIG_TOSHIBA_RBTX4938 is not set ++# CONFIG_WR_PPMC is not set ++CONFIG_SOC_JZ4750=y ++CONFIG_JZSOC=y ++CONFIG_JZRISC=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++# CONFIG_ARCH_HAS_ILOG2_U32 is not set ++# CONFIG_ARCH_HAS_ILOG2_U64 is not set ++CONFIG_ARCH_SUPPORTS_OPROFILE=y ++CONFIG_GENERIC_FIND_NEXT_BIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_TIME=y ++CONFIG_GENERIC_CMOS_UPDATE=y ++CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y ++# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set ++CONFIG_DMA_NONCOHERENT=y ++CONFIG_DMA_NEED_PCI_MAP_STATE=y ++# CONFIG_HOTPLUG_CPU is not set ++# CONFIG_NO_IOPORT is not set ++# CONFIG_CPU_BIG_ENDIAN is not set ++CONFIG_CPU_LITTLE_ENDIAN=y ++CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y ++CONFIG_MIPS_L1_CACHE_SHIFT=5 ++ ++# ++# CPU selection ++# ++# CONFIG_CPU_LOONGSON2 is not set ++CONFIG_CPU_MIPS32_R1=y ++# CONFIG_CPU_MIPS32_R2 is not set ++# CONFIG_CPU_MIPS64_R1 is not set ++# CONFIG_CPU_MIPS64_R2 is not set ++# CONFIG_CPU_R3000 is not set ++# CONFIG_CPU_TX39XX is not set ++# CONFIG_CPU_VR41XX is not set ++# CONFIG_CPU_R4300 is not set ++# CONFIG_CPU_R4X00 is not set ++# CONFIG_CPU_TX49XX is not set ++# CONFIG_CPU_R5000 is not set ++# CONFIG_CPU_R5432 is not set ++# CONFIG_CPU_R6000 is not set ++# CONFIG_CPU_NEVADA is not set ++# CONFIG_CPU_R8000 is not set ++# CONFIG_CPU_R10000 is not set ++# CONFIG_CPU_RM7000 is not set ++# CONFIG_CPU_RM9000 is not set ++# CONFIG_CPU_SB1 is not set ++CONFIG_SYS_HAS_CPU_MIPS32_R1=y ++CONFIG_CPU_MIPS32=y ++CONFIG_CPU_MIPSR1=y ++CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y ++CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y ++ ++# ++# Kernel type ++# ++CONFIG_32BIT=y ++# CONFIG_64BIT is not set ++CONFIG_PAGE_SIZE_4KB=y ++# CONFIG_PAGE_SIZE_8KB is not set ++# CONFIG_PAGE_SIZE_16KB is not set ++# CONFIG_PAGE_SIZE_64KB is not set ++CONFIG_CPU_HAS_PREFETCH=y ++CONFIG_MIPS_MT_DISABLED=y ++# CONFIG_MIPS_MT_SMP is not set ++# CONFIG_MIPS_MT_SMTC is not set ++CONFIG_CPU_HAS_LLSC=y ++CONFIG_CPU_HAS_SYNC=y ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_CPU_SUPPORTS_HIGHMEM=y ++CONFIG_ARCH_FLATMEM_ENABLE=y ++CONFIG_ARCH_POPULATES_NODE_MAP=y ++CONFIG_SELECT_MEMORY_MODEL=y ++CONFIG_FLATMEM_MANUAL=y ++# CONFIG_DISCONTIGMEM_MANUAL is not set ++# CONFIG_SPARSEMEM_MANUAL is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++# CONFIG_SPARSEMEM_STATIC is not set ++# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++# CONFIG_RESOURCES_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_TICK_ONESHOT is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_FORCE_MAX_ZONEORDER=12 ++# CONFIG_HZ_48 is not set ++CONFIG_HZ_100=y ++# CONFIG_HZ_128 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_256 is not set ++# CONFIG_HZ_1000 is not set ++# CONFIG_HZ_1024 is not set ++CONFIG_SYS_SUPPORTS_ARBIT_HZ=y ++CONFIG_HZ=100 ++# CONFIG_PREEMPT_NONE is not set ++# CONFIG_PREEMPT_VOLUNTARY is not set ++CONFIG_PREEMPT=y ++CONFIG_PREEMPT_BKL=y ++# CONFIG_KEXEC is not set ++CONFIG_SECCOMP=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++ ++# ++# General setup ++# ++CONFIG_EXPERIMENTAL=y ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_LOCK_KERNEL=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++# CONFIG_USER_NS is not set ++# CONFIG_PID_NS is not set ++# CONFIG_AUDIT is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=14 ++# CONFIG_CGROUPS is not set ++CONFIG_FAIR_GROUP_SCHED=y ++CONFIG_FAIR_USER_SCHED=y ++# CONFIG_FAIR_CGROUP_SCHED is not set ++CONFIG_SYSFS_DEPRECATED=y ++CONFIG_RELAY=y ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_EMBEDDED=y ++CONFIG_SYSCTL_SYSCALL=y ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_EXTRA_PASS is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_ANON_INODES=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLAB=y ++# CONFIG_SLUB is not set ++# CONFIG_SLOB is not set ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++# CONFIG_TINY_SHMEM is not set ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++CONFIG_KMOD=y ++CONFIG_BLOCK=y ++# CONFIG_LBD is not set ++# CONFIG_BLK_DEV_IO_TRACE is not set ++# CONFIG_LSF is not set ++# CONFIG_BLK_DEV_BSG is not set ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_AS=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_AS=y ++# CONFIG_DEFAULT_DEADLINE is not set ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="anticipatory" ++ ++# ++# Bus options (PCI, PCMCIA, EISA, ISA, TC) ++# ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++CONFIG_MMU=y ++# CONFIG_PCCARD is not set ++ ++# ++# Executable file formats ++# ++CONFIG_BINFMT_ELF=y ++# CONFIG_BINFMT_MISC is not set ++CONFIG_TRAD_SIGNALS=y ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ_JZ=y ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_TABLE=y ++# CONFIG_CPU_FREQ_DEBUG is not set ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++ ++# ++# Power management options ++# ++CONFIG_PM=y ++CONFIG_PM_LEGACY=y ++# CONFIG_PM_DEBUG is not set ++CONFIG_SUSPEND_UP_POSSIBLE=y ++# CONFIG_SUSPEND is not set ++ ++# ++# Networking ++# ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_MMAP is not set ++CONFIG_UNIX=y ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_FIB_HASH=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++CONFIG_INET_DIAG=m ++CONFIG_INET_TCP_DIAG=m ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NETFILTER is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_ECONET is not set ++# CONFIG_WAN_ROUTER is not set ++# CONFIG_NET_SCHED is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++ ++# ++# Wireless ++# ++# CONFIG_CFG80211 is not set ++CONFIG_WIRELESS_EXT=y ++# CONFIG_MAC80211 is not set ++# CONFIG_IEEE80211 is not set ++# CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++# CONFIG_FW_LOADER is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_DEBUG is not set ++# CONFIG_MTD_CONCAT is not set ++CONFIG_MTD_PARTITIONS=y ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOC2000 is not set ++# CONFIG_MTD_DOC2001 is not set ++# CONFIG_MTD_DOC2001PLUS is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_VERIFY_WRITE is not set ++# CONFIG_MTD_NAND_ECC_SMC is not set ++# CONFIG_MTD_NAND_MUSEUM_IDS is not set ++CONFIG_MTD_NAND_IDS=y ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_MTD_ALAUDA is not set ++CONFIG_MTD_NAND_JZ4750=y ++# CONFIG_MTD_NAND_CS2 is not set ++# CONFIG_MTD_NAND_CS3 is not set ++# CONFIG_MTD_NAND_CS4 is not set ++CONFIG_MTD_NAND_MULTI_PLANE=y ++# CONFIG_MTD_HW_HM_ECC is not set ++# CONFIG_MTD_SW_HM_ECC is not set ++# CONFIG_MTD_HW_RS_ECC is not set ++CONFIG_MTD_HW_BCH_ECC=y ++CONFIG_MTD_HW_BCH_4BIT=y ++# CONFIG_MTD_HW_BCH_8BIT is not set ++CONFIG_MTD_NAND_DMA=y ++# CONFIG_MTD_NAND_DMABUF is not set ++# CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE is not set ++CONFIG_MTD_OOB_COPIES=3 ++CONFIG_MTD_BADBLOCK_FLAG_PAGE=127 ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# UBI - Unsorted block images ++# ++# CONFIG_MTD_UBI is not set ++# CONFIG_MTD_UBI_BLKDEVS is not set ++# CONFIG_PARPORT is not set ++# CONFIG_PNP is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_UB is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++CONFIG_MISC_DEVICES=y ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_TGT is not set ++# CONFIG_SCSI_NETLINK is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++ ++# ++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs ++# ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++CONFIG_SCSI_WAIT_SCAN=m ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++CONFIG_SCSI_LOWLEVEL=y ++# CONFIG_ISCSI_TCP is not set ++# CONFIG_SCSI_DEBUG is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++CONFIG_NETDEVICES=y ++# CONFIG_NETDEVICES_MULTIQUEUE is not set ++# CONFIG_DUMMY is not set ++# CONFIG_BONDING is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++CONFIG_NET_ETHERNET=y ++CONFIG_MII=y ++CONFIG_JZCS8900=y ++# CONFIG_AX88796 is not set ++# CONFIG_DM9000 is not set ++# CONFIG_IBM_NEW_EMAC_ZMII is not set ++# CONFIG_IBM_NEW_EMAC_RGMII is not set ++# CONFIG_IBM_NEW_EMAC_TAH is not set ++# CONFIG_IBM_NEW_EMAC_EMAC4 is not set ++# CONFIG_B44 is not set ++# CONFIG_NETDEV_1000 is not set ++# CONFIG_NETDEV_10000 is not set ++ ++# ++# Wireless LAN ++# ++# CONFIG_WLAN_PRE80211 is not set ++# CONFIG_WLAN_80211 is not set ++ ++# ++# USB Network Adapters ++# ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_WAN is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++# CONFIG_SHAPER is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_ISDN is not set ++# CONFIG_PHONE is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++# CONFIG_INPUT_EVDEV is not set ++# CONFIG_INPUT_EVBUG is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_JZ is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++# CONFIG_SERIO_I8042 is not set ++CONFIG_SERIO_SERPORT=y ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_VT_CONSOLE=y ++CONFIG_HW_CONSOLE=y ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_SERIAL_NONSTANDARD is not set ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_NR_UARTS=4 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=4 ++# CONFIG_SERIAL_8250_EXTENDED is not set ++ ++# ++# Non-8250 serial port support ++# ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++CONFIG_UNIX98_PTYS=y ++CONFIG_LEGACY_PTYS=y ++CONFIG_LEGACY_PTY_COUNT=2 ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_RTC is not set ++# CONFIG_RTC_PCF8563 is not set ++# CONFIG_RTC_JZ is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++ ++# ++# JZSOC char device support ++# ++CONFIG_JZCHAR=y ++# CONFIG_JZ_CIM is not set ++# CONFIG_JZ_TPANEL_ATA2508 is not set ++CONFIG_JZ_TPANEL=y ++CONFIG_JZ_SADC=y ++# CONFIG_JZ_TPANEL_AK4182 is not set ++# CONFIG_JZ_TPANEL_UCB1400 is not set ++# CONFIG_JZ_TPANEL_WM9712 is not set ++# CONFIG_JZ_UDC_HOTPLUG is not set ++CONFIG_JZ_POWEROFF=y ++# CONFIG_JZ_OW is not set ++CONFIG_JZ_TCSM=y ++# CONFIG_I2C is not set ++ ++# ++# SPI support ++# ++# CONFIG_SPI is not set ++# CONFIG_SPI_MASTER is not set ++# CONFIG_W1 is not set ++# CONFIG_POWER_SUPPLY is not set ++# CONFIG_HWMON is not set ++# CONFIG_WATCHDOG is not set ++ ++# ++# Sonics Silicon Backplane ++# ++CONFIG_SSB_POSSIBLE=y ++# CONFIG_SSB is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_SM501 is not set ++ ++# ++# Multimedia devices ++# ++# CONFIG_VIDEO_DEV is not set ++# CONFIG_DVB_CORE is not set ++# CONFIG_DAB is not set ++ ++# ++# Graphics support ++# ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++# CONFIG_FB_DDC is not set ++CONFIG_FB_CFB_FILLRECT=y ++CONFIG_FB_CFB_COPYAREA=y ++CONFIG_FB_CFB_IMAGEBLIT=y ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_SYS_FOPS is not set ++CONFIG_FB_DEFERRED_IO=y ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++CONFIG_FB_JZSOC=y ++CONFIG_FB_JZ4750_TVE=y ++# CONFIG_IPU_JZ4750 is not set ++CONFIG_FB_JZ4750_LCD=y ++# CONFIG_FB_JZ4750_LCD_USE_2LAYER_FRAMEBUFFER is not set ++# CONFIG_FB_JZ4750_SLCD is not set ++# CONFIG_JZ4750_LCD_SAMSUNG_LTP400WQF01 is not set ++# CONFIG_JZ4750_LCD_SAMSUNG_LTP400WQF02 is not set ++CONFIG_JZ4750_LCD_AUO_A043FL01V2=y ++# CONFIG_JZ4750_LCD_FOXCONN_PT035TN01 is not set ++# CONFIG_JZ4750_LCD_INNOLUX_PT035TN01_SERIAL is not set ++# CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DELTA is not set ++# CONFIG_JZ4750_LCD_TRULY_TFTG320240DTSW_18BIT is not set ++# CONFIG_JZ4750_LCD_TRULY_TFT_GG1P0319LTSW_W is not set ++# CONFIG_JZ4750_SLCD_KGM701A3_TFT_SPFD5420A is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++ ++# ++# Display device support ++# ++# CONFIG_DISPLAY_SUPPORT is not set ++ ++# ++# Console display driver support ++# ++# CONFIG_VGA_CONSOLE is not set ++CONFIG_DUMMY_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_CURSOR_FLASH is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set ++CONFIG_FONTS=y ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++# CONFIG_FONT_6x11 is not set ++# CONFIG_FONT_7x14 is not set ++# CONFIG_FONT_PEARL_8x8 is not set ++# CONFIG_FONT_ACORN_8x8 is not set ++# CONFIG_FONT_MINI_4x6 is not set ++# CONFIG_FONT_SUN8x16 is not set ++# CONFIG_FONT_SUN12x22 is not set ++# CONFIG_FONT_10x18 is not set ++CONFIG_LOGO=y ++CONFIG_LOGO_LINUX_MONO=y ++CONFIG_LOGO_LINUX_VGA16=y ++CONFIG_LOGO_LINUX_CLUT224=y ++ ++# ++# Sound ++# ++CONFIG_SOUND=y ++ ++# ++# Advanced Linux Sound Architecture ++# ++# CONFIG_SND is not set ++ ++# ++# Open Sound System ++# ++CONFIG_SOUND_PRIME=y ++CONFIG_OSS_OBSOLETE=y ++# CONFIG_SOUND_JZ_AC97 is not set ++CONFIG_SOUND_JZ_I2S=y ++# CONFIG_SOUND_JZ_PCM is not set ++# CONFIG_I2S_AK4642EN is not set ++# CONFIG_I2S_ICODEC is not set ++CONFIG_I2S_DLV=y ++# CONFIG_SOUND_MSNDCLAS is not set ++# CONFIG_SOUND_MSNDPIN is not set ++CONFIG_HID_SUPPORT=y ++CONFIG_HID=y ++# CONFIG_HID_DEBUG is not set ++# CONFIG_HIDRAW is not set ++ ++# ++# USB Input Devices ++# ++CONFIG_USB_HID=y ++# CONFIG_USB_HIDINPUT_POWERBOOK is not set ++# CONFIG_HID_FF is not set ++CONFIG_USB_HIDDEV=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB_ARCH_HAS_OHCI=y ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++CONFIG_USB=y ++# CONFIG_USB_DEBUG is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEVICEFS is not set ++CONFIG_USB_DEVICE_CLASS=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_SUSPEND is not set ++# CONFIG_USB_PERSIST is not set ++# CONFIG_USB_OTG is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_ISP116X_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set ++# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++ ++# ++# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' ++# ++ ++# ++# may also be needed; see USB_STORAGE Help for more information ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_DPCM is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_LIBUSUAL is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++CONFIG_USB_MON=y ++ ++# ++# USB port drivers ++# ++ ++# ++# USB Serial Converter support ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_AUERSWALD is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_BERRY_CHARGE is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_PHIDGET is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++ ++# ++# USB DSL modem support ++# ++ ++# ++# USB Gadget Support ++# ++# CONFIG_USB_GADGET is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_UNSAFE_RESUME is not set ++ ++# ++# MMC/SD Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_SDIO_UART is not set ++ ++# ++# MMC/SD Host Controller Drivers ++# ++CONFIG_MSC0_JZ4750=y ++# CONFIG_JZ4750_MSC0_BUS_1 is not set ++CONFIG_JZ4750_MSC0_BUS_4=y ++# CONFIG_JZ4750_MSC0_BUS_8 is not set ++# CONFIG_MSC1_JZ4750 is not set ++# CONFIG_JZ4750_BOOT_FROM_MSC0 is not set ++# CONFIG_NEW_LEDS is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++ ++# ++# Userspace I/O ++# ++# CONFIG_UIO is not set ++ ++# ++# File systems ++# ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++# CONFIG_EXT2_FS_POSIX_ACL is not set ++# CONFIG_EXT2_FS_SECURITY is not set ++# CONFIG_EXT2_FS_XIP is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4DEV_FS is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++CONFIG_FS_POSIX_ACL=y ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++CONFIG_MINIX_FS=y ++# CONFIG_ROMFS_FS is not set ++CONFIG_INOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_QUOTA is not set ++CONFIG_DNOTIFY=y ++# CONFIG_AUTOFS_FS is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_KCORE=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++ ++# ++# Miscellaneous filesystems ++# ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_JFFS2_FS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_NFS_DIRECTIO=y ++# CONFIG_NFSD is not set ++CONFIG_ROOT_NFS=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++CONFIG_SUNRPC_GSS=y ++# CONFIG_SUNRPC_BIND34 is not set ++CONFIG_RPCSEC_GSS_KRB5=y ++# CONFIG_RPCSEC_GSS_SPKM3 is not set ++# CONFIG_SMB_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++CONFIG_NLS_CODEPAGE_936=y ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++# CONFIG_DLM is not set ++ ++# ++# Yaffs2 Filesystems ++# ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set ++# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set ++CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK=y ++CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y ++CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS=10 ++CONFIG_INSTRUMENTATION=y ++# CONFIG_PROFILING is not set ++# CONFIG_MARKERS is not set ++ ++# ++# Kernel hacking ++# ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++# CONFIG_PRINTK_TIME is not set ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_KERNEL is not set ++# CONFIG_SAMPLES is not set ++CONFIG_CMDLINE="" ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITY_FILE_CAPABILITIES is not set ++CONFIG_CRYPTO=y ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_MANAGER=y ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_MD4 is not set ++CONFIG_CRYPTO_MD5=y ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_WP512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_ECB is not set ++CONFIG_CRYPTO_CBC=y ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_XTS is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_AES is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_CRC32C is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_TEST is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_LZO is not set ++CONFIG_CRYPTO_HW=y ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++# CONFIG_CRC_CCITT is not set ++# CONFIG_CRC16 is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++CONFIG_PLIST=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y +diff -urN linux-2.6.24.7.old/arch/mips/configs/dipper_defconfig linux-2.6.24.7/arch/mips/configs/dipper_defconfig +--- linux-2.6.24.7.old/arch/mips/configs/dipper_defconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/configs/dipper_defconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1281 @@ ++# ++# Automatically generated make config: don't edit ++# Linux kernel version: 2.6.24.3 ++# Thu Jun 12 13:55:45 2008 ++# ++CONFIG_MIPS=y ++ ++# ++# Machine selection ++# ++# CONFIG_JZ4730_PMP is not set ++# CONFIG_JZ4740_PAVO is not set ++# CONFIG_JZ4740_LEO is not set ++# CONFIG_JZ4740_LYRA is not set ++CONFIG_JZ4725_DIPPER=y ++# CONFIG_JZ4720_VIRGO is not set ++# CONFIG_JZ4750_FUWA is not set ++# CONFIG_MACH_ALCHEMY is not set ++# CONFIG_BASLER_EXCITE is not set ++# CONFIG_BCM47XX is not set ++# CONFIG_MIPS_COBALT is not set ++# CONFIG_MACH_DECSTATION is not set ++# CONFIG_MACH_JAZZ is not set ++# CONFIG_LASAT is not set ++# CONFIG_LEMOTE_FULONG is not set ++# CONFIG_MIPS_ATLAS is not set ++# CONFIG_MIPS_MALTA is not set ++# CONFIG_MIPS_SEAD is not set ++# CONFIG_MIPS_SIM is not set ++# CONFIG_MARKEINS is not set ++# CONFIG_MACH_VR41XX is not set ++# CONFIG_PNX8550_JBS is not set ++# CONFIG_PNX8550_STB810 is not set ++# CONFIG_PMC_MSP is not set ++# CONFIG_PMC_YOSEMITE is not set ++# CONFIG_QEMU is not set ++# CONFIG_SGI_IP22 is not set ++# CONFIG_SGI_IP27 is not set ++# CONFIG_SGI_IP32 is not set ++# CONFIG_SIBYTE_CRHINE is not set ++# CONFIG_SIBYTE_CARMEL is not set ++# CONFIG_SIBYTE_CRHONE is not set ++# CONFIG_SIBYTE_RHONE is not set ++# CONFIG_SIBYTE_SWARM is not set ++# CONFIG_SIBYTE_LITTLESUR is not set ++# CONFIG_SIBYTE_SENTOSA is not set ++# CONFIG_SIBYTE_PTSWARM is not set ++# CONFIG_SIBYTE_BIGSUR is not set ++# CONFIG_SNI_RM is not set ++# CONFIG_TOSHIBA_JMR3927 is not set ++# CONFIG_TOSHIBA_RBTX4927 is not set ++# CONFIG_TOSHIBA_RBTX4938 is not set ++# CONFIG_WR_PPMC is not set ++CONFIG_SOC_JZ4740=y ++CONFIG_SOC_JZ4725=y ++CONFIG_JZSOC=y ++CONFIG_JZRISC=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++# CONFIG_ARCH_HAS_ILOG2_U32 is not set ++# CONFIG_ARCH_HAS_ILOG2_U64 is not set ++CONFIG_ARCH_SUPPORTS_OPROFILE=y ++CONFIG_GENERIC_FIND_NEXT_BIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_TIME=y ++CONFIG_GENERIC_CMOS_UPDATE=y ++CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y ++# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set ++CONFIG_DMA_NONCOHERENT=y ++CONFIG_DMA_NEED_PCI_MAP_STATE=y ++# CONFIG_HOTPLUG_CPU is not set ++# CONFIG_NO_IOPORT is not set ++# CONFIG_CPU_BIG_ENDIAN is not set ++CONFIG_CPU_LITTLE_ENDIAN=y ++CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y ++CONFIG_MIPS_L1_CACHE_SHIFT=5 ++ ++# ++# CPU selection ++# ++# CONFIG_CPU_LOONGSON2 is not set ++CONFIG_CPU_MIPS32_R1=y ++# CONFIG_CPU_MIPS32_R2 is not set ++# CONFIG_CPU_MIPS64_R1 is not set ++# CONFIG_CPU_MIPS64_R2 is not set ++# CONFIG_CPU_R3000 is not set ++# CONFIG_CPU_TX39XX is not set ++# CONFIG_CPU_VR41XX is not set ++# CONFIG_CPU_R4300 is not set ++# CONFIG_CPU_R4X00 is not set ++# CONFIG_CPU_TX49XX is not set ++# CONFIG_CPU_R5000 is not set ++# CONFIG_CPU_R5432 is not set ++# CONFIG_CPU_R6000 is not set ++# CONFIG_CPU_NEVADA is not set ++# CONFIG_CPU_R8000 is not set ++# CONFIG_CPU_R10000 is not set ++# CONFIG_CPU_RM7000 is not set ++# CONFIG_CPU_RM9000 is not set ++# CONFIG_CPU_SB1 is not set ++CONFIG_SYS_HAS_CPU_MIPS32_R1=y ++CONFIG_CPU_MIPS32=y ++CONFIG_CPU_MIPSR1=y ++CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y ++CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y ++ ++# ++# Kernel type ++# ++CONFIG_32BIT=y ++# CONFIG_64BIT is not set ++CONFIG_PAGE_SIZE_4KB=y ++# CONFIG_PAGE_SIZE_8KB is not set ++# CONFIG_PAGE_SIZE_16KB is not set ++# CONFIG_PAGE_SIZE_64KB is not set ++CONFIG_CPU_HAS_PREFETCH=y ++CONFIG_MIPS_MT_DISABLED=y ++# CONFIG_MIPS_MT_SMP is not set ++# CONFIG_MIPS_MT_SMTC is not set ++CONFIG_CPU_HAS_LLSC=y ++CONFIG_CPU_HAS_SYNC=y ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_CPU_SUPPORTS_HIGHMEM=y ++CONFIG_ARCH_FLATMEM_ENABLE=y ++CONFIG_ARCH_POPULATES_NODE_MAP=y ++CONFIG_SELECT_MEMORY_MODEL=y ++CONFIG_FLATMEM_MANUAL=y ++# CONFIG_DISCONTIGMEM_MANUAL is not set ++# CONFIG_SPARSEMEM_MANUAL is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++# CONFIG_SPARSEMEM_STATIC is not set ++# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++# CONFIG_RESOURCES_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_TICK_ONESHOT is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++# CONFIG_HZ_48 is not set ++CONFIG_HZ_100=y ++# CONFIG_HZ_128 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_256 is not set ++# CONFIG_HZ_1000 is not set ++# CONFIG_HZ_1024 is not set ++CONFIG_SYS_SUPPORTS_ARBIT_HZ=y ++CONFIG_HZ=100 ++# CONFIG_PREEMPT_NONE is not set ++# CONFIG_PREEMPT_VOLUNTARY is not set ++CONFIG_PREEMPT=y ++CONFIG_PREEMPT_BKL=y ++# CONFIG_KEXEC is not set ++CONFIG_SECCOMP=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++ ++# ++# General setup ++# ++CONFIG_EXPERIMENTAL=y ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_LOCK_KERNEL=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++# CONFIG_USER_NS is not set ++# CONFIG_PID_NS is not set ++# CONFIG_AUDIT is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=14 ++# CONFIG_CGROUPS is not set ++CONFIG_FAIR_GROUP_SCHED=y ++CONFIG_FAIR_USER_SCHED=y ++# CONFIG_FAIR_CGROUP_SCHED is not set ++CONFIG_SYSFS_DEPRECATED=y ++CONFIG_RELAY=y ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_EMBEDDED=y ++CONFIG_SYSCTL_SYSCALL=y ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_EXTRA_PASS is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_ANON_INODES=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLAB=y ++# CONFIG_SLUB is not set ++# CONFIG_SLOB is not set ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++# CONFIG_TINY_SHMEM is not set ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++CONFIG_KMOD=y ++CONFIG_BLOCK=y ++# CONFIG_LBD is not set ++# CONFIG_BLK_DEV_IO_TRACE is not set ++# CONFIG_LSF is not set ++# CONFIG_BLK_DEV_BSG is not set ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_AS=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_AS=y ++# CONFIG_DEFAULT_DEADLINE is not set ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="anticipatory" ++ ++# ++# Bus options (PCI, PCMCIA, EISA, ISA, TC) ++# ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++CONFIG_MMU=y ++# CONFIG_PCCARD is not set ++ ++# ++# Executable file formats ++# ++CONFIG_BINFMT_ELF=y ++# CONFIG_BINFMT_MISC is not set ++CONFIG_TRAD_SIGNALS=y ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ_JZ=y ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_TABLE=y ++# CONFIG_CPU_FREQ_DEBUG is not set ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++ ++# ++# Power management options ++# ++CONFIG_PM=y ++CONFIG_PM_LEGACY=y ++# CONFIG_PM_DEBUG is not set ++CONFIG_SUSPEND_UP_POSSIBLE=y ++# CONFIG_SUSPEND is not set ++ ++# ++# Networking ++# ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_MMAP is not set ++CONFIG_UNIX=y ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_FIB_HASH=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++# CONFIG_INET_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NETFILTER is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_ECONET is not set ++# CONFIG_WAN_ROUTER is not set ++# CONFIG_NET_SCHED is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++ ++# ++# Wireless ++# ++# CONFIG_CFG80211 is not set ++CONFIG_WIRELESS_EXT=y ++# CONFIG_MAC80211 is not set ++# CONFIG_IEEE80211 is not set ++# CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++# CONFIG_FW_LOADER is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_DEBUG is not set ++# CONFIG_MTD_CONCAT is not set ++CONFIG_MTD_PARTITIONS=y ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOC2000 is not set ++# CONFIG_MTD_DOC2001 is not set ++# CONFIG_MTD_DOC2001PLUS is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_VERIFY_WRITE is not set ++# CONFIG_MTD_NAND_ECC_SMC is not set ++# CONFIG_MTD_NAND_MUSEUM_IDS is not set ++CONFIG_MTD_NAND_IDS=y ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_MTD_ALAUDA is not set ++CONFIG_MTD_NAND_JZ4740=y ++# CONFIG_MTD_HW_HM_ECC is not set ++# CONFIG_MTD_SW_HM_ECC is not set ++CONFIG_MTD_HW_RS_ECC=y ++# CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE is not set ++CONFIG_MTD_OOB_COPIES=3 ++CONFIG_MTD_BADBLOCK_FLAG_PAGE=0 ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# UBI - Unsorted block images ++# ++CONFIG_MTD_UBI=m ++CONFIG_MTD_UBI_WL_THRESHOLD=256 ++CONFIG_MTD_UBI_BEB_RESERVE=1 ++# CONFIG_MTD_UBI_GLUEBI is not set ++ ++# ++# UBI debugging options ++# ++# CONFIG_MTD_UBI_DEBUG is not set ++CONFIG_MTD_UBI_BLKDEVS=m ++CONFIG_MTD_UBI_BLOCK=m ++# CONFIG_PARPORT is not set ++CONFIG_PNP=y ++# CONFIG_PNP_DEBUG is not set ++ ++# ++# Protocols ++# ++# CONFIG_PNPACPI is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_UB is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=2 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++CONFIG_MISC_DEVICES=y ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_TGT is not set ++# CONFIG_SCSI_NETLINK is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++ ++# ++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs ++# ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++CONFIG_SCSI_WAIT_SCAN=m ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++CONFIG_SCSI_LOWLEVEL=y ++# CONFIG_ISCSI_TCP is not set ++# CONFIG_SCSI_DEBUG is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++CONFIG_NETDEVICES=y ++# CONFIG_NETDEVICES_MULTIQUEUE is not set ++# CONFIG_DUMMY is not set ++# CONFIG_BONDING is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NET_SB1000 is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++CONFIG_NET_ETHERNET=y ++CONFIG_MII=y ++CONFIG_JZCS8900=y ++# CONFIG_AX88796 is not set ++# CONFIG_DM9000 is not set ++# CONFIG_IBM_NEW_EMAC_ZMII is not set ++# CONFIG_IBM_NEW_EMAC_RGMII is not set ++# CONFIG_IBM_NEW_EMAC_TAH is not set ++# CONFIG_IBM_NEW_EMAC_EMAC4 is not set ++# CONFIG_B44 is not set ++# CONFIG_NETDEV_1000 is not set ++# CONFIG_NETDEV_10000 is not set ++ ++# ++# Wireless LAN ++# ++# CONFIG_WLAN_PRE80211 is not set ++# CONFIG_WLAN_80211 is not set ++ ++# ++# USB Network Adapters ++# ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_WAN is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++# CONFIG_SHAPER is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_ISDN is not set ++# CONFIG_PHONE is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++# CONFIG_SERIO_I8042 is not set ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_LIBPS2 is not set ++# CONFIG_SERIO_RAW is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_VT_CONSOLE=y ++CONFIG_HW_CONSOLE=y ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_SERIAL_NONSTANDARD is not set ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_PNP=y ++CONFIG_SERIAL_8250_NR_UARTS=2 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=2 ++# CONFIG_SERIAL_8250_EXTENDED is not set ++ ++# ++# Non-8250 serial port support ++# ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++CONFIG_UNIX98_PTYS=y ++CONFIG_LEGACY_PTYS=y ++CONFIG_LEGACY_PTY_COUNT=2 ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_RTC is not set ++# CONFIG_RTC_PCF8563 is not set ++CONFIG_RTC_JZ=y ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++ ++# ++# JZSOC char device support ++# ++CONFIG_JZCHAR=y ++# CONFIG_JZ_CIM is not set ++# CONFIG_JZ_TPANEL_ATA2508 is not set ++CONFIG_JZ_TPANEL=y ++CONFIG_JZ_SADC=y ++# CONFIG_JZ_TPANEL_AK4182 is not set ++# CONFIG_JZ_TPANEL_UCB1400 is not set ++# CONFIG_JZ_TPANEL_WM9712 is not set ++CONFIG_JZ_UDC_HOTPLUG=y ++CONFIG_JZ_POWEROFF=y ++# CONFIG_JZ_OW is not set ++# CONFIG_I2C is not set ++ ++# ++# SPI support ++# ++# CONFIG_SPI is not set ++# CONFIG_SPI_MASTER is not set ++# CONFIG_W1 is not set ++# CONFIG_POWER_SUPPLY is not set ++# CONFIG_HWMON is not set ++CONFIG_WATCHDOG=y ++# CONFIG_WATCHDOG_NOWAYOUT is not set ++ ++# ++# Watchdog Device Drivers ++# ++CONFIG_JZ_WDT=y ++# CONFIG_SOFT_WATCHDOG is not set ++ ++# ++# USB-based Watchdog Cards ++# ++# CONFIG_USBPCWATCHDOG is not set ++ ++# ++# Sonics Silicon Backplane ++# ++CONFIG_SSB_POSSIBLE=y ++# CONFIG_SSB is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_SM501 is not set ++ ++# ++# Multimedia devices ++# ++CONFIG_VIDEO_DEV=y ++CONFIG_VIDEO_V4L1=y ++CONFIG_VIDEO_V4L1_COMPAT=y ++CONFIG_VIDEO_V4L2=y ++CONFIG_VIDEO_CAPTURE_DRIVERS=y ++# CONFIG_VIDEO_ADV_DEBUG is not set ++CONFIG_VIDEO_HELPER_CHIPS_AUTO=y ++# CONFIG_VIDEO_VIVI is not set ++# CONFIG_VIDEO_CPIA is not set ++# CONFIG_VIDEO_CPIA2 is not set ++CONFIG_VIDEO_JZ_CIM=y ++CONFIG_VIDEO_JZ_SENSOR=y ++CONFIG_V4L_USB_DRIVERS=y ++# CONFIG_USB_VICAM is not set ++# CONFIG_USB_IBMCAM is not set ++# CONFIG_USB_KONICAWC is not set ++# CONFIG_USB_QUICKCAM_MESSENGER is not set ++# CONFIG_USB_ET61X251 is not set ++# CONFIG_USB_OV511 is not set ++# CONFIG_USB_SE401 is not set ++# CONFIG_USB_SN9C102 is not set ++# CONFIG_USB_STV680 is not set ++# CONFIG_USB_ZC0301 is not set ++# CONFIG_USB_PWC is not set ++# CONFIG_USB_ZR364XX is not set ++CONFIG_RADIO_ADAPTERS=y ++# CONFIG_USB_DSBR is not set ++# CONFIG_DVB_CORE is not set ++# CONFIG_DAB is not set ++ ++# ++# Graphics support ++# ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++# CONFIG_FB_DDC is not set ++CONFIG_FB_CFB_FILLRECT=y ++CONFIG_FB_CFB_COPYAREA=y ++CONFIG_FB_CFB_IMAGEBLIT=y ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_SYS_FOPS is not set ++CONFIG_FB_DEFERRED_IO=y ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++CONFIG_FB_JZSOC=y ++# CONFIG_FB_JZ4740_SLCD is not set ++CONFIG_FB_JZLCD_4730_4740=y ++CONFIG_JZLCD_FRAMEBUFFER_MAX=1 ++# CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT is not set ++# CONFIG_JZLCD_SHARP_LQ035Q7 is not set ++# CONFIG_JZLCD_SAMSUNG_LTS350Q1 is not set ++# CONFIG_JZLCD_SAMSUNG_LTV350QVF04 is not set ++# CONFIG_JZLCD_SAMSUNG_LTP400WQF01 is not set ++CONFIG_JZLCD_SAMSUNG_LTP400WQF02=y ++# CONFIG_JZLCD_AUO_A030FL01_V1 is not set ++# CONFIG_JZLCD_TRULY_TFTG320240DTSW is not set ++# CONFIG_JZLCD_TRULY_TFTG320240DTSW_SERIAL is not set ++# CONFIG_JZLCD_TRULY_TFTG240320UTSW_63W_E is not set ++# CONFIG_JZLCD_FOXCONN_PT035TN01 is not set ++# CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL is not set ++# CONFIG_JZLCD_TOSHIBA_LTM084P363 is not set ++# CONFIG_JZLCD_HYNIX_HT10X21 is not set ++# CONFIG_JZLCD_INNOLUX_AT080TN42 is not set ++# CONFIG_JZLCD_CSTN_800x600 is not set ++# CONFIG_JZLCD_CSTN_320x240 is not set ++# CONFIG_JZLCD_MSTN_480x320 is not set ++# CONFIG_JZLCD_MSTN_320x240 is not set ++# CONFIG_JZLCD_MSTN_240x128 is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++ ++# ++# Display device support ++# ++# CONFIG_DISPLAY_SUPPORT is not set ++ ++# ++# Console display driver support ++# ++# CONFIG_VGA_CONSOLE is not set ++CONFIG_DUMMY_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_CURSOR_FLASH is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set ++CONFIG_FONTS=y ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++# CONFIG_FONT_6x11 is not set ++# CONFIG_FONT_7x14 is not set ++# CONFIG_FONT_PEARL_8x8 is not set ++# CONFIG_FONT_ACORN_8x8 is not set ++# CONFIG_FONT_MINI_4x6 is not set ++# CONFIG_FONT_SUN8x16 is not set ++# CONFIG_FONT_SUN12x22 is not set ++# CONFIG_FONT_10x18 is not set ++CONFIG_LOGO=y ++CONFIG_LOGO_LINUX_MONO=y ++CONFIG_LOGO_LINUX_VGA16=y ++CONFIG_LOGO_LINUX_CLUT224=y ++ ++# ++# Sound ++# ++CONFIG_SOUND=y ++ ++# ++# Advanced Linux Sound Architecture ++# ++# CONFIG_SND is not set ++ ++# ++# Open Sound System ++# ++CONFIG_SOUND_PRIME=y ++CONFIG_OSS_OBSOLETE=y ++# CONFIG_SOUND_JZ_AC97 is not set ++CONFIG_SOUND_JZ_I2S=y ++# CONFIG_SOUND_JZ_PCM is not set ++# CONFIG_I2S_AK4642EN is not set ++CONFIG_I2S_ICODEC=y ++# CONFIG_SOUND_MSNDCLAS is not set ++# CONFIG_SOUND_MSNDPIN is not set ++CONFIG_HID_SUPPORT=y ++CONFIG_HID=y ++# CONFIG_HID_DEBUG is not set ++# CONFIG_HIDRAW is not set ++ ++# ++# USB Input Devices ++# ++CONFIG_USB_HID=y ++# CONFIG_USB_HIDINPUT_POWERBOOK is not set ++# CONFIG_HID_FF is not set ++# CONFIG_USB_HIDDEV is not set ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB_ARCH_HAS_OHCI=y ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++CONFIG_USB=y ++# CONFIG_USB_DEBUG is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEVICEFS is not set ++CONFIG_USB_DEVICE_CLASS=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_SUSPEND is not set ++# CONFIG_USB_PERSIST is not set ++# CONFIG_USB_OTG is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_ISP116X_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set ++# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++ ++# ++# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' ++# ++ ++# ++# may also be needed; see USB_STORAGE Help for more information ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_DPCM is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_LIBUSUAL is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++CONFIG_USB_MON=y ++ ++# ++# USB port drivers ++# ++ ++# ++# USB Serial Converter support ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_AUERSWALD is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_BERRY_CHARGE is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_PHIDGET is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++ ++# ++# USB DSL modem support ++# ++ ++# ++# USB Gadget Support ++# ++CONFIG_USB_GADGET=m ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++# CONFIG_USB_GADGET_DEBUG_FS is not set ++CONFIG_USB_GADGET_SELECTED=y ++CONFIG_USB_GADGET_JZ4740=y ++CONFIG_USB_JZ4740=m ++# CONFIG_USB_GADGET_JZ4730 is not set ++# CONFIG_USB_GADGET_AMD5536UDC is not set ++# CONFIG_USB_GADGET_ATMEL_USBA is not set ++# CONFIG_USB_GADGET_FSL_USB2 is not set ++# CONFIG_USB_GADGET_NET2280 is not set ++# CONFIG_USB_GADGET_PXA2XX is not set ++# CONFIG_USB_GADGET_M66592 is not set ++# CONFIG_USB_GADGET_GOKU is not set ++# CONFIG_USB_GADGET_LH7A40X is not set ++# CONFIG_USB_GADGET_OMAP is not set ++# CONFIG_USB_GADGET_S3C2410 is not set ++# CONFIG_USB_GADGET_AT91 is not set ++# CONFIG_USB_GADGET_DUMMY_HCD is not set ++CONFIG_USB_GADGET_DUALSPEED=y ++# CONFIG_USB_ZERO is not set ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_GADGETFS is not set ++CONFIG_USB_FILE_STORAGE=m ++# CONFIG_USB_FILE_STORAGE_TEST is not set ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_MIDI_GADGET is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_UNSAFE_RESUME is not set ++ ++# ++# MMC/SD Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_SDIO_UART is not set ++ ++# ++# MMC/SD Host Controller Drivers ++# ++CONFIG_MMC_JZ=y ++# CONFIG_JZ_MMC_BUS_4 is not set ++CONFIG_JZ_MMC_BUS_1=y ++# CONFIG_NEW_LEDS is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++ ++# ++# Userspace I/O ++# ++# CONFIG_UIO is not set ++ ++# ++# File systems ++# ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++# CONFIG_EXT2_FS_POSIX_ACL is not set ++# CONFIG_EXT2_FS_SECURITY is not set ++# CONFIG_EXT2_FS_XIP is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4DEV_FS is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++CONFIG_FS_POSIX_ACL=y ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++CONFIG_MINIX_FS=y ++# CONFIG_ROMFS_FS is not set ++CONFIG_INOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_QUOTA is not set ++CONFIG_DNOTIFY=y ++# CONFIG_AUTOFS_FS is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_KCORE=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++ ++# ++# Miscellaneous filesystems ++# ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_JFFS2_FS is not set ++CONFIG_UBIFS_FS=m ++# CONFIG_UBIFS_FS_XATTR is not set ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_UBIFS_FS_DEBUG is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_NFS_DIRECTIO=y ++# CONFIG_NFSD is not set ++CONFIG_ROOT_NFS=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++CONFIG_SUNRPC_GSS=y ++# CONFIG_SUNRPC_BIND34 is not set ++CONFIG_RPCSEC_GSS_KRB5=y ++# CONFIG_RPCSEC_GSS_SPKM3 is not set ++# CONFIG_SMB_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++CONFIG_NLS_CODEPAGE_936=y ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++# CONFIG_DLM is not set ++ ++# ++# Yaffs2 Filesystems ++# ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set ++# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set ++CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK=y ++CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y ++CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS=10 ++CONFIG_INSTRUMENTATION=y ++# CONFIG_PROFILING is not set ++# CONFIG_MARKERS is not set ++ ++# ++# Kernel hacking ++# ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++# CONFIG_PRINTK_TIME is not set ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_UNUSED_SYMBOLS is not set ++CONFIG_DEBUG_FS=y ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_KERNEL is not set ++# CONFIG_SAMPLES is not set ++CONFIG_CMDLINE="" ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITY_FILE_CAPABILITIES is not set ++CONFIG_CRYPTO=y ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_MANAGER=y ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_MD4 is not set ++CONFIG_CRYPTO_MD5=y ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_WP512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_ECB is not set ++CONFIG_CRYPTO_CBC=y ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_XTS is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_AES is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_SEED is not set ++CONFIG_CRYPTO_DEFLATE=m ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_CRC32C is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_TEST is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++CONFIG_CRYPTO_LZO=m ++CONFIG_CRYPTO_HW=y ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=m ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++CONFIG_ZLIB_INFLATE=m ++CONFIG_ZLIB_DEFLATE=m ++CONFIG_LZO_COMPRESS=m ++CONFIG_LZO_DECOMPRESS=m ++CONFIG_PLIST=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y +diff -urN linux-2.6.24.7.old/arch/mips/configs/fuwa_defconfig linux-2.6.24.7/arch/mips/configs/fuwa_defconfig +--- linux-2.6.24.7.old/arch/mips/configs/fuwa_defconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/configs/fuwa_defconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,928 @@ ++# ++# Automatically generated make config: don't edit ++# Linux kernel version: 2.6.24.3 ++# Fri Jul 4 19:20:22 2008 ++# ++CONFIG_MIPS=y ++ ++# ++# Machine selection ++# ++# CONFIG_JZ4730_PMP is not set ++# CONFIG_JZ4740_PAVO is not set ++# CONFIG_JZ4740_LEO is not set ++# CONFIG_JZ4740_LYRA is not set ++# CONFIG_JZ4725_DIPPER is not set ++# CONFIG_JZ4720_VIRGO is not set ++CONFIG_JZ4750_FUWA=y ++# CONFIG_MACH_ALCHEMY is not set ++# CONFIG_BASLER_EXCITE is not set ++# CONFIG_BCM47XX is not set ++# CONFIG_MIPS_COBALT is not set ++# CONFIG_MACH_DECSTATION is not set ++# CONFIG_MACH_JAZZ is not set ++# CONFIG_LASAT is not set ++# CONFIG_LEMOTE_FULONG is not set ++# CONFIG_MIPS_ATLAS is not set ++# CONFIG_MIPS_MALTA is not set ++# CONFIG_MIPS_SEAD is not set ++# CONFIG_MIPS_SIM is not set ++# CONFIG_MARKEINS is not set ++# CONFIG_MACH_VR41XX is not set ++# CONFIG_PNX8550_JBS is not set ++# CONFIG_PNX8550_STB810 is not set ++# CONFIG_PMC_MSP is not set ++# CONFIG_PMC_YOSEMITE is not set ++# CONFIG_QEMU is not set ++# CONFIG_SGI_IP22 is not set ++# CONFIG_SGI_IP27 is not set ++# CONFIG_SGI_IP32 is not set ++# CONFIG_SIBYTE_CRHINE is not set ++# CONFIG_SIBYTE_CARMEL is not set ++# CONFIG_SIBYTE_CRHONE is not set ++# CONFIG_SIBYTE_RHONE is not set ++# CONFIG_SIBYTE_SWARM is not set ++# CONFIG_SIBYTE_LITTLESUR is not set ++# CONFIG_SIBYTE_SENTOSA is not set ++# CONFIG_SIBYTE_PTSWARM is not set ++# CONFIG_SIBYTE_BIGSUR is not set ++# CONFIG_SNI_RM is not set ++# CONFIG_TOSHIBA_JMR3927 is not set ++# CONFIG_TOSHIBA_RBTX4927 is not set ++# CONFIG_TOSHIBA_RBTX4938 is not set ++# CONFIG_WR_PPMC is not set ++CONFIG_SOC_JZ4750=y ++CONFIG_JZ_FPGA=y ++CONFIG_JZSOC=y ++CONFIG_JZRISC=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++# CONFIG_ARCH_HAS_ILOG2_U32 is not set ++# CONFIG_ARCH_HAS_ILOG2_U64 is not set ++CONFIG_ARCH_SUPPORTS_OPROFILE=y ++CONFIG_GENERIC_FIND_NEXT_BIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_TIME=y ++CONFIG_GENERIC_CMOS_UPDATE=y ++CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y ++# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set ++CONFIG_DMA_NONCOHERENT=y ++CONFIG_DMA_NEED_PCI_MAP_STATE=y ++# CONFIG_HOTPLUG_CPU is not set ++# CONFIG_NO_IOPORT is not set ++# CONFIG_CPU_BIG_ENDIAN is not set ++CONFIG_CPU_LITTLE_ENDIAN=y ++CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y ++CONFIG_MIPS_L1_CACHE_SHIFT=5 ++ ++# ++# CPU selection ++# ++# CONFIG_CPU_LOONGSON2 is not set ++CONFIG_CPU_MIPS32_R1=y ++# CONFIG_CPU_MIPS32_R2 is not set ++# CONFIG_CPU_MIPS64_R1 is not set ++# CONFIG_CPU_MIPS64_R2 is not set ++# CONFIG_CPU_R3000 is not set ++# CONFIG_CPU_TX39XX is not set ++# CONFIG_CPU_VR41XX is not set ++# CONFIG_CPU_R4300 is not set ++# CONFIG_CPU_R4X00 is not set ++# CONFIG_CPU_TX49XX is not set ++# CONFIG_CPU_R5000 is not set ++# CONFIG_CPU_R5432 is not set ++# CONFIG_CPU_R6000 is not set ++# CONFIG_CPU_NEVADA is not set ++# CONFIG_CPU_R8000 is not set ++# CONFIG_CPU_R10000 is not set ++# CONFIG_CPU_RM7000 is not set ++# CONFIG_CPU_RM9000 is not set ++# CONFIG_CPU_SB1 is not set ++CONFIG_SYS_HAS_CPU_MIPS32_R1=y ++CONFIG_CPU_MIPS32=y ++CONFIG_CPU_MIPSR1=y ++CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y ++CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y ++ ++# ++# Kernel type ++# ++CONFIG_32BIT=y ++# CONFIG_64BIT is not set ++CONFIG_PAGE_SIZE_4KB=y ++# CONFIG_PAGE_SIZE_8KB is not set ++# CONFIG_PAGE_SIZE_16KB is not set ++# CONFIG_PAGE_SIZE_64KB is not set ++CONFIG_CPU_HAS_PREFETCH=y ++CONFIG_MIPS_MT_DISABLED=y ++# CONFIG_MIPS_MT_SMP is not set ++# CONFIG_MIPS_MT_SMTC is not set ++CONFIG_CPU_HAS_LLSC=y ++CONFIG_CPU_HAS_SYNC=y ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_CPU_SUPPORTS_HIGHMEM=y ++CONFIG_ARCH_FLATMEM_ENABLE=y ++CONFIG_ARCH_POPULATES_NODE_MAP=y ++CONFIG_SELECT_MEMORY_MODEL=y ++CONFIG_FLATMEM_MANUAL=y ++# CONFIG_DISCONTIGMEM_MANUAL is not set ++# CONFIG_SPARSEMEM_MANUAL is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++# CONFIG_SPARSEMEM_STATIC is not set ++# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++# CONFIG_RESOURCES_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_TICK_ONESHOT is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++# CONFIG_HZ_48 is not set ++CONFIG_HZ_100=y ++# CONFIG_HZ_128 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_256 is not set ++# CONFIG_HZ_1000 is not set ++# CONFIG_HZ_1024 is not set ++CONFIG_SYS_SUPPORTS_ARBIT_HZ=y ++CONFIG_HZ=100 ++# CONFIG_PREEMPT_NONE is not set ++# CONFIG_PREEMPT_VOLUNTARY is not set ++CONFIG_PREEMPT=y ++CONFIG_PREEMPT_BKL=y ++# CONFIG_KEXEC is not set ++CONFIG_SECCOMP=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++ ++# ++# General setup ++# ++CONFIG_EXPERIMENTAL=y ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_LOCK_KERNEL=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++# CONFIG_USER_NS is not set ++# CONFIG_PID_NS is not set ++# CONFIG_AUDIT is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=14 ++# CONFIG_CGROUPS is not set ++CONFIG_FAIR_GROUP_SCHED=y ++CONFIG_FAIR_USER_SCHED=y ++# CONFIG_FAIR_CGROUP_SCHED is not set ++CONFIG_SYSFS_DEPRECATED=y ++CONFIG_RELAY=y ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_EMBEDDED=y ++CONFIG_SYSCTL_SYSCALL=y ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_EXTRA_PASS is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_ANON_INODES=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLAB=y ++# CONFIG_SLUB is not set ++# CONFIG_SLOB is not set ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++# CONFIG_TINY_SHMEM is not set ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++CONFIG_KMOD=y ++CONFIG_BLOCK=y ++# CONFIG_LBD is not set ++# CONFIG_BLK_DEV_IO_TRACE is not set ++# CONFIG_LSF is not set ++# CONFIG_BLK_DEV_BSG is not set ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_AS=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_AS=y ++# CONFIG_DEFAULT_DEADLINE is not set ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="anticipatory" ++ ++# ++# Bus options (PCI, PCMCIA, EISA, ISA, TC) ++# ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++CONFIG_MMU=y ++# CONFIG_PCCARD is not set ++ ++# ++# Executable file formats ++# ++CONFIG_BINFMT_ELF=y ++# CONFIG_BINFMT_MISC is not set ++CONFIG_TRAD_SIGNALS=y ++ ++# ++# CPU Frequency scaling ++# ++# CONFIG_CPU_FREQ_JZ is not set ++ ++# ++# Power management options ++# ++# CONFIG_PM is not set ++CONFIG_SUSPEND_UP_POSSIBLE=y ++ ++# ++# Networking ++# ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_MMAP is not set ++CONFIG_UNIX=y ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_FIB_HASH=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++CONFIG_INET_DIAG=m ++CONFIG_INET_TCP_DIAG=m ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NETFILTER is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_ECONET is not set ++# CONFIG_WAN_ROUTER is not set ++# CONFIG_NET_SCHED is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++ ++# ++# Wireless ++# ++# CONFIG_CFG80211 is not set ++# CONFIG_WIRELESS_EXT is not set ++# CONFIG_MAC80211 is not set ++# CONFIG_IEEE80211 is not set ++# CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++# CONFIG_FW_LOADER is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_DEBUG is not set ++# CONFIG_MTD_CONCAT is not set ++CONFIG_MTD_PARTITIONS=y ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOC2000 is not set ++# CONFIG_MTD_DOC2001 is not set ++# CONFIG_MTD_DOC2001PLUS is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_VERIFY_WRITE is not set ++# CONFIG_MTD_NAND_ECC_SMC is not set ++# CONFIG_MTD_NAND_MUSEUM_IDS is not set ++CONFIG_MTD_NAND_IDS=y ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++CONFIG_MTD_NAND_JZ4750=y ++# CONFIG_MTD_HW_HM_ECC is not set ++# CONFIG_MTD_SW_HM_ECC is not set ++# CONFIG_MTD_HW_RS_ECC is not set ++CONFIG_MTD_HW_BCH_ECC=y ++CONFIG_MTD_NAND_DMA=y ++# CONFIG_MTD_NAND_NO_DMA is not set ++CONFIG_MTD_HW_BCH_4BIT=y ++# CONFIG_MTD_HW_BCH_8BIT is not set ++# CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE is not set ++CONFIG_MTD_OOB_COPIES=0 ++CONFIG_MTD_BADBLOCK_FLAG_PAGE=127 ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# UBI - Unsorted block images ++# ++# CONFIG_MTD_UBI is not set ++# CONFIG_MTD_UBI_BLKDEVS is not set ++# CONFIG_PARPORT is not set ++# CONFIG_PNP is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++CONFIG_MISC_DEVICES=y ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++# CONFIG_RAID_ATTRS is not set ++# CONFIG_SCSI is not set ++# CONFIG_SCSI_DMA is not set ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++CONFIG_NETDEVICES=y ++# CONFIG_NETDEVICES_MULTIQUEUE is not set ++# CONFIG_DUMMY is not set ++# CONFIG_BONDING is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++CONFIG_NET_ETHERNET=y ++CONFIG_MII=y ++CONFIG_JZ_ETH=y ++# CONFIG_AX88796 is not set ++# CONFIG_DM9000 is not set ++# CONFIG_IBM_NEW_EMAC_ZMII is not set ++# CONFIG_IBM_NEW_EMAC_RGMII is not set ++# CONFIG_IBM_NEW_EMAC_TAH is not set ++# CONFIG_IBM_NEW_EMAC_EMAC4 is not set ++# CONFIG_B44 is not set ++CONFIG_NETDEV_1000=y ++CONFIG_NETDEV_10000=y ++ ++# ++# Wireless LAN ++# ++# CONFIG_WLAN_PRE80211 is not set ++# CONFIG_WLAN_80211 is not set ++# CONFIG_WAN is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++# CONFIG_SHAPER is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_ISDN is not set ++# CONFIG_PHONE is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++ ++# ++# Userland interfaces ++# ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++# CONFIG_INPUT_EVDEV is not set ++# CONFIG_INPUT_EVBUG is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++# CONFIG_SERIO_I8042 is not set ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_LIBPS2 is not set ++# CONFIG_SERIO_RAW is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_VT_CONSOLE=y ++CONFIG_HW_CONSOLE=y ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_SERIAL_NONSTANDARD is not set ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_NR_UARTS=4 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=4 ++# CONFIG_SERIAL_8250_EXTENDED is not set ++ ++# ++# Non-8250 serial port support ++# ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++CONFIG_UNIX98_PTYS=y ++CONFIG_LEGACY_PTYS=y ++CONFIG_LEGACY_PTY_COUNT=2 ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_RTC is not set ++# CONFIG_RTC_PCF8563 is not set ++# CONFIG_RTC_JZ is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++ ++# ++# JZSOC char device support ++# ++# CONFIG_JZCHAR is not set ++# CONFIG_I2C is not set ++ ++# ++# SPI support ++# ++# CONFIG_SPI is not set ++# CONFIG_SPI_MASTER is not set ++# CONFIG_W1 is not set ++# CONFIG_POWER_SUPPLY is not set ++# CONFIG_HWMON is not set ++# CONFIG_WATCHDOG is not set ++ ++# ++# Sonics Silicon Backplane ++# ++CONFIG_SSB_POSSIBLE=y ++# CONFIG_SSB is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_SM501 is not set ++ ++# ++# Multimedia devices ++# ++# CONFIG_VIDEO_DEV is not set ++# CONFIG_DVB_CORE is not set ++# CONFIG_DAB is not set ++ ++# ++# Graphics support ++# ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++# CONFIG_FB is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++ ++# ++# Display device support ++# ++# CONFIG_DISPLAY_SUPPORT is not set ++ ++# ++# Console display driver support ++# ++# CONFIG_VGA_CONSOLE is not set ++CONFIG_DUMMY_CONSOLE=y ++ ++# ++# Sound ++# ++# CONFIG_SOUND is not set ++CONFIG_HID_SUPPORT=y ++CONFIG_HID=y ++# CONFIG_HID_DEBUG is not set ++# CONFIG_HIDRAW is not set ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB_ARCH_HAS_OHCI=y ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++# CONFIG_USB is not set ++ ++# ++# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' ++# ++ ++# ++# USB Gadget Support ++# ++# CONFIG_USB_GADGET is not set ++# CONFIG_MMC is not set ++# CONFIG_NEW_LEDS is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++ ++# ++# Userspace I/O ++# ++# CONFIG_UIO is not set ++ ++# ++# File systems ++# ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++# CONFIG_EXT2_FS_POSIX_ACL is not set ++# CONFIG_EXT2_FS_SECURITY is not set ++# CONFIG_EXT2_FS_XIP is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4DEV_FS is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++CONFIG_FS_POSIX_ACL=y ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++CONFIG_MINIX_FS=y ++# CONFIG_ROMFS_FS is not set ++CONFIG_INOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_QUOTA is not set ++CONFIG_DNOTIFY=y ++# CONFIG_AUTOFS_FS is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_KCORE=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++ ++# ++# Miscellaneous filesystems ++# ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_JFFS2_FS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_NFS_DIRECTIO=y ++# CONFIG_NFSD is not set ++CONFIG_ROOT_NFS=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++CONFIG_SUNRPC_GSS=y ++# CONFIG_SUNRPC_BIND34 is not set ++CONFIG_RPCSEC_GSS_KRB5=y ++# CONFIG_RPCSEC_GSS_SPKM3 is not set ++# CONFIG_SMB_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++CONFIG_NLS_CODEPAGE_936=y ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++# CONFIG_DLM is not set ++ ++# ++# Yaffs2 Filesystems ++# ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set ++# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set ++CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK=y ++CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y ++CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS=10 ++CONFIG_INSTRUMENTATION=y ++# CONFIG_PROFILING is not set ++# CONFIG_MARKERS is not set ++ ++# ++# Kernel hacking ++# ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++# CONFIG_PRINTK_TIME is not set ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_KERNEL is not set ++# CONFIG_SAMPLES is not set ++CONFIG_CMDLINE="" ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITY_FILE_CAPABILITIES is not set ++CONFIG_CRYPTO=y ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_MANAGER=y ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_MD4 is not set ++CONFIG_CRYPTO_MD5=y ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_WP512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_ECB is not set ++CONFIG_CRYPTO_CBC=y ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_XTS is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_AES is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_CRC32C is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_TEST is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_LZO is not set ++CONFIG_CRYPTO_HW=y ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++# CONFIG_CRC_CCITT is not set ++# CONFIG_CRC16 is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++CONFIG_PLIST=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y +diff -urN linux-2.6.24.7.old/arch/mips/configs/leo_defconfig linux-2.6.24.7/arch/mips/configs/leo_defconfig +--- linux-2.6.24.7.old/arch/mips/configs/leo_defconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/configs/leo_defconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1256 @@ ++# ++# Automatically generated make config: don't edit ++# Linux kernel version: 2.6.24.3 ++# Thu Jun 12 13:59:18 2008 ++# ++CONFIG_MIPS=y ++ ++# ++# Machine selection ++# ++# CONFIG_JZ4730_PMP is not set ++# CONFIG_JZ4740_PAVO is not set ++CONFIG_JZ4740_LEO=y ++# CONFIG_JZ4740_LYRA is not set ++# CONFIG_JZ4725_DIPPER is not set ++# CONFIG_JZ4720_VIRGO is not set ++# CONFIG_JZ4750_FUWA is not set ++# CONFIG_MACH_ALCHEMY is not set ++# CONFIG_BASLER_EXCITE is not set ++# CONFIG_BCM47XX is not set ++# CONFIG_MIPS_COBALT is not set ++# CONFIG_MACH_DECSTATION is not set ++# CONFIG_MACH_JAZZ is not set ++# CONFIG_LASAT is not set ++# CONFIG_LEMOTE_FULONG is not set ++# CONFIG_MIPS_ATLAS is not set ++# CONFIG_MIPS_MALTA is not set ++# CONFIG_MIPS_SEAD is not set ++# CONFIG_MIPS_SIM is not set ++# CONFIG_MARKEINS is not set ++# CONFIG_MACH_VR41XX is not set ++# CONFIG_PNX8550_JBS is not set ++# CONFIG_PNX8550_STB810 is not set ++# CONFIG_PMC_MSP is not set ++# CONFIG_PMC_YOSEMITE is not set ++# CONFIG_QEMU is not set ++# CONFIG_SGI_IP22 is not set ++# CONFIG_SGI_IP27 is not set ++# CONFIG_SGI_IP32 is not set ++# CONFIG_SIBYTE_CRHINE is not set ++# CONFIG_SIBYTE_CARMEL is not set ++# CONFIG_SIBYTE_CRHONE is not set ++# CONFIG_SIBYTE_RHONE is not set ++# CONFIG_SIBYTE_SWARM is not set ++# CONFIG_SIBYTE_LITTLESUR is not set ++# CONFIG_SIBYTE_SENTOSA is not set ++# CONFIG_SIBYTE_PTSWARM is not set ++# CONFIG_SIBYTE_BIGSUR is not set ++# CONFIG_SNI_RM is not set ++# CONFIG_TOSHIBA_JMR3927 is not set ++# CONFIG_TOSHIBA_RBTX4927 is not set ++# CONFIG_TOSHIBA_RBTX4938 is not set ++# CONFIG_WR_PPMC is not set ++CONFIG_SOC_JZ4740=y ++CONFIG_JZSOC=y ++CONFIG_JZRISC=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++# CONFIG_ARCH_HAS_ILOG2_U32 is not set ++# CONFIG_ARCH_HAS_ILOG2_U64 is not set ++CONFIG_ARCH_SUPPORTS_OPROFILE=y ++CONFIG_GENERIC_FIND_NEXT_BIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_TIME=y ++CONFIG_GENERIC_CMOS_UPDATE=y ++CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y ++# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set ++CONFIG_DMA_NONCOHERENT=y ++CONFIG_DMA_NEED_PCI_MAP_STATE=y ++# CONFIG_HOTPLUG_CPU is not set ++# CONFIG_NO_IOPORT is not set ++# CONFIG_CPU_BIG_ENDIAN is not set ++CONFIG_CPU_LITTLE_ENDIAN=y ++CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y ++CONFIG_MIPS_L1_CACHE_SHIFT=5 ++ ++# ++# CPU selection ++# ++# CONFIG_CPU_LOONGSON2 is not set ++CONFIG_CPU_MIPS32_R1=y ++# CONFIG_CPU_MIPS32_R2 is not set ++# CONFIG_CPU_MIPS64_R1 is not set ++# CONFIG_CPU_MIPS64_R2 is not set ++# CONFIG_CPU_R3000 is not set ++# CONFIG_CPU_TX39XX is not set ++# CONFIG_CPU_VR41XX is not set ++# CONFIG_CPU_R4300 is not set ++# CONFIG_CPU_R4X00 is not set ++# CONFIG_CPU_TX49XX is not set ++# CONFIG_CPU_R5000 is not set ++# CONFIG_CPU_R5432 is not set ++# CONFIG_CPU_R6000 is not set ++# CONFIG_CPU_NEVADA is not set ++# CONFIG_CPU_R8000 is not set ++# CONFIG_CPU_R10000 is not set ++# CONFIG_CPU_RM7000 is not set ++# CONFIG_CPU_RM9000 is not set ++# CONFIG_CPU_SB1 is not set ++CONFIG_SYS_HAS_CPU_MIPS32_R1=y ++CONFIG_CPU_MIPS32=y ++CONFIG_CPU_MIPSR1=y ++CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y ++CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y ++ ++# ++# Kernel type ++# ++CONFIG_32BIT=y ++# CONFIG_64BIT is not set ++CONFIG_PAGE_SIZE_4KB=y ++# CONFIG_PAGE_SIZE_8KB is not set ++# CONFIG_PAGE_SIZE_16KB is not set ++# CONFIG_PAGE_SIZE_64KB is not set ++CONFIG_CPU_HAS_PREFETCH=y ++CONFIG_MIPS_MT_DISABLED=y ++# CONFIG_MIPS_MT_SMP is not set ++# CONFIG_MIPS_MT_SMTC is not set ++CONFIG_CPU_HAS_LLSC=y ++CONFIG_CPU_HAS_SYNC=y ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_CPU_SUPPORTS_HIGHMEM=y ++CONFIG_ARCH_FLATMEM_ENABLE=y ++CONFIG_ARCH_POPULATES_NODE_MAP=y ++CONFIG_SELECT_MEMORY_MODEL=y ++CONFIG_FLATMEM_MANUAL=y ++# CONFIG_DISCONTIGMEM_MANUAL is not set ++# CONFIG_SPARSEMEM_MANUAL is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++# CONFIG_SPARSEMEM_STATIC is not set ++# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++# CONFIG_RESOURCES_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_TICK_ONESHOT is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++# CONFIG_HZ_48 is not set ++CONFIG_HZ_100=y ++# CONFIG_HZ_128 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_256 is not set ++# CONFIG_HZ_1000 is not set ++# CONFIG_HZ_1024 is not set ++CONFIG_SYS_SUPPORTS_ARBIT_HZ=y ++CONFIG_HZ=100 ++# CONFIG_PREEMPT_NONE is not set ++# CONFIG_PREEMPT_VOLUNTARY is not set ++CONFIG_PREEMPT=y ++CONFIG_PREEMPT_BKL=y ++# CONFIG_KEXEC is not set ++CONFIG_SECCOMP=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++ ++# ++# General setup ++# ++CONFIG_EXPERIMENTAL=y ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_LOCK_KERNEL=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++# CONFIG_USER_NS is not set ++# CONFIG_PID_NS is not set ++# CONFIG_AUDIT is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=14 ++# CONFIG_CGROUPS is not set ++CONFIG_FAIR_GROUP_SCHED=y ++CONFIG_FAIR_USER_SCHED=y ++# CONFIG_FAIR_CGROUP_SCHED is not set ++CONFIG_SYSFS_DEPRECATED=y ++CONFIG_RELAY=y ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_EMBEDDED=y ++CONFIG_SYSCTL_SYSCALL=y ++CONFIG_KALLSYMS=y ++CONFIG_KALLSYMS_ALL=y ++# CONFIG_KALLSYMS_EXTRA_PASS is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_ANON_INODES=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLAB=y ++# CONFIG_SLUB is not set ++# CONFIG_SLOB is not set ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++# CONFIG_TINY_SHMEM is not set ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++CONFIG_KMOD=y ++CONFIG_BLOCK=y ++# CONFIG_LBD is not set ++# CONFIG_BLK_DEV_IO_TRACE is not set ++# CONFIG_LSF is not set ++# CONFIG_BLK_DEV_BSG is not set ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_AS=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_AS=y ++# CONFIG_DEFAULT_DEADLINE is not set ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="anticipatory" ++ ++# ++# Bus options (PCI, PCMCIA, EISA, ISA, TC) ++# ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++CONFIG_MMU=y ++# CONFIG_PCCARD is not set ++ ++# ++# Executable file formats ++# ++CONFIG_BINFMT_ELF=y ++# CONFIG_BINFMT_MISC is not set ++CONFIG_TRAD_SIGNALS=y ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ_JZ=y ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_TABLE=y ++# CONFIG_CPU_FREQ_DEBUG is not set ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++ ++# ++# Power management options ++# ++CONFIG_PM=y ++CONFIG_PM_LEGACY=y ++# CONFIG_PM_DEBUG is not set ++CONFIG_SUSPEND_UP_POSSIBLE=y ++# CONFIG_SUSPEND is not set ++ ++# ++# Networking ++# ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_MMAP is not set ++CONFIG_UNIX=y ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_FIB_HASH=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++# CONFIG_INET_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NETFILTER is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_ECONET is not set ++# CONFIG_WAN_ROUTER is not set ++# CONFIG_NET_SCHED is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++ ++# ++# Wireless ++# ++# CONFIG_CFG80211 is not set ++CONFIG_WIRELESS_EXT=y ++# CONFIG_MAC80211 is not set ++# CONFIG_IEEE80211 is not set ++# CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++# CONFIG_FW_LOADER is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_DEBUG is not set ++CONFIG_MTD_CONCAT=y ++CONFIG_MTD_PARTITIONS=y ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++CONFIG_MTD_BLOCK2MTD=y ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOC2000 is not set ++# CONFIG_MTD_DOC2001 is not set ++# CONFIG_MTD_DOC2001PLUS is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_VERIFY_WRITE is not set ++# CONFIG_MTD_NAND_ECC_SMC is not set ++# CONFIG_MTD_NAND_MUSEUM_IDS is not set ++CONFIG_MTD_NAND_IDS=y ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_MTD_ALAUDA is not set ++CONFIG_MTD_NAND_JZ4740=y ++# CONFIG_MTD_HW_HM_ECC is not set ++CONFIG_MTD_SW_HM_ECC=y ++# CONFIG_MTD_HW_RS_ECC is not set ++# CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE is not set ++CONFIG_MTD_OOB_COPIES=3 ++CONFIG_MTD_BADBLOCK_FLAG_PAGE=0 ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# UBI - Unsorted block images ++# ++CONFIG_MTD_UBI=m ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_RESERVE=1 ++# CONFIG_MTD_UBI_GLUEBI is not set ++ ++# ++# UBI debugging options ++# ++# CONFIG_MTD_UBI_DEBUG is not set ++CONFIG_MTD_UBI_BLKDEVS=m ++CONFIG_MTD_UBI_BLOCK=m ++# CONFIG_PARPORT is not set ++CONFIG_PNP=y ++# CONFIG_PNP_DEBUG is not set ++ ++# ++# Protocols ++# ++# CONFIG_PNPACPI is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_UB is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=2 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++CONFIG_MISC_DEVICES=y ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_TGT is not set ++# CONFIG_SCSI_NETLINK is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++ ++# ++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs ++# ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++CONFIG_SCSI_WAIT_SCAN=m ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++CONFIG_SCSI_LOWLEVEL=y ++# CONFIG_ISCSI_TCP is not set ++# CONFIG_SCSI_DEBUG is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_NETDEVICES is not set ++# CONFIG_ISDN is not set ++# CONFIG_PHONE is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++# CONFIG_SERIO_I8042 is not set ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_LIBPS2 is not set ++# CONFIG_SERIO_RAW is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_VT_CONSOLE=y ++CONFIG_HW_CONSOLE=y ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_SERIAL_NONSTANDARD is not set ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_PNP=y ++CONFIG_SERIAL_8250_NR_UARTS=2 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=2 ++# CONFIG_SERIAL_8250_EXTENDED is not set ++ ++# ++# Non-8250 serial port support ++# ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++CONFIG_UNIX98_PTYS=y ++CONFIG_LEGACY_PTYS=y ++CONFIG_LEGACY_PTY_COUNT=2 ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_RTC is not set ++# CONFIG_RTC_PCF8563 is not set ++CONFIG_RTC_JZ=y ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++ ++# ++# JZSOC char device support ++# ++CONFIG_JZCHAR=y ++# CONFIG_JZ_CIM is not set ++# CONFIG_JZ_TPANEL_ATA2508 is not set ++CONFIG_JZ_TPANEL=y ++CONFIG_JZ_SADC=y ++# CONFIG_JZ_TPANEL_AK4182 is not set ++# CONFIG_JZ_TPANEL_UCB1400 is not set ++# CONFIG_JZ_TPANEL_WM9712 is not set ++CONFIG_JZ_UDC_HOTPLUG=y ++CONFIG_JZ_POWEROFF=y ++# CONFIG_JZ_OW is not set ++# CONFIG_I2C is not set ++ ++# ++# SPI support ++# ++# CONFIG_SPI is not set ++# CONFIG_SPI_MASTER is not set ++# CONFIG_W1 is not set ++# CONFIG_POWER_SUPPLY is not set ++# CONFIG_HWMON is not set ++CONFIG_WATCHDOG=y ++# CONFIG_WATCHDOG_NOWAYOUT is not set ++ ++# ++# Watchdog Device Drivers ++# ++CONFIG_JZ_WDT=y ++# CONFIG_SOFT_WATCHDOG is not set ++ ++# ++# USB-based Watchdog Cards ++# ++# CONFIG_USBPCWATCHDOG is not set ++ ++# ++# Sonics Silicon Backplane ++# ++CONFIG_SSB_POSSIBLE=y ++# CONFIG_SSB is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_SM501 is not set ++ ++# ++# Multimedia devices ++# ++CONFIG_VIDEO_DEV=y ++CONFIG_VIDEO_V4L1=y ++CONFIG_VIDEO_V4L1_COMPAT=y ++CONFIG_VIDEO_V4L2=y ++CONFIG_VIDEO_CAPTURE_DRIVERS=y ++# CONFIG_VIDEO_ADV_DEBUG is not set ++CONFIG_VIDEO_HELPER_CHIPS_AUTO=y ++# CONFIG_VIDEO_VIVI is not set ++# CONFIG_VIDEO_CPIA is not set ++# CONFIG_VIDEO_CPIA2 is not set ++CONFIG_VIDEO_JZ_CIM=y ++CONFIG_VIDEO_JZ_SENSOR=y ++CONFIG_V4L_USB_DRIVERS=y ++# CONFIG_USB_VICAM is not set ++# CONFIG_USB_IBMCAM is not set ++# CONFIG_USB_KONICAWC is not set ++# CONFIG_USB_QUICKCAM_MESSENGER is not set ++# CONFIG_USB_ET61X251 is not set ++# CONFIG_USB_OV511 is not set ++# CONFIG_USB_SE401 is not set ++# CONFIG_USB_SN9C102 is not set ++# CONFIG_USB_STV680 is not set ++# CONFIG_USB_ZC0301 is not set ++# CONFIG_USB_PWC is not set ++# CONFIG_USB_ZR364XX is not set ++CONFIG_RADIO_ADAPTERS=y ++# CONFIG_USB_DSBR is not set ++# CONFIG_DVB_CORE is not set ++# CONFIG_DAB is not set ++ ++# ++# Graphics support ++# ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++# CONFIG_FB_DDC is not set ++CONFIG_FB_CFB_FILLRECT=y ++CONFIG_FB_CFB_COPYAREA=y ++CONFIG_FB_CFB_IMAGEBLIT=y ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_SYS_FOPS is not set ++CONFIG_FB_DEFERRED_IO=y ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++CONFIG_FB_JZSOC=y ++# CONFIG_FB_JZ4740_SLCD is not set ++CONFIG_FB_JZLCD_4730_4740=y ++CONFIG_JZLCD_FRAMEBUFFER_MAX=1 ++# CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT is not set ++# CONFIG_JZLCD_SHARP_LQ035Q7 is not set ++# CONFIG_JZLCD_SAMSUNG_LTS350Q1 is not set ++# CONFIG_JZLCD_SAMSUNG_LTV350QVF04 is not set ++# CONFIG_JZLCD_SAMSUNG_LTP400WQF01 is not set ++CONFIG_JZLCD_SAMSUNG_LTP400WQF02=y ++# CONFIG_JZLCD_AUO_A030FL01_V1 is not set ++# CONFIG_JZLCD_TRULY_TFTG320240DTSW is not set ++# CONFIG_JZLCD_TRULY_TFTG320240DTSW_SERIAL is not set ++# CONFIG_JZLCD_TRULY_TFTG240320UTSW_63W_E is not set ++# CONFIG_JZLCD_FOXCONN_PT035TN01 is not set ++# CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL is not set ++# CONFIG_JZLCD_TOSHIBA_LTM084P363 is not set ++# CONFIG_JZLCD_HYNIX_HT10X21 is not set ++# CONFIG_JZLCD_INNOLUX_AT080TN42 is not set ++# CONFIG_JZLCD_CSTN_800x600 is not set ++# CONFIG_JZLCD_CSTN_320x240 is not set ++# CONFIG_JZLCD_MSTN_480x320 is not set ++# CONFIG_JZLCD_MSTN_320x240 is not set ++# CONFIG_JZLCD_MSTN_240x128 is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++ ++# ++# Display device support ++# ++# CONFIG_DISPLAY_SUPPORT is not set ++ ++# ++# Console display driver support ++# ++# CONFIG_VGA_CONSOLE is not set ++CONFIG_DUMMY_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_CURSOR_FLASH is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set ++CONFIG_FONTS=y ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++# CONFIG_FONT_6x11 is not set ++# CONFIG_FONT_7x14 is not set ++# CONFIG_FONT_PEARL_8x8 is not set ++# CONFIG_FONT_ACORN_8x8 is not set ++# CONFIG_FONT_MINI_4x6 is not set ++# CONFIG_FONT_SUN8x16 is not set ++# CONFIG_FONT_SUN12x22 is not set ++# CONFIG_FONT_10x18 is not set ++CONFIG_LOGO=y ++CONFIG_LOGO_LINUX_MONO=y ++CONFIG_LOGO_LINUX_VGA16=y ++CONFIG_LOGO_LINUX_CLUT224=y ++ ++# ++# Sound ++# ++CONFIG_SOUND=y ++ ++# ++# Advanced Linux Sound Architecture ++# ++CONFIG_SND=y ++CONFIG_SND_TIMER=y ++CONFIG_SND_PCM=y ++# CONFIG_SND_SEQUENCER is not set ++CONFIG_SND_OSSEMUL=y ++CONFIG_SND_MIXER_OSS=y ++CONFIG_SND_PCM_OSS=y ++CONFIG_SND_PCM_OSS_PLUGINS=y ++# CONFIG_SND_DYNAMIC_MINORS is not set ++CONFIG_SND_SUPPORT_OLD_API=y ++CONFIG_SND_VERBOSE_PROCFS=y ++# CONFIG_SND_VERBOSE_PRINTK is not set ++# CONFIG_SND_DEBUG is not set ++ ++# ++# Generic devices ++# ++# CONFIG_SND_DUMMY is not set ++# CONFIG_SND_MTPAV is not set ++# CONFIG_SND_SERIAL_U16550 is not set ++# CONFIG_SND_MPU401 is not set ++ ++# ++# ALSA MIPS devices ++# ++ ++# ++# USB devices ++# ++# CONFIG_SND_USB_AUDIO is not set ++# CONFIG_SND_USB_CAIAQ is not set ++ ++# ++# System on Chip audio support ++# ++CONFIG_SND_SOC=y ++ ++# ++# SoC Audio support for SuperH ++# ++ ++# ++# Open Sound System ++# ++# CONFIG_SOUND_PRIME is not set ++CONFIG_HID_SUPPORT=y ++CONFIG_HID=y ++# CONFIG_HID_DEBUG is not set ++# CONFIG_HIDRAW is not set ++ ++# ++# USB Input Devices ++# ++CONFIG_USB_HID=y ++# CONFIG_USB_HIDINPUT_POWERBOOK is not set ++# CONFIG_HID_FF is not set ++# CONFIG_USB_HIDDEV is not set ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB_ARCH_HAS_OHCI=y ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++CONFIG_USB=y ++# CONFIG_USB_DEBUG is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEVICEFS is not set ++CONFIG_USB_DEVICE_CLASS=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_SUSPEND is not set ++# CONFIG_USB_PERSIST is not set ++# CONFIG_USB_OTG is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_ISP116X_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set ++# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++ ++# ++# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' ++# ++ ++# ++# may also be needed; see USB_STORAGE Help for more information ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_DPCM is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_LIBUSUAL is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++CONFIG_USB_MON=y ++ ++# ++# USB port drivers ++# ++ ++# ++# USB Serial Converter support ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_AUERSWALD is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_BERRY_CHARGE is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_PHIDGET is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++ ++# ++# USB DSL modem support ++# ++ ++# ++# USB Gadget Support ++# ++CONFIG_USB_GADGET=m ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++# CONFIG_USB_GADGET_DEBUG_FS is not set ++CONFIG_USB_GADGET_SELECTED=y ++CONFIG_USB_GADGET_JZ4740=y ++CONFIG_USB_JZ4740=m ++# CONFIG_USB_GADGET_JZ4730 is not set ++# CONFIG_USB_GADGET_AMD5536UDC is not set ++# CONFIG_USB_GADGET_ATMEL_USBA is not set ++# CONFIG_USB_GADGET_FSL_USB2 is not set ++# CONFIG_USB_GADGET_NET2280 is not set ++# CONFIG_USB_GADGET_PXA2XX is not set ++# CONFIG_USB_GADGET_M66592 is not set ++# CONFIG_USB_GADGET_GOKU is not set ++# CONFIG_USB_GADGET_LH7A40X is not set ++# CONFIG_USB_GADGET_OMAP is not set ++# CONFIG_USB_GADGET_S3C2410 is not set ++# CONFIG_USB_GADGET_AT91 is not set ++# CONFIG_USB_GADGET_DUMMY_HCD is not set ++CONFIG_USB_GADGET_DUALSPEED=y ++# CONFIG_USB_ZERO is not set ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_GADGETFS is not set ++CONFIG_USB_FILE_STORAGE=m ++CONFIG_USB_FILE_STORAGE_TEST=y ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_MIDI_GADGET is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_UNSAFE_RESUME is not set ++ ++# ++# MMC/SD Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_SDIO_UART is not set ++ ++# ++# MMC/SD Host Controller Drivers ++# ++CONFIG_MMC_JZ=y ++CONFIG_JZ_MMC_BUS_4=y ++# CONFIG_JZ_MMC_BUS_1 is not set ++# CONFIG_NEW_LEDS is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++ ++# ++# Userspace I/O ++# ++# CONFIG_UIO is not set ++ ++# ++# File systems ++# ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++# CONFIG_EXT2_FS_POSIX_ACL is not set ++# CONFIG_EXT2_FS_SECURITY is not set ++# CONFIG_EXT2_FS_XIP is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4DEV_FS is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++CONFIG_FS_POSIX_ACL=y ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++CONFIG_MINIX_FS=y ++# CONFIG_ROMFS_FS is not set ++CONFIG_INOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_QUOTA is not set ++CONFIG_DNOTIFY=y ++# CONFIG_AUTOFS_FS is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_KCORE=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++ ++# ++# Miscellaneous filesystems ++# ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_JFFS2_FS is not set ++CONFIG_UBIFS_FS=m ++# CONFIG_UBIFS_FS_XATTR is not set ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++CONFIG_UBIFS_FS_DEBUG=y ++CONFIG_UBIFS_FS_DEBUG_MSG_LVL=0 ++# CONFIG_UBIFS_FS_DEBUG_CHKS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_NFS_DIRECTIO=y ++# CONFIG_NFSD is not set ++CONFIG_ROOT_NFS=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++CONFIG_SUNRPC_GSS=y ++# CONFIG_SUNRPC_BIND34 is not set ++CONFIG_RPCSEC_GSS_KRB5=y ++# CONFIG_RPCSEC_GSS_SPKM3 is not set ++# CONFIG_SMB_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++CONFIG_NLS_CODEPAGE_936=y ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++# CONFIG_DLM is not set ++ ++# ++# Yaffs2 Filesystems ++# ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set ++# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set ++CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK=y ++CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y ++CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS=10 ++CONFIG_INSTRUMENTATION=y ++# CONFIG_PROFILING is not set ++# CONFIG_MARKERS is not set ++ ++# ++# Kernel hacking ++# ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++# CONFIG_PRINTK_TIME is not set ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_UNUSED_SYMBOLS is not set ++CONFIG_DEBUG_FS=y ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_KERNEL is not set ++# CONFIG_SAMPLES is not set ++CONFIG_CMDLINE="" ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITY_FILE_CAPABILITIES is not set ++CONFIG_CRYPTO=y ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_MANAGER=y ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_MD4 is not set ++CONFIG_CRYPTO_MD5=y ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_WP512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_ECB is not set ++CONFIG_CRYPTO_CBC=y ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_XTS is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_AES is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_SEED is not set ++CONFIG_CRYPTO_DEFLATE=m ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_CRC32C is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_TEST is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++CONFIG_CRYPTO_LZO=m ++CONFIG_CRYPTO_HW=y ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=m ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++CONFIG_ZLIB_INFLATE=m ++CONFIG_ZLIB_DEFLATE=m ++CONFIG_LZO_COMPRESS=m ++CONFIG_LZO_DECOMPRESS=m ++CONFIG_PLIST=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y +diff -urN linux-2.6.24.7.old/arch/mips/configs/lyra_defconfig linux-2.6.24.7/arch/mips/configs/lyra_defconfig +--- linux-2.6.24.7.old/arch/mips/configs/lyra_defconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/configs/lyra_defconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,981 @@ ++# ++# Automatically generated make config: don't edit ++# Linux kernel version: 2.6.24.3 ++# Thu Jun 12 13:53:57 2008 ++# ++CONFIG_MIPS=y ++ ++# ++# Machine selection ++# ++# CONFIG_JZ4730_PMP is not set ++# CONFIG_JZ4740_PAVO is not set ++# CONFIG_JZ4740_LEO is not set ++CONFIG_JZ4740_LYRA=y ++# CONFIG_JZ4725_DIPPER is not set ++# CONFIG_JZ4720_VIRGO is not set ++# CONFIG_JZ4750_FUWA is not set ++# CONFIG_MACH_ALCHEMY is not set ++# CONFIG_BASLER_EXCITE is not set ++# CONFIG_BCM47XX is not set ++# CONFIG_MIPS_COBALT is not set ++# CONFIG_MACH_DECSTATION is not set ++# CONFIG_MACH_JAZZ is not set ++# CONFIG_LASAT is not set ++# CONFIG_LEMOTE_FULONG is not set ++# CONFIG_MIPS_ATLAS is not set ++# CONFIG_MIPS_MALTA is not set ++# CONFIG_MIPS_SEAD is not set ++# CONFIG_MIPS_SIM is not set ++# CONFIG_MARKEINS is not set ++# CONFIG_MACH_VR41XX is not set ++# CONFIG_PNX8550_JBS is not set ++# CONFIG_PNX8550_STB810 is not set ++# CONFIG_PMC_MSP is not set ++# CONFIG_PMC_YOSEMITE is not set ++# CONFIG_QEMU is not set ++# CONFIG_SGI_IP22 is not set ++# CONFIG_SGI_IP27 is not set ++# CONFIG_SGI_IP32 is not set ++# CONFIG_SIBYTE_CRHINE is not set ++# CONFIG_SIBYTE_CARMEL is not set ++# CONFIG_SIBYTE_CRHONE is not set ++# CONFIG_SIBYTE_RHONE is not set ++# CONFIG_SIBYTE_SWARM is not set ++# CONFIG_SIBYTE_LITTLESUR is not set ++# CONFIG_SIBYTE_SENTOSA is not set ++# CONFIG_SIBYTE_PTSWARM is not set ++# CONFIG_SIBYTE_BIGSUR is not set ++# CONFIG_SNI_RM is not set ++# CONFIG_TOSHIBA_JMR3927 is not set ++# CONFIG_TOSHIBA_RBTX4927 is not set ++# CONFIG_TOSHIBA_RBTX4938 is not set ++# CONFIG_WR_PPMC is not set ++CONFIG_SOC_JZ4740=y ++CONFIG_JZSOC=y ++CONFIG_JZRISC=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++# CONFIG_ARCH_HAS_ILOG2_U32 is not set ++# CONFIG_ARCH_HAS_ILOG2_U64 is not set ++CONFIG_ARCH_SUPPORTS_OPROFILE=y ++CONFIG_GENERIC_FIND_NEXT_BIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_TIME=y ++CONFIG_GENERIC_CMOS_UPDATE=y ++CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y ++# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set ++CONFIG_DMA_NONCOHERENT=y ++CONFIG_DMA_NEED_PCI_MAP_STATE=y ++# CONFIG_HOTPLUG_CPU is not set ++# CONFIG_NO_IOPORT is not set ++# CONFIG_CPU_BIG_ENDIAN is not set ++CONFIG_CPU_LITTLE_ENDIAN=y ++CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y ++CONFIG_MIPS_L1_CACHE_SHIFT=5 ++ ++# ++# CPU selection ++# ++# CONFIG_CPU_LOONGSON2 is not set ++CONFIG_CPU_MIPS32_R1=y ++# CONFIG_CPU_MIPS32_R2 is not set ++# CONFIG_CPU_MIPS64_R1 is not set ++# CONFIG_CPU_MIPS64_R2 is not set ++# CONFIG_CPU_R3000 is not set ++# CONFIG_CPU_TX39XX is not set ++# CONFIG_CPU_VR41XX is not set ++# CONFIG_CPU_R4300 is not set ++# CONFIG_CPU_R4X00 is not set ++# CONFIG_CPU_TX49XX is not set ++# CONFIG_CPU_R5000 is not set ++# CONFIG_CPU_R5432 is not set ++# CONFIG_CPU_R6000 is not set ++# CONFIG_CPU_NEVADA is not set ++# CONFIG_CPU_R8000 is not set ++# CONFIG_CPU_R10000 is not set ++# CONFIG_CPU_RM7000 is not set ++# CONFIG_CPU_RM9000 is not set ++# CONFIG_CPU_SB1 is not set ++CONFIG_SYS_HAS_CPU_MIPS32_R1=y ++CONFIG_CPU_MIPS32=y ++CONFIG_CPU_MIPSR1=y ++CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y ++CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y ++ ++# ++# Kernel type ++# ++CONFIG_32BIT=y ++# CONFIG_64BIT is not set ++CONFIG_PAGE_SIZE_4KB=y ++# CONFIG_PAGE_SIZE_8KB is not set ++# CONFIG_PAGE_SIZE_16KB is not set ++# CONFIG_PAGE_SIZE_64KB is not set ++CONFIG_CPU_HAS_PREFETCH=y ++CONFIG_MIPS_MT_DISABLED=y ++# CONFIG_MIPS_MT_SMP is not set ++# CONFIG_MIPS_MT_SMTC is not set ++CONFIG_CPU_HAS_LLSC=y ++CONFIG_CPU_HAS_SYNC=y ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_CPU_SUPPORTS_HIGHMEM=y ++CONFIG_ARCH_FLATMEM_ENABLE=y ++CONFIG_ARCH_POPULATES_NODE_MAP=y ++CONFIG_SELECT_MEMORY_MODEL=y ++CONFIG_FLATMEM_MANUAL=y ++# CONFIG_DISCONTIGMEM_MANUAL is not set ++# CONFIG_SPARSEMEM_MANUAL is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++# CONFIG_SPARSEMEM_STATIC is not set ++# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++# CONFIG_RESOURCES_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_TICK_ONESHOT is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++# CONFIG_HZ_48 is not set ++CONFIG_HZ_100=y ++# CONFIG_HZ_128 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_256 is not set ++# CONFIG_HZ_1000 is not set ++# CONFIG_HZ_1024 is not set ++CONFIG_SYS_SUPPORTS_ARBIT_HZ=y ++CONFIG_HZ=100 ++# CONFIG_PREEMPT_NONE is not set ++# CONFIG_PREEMPT_VOLUNTARY is not set ++CONFIG_PREEMPT=y ++CONFIG_PREEMPT_BKL=y ++# CONFIG_KEXEC is not set ++CONFIG_SECCOMP=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++ ++# ++# General setup ++# ++CONFIG_EXPERIMENTAL=y ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_LOCK_KERNEL=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_USER_NS is not set ++# CONFIG_PID_NS is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=14 ++# CONFIG_CGROUPS is not set ++CONFIG_FAIR_GROUP_SCHED=y ++CONFIG_FAIR_USER_SCHED=y ++# CONFIG_FAIR_CGROUP_SCHED is not set ++CONFIG_SYSFS_DEPRECATED=y ++CONFIG_RELAY=y ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_EMBEDDED=y ++CONFIG_SYSCTL_SYSCALL=y ++CONFIG_KALLSYMS=y ++CONFIG_KALLSYMS_ALL=y ++# CONFIG_KALLSYMS_EXTRA_PASS is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_ANON_INODES=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLAB=y ++# CONFIG_SLUB is not set ++# CONFIG_SLOB is not set ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++# CONFIG_TINY_SHMEM is not set ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++CONFIG_KMOD=y ++CONFIG_BLOCK=y ++# CONFIG_LBD is not set ++# CONFIG_BLK_DEV_IO_TRACE is not set ++# CONFIG_LSF is not set ++# CONFIG_BLK_DEV_BSG is not set ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_AS=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_AS=y ++# CONFIG_DEFAULT_DEADLINE is not set ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="anticipatory" ++ ++# ++# Bus options (PCI, PCMCIA, EISA, ISA, TC) ++# ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++CONFIG_MMU=y ++# CONFIG_PCCARD is not set ++ ++# ++# Executable file formats ++# ++CONFIG_BINFMT_ELF=y ++# CONFIG_BINFMT_MISC is not set ++CONFIG_TRAD_SIGNALS=y ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ_JZ=y ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_TABLE=y ++# CONFIG_CPU_FREQ_DEBUG is not set ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++ ++# ++# Power management options ++# ++CONFIG_PM=y ++CONFIG_PM_LEGACY=y ++# CONFIG_PM_DEBUG is not set ++CONFIG_PM_SLEEP=y ++CONFIG_SUSPEND_UP_POSSIBLE=y ++CONFIG_SUSPEND=y ++ ++# ++# Networking ++# ++# CONFIG_NET is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++# CONFIG_FW_LOADER is not set ++# CONFIG_SYS_HYPERVISOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_DEBUG is not set ++CONFIG_MTD_CONCAT=y ++CONFIG_MTD_PARTITIONS=y ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++CONFIG_MTD_BLOCK2MTD=y ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOC2000 is not set ++# CONFIG_MTD_DOC2001 is not set ++# CONFIG_MTD_DOC2001PLUS is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_VERIFY_WRITE is not set ++# CONFIG_MTD_NAND_ECC_SMC is not set ++# CONFIG_MTD_NAND_MUSEUM_IDS is not set ++CONFIG_MTD_NAND_IDS=y ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++CONFIG_MTD_NAND_JZ4740=y ++# CONFIG_MTD_HW_HM_ECC is not set ++# CONFIG_MTD_SW_HM_ECC is not set ++CONFIG_MTD_HW_RS_ECC=y ++# CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE is not set ++CONFIG_MTD_OOB_COPIES=3 ++CONFIG_MTD_BADBLOCK_FLAG_PAGE=127 ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# UBI - Unsorted block images ++# ++CONFIG_MTD_UBI=m ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_RESERVE=1 ++CONFIG_MTD_UBI_GLUEBI=y ++ ++# ++# UBI debugging options ++# ++# CONFIG_MTD_UBI_DEBUG is not set ++# CONFIG_MTD_UBI_BLKDEVS is not set ++# CONFIG_PARPORT is not set ++# CONFIG_PNP is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 ++# CONFIG_CDROM_PKTCDVD is not set ++CONFIG_MISC_DEVICES=y ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_TGT is not set ++# CONFIG_SCSI_NETLINK is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++ ++# ++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs ++# ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++CONFIG_SCSI_WAIT_SCAN=m ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++CONFIG_SCSI_LOWLEVEL=y ++# CONFIG_SCSI_DEBUG is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_PHONE is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++# CONFIG_SERIO_I8042 is not set ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_LIBPS2 is not set ++# CONFIG_SERIO_RAW is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_VT_CONSOLE=y ++CONFIG_HW_CONSOLE=y ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_SERIAL_NONSTANDARD is not set ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_NR_UARTS=4 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=4 ++# CONFIG_SERIAL_8250_EXTENDED is not set ++ ++# ++# Non-8250 serial port support ++# ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++CONFIG_UNIX98_PTYS=y ++CONFIG_LEGACY_PTYS=y ++CONFIG_LEGACY_PTY_COUNT=256 ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_RTC is not set ++# CONFIG_RTC_PCF8563 is not set ++CONFIG_RTC_JZ=y ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++ ++# ++# JZSOC char device support ++# ++CONFIG_JZCHAR=y ++# CONFIG_JZ_CIM is not set ++CONFIG_JZ_TPANEL_ATA2508=y ++# CONFIG_JZ_TPANEL is not set ++CONFIG_JZ_UDC_HOTPLUG=y ++CONFIG_JZ_POWEROFF=y ++# CONFIG_JZ_OW is not set ++# CONFIG_I2C is not set ++ ++# ++# SPI support ++# ++# CONFIG_SPI is not set ++# CONFIG_SPI_MASTER is not set ++# CONFIG_W1 is not set ++# CONFIG_POWER_SUPPLY is not set ++# CONFIG_HWMON is not set ++CONFIG_WATCHDOG=y ++# CONFIG_WATCHDOG_NOWAYOUT is not set ++ ++# ++# Watchdog Device Drivers ++# ++CONFIG_JZ_WDT=y ++# CONFIG_SOFT_WATCHDOG is not set ++ ++# ++# Sonics Silicon Backplane ++# ++CONFIG_SSB_POSSIBLE=y ++# CONFIG_SSB is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_SM501 is not set ++ ++# ++# Multimedia devices ++# ++# CONFIG_VIDEO_DEV is not set ++# CONFIG_DAB is not set ++ ++# ++# Graphics support ++# ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++# CONFIG_FB_DDC is not set ++CONFIG_FB_CFB_FILLRECT=y ++CONFIG_FB_CFB_COPYAREA=y ++CONFIG_FB_CFB_IMAGEBLIT=y ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_SYS_FOPS is not set ++CONFIG_FB_DEFERRED_IO=y ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++CONFIG_FB_JZSOC=y ++# CONFIG_FB_JZ4740_SLCD is not set ++CONFIG_FB_JZLCD_4730_4740=y ++CONFIG_JZLCD_FRAMEBUFFER_MAX=1 ++# CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT is not set ++# CONFIG_JZLCD_SHARP_LQ035Q7 is not set ++# CONFIG_JZLCD_SAMSUNG_LTS350Q1 is not set ++# CONFIG_JZLCD_SAMSUNG_LTV350QVF04 is not set ++# CONFIG_JZLCD_SAMSUNG_LTP400WQF01 is not set ++# CONFIG_JZLCD_SAMSUNG_LTP400WQF02 is not set ++CONFIG_JZLCD_AUO_A030FL01_V1=y ++# CONFIG_JZLCD_TRULY_TFTG320240DTSW is not set ++# CONFIG_JZLCD_TRULY_TFTG320240DTSW_SERIAL is not set ++# CONFIG_JZLCD_TRULY_TFTG240320UTSW_63W_E is not set ++# CONFIG_JZLCD_FOXCONN_PT035TN01 is not set ++# CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL is not set ++# CONFIG_JZLCD_TOSHIBA_LTM084P363 is not set ++# CONFIG_JZLCD_HYNIX_HT10X21 is not set ++# CONFIG_JZLCD_INNOLUX_AT080TN42 is not set ++# CONFIG_JZLCD_CSTN_800x600 is not set ++# CONFIG_JZLCD_CSTN_320x240 is not set ++# CONFIG_JZLCD_MSTN_480x320 is not set ++# CONFIG_JZLCD_MSTN_320x240 is not set ++# CONFIG_JZLCD_MSTN_240x128 is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++ ++# ++# Display device support ++# ++# CONFIG_DISPLAY_SUPPORT is not set ++ ++# ++# Console display driver support ++# ++# CONFIG_VGA_CONSOLE is not set ++CONFIG_DUMMY_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_CURSOR_FLASH is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set ++CONFIG_FONTS=y ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++# CONFIG_FONT_6x11 is not set ++# CONFIG_FONT_7x14 is not set ++# CONFIG_FONT_PEARL_8x8 is not set ++# CONFIG_FONT_ACORN_8x8 is not set ++# CONFIG_FONT_MINI_4x6 is not set ++# CONFIG_FONT_SUN8x16 is not set ++# CONFIG_FONT_SUN12x22 is not set ++# CONFIG_FONT_10x18 is not set ++CONFIG_LOGO=y ++CONFIG_LOGO_LINUX_MONO=y ++CONFIG_LOGO_LINUX_VGA16=y ++CONFIG_LOGO_LINUX_CLUT224=y ++ ++# ++# Sound ++# ++CONFIG_SOUND=y ++ ++# ++# Advanced Linux Sound Architecture ++# ++# CONFIG_SND is not set ++ ++# ++# Open Sound System ++# ++CONFIG_SOUND_PRIME=y ++CONFIG_OSS_OBSOLETE=y ++# CONFIG_SOUND_JZ_AC97 is not set ++CONFIG_SOUND_JZ_I2S=y ++# CONFIG_SOUND_JZ_PCM is not set ++# CONFIG_I2S_AK4642EN is not set ++CONFIG_I2S_ICODEC=y ++# CONFIG_SOUND_MSNDCLAS is not set ++# CONFIG_SOUND_MSNDPIN is not set ++CONFIG_HID_SUPPORT=y ++CONFIG_HID=y ++# CONFIG_HID_DEBUG is not set ++# CONFIG_HIDRAW is not set ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB_ARCH_HAS_OHCI=y ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++# CONFIG_USB is not set ++ ++# ++# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' ++# ++ ++# ++# USB Gadget Support ++# ++CONFIG_USB_GADGET=m ++CONFIG_USB_GADGET_DEBUG_FILES=y ++# CONFIG_USB_GADGET_DEBUG_FS is not set ++CONFIG_USB_GADGET_SELECTED=y ++CONFIG_USB_GADGET_JZ4740=y ++CONFIG_USB_JZ4740=m ++# CONFIG_USB_GADGET_JZ4730 is not set ++# CONFIG_USB_GADGET_AMD5536UDC is not set ++# CONFIG_USB_GADGET_ATMEL_USBA is not set ++# CONFIG_USB_GADGET_FSL_USB2 is not set ++# CONFIG_USB_GADGET_NET2280 is not set ++# CONFIG_USB_GADGET_PXA2XX is not set ++# CONFIG_USB_GADGET_M66592 is not set ++# CONFIG_USB_GADGET_GOKU is not set ++# CONFIG_USB_GADGET_LH7A40X is not set ++# CONFIG_USB_GADGET_OMAP is not set ++# CONFIG_USB_GADGET_S3C2410 is not set ++# CONFIG_USB_GADGET_AT91 is not set ++# CONFIG_USB_GADGET_DUMMY_HCD is not set ++CONFIG_USB_GADGET_DUALSPEED=y ++# CONFIG_USB_ZERO is not set ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_GADGETFS is not set ++CONFIG_USB_FILE_STORAGE=m ++# CONFIG_USB_FILE_STORAGE_TEST is not set ++CONFIG_USB_G_SERIAL=m ++# CONFIG_USB_MIDI_GADGET is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_UNSAFE_RESUME is not set ++ ++# ++# MMC/SD Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_SDIO_UART is not set ++ ++# ++# MMC/SD Host Controller Drivers ++# ++# CONFIG_MMC_JZ is not set ++# CONFIG_NEW_LEDS is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++ ++# ++# Userspace I/O ++# ++# CONFIG_UIO is not set ++ ++# ++# File systems ++# ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++# CONFIG_EXT2_FS_POSIX_ACL is not set ++# CONFIG_EXT2_FS_SECURITY is not set ++# CONFIG_EXT2_FS_XIP is not set ++CONFIG_EXT3_FS=y ++CONFIG_EXT3_FS_XATTR=y ++# CONFIG_EXT3_FS_POSIX_ACL is not set ++# CONFIG_EXT3_FS_SECURITY is not set ++# CONFIG_EXT4DEV_FS is not set ++CONFIG_JBD=y ++# CONFIG_JBD_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_FS_POSIX_ACL is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++CONFIG_MINIX_FS=y ++# CONFIG_ROMFS_FS is not set ++CONFIG_INOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_QUOTA is not set ++CONFIG_DNOTIFY=y ++# CONFIG_AUTOFS_FS is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_KCORE=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++ ++# ++# Miscellaneous filesystems ++# ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_JFFS2_FS is not set ++CONFIG_UBIFS_FS=m ++# CONFIG_UBIFS_FS_XATTR is not set ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++CONFIG_UBIFS_FS_DEBUG=y ++CONFIG_UBIFS_FS_DEBUG_MSG_LVL=0 ++# CONFIG_UBIFS_FS_DEBUG_CHKS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++CONFIG_NLS_CODEPAGE_936=y ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++ ++# ++# Yaffs2 Filesystems ++# ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set ++# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set ++CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK=y ++CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y ++CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS=10 ++CONFIG_INSTRUMENTATION=y ++# CONFIG_PROFILING is not set ++# CONFIG_MARKERS is not set ++ ++# ++# Kernel hacking ++# ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++# CONFIG_PRINTK_TIME is not set ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_UNUSED_SYMBOLS is not set ++CONFIG_DEBUG_FS=y ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_KERNEL is not set ++# CONFIG_SAMPLES is not set ++CONFIG_CMDLINE="" ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITY_FILE_CAPABILITIES is not set ++CONFIG_CRYPTO=y ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_MANAGER=y ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_MD4 is not set ++CONFIG_CRYPTO_MD5=y ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_WP512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_ECB is not set ++CONFIG_CRYPTO_CBC=y ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_XTS is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_AES is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_SEED is not set ++CONFIG_CRYPTO_DEFLATE=m ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_CRC32C is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_TEST is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++CONFIG_CRYPTO_LZO=m ++CONFIG_CRYPTO_HW=y ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=m ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++CONFIG_ZLIB_INFLATE=m ++CONFIG_ZLIB_DEFLATE=m ++CONFIG_LZO_COMPRESS=m ++CONFIG_LZO_DECOMPRESS=m ++CONFIG_PLIST=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y +diff -urN linux-2.6.24.7.old/arch/mips/configs/pavo_defconfig linux-2.6.24.7/arch/mips/configs/pavo_defconfig +--- linux-2.6.24.7.old/arch/mips/configs/pavo_defconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/configs/pavo_defconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1293 @@ ++# ++# Automatically generated make config: don't edit ++# Linux kernel version: 2.6.24.3 ++# Sat Nov 15 16:15:17 2008 ++# ++CONFIG_MIPS=y ++ ++# ++# Machine selection ++# ++# CONFIG_JZ4730_PMP is not set ++CONFIG_JZ4740_PAVO=y ++# CONFIG_JZ4740_LEO is not set ++# CONFIG_JZ4740_LYRA is not set ++# CONFIG_JZ4725_DIPPER is not set ++# CONFIG_JZ4720_VIRGO is not set ++# CONFIG_JZ4750_FUWA is not set ++# CONFIG_JZ4750_APUS is not set ++# CONFIG_MACH_ALCHEMY is not set ++# CONFIG_BASLER_EXCITE is not set ++# CONFIG_BCM47XX is not set ++# CONFIG_MIPS_COBALT is not set ++# CONFIG_MACH_DECSTATION is not set ++# CONFIG_MACH_JAZZ is not set ++# CONFIG_LASAT is not set ++# CONFIG_LEMOTE_FULONG is not set ++# CONFIG_MIPS_ATLAS is not set ++# CONFIG_MIPS_MALTA is not set ++# CONFIG_MIPS_SEAD is not set ++# CONFIG_MIPS_SIM is not set ++# CONFIG_MARKEINS is not set ++# CONFIG_MACH_VR41XX is not set ++# CONFIG_PNX8550_JBS is not set ++# CONFIG_PNX8550_STB810 is not set ++# CONFIG_PMC_MSP is not set ++# CONFIG_PMC_YOSEMITE is not set ++# CONFIG_QEMU is not set ++# CONFIG_SGI_IP22 is not set ++# CONFIG_SGI_IP27 is not set ++# CONFIG_SGI_IP32 is not set ++# CONFIG_SIBYTE_CRHINE is not set ++# CONFIG_SIBYTE_CARMEL is not set ++# CONFIG_SIBYTE_CRHONE is not set ++# CONFIG_SIBYTE_RHONE is not set ++# CONFIG_SIBYTE_SWARM is not set ++# CONFIG_SIBYTE_LITTLESUR is not set ++# CONFIG_SIBYTE_SENTOSA is not set ++# CONFIG_SIBYTE_PTSWARM is not set ++# CONFIG_SIBYTE_BIGSUR is not set ++# CONFIG_SNI_RM is not set ++# CONFIG_TOSHIBA_JMR3927 is not set ++# CONFIG_TOSHIBA_RBTX4927 is not set ++# CONFIG_TOSHIBA_RBTX4938 is not set ++# CONFIG_WR_PPMC is not set ++CONFIG_SOC_JZ4740=y ++CONFIG_JZSOC=y ++CONFIG_JZRISC=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++# CONFIG_ARCH_HAS_ILOG2_U32 is not set ++# CONFIG_ARCH_HAS_ILOG2_U64 is not set ++CONFIG_ARCH_SUPPORTS_OPROFILE=y ++CONFIG_GENERIC_FIND_NEXT_BIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_TIME=y ++CONFIG_GENERIC_CMOS_UPDATE=y ++CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y ++# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set ++CONFIG_DMA_NONCOHERENT=y ++CONFIG_DMA_NEED_PCI_MAP_STATE=y ++# CONFIG_HOTPLUG_CPU is not set ++# CONFIG_NO_IOPORT is not set ++# CONFIG_CPU_BIG_ENDIAN is not set ++CONFIG_CPU_LITTLE_ENDIAN=y ++CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y ++CONFIG_MIPS_L1_CACHE_SHIFT=5 ++ ++# ++# CPU selection ++# ++# CONFIG_CPU_LOONGSON2 is not set ++CONFIG_CPU_MIPS32_R1=y ++# CONFIG_CPU_MIPS32_R2 is not set ++# CONFIG_CPU_MIPS64_R1 is not set ++# CONFIG_CPU_MIPS64_R2 is not set ++# CONFIG_CPU_R3000 is not set ++# CONFIG_CPU_TX39XX is not set ++# CONFIG_CPU_VR41XX is not set ++# CONFIG_CPU_R4300 is not set ++# CONFIG_CPU_R4X00 is not set ++# CONFIG_CPU_TX49XX is not set ++# CONFIG_CPU_R5000 is not set ++# CONFIG_CPU_R5432 is not set ++# CONFIG_CPU_R6000 is not set ++# CONFIG_CPU_NEVADA is not set ++# CONFIG_CPU_R8000 is not set ++# CONFIG_CPU_R10000 is not set ++# CONFIG_CPU_RM7000 is not set ++# CONFIG_CPU_RM9000 is not set ++# CONFIG_CPU_SB1 is not set ++CONFIG_SYS_HAS_CPU_MIPS32_R1=y ++CONFIG_CPU_MIPS32=y ++CONFIG_CPU_MIPSR1=y ++CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y ++CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y ++ ++# ++# Kernel type ++# ++CONFIG_32BIT=y ++# CONFIG_64BIT is not set ++CONFIG_PAGE_SIZE_4KB=y ++# CONFIG_PAGE_SIZE_8KB is not set ++# CONFIG_PAGE_SIZE_16KB is not set ++# CONFIG_PAGE_SIZE_64KB is not set ++CONFIG_CPU_HAS_PREFETCH=y ++CONFIG_MIPS_MT_DISABLED=y ++# CONFIG_MIPS_MT_SMP is not set ++# CONFIG_MIPS_MT_SMTC is not set ++CONFIG_CPU_HAS_LLSC=y ++CONFIG_CPU_HAS_SYNC=y ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_CPU_SUPPORTS_HIGHMEM=y ++CONFIG_ARCH_FLATMEM_ENABLE=y ++CONFIG_ARCH_POPULATES_NODE_MAP=y ++CONFIG_SELECT_MEMORY_MODEL=y ++CONFIG_FLATMEM_MANUAL=y ++# CONFIG_DISCONTIGMEM_MANUAL is not set ++# CONFIG_SPARSEMEM_MANUAL is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++# CONFIG_SPARSEMEM_STATIC is not set ++# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++# CONFIG_RESOURCES_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_TICK_ONESHOT is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++# CONFIG_HZ_48 is not set ++CONFIG_HZ_100=y ++# CONFIG_HZ_128 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_256 is not set ++# CONFIG_HZ_1000 is not set ++# CONFIG_HZ_1024 is not set ++CONFIG_SYS_SUPPORTS_ARBIT_HZ=y ++CONFIG_HZ=100 ++# CONFIG_PREEMPT_NONE is not set ++# CONFIG_PREEMPT_VOLUNTARY is not set ++CONFIG_PREEMPT=y ++CONFIG_PREEMPT_BKL=y ++# CONFIG_KEXEC is not set ++CONFIG_SECCOMP=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++ ++# ++# General setup ++# ++CONFIG_EXPERIMENTAL=y ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_LOCK_KERNEL=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++# CONFIG_USER_NS is not set ++# CONFIG_PID_NS is not set ++# CONFIG_AUDIT is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=14 ++# CONFIG_CGROUPS is not set ++CONFIG_FAIR_GROUP_SCHED=y ++CONFIG_FAIR_USER_SCHED=y ++# CONFIG_FAIR_CGROUP_SCHED is not set ++CONFIG_SYSFS_DEPRECATED=y ++CONFIG_RELAY=y ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_EMBEDDED=y ++CONFIG_SYSCTL_SYSCALL=y ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_EXTRA_PASS is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_ANON_INODES=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLAB=y ++# CONFIG_SLUB is not set ++# CONFIG_SLOB is not set ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++# CONFIG_TINY_SHMEM is not set ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++CONFIG_KMOD=y ++CONFIG_BLOCK=y ++# CONFIG_LBD is not set ++# CONFIG_BLK_DEV_IO_TRACE is not set ++# CONFIG_LSF is not set ++# CONFIG_BLK_DEV_BSG is not set ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_AS=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_AS=y ++# CONFIG_DEFAULT_DEADLINE is not set ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="anticipatory" ++ ++# ++# Bus options (PCI, PCMCIA, EISA, ISA, TC) ++# ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++CONFIG_MMU=y ++# CONFIG_PCCARD is not set ++ ++# ++# Executable file formats ++# ++CONFIG_BINFMT_ELF=y ++# CONFIG_BINFMT_MISC is not set ++CONFIG_TRAD_SIGNALS=y ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ_JZ=y ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_TABLE=y ++# CONFIG_CPU_FREQ_DEBUG is not set ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++ ++# ++# Power management options ++# ++CONFIG_PM=y ++CONFIG_PM_LEGACY=y ++# CONFIG_PM_DEBUG is not set ++CONFIG_SUSPEND_UP_POSSIBLE=y ++# CONFIG_SUSPEND is not set ++ ++# ++# Networking ++# ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_MMAP is not set ++CONFIG_UNIX=y ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_FIB_HASH=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++# CONFIG_INET_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NETFILTER is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_ECONET is not set ++# CONFIG_WAN_ROUTER is not set ++# CONFIG_NET_SCHED is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++ ++# ++# Wireless ++# ++# CONFIG_CFG80211 is not set ++CONFIG_WIRELESS_EXT=y ++# CONFIG_MAC80211 is not set ++# CONFIG_IEEE80211 is not set ++# CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++# CONFIG_FW_LOADER is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_DEBUG is not set ++# CONFIG_MTD_CONCAT is not set ++CONFIG_MTD_PARTITIONS=y ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOC2000 is not set ++# CONFIG_MTD_DOC2001 is not set ++# CONFIG_MTD_DOC2001PLUS is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_VERIFY_WRITE is not set ++# CONFIG_MTD_NAND_ECC_SMC is not set ++# CONFIG_MTD_NAND_MUSEUM_IDS is not set ++CONFIG_MTD_NAND_IDS=y ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_MTD_ALAUDA is not set ++CONFIG_MTD_NAND_JZ4740=y ++# CONFIG_MTD_NAND_CS2 is not set ++# CONFIG_MTD_NAND_CS3 is not set ++# CONFIG_MTD_NAND_CS4 is not set ++CONFIG_MTD_NAND_MULTI_PLANE=y ++# CONFIG_MTD_HW_HM_ECC is not set ++# CONFIG_MTD_SW_HM_ECC is not set ++CONFIG_MTD_HW_RS_ECC=y ++# CONFIG_MTD_HW_BCH_ECC is not set ++# CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE is not set ++CONFIG_MTD_OOB_COPIES=3 ++CONFIG_MTD_BADBLOCK_FLAG_PAGE=127 ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# UBI - Unsorted block images ++# ++CONFIG_MTD_UBI=m ++CONFIG_MTD_UBI_WL_THRESHOLD=256 ++CONFIG_MTD_UBI_BEB_RESERVE=1 ++# CONFIG_MTD_UBI_GLUEBI is not set ++ ++# ++# UBI debugging options ++# ++# CONFIG_MTD_UBI_DEBUG is not set ++CONFIG_MTD_UBI_BLKDEVS=m ++CONFIG_MTD_UBI_BLOCK=m ++# CONFIG_PARPORT is not set ++CONFIG_PNP=y ++# CONFIG_PNP_DEBUG is not set ++ ++# ++# Protocols ++# ++# CONFIG_PNPACPI is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_UB is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=2 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++CONFIG_MISC_DEVICES=y ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_TGT is not set ++# CONFIG_SCSI_NETLINK is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++ ++# ++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs ++# ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++CONFIG_SCSI_WAIT_SCAN=m ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++CONFIG_SCSI_LOWLEVEL=y ++# CONFIG_ISCSI_TCP is not set ++# CONFIG_SCSI_DEBUG is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++CONFIG_NETDEVICES=y ++# CONFIG_NETDEVICES_MULTIQUEUE is not set ++# CONFIG_DUMMY is not set ++# CONFIG_BONDING is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NET_SB1000 is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++CONFIG_NET_ETHERNET=y ++CONFIG_MII=y ++CONFIG_JZCS8900=y ++# CONFIG_AX88796 is not set ++# CONFIG_DM9000 is not set ++# CONFIG_IBM_NEW_EMAC_ZMII is not set ++# CONFIG_IBM_NEW_EMAC_RGMII is not set ++# CONFIG_IBM_NEW_EMAC_TAH is not set ++# CONFIG_IBM_NEW_EMAC_EMAC4 is not set ++# CONFIG_B44 is not set ++# CONFIG_NETDEV_1000 is not set ++# CONFIG_NETDEV_10000 is not set ++ ++# ++# Wireless LAN ++# ++# CONFIG_WLAN_PRE80211 is not set ++# CONFIG_WLAN_80211 is not set ++ ++# ++# USB Network Adapters ++# ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_WAN is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++# CONFIG_SHAPER is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_ISDN is not set ++# CONFIG_PHONE is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++# CONFIG_SERIO_I8042 is not set ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_LIBPS2 is not set ++# CONFIG_SERIO_RAW is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_VT_CONSOLE=y ++CONFIG_HW_CONSOLE=y ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_SERIAL_NONSTANDARD is not set ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_PNP=y ++CONFIG_SERIAL_8250_NR_UARTS=2 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=2 ++# CONFIG_SERIAL_8250_EXTENDED is not set ++ ++# ++# Non-8250 serial port support ++# ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++CONFIG_UNIX98_PTYS=y ++CONFIG_LEGACY_PTYS=y ++CONFIG_LEGACY_PTY_COUNT=2 ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_RTC is not set ++# CONFIG_RTC_PCF8563 is not set ++CONFIG_RTC_JZ=y ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++ ++# ++# JZSOC char device support ++# ++CONFIG_JZCHAR=y ++# CONFIG_JZ_CIM is not set ++# CONFIG_JZ_TPANEL_ATA2508 is not set ++CONFIG_JZ_TPANEL=y ++CONFIG_JZ_SADC=y ++# CONFIG_JZ_TPANEL_AK4182 is not set ++# CONFIG_JZ_TPANEL_UCB1400 is not set ++# CONFIG_JZ_TPANEL_WM9712 is not set ++CONFIG_JZ_UDC_HOTPLUG=y ++CONFIG_JZ_POWEROFF=y ++# CONFIG_JZ_OW is not set ++# CONFIG_I2C is not set ++ ++# ++# SPI support ++# ++# CONFIG_SPI is not set ++# CONFIG_SPI_MASTER is not set ++# CONFIG_W1 is not set ++# CONFIG_POWER_SUPPLY is not set ++# CONFIG_HWMON is not set ++CONFIG_WATCHDOG=y ++# CONFIG_WATCHDOG_NOWAYOUT is not set ++ ++# ++# Watchdog Device Drivers ++# ++CONFIG_JZ_WDT=y ++# CONFIG_SOFT_WATCHDOG is not set ++ ++# ++# USB-based Watchdog Cards ++# ++# CONFIG_USBPCWATCHDOG is not set ++ ++# ++# Sonics Silicon Backplane ++# ++CONFIG_SSB_POSSIBLE=y ++# CONFIG_SSB is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_SM501 is not set ++ ++# ++# Multimedia devices ++# ++CONFIG_VIDEO_DEV=y ++CONFIG_VIDEO_V4L1=y ++CONFIG_VIDEO_V4L1_COMPAT=y ++CONFIG_VIDEO_V4L2=y ++CONFIG_VIDEO_CAPTURE_DRIVERS=y ++# CONFIG_VIDEO_ADV_DEBUG is not set ++CONFIG_VIDEO_HELPER_CHIPS_AUTO=y ++# CONFIG_VIDEO_VIVI is not set ++# CONFIG_VIDEO_CPIA is not set ++# CONFIG_VIDEO_CPIA2 is not set ++CONFIG_VIDEO_JZ_CIM=y ++CONFIG_VIDEO_JZ_SENSOR=y ++CONFIG_V4L_USB_DRIVERS=y ++# CONFIG_USB_VICAM is not set ++# CONFIG_USB_IBMCAM is not set ++# CONFIG_USB_KONICAWC is not set ++# CONFIG_USB_QUICKCAM_MESSENGER is not set ++# CONFIG_USB_ET61X251 is not set ++# CONFIG_USB_OV511 is not set ++# CONFIG_USB_SE401 is not set ++# CONFIG_USB_SN9C102 is not set ++# CONFIG_USB_STV680 is not set ++# CONFIG_USB_ZC0301 is not set ++# CONFIG_USB_PWC is not set ++# CONFIG_USB_ZR364XX is not set ++CONFIG_RADIO_ADAPTERS=y ++# CONFIG_USB_DSBR is not set ++# CONFIG_DVB_CORE is not set ++# CONFIG_DAB is not set ++ ++# ++# Graphics support ++# ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++# CONFIG_FB_DDC is not set ++CONFIG_FB_CFB_FILLRECT=y ++CONFIG_FB_CFB_COPYAREA=y ++CONFIG_FB_CFB_IMAGEBLIT=y ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_SYS_FOPS is not set ++CONFIG_FB_DEFERRED_IO=y ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++CONFIG_FB_JZSOC=y ++# CONFIG_FB_JZ4740_SLCD is not set ++CONFIG_FB_JZLCD_4730_4740=y ++CONFIG_JZLCD_FRAMEBUFFER_MAX=1 ++# CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT is not set ++# CONFIG_JZLCD_SHARP_LQ035Q7 is not set ++# CONFIG_JZLCD_SAMSUNG_LTS350Q1 is not set ++# CONFIG_JZLCD_SAMSUNG_LTV350QVF04 is not set ++# CONFIG_JZLCD_SAMSUNG_LTP400WQF01 is not set ++CONFIG_JZLCD_SAMSUNG_LTP400WQF02=y ++# CONFIG_JZLCD_AUO_A030FL01_V1 is not set ++# CONFIG_JZLCD_TRULY_TFTG320240DTSW is not set ++# CONFIG_JZLCD_TRULY_TFTG320240DTSW_SERIAL is not set ++# CONFIG_JZLCD_TRULY_TFTG240320UTSW_63W_E is not set ++# CONFIG_JZLCD_FOXCONN_PT035TN01 is not set ++# CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL is not set ++# CONFIG_JZLCD_TOSHIBA_LTM084P363 is not set ++# CONFIG_JZLCD_HYNIX_HT10X21 is not set ++# CONFIG_JZLCD_INNOLUX_AT080TN42 is not set ++# CONFIG_JZLCD_CSTN_800x600 is not set ++# CONFIG_JZLCD_CSTN_320x240 is not set ++# CONFIG_JZLCD_MSTN_480x320 is not set ++# CONFIG_JZLCD_MSTN_320x240 is not set ++# CONFIG_JZLCD_MSTN_240x128 is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++ ++# ++# Display device support ++# ++# CONFIG_DISPLAY_SUPPORT is not set ++ ++# ++# Console display driver support ++# ++# CONFIG_VGA_CONSOLE is not set ++CONFIG_DUMMY_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_CURSOR_FLASH is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set ++CONFIG_FONTS=y ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++# CONFIG_FONT_6x11 is not set ++# CONFIG_FONT_7x14 is not set ++# CONFIG_FONT_PEARL_8x8 is not set ++# CONFIG_FONT_ACORN_8x8 is not set ++# CONFIG_FONT_MINI_4x6 is not set ++# CONFIG_FONT_SUN8x16 is not set ++# CONFIG_FONT_SUN12x22 is not set ++# CONFIG_FONT_10x18 is not set ++CONFIG_LOGO=y ++CONFIG_LOGO_LINUX_MONO=y ++CONFIG_LOGO_LINUX_VGA16=y ++CONFIG_LOGO_LINUX_CLUT224=y ++ ++# ++# Sound ++# ++CONFIG_SOUND=y ++ ++# ++# Advanced Linux Sound Architecture ++# ++# CONFIG_SND is not set ++ ++# ++# Open Sound System ++# ++CONFIG_SOUND_PRIME=y ++CONFIG_OSS_OBSOLETE=y ++# CONFIG_SOUND_JZ_AC97 is not set ++CONFIG_SOUND_JZ_I2S=y ++# CONFIG_SOUND_JZ_PCM is not set ++# CONFIG_I2S_AK4642EN is not set ++CONFIG_I2S_ICODEC=y ++# CONFIG_I2S_DLV is not set ++# CONFIG_SOUND_MSNDCLAS is not set ++# CONFIG_SOUND_MSNDPIN is not set ++CONFIG_HID_SUPPORT=y ++CONFIG_HID=y ++# CONFIG_HID_DEBUG is not set ++# CONFIG_HIDRAW is not set ++ ++# ++# USB Input Devices ++# ++CONFIG_USB_HID=y ++# CONFIG_USB_HIDINPUT_POWERBOOK is not set ++# CONFIG_HID_FF is not set ++# CONFIG_USB_HIDDEV is not set ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB_ARCH_HAS_OHCI=y ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++CONFIG_USB=y ++# CONFIG_USB_DEBUG is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEVICEFS is not set ++CONFIG_USB_DEVICE_CLASS=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_SUSPEND is not set ++# CONFIG_USB_PERSIST is not set ++# CONFIG_USB_OTG is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_ISP116X_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set ++# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++ ++# ++# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' ++# ++ ++# ++# may also be needed; see USB_STORAGE Help for more information ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_DPCM is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_LIBUSUAL is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++CONFIG_USB_MON=y ++ ++# ++# USB port drivers ++# ++ ++# ++# USB Serial Converter support ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_AUERSWALD is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_BERRY_CHARGE is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_PHIDGET is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++ ++# ++# USB DSL modem support ++# ++ ++# ++# USB Gadget Support ++# ++CONFIG_USB_GADGET=m ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++# CONFIG_USB_GADGET_DEBUG_FS is not set ++CONFIG_USB_GADGET_SELECTED=y ++CONFIG_USB_GADGET_JZ4740=y ++CONFIG_USB_JZ4740=m ++# CONFIG_USB_GADGET_JZ4730 is not set ++# CONFIG_USB_GADGET_AMD5536UDC is not set ++# CONFIG_USB_GADGET_ATMEL_USBA is not set ++# CONFIG_USB_GADGET_FSL_USB2 is not set ++# CONFIG_USB_GADGET_NET2280 is not set ++# CONFIG_USB_GADGET_PXA2XX is not set ++# CONFIG_USB_GADGET_M66592 is not set ++# CONFIG_USB_GADGET_GOKU is not set ++# CONFIG_USB_GADGET_LH7A40X is not set ++# CONFIG_USB_GADGET_OMAP is not set ++# CONFIG_USB_GADGET_S3C2410 is not set ++# CONFIG_USB_GADGET_AT91 is not set ++# CONFIG_USB_GADGET_DUMMY_HCD is not set ++CONFIG_USB_GADGET_DUALSPEED=y ++# CONFIG_USB_ZERO is not set ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_GADGETFS is not set ++CONFIG_USB_FILE_STORAGE=m ++# CONFIG_USB_FILE_STORAGE_TEST is not set ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_MIDI_GADGET is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_UNSAFE_RESUME is not set ++ ++# ++# MMC/SD Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_SDIO_UART is not set ++ ++# ++# MMC/SD Host Controller Drivers ++# ++CONFIG_MMC_JZ=y ++# CONFIG_JZ_MMC_BUS_1 is not set ++CONFIG_JZ_MMC_BUS_4=y ++# CONFIG_NEW_LEDS is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++ ++# ++# Userspace I/O ++# ++# CONFIG_UIO is not set ++ ++# ++# File systems ++# ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++# CONFIG_EXT2_FS_POSIX_ACL is not set ++# CONFIG_EXT2_FS_SECURITY is not set ++# CONFIG_EXT2_FS_XIP is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4DEV_FS is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++CONFIG_FS_POSIX_ACL=y ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++CONFIG_MINIX_FS=y ++# CONFIG_ROMFS_FS is not set ++CONFIG_INOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_QUOTA is not set ++CONFIG_DNOTIFY=y ++# CONFIG_AUTOFS_FS is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_KCORE=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++ ++# ++# Miscellaneous filesystems ++# ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_JFFS2_FS is not set ++CONFIG_UBIFS_FS=m ++# CONFIG_UBIFS_FS_XATTR is not set ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_UBIFS_FS_DEBUG is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_NFS_DIRECTIO=y ++# CONFIG_NFSD is not set ++CONFIG_ROOT_NFS=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++CONFIG_SUNRPC_GSS=y ++# CONFIG_SUNRPC_BIND34 is not set ++CONFIG_RPCSEC_GSS_KRB5=y ++# CONFIG_RPCSEC_GSS_SPKM3 is not set ++# CONFIG_SMB_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++CONFIG_NLS_CODEPAGE_936=y ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++# CONFIG_DLM is not set ++ ++# ++# Yaffs2 Filesystems ++# ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++# CONFIG_YAFFS_ECC_BCH is not set ++CONFIG_YAFFS_ECC_RS=y ++# CONFIG_YAFFS_ECC_HAMMING is not set ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set ++# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set ++CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK=y ++CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y ++CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS=10 ++CONFIG_INSTRUMENTATION=y ++# CONFIG_PROFILING is not set ++# CONFIG_MARKERS is not set ++ ++# ++# Kernel hacking ++# ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++# CONFIG_PRINTK_TIME is not set ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_UNUSED_SYMBOLS is not set ++CONFIG_DEBUG_FS=y ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_KERNEL is not set ++# CONFIG_SAMPLES is not set ++CONFIG_CMDLINE="" ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITY_FILE_CAPABILITIES is not set ++CONFIG_CRYPTO=y ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_MANAGER=y ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_MD4 is not set ++CONFIG_CRYPTO_MD5=y ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_WP512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_ECB is not set ++CONFIG_CRYPTO_CBC=y ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_XTS is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_AES is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_SEED is not set ++CONFIG_CRYPTO_DEFLATE=m ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_CRC32C is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_TEST is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++CONFIG_CRYPTO_LZO=m ++CONFIG_CRYPTO_HW=y ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=m ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++CONFIG_ZLIB_INFLATE=m ++CONFIG_ZLIB_DEFLATE=m ++CONFIG_LZO_COMPRESS=m ++CONFIG_LZO_DECOMPRESS=m ++CONFIG_REED_SOLOMON=y ++CONFIG_REED_SOLOMON_ENC8=y ++CONFIG_REED_SOLOMON_DEC8=y ++CONFIG_PLIST=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y +diff -urN linux-2.6.24.7.old/arch/mips/configs/pmp_defconfig linux-2.6.24.7/arch/mips/configs/pmp_defconfig +--- linux-2.6.24.7.old/arch/mips/configs/pmp_defconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/configs/pmp_defconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1212 @@ ++# ++# Automatically generated make config: don't edit ++# Linux kernel version: 2.6.24.3 ++# Thu Jun 12 13:37:10 2008 ++# ++CONFIG_MIPS=y ++ ++# ++# Machine selection ++# ++CONFIG_JZ4730_PMP=y ++# CONFIG_JZ4740_PAVO is not set ++# CONFIG_JZ4740_LEO is not set ++# CONFIG_JZ4740_LYRA is not set ++# CONFIG_JZ4725_DIPPER is not set ++# CONFIG_JZ4720_VIRGO is not set ++# CONFIG_JZ4750_FUWA is not set ++# CONFIG_MACH_ALCHEMY is not set ++# CONFIG_BASLER_EXCITE is not set ++# CONFIG_BCM47XX is not set ++# CONFIG_MIPS_COBALT is not set ++# CONFIG_MACH_DECSTATION is not set ++# CONFIG_MACH_JAZZ is not set ++# CONFIG_LASAT is not set ++# CONFIG_LEMOTE_FULONG is not set ++# CONFIG_MIPS_ATLAS is not set ++# CONFIG_MIPS_MALTA is not set ++# CONFIG_MIPS_SEAD is not set ++# CONFIG_MIPS_SIM is not set ++# CONFIG_MARKEINS is not set ++# CONFIG_MACH_VR41XX is not set ++# CONFIG_PNX8550_JBS is not set ++# CONFIG_PNX8550_STB810 is not set ++# CONFIG_PMC_MSP is not set ++# CONFIG_PMC_YOSEMITE is not set ++# CONFIG_QEMU is not set ++# CONFIG_SGI_IP22 is not set ++# CONFIG_SGI_IP27 is not set ++# CONFIG_SGI_IP32 is not set ++# CONFIG_SIBYTE_CRHINE is not set ++# CONFIG_SIBYTE_CARMEL is not set ++# CONFIG_SIBYTE_CRHONE is not set ++# CONFIG_SIBYTE_RHONE is not set ++# CONFIG_SIBYTE_SWARM is not set ++# CONFIG_SIBYTE_LITTLESUR is not set ++# CONFIG_SIBYTE_SENTOSA is not set ++# CONFIG_SIBYTE_PTSWARM is not set ++# CONFIG_SIBYTE_BIGSUR is not set ++# CONFIG_SNI_RM is not set ++# CONFIG_TOSHIBA_JMR3927 is not set ++# CONFIG_TOSHIBA_RBTX4927 is not set ++# CONFIG_TOSHIBA_RBTX4938 is not set ++# CONFIG_WR_PPMC is not set ++CONFIG_SOC_JZ4730=y ++CONFIG_JZSOC=y ++CONFIG_JZRISC=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++# CONFIG_ARCH_HAS_ILOG2_U32 is not set ++# CONFIG_ARCH_HAS_ILOG2_U64 is not set ++CONFIG_ARCH_SUPPORTS_OPROFILE=y ++CONFIG_GENERIC_FIND_NEXT_BIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_TIME=y ++CONFIG_GENERIC_CMOS_UPDATE=y ++CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y ++# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set ++CONFIG_DMA_NONCOHERENT=y ++CONFIG_DMA_NEED_PCI_MAP_STATE=y ++# CONFIG_HOTPLUG_CPU is not set ++# CONFIG_NO_IOPORT is not set ++# CONFIG_CPU_BIG_ENDIAN is not set ++CONFIG_CPU_LITTLE_ENDIAN=y ++CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y ++CONFIG_MIPS_L1_CACHE_SHIFT=5 ++ ++# ++# CPU selection ++# ++# CONFIG_CPU_LOONGSON2 is not set ++CONFIG_CPU_MIPS32_R1=y ++# CONFIG_CPU_MIPS32_R2 is not set ++# CONFIG_CPU_MIPS64_R1 is not set ++# CONFIG_CPU_MIPS64_R2 is not set ++# CONFIG_CPU_R3000 is not set ++# CONFIG_CPU_TX39XX is not set ++# CONFIG_CPU_VR41XX is not set ++# CONFIG_CPU_R4300 is not set ++# CONFIG_CPU_R4X00 is not set ++# CONFIG_CPU_TX49XX is not set ++# CONFIG_CPU_R5000 is not set ++# CONFIG_CPU_R5432 is not set ++# CONFIG_CPU_R6000 is not set ++# CONFIG_CPU_NEVADA is not set ++# CONFIG_CPU_R8000 is not set ++# CONFIG_CPU_R10000 is not set ++# CONFIG_CPU_RM7000 is not set ++# CONFIG_CPU_RM9000 is not set ++# CONFIG_CPU_SB1 is not set ++CONFIG_SYS_HAS_CPU_MIPS32_R1=y ++CONFIG_CPU_MIPS32=y ++CONFIG_CPU_MIPSR1=y ++CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y ++CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y ++ ++# ++# Kernel type ++# ++CONFIG_32BIT=y ++# CONFIG_64BIT is not set ++CONFIG_PAGE_SIZE_4KB=y ++# CONFIG_PAGE_SIZE_8KB is not set ++# CONFIG_PAGE_SIZE_16KB is not set ++# CONFIG_PAGE_SIZE_64KB is not set ++CONFIG_CPU_HAS_PREFETCH=y ++CONFIG_MIPS_MT_DISABLED=y ++# CONFIG_MIPS_MT_SMP is not set ++# CONFIG_MIPS_MT_SMTC is not set ++CONFIG_CPU_HAS_LLSC=y ++CONFIG_CPU_HAS_SYNC=y ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_CPU_SUPPORTS_HIGHMEM=y ++CONFIG_ARCH_FLATMEM_ENABLE=y ++CONFIG_ARCH_POPULATES_NODE_MAP=y ++CONFIG_SELECT_MEMORY_MODEL=y ++CONFIG_FLATMEM_MANUAL=y ++# CONFIG_DISCONTIGMEM_MANUAL is not set ++# CONFIG_SPARSEMEM_MANUAL is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++# CONFIG_SPARSEMEM_STATIC is not set ++# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++# CONFIG_RESOURCES_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_TICK_ONESHOT is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++# CONFIG_HZ_48 is not set ++CONFIG_HZ_100=y ++# CONFIG_HZ_128 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_256 is not set ++# CONFIG_HZ_1000 is not set ++# CONFIG_HZ_1024 is not set ++CONFIG_SYS_SUPPORTS_ARBIT_HZ=y ++CONFIG_HZ=100 ++# CONFIG_PREEMPT_NONE is not set ++# CONFIG_PREEMPT_VOLUNTARY is not set ++CONFIG_PREEMPT=y ++CONFIG_PREEMPT_BKL=y ++# CONFIG_KEXEC is not set ++CONFIG_SECCOMP=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++ ++# ++# General setup ++# ++CONFIG_EXPERIMENTAL=y ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_LOCK_KERNEL=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++# CONFIG_USER_NS is not set ++# CONFIG_PID_NS is not set ++# CONFIG_AUDIT is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=14 ++# CONFIG_CGROUPS is not set ++CONFIG_FAIR_GROUP_SCHED=y ++CONFIG_FAIR_USER_SCHED=y ++# CONFIG_FAIR_CGROUP_SCHED is not set ++CONFIG_SYSFS_DEPRECATED=y ++CONFIG_RELAY=y ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_EMBEDDED=y ++CONFIG_SYSCTL_SYSCALL=y ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_EXTRA_PASS is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_ANON_INODES=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLAB=y ++# CONFIG_SLUB is not set ++# CONFIG_SLOB is not set ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++# CONFIG_TINY_SHMEM is not set ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++CONFIG_KMOD=y ++CONFIG_BLOCK=y ++# CONFIG_LBD is not set ++# CONFIG_BLK_DEV_IO_TRACE is not set ++# CONFIG_LSF is not set ++# CONFIG_BLK_DEV_BSG is not set ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_AS=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_AS=y ++# CONFIG_DEFAULT_DEADLINE is not set ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="anticipatory" ++ ++# ++# Bus options (PCI, PCMCIA, EISA, ISA, TC) ++# ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++CONFIG_MMU=y ++# CONFIG_PCCARD is not set ++ ++# ++# Executable file formats ++# ++CONFIG_BINFMT_ELF=y ++# CONFIG_BINFMT_MISC is not set ++CONFIG_TRAD_SIGNALS=y ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ_JZ=y ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_TABLE=y ++# CONFIG_CPU_FREQ_DEBUG is not set ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++ ++# ++# Power management options ++# ++CONFIG_PM=y ++CONFIG_PM_LEGACY=y ++# CONFIG_PM_DEBUG is not set ++CONFIG_PM_SLEEP=y ++CONFIG_SUSPEND_UP_POSSIBLE=y ++CONFIG_SUSPEND=y ++ ++# ++# Networking ++# ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_MMAP is not set ++CONFIG_UNIX=y ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_FIB_HASH=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++# CONFIG_INET_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NETFILTER is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_ECONET is not set ++# CONFIG_WAN_ROUTER is not set ++# CONFIG_NET_SCHED is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++ ++# ++# Wireless ++# ++# CONFIG_CFG80211 is not set ++# CONFIG_WIRELESS_EXT is not set ++# CONFIG_MAC80211 is not set ++# CONFIG_IEEE80211 is not set ++# CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++# CONFIG_FW_LOADER is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_DEBUG is not set ++CONFIG_MTD_CONCAT=y ++CONFIG_MTD_PARTITIONS=y ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOC2000 is not set ++# CONFIG_MTD_DOC2001 is not set ++# CONFIG_MTD_DOC2001PLUS is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_VERIFY_WRITE is not set ++# CONFIG_MTD_NAND_ECC_SMC is not set ++# CONFIG_MTD_NAND_MUSEUM_IDS is not set ++CONFIG_MTD_NAND_IDS=y ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_MTD_ALAUDA is not set ++CONFIG_MTD_NAND_JZ4730=y ++CONFIG_MTD_HW_HM_ECC=y ++# CONFIG_MTD_SW_HM_ECC is not set ++# CONFIG_MTD_HW_RS_ECC is not set ++# CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE is not set ++CONFIG_MTD_OOB_COPIES=3 ++CONFIG_MTD_BADBLOCK_FLAG_PAGE=0 ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# UBI - Unsorted block images ++# ++# CONFIG_MTD_UBI is not set ++# CONFIG_MTD_UBI_BLKDEVS is not set ++# CONFIG_PARPORT is not set ++# CONFIG_PNP is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_UB is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++CONFIG_MISC_DEVICES=y ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_TGT is not set ++# CONFIG_SCSI_NETLINK is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++ ++# ++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs ++# ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++CONFIG_SCSI_WAIT_SCAN=m ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++CONFIG_SCSI_LOWLEVEL=y ++# CONFIG_ISCSI_TCP is not set ++# CONFIG_SCSI_DEBUG is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++CONFIG_NETDEVICES=y ++# CONFIG_NETDEVICES_MULTIQUEUE is not set ++# CONFIG_DUMMY is not set ++# CONFIG_BONDING is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++CONFIG_NET_ETHERNET=y ++CONFIG_MII=y ++CONFIG_JZ_ETH=y ++# CONFIG_AX88796 is not set ++# CONFIG_DM9000 is not set ++# CONFIG_IBM_NEW_EMAC_ZMII is not set ++# CONFIG_IBM_NEW_EMAC_RGMII is not set ++# CONFIG_IBM_NEW_EMAC_TAH is not set ++# CONFIG_IBM_NEW_EMAC_EMAC4 is not set ++# CONFIG_B44 is not set ++# CONFIG_NETDEV_1000 is not set ++# CONFIG_NETDEV_10000 is not set ++ ++# ++# Wireless LAN ++# ++# CONFIG_WLAN_PRE80211 is not set ++# CONFIG_WLAN_80211 is not set ++ ++# ++# USB Network Adapters ++# ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_WAN is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++# CONFIG_SHAPER is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_ISDN is not set ++# CONFIG_PHONE is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++# CONFIG_SERIO_I8042 is not set ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_LIBPS2 is not set ++# CONFIG_SERIO_RAW is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_VT_CONSOLE=y ++CONFIG_HW_CONSOLE=y ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_SERIAL_NONSTANDARD is not set ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_NR_UARTS=4 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=4 ++# CONFIG_SERIAL_8250_EXTENDED is not set ++ ++# ++# Non-8250 serial port support ++# ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++CONFIG_UNIX98_PTYS=y ++CONFIG_LEGACY_PTYS=y ++CONFIG_LEGACY_PTY_COUNT=256 ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_RTC is not set ++CONFIG_RTC_PCF8563=y ++# CONFIG_RTC_JZ is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++ ++# ++# JZSOC char device support ++# ++CONFIG_JZCHAR=y ++# CONFIG_JZ_CIM is not set ++# CONFIG_JZ_TPANEL_ATA2508 is not set ++CONFIG_JZ_TPANEL=y ++# CONFIG_JZ_SADC is not set ++CONFIG_JZ_TPANEL_AK4182=y ++# CONFIG_JZ_TPANEL_UCB1400 is not set ++# CONFIG_JZ_TPANEL_WM9712 is not set ++# CONFIG_JZ_UDC_HOTPLUG is not set ++CONFIG_JZ_POWEROFF=y ++# CONFIG_JZ_OW is not set ++# CONFIG_I2C is not set ++ ++# ++# SPI support ++# ++# CONFIG_SPI is not set ++# CONFIG_SPI_MASTER is not set ++# CONFIG_W1 is not set ++# CONFIG_POWER_SUPPLY is not set ++# CONFIG_HWMON is not set ++CONFIG_WATCHDOG=y ++# CONFIG_WATCHDOG_NOWAYOUT is not set ++ ++# ++# Watchdog Device Drivers ++# ++CONFIG_JZ_WDT=y ++# CONFIG_SOFT_WATCHDOG is not set ++ ++# ++# USB-based Watchdog Cards ++# ++# CONFIG_USBPCWATCHDOG is not set ++ ++# ++# Sonics Silicon Backplane ++# ++CONFIG_SSB_POSSIBLE=y ++# CONFIG_SSB is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_SM501 is not set ++ ++# ++# Multimedia devices ++# ++CONFIG_VIDEO_DEV=y ++# CONFIG_VIDEO_V4L1 is not set ++# CONFIG_VIDEO_V4L1_COMPAT is not set ++CONFIG_VIDEO_V4L2=y ++CONFIG_VIDEO_CAPTURE_DRIVERS=y ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set ++ ++# ++# Encoders/decoders and other helper chips ++# ++ ++# ++# Audio decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# MPEG video encoders ++# ++# CONFIG_VIDEO_CX2341X is not set ++ ++# ++# Video encoders ++# ++ ++# ++# Video improvement chips ++# ++# CONFIG_VIDEO_VIVI is not set ++CONFIG_VIDEO_JZ_CIM=m ++CONFIG_VIDEO_JZ_SENSOR=m ++# CONFIG_V4L_USB_DRIVERS is not set ++CONFIG_RADIO_ADAPTERS=y ++# CONFIG_USB_DSBR is not set ++# CONFIG_DVB_CORE is not set ++# CONFIG_DAB is not set ++ ++# ++# Graphics support ++# ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++# CONFIG_FB_DDC is not set ++CONFIG_FB_CFB_FILLRECT=y ++CONFIG_FB_CFB_COPYAREA=y ++CONFIG_FB_CFB_IMAGEBLIT=y ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_SYS_FOPS is not set ++CONFIG_FB_DEFERRED_IO=y ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++CONFIG_FB_JZSOC=y ++CONFIG_FB_JZLCD_4730_4740=y ++CONFIG_JZLCD_FRAMEBUFFER_MAX=1 ++# CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT is not set ++# CONFIG_JZLCD_SHARP_LQ035Q7 is not set ++# CONFIG_JZLCD_SAMSUNG_LTS350Q1 is not set ++# CONFIG_JZLCD_SAMSUNG_LTV350QVF04 is not set ++CONFIG_JZLCD_SAMSUNG_LTP400WQF01=y ++# CONFIG_JZLCD_SAMSUNG_LTP400WQF02 is not set ++# CONFIG_JZLCD_AUO_A030FL01_V1 is not set ++# CONFIG_JZLCD_TRULY_TFTG320240DTSW is not set ++# CONFIG_JZLCD_TRULY_TFTG320240DTSW_SERIAL is not set ++# CONFIG_JZLCD_TRULY_TFTG240320UTSW_63W_E is not set ++# CONFIG_JZLCD_FOXCONN_PT035TN01 is not set ++# CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL is not set ++# CONFIG_JZLCD_TOSHIBA_LTM084P363 is not set ++# CONFIG_JZLCD_HYNIX_HT10X21 is not set ++# CONFIG_JZLCD_INNOLUX_AT080TN42 is not set ++# CONFIG_JZLCD_CSTN_800x600 is not set ++# CONFIG_JZLCD_CSTN_320x240 is not set ++# CONFIG_JZLCD_MSTN_480x320 is not set ++# CONFIG_JZLCD_MSTN_320x240 is not set ++# CONFIG_JZLCD_MSTN_240x128 is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++ ++# ++# Display device support ++# ++# CONFIG_DISPLAY_SUPPORT is not set ++ ++# ++# Console display driver support ++# ++# CONFIG_VGA_CONSOLE is not set ++CONFIG_DUMMY_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_CURSOR_FLASH is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set ++CONFIG_FONTS=y ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++# CONFIG_FONT_6x11 is not set ++# CONFIG_FONT_7x14 is not set ++# CONFIG_FONT_PEARL_8x8 is not set ++# CONFIG_FONT_ACORN_8x8 is not set ++# CONFIG_FONT_MINI_4x6 is not set ++# CONFIG_FONT_SUN8x16 is not set ++# CONFIG_FONT_SUN12x22 is not set ++# CONFIG_FONT_10x18 is not set ++CONFIG_LOGO=y ++CONFIG_LOGO_LINUX_MONO=y ++CONFIG_LOGO_LINUX_VGA16=y ++CONFIG_LOGO_LINUX_CLUT224=y ++ ++# ++# Sound ++# ++# CONFIG_SOUND is not set ++CONFIG_HID_SUPPORT=y ++CONFIG_HID=y ++# CONFIG_HID_DEBUG is not set ++# CONFIG_HIDRAW is not set ++ ++# ++# USB Input Devices ++# ++CONFIG_USB_HID=y ++# CONFIG_USB_HIDINPUT_POWERBOOK is not set ++# CONFIG_HID_FF is not set ++CONFIG_USB_HIDDEV=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB_ARCH_HAS_OHCI=y ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++CONFIG_USB=y ++# CONFIG_USB_DEBUG is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEVICEFS=y ++# CONFIG_USB_DEVICE_CLASS is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_SUSPEND is not set ++# CONFIG_USB_PERSIST is not set ++# CONFIG_USB_OTG is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_ISP116X_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set ++# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++ ++# ++# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' ++# ++ ++# ++# may also be needed; see USB_STORAGE Help for more information ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_DPCM is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_LIBUSUAL is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USB_MON is not set ++ ++# ++# USB port drivers ++# ++ ++# ++# USB Serial Converter support ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_AUERSWALD is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_BERRY_CHARGE is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_PHIDGET is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++ ++# ++# USB DSL modem support ++# ++ ++# ++# USB Gadget Support ++# ++CONFIG_USB_GADGET=m ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_SELECTED=y ++# CONFIG_USB_GADGET_JZ4740 is not set ++CONFIG_USB_GADGET_JZ4730=y ++CONFIG_USB_JZ4730=m ++# CONFIG_USB_GADGET_AMD5536UDC is not set ++# CONFIG_USB_GADGET_ATMEL_USBA is not set ++# CONFIG_USB_GADGET_FSL_USB2 is not set ++# CONFIG_USB_GADGET_NET2280 is not set ++# CONFIG_USB_GADGET_PXA2XX is not set ++# CONFIG_USB_GADGET_M66592 is not set ++# CONFIG_USB_GADGET_GOKU is not set ++# CONFIG_USB_GADGET_LH7A40X is not set ++# CONFIG_USB_GADGET_OMAP is not set ++# CONFIG_USB_GADGET_S3C2410 is not set ++# CONFIG_USB_GADGET_AT91 is not set ++# CONFIG_USB_GADGET_DUMMY_HCD is not set ++# CONFIG_USB_GADGET_DUALSPEED is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_ETH=m ++CONFIG_USB_ETH_RNDIS=y ++# CONFIG_USB_GADGETFS is not set ++CONFIG_USB_FILE_STORAGE=m ++# CONFIG_USB_FILE_STORAGE_TEST is not set ++CONFIG_USB_G_SERIAL=m ++# CONFIG_USB_MIDI_GADGET is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_UNSAFE_RESUME is not set ++ ++# ++# MMC/SD Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_SDIO_UART is not set ++ ++# ++# MMC/SD Host Controller Drivers ++# ++CONFIG_MMC_JZ=y ++CONFIG_JZ_MMC_BUS_4=y ++# CONFIG_JZ_MMC_BUS_1 is not set ++# CONFIG_NEW_LEDS is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++ ++# ++# Userspace I/O ++# ++# CONFIG_UIO is not set ++ ++# ++# File systems ++# ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++# CONFIG_EXT2_FS_POSIX_ACL is not set ++# CONFIG_EXT2_FS_SECURITY is not set ++# CONFIG_EXT2_FS_XIP is not set ++CONFIG_EXT3_FS=y ++CONFIG_EXT3_FS_XATTR=y ++# CONFIG_EXT3_FS_POSIX_ACL is not set ++# CONFIG_EXT3_FS_SECURITY is not set ++# CONFIG_EXT4DEV_FS is not set ++CONFIG_JBD=y ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_FS_POSIX_ACL is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++CONFIG_MINIX_FS=y ++# CONFIG_ROMFS_FS is not set ++CONFIG_INOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_QUOTA is not set ++CONFIG_DNOTIFY=y ++# CONFIG_AUTOFS_FS is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_KCORE=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++ ++# ++# Miscellaneous filesystems ++# ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_JFFS2_FS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++# CONFIG_NFS_V3_ACL is not set ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_DIRECTIO is not set ++# CONFIG_NFSD is not set ++CONFIG_ROOT_NFS=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_BIND34 is not set ++# CONFIG_RPCSEC_GSS_KRB5 is not set ++# CONFIG_RPCSEC_GSS_SPKM3 is not set ++# CONFIG_SMB_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++CONFIG_BSD_DISKLABEL=y ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++CONFIG_NLS_CODEPAGE_936=y ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++# CONFIG_DLM is not set ++ ++# ++# Yaffs2 Filesystems ++# ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set ++# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set ++CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK=y ++CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y ++CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS=10 ++CONFIG_INSTRUMENTATION=y ++# CONFIG_PROFILING is not set ++# CONFIG_MARKERS is not set ++ ++# ++# Kernel hacking ++# ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++# CONFIG_PRINTK_TIME is not set ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_KERNEL is not set ++# CONFIG_SAMPLES is not set ++CONFIG_CMDLINE="" ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITY_FILE_CAPABILITIES is not set ++# CONFIG_CRYPTO is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++# CONFIG_CRC_CCITT is not set ++# CONFIG_CRC16 is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++CONFIG_PLIST=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y +diff -urN linux-2.6.24.7.old/arch/mips/configs/slt50_defconfig linux-2.6.24.7/arch/mips/configs/slt50_defconfig +--- linux-2.6.24.7.old/arch/mips/configs/slt50_defconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/configs/slt50_defconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1036 @@ ++# ++# Automatically generated make config: don't edit ++# Linux kernel version: 2.6.24.3 ++# Tue Nov 25 09:38:56 2008 ++# ++CONFIG_MIPS=y ++ ++# ++# Machine selection ++# ++# CONFIG_JZ4730_PMP is not set ++# CONFIG_JZ4740_PAVO is not set ++# CONFIG_JZ4740_LEO is not set ++# CONFIG_JZ4740_LYRA is not set ++# CONFIG_JZ4725_DIPPER is not set ++# CONFIG_JZ4720_VIRGO is not set ++# CONFIG_JZ4750_FUWA is not set ++# CONFIG_JZ4750_APUS is not set ++CONFIG_JZ4750_SLT50=y ++# CONFIG_MACH_ALCHEMY is not set ++# CONFIG_BASLER_EXCITE is not set ++# CONFIG_BCM47XX is not set ++# CONFIG_MIPS_COBALT is not set ++# CONFIG_MACH_DECSTATION is not set ++# CONFIG_MACH_JAZZ is not set ++# CONFIG_LASAT is not set ++# CONFIG_LEMOTE_FULONG is not set ++# CONFIG_MIPS_ATLAS is not set ++# CONFIG_MIPS_MALTA is not set ++# CONFIG_MIPS_SEAD is not set ++# CONFIG_MIPS_SIM is not set ++# CONFIG_MARKEINS is not set ++# CONFIG_MACH_VR41XX is not set ++# CONFIG_PNX8550_JBS is not set ++# CONFIG_PNX8550_STB810 is not set ++# CONFIG_PMC_MSP is not set ++# CONFIG_PMC_YOSEMITE is not set ++# CONFIG_QEMU is not set ++# CONFIG_SGI_IP22 is not set ++# CONFIG_SGI_IP27 is not set ++# CONFIG_SGI_IP32 is not set ++# CONFIG_SIBYTE_CRHINE is not set ++# CONFIG_SIBYTE_CARMEL is not set ++# CONFIG_SIBYTE_CRHONE is not set ++# CONFIG_SIBYTE_RHONE is not set ++# CONFIG_SIBYTE_SWARM is not set ++# CONFIG_SIBYTE_LITTLESUR is not set ++# CONFIG_SIBYTE_SENTOSA is not set ++# CONFIG_SIBYTE_PTSWARM is not set ++# CONFIG_SIBYTE_BIGSUR is not set ++# CONFIG_SNI_RM is not set ++# CONFIG_TOSHIBA_JMR3927 is not set ++# CONFIG_TOSHIBA_RBTX4927 is not set ++# CONFIG_TOSHIBA_RBTX4938 is not set ++# CONFIG_WR_PPMC is not set ++CONFIG_SOC_JZ4750=y ++CONFIG_JZSOC=y ++CONFIG_JZRISC=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++# CONFIG_ARCH_HAS_ILOG2_U32 is not set ++# CONFIG_ARCH_HAS_ILOG2_U64 is not set ++CONFIG_ARCH_SUPPORTS_OPROFILE=y ++CONFIG_GENERIC_FIND_NEXT_BIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_TIME=y ++CONFIG_GENERIC_CMOS_UPDATE=y ++CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y ++# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set ++CONFIG_DMA_NONCOHERENT=y ++CONFIG_DMA_NEED_PCI_MAP_STATE=y ++# CONFIG_HOTPLUG_CPU is not set ++# CONFIG_NO_IOPORT is not set ++# CONFIG_CPU_BIG_ENDIAN is not set ++CONFIG_CPU_LITTLE_ENDIAN=y ++CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y ++CONFIG_MIPS_L1_CACHE_SHIFT=5 ++ ++# ++# CPU selection ++# ++# CONFIG_CPU_LOONGSON2 is not set ++CONFIG_CPU_MIPS32_R1=y ++# CONFIG_CPU_MIPS32_R2 is not set ++# CONFIG_CPU_MIPS64_R1 is not set ++# CONFIG_CPU_MIPS64_R2 is not set ++# CONFIG_CPU_R3000 is not set ++# CONFIG_CPU_TX39XX is not set ++# CONFIG_CPU_VR41XX is not set ++# CONFIG_CPU_R4300 is not set ++# CONFIG_CPU_R4X00 is not set ++# CONFIG_CPU_TX49XX is not set ++# CONFIG_CPU_R5000 is not set ++# CONFIG_CPU_R5432 is not set ++# CONFIG_CPU_R6000 is not set ++# CONFIG_CPU_NEVADA is not set ++# CONFIG_CPU_R8000 is not set ++# CONFIG_CPU_R10000 is not set ++# CONFIG_CPU_RM7000 is not set ++# CONFIG_CPU_RM9000 is not set ++# CONFIG_CPU_SB1 is not set ++CONFIG_SYS_HAS_CPU_MIPS32_R1=y ++CONFIG_CPU_MIPS32=y ++CONFIG_CPU_MIPSR1=y ++CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y ++CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y ++ ++# ++# Kernel type ++# ++CONFIG_32BIT=y ++# CONFIG_64BIT is not set ++CONFIG_PAGE_SIZE_4KB=y ++# CONFIG_PAGE_SIZE_8KB is not set ++# CONFIG_PAGE_SIZE_16KB is not set ++# CONFIG_PAGE_SIZE_64KB is not set ++CONFIG_CPU_HAS_PREFETCH=y ++CONFIG_MIPS_MT_DISABLED=y ++# CONFIG_MIPS_MT_SMP is not set ++# CONFIG_MIPS_MT_SMTC is not set ++CONFIG_CPU_HAS_LLSC=y ++CONFIG_CPU_HAS_SYNC=y ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_CPU_SUPPORTS_HIGHMEM=y ++CONFIG_ARCH_FLATMEM_ENABLE=y ++CONFIG_ARCH_POPULATES_NODE_MAP=y ++CONFIG_SELECT_MEMORY_MODEL=y ++CONFIG_FLATMEM_MANUAL=y ++# CONFIG_DISCONTIGMEM_MANUAL is not set ++# CONFIG_SPARSEMEM_MANUAL is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++# CONFIG_SPARSEMEM_STATIC is not set ++# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++# CONFIG_RESOURCES_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_TICK_ONESHOT is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++# CONFIG_HZ_48 is not set ++CONFIG_HZ_100=y ++# CONFIG_HZ_128 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_256 is not set ++# CONFIG_HZ_1000 is not set ++# CONFIG_HZ_1024 is not set ++CONFIG_SYS_SUPPORTS_ARBIT_HZ=y ++CONFIG_HZ=100 ++# CONFIG_PREEMPT_NONE is not set ++# CONFIG_PREEMPT_VOLUNTARY is not set ++CONFIG_PREEMPT=y ++CONFIG_PREEMPT_BKL=y ++# CONFIG_KEXEC is not set ++CONFIG_SECCOMP=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++ ++# ++# General setup ++# ++CONFIG_EXPERIMENTAL=y ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_LOCK_KERNEL=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++# CONFIG_USER_NS is not set ++# CONFIG_PID_NS is not set ++# CONFIG_AUDIT is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=14 ++# CONFIG_CGROUPS is not set ++CONFIG_FAIR_GROUP_SCHED=y ++CONFIG_FAIR_USER_SCHED=y ++# CONFIG_FAIR_CGROUP_SCHED is not set ++CONFIG_SYSFS_DEPRECATED=y ++CONFIG_RELAY=y ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_EMBEDDED=y ++CONFIG_SYSCTL_SYSCALL=y ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_EXTRA_PASS is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_ANON_INODES=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLAB=y ++# CONFIG_SLUB is not set ++# CONFIG_SLOB is not set ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++# CONFIG_TINY_SHMEM is not set ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++CONFIG_KMOD=y ++CONFIG_BLOCK=y ++# CONFIG_LBD is not set ++# CONFIG_BLK_DEV_IO_TRACE is not set ++# CONFIG_LSF is not set ++# CONFIG_BLK_DEV_BSG is not set ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_AS=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_AS=y ++# CONFIG_DEFAULT_DEADLINE is not set ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="anticipatory" ++ ++# ++# Bus options (PCI, PCMCIA, EISA, ISA, TC) ++# ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++CONFIG_MMU=y ++# CONFIG_PCCARD is not set ++ ++# ++# Executable file formats ++# ++CONFIG_BINFMT_ELF=y ++# CONFIG_BINFMT_MISC is not set ++CONFIG_TRAD_SIGNALS=y ++ ++# ++# CPU Frequency scaling ++# ++# CONFIG_CPU_FREQ_JZ is not set ++ ++# ++# Power management options ++# ++# CONFIG_PM is not set ++CONFIG_SUSPEND_UP_POSSIBLE=y ++ ++# ++# Networking ++# ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_MMAP is not set ++CONFIG_UNIX=y ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_FIB_HASH=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++CONFIG_INET_DIAG=m ++CONFIG_INET_TCP_DIAG=m ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NETFILTER is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_ECONET is not set ++# CONFIG_WAN_ROUTER is not set ++# CONFIG_NET_SCHED is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++ ++# ++# Wireless ++# ++# CONFIG_CFG80211 is not set ++CONFIG_WIRELESS_EXT=y ++# CONFIG_MAC80211 is not set ++# CONFIG_IEEE80211 is not set ++# CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++# CONFIG_FW_LOADER is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_DEBUG is not set ++# CONFIG_MTD_CONCAT is not set ++CONFIG_MTD_PARTITIONS=y ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOC2000 is not set ++# CONFIG_MTD_DOC2001 is not set ++# CONFIG_MTD_DOC2001PLUS is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_VERIFY_WRITE is not set ++# CONFIG_MTD_NAND_ECC_SMC is not set ++# CONFIG_MTD_NAND_MUSEUM_IDS is not set ++CONFIG_MTD_NAND_IDS=y ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_MTD_ALAUDA is not set ++CONFIG_MTD_NAND_JZ4750=y ++# CONFIG_MTD_NAND_CS2 is not set ++# CONFIG_MTD_NAND_CS3 is not set ++# CONFIG_MTD_NAND_CS4 is not set ++# CONFIG_MTD_NAND_MULTI_PLANE is not set ++# CONFIG_MTD_HW_HM_ECC is not set ++# CONFIG_MTD_SW_HM_ECC is not set ++# CONFIG_MTD_HW_RS_ECC is not set ++CONFIG_MTD_HW_BCH_ECC=y ++CONFIG_MTD_HW_BCH_4BIT=y ++# CONFIG_MTD_HW_BCH_8BIT is not set ++CONFIG_MTD_NAND_DMA=y ++# CONFIG_MTD_NAND_DMABUF is not set ++# CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE is not set ++CONFIG_MTD_OOB_COPIES=3 ++CONFIG_MTD_BADBLOCK_FLAG_PAGE=127 ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# UBI - Unsorted block images ++# ++# CONFIG_MTD_UBI is not set ++# CONFIG_MTD_UBI_BLKDEVS is not set ++# CONFIG_PARPORT is not set ++# CONFIG_PNP is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_UB is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++CONFIG_MISC_DEVICES=y ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_TGT is not set ++# CONFIG_SCSI_NETLINK is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++ ++# ++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs ++# ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++CONFIG_SCSI_WAIT_SCAN=m ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++CONFIG_SCSI_LOWLEVEL=y ++# CONFIG_ISCSI_TCP is not set ++# CONFIG_SCSI_DEBUG is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_NETDEVICES is not set ++# CONFIG_ISDN is not set ++# CONFIG_PHONE is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++# CONFIG_INPUT_EVDEV is not set ++# CONFIG_INPUT_EVBUG is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_JZ is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++# CONFIG_SERIO_I8042 is not set ++CONFIG_SERIO_SERPORT=y ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_VT_CONSOLE=y ++CONFIG_HW_CONSOLE=y ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_SERIAL_NONSTANDARD is not set ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_NR_UARTS=4 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=4 ++# CONFIG_SERIAL_8250_EXTENDED is not set ++ ++# ++# Non-8250 serial port support ++# ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++CONFIG_UNIX98_PTYS=y ++CONFIG_LEGACY_PTYS=y ++CONFIG_LEGACY_PTY_COUNT=2 ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_RTC is not set ++# CONFIG_RTC_PCF8563 is not set ++# CONFIG_RTC_JZ is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++ ++# ++# JZSOC char device support ++# ++CONFIG_JZCHAR=y ++# CONFIG_JZ_CIM is not set ++# CONFIG_JZ_TPANEL_ATA2508 is not set ++# CONFIG_JZ_TPANEL is not set ++CONFIG_JZ_UDC_HOTPLUG=y ++# CONFIG_JZ_POWEROFF is not set ++# CONFIG_JZ_OW is not set ++# CONFIG_I2C is not set ++ ++# ++# SPI support ++# ++# CONFIG_SPI is not set ++# CONFIG_SPI_MASTER is not set ++# CONFIG_W1 is not set ++# CONFIG_POWER_SUPPLY is not set ++# CONFIG_HWMON is not set ++# CONFIG_WATCHDOG is not set ++ ++# ++# Sonics Silicon Backplane ++# ++CONFIG_SSB_POSSIBLE=y ++# CONFIG_SSB is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_SM501 is not set ++ ++# ++# Multimedia devices ++# ++# CONFIG_VIDEO_DEV is not set ++# CONFIG_DVB_CORE is not set ++# CONFIG_DAB is not set ++ ++# ++# Graphics support ++# ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++# CONFIG_FB is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++ ++# ++# Display device support ++# ++# CONFIG_DISPLAY_SUPPORT is not set ++ ++# ++# Console display driver support ++# ++# CONFIG_VGA_CONSOLE is not set ++CONFIG_DUMMY_CONSOLE=y ++ ++# ++# Sound ++# ++# CONFIG_SOUND is not set ++CONFIG_HID_SUPPORT=y ++CONFIG_HID=y ++# CONFIG_HID_DEBUG is not set ++# CONFIG_HIDRAW is not set ++ ++# ++# USB Input Devices ++# ++CONFIG_USB_HID=y ++# CONFIG_USB_HIDINPUT_POWERBOOK is not set ++# CONFIG_HID_FF is not set ++CONFIG_USB_HIDDEV=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB_ARCH_HAS_OHCI=y ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++CONFIG_USB=y ++# CONFIG_USB_DEBUG is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEVICEFS is not set ++CONFIG_USB_DEVICE_CLASS=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_ISP116X_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set ++# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++ ++# ++# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' ++# ++ ++# ++# may also be needed; see USB_STORAGE Help for more information ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_DPCM is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_LIBUSUAL is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++CONFIG_USB_MON=y ++ ++# ++# USB port drivers ++# ++ ++# ++# USB Serial Converter support ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_AUERSWALD is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_BERRY_CHARGE is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_PHIDGET is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++ ++# ++# USB DSL modem support ++# ++ ++# ++# USB Gadget Support ++# ++# CONFIG_USB_GADGET is not set ++# CONFIG_MMC is not set ++# CONFIG_NEW_LEDS is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++ ++# ++# Userspace I/O ++# ++# CONFIG_UIO is not set ++ ++# ++# File systems ++# ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++# CONFIG_EXT2_FS_POSIX_ACL is not set ++# CONFIG_EXT2_FS_SECURITY is not set ++# CONFIG_EXT2_FS_XIP is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4DEV_FS is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++CONFIG_FS_POSIX_ACL=y ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++CONFIG_MINIX_FS=y ++# CONFIG_ROMFS_FS is not set ++CONFIG_INOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_QUOTA is not set ++CONFIG_DNOTIFY=y ++# CONFIG_AUTOFS_FS is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_KCORE=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++ ++# ++# Miscellaneous filesystems ++# ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_JFFS2_FS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_NFS_DIRECTIO=y ++# CONFIG_NFSD is not set ++CONFIG_ROOT_NFS=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++CONFIG_SUNRPC_GSS=y ++# CONFIG_SUNRPC_BIND34 is not set ++CONFIG_RPCSEC_GSS_KRB5=y ++# CONFIG_RPCSEC_GSS_SPKM3 is not set ++# CONFIG_SMB_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++CONFIG_NLS_CODEPAGE_936=y ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++# CONFIG_DLM is not set ++ ++# ++# Yaffs2 Filesystems ++# ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++# CONFIG_YAFFS_ECC_BCH is not set ++CONFIG_YAFFS_ECC_RS=y ++# CONFIG_YAFFS_ECC_HAMMING is not set ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set ++# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set ++CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK=y ++CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y ++CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS=10 ++CONFIG_INSTRUMENTATION=y ++# CONFIG_PROFILING is not set ++# CONFIG_MARKERS is not set ++ ++# ++# Kernel hacking ++# ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++# CONFIG_PRINTK_TIME is not set ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_KERNEL is not set ++# CONFIG_SAMPLES is not set ++CONFIG_CMDLINE="" ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITY_FILE_CAPABILITIES is not set ++CONFIG_CRYPTO=y ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_MANAGER=y ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_MD4 is not set ++CONFIG_CRYPTO_MD5=y ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_WP512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_ECB is not set ++CONFIG_CRYPTO_CBC=y ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_XTS is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_AES is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_CRC32C is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_TEST is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_LZO is not set ++CONFIG_CRYPTO_HW=y ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++# CONFIG_CRC_CCITT is not set ++# CONFIG_CRC16 is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++CONFIG_REED_SOLOMON=y ++CONFIG_REED_SOLOMON_ENC8=y ++CONFIG_REED_SOLOMON_DEC8=y ++CONFIG_PLIST=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y +diff -urN linux-2.6.24.7.old/arch/mips/configs/virgo_defconfig linux-2.6.24.7/arch/mips/configs/virgo_defconfig +--- linux-2.6.24.7.old/arch/mips/configs/virgo_defconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/configs/virgo_defconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1281 @@ ++# ++# Automatically generated make config: don't edit ++# Linux kernel version: 2.6.24.3 ++# Thu Jun 12 13:52:15 2008 ++# ++CONFIG_MIPS=y ++ ++# ++# Machine selection ++# ++# CONFIG_JZ4730_PMP is not set ++# CONFIG_JZ4740_PAVO is not set ++# CONFIG_JZ4740_LEO is not set ++# CONFIG_JZ4740_LYRA is not set ++# CONFIG_JZ4725_DIPPER is not set ++CONFIG_JZ4720_VIRGO=y ++# CONFIG_JZ4750_FUWA is not set ++# CONFIG_MACH_ALCHEMY is not set ++# CONFIG_BASLER_EXCITE is not set ++# CONFIG_BCM47XX is not set ++# CONFIG_MIPS_COBALT is not set ++# CONFIG_MACH_DECSTATION is not set ++# CONFIG_MACH_JAZZ is not set ++# CONFIG_LASAT is not set ++# CONFIG_LEMOTE_FULONG is not set ++# CONFIG_MIPS_ATLAS is not set ++# CONFIG_MIPS_MALTA is not set ++# CONFIG_MIPS_SEAD is not set ++# CONFIG_MIPS_SIM is not set ++# CONFIG_MARKEINS is not set ++# CONFIG_MACH_VR41XX is not set ++# CONFIG_PNX8550_JBS is not set ++# CONFIG_PNX8550_STB810 is not set ++# CONFIG_PMC_MSP is not set ++# CONFIG_PMC_YOSEMITE is not set ++# CONFIG_QEMU is not set ++# CONFIG_SGI_IP22 is not set ++# CONFIG_SGI_IP27 is not set ++# CONFIG_SGI_IP32 is not set ++# CONFIG_SIBYTE_CRHINE is not set ++# CONFIG_SIBYTE_CARMEL is not set ++# CONFIG_SIBYTE_CRHONE is not set ++# CONFIG_SIBYTE_RHONE is not set ++# CONFIG_SIBYTE_SWARM is not set ++# CONFIG_SIBYTE_LITTLESUR is not set ++# CONFIG_SIBYTE_SENTOSA is not set ++# CONFIG_SIBYTE_PTSWARM is not set ++# CONFIG_SIBYTE_BIGSUR is not set ++# CONFIG_SNI_RM is not set ++# CONFIG_TOSHIBA_JMR3927 is not set ++# CONFIG_TOSHIBA_RBTX4927 is not set ++# CONFIG_TOSHIBA_RBTX4938 is not set ++# CONFIG_WR_PPMC is not set ++CONFIG_SOC_JZ4740=y ++CONFIG_SOC_JZ4720=y ++CONFIG_JZSOC=y ++CONFIG_JZRISC=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++# CONFIG_ARCH_HAS_ILOG2_U32 is not set ++# CONFIG_ARCH_HAS_ILOG2_U64 is not set ++CONFIG_ARCH_SUPPORTS_OPROFILE=y ++CONFIG_GENERIC_FIND_NEXT_BIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_TIME=y ++CONFIG_GENERIC_CMOS_UPDATE=y ++CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y ++# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set ++CONFIG_DMA_NONCOHERENT=y ++CONFIG_DMA_NEED_PCI_MAP_STATE=y ++# CONFIG_HOTPLUG_CPU is not set ++# CONFIG_NO_IOPORT is not set ++# CONFIG_CPU_BIG_ENDIAN is not set ++CONFIG_CPU_LITTLE_ENDIAN=y ++CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y ++CONFIG_MIPS_L1_CACHE_SHIFT=5 ++ ++# ++# CPU selection ++# ++# CONFIG_CPU_LOONGSON2 is not set ++CONFIG_CPU_MIPS32_R1=y ++# CONFIG_CPU_MIPS32_R2 is not set ++# CONFIG_CPU_MIPS64_R1 is not set ++# CONFIG_CPU_MIPS64_R2 is not set ++# CONFIG_CPU_R3000 is not set ++# CONFIG_CPU_TX39XX is not set ++# CONFIG_CPU_VR41XX is not set ++# CONFIG_CPU_R4300 is not set ++# CONFIG_CPU_R4X00 is not set ++# CONFIG_CPU_TX49XX is not set ++# CONFIG_CPU_R5000 is not set ++# CONFIG_CPU_R5432 is not set ++# CONFIG_CPU_R6000 is not set ++# CONFIG_CPU_NEVADA is not set ++# CONFIG_CPU_R8000 is not set ++# CONFIG_CPU_R10000 is not set ++# CONFIG_CPU_RM7000 is not set ++# CONFIG_CPU_RM9000 is not set ++# CONFIG_CPU_SB1 is not set ++CONFIG_SYS_HAS_CPU_MIPS32_R1=y ++CONFIG_CPU_MIPS32=y ++CONFIG_CPU_MIPSR1=y ++CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y ++CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y ++ ++# ++# Kernel type ++# ++CONFIG_32BIT=y ++# CONFIG_64BIT is not set ++CONFIG_PAGE_SIZE_4KB=y ++# CONFIG_PAGE_SIZE_8KB is not set ++# CONFIG_PAGE_SIZE_16KB is not set ++# CONFIG_PAGE_SIZE_64KB is not set ++CONFIG_CPU_HAS_PREFETCH=y ++CONFIG_MIPS_MT_DISABLED=y ++# CONFIG_MIPS_MT_SMP is not set ++# CONFIG_MIPS_MT_SMTC is not set ++CONFIG_CPU_HAS_LLSC=y ++CONFIG_CPU_HAS_SYNC=y ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_CPU_SUPPORTS_HIGHMEM=y ++CONFIG_ARCH_FLATMEM_ENABLE=y ++CONFIG_ARCH_POPULATES_NODE_MAP=y ++CONFIG_SELECT_MEMORY_MODEL=y ++CONFIG_FLATMEM_MANUAL=y ++# CONFIG_DISCONTIGMEM_MANUAL is not set ++# CONFIG_SPARSEMEM_MANUAL is not set ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++# CONFIG_SPARSEMEM_STATIC is not set ++# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++# CONFIG_RESOURCES_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_TICK_ONESHOT is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++# CONFIG_HZ_48 is not set ++CONFIG_HZ_100=y ++# CONFIG_HZ_128 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_256 is not set ++# CONFIG_HZ_1000 is not set ++# CONFIG_HZ_1024 is not set ++CONFIG_SYS_SUPPORTS_ARBIT_HZ=y ++CONFIG_HZ=100 ++# CONFIG_PREEMPT_NONE is not set ++# CONFIG_PREEMPT_VOLUNTARY is not set ++CONFIG_PREEMPT=y ++CONFIG_PREEMPT_BKL=y ++# CONFIG_KEXEC is not set ++CONFIG_SECCOMP=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++ ++# ++# General setup ++# ++CONFIG_EXPERIMENTAL=y ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_LOCK_KERNEL=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++# CONFIG_USER_NS is not set ++# CONFIG_PID_NS is not set ++# CONFIG_AUDIT is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=14 ++# CONFIG_CGROUPS is not set ++CONFIG_FAIR_GROUP_SCHED=y ++CONFIG_FAIR_USER_SCHED=y ++# CONFIG_FAIR_CGROUP_SCHED is not set ++CONFIG_SYSFS_DEPRECATED=y ++CONFIG_RELAY=y ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_EMBEDDED=y ++CONFIG_SYSCTL_SYSCALL=y ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_EXTRA_PASS is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_ANON_INODES=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLAB=y ++# CONFIG_SLUB is not set ++# CONFIG_SLOB is not set ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++# CONFIG_TINY_SHMEM is not set ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++CONFIG_KMOD=y ++CONFIG_BLOCK=y ++# CONFIG_LBD is not set ++# CONFIG_BLK_DEV_IO_TRACE is not set ++# CONFIG_LSF is not set ++# CONFIG_BLK_DEV_BSG is not set ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_AS=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_AS=y ++# CONFIG_DEFAULT_DEADLINE is not set ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="anticipatory" ++ ++# ++# Bus options (PCI, PCMCIA, EISA, ISA, TC) ++# ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++CONFIG_MMU=y ++# CONFIG_PCCARD is not set ++ ++# ++# Executable file formats ++# ++CONFIG_BINFMT_ELF=y ++# CONFIG_BINFMT_MISC is not set ++CONFIG_TRAD_SIGNALS=y ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ_JZ=y ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_TABLE=y ++# CONFIG_CPU_FREQ_DEBUG is not set ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++ ++# ++# Power management options ++# ++CONFIG_PM=y ++CONFIG_PM_LEGACY=y ++# CONFIG_PM_DEBUG is not set ++CONFIG_SUSPEND_UP_POSSIBLE=y ++# CONFIG_SUSPEND is not set ++ ++# ++# Networking ++# ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_MMAP is not set ++CONFIG_UNIX=y ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_FIB_HASH=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE is not set ++# CONFIG_ARPD is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++# CONFIG_INET_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NETFILTER is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_ECONET is not set ++# CONFIG_WAN_ROUTER is not set ++# CONFIG_NET_SCHED is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++ ++# ++# Wireless ++# ++# CONFIG_CFG80211 is not set ++CONFIG_WIRELESS_EXT=y ++# CONFIG_MAC80211 is not set ++# CONFIG_IEEE80211 is not set ++# CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++# CONFIG_FW_LOADER is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_DEBUG is not set ++# CONFIG_MTD_CONCAT is not set ++CONFIG_MTD_PARTITIONS=y ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_MTD_OOPS is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOC2000 is not set ++# CONFIG_MTD_DOC2001 is not set ++# CONFIG_MTD_DOC2001PLUS is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_VERIFY_WRITE is not set ++# CONFIG_MTD_NAND_ECC_SMC is not set ++# CONFIG_MTD_NAND_MUSEUM_IDS is not set ++CONFIG_MTD_NAND_IDS=y ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_MTD_ALAUDA is not set ++CONFIG_MTD_NAND_JZ4740=y ++# CONFIG_MTD_HW_HM_ECC is not set ++# CONFIG_MTD_SW_HM_ECC is not set ++CONFIG_MTD_HW_RS_ECC=y ++# CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE is not set ++CONFIG_MTD_OOB_COPIES=3 ++CONFIG_MTD_BADBLOCK_FLAG_PAGE=127 ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# UBI - Unsorted block images ++# ++CONFIG_MTD_UBI=m ++CONFIG_MTD_UBI_WL_THRESHOLD=256 ++CONFIG_MTD_UBI_BEB_RESERVE=1 ++# CONFIG_MTD_UBI_GLUEBI is not set ++ ++# ++# UBI debugging options ++# ++# CONFIG_MTD_UBI_DEBUG is not set ++CONFIG_MTD_UBI_BLKDEVS=m ++CONFIG_MTD_UBI_BLOCK=m ++# CONFIG_PARPORT is not set ++CONFIG_PNP=y ++# CONFIG_PNP_DEBUG is not set ++ ++# ++# Protocols ++# ++# CONFIG_PNPACPI is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_UB is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=2 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++CONFIG_MISC_DEVICES=y ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_TGT is not set ++# CONFIG_SCSI_NETLINK is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++ ++# ++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs ++# ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++CONFIG_SCSI_WAIT_SCAN=m ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++CONFIG_SCSI_LOWLEVEL=y ++# CONFIG_ISCSI_TCP is not set ++# CONFIG_SCSI_DEBUG is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++CONFIG_NETDEVICES=y ++# CONFIG_NETDEVICES_MULTIQUEUE is not set ++# CONFIG_DUMMY is not set ++# CONFIG_BONDING is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NET_SB1000 is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++CONFIG_NET_ETHERNET=y ++CONFIG_MII=y ++CONFIG_JZCS8900=y ++# CONFIG_AX88796 is not set ++# CONFIG_DM9000 is not set ++# CONFIG_IBM_NEW_EMAC_ZMII is not set ++# CONFIG_IBM_NEW_EMAC_RGMII is not set ++# CONFIG_IBM_NEW_EMAC_TAH is not set ++# CONFIG_IBM_NEW_EMAC_EMAC4 is not set ++# CONFIG_B44 is not set ++# CONFIG_NETDEV_1000 is not set ++# CONFIG_NETDEV_10000 is not set ++ ++# ++# Wireless LAN ++# ++# CONFIG_WLAN_PRE80211 is not set ++# CONFIG_WLAN_80211 is not set ++ ++# ++# USB Network Adapters ++# ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_WAN is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++# CONFIG_SHAPER is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_ISDN is not set ++# CONFIG_PHONE is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++# CONFIG_SERIO_I8042 is not set ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_LIBPS2 is not set ++# CONFIG_SERIO_RAW is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_VT_CONSOLE=y ++CONFIG_HW_CONSOLE=y ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_SERIAL_NONSTANDARD is not set ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_PNP=y ++CONFIG_SERIAL_8250_NR_UARTS=2 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=2 ++# CONFIG_SERIAL_8250_EXTENDED is not set ++ ++# ++# Non-8250 serial port support ++# ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++CONFIG_UNIX98_PTYS=y ++CONFIG_LEGACY_PTYS=y ++CONFIG_LEGACY_PTY_COUNT=2 ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_RTC is not set ++# CONFIG_RTC_PCF8563 is not set ++CONFIG_RTC_JZ=y ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++ ++# ++# JZSOC char device support ++# ++CONFIG_JZCHAR=y ++# CONFIG_JZ_CIM is not set ++# CONFIG_JZ_TPANEL_ATA2508 is not set ++CONFIG_JZ_TPANEL=y ++CONFIG_JZ_SADC=y ++# CONFIG_JZ_TPANEL_AK4182 is not set ++# CONFIG_JZ_TPANEL_UCB1400 is not set ++# CONFIG_JZ_TPANEL_WM9712 is not set ++CONFIG_JZ_UDC_HOTPLUG=y ++CONFIG_JZ_POWEROFF=y ++# CONFIG_JZ_OW is not set ++# CONFIG_I2C is not set ++ ++# ++# SPI support ++# ++# CONFIG_SPI is not set ++# CONFIG_SPI_MASTER is not set ++# CONFIG_W1 is not set ++# CONFIG_POWER_SUPPLY is not set ++# CONFIG_HWMON is not set ++CONFIG_WATCHDOG=y ++# CONFIG_WATCHDOG_NOWAYOUT is not set ++ ++# ++# Watchdog Device Drivers ++# ++CONFIG_JZ_WDT=y ++# CONFIG_SOFT_WATCHDOG is not set ++ ++# ++# USB-based Watchdog Cards ++# ++# CONFIG_USBPCWATCHDOG is not set ++ ++# ++# Sonics Silicon Backplane ++# ++CONFIG_SSB_POSSIBLE=y ++# CONFIG_SSB is not set ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MFD_SM501 is not set ++ ++# ++# Multimedia devices ++# ++CONFIG_VIDEO_DEV=y ++CONFIG_VIDEO_V4L1=y ++CONFIG_VIDEO_V4L1_COMPAT=y ++CONFIG_VIDEO_V4L2=y ++CONFIG_VIDEO_CAPTURE_DRIVERS=y ++# CONFIG_VIDEO_ADV_DEBUG is not set ++CONFIG_VIDEO_HELPER_CHIPS_AUTO=y ++# CONFIG_VIDEO_VIVI is not set ++# CONFIG_VIDEO_CPIA is not set ++# CONFIG_VIDEO_CPIA2 is not set ++CONFIG_VIDEO_JZ_CIM=y ++CONFIG_VIDEO_JZ_SENSOR=y ++CONFIG_V4L_USB_DRIVERS=y ++# CONFIG_USB_VICAM is not set ++# CONFIG_USB_IBMCAM is not set ++# CONFIG_USB_KONICAWC is not set ++# CONFIG_USB_QUICKCAM_MESSENGER is not set ++# CONFIG_USB_ET61X251 is not set ++# CONFIG_USB_OV511 is not set ++# CONFIG_USB_SE401 is not set ++# CONFIG_USB_SN9C102 is not set ++# CONFIG_USB_STV680 is not set ++# CONFIG_USB_ZC0301 is not set ++# CONFIG_USB_PWC is not set ++# CONFIG_USB_ZR364XX is not set ++CONFIG_RADIO_ADAPTERS=y ++# CONFIG_USB_DSBR is not set ++# CONFIG_DVB_CORE is not set ++# CONFIG_DAB is not set ++ ++# ++# Graphics support ++# ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++# CONFIG_FB_DDC is not set ++CONFIG_FB_CFB_FILLRECT=y ++CONFIG_FB_CFB_COPYAREA=y ++CONFIG_FB_CFB_IMAGEBLIT=y ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_SYS_FOPS is not set ++CONFIG_FB_DEFERRED_IO=y ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++CONFIG_FB_JZSOC=y ++# CONFIG_FB_JZ4740_SLCD is not set ++CONFIG_FB_JZLCD_4730_4740=y ++CONFIG_JZLCD_FRAMEBUFFER_MAX=1 ++# CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT is not set ++# CONFIG_JZLCD_SHARP_LQ035Q7 is not set ++# CONFIG_JZLCD_SAMSUNG_LTS350Q1 is not set ++# CONFIG_JZLCD_SAMSUNG_LTV350QVF04 is not set ++# CONFIG_JZLCD_SAMSUNG_LTP400WQF01 is not set ++CONFIG_JZLCD_SAMSUNG_LTP400WQF02=y ++# CONFIG_JZLCD_AUO_A030FL01_V1 is not set ++# CONFIG_JZLCD_TRULY_TFTG320240DTSW is not set ++# CONFIG_JZLCD_TRULY_TFTG320240DTSW_SERIAL is not set ++# CONFIG_JZLCD_TRULY_TFTG240320UTSW_63W_E is not set ++# CONFIG_JZLCD_FOXCONN_PT035TN01 is not set ++# CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL is not set ++# CONFIG_JZLCD_TOSHIBA_LTM084P363 is not set ++# CONFIG_JZLCD_HYNIX_HT10X21 is not set ++# CONFIG_JZLCD_INNOLUX_AT080TN42 is not set ++# CONFIG_JZLCD_CSTN_800x600 is not set ++# CONFIG_JZLCD_CSTN_320x240 is not set ++# CONFIG_JZLCD_MSTN_480x320 is not set ++# CONFIG_JZLCD_MSTN_320x240 is not set ++# CONFIG_JZLCD_MSTN_240x128 is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++ ++# ++# Display device support ++# ++# CONFIG_DISPLAY_SUPPORT is not set ++ ++# ++# Console display driver support ++# ++# CONFIG_VGA_CONSOLE is not set ++CONFIG_DUMMY_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_CURSOR_FLASH is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set ++CONFIG_FONTS=y ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++# CONFIG_FONT_6x11 is not set ++# CONFIG_FONT_7x14 is not set ++# CONFIG_FONT_PEARL_8x8 is not set ++# CONFIG_FONT_ACORN_8x8 is not set ++# CONFIG_FONT_MINI_4x6 is not set ++# CONFIG_FONT_SUN8x16 is not set ++# CONFIG_FONT_SUN12x22 is not set ++# CONFIG_FONT_10x18 is not set ++CONFIG_LOGO=y ++CONFIG_LOGO_LINUX_MONO=y ++CONFIG_LOGO_LINUX_VGA16=y ++CONFIG_LOGO_LINUX_CLUT224=y ++ ++# ++# Sound ++# ++CONFIG_SOUND=y ++ ++# ++# Advanced Linux Sound Architecture ++# ++# CONFIG_SND is not set ++ ++# ++# Open Sound System ++# ++CONFIG_SOUND_PRIME=y ++CONFIG_OSS_OBSOLETE=y ++# CONFIG_SOUND_JZ_AC97 is not set ++CONFIG_SOUND_JZ_I2S=y ++# CONFIG_SOUND_JZ_PCM is not set ++# CONFIG_I2S_AK4642EN is not set ++CONFIG_I2S_ICODEC=y ++# CONFIG_SOUND_MSNDCLAS is not set ++# CONFIG_SOUND_MSNDPIN is not set ++CONFIG_HID_SUPPORT=y ++CONFIG_HID=y ++# CONFIG_HID_DEBUG is not set ++# CONFIG_HIDRAW is not set ++ ++# ++# USB Input Devices ++# ++CONFIG_USB_HID=y ++# CONFIG_USB_HIDINPUT_POWERBOOK is not set ++# CONFIG_HID_FF is not set ++# CONFIG_USB_HIDDEV is not set ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB_ARCH_HAS_OHCI=y ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++CONFIG_USB=y ++# CONFIG_USB_DEBUG is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEVICEFS is not set ++CONFIG_USB_DEVICE_CLASS=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_SUSPEND is not set ++# CONFIG_USB_PERSIST is not set ++# CONFIG_USB_OTG is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_ISP116X_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set ++# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++ ++# ++# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' ++# ++ ++# ++# may also be needed; see USB_STORAGE Help for more information ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_DPCM is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_LIBUSUAL is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++CONFIG_USB_MON=y ++ ++# ++# USB port drivers ++# ++ ++# ++# USB Serial Converter support ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_AUERSWALD is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_BERRY_CHARGE is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_PHIDGET is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++ ++# ++# USB DSL modem support ++# ++ ++# ++# USB Gadget Support ++# ++CONFIG_USB_GADGET=m ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++# CONFIG_USB_GADGET_DEBUG_FS is not set ++CONFIG_USB_GADGET_SELECTED=y ++CONFIG_USB_GADGET_JZ4740=y ++CONFIG_USB_JZ4740=m ++# CONFIG_USB_GADGET_JZ4730 is not set ++# CONFIG_USB_GADGET_AMD5536UDC is not set ++# CONFIG_USB_GADGET_ATMEL_USBA is not set ++# CONFIG_USB_GADGET_FSL_USB2 is not set ++# CONFIG_USB_GADGET_NET2280 is not set ++# CONFIG_USB_GADGET_PXA2XX is not set ++# CONFIG_USB_GADGET_M66592 is not set ++# CONFIG_USB_GADGET_GOKU is not set ++# CONFIG_USB_GADGET_LH7A40X is not set ++# CONFIG_USB_GADGET_OMAP is not set ++# CONFIG_USB_GADGET_S3C2410 is not set ++# CONFIG_USB_GADGET_AT91 is not set ++# CONFIG_USB_GADGET_DUMMY_HCD is not set ++CONFIG_USB_GADGET_DUALSPEED=y ++# CONFIG_USB_ZERO is not set ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_GADGETFS is not set ++CONFIG_USB_FILE_STORAGE=m ++# CONFIG_USB_FILE_STORAGE_TEST is not set ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_MIDI_GADGET is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_UNSAFE_RESUME is not set ++ ++# ++# MMC/SD Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_SDIO_UART is not set ++ ++# ++# MMC/SD Host Controller Drivers ++# ++CONFIG_MMC_JZ=y ++CONFIG_JZ_MMC_BUS_4=y ++# CONFIG_JZ_MMC_BUS_1 is not set ++# CONFIG_NEW_LEDS is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++ ++# ++# Userspace I/O ++# ++# CONFIG_UIO is not set ++ ++# ++# File systems ++# ++CONFIG_EXT2_FS=y ++CONFIG_EXT2_FS_XATTR=y ++# CONFIG_EXT2_FS_POSIX_ACL is not set ++# CONFIG_EXT2_FS_SECURITY is not set ++# CONFIG_EXT2_FS_XIP is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4DEV_FS is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++CONFIG_FS_POSIX_ACL=y ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++CONFIG_MINIX_FS=y ++# CONFIG_ROMFS_FS is not set ++CONFIG_INOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_QUOTA is not set ++CONFIG_DNOTIFY=y ++# CONFIG_AUTOFS_FS is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_KCORE=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_HUGETLB_PAGE is not set ++# CONFIG_CONFIGFS_FS is not set ++ ++# ++# Miscellaneous filesystems ++# ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_JFFS2_FS is not set ++CONFIG_UBIFS_FS=m ++# CONFIG_UBIFS_FS_XATTR is not set ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_UBIFS_FS_DEBUG is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_NFS_DIRECTIO=y ++# CONFIG_NFSD is not set ++CONFIG_ROOT_NFS=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++CONFIG_SUNRPC_GSS=y ++# CONFIG_SUNRPC_BIND34 is not set ++CONFIG_RPCSEC_GSS_KRB5=y ++# CONFIG_RPCSEC_GSS_SPKM3 is not set ++# CONFIG_SMB_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++CONFIG_NLS_CODEPAGE_936=y ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++# CONFIG_DLM is not set ++ ++# ++# Yaffs2 Filesystems ++# ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set ++# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set ++CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK=y ++CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y ++CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS=10 ++CONFIG_INSTRUMENTATION=y ++# CONFIG_PROFILING is not set ++# CONFIG_MARKERS is not set ++ ++# ++# Kernel hacking ++# ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++# CONFIG_PRINTK_TIME is not set ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_UNUSED_SYMBOLS is not set ++CONFIG_DEBUG_FS=y ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_KERNEL is not set ++# CONFIG_SAMPLES is not set ++CONFIG_CMDLINE="" ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITY_FILE_CAPABILITIES is not set ++CONFIG_CRYPTO=y ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_MANAGER=y ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_MD4 is not set ++CONFIG_CRYPTO_MD5=y ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_WP512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_ECB is not set ++CONFIG_CRYPTO_CBC=y ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_XTS is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_AES is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_SEED is not set ++CONFIG_CRYPTO_DEFLATE=m ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_CRC32C is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_TEST is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++CONFIG_CRYPTO_LZO=m ++CONFIG_CRYPTO_HW=y ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=m ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++CONFIG_ZLIB_INFLATE=m ++CONFIG_ZLIB_DEFLATE=m ++CONFIG_LZO_COMPRESS=m ++CONFIG_LZO_DECOMPRESS=m ++CONFIG_PLIST=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y +diff -urN linux-2.6.24.7.old/arch/mips/jz4730/Makefile linux-2.6.24.7/arch/mips/jz4730/Makefile +--- linux-2.6.24.7.old/arch/mips/jz4730/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4730/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,22 @@ ++# ++# Makefile for the Ingenic JZ4730. ++# ++ ++# Object file lists. ++ ++obj-y += prom.o irq.o time.o reset.o setup.o dma.o \ ++ platform.o i2c.o ++ ++obj-$(CONFIG_PROC_FS) += proc.o ++ ++# board specific support ++ ++obj-$(CONFIG_JZ4730_PMP) += board-pmp.o ++ ++# CPU Frequency scaling support ++ ++obj-$(CONFIG_CPU_FREQ_JZ) +=cpufreq.o ++ ++# PM support ++ ++obj-$(CONFIG_PM_LEGACY) +=pm.o sleep.o +diff -urN linux-2.6.24.7.old/arch/mips/jz4730/board-pmp.c linux-2.6.24.7/arch/mips/jz4730/board-pmp.c +--- linux-2.6.24.7.old/arch/mips/jz4730/board-pmp.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4730/board-pmp.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,109 @@ ++/* ++ * linux/arch/mips/jz4730/board-pmp.c ++ * ++ * JZ4730 PMP board setup routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++extern void (*jz_timer_callback)(void); ++ ++static void dancing(void) ++{ ++ static unsigned int count = 0; ++ ++ count ++; ++ count &= 1; ++ if (count) ++ __gpio_set_pin(GPIO_LED_EN); ++ else ++ __gpio_clear_pin(GPIO_LED_EN); ++} ++ ++static void pmp_timer_ack(void) ++{ ++ static unsigned int count = 0; ++ count ++; ++ if (count % 100 == 0) { ++ count = 0; ++ dancing(); ++ } ++} ++ ++static void __init board_cpm_setup(void) ++{ ++ __cpm_start_all(); ++} ++ ++static void __init board_gpio_setup(void) ++{ ++ /* ++ * Most of the gpios have been setup in the bootloader. ++ */ ++ ++ __harb_usb0_uhc(); ++ __gpio_as_dma(); ++ __gpio_as_eth(); ++ __gpio_as_usb(); ++ __gpio_as_lcd_master(); ++#if defined(CONFIG_I2S_AK4642EN) ++ __gpio_as_scc1(); ++#endif ++#if defined(CONFIG_I2S_TSC2301) || defined(CONFIG_I2S_TLC320AIC23) ++ __gpio_as_ssi(); ++#endif ++ //__gpio_as_ac97(); ++#if defined(CONFIG_I2S_TSC2301) || defined(CONFIG_I2S_TLC320AIC23) || defined(CONFIG_I2S_CS42L51) ++ __gpio_as_i2s_slave(); ++#endif ++ __gpio_as_cim(); ++ __gpio_as_msc(); ++ ++ __gpio_as_output(GPIO_LED_EN); ++ __gpio_set_pin(GPIO_LED_EN); ++ ++ __gpio_as_output(GPIO_DISP_OFF_N); ++ __gpio_set_pin(GPIO_DISP_OFF_N); ++ __gpio_as_output(GPIO_PWM0); ++ __gpio_set_pin(GPIO_PWM0); ++ ++ __gpio_as_input(GPIO_RTC_IRQ); ++ __gpio_as_output(GPIO_USB_CLK_EN); ++ __gpio_set_pin(GPIO_USB_CLK_EN); ++ ++ __gpio_as_input(GPIO_CHARG_STAT); ++ __gpio_disable_pull(GPIO_CHARG_STAT); ++ ++ __gpio_as_input(GPIO_UDC_HOTPLUG); ++ __gpio_disable_pull(GPIO_UDC_HOTPLUG); ++ __gpio_disable_pull(54); /* fixed ic bug, the pull of gpio pin 86 is as pin 54 */ ++} ++ ++void __init jz_board_setup(void) ++{ ++ printk("JZ4730 PMP board setup\n"); ++ ++ board_cpm_setup(); ++ board_gpio_setup(); ++ ++ jz_timer_callback = pmp_timer_ack; ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4730/cpufreq.c linux-2.6.24.7/arch/mips/jz4730/cpufreq.c +--- linux-2.6.24.7.old/arch/mips/jz4730/cpufreq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4730/cpufreq.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,596 @@ ++ ++/* ++ * linux/arch/mips/jz4730/cpufreq.c ++ * ++ * cpufreq driver for JZ4730 ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ ++ "cpufreq-jz4730", msg) ++ ++#undef CHANGE_PLL ++ ++#define PLL_UNCHANGED 0 ++#define PLL_GOES_UP 1 ++#define PLL_GOES_DOWN 2 ++ ++#define PLL_WAIT_500NS (500*(__cpm_get_iclk()/1000000000)) ++ ++/* Saved the boot-time parameters */ ++static struct { ++ /* SDRAM parameters */ ++ unsigned int mclk; /* memory clock, KHz */ ++ unsigned int tras; /* RAS pulse width, cycles of mclk */ ++ unsigned int rcd; /* RAS to CAS Delay, cycles of mclk */ ++ unsigned int tpc; /* RAS Precharge time, cycles of mclk */ ++ unsigned int trwl; /* Write Precharge Time, cycles of mclk */ ++ unsigned int trc; /* RAS Cycle Time, cycles of mclk */ ++ unsigned int rtcor; /* Refresh Time Constant */ ++ unsigned int sdram_initialized; ++ ++ /* LCD parameters */ ++ unsigned int lcd_clk; /* LCD clock, Hz */ ++ unsigned int lcdpix_clk; /* LCD Pixel clock, Hz */ ++ unsigned int lcd_clks_initialized; ++} boot_config; ++ ++struct jz4730_freq_percpu_info { ++ struct cpufreq_frequency_table table[7]; ++}; ++ ++static struct jz4730_freq_percpu_info jz4730_freq_table; ++ ++/* ++ * This contains the registers value for an operating point. ++ * If only part of a register needs to change then there is ++ * a mask value for that register. ++ * When going to a new operating point the current register ++ * value is ANDed with the ~mask and ORed with the new value. ++ */ ++struct dpm_regs { ++ u32 cfcr; /* Clock Freq Control Register */ ++ u32 cfcr_mask; /* Clock Freq Control Register mask */ ++ u32 cfcr2; /* Clock Freq Control Register 2 */ ++ u32 cfcr2_mask; /* Clock Freq Control Register 2 mask */ ++ u32 plcr1; /* PLL1 Control Register */ ++ u32 plcr1_mask; /* PLL1 Control Register mask */ ++ u32 pll_up_flag; /* New PLL freq is higher than current or not */ ++}; ++ ++extern jz_clocks_t jz_clocks; ++ ++static void jz_update_clocks(void) ++{ ++ /* Next clocks must be updated if we have changed ++ * the PLL or divisors. ++ */ ++ jz_clocks.iclk = __cpm_get_iclk(); ++ jz_clocks.sclk = __cpm_get_sclk(); ++ jz_clocks.mclk = __cpm_get_mclk(); ++ jz_clocks.pclk = __cpm_get_pclk(); ++ jz_clocks.lcdclk = __cpm_get_lcdclk(); ++ jz_clocks.pixclk = __cpm_get_pixclk(); ++} ++ ++static void ++jz_init_boot_config(void) ++{ ++ if (!boot_config.lcd_clks_initialized) { ++ /* the first time to scale pll */ ++ boot_config.lcd_clk = __cpm_get_lcdclk(); ++ boot_config.lcdpix_clk = __cpm_get_pixclk(); ++ boot_config.lcd_clks_initialized = 1; ++ } ++ ++ if (!boot_config.sdram_initialized) { ++ /* the first time to scale frequencies */ ++ unsigned int dmcr, rtcor; ++ unsigned int tras, rcd, tpc, trwl, trc; ++ ++ dmcr = REG_EMC_DMCR; ++ rtcor = REG_EMC_RTCOR; ++ ++ tras = (dmcr >> 13) & 0x7; ++ rcd = (dmcr >> 11) & 0x3; ++ tpc = (dmcr >> 8) & 0x7; ++ trwl = (dmcr >> 5) & 0x3; ++ trc = (dmcr >> 2) & 0x7; ++ ++ boot_config.mclk = __cpm_get_mclk() / 1000; ++ boot_config.tras = tras + 4; ++ boot_config.rcd = rcd + 1; ++ boot_config.tpc = tpc + 1; ++ boot_config.trwl = trwl + 1; ++ boot_config.trc = trc * 2 + 1; ++ boot_config.rtcor = rtcor; ++ ++ boot_config.sdram_initialized = 1; ++ } ++} ++ ++static void jz_update_dram_rtcor(unsigned int new_mclk) ++{ ++ unsigned int rtcor; ++ ++ new_mclk /= 1000; ++ rtcor = boot_config.rtcor * new_mclk / boot_config.mclk; ++ rtcor--; ++ ++ if (rtcor < 1) rtcor = 1; ++ if (rtcor > 255) rtcor = 255; ++ ++ REG_EMC_RTCOR = rtcor; ++ REG_EMC_RTCNT = rtcor; ++} ++ ++static void jz_update_dram_dmcr(unsigned int new_mclk) ++{ ++ unsigned int dmcr; ++ unsigned int tras, rcd, tpc, trwl, trc; ++ unsigned int valid_time, new_time; /* ns */ ++ ++ new_mclk /= 1000; ++ tras = boot_config.tras * new_mclk / boot_config.mclk; ++ rcd = boot_config.rcd * new_mclk / boot_config.mclk; ++ tpc = boot_config.tpc * new_mclk / boot_config.mclk; ++ trwl = boot_config.trwl * new_mclk / boot_config.mclk; ++ trc = boot_config.trc * new_mclk / boot_config.mclk; ++ ++ /* Validation checking */ ++ valid_time = (boot_config.tras * 1000000) / boot_config.mclk; ++ new_time = (tras * 1000000) / new_mclk; ++ if (new_time < valid_time) tras += 1; ++ ++ valid_time = (boot_config.rcd * 1000000) / boot_config.mclk; ++ new_time = (rcd * 1000000) / new_mclk; ++ if (new_time < valid_time) rcd += 1; ++ ++ valid_time = (boot_config.tpc * 1000000) / boot_config.mclk; ++ new_time = (tpc * 1000000) / new_mclk; ++ if (new_time < valid_time) tpc += 1; ++ ++ valid_time = (boot_config.trwl * 1000000) / boot_config.mclk; ++ new_time = (trwl * 1000000) / new_mclk; ++ if (new_time < valid_time) trwl += 1; ++ ++ valid_time = (boot_config.trc * 1000000) / boot_config.mclk; ++ new_time = (trc * 1000000) / new_mclk; ++ if (new_time < valid_time) trc += 2; ++ ++ tras = (tras < 4) ? 4: tras; ++ tras = (tras > 11) ? 11: tras; ++ tras -= 4; ++ ++ rcd = (rcd < 1) ? 1: rcd; ++ rcd = (rcd > 4) ? 4: rcd; ++ rcd -= 1; ++ ++ tpc = (tpc < 1) ? 1: tpc; ++ tpc = (tpc > 8) ? 8: tpc; ++ tpc -= 1; ++ ++ trwl = (trwl < 1) ? 1: trwl; ++ trwl = (trwl > 4) ? 4: trwl; ++ trwl -= 1; ++ ++ trc = (trc < 1) ? 1: trc; ++ trc = (trc > 15) ? 15: trc; ++ trc /= 2; ++ ++ dmcr = REG_EMC_DMCR; ++ ++ dmcr &= ~(EMC_DMCR_TRAS_MASK | EMC_DMCR_RCD_MASK | EMC_DMCR_TPC_MASK | EMC_DMCR_TRWL_MASK | EMC_DMCR_TRC_MASK); ++ dmcr |= ((tras << EMC_DMCR_TRAS_BIT) | (rcd << EMC_DMCR_RCD_BIT) | (tpc << EMC_DMCR_TPC_BIT) | (trwl << EMC_DMCR_TRWL_BIT) | (trc << EMC_DMCR_TRC_BIT)); ++ ++ REG_EMC_DMCR = dmcr; ++} ++ ++static void jz_update_dram_prev(unsigned int cur_mclk, unsigned int new_mclk) ++{ ++ /* No risk, no fun: run with interrupts on! */ ++ if (new_mclk > cur_mclk) { ++ /* We're going FASTER, so first update TRAS, RCD, TPC, TRWL ++ * and TRC of DMCR before changing the frequency. ++ */ ++ jz_update_dram_dmcr(new_mclk); ++ } else { ++ /* We're going SLOWER: first update RTCOR value ++ * before changing the frequency. ++ */ ++ jz_update_dram_rtcor(new_mclk); ++ } ++} ++ ++static void jz_update_dram_post(unsigned int cur_mclk, unsigned int new_mclk) ++{ ++ /* No risk, no fun: run with interrupts on! */ ++ if (new_mclk > cur_mclk) { ++ /* We're going FASTER, so update RTCOR ++ * after changing the frequency ++ */ ++ jz_update_dram_rtcor(new_mclk); ++ } else { ++ /* We're going SLOWER: so update TRAS, RCD, TPC, TRWL ++ * and TRC of DMCR after changing the frequency. ++ */ ++ jz_update_dram_dmcr(new_mclk); ++ } ++} ++ ++static void jz_scale_divisors(struct dpm_regs *regs) ++{ ++ unsigned int cfcr; ++ unsigned int cur_mclk, new_mclk; ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ unsigned int tmp = 0, wait = PLL_WAIT_500NS; ++ ++ cfcr = REG_CPM_CFCR; ++ cfcr &= ~((unsigned long)regs->cfcr_mask); ++ cfcr |= regs->cfcr; ++ cfcr |= CPM_CFCR_UPE; /* update immediately */ ++ ++ cur_mclk = __cpm_get_mclk(); ++ new_mclk = __cpm_get_pllout() / div[(cfcr & CPM_CFCR_MFR_MASK) >> CPM_CFCR_MFR_BIT]; ++ ++ /* Update some DRAM parameters before changing frequency */ ++ jz_update_dram_prev(cur_mclk, new_mclk); ++ ++ /* update register to change the clocks. ++ * align this code to a cache line. ++ */ ++ __asm__ __volatile__( ++ ".set noreorder\n\t" ++ ".align 5\n" ++ "sw %1,0(%0)\n\t" ++ "li %3,0\n\t" ++ "1:\n\t" ++ "bne %3,%2,1b\n\t" ++ "addi %3, 1\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ ".set reorder\n\t" ++ : ++ : "r" (CPM_CFCR), "r" (cfcr), "r" (wait), "r" (tmp)); ++ ++ /* Update some other DRAM parameters after changing frequency */ ++ jz_update_dram_post(cur_mclk, new_mclk); ++} ++ ++#ifdef CHANGE_PLL ++/* Maintain the LCD clock and pixel clock */ ++static void jz_scale_lcd_divisors(struct dpm_regs *regs) ++{ ++ unsigned int new_pll, new_lcd_div, new_lcdpix_div; ++ unsigned int cfcr; ++ unsigned int tmp = 0, wait = PLL_WAIT_500NS; ++ ++ if (!boot_config.lcd_clks_initialized) return; ++ ++ new_pll = __cpm_get_pllout(); ++ new_lcd_div = new_pll / boot_config.lcd_clk; ++ new_lcdpix_div = new_pll / boot_config.lcdpix_clk; ++ ++ if (new_lcd_div < 1) ++ new_lcd_div = 1; ++ if (new_lcd_div > 16) ++ new_lcd_div = 16; ++ ++ if (new_lcdpix_div < 1) ++ new_lcdpix_div = 1; ++ if (new_lcdpix_div > 512) ++ new_lcdpix_div = 512; ++ ++ REG_CPM_CFCR2 = new_lcdpix_div - 1; ++ ++ cfcr = REG_CPM_CFCR; ++ cfcr &= ~CPM_CFCR_LFR_MASK; ++ cfcr |= ((new_lcd_div - 1) << CPM_CFCR_LFR_BIT); ++ cfcr |= CPM_CFCR_UPE; /* update immediately */ ++ ++ /* update register to change the clocks. ++ * align this code to a cache line. ++ */ ++ __asm__ __volatile__( ++ ".set noreorder\n\t" ++ ".align 5\n" ++ "sw %1,0(%0)\n\t" ++ "li %3,0\n\t" ++ "1:\n\t" ++ "bne %3,%2,1b\n\t" ++ "addi %3, 1\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ ".set reorder\n\t" ++ : ++ : "r" (CPM_CFCR), "r" (cfcr), "r" (wait), "r" (tmp)); ++} ++ ++static void jz_scale_pll(struct dpm_regs *regs) ++{ ++ unsigned int plcr1; ++ unsigned int cur_mclk, new_mclk, new_pll; ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ int od[] = {1, 2, 2, 4}; ++ ++ plcr1 = REG_CPM_PLCR1; ++ plcr1 &= ~(regs->plcr1_mask | CPM_PLCR1_PLL1S | CPM_PLCR1_PLL1EN | CPM_PLCR1_PLL1ST_MASK); ++ regs->plcr1 &= ~CPM_PLCR1_PLL1EN; ++ plcr1 |= (regs->plcr1 | 0xff); ++ ++ /* Update some DRAM parameters before changing frequency */ ++ new_pll = JZ_EXTAL * ((plcr1>>23)+2) / ((((plcr1>>18)&0x1f)+2) * od[(plcr1>>16)&0x03]); ++ cur_mclk = __cpm_get_mclk(); ++ new_mclk = new_pll / div[(REG_CPM_CFCR>>16) & 0xf]; ++ ++ /* ++ * Update some SDRAM parameters ++ */ ++ jz_update_dram_prev(cur_mclk, new_mclk); ++ ++ /* ++ * Update PLL, align code to cache line. ++ */ ++ plcr1 |= CPM_PLCR1_PLL1EN; ++ __asm__ __volatile__( ++ ".set noreorder\n\t" ++ ".align 5\n" ++ "sw %1,0(%0)\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ ".set reorder\n\t" ++ : ++ : "r" (CPM_PLCR1), "r" (plcr1)); ++ ++ /* Update some other DRAM parameters after changing frequency */ ++ jz_update_dram_post(cur_mclk, new_mclk); ++} ++#endif ++ ++static void jz4730_transition(struct dpm_regs *regs) ++{ ++ /* ++ * Get and save some boot-time conditions. ++ */ ++ jz_init_boot_config(); ++ ++#ifdef CHANGE_PLL ++ /* ++ * Disable LCD before scaling pll. ++ * LCD and LCD pixel clocks should not be changed even if the PLL ++ * output frequency has been changed. ++ */ ++ REG_LCD_CTRL &= ~LCD_CTRL_ENA; ++#endif ++ /* ++ * Stop module clocks before scaling PLL ++ */ ++ __cpm_stop_eth(); ++ __cpm_stop_aic_pclk(); ++ __cpm_stop_aic_bitclk(); ++ ++ /* ... add more as necessary */ ++ ++ if (regs->pll_up_flag == PLL_GOES_UP) { ++ /* the pll frequency is going up, so change dividors first */ ++ jz_scale_divisors(regs); ++#ifdef CHANGE_PLL ++ jz_scale_pll(regs); ++#endif ++ } ++ else if (regs->pll_up_flag == PLL_GOES_DOWN) { ++ /* the pll frequency is going down, so change pll first */ ++#ifdef CHANGE_PLL ++ jz_scale_pll(regs); ++#endif ++ jz_scale_divisors(regs); ++ } ++ else { ++ /* the pll frequency is unchanged, so change divisors only */ ++ jz_scale_divisors(regs); ++ } ++ ++ /* ++ * Restart module clocks before scaling PLL ++ */ ++ __cpm_start_eth(); ++ __cpm_start_aic_pclk(); ++ __cpm_start_aic_bitclk(); ++ ++ /* ... add more as necessary */ ++ ++#ifdef CHANGE_PLL ++ /* Scale the LCD divisors after scaling pll */ ++ if (regs->pll_up_flag != PLL_UNCHANGED) { ++ jz_scale_lcd_divisors(regs); ++ } ++ ++ /* Enable LCD controller */ ++ REG_LCD_CTRL &= ~LCD_CTRL_DIS; ++ REG_LCD_CTRL |= LCD_CTRL_ENA; ++#endif ++ ++ /* Update system clocks */ ++ jz_update_clocks(); ++} ++ ++extern unsigned int idle_times; ++static unsigned int jz4730_freq_get(unsigned int cpu) ++{ ++ return (__cpm_get_iclk() / 1000); ++} ++ ++static unsigned int index_to_divisor(unsigned int index, struct dpm_regs *regs) ++{ ++ int n2FR[33] = { ++ 0, 0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 0, 6, 0, 0, 0, ++ 7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, ++ 9 ++ }; ++ int div[4] = {1, 2, 2, 2}; /* divisors of I:S:P:M */ ++ unsigned int div_of_cclk, new_freq, i; ++ ++ regs->pll_up_flag = PLL_UNCHANGED; ++ regs->cfcr_mask = CPM_CFCR_IFR_MASK | CPM_CFCR_SFR_MASK | CPM_CFCR_PFR_MASK | CPM_CFCR_MFR_MASK; ++ ++ new_freq = jz4730_freq_table.table[index].frequency; ++ ++ do { ++ div_of_cclk = __cpm_get_pllout() / (1000 * new_freq); ++ } while (div_of_cclk==0); ++ ++ if(div_of_cclk == 1 || div_of_cclk == 2 || div_of_cclk == 4) { ++ for(i = 1; i<4; i++) { ++ div[i] = 3; ++ } ++ } else { ++ for(i = 1; i<4; i++) { ++ div[i] = 2; ++ } ++ } ++ ++ for(i = 0; i<4; i++) { ++ div[i] *= div_of_cclk; ++ } ++ ++ dprintk("divisors of I:S:P:M = %d:%d:%d:%d\n", div[0], div[1], div[2], div[3]); ++ ++ regs->cfcr = (n2FR[div[0]] << CPM_CFCR_IFR_BIT) | ++ (n2FR[div[1]] << CPM_CFCR_SFR_BIT) | ++ (n2FR[div[2]] << CPM_CFCR_PFR_BIT) | ++ (n2FR[div[3]] << CPM_CFCR_MFR_BIT); ++ ++ return div_of_cclk; ++} ++ ++static void jz4730_set_cpu_divider_index(unsigned int cpu, unsigned int index) ++{ ++ unsigned long divisor, old_divisor; ++ struct cpufreq_freqs freqs; ++ struct dpm_regs regs; ++ ++ old_divisor = __cpm_get_pllout() / __cpm_get_iclk(); ++ divisor = index_to_divisor(index, ®s); ++ ++ freqs.old = __cpm_get_iclk() / 1000; ++ freqs.new = __cpm_get_pllout() / (1000 * divisor); ++ freqs.cpu = cpu; ++ ++ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); ++ ++ if (old_divisor != divisor) ++ jz4730_transition(®s); ++ ++ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); ++} ++ ++static int jz4730_freq_target(struct cpufreq_policy *policy, ++ unsigned int target_freq, ++ unsigned int relation) ++{ ++ unsigned int new_index = 0; ++ ++ if (cpufreq_frequency_table_target(policy, ++ &jz4730_freq_table.table[0], ++ target_freq, relation, &new_index)) ++ return -EINVAL; ++ ++ jz4730_set_cpu_divider_index(policy->cpu, new_index); ++ ++ dprintk("new frequency is %d KHz (REG_CPM_CFCR:0x%x)\n", __cpm_get_iclk() / 1000, REG_CPM_CFCR); ++ ++ return 0; ++} ++ ++static int jz4730_freq_verify(struct cpufreq_policy *policy) ++{ ++ return cpufreq_frequency_table_verify(policy, ++ &jz4730_freq_table.table[0]); ++} ++ ++static int __init jz4730_cpufreq_driver_init(struct cpufreq_policy *policy) ++{ ++ ++ struct cpufreq_frequency_table *table = &jz4730_freq_table.table[0]; ++ unsigned int MAX_FREQ; ++ ++ dprintk(KERN_INFO "Jz4730 cpufreq driver\n"); ++ ++ if (policy->cpu != 0) ++ return -EINVAL; ++ ++ policy->cur = MAX_FREQ = __cpm_get_iclk() / 1000; /* in kHz. Current and max frequency is determined by u-boot */ ++ policy->governor = CPUFREQ_DEFAULT_GOVERNOR; ++ ++ policy->cpuinfo.min_freq = MAX_FREQ/8; ++ policy->cpuinfo.max_freq = MAX_FREQ; ++ policy->cpuinfo.transition_latency = 100000; /* in 10^(-9) s = nanoseconds */ ++ ++ table[0].index = 0; ++ table[0].frequency = MAX_FREQ/8; ++ table[1].index = 1; ++ table[1].frequency = MAX_FREQ/6; ++ table[2].index = 2; ++ table[2].frequency = MAX_FREQ/4; ++ table[3].index = 3; ++ table[3].frequency = MAX_FREQ/3; ++ table[4].index = 4; ++ table[4].frequency = MAX_FREQ/2; ++ table[5].index = 5; ++ table[5].frequency = MAX_FREQ; ++ table[6].index = 6; ++ table[6].frequency = CPUFREQ_TABLE_END; ++ ++ return cpufreq_frequency_table_cpuinfo(policy, table); ++} ++ ++static struct cpufreq_driver cpufreq_jz4730_driver = { ++// .flags = CPUFREQ_STICKY, ++ .init = jz4730_cpufreq_driver_init, ++ .verify = jz4730_freq_verify, ++ .target = jz4730_freq_target, ++ .get = jz4730_freq_get, ++ .name = "jz4730", ++}; ++ ++static int __init jz4730_cpufreq_init(void) ++{ ++ return cpufreq_register_driver(&cpufreq_jz4730_driver); ++} ++ ++static void __exit jz4730_cpufreq_exit(void) ++{ ++ cpufreq_unregister_driver(&cpufreq_jz4730_driver); ++} ++ ++module_init(jz4730_cpufreq_init); ++module_exit(jz4730_cpufreq_exit); ++ ++MODULE_AUTHOR("Regen "); ++MODULE_DESCRIPTION("cpufreq driver for Jz4730"); ++MODULE_LICENSE("GPL"); ++ +diff -urN linux-2.6.24.7.old/arch/mips/jz4730/dma.c linux-2.6.24.7/arch/mips/jz4730/dma.c +--- linux-2.6.24.7.old/arch/mips/jz4730/dma.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4730/dma.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,509 @@ ++/* ++ * linux/arch/mips/jz4730/dma.c ++ * ++ * JZ4730 DMA PC-like APIs. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* ++ * A note on resource allocation: ++ * ++ * All drivers needing DMA channels, should allocate and release them ++ * through the public routines `jz_request_dma()' and `jz_free_dma()'. ++ * ++ * In order to avoid problems, all processes should allocate resources in ++ * the same sequence and release them in the reverse order. ++ * ++ * So, when allocating DMAs and IRQs, first allocate the DMA, then the IRQ. ++ * When releasing them, first release the IRQ, then release the DMA. The ++ * main reason for this order is that, if you are requesting the DMA buffer ++ * done interrupt, you won't know the irq number until the DMA channel is ++ * returned from jz_request_dma(). ++ */ ++ ++struct jz_dma_chan jz_dma_table[NUM_DMA] = { ++ {dev_id:-1,}, ++ {dev_id:-1,}, ++ {dev_id:-1,}, ++ {dev_id:-1,}, ++ {dev_id:-1,}, ++ {dev_id:-1,}, ++}; ++ ++ ++// Device FIFO addresses and default DMA modes ++static const struct { ++ unsigned int fifo_addr; ++ unsigned int dma_mode; ++ unsigned int dma_source; ++} dma_dev_table[NUM_DMA_DEV] = { ++ {CPHYSADDR(UART0_BASE), DMA_8bit_TX_CONF|DMA_MODE_WRITE, DMAC_DRSR_RS_UART0OUT}, ++ {CPHYSADDR(UART0_BASE), DMA_8bit_RX_CONF|DMA_MODE_READ, DMAC_DRSR_RS_UART0IN}, ++ {CPHYSADDR(UART1_BASE), DMA_8bit_TX_CONF|DMA_MODE_WRITE, DMAC_DRSR_RS_UART1OUT}, ++ {CPHYSADDR(UART1_BASE), DMA_8bit_RX_CONF|DMA_MODE_READ, DMAC_DRSR_RS_UART1IN}, ++ {CPHYSADDR(UART2_BASE), DMA_8bit_TX_CONF|DMA_MODE_WRITE, DMAC_DRSR_RS_UART2OUT}, ++ {CPHYSADDR(UART2_BASE), DMA_8bit_RX_CONF|DMA_MODE_READ, DMAC_DRSR_RS_UART2IN}, ++ {CPHYSADDR(UART3_BASE), DMA_8bit_TX_CONF|DMA_MODE_WRITE, DMAC_DRSR_RS_UART3OUT}, ++ {CPHYSADDR(UART3_BASE), DMA_8bit_RX_CONF|DMA_MODE_READ, DMAC_DRSR_RS_UART3IN}, ++ {CPHYSADDR(SSI_DR), DMA_32bit_TX_CONF|DMA_MODE_WRITE, DMAC_DRSR_RS_SSIOUT}, ++ {CPHYSADDR(SSI_DR), DMA_32bit_RX_CONF|DMA_MODE_READ, DMAC_DRSR_RS_SSIIN}, ++ {CPHYSADDR(MSC_TXFIFO), DMA_32bit_TX_CONF|DMA_MODE_WRITE, DMAC_DRSR_RS_MSCOUT}, ++ {CPHYSADDR(MSC_RXFIFO), DMA_32bit_RX_CONF|DMA_MODE_READ, DMAC_DRSR_RS_MSCIN}, ++ {CPHYSADDR(AIC_DR), DMA_32bit_TX_CONF|DMA_MODE_WRITE, DMAC_DRSR_RS_AICOUT}, ++ {CPHYSADDR(AIC_DR), DMA_32bit_RX_CONF|DMA_MODE_READ, DMAC_DRSR_RS_AICIN}, ++ {0, DMA_AUTOINIT, 0}, ++}; ++ ++ ++int jz_dma_read_proc(char *buf, char **start, off_t fpos, ++ int length, int *eof, void *data) ++{ ++ int i, len = 0; ++ struct jz_dma_chan *chan; ++ ++ for (i = 0; i < NUM_DMA; i++) { ++ if ((chan = get_dma_chan(i)) != NULL) { ++ len += sprintf(buf + len, "%2d: %s\n", ++ i, chan->dev_str); ++ } ++ } ++ ++ if (fpos >= len) { ++ *start = buf; ++ *eof = 1; ++ return 0; ++ } ++ *start = buf + fpos; ++ if ((len -= fpos) > length) ++ return length; ++ *eof = 1; ++ return len; ++} ++ ++ ++void dump_jz_dma_channel(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan; ++ ++ if (dmanr > NUM_DMA) ++ return; ++ chan = &jz_dma_table[dmanr]; ++ ++ printk(KERN_INFO "DMA%d Register Dump:\n", dmanr); ++ printk(KERN_INFO " DMACR= 0x%08x\n", REG_DMAC_DMACR); ++ printk(KERN_INFO " DSAR = 0x%08x\n", REG_DMAC_DSAR(dmanr)); ++ printk(KERN_INFO " DDAR = 0x%08x\n", REG_DMAC_DDAR(dmanr)); ++ printk(KERN_INFO " DTCR = 0x%08x\n", REG_DMAC_DTCR(dmanr)); ++ printk(KERN_INFO " DRSR = 0x%08x\n", REG_DMAC_DRSR(dmanr)); ++ printk(KERN_INFO " DCCSR = 0x%08x\n", REG_DMAC_DCCSR(dmanr)); ++} ++ ++ ++/** ++ * jz_request_dma - dynamically allcate an idle DMA channel to return ++ * @dev_id: the specified dma device id or DMA_ID_RAW_REQ ++ * @dev_str: the specified dma device string name ++ * @irqhandler: the irq handler, or NULL ++ * @irqflags: the irq handler flags ++ * @irq_dev_id: the irq handler device id for shared irq ++ * ++ * Finds a free channel, and binds the requested device to it. ++ * Returns the allocated channel number, or negative on error. ++ * Requests the DMA done IRQ if irqhandler != NULL. ++ * ++*/ ++int jz_request_dma(int dev_id, const char *dev_str, ++ irqreturn_t (*irqhandler)(int, void *), ++ unsigned long irqflags, ++ void *irq_dev_id) ++{ ++ struct jz_dma_chan *chan; ++ int i, ret; ++ ++ if (dev_id < 0 || dev_id >= NUM_DMA_DEV) ++ return -EINVAL; ++ ++ for (i = 0; i < NUM_DMA; i++) { ++ if (jz_dma_table[i].dev_id < 0) ++ break; ++ } ++ if (i == NUM_DMA) ++ return -ENODEV; ++ ++ chan = &jz_dma_table[i]; ++ ++ if (irqhandler) { ++ chan->irq = IRQ_DMA_0 + i; // see intc.h ++ chan->irq_dev = irq_dev_id; ++ if ((ret = request_irq(chan->irq, irqhandler, irqflags, ++ dev_str, chan->irq_dev))) { ++ chan->irq = 0; ++ chan->irq_dev = NULL; ++ return ret; ++ } ++ } else { ++ chan->irq = 0; ++ chan->irq_dev = NULL; ++ } ++ ++ // fill it in ++ chan->io = i; ++ chan->dev_id = dev_id; ++ chan->dev_str = dev_str; ++ chan->fifo_addr = dma_dev_table[dev_id].fifo_addr; ++ chan->mode = dma_dev_table[dev_id].dma_mode; ++ chan->source = dma_dev_table[dev_id].dma_source; ++ ++ return i; ++} ++ ++void jz_free_dma(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) { ++ printk("Trying to free DMA%d\n", dmanr); ++ return; ++ } ++ ++ disable_dma(dmanr); ++ if (chan->irq) ++ free_irq(chan->irq, chan->irq_dev); ++ ++ chan->irq = 0; ++ chan->irq_dev = NULL; ++ chan->dev_id = -1; ++} ++ ++void jz_set_dma_dest_width(int dmanr, int nbit) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ chan->mode &= ~DMAC_DCCSR_DWDH_MASK; ++ switch (nbit) { ++ case 8: ++ chan->mode |= DMAC_DCCSR_DWDH_8; ++ break; ++ case 16: ++ chan->mode |= DMAC_DCCSR_DWDH_16; ++ break; ++ case 32: ++ chan->mode |= DMAC_DCCSR_DWDH_32; ++ break; ++ } ++} ++ ++void jz_set_dma_src_width(int dmanr, int nbit) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ chan->mode &= ~DMAC_DCCSR_SWDH_MASK; ++ switch (nbit) { ++ case 8: ++ chan->mode |= DMAC_DCCSR_SWDH_8; ++ break; ++ case 16: ++ chan->mode |= DMAC_DCCSR_SWDH_16; ++ break; ++ case 32: ++ chan->mode |= DMAC_DCCSR_SWDH_32; ++ break; ++ } ++} ++ ++void jz_set_dma_block_size(int dmanr, int nbyte) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ chan->mode &= ~DMAC_DCCSR_DS_MASK; ++ switch (nbyte) { ++ case 1: ++ chan->mode |= DMAC_DCCSR_DS_8b; ++ break; ++ case 2: ++ chan->mode |= DMAC_DCCSR_DS_16b; ++ break; ++ case 4: ++ chan->mode |= DMAC_DCCSR_DS_32b; ++ break; ++ case 16: ++ chan->mode |= DMAC_DCCSR_DS_16B; ++ break; ++ case 32: ++ chan->mode |= DMAC_DCCSR_DS_32B; ++ break; ++ } ++} ++ ++/** ++ * jz_set_dma_mode - do the raw settings for the specified DMA channel ++ * @dmanr: the specified DMA channel ++ * @mode: dma operate mode, DMA_MODE_READ or DMA_MODE_WRITE ++ * @dma_mode: dma raw mode ++ * @dma_source: dma raw request source ++ * @fifo_addr: dma raw device fifo address ++ * ++ * Ensure call jz_request_dma(DMA_ID_RAW_REQ, ...) first, then call ++ * jz_set_dma_mode() rather than set_dma_mode() if you work with ++ * and external request dma device. ++ * ++ * NOTE: Don not dynamically allocate dma channel if one external request ++ * dma device will occupy this channel. ++*/ ++int jz_set_dma_mode(unsigned int dmanr, unsigned int mode, ++ unsigned int dma_mode, unsigned int dma_source, ++ unsigned int fifo_addr) ++{ ++ int dev_id, i; ++ struct jz_dma_chan *chan; ++ ++ if (dmanr > NUM_DMA) ++ return -ENODEV; ++ for (i = 0; i < NUM_DMA; i++) { ++ if (jz_dma_table[i].dev_id < 0) ++ break; ++ } ++ if (i == NUM_DMA) ++ return -ENODEV; ++ ++ chan = &jz_dma_table[dmanr]; ++ dev_id = chan->dev_id; ++ if (dev_id > 0) { ++ printk(KERN_DEBUG "%s sets the allocated DMA channel %d!\n", ++ __FUNCTION__, dmanr); ++ return -ENODEV; ++ } ++ ++ /* clone it from the dynamically allocated. */ ++ if (i != dmanr) { ++ chan->irq = jz_dma_table[i].irq; ++ chan->irq_dev = jz_dma_table[i].irq_dev; ++ chan->dev_str = jz_dma_table[i].dev_str; ++ jz_dma_table[i].irq = 0; ++ jz_dma_table[i].irq_dev = NULL; ++ jz_dma_table[i].dev_id = -1; ++ } ++ chan->dev_id = DMA_ID_RAW_SET; ++ chan->io = dmanr; ++ chan->fifo_addr = fifo_addr; ++ chan->mode = dma_mode; ++ chan->source = dma_source; ++ ++ set_dma_mode(dmanr, dma_mode); ++ ++ return dmanr; ++} ++ ++void enable_dma(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ ++ REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TC | DMAC_DCCSR_AR); ++ __dmac_enable_channel(dmanr); ++ if (chan->irq) ++ __dmac_channel_enable_irq(dmanr); ++} ++ ++#define DMA_DISABLE_POLL 0x5000 ++ ++void disable_dma(unsigned int dmanr) ++{ ++ int i; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ if (!__dmac_channel_enabled(dmanr)) ++ return; ++ ++ for (i = 0; i < DMA_DISABLE_POLL; i++) ++ if (__dmac_channel_transmit_end_detected(dmanr)) ++ break; ++#if 0 ++ if (i == DMA_DISABLE_POLL) ++ printk(KERN_INFO "disable_dma: poll expired!\n"); ++#endif ++ ++ __dmac_disable_channel(dmanr); ++ if (chan->irq) ++ __dmac_channel_disable_irq(dmanr); ++} ++ ++/* note: DMA_MODE_MASK is simulated by sw, DCCSR_MODE_MASK mask hw bits */ ++void set_dma_mode(unsigned int dmanr, unsigned int mode) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ mode &= ~(DMAC_DCCSR_TC | DMAC_DCCSR_AR); ++ chan->mode |= mode & ~(DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM | DMAC_DCCSR_DAM); ++ mode &= DMA_MODE_MASK; ++ if (mode == DMA_MODE_READ) { ++ chan->mode |= DMAC_DCCSR_DAM; ++ chan->mode &= ~DMAC_DCCSR_SAM; ++ } else if (mode == DMA_MODE_WRITE) { ++ chan->mode |= DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM; ++ chan->mode &= ~DMAC_DCCSR_DAM; ++ } else { ++ printk(KERN_DEBUG "set_dma_mode() support DMA_MODE_READ or DMA_MODE_WRITE!\n"); ++ } ++ REG_DMAC_DCCSR(chan->io) = chan->mode & ~DMA_MODE_MASK; ++ REG_DMAC_DRSR(chan->io) = chan->source; ++} ++ ++void jz_set_oss_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ switch (audio_fmt) { ++ case AFMT_U8: ++ /* SNDRV_PCM_FORMAT_S8 burst mode : 32BIT */ ++ break; ++ case AFMT_S16_LE: ++ /* SNDRV_PCM_FORMAT_S16_LE burst mode : 16BYTE */ ++ if (mode == DMA_MODE_READ) { ++ mode &= ~(DMAC_DCCSR_TC | DMAC_DCCSR_AR); ++ chan->mode = DMA_AIC_32_16BYTE_RX_CMD | DMA_MODE_READ; ++ chan->mode |= mode & ~(DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM | DMAC_DCCSR_DAM); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCCSR_DAM; ++ chan->mode &= ~DMAC_DCCSR_SAM; ++ } else if (mode == DMA_MODE_WRITE) { ++ mode &= ~(DMAC_DCCSR_TC | DMAC_DCCSR_AR); ++ chan->mode = DMA_AIC_32_16BYTE_TX_CMD | DMA_MODE_WRITE; ++ chan->mode |= mode & ~(DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM |DMAC_DCCSR_DAM); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM; ++ chan->mode &= ~DMAC_DCCSR_DAM; ++ } else ++ printk("jz_set_oss_dma() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); ++ ++ REG_DMAC_DCCSR(chan->io) = chan->mode & ~DMA_MODE_MASK; ++ REG_DMAC_DRSR(chan->io) = chan->source; ++ break; ++ } ++} ++ ++void jz_set_alsa_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ switch (audio_fmt) { ++ case 8: ++ /* SNDRV_PCM_FORMAT_S8 burst mode : 32BIT */ ++ break; ++ case 16: ++ /* SNDRV_PCM_FORMAT_S16_LE burst mode : 16BYTE */ ++ if (mode == DMA_MODE_READ) { ++ mode &= ~(DMAC_DCCSR_TC | DMAC_DCCSR_AR); ++ chan->mode = DMA_AIC_16BYTE_RX_CMD | DMA_MODE_READ; ++ chan->mode |= mode & ~(DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM | DMAC_DCCSR_DAM); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCCSR_DAM; ++ chan->mode &= ~DMAC_DCCSR_SAM; ++ } else if (mode == DMA_MODE_WRITE) { ++ mode &= ~(DMAC_DCCSR_TC | DMAC_DCCSR_AR); ++ chan->mode = DMA_AIC_16BYTE_TX_CMD | DMA_MODE_WRITE; ++ chan->mode |= mode & ~(DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM | DMAC_DCCSR_DAM); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM; ++ chan->mode &= ~DMAC_DCCSR_DAM; ++ } else ++ printk("jz_set_alsa_dma() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); ++ ++ REG_DMAC_DCCSR(chan->io) = chan->mode & ~DMA_MODE_MASK; ++ REG_DMAC_DRSR(chan->io) = chan->source; ++ break; ++ } ++} ++ ++void set_dma_addr(unsigned int dmanr, unsigned int a) ++{ ++ unsigned int mode; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ mode = chan->mode & DMA_MODE_MASK; ++ if (mode == DMA_MODE_READ) { ++ REG_DMAC_DSAR(chan->io) = chan->fifo_addr; ++ REG_DMAC_DDAR(chan->io) = a; ++ } else if (mode == DMA_MODE_WRITE) { ++ REG_DMAC_DSAR(chan->io) = a; ++ REG_DMAC_DDAR(chan->io) = chan->fifo_addr; ++ } else ++ printk(KERN_DEBUG "Driver should call set_dma_mode() ahead set_dma_addr()!\n"); ++} ++ ++void set_dma_count(unsigned int dmanr, unsigned int count) ++{ ++ unsigned int mode; ++ int dma_ds[] = {4, 1, 2, 16, 32}; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ mode = (chan->mode & DMAC_DCCSR_DS_MASK) >> DMAC_DCCSR_DS_BIT; ++ count = count / dma_ds[mode]; ++ REG_DMAC_DTCR(chan->io) = count; ++} ++ ++int get_dma_residue(unsigned int dmanr) ++{ ++ int count; ++ unsigned int mode; ++ int dma_ds[] = {4, 1, 2, 16, 32}; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 0; ++ ++ mode = (chan->mode & DMAC_DCCSR_DS_MASK) >> DMAC_DCCSR_DS_BIT; ++ count = REG_DMAC_DTCR(chan->io); ++ count = count * dma_ds[mode]; ++ ++ return count; ++} ++ ++EXPORT_SYMBOL(jz_dma_table); ++EXPORT_SYMBOL(jz_request_dma); ++EXPORT_SYMBOL(jz_free_dma); ++EXPORT_SYMBOL(jz_set_dma_src_width); ++EXPORT_SYMBOL(jz_set_dma_dest_width); ++EXPORT_SYMBOL(jz_set_dma_block_size); ++EXPORT_SYMBOL(jz_set_dma_mode); ++EXPORT_SYMBOL(set_dma_mode); ++EXPORT_SYMBOL(jz_set_oss_dma); ++EXPORT_SYMBOL(jz_set_alsa_dma); ++EXPORT_SYMBOL(set_dma_addr); ++EXPORT_SYMBOL(set_dma_count); ++EXPORT_SYMBOL(get_dma_residue); ++EXPORT_SYMBOL(enable_dma); ++EXPORT_SYMBOL(disable_dma); ++EXPORT_SYMBOL(dump_jz_dma_channel); +diff -urN linux-2.6.24.7.old/arch/mips/jz4730/i2c.c linux-2.6.24.7/arch/mips/jz4730/i2c.c +--- linux-2.6.24.7.old/arch/mips/jz4730/i2c.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4730/i2c.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,214 @@ ++/* ++ * linux/arch/mips/jz4730/i2c.c ++ * ++ * JZ4730 I2C APIs. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* I2C protocol */ ++#define I2C_READ 1 ++#define I2C_WRITE 0 ++ ++#define TIMEOUT 1000 ++ ++/* ++ * I2C bus protocol basic routines ++ */ ++static int i2c_put_data(unsigned char data) ++{ ++ unsigned int timeout = TIMEOUT * 10; ++ ++ __i2c_write(data); ++ __i2c_set_drf(); ++ while (__i2c_check_drf() != 0); ++ while (!__i2c_transmit_ended()); ++ while (!__i2c_received_ack() && timeout) ++ timeout--; ++ ++ if (timeout) ++ return 0; ++ else ++ return -ETIMEDOUT; ++} ++ ++static int i2c_get_data(unsigned char *data, int ack) ++{ ++ int timeout = TIMEOUT*10; ++ ++ if (!ack) ++ __i2c_send_nack(); ++ else ++ __i2c_send_ack(); ++ ++ while (__i2c_check_drf() == 0 && timeout) ++ timeout--; ++ ++ if (timeout) { ++ if (!ack) ++ __i2c_send_stop(); ++ *data = __i2c_read(); ++ __i2c_clear_drf(); ++ return 0; ++ } else ++ return -ETIMEDOUT; ++} ++ ++/* ++ * I2C interface ++ */ ++void i2c_open(void) ++{ ++ __i2c_set_clk(jz_clocks.devclk, 10000); /* default 10 KHz */ ++ __i2c_enable(); ++} ++ ++void i2c_close(void) ++{ ++ udelay(300); /* wait for STOP goes over. */ ++ __i2c_disable(); ++} ++ ++void i2c_setclk(unsigned int i2cclk) ++{ ++ __i2c_set_clk(jz_clocks.devclk, i2cclk); ++} ++ ++int i2c_lseek(unsigned char device, unsigned char offset) ++{ ++ __i2c_send_nack(); /* Master does not send ACK, slave sends it */ ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) ++ goto device_err; ++ if (i2c_put_data(offset) < 0) ++ goto address_err; ++ return 0; ++ device_err: ++ printk(KERN_DEBUG "No I2C device (0x%02x) installed.\n", device); ++ __i2c_send_stop(); ++ return -ENODEV; ++ address_err: ++ printk(KERN_DEBUG "No I2C device (0x%02x) response.\n", device); ++ __i2c_send_stop(); ++ return -EREMOTEIO; ++} ++ ++int i2c_read(unsigned char device, unsigned char *buf, ++ unsigned char address, int count) ++{ ++ int cnt = count; ++ int timeout = 5; ++ ++L_try_again: ++ ++ if (timeout < 0) ++ goto L_timeout; ++ ++ __i2c_send_nack(); /* Master does not send ACK, slave sends it */ ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) ++ goto device_werr; ++ if (i2c_put_data(address) < 0) ++ goto address_err; ++ ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_READ ) < 0) ++ goto device_rerr; ++ __i2c_send_ack(); /* Master sends ACK for continue reading */ ++ while (cnt) { ++ if (cnt == 1) { ++ if (i2c_get_data(buf, 0) < 0) ++ break; ++ } else { ++ if (i2c_get_data(buf, 1) < 0) ++ break; ++ } ++ cnt--; ++ buf++; ++ } ++ ++ __i2c_send_stop(); ++ return count - cnt; ++ device_rerr: ++ device_werr: ++ address_err: ++ timeout --; ++ __i2c_send_stop(); ++ goto L_try_again; ++ ++L_timeout: ++ __i2c_send_stop(); ++ printk("Read I2C device 0x%2x failed.\n", device); ++ return -ENODEV; ++} ++ ++int i2c_write(unsigned char device, unsigned char *buf, ++ unsigned char address, int count) ++{ ++ int cnt = count; ++ int cnt_in_pg; ++ int timeout = 5; ++ unsigned char *tmpbuf; ++ unsigned char tmpaddr; ++ ++ __i2c_send_nack(); /* Master does not send ACK, slave sends it */ ++ ++ W_try_again: ++ if (timeout < 0) ++ goto W_timeout; ++ ++ cnt = count; ++ tmpbuf = (unsigned char *)buf; ++ tmpaddr = address; ++ ++ start_write_page: ++ cnt_in_pg = 0; ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) ++ goto device_err; ++ if (i2c_put_data(tmpaddr) < 0) ++ goto address_err; ++ while (cnt) { ++ if (++cnt_in_pg > 8) { ++ __i2c_send_stop(); ++ mdelay(1); ++ tmpaddr += 8; ++ goto start_write_page; ++ } ++ if (i2c_put_data(*tmpbuf) < 0) ++ break; ++ cnt--; ++ tmpbuf++; ++ } ++ __i2c_send_stop(); ++ return count - cnt; ++ device_err: ++ address_err: ++ timeout--; ++ __i2c_send_stop(); ++ goto W_try_again; ++ ++ W_timeout: ++ printk(KERN_DEBUG "Write I2C device 0x%2x failed.\n", device); ++ __i2c_send_stop(); ++ return -ENODEV; ++} ++ ++EXPORT_SYMBOL(i2c_open); ++EXPORT_SYMBOL(i2c_close); ++EXPORT_SYMBOL(i2c_setclk); ++EXPORT_SYMBOL(i2c_read); ++EXPORT_SYMBOL(i2c_write); +diff -urN linux-2.6.24.7.old/arch/mips/jz4730/irq.c linux-2.6.24.7/arch/mips/jz4730/irq.c +--- linux-2.6.24.7.old/arch/mips/jz4730/irq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4730/irq.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,266 @@ ++/* ++ * linux/arch/mips/jz4730/irq.c ++ * ++ * JZ4730 interrupt routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * INTC irq type ++ */ ++ ++static void enable_intc_irq(unsigned int irq) ++{ ++ __intc_unmask_irq(irq); ++} ++ ++static void disable_intc_irq(unsigned int irq) ++{ ++ __intc_mask_irq(irq); ++} ++ ++static void mask_and_ack_intc_irq(unsigned int irq) ++{ ++ __intc_mask_irq(irq); ++ __intc_ack_irq(irq); ++} ++ ++static void end_intc_irq(unsigned int irq) ++{ ++ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { ++ enable_intc_irq(irq); ++ } ++} ++ ++static unsigned int startup_intc_irq(unsigned int irq) ++{ ++ enable_intc_irq(irq); ++ return 0; ++} ++ ++static void shutdown_intc_irq(unsigned int irq) ++{ ++ disable_intc_irq(irq); ++} ++ ++static struct irq_chip intc_irq_type = { ++ .typename = "INTC", ++ .startup = startup_intc_irq, ++ .shutdown = shutdown_intc_irq, ++ .enable = enable_intc_irq, ++ .disable = disable_intc_irq, ++ .ack = mask_and_ack_intc_irq, ++ .end = end_intc_irq, ++}; ++ ++/* ++ * GPIO irq type ++ */ ++ ++static void enable_gpio_irq(unsigned int irq) ++{ ++ unsigned int intc_irq; ++ ++ if (irq < (IRQ_GPIO_0 + 32)) { ++ intc_irq = IRQ_GPIO0; ++ } ++ else if (irq < (IRQ_GPIO_0 + 64)) { ++ intc_irq = IRQ_GPIO1; ++ } ++ else if (irq < (IRQ_GPIO_0 + 96)) { ++ intc_irq = IRQ_GPIO2; ++ } ++ else { ++ intc_irq = IRQ_GPIO3; ++ } ++ ++ enable_intc_irq(intc_irq); ++ __gpio_unmask_irq(irq - IRQ_GPIO_0); ++} ++ ++static void disable_gpio_irq(unsigned int irq) ++{ ++ __gpio_mask_irq(irq - IRQ_GPIO_0); ++} ++ ++static void mask_and_ack_gpio_irq(unsigned int irq) ++{ ++ __gpio_mask_irq(irq - IRQ_GPIO_0); ++ __gpio_ack_irq(irq - IRQ_GPIO_0); ++} ++ ++static void end_gpio_irq(unsigned int irq) ++{ ++ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { ++ enable_gpio_irq(irq); ++ } ++} ++ ++static unsigned int startup_gpio_irq(unsigned int irq) ++{ ++ enable_gpio_irq(irq); ++ return 0; ++} ++ ++static void shutdown_gpio_irq(unsigned int irq) ++{ ++ disable_gpio_irq(irq); ++} ++ ++static struct irq_chip gpio_irq_type = { ++ .typename = "GPIO", ++ .startup = startup_gpio_irq, ++ .shutdown = shutdown_gpio_irq, ++ .enable = enable_gpio_irq, ++ .disable = disable_gpio_irq, ++ .ack = mask_and_ack_gpio_irq, ++ .end = end_gpio_irq, ++}; ++ ++/* ++ * DMA irq type ++ */ ++ ++static void enable_dma_irq(unsigned int irq) ++{ ++ __intc_unmask_irq(IRQ_DMAC); ++ __dmac_channel_enable_irq(irq - IRQ_DMA_0); ++} ++ ++static void disable_dma_irq(unsigned int irq) ++{ ++ __dmac_channel_disable_irq(irq - IRQ_DMA_0); ++} ++ ++static void mask_and_ack_dma_irq(unsigned int irq) ++{ ++ __intc_ack_irq(IRQ_DMAC); ++ __dmac_channel_disable_irq(irq - IRQ_DMA_0); ++} ++ ++static void end_dma_irq(unsigned int irq) ++{ ++ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { ++ enable_dma_irq(irq); ++ } ++} ++ ++static unsigned int startup_dma_irq(unsigned int irq) ++{ ++ enable_dma_irq(irq); ++ return 0; ++} ++ ++static void shutdown_dma_irq(unsigned int irq) ++{ ++ disable_dma_irq(irq); ++} ++ ++static struct irq_chip dma_irq_type = { ++ .typename = "DMA", ++ .startup = startup_dma_irq, ++ .shutdown = shutdown_dma_irq, ++ .enable = enable_dma_irq, ++ .disable = disable_dma_irq, ++ .ack = mask_and_ack_dma_irq, ++ .end = end_dma_irq, ++}; ++ ++//---------------------------------------------------------------------- ++ ++void __init arch_init_irq(void) ++{ ++ int i; ++ ++ clear_c0_status(0xff04); /* clear ERL */ ++ set_c0_status(0x0400); /* set IP2 */ ++ ++ /* Set up INTC irq ++ */ ++ for (i = 0; i < 32; i++) { ++ disable_intc_irq(i); ++ irq_desc[i].chip = &intc_irq_type; ++ } ++ ++ /* Set up DMAC irq ++ */ ++ for (i = 0; i < NUM_DMA; i++) { ++ disable_dma_irq(IRQ_DMA_0 + i); ++ irq_desc[IRQ_DMA_0 + i].chip = &dma_irq_type; ++ } ++ ++ /* Set up GPIO irq ++ */ ++ for (i = 0; i < NUM_GPIO; i++) { ++ disable_gpio_irq(IRQ_GPIO_0 + i); ++ irq_desc[IRQ_GPIO_0 + i].chip = &gpio_irq_type; ++ } ++} ++ ++static int plat_real_irq(int irq) ++{ ++ switch (irq) { ++ case IRQ_GPIO0: ++ irq = __gpio_group_irq(0) + IRQ_GPIO_0; ++ break; ++ case IRQ_GPIO1: ++ irq = __gpio_group_irq(1) + IRQ_GPIO_0 + 32; ++ break; ++ case IRQ_GPIO2: ++ irq = __gpio_group_irq(2) + IRQ_GPIO_0 + 64; ++ break; ++ case IRQ_GPIO3: ++ irq = __gpio_group_irq(3) + IRQ_GPIO_0 + 96; ++ break; ++ case IRQ_DMAC: ++ irq = __dmac_get_irq() + IRQ_DMA_0; ++ break; ++ } ++ ++ return irq; ++} ++ ++asmlinkage void plat_irq_dispatch(void) ++{ ++ int irq = 0; ++ static unsigned long intc_ipr = 0; ++ ++ intc_ipr |= REG_INTC_IPR; ++ ++ if (!intc_ipr) return; ++ ++ irq = ffs(intc_ipr) - 1; ++ intc_ipr &= ~(1< ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* OHCI (USB full speed host controller) */ ++static struct resource jz_usb_ohci_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(UHC_BASE), // phys addr for ioremap ++ .end = CPHYSADDR(UHC_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_UHC, ++ .end = IRQ_UHC, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++/* The dmamask must be set for OHCI to work */ ++static u64 ohci_dmamask = ~(u32)0; ++ ++static struct platform_device jz_usb_ohci_device = { ++ .name = "jz-ohci", ++ .id = 0, ++ .dev = { ++ .dma_mask = &ohci_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_usb_ohci_resources), ++ .resource = jz_usb_ohci_resources, ++}; ++ ++/*** LCD controller ***/ ++static struct resource jz_lcd_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(LCD_BASE), ++ .end = CPHYSADDR(LCD_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_LCD, ++ .end = IRQ_LCD, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static u64 jz_lcd_dmamask = ~(u32)0; ++ ++static struct platform_device jz_lcd_device = { ++ .name = "jz-lcd", ++ .id = 0, ++ .dev = { ++ .dma_mask = &jz_lcd_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_lcd_resources), ++ .resource = jz_lcd_resources, ++}; ++ ++/* UDC (USB gadget controller) */ ++static struct resource jz_usb_gdt_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(UDC_BASE), ++ .end = CPHYSADDR(UDC_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_UDC, ++ .end = IRQ_UDC, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static u64 udc_dmamask = ~(u32)0; ++ ++static struct platform_device jz_usb_gdt_device = { ++ .name = "jz-udc", ++ .id = 0, ++ .dev = { ++ .dma_mask = &udc_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_usb_gdt_resources), ++ .resource = jz_usb_gdt_resources, ++}; ++ ++/** MMC/SD controller **/ ++static struct resource jz_mmc_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(MSC_BASE), ++ .end = CPHYSADDR(MSC_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_MSC, ++ .end = IRQ_MSC, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static u64 jz_mmc_dmamask = ~(u32)0; ++ ++static struct platform_device jz_mmc_device = { ++ .name = "jz-mmc", ++ .id = 0, ++ .dev = { ++ .dma_mask = &jz_mmc_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_mmc_resources), ++ .resource = jz_mmc_resources, ++}; ++ ++/* All */ ++static struct platform_device *jz_platform_devices[] __initdata = { ++ &jz_usb_ohci_device, ++ &jz_lcd_device, ++ &jz_usb_gdt_device, ++ &jz_mmc_device, ++}; ++ ++static int __init jz_platform_init(void) ++{ ++ return platform_add_devices(jz_platform_devices, ARRAY_SIZE(jz_platform_devices)); ++} ++ ++arch_initcall(jz_platform_init); +diff -urN linux-2.6.24.7.old/arch/mips/jz4730/pm.c linux-2.6.24.7/arch/mips/jz4730/pm.c +--- linux-2.6.24.7.old/arch/mips/jz4730/pm.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4730/pm.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1098 @@ ++/* ++ * linux/arch/mips/jz4730/pm.c ++ * ++ * Jz4730 Power Management Routines ++ * ++ * Copyright 2005 Ingenic Semiconductor ++ * Wei Jianli ++ * Huang Lihong ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++extern void jz_cpu_suspend(void); ++extern void jz_cpu_resume(void); ++ ++static void jz_board_pm_suspend(void); ++ ++#define SAVE(x,s) sleep_save[SLEEP_SAVE_##x] = REG##s(x) ++#define RESTORE(x,s) REG##s(x) = sleep_save[SLEEP_SAVE_##x] ++ ++/* ++ * List of global jz4730 peripheral registers to preserve. ++ * More ones like core register and general purpose register values ++ * are preserved with the stack pointer in sleep.S. ++ */ ++enum { SLEEP_SAVE_START = 0, ++ ++ /* CPM */ ++ SLEEP_SAVE_CPM_MSCR, SLEEP_SAVE_CPM_PLCR1, ++ ++ /* WDT */ ++ SLEEP_SAVE_WDT_WTCNT, SLEEP_SAVE_WDT_WTCSR, ++ ++ /* OST */ ++ SLEEP_SAVE_OST_TER, ++ SLEEP_SAVE_OST_TCSR0, SLEEP_SAVE_OST_TCSR1, SLEEP_SAVE_OST_TCSR2, ++ SLEEP_SAVE_OST_TRDR0, SLEEP_SAVE_OST_TRDR1, SLEEP_SAVE_OST_TRDR2, ++ SLEEP_SAVE_OST_TCNT0, SLEEP_SAVE_OST_TCNT1, SLEEP_SAVE_OST_TCNT2, ++ ++ /* HARB */ ++ SLEEP_SAVE_HARB_HAPOR, SLEEP_SAVE_HARB_HMCTR, SLEEP_SAVE_HARB_HMLTR, ++ ++ /* EMC */ ++ SLEEP_SAVE_EMC_SMCR0, SLEEP_SAVE_EMC_SMCR1, SLEEP_SAVE_EMC_SMCR2, SLEEP_SAVE_EMC_SMCR3, ++ SLEEP_SAVE_EMC_SMCR4, SLEEP_SAVE_EMC_SMCR5, ++ ++ /* GPIO */ ++ SLEEP_SAVE_GPIO_GPDR0, SLEEP_SAVE_GPIO_GPDR1, SLEEP_SAVE_GPIO_GPDR2, SLEEP_SAVE_GPIO_GPDR3, ++ SLEEP_SAVE_GPIO_GPDIR0, SLEEP_SAVE_GPIO_GPDIR1, SLEEP_SAVE_GPIO_GPDIR2, SLEEP_SAVE_GPIO_GPDIR3, ++ SLEEP_SAVE_GPIO_GPODR0, SLEEP_SAVE_GPIO_GPODR1, SLEEP_SAVE_GPIO_GPODR2, SLEEP_SAVE_GPIO_GPODR3, ++ SLEEP_SAVE_GPIO_GPPUR0, SLEEP_SAVE_GPIO_GPPUR1, SLEEP_SAVE_GPIO_GPPUR2, SLEEP_SAVE_GPIO_GPPUR3, ++ SLEEP_SAVE_GPIO_GPALR0, SLEEP_SAVE_GPIO_GPALR1, SLEEP_SAVE_GPIO_GPALR2, SLEEP_SAVE_GPIO_GPALR3, ++ SLEEP_SAVE_GPIO_GPAUR0, SLEEP_SAVE_GPIO_GPAUR1, SLEEP_SAVE_GPIO_GPAUR2, SLEEP_SAVE_GPIO_GPAUR3, ++ SLEEP_SAVE_GPIO_GPIDLR0, SLEEP_SAVE_GPIO_GPIDLR1, SLEEP_SAVE_GPIO_GPIDLR2, SLEEP_SAVE_GPIO_GPIDLR3, ++ SLEEP_SAVE_GPIO_GPIDUR0, SLEEP_SAVE_GPIO_GPIDUR1, SLEEP_SAVE_GPIO_GPIDUR2, SLEEP_SAVE_GPIO_GPIDUR3, ++ SLEEP_SAVE_GPIO_GPIER0, SLEEP_SAVE_GPIO_GPIER1, SLEEP_SAVE_GPIO_GPIER2, SLEEP_SAVE_GPIO_GPIER3, ++ SLEEP_SAVE_GPIO_GPIMR0, SLEEP_SAVE_GPIO_GPIMR1, SLEEP_SAVE_GPIO_GPIMR2, SLEEP_SAVE_GPIO_GPIMR3, ++ SLEEP_SAVE_GPIO_GPFR0, SLEEP_SAVE_GPIO_GPFR1, SLEEP_SAVE_GPIO_GPFR2, SLEEP_SAVE_GPIO_GPFR3, ++ ++ /* UART(0-3) */ ++ SLEEP_SAVE_UART0_IER, SLEEP_SAVE_UART0_LCR, SLEEP_SAVE_UART0_MCR, SLEEP_SAVE_UART0_SPR, SLEEP_SAVE_UART0_DLLR, SLEEP_SAVE_UART0_DLHR, ++ SLEEP_SAVE_UART1_IER, SLEEP_SAVE_UART1_LCR, SLEEP_SAVE_UART1_MCR, SLEEP_SAVE_UART1_SPR, SLEEP_SAVE_UART1_DLLR, SLEEP_SAVE_UART1_DLHR, ++ SLEEP_SAVE_UART2_IER, SLEEP_SAVE_UART2_LCR, SLEEP_SAVE_UART2_MCR, SLEEP_SAVE_UART2_SPR, SLEEP_SAVE_UART2_DLLR, SLEEP_SAVE_UART2_DLHR, ++ SLEEP_SAVE_UART3_IER, SLEEP_SAVE_UART3_LCR, SLEEP_SAVE_UART3_MCR, SLEEP_SAVE_UART3_SPR, SLEEP_SAVE_UART3_DLLR, SLEEP_SAVE_UART3_DLHR, ++ ++ /* DMAC */ ++ SLEEP_SAVE_DMAC_DMACR, ++ SLEEP_SAVE_DMAC_DSAR0, SLEEP_SAVE_DMAC_DSAR1, SLEEP_SAVE_DMAC_DSAR2, SLEEP_SAVE_DMAC_DSAR3, SLEEP_SAVE_DMAC_DSAR4, SLEEP_SAVE_DMAC_DSAR5, SLEEP_SAVE_DMAC_DSAR6, SLEEP_SAVE_DMAC_DSAR7, ++ SLEEP_SAVE_DMAC_DDAR0, SLEEP_SAVE_DMAC_DDAR1, SLEEP_SAVE_DMAC_DDAR2, SLEEP_SAVE_DMAC_DDAR3, SLEEP_SAVE_DMAC_DDAR4, SLEEP_SAVE_DMAC_DDAR5, SLEEP_SAVE_DMAC_DDAR6, SLEEP_SAVE_DMAC_DDAR7, ++ SLEEP_SAVE_DMAC_DTCR0, SLEEP_SAVE_DMAC_DTCR1, SLEEP_SAVE_DMAC_DTCR2, SLEEP_SAVE_DMAC_DTCR3, SLEEP_SAVE_DMAC_DTCR4, SLEEP_SAVE_DMAC_DTCR5, SLEEP_SAVE_DMAC_DTCR6, SLEEP_SAVE_DMAC_DTCR7, ++ SLEEP_SAVE_DMAC_DRSR0, SLEEP_SAVE_DMAC_DRSR1, SLEEP_SAVE_DMAC_DRSR2, SLEEP_SAVE_DMAC_DRSR3, SLEEP_SAVE_DMAC_DRSR4, SLEEP_SAVE_DMAC_DRSR5, SLEEP_SAVE_DMAC_DRSR6, SLEEP_SAVE_DMAC_DRSR7, ++ SLEEP_SAVE_DMAC_DCCSR0, SLEEP_SAVE_DMAC_DCCSR1, SLEEP_SAVE_DMAC_DCCSR2, SLEEP_SAVE_DMAC_DCCSR3, SLEEP_SAVE_DMAC_DCCSR4, SLEEP_SAVE_DMAC_DCCSR5, SLEEP_SAVE_DMAC_DCCSR6, SLEEP_SAVE_DMAC_DCCSR7, ++ ++ /* INTC */ ++ SLEEP_SAVE_INTC_IPR, SLEEP_SAVE_INTC_ISR, SLEEP_SAVE_INTC_IMR, ++ ++ /* Checksum */ ++ SLEEP_SAVE_CKSUM, ++ ++ SLEEP_SAVE_SIZE ++}; ++ ++static unsigned long sleep_save[SLEEP_SAVE_SIZE]; ++ ++static int jz_pm_do_suspend(void) ++{ ++ unsigned long checksum = 0; ++ unsigned long imr = REG_INTC_IMR; ++ int i; ++ ++ printk("Put cpu into suspend mode.\n"); ++ ++ /* Mask all interrupts */ ++ REG_INTC_IMSR = 0xffffffff; ++ ++ /* Preserve current time */ ++ REG_RTC_RSR = xtime.tv_sec; ++ ++ REG_CPM_OCR |= CPM_OCR_SUSPEND_PHY0; /* suspend USB PHY 0 */ ++ REG_CPM_OCR |= CPM_OCR_SUSPEND_PHY1; /* suspend USB PHY 1 */ ++ REG_CPM_OCR |= CPM_OCR_EXT_RTC_CLK; /* select the external RTC clock (32.768KHz) */ ++ ++ /* Disable NAND ctroller */ ++ REG_EMC_NFCSR &= ~(EMC_NFCSR_NFE | EMC_NFCSR_FCE); ++ ++ /* ++ * Temporary solution. This won't be necessary once ++ * we move this support into the device drivers. ++ * Save the on-chip modules ++ */ ++ SAVE(UART0_LCR, 8); SAVE(UART0_MCR, 8); SAVE(UART0_SPR, 8); ++ REG8(UART0_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ ++ SAVE(UART0_DLLR, 8); SAVE(UART0_DLHR, 8); ++ REG8(UART0_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ ++ SAVE(UART0_IER, 8); ++ ++ SAVE(UART1_LCR, 8); SAVE(UART1_MCR, 8); SAVE(UART1_SPR, 8); ++ REG8(UART1_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ ++ SAVE(UART1_DLLR, 8); SAVE(UART1_DLHR, 8); ++ REG8(UART1_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ ++ SAVE(UART1_IER, 8); ++ ++ SAVE(UART2_LCR, 8); SAVE(UART2_MCR, 8); SAVE(UART2_SPR, 8); ++ REG8(UART2_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ ++ SAVE(UART2_DLLR, 8); SAVE(UART2_DLHR, 8); ++ REG8(UART2_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ ++ SAVE(UART2_IER, 8); ++ ++ SAVE(UART3_LCR, 8); SAVE(UART3_MCR, 8); SAVE(UART3_SPR, 8); ++ REG8(UART3_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ ++ SAVE(UART3_DLLR, 8); SAVE(UART3_DLHR, 8); ++ REG8(UART3_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ ++ SAVE(UART3_IER, 8); ++ ++ /* Save vital registers */ ++ ++ SAVE(OST_TER, 8); ++ SAVE(OST_TCSR0, 16); SAVE(OST_TCSR1, 16); SAVE(OST_TCSR2, 16); ++ SAVE(OST_TRDR0, 32); SAVE(OST_TRDR1, 32); SAVE(OST_TRDR2, 32); ++ SAVE(OST_TCNT0, 32); SAVE(OST_TCNT1, 32); SAVE(OST_TCNT2, 32); ++ ++ SAVE(HARB_HAPOR, 32); SAVE(HARB_HMCTR, 32); SAVE(HARB_HMLTR, 32); ++ ++ SAVE(EMC_SMCR0, 32); SAVE(EMC_SMCR1, 32); SAVE(EMC_SMCR2, 32); SAVE(EMC_SMCR3, 32); ++ SAVE(EMC_SMCR4, 32); SAVE(EMC_SMCR5, 32); ++ ++ SAVE(GPIO_GPDR0, 32); SAVE(GPIO_GPDR1, 32); SAVE(GPIO_GPDR2, 32); ++ SAVE(GPIO_GPDR3, 32); ++ SAVE(GPIO_GPDIR0, 32); SAVE(GPIO_GPDIR1, 32); SAVE(GPIO_GPDIR2, 32); ++ SAVE(GPIO_GPDIR3, 32); ++ SAVE(GPIO_GPODR0, 32); SAVE(GPIO_GPODR1, 32); SAVE(GPIO_GPODR2, 32); ++ SAVE(GPIO_GPODR3, 32); ++ SAVE(GPIO_GPPUR0, 32); SAVE(GPIO_GPPUR1, 32); SAVE(GPIO_GPPUR2, 32); ++ SAVE(GPIO_GPPUR3, 32); ++ SAVE(GPIO_GPALR0, 32); SAVE(GPIO_GPALR1, 32); SAVE(GPIO_GPALR2, 32); ++ SAVE(GPIO_GPALR3, 32); ++ SAVE(GPIO_GPAUR0, 32); SAVE(GPIO_GPAUR1, 32); SAVE(GPIO_GPAUR2, 32); ++ SAVE(GPIO_GPAUR3, 32); ++ SAVE(GPIO_GPIDLR0, 32); SAVE(GPIO_GPIDLR1, 32); SAVE(GPIO_GPIDLR2, 32); ++ SAVE(GPIO_GPIDLR3, 32); ++ SAVE(GPIO_GPIDUR0, 32); SAVE(GPIO_GPIDUR1, 32); SAVE(GPIO_GPIDUR2, 32); ++ SAVE(GPIO_GPIDUR3, 32); ++ SAVE(GPIO_GPIER0, 32); SAVE(GPIO_GPIER1, 32); SAVE(GPIO_GPIER2, 32); ++ SAVE(GPIO_GPIER3, 32); ++ SAVE(GPIO_GPIMR0, 32); SAVE(GPIO_GPIMR1, 32); SAVE(GPIO_GPIMR2, 32); ++ SAVE(GPIO_GPIMR3, 32); ++ SAVE(GPIO_GPFR0, 32); SAVE(GPIO_GPFR1, 32); SAVE(GPIO_GPFR2, 32); ++ SAVE(GPIO_GPFR3, 32); ++ ++ SAVE(DMAC_DMACR, 32); ++ SAVE(DMAC_DSAR0, 32); SAVE(DMAC_DSAR1, 32); SAVE(DMAC_DSAR2, 32); SAVE(DMAC_DSAR3, 32); SAVE(DMAC_DSAR4, 32); SAVE(DMAC_DSAR5, 32); SAVE(DMAC_DSAR6, 32); SAVE(DMAC_DSAR7, 32); ++ SAVE(DMAC_DDAR0, 32); SAVE(DMAC_DDAR1, 32); SAVE(DMAC_DDAR2, 32); SAVE(DMAC_DDAR3, 32); SAVE(DMAC_DDAR4, 32); SAVE(DMAC_DDAR5, 32); SAVE(DMAC_DDAR6, 32); SAVE(DMAC_DDAR7, 32); ++ SAVE(DMAC_DTCR0, 32); SAVE(DMAC_DTCR1, 32); SAVE(DMAC_DTCR2, 32); SAVE(DMAC_DTCR3, 32); SAVE(DMAC_DTCR4, 32); SAVE(DMAC_DTCR5, 32); SAVE(DMAC_DTCR6, 32); SAVE(DMAC_DTCR7, 32); ++ SAVE(DMAC_DRSR0, 32); SAVE(DMAC_DRSR1, 32); SAVE(DMAC_DRSR2, 32); SAVE(DMAC_DRSR3, 32); SAVE(DMAC_DRSR4, 32); SAVE(DMAC_DRSR5, 32); SAVE(DMAC_DRSR6, 32); SAVE(DMAC_DRSR7, 32); ++ SAVE(DMAC_DCCSR0, 32); SAVE(DMAC_DCCSR1, 32); SAVE(DMAC_DCCSR2, 32); SAVE(DMAC_DCCSR3, 32); SAVE(DMAC_DCCSR4, 32); SAVE(DMAC_DCCSR5, 32); SAVE(DMAC_DCCSR6, 32); SAVE(DMAC_DCCSR7, 32); ++ ++ SAVE(INTC_IPR, 32);SAVE(INTC_ISR, 32);SAVE(INTC_IMR, 32); ++ ++ SAVE(WDT_WTCNT, 32);SAVE(WDT_WTCSR, 8); ++ ++ /* Mask all interrupts */ ++ REG_INTC_IMSR = 0xffffffff; ++ ++ /* Save module clocks */ ++ SAVE(CPM_MSCR, 32); ++ ++ /* Save PLL */ ++ SAVE(CPM_PLCR1, 32); ++ ++ /* Stop module clocks */ ++ __cpm_stop_uart0(); ++ __cpm_stop_uart1(); ++ __cpm_stop_uart2(); ++ __cpm_stop_uart3(); ++ __cpm_stop_uhc(); ++ __cpm_stop_udc(); ++ __cpm_stop_eth(); ++ __cpm_stop_cim(); ++ __cpm_stop_kbc(); ++ __cpm_stop_scc(); ++ __cpm_stop_ssi(); ++ __cpm_stop_ost(); ++ ++ /* platform-specific pm routine */ ++ jz_board_pm_suspend(); ++ ++ /* Clear previous reset status */ ++ REG_CPM_RSTR &= ~(CPM_RSTR_HR | CPM_RSTR_WR | CPM_RSTR_SR); ++ ++ /* Set resume return address */ ++ REG_CPM_SPR = virt_to_phys(jz_cpu_resume); ++ ++ /* Before sleeping, calculate and save a checksum */ ++ for (i = 0; i < SLEEP_SAVE_SIZE - 1; i++) ++ checksum += sleep_save[i]; ++ sleep_save[SLEEP_SAVE_CKSUM] = checksum; ++ ++ /* *** go zzz *** */ ++ jz_cpu_suspend(); ++#if 0 ++ /* after sleeping, validate the checksum */ ++ checksum = 0; ++ for (i = 0; i < SLEEP_SAVE_SIZE - 1; i++) ++ checksum += sleep_save[i]; ++ ++ /* if invalid, display message and wait for a hardware reset */ ++ if (checksum != sleep_save[SLEEP_SAVE_CKSUM]) { ++ /** Add platform-specific message display codes here **/ ++ while (1); ++ } ++#endif ++ /* Restore PLL */ ++ RESTORE(CPM_PLCR1, 32); ++ ++ /* Restore module clocks */ ++ RESTORE(CPM_MSCR, 32); ++ ++ /* Ensure not to come back here if it wasn't intended */ ++ REG_CPM_SPR = 0; ++ ++ /* Restore registers */ ++ ++ RESTORE(GPIO_GPDR0, 32); RESTORE(GPIO_GPDR1, 32); RESTORE(GPIO_GPDR2, 32); ++ RESTORE(GPIO_GPDR3, 32); ++ RESTORE(GPIO_GPDIR0, 32); RESTORE(GPIO_GPDIR1, 32); RESTORE(GPIO_GPDIR2, 32); ++ RESTORE(GPIO_GPDIR3, 32); ++ RESTORE(GPIO_GPODR0, 32); RESTORE(GPIO_GPODR1, 32); RESTORE(GPIO_GPODR2, 32); ++ RESTORE(GPIO_GPODR3, 32); ++ RESTORE(GPIO_GPPUR0, 32); RESTORE(GPIO_GPPUR1, 32); RESTORE(GPIO_GPPUR2, 32); ++ RESTORE(GPIO_GPPUR3, 32); ++ RESTORE(GPIO_GPALR0, 32); RESTORE(GPIO_GPALR1, 32); RESTORE(GPIO_GPALR2, 32); ++ RESTORE(GPIO_GPALR3, 32); ++ RESTORE(GPIO_GPAUR0, 32); RESTORE(GPIO_GPAUR1, 32); RESTORE(GPIO_GPAUR2, 32); ++ RESTORE(GPIO_GPAUR3, 32); ++ RESTORE(GPIO_GPIDLR0, 32);RESTORE(GPIO_GPIDLR1, 32);RESTORE(GPIO_GPIDLR2, 32); ++ RESTORE(GPIO_GPIDLR3, 32); ++ RESTORE(GPIO_GPIDUR0, 32);RESTORE(GPIO_GPIDUR1, 32);RESTORE(GPIO_GPIDUR2, 32); ++ RESTORE(GPIO_GPIDUR3, 32); ++ RESTORE(GPIO_GPIER0, 32); RESTORE(GPIO_GPIER1, 32); RESTORE(GPIO_GPIER2, 32); ++ RESTORE(GPIO_GPIER3, 32); ++ RESTORE(GPIO_GPIMR0, 32); RESTORE(GPIO_GPIMR1, 32); RESTORE(GPIO_GPIMR2, 32); ++ RESTORE(GPIO_GPIMR3, 32); ++ RESTORE(GPIO_GPFR0, 32); RESTORE(GPIO_GPFR1, 32); RESTORE(GPIO_GPFR2, 32); ++ RESTORE(GPIO_GPFR3, 32); ++ ++ RESTORE(EMC_SMCR0, 32); RESTORE(EMC_SMCR1, 32); RESTORE(EMC_SMCR2, 32); RESTORE(EMC_SMCR3, 32); ++ RESTORE(EMC_SMCR4, 32); RESTORE(EMC_SMCR5, 32); ++ ++ RESTORE(HARB_HAPOR, 32); RESTORE(HARB_HMCTR, 32); RESTORE(HARB_HMLTR, 32); ++ ++ RESTORE(OST_TCNT0, 32); RESTORE(OST_TCNT1, 32); RESTORE(OST_TCNT2, 32); ++ RESTORE(OST_TRDR0, 32); RESTORE(OST_TRDR1, 32); RESTORE(OST_TRDR2, 32); ++ RESTORE(OST_TCSR0, 16); RESTORE(OST_TCSR1, 16); RESTORE(OST_TCSR2, 16); ++ RESTORE(OST_TER, 8); ++ ++ RESTORE(DMAC_DMACR, 32); ++ RESTORE(DMAC_DSAR0, 32); RESTORE(DMAC_DSAR1, 32); RESTORE(DMAC_DSAR2, 32); RESTORE(DMAC_DSAR3, 32); RESTORE(DMAC_DSAR4, 32); RESTORE(DMAC_DSAR5, 32); RESTORE(DMAC_DSAR6, 32); RESTORE(DMAC_DSAR7, 32); ++ RESTORE(DMAC_DDAR0, 32); RESTORE(DMAC_DDAR1, 32); RESTORE(DMAC_DDAR2, 32); RESTORE(DMAC_DDAR3, 32); RESTORE(DMAC_DDAR4, 32); RESTORE(DMAC_DDAR5, 32); RESTORE(DMAC_DDAR6, 32); RESTORE(DMAC_DDAR7, 32); ++ RESTORE(DMAC_DTCR0, 32); RESTORE(DMAC_DTCR1, 32); RESTORE(DMAC_DTCR2, 32); RESTORE(DMAC_DTCR3, 32); RESTORE(DMAC_DTCR4, 32); RESTORE(DMAC_DTCR5, 32); RESTORE(DMAC_DTCR6, 32); RESTORE(DMAC_DTCR7, 32); ++ RESTORE(DMAC_DRSR0, 32); RESTORE(DMAC_DRSR1, 32); RESTORE(DMAC_DRSR2, 32); RESTORE(DMAC_DRSR3, 32); RESTORE(DMAC_DRSR4, 32); RESTORE(DMAC_DRSR5, 32); RESTORE(DMAC_DRSR6, 32); RESTORE(DMAC_DRSR7, 32); ++ RESTORE(DMAC_DCCSR0, 32); RESTORE(DMAC_DCCSR1, 32); RESTORE(DMAC_DCCSR2, 32); RESTORE(DMAC_DCCSR3, 32); RESTORE(DMAC_DCCSR4, 32); RESTORE(DMAC_DCCSR5, 32); RESTORE(DMAC_DCCSR6, 32); RESTORE(DMAC_DCCSR7, 32); ++ ++ RESTORE(INTC_IPR, 32);RESTORE(INTC_ISR, 32);RESTORE(INTC_IMR, 32); ++ ++ REG_WDT_WTCNT = 0; RESTORE(WDT_WTCSR, 8); ++ ++ /* ++ * Temporary solution. This won't be necessary once ++ * we move this support into the device drivers. ++ * Restore the on-chip modules. ++ */ ++ ++ /* FIFO control reg, write-only */ ++ REG8(UART0_FCR) = UARTFCR_FE | UARTFCR_RFLS | UARTFCR_TFLS | UARTFCR_UUE; ++ REG8(UART1_FCR) = UARTFCR_FE | UARTFCR_RFLS | UARTFCR_TFLS | UARTFCR_UUE; ++ REG8(UART2_FCR) = UARTFCR_FE | UARTFCR_RFLS | UARTFCR_TFLS | UARTFCR_UUE; ++ REG8(UART3_FCR) = UARTFCR_FE | UARTFCR_RFLS | UARTFCR_TFLS | UARTFCR_UUE; ++ ++ REG8(UART0_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ ++ RESTORE(UART0_DLLR, 8); RESTORE(UART0_DLHR, 8); ++ REG8(UART0_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ ++ RESTORE(UART0_IER, 8); ++ RESTORE(UART0_MCR, 8); RESTORE(UART0_SPR, 8); RESTORE(UART0_LCR, 8); ++ ++ REG8(UART1_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ ++ RESTORE(UART1_DLLR, 8); RESTORE(UART1_DLHR, 8); ++ REG8(UART1_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ ++ RESTORE(UART1_IER, 8); ++ RESTORE(UART1_MCR, 8); RESTORE(UART1_SPR, 8); RESTORE(UART1_LCR, 8); ++ ++ REG8(UART2_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ ++ RESTORE(UART2_DLLR, 8); RESTORE(UART2_DLHR, 8); ++ REG8(UART2_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ ++ RESTORE(UART2_IER, 8); ++ RESTORE(UART2_MCR, 8); RESTORE(UART2_SPR, 8); RESTORE(UART2_LCR, 8); ++ ++ REG8(UART3_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ ++ RESTORE(UART3_DLLR, 8); RESTORE(UART3_DLHR, 8); ++ REG8(UART3_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ ++ RESTORE(UART3_IER, 8); ++ RESTORE(UART3_MCR, 8); RESTORE(UART3_SPR, 8); RESTORE(UART3_LCR, 8); ++ ++ REG_CPM_OCR &= ~CPM_OCR_SUSPEND_PHY0; /* resume USB PHY 0 */ ++ REG_CPM_OCR &= ~CPM_OCR_SUSPEND_PHY1; /* resume USB PHY 1 */ ++#if 0 ++ REG_CPM_OCR &= ~CPM_OCR_EXT_RTC_CLK; /* use internal RTC clock (JZ_EXTAL/128 Hz) */ ++#else ++ REG_CPM_OCR |= CPM_OCR_EXT_RTC_CLK; /* use external RTC clock (32.768 KHz) */ ++#endif ++ ++ /* Enable NAND ctroller */ ++ REG_EMC_NFCSR |= EMC_NFCSR_NFE; ++ ++ /* Restore current time */ ++ xtime.tv_sec = REG_RTC_RSR; ++ ++ /* Restore interrupts */ ++ REG_INTC_IMSR = imr; ++ REG_INTC_IMCR = ~imr; ++ ++ return 0; ++} ++ ++/* NOTES: ++ * 1: Pins that are floated (NC) should be set as input and pull-enable. ++ * 2: Pins that are pull-up or pull-down by outside should be set as input ++ * and pull-disable. ++ * 3: Pins that are connected to a chipset should be set as pull-disable. ++ */ ++static void jz_board_pm_gpio_setup(void) ++{ ++ /* CIM_D0(IN)/PULL-UP/GP0 */ ++ __gpio_as_input(0); ++ __gpio_enable_pull(0); ++ ++ /* CIM_D1(IN)/PULL-UP/GP1 */ ++ __gpio_as_input(1); ++ __gpio_enable_pull(1); ++ ++ /* CIM_D2(IN)/PULL-UP/GP2 */ ++ __gpio_as_input(2); ++ __gpio_enable_pull(2); ++ ++ /* CIM_D3(IN)/PULL-UP/GP3 */ ++ __gpio_as_input(3); ++ __gpio_enable_pull(3); ++ ++ /* CIM_D4(IN)/PULL-DOWN/GP4 */ ++ __gpio_as_input(4); ++ __gpio_enable_pull(4); ++ ++ /* CIM_D5(IN)/PULL-DOWN/GP5 */ ++ __gpio_as_input(5); ++ __gpio_enable_pull(5); ++ ++ /* CIM_D6(IN)/PULL-DOWN/GP6 */ ++ __gpio_as_input(6); ++ __gpio_enable_pull(6); ++ ++ /* CIM_D7(IN)/PULL-DOWN/GP7 */ ++ __gpio_as_input(7); ++ __gpio_enable_pull(7); ++ ++ /* CIM_VSYNC(IN)/PULL-DOWN/GP8 */ ++ __gpio_as_input(8); ++ __gpio_enable_pull(8); ++ ++ /* CIM_HSYNC(IN)/PULL-UP/GP9 */ ++ __gpio_as_input(9); ++ __gpio_enable_pull(9); ++ ++ /* CIM_PCLK(IN)/PULL-DOWN/GP10 */ ++ __gpio_as_input(10); ++ __gpio_enable_pull(10); ++ ++ /* CIM_MCLK(OUT)/PULL-DOWN/GP11 */ ++ __gpio_as_input(11); ++ __gpio_enable_pull(11); ++ ++ /* DMA_DREQ0(IN)/CHIP_MODE/PULL-UP/GP12 */ ++ __gpio_as_input(12); ++ __gpio_enable_pull(12); ++ ++ /* DMA_DACK0(OUT)/PULL-UP/GP13 */ /* GPIO13 */ ++ __gpio_as_input(13); ++ __gpio_disable_pull(13); ++ ++ /* GP14 */ ++ /* GP15 */ ++ ++ /* RXD3(IN)/PULL-UP/GP16 */ ++ __gpio_as_input(16); ++ __gpio_enable_pull(16); ++ ++ /* CTS3(IN)/PULL-UP/GP17 */ ++ __gpio_as_input(17); ++ __gpio_enable_pull(17); ++ ++ /* GP18 */ ++ /* GP19 */ ++ /* GP20 */ ++ ++ /* TXD3(OUT)/PULL-UP/GP21 */ ++ __gpio_as_input(21); ++ __gpio_enable_pull(21); ++ ++ /* GP22 */ ++ ++ /* RTS3(OUT)/PULL-UP/GP23 */ ++ __gpio_as_input(23); ++ __gpio_enable_pull(23); ++ ++ /* RXD1(IN)/PULL-UP/GP24 */ /* IR_RXD */ ++ __gpio_as_input(24); ++ __gpio_enable_pull(24); ++ ++ /* TXD1(OUT)/PULL-UP/GP25 */ /* IR_TXD */ ++ __gpio_disable_pull(25); ++ __gpio_as_output(25); ++ __cpm_set_pin(25); ++ ++ /* DMA_AEN(OUT)/PULL-UP/GP26 */ /* CIM_PWD_N */ ++ __gpio_as_input(26); ++ __gpio_disable_pull(26); ++ ++ /* DMA_EOP(OUT)/PULL-UP/GP27 */ /* SW4 */ ++ __gpio_as_input(27); ++ __gpio_disable_pull(27); ++ ++ /* USB_CLK(IN)/PULL-UP/GP28 */ ++ __gpio_as_input(28); ++ __gpio_disable_pull(28); ++ ++ /* USB_PPWR0(OUT)/PULL-UP/GP29 */ /* USB_CLK_EN */ ++ __gpio_disable_pull(29); ++ __gpio_as_output(29); ++ __cpm_clear_pin(29); /* disable USB 48MHz clock */ ++ ++ /* GP30 */ ++ /* GP31 */ ++ ++ /* PS2_KCLK(IO)/PULL-UP/GP32 */ ++ __gpio_as_input(32); ++ __gpio_enable_pull(32); ++ ++ /* PS2_KDATA(IO)/PULL-UP/GP33 */ /* CIM_RST */ ++ __gpio_as_input(33); ++ __gpio_enable_pull(33); ++ ++ /* MSC_D0(IO)/PULL-UP/GP34 */ ++ __gpio_as_input(34); ++ __gpio_disable_pull(34); ++ ++ /* MSC_D1(IO)/PULL-UP/GP35 */ ++ __gpio_as_input(35); ++ __gpio_disable_pull(35); ++ ++ /* MSC_D2(IO)/PULL-UP/GP36 */ ++ __gpio_as_input(36); ++ __gpio_disable_pull(36); ++ ++ /* MSC_D3(IO)/PULL-UP/GP37 */ ++ __gpio_as_input(37); ++ __gpio_disable_pull(37); ++ ++ /* MSC_CMD(IO)/PULL-UP/GP38 */ ++ __gpio_as_input(38); ++ __gpio_disable_pull(38); ++ ++ /* MSC_CLK(OUT)/PULL-UP/GP39 */ ++ __gpio_as_input(39); ++ __gpio_enable_pull(39); ++ ++ /* LCD_D0(OUT)/PULL-UP/GP40 */ ++ __gpio_as_input(40); ++ __gpio_enable_pull(40); ++ ++ /* LCD_D1(OUT)/PULL-UP/GP41 */ ++ __gpio_as_input(41); ++ __gpio_enable_pull(41); ++ ++ /* LCD_D2(OUT)/PULL-UP/GP42 */ ++ __gpio_as_input(42); ++ __gpio_enable_pull(42); ++ ++ /* LCD_D3(OUT)/PULL-UP/GP43 */ ++ __gpio_as_input(43); ++ __gpio_enable_pull(43); ++ ++ /* LCD_D4(OUT)/PULL-UP/GP44 */ ++ __gpio_as_input(44); ++ __gpio_enable_pull(44); ++ ++ /* LCD_D5(OUT)/PULL-UP/GP45 */ ++ __gpio_as_input(45); ++ __gpio_enable_pull(45); ++ ++ /* LCD_D6(OUT)/PULL-UP/GP46 */ ++ __gpio_as_input(46); ++ __gpio_enable_pull(46); ++ ++ /* LCD_D7(OUT)/PULL-UP/GP47 */ ++ __gpio_as_input(47); ++ __gpio_enable_pull(47); ++ ++ /* LCD_D8(OUT)/PULL-DOWN/GP48 */ ++ __gpio_as_input(48); ++ __gpio_enable_pull(48); ++ ++ /* LCD_D9(OUT)/PULL-DOWN/GP49 */ ++ __gpio_as_input(49); ++ __gpio_enable_pull(49); ++ ++ /* LCD_D10(OUT)/PULL-DOWN/GP50 */ ++ __gpio_as_input(50); ++ __gpio_enable_pull(50); ++ ++ /* LCD_D11(OUT)/PULL-DOWN/GP51 */ ++ __gpio_as_input(51); ++ __gpio_enable_pull(51); ++ ++ /* LCD_D12(OUT)/PULL-DOWN/GP52 */ ++ __gpio_as_input(52); ++ __gpio_enable_pull(52); ++ ++ /* LCD_D13(OUT)/PULL-DOWN/GP53 */ ++ __gpio_as_input(53); ++ __gpio_enable_pull(53); ++ ++ /* LCD_D14(OUT)/PULL-DOWN/GP54 */ ++ __gpio_as_input(54); ++ __gpio_enable_pull(54); ++ ++ /* LCD_D15(OUT)/PULL-DOWN/GP55 */ ++ __gpio_as_input(55); ++ __gpio_enable_pull(55); ++ ++ /* LCD_VSYNC(IN)/PULL-DOWN/GP56 */ ++ __gpio_as_input(56); ++ __gpio_enable_pull(56); ++ ++ /* LCD_HSYNC(IN)/PULL-UP/GP57 */ ++ __gpio_as_input(57); ++ __gpio_enable_pull(57); ++ ++ /* LCD_PCLK(IN)/PULL-DOWN/GP58 */ ++ __gpio_as_input(58); ++ __gpio_enable_pull(58); ++ ++ /* LCD_DE(OUT)/PULL-DOWN/GP59 */ ++ __gpio_as_input(59); ++ __gpio_enable_pull(59); ++ ++ /* LCD_SPL(OUT)/PULL-UP/GP60 */ ++ __gpio_as_input(60); ++ __gpio_disable_pull(60); ++ ++ /* LCD_CLS(OUT)/PULL-UP/GP61 */ ++ __gpio_as_input(61); ++ __gpio_disable_pull(61); ++ ++ /* LCD_PS(OUT)/PULL-UP/GP62 */ ++ __gpio_as_input(62); ++ __gpio_disable_pull(62); ++ ++ /* LCD_REV(OUT)/PULL-UP/GP63 */ ++ __gpio_as_input(63); ++ __gpio_enable_pull(63); ++ ++ /* SCC0_DAT(IO)/PULL-UP/GP64 */ /* Keypad */ ++ __gpio_as_input(64); ++ __gpio_enable_pull(64); ++ ++ /* SCC1_DAT(IO)/PULL-UP/GP65 */ /* SW5 */ ++ __gpio_as_input(65); ++ __gpio_disable_pull(65); ++ ++ /* SCC0_CLK(OUT)/PULL-UP/GP66 */ /* PW_O */ ++ __gpio_disable_pull(66); ++ __gpio_as_output(66); ++ __cpm_set_pin(66); ++ ++ /* SCC1_CLK(OUT)/PULL-UP/GP67 */ /* SW6 */ ++ __gpio_as_input(67); ++ __gpio_disable_pull(67); ++ ++ /* SYS_CLK(OUT)/PULL-UP/GP68 */ /* I2S_CLK */ ++ __gpio_disable_pull(68); ++ ++ /* ACRESET_N(OUT)/PULL-UP/GP69 */ /* AK4642 PDN */ ++ __gpio_disable_pull(69); ++ __gpio_as_output(69); ++ __cpm_clear_pin(69); ++ ++ /* SDATA_OUT(OUT)/PULL-UP/GP70 */ /* I2S_DIN */ ++ __gpio_disable_pull(70); ++ ++ /* SDATA_IN(IN)/PULL-UP/GP71 */ /* I2S_DOUT */ ++ __gpio_disable_pull(71); ++ ++ /* SSI_CLK(OUT)/PULL-UP/GP72 */ /* SSI_CLK */ ++ __gpio_as_input(72); ++ __gpio_enable_pull(72); ++ ++ /* SSI_CE1_N(OUT)/PULL-UP/GP73 */ /* SSI_CE1_N */ ++ __gpio_as_input(73); ++ __gpio_enable_pull(73); ++ ++ /* SSI_DT(OUT)/PULL-UP/GP74 */ /* SSI_DT */ ++ __gpio_as_input(74); ++ __gpio_enable_pull(74); ++ ++ /* SSI_DR(IN)/PULL-UP/GP75 */ /* SSI_DR */ ++ __gpio_as_input(75); ++ __gpio_enable_pull(75); ++ ++ /* SSI_CE2_N(OUT)/SSI_GPC/PULL-UP/GP76 */ ++ __gpio_as_input(76); ++ __gpio_enable_pull(76); ++ ++ /* BITCLK_IN(IN)/PULL-UP/GP77 */ /* I2S_BITCLK */ ++ __gpio_disable_pull(77); ++ ++ /* SYNC_IN(IN)/PULL-UP/GP78 */ /* I2S_LRCIN */ ++ __gpio_disable_pull(78); ++ ++ /* FRE_N(OUT)/PULL-UP/GP79 */ ++ __gpio_enable_pull(79); ++ __gpio_as_input(79); ++ ++ /* FWE_N(OUT)/PULL-UP/GP80 */ ++ __gpio_enable_pull(80); ++ __gpio_as_input(80); ++ ++ /* FRB_N(IN)/PULL-UP/GP81 */ ++ __gpio_enable_pull(81); ++ __gpio_as_input(81); ++ ++ /* DCS1_N(OUT)/PULL-UP/GP82 */ /* SD_WP */ ++ __gpio_as_input(82); ++ __gpio_enable_pull(82); ++ ++ /* CS1_N(OUT)/PULL-UP/GP83 */ /* JACK_PLUG */ ++ __gpio_as_input(83); ++ __gpio_disable_pull(83); ++ ++ /* CS2_N(OUT)/PULL-UP/GP84 */ /* DC_DETE */ ++ __gpio_as_input(84); ++ __gpio_disable_pull(84); ++ ++ /* CS3_N(OUT)/PULL-UP/GP85 */ /* NAND CS# */ ++ __gpio_enable_pull(85); ++ __gpio_as_input(85); ++ ++ /* CS4_N/(OUT)PULL-UP/GP86 */ /* PULL_OFF */ ++ __gpio_disable_pull(86); ++ __gpio_as_output(86); ++// __cpm_set_pin(86); ++ __cpm_clear_pin(86); ++ ++ /* CS5_N(OUT)/PULL-UP/GP87 */ /* IR_SD */ ++ __gpio_as_input(87); ++ __gpio_disable_pull(87); ++ ++ /* INPACK_N(IN)/PULL-UP/GP88 */ /* SW7 */ ++ __gpio_as_input(88); ++ __gpio_disable_pull(88); ++ ++ /* BVD2(IN)/PULL-UP/GP89 */ /* SW8 */ ++ __gpio_as_input(89); ++ __gpio_disable_pull(89); ++ ++ /* PCE1_N(OUT)/PULL-UP/GP90 */ /* SD_CD_N */ ++ __gpio_as_input(90); ++ __gpio_enable_pull(90); ++ ++ /* PSKTSEL_N(OUT)/PULL-UP/GP91 */ /* SD_VCC_3V_EN_N */ ++ __gpio_disable_pull(91); ++ __gpio_as_output(91); ++ __cpm_clear_pin(91); ++ ++ /* IOIS16_N(IN)/PULL-UP/GP92 */ /* LED_EN */ ++ __gpio_disable_pull(92); ++ __gpio_as_output(92); ++ __cpm_clear_pin(92); ++ ++ /* PCE2_N(OUT)/PULL-UP/GP93 */ /* LCD_DISP_OFF_N */ ++ __gpio_disable_pull(93); ++ __gpio_as_input(93); ++ ++ /* PWM0(OUT)/PULL-UP/GP94 */ /* LCD backlight off */ ++ __gpio_disable_pull(94); ++ __gpio_as_output(94); ++ __cpm_clear_pin(94); ++ ++ /* PWM1(OUT)/PULL-UP/GP95 */ ++ __gpio_disable_pull(95); ++ __gpio_as_output(95); ++ __cpm_clear_pin(95); ++ ++ /* PRT(OUT)/PULL-UP/GP96 */ /* RTC_IRQ */ ++ __gpio_as_input(96); ++ __gpio_disable_pull(96); ++ ++ /* PRT(OUT)/PULL-UP/GP97 */ /* PW_I */ ++ __gpio_as_input(97); ++ __gpio_disable_pull(97); ++ ++ /* PRT(OUT)/PULL-UP/GP98 */ /* Keypad */ ++ __gpio_as_input(98); ++ __gpio_disable_pull(98); ++ ++ /* PRT(OUT)/PULL-UP/GP99 */ /* Keypad */ ++ __gpio_as_input(99); ++ __gpio_disable_pull(99); ++ ++ /* PRT(OUT)/PULL-UP/GP100 */ /* Keypad */ ++ __gpio_as_input(100); ++ __gpio_disable_pull(100); ++ ++ /* PRT(OUT)/PULL-UP/GP101 */ /* Keypad */ ++ __gpio_as_input(101); ++ __gpio_disable_pull(101); ++ ++ /* PRT(OUT)/PULL-UP/GP102 */ /* Keypad */ ++ __gpio_as_input(102); ++ __gpio_disable_pull(102); ++ ++ /* PRT(OUT)/PULL-UP/GP103 */ /* Keypad */ ++ __gpio_as_input(103); ++ __gpio_enable_pull(103); ++ ++ /* PRT(OUT)/PULL-UP/GP104 */ /* Keypad */ ++ __gpio_as_input(104); ++ __gpio_enable_pull(104); ++ ++ /* PRT(OUT)/PULL-UP/GP105 */ /* Keypad */ ++ __gpio_as_input(105); ++ __gpio_enable_pull(105); ++ ++ /* PRT(OUT)/PULL-UP/GP106 */ /* 5V_ON */ ++ __gpio_disable_pull(106); ++ __gpio_as_output(106); ++ __cpm_clear_pin(106); ++ ++ /* PRT(IN)/PULL-UP/GP107 */ /* GSM_BOOT */ ++ __gpio_as_input(107); ++ __gpio_enable_pull(107); ++ ++ /* PRT(IN)/PULL-UP/GP108 */ /* GSM_RESET */ ++ __gpio_as_input(108); ++ __gpio_enable_pull(108); ++ ++ /* PRT(IN)/PULL-UP/GP109 */ /* GSM_EN */ ++ __gpio_as_input(109); ++ __gpio_enable_pull(109); ++ ++ /* PRT(IN)/PULL-UP/GP110 */ /* GSM_RING */ ++ __gpio_as_input(110); ++ __gpio_enable_pull(110); ++ ++ /* PRT(IN)/UART2_RXD/PULL-UP/GP111 */ /* Keypad */ ++ __gpio_as_input(111); ++ __gpio_enable_pull(111); ++ ++ /* MII_TX_EN(OUT)/PULL-UP/GP112 */ ++ __gpio_as_input(112); ++ __gpio_enable_pull(112); ++ ++ /* MII_RX_DV(IN)/PULL-UP/GP113 */ ++ __gpio_as_input(113); ++ __gpio_enable_pull(113); ++ ++ /* MII_RX_ER(IN)/PULL-UP/GP114 */ ++ __gpio_as_input(114); ++ __gpio_enable_pull(114); ++ ++ /* MII_COL(IN)/PULL-UP/GP115 */ ++ __gpio_as_input(115); ++ __gpio_enable_pull(115); ++ ++ /* MII_CRS(IN)/PULL-UP/GP116 */ ++ __gpio_as_input(116); ++ __gpio_enable_pull(116); ++ ++ /* MII_TXD0(OUT)/PULL-UP/GP117 */ ++ __gpio_as_input(117); ++ __gpio_enable_pull(117); ++ ++ /* MII_TXD1(OUT)/PULL-UP/GP118 */ ++ __gpio_as_input(118); ++ __gpio_enable_pull(118); ++ ++ /* MII_TXD2(OUT)/PULL-UP/GP119 */ ++ __gpio_as_input(119); ++ __gpio_enable_pull(119); ++ ++ /* MII_TXD3(OUT)/PULL-UP/GP120 */ ++ __gpio_as_input(120); ++ __gpio_enable_pull(120); ++ ++ /* MII_RXD0(IN)/PULL-UP/GP121 */ ++ __gpio_as_input(121); ++ __gpio_enable_pull(121); ++ ++ /* MII_RXD1(IN)/PULL-UP/GP122 */ ++ __gpio_as_input(122); ++ __gpio_enable_pull(122); ++ ++ /* MII_RXD2(IN)/PULL-UP/GP123 */ ++ __gpio_as_input(123); ++ __gpio_enable_pull(123); ++ ++ /* MII_RXD3(IN)/PULL-UP/GP124 */ ++ __gpio_as_input(124); ++ __gpio_enable_pull(124); ++ ++ /* UART2_TXD(OUT)/PULL-UP/GP125 */ /* CHARG_STAT */ ++ __gpio_as_output(125); ++ __gpio_disable_pull(125); ++ __cpm_clear_pin(125); ++ ++ /* UART0_RXD(IN)/PULL-UP/GP126 */ ++ __gpio_as_input(126); ++ __gpio_enable_pull(126); ++ ++ /* UART0_TXD(OUT)/PULL-UP/GP127 */ ++ __gpio_as_input(127); ++ __gpio_enable_pull(127); ++} ++ ++/* ++ * In order to save power most, all gpio pins should be put to their ++ * proper states during low power mode. ++ */ ++static void jz_board_pm_suspend(void) ++{ ++ /* Setup the state of all the GPIO pins during low-power mode */ ++ jz_board_pm_gpio_setup(); ++ ++ /* Allow next interrupts to wakeup the system. ++ */ ++ REG_CPM_WER = 0; /* Clear all first */ ++ ++ /* RTC alarm */ ++ REG_CPM_WER |= 1 << 0; ++ REG_CPM_WRER |= 1 << 0; ++ REG_CPM_WFER |= 1 << 0; ++ __gpio_as_irq_rise_edge(96); ++ ++ /* Power_I key */ ++ REG_CPM_WER |= 1 << 1; ++ REG_CPM_WRER |= 1 << 1; ++ REG_CPM_WFER |= 1 << 1; ++ __gpio_as_irq_rise_edge(97); ++ ++ /* enable INTC irq */ ++ __intc_unmask_irq(IRQ_GPIO3); ++ ++#if 0 ++ /* Enable RTC alarm */ ++ REG_CPM_WER |= CPM_WER_WERTC; ++ REG_RTC_RGR = 32767; ++ REG_RTC_RCR &= ~RTC_RCR_AE; ++ REG_RTC_RSR = 0; ++ REG_RTC_RSAR = 30; ++ REG_RTC_RCR = RTC_RCR_AE | RTC_RCR_AIE | RTC_RCR_START; ++#endif ++} ++ ++/* ++ * We don't use sleep mode of jz4730 for it has bug, the suspend mode ++ * implemented by hibernate mode is used instead of it. ++ */ ++static int jz_pm_do_sleep(void) ++{ ++ printk("It was deprecated, please use /proc/sys/pm/suspend.\n"); ++#if 0 ++ unsigned long imr = REG_INTC_IMR; ++ ++ /* Preserve current time */ ++ REG_RTC_RSR = xtime.tv_sec; ++ ++ /* Mask all interrupts */ ++ REG_INTC_IMSR = 0xffffffff; ++ ++ /* Just allow next interrupts to wakeup the system. ++ * Note: modify this according to your system. ++ */ ++ /* RTC alarm */ ++ __gpio_as_irq_fall_edge(96); /* GPIO 96 */ ++ ++ /* POWER_I key */ ++ __gpio_as_irq_rise_edge(97); /* GPIO 97 */ ++ ++ /* Enable INTC */ ++ __intc_unmask_irq(IRQ_GPIO3); ++ ++ /* Disable modules e.g. LCD backlight */ ++ ++ /* Stop module clocks */ ++ __cpm_stop_uhc(); ++ ++ /* Enter SLEEP mode ++ * Put SDRAM into self-refresh mode. ++ */ ++ REG_CPM_LPCR &= ~CPM_LPCR_LPM_MASK; ++ REG_CPM_LPCR |= CPM_LPCR_LPM_SLEEP; ++ ++ __asm__(".set\tmips3\n\t" ++ ".set noreorder\n\t" ++ ".align 5\n\t" ++ "wait\n\t" ++ "nop\n\t" ++ ".set reorder\n\t" ++ ".set\tmips0"); ++ ++ /* Restore to IDLE mode */ ++ REG_CPM_LPCR &= ~CPM_LPCR_LPM_MASK; ++ REG_CPM_LPCR |= CPM_LPCR_LPM_IDLE; ++ ++ /* Restore clock of usb host */ ++ __cpm_start_uhc(); ++ ++ /* Restore interrupts */ ++ REG_INTC_IMSR = imr; ++ REG_INTC_IMCR = ~imr; ++ ++ /* Restore current time */ ++ xtime.tv_sec = REG_RTC_RSR; ++#endif ++ return 0; ++} ++ ++#define K0BASE KSEG0 ++void jz_flush_cache_all(void) ++{ ++ unsigned long addr; ++ ++ /* Clear CP0 TagLo */ ++ asm volatile ("mtc0 $0, $28\n\t"::); ++ ++ for (addr = K0BASE; addr < (K0BASE + 0x4000); addr += 32) { ++ asm volatile ( ++ ".set mips3\n\t" ++ " cache %0, 0(%1)\n\t" ++ ".set mips2\n\t" ++ : ++ : "I" (Index_Writeback_Inv_D), "r"(addr)); ++ ++ asm volatile ( ++ ".set mips3\n\t" ++ " cache %0, 0(%1)\n\t" ++ ".set mips2\n\t" ++ : ++ : "I" (Index_Store_Tag_I), "r"(addr)); ++ } ++ ++ asm volatile ("sync\n\t"::); ++ ++ /* invalidate BTB */ ++ asm volatile ( ++ ".set mips32\n\t" ++ " mfc0 %0, $16, 7\n\t" ++ " nop\n\t" ++ " ori $0, 2\n\t" ++ " mtc0 %0, $16, 7\n\t" ++ " nop\n\t" ++ ".set mips2\n\t" ++ : ++ : "r"(addr)); ++} ++ ++/* Put CPU to HIBERNATE mode */ ++int jz_pm_suspend(void) ++{ ++ int retval; ++ ++ pm_send_all(PM_SUSPEND, (void *)3); ++ ++ retval = jz_pm_do_suspend(); ++ ++ pm_send_all(PM_RESUME, (void *)0); ++ ++ return retval; ++} ++ ++#if 0 ++/* Put CPU to SLEEP mode */ ++int jz_pm_sleep(void) ++{ ++ return jz_pm_do_sleep(); ++} ++ ++/* Put CPU to IDLE mode, used for dpm in linux 2.4 */ ++void jz_pm_idle(void) ++{ ++ local_irq_disable(); ++ if (!need_resched()) { ++ local_irq_enable(); ++ cpu_wait(); ++ } ++} ++#endif ++ ++#ifdef CONFIG_SYSCTL ++ ++/* ++ * Use a temporary sysctl number. Horrid, but will be cleaned up in 2.6 ++ * when all the PM interfaces exist nicely. ++ */ ++#define CTL_PM_SUSPEND 1 ++#define CTL_PM_HIBERNATE 2 ++ ++/*---------------------------------------------------------------------------- ++ * Power Management sleep sysctl proc interface ++ * ++ * A write to /proc/sys/pm/suspend invokes this function ++ * which initiates a sleep. ++ *--------------------------------------------------------------------------*/ ++static int sysctl_jz_pm_sleep(void) ++{ ++ return jz_pm_suspend(); ++} ++ ++static struct ctl_table pm_table[] = ++{ ++ { ++ .ctl_name = CTL_UNNUMBERED, ++ .procname = "suspend", ++ .data = NULL, ++ .maxlen = 0, ++ .mode = 0600, ++ .proc_handler = &sysctl_jz_pm_sleep, ++ }, ++ { .ctl_name = 0} ++}; ++ ++static struct ctl_table pm_dir_table[] = ++{ ++ { ++ .ctl_name = CTL_UNNUMBERED, ++ .procname = "pm", ++ .mode = 0555, ++ .child = pm_table, ++ }, ++ { .ctl_name = 0} ++}; ++ ++#endif /* CONFIG_SYSCTL */ ++ ++/* ++ * Initialize power interface ++ */ ++static int __init jz_pm_init(void) ++{ ++ printk("Power Management for JZ\n"); ++ ++#ifdef CONFIG_SYSCTL ++ register_sysctl_table(pm_dir_table); ++#endif ++ ++ return 0; ++} ++ ++module_init(jz_pm_init); +diff -urN linux-2.6.24.7.old/arch/mips/jz4730/proc.c linux-2.6.24.7/arch/mips/jz4730/proc.c +--- linux-2.6.24.7.old/arch/mips/jz4730/proc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4730/proc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,292 @@ ++/* ++ * linux/arch/mips/jz4730/proc.c ++ * ++ * /proc/jz/ procfs for on-chip peripherals. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++struct proc_dir_entry *proc_jz_root; ++ ++/* ++ * EMC Module ++ */ ++static int emc_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ ++ len += sprintf (page+len, "BCR: 0x%08x\n", REG_EMC_BCR); ++ len += sprintf (page+len, "SMCR(0-5): 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", REG_EMC_SMCR0, REG_EMC_SMCR1, REG_EMC_SMCR2, REG_EMC_SMCR3, REG_EMC_SMCR4, REG_EMC_SMCR5); ++ len += sprintf (page+len, "SACR(0-5): 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", REG_EMC_SACR0, REG_EMC_SACR1, REG_EMC_SACR2, REG_EMC_SACR3, REG_EMC_SACR4, REG_EMC_SACR5); ++ len += sprintf (page+len, "DMCR: 0x%08x\n", REG_EMC_DMCR); ++ len += sprintf (page+len, "RTCSR: 0x%04x\n", REG_EMC_RTCSR); ++ len += sprintf (page+len, "RTCOR: 0x%04x\n", REG_EMC_RTCOR); ++ len += sprintf (page+len, "DMAR(0-1): 0x%08x 0x%08x\n", REG_EMC_DMAR1, REG_EMC_DMAR2); ++ return len; ++} ++ ++/* ++ * Power Manager Module ++ */ ++static int pmc_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ unsigned long lpcr = REG_CPM_LPCR; ++ unsigned long mscr = REG_CPM_MSCR; ++ ++ len += sprintf (page+len, "LPCR : 0x%08lx\n", lpcr); ++ len += sprintf (page+len, "Low Power Mode : %s\n", ++ ((lpcr & CPM_LPCR_LPM_MASK) == (CPM_LPCR_LPM_IDLE)) ? ++ "idle" : (((lpcr & CPM_LPCR_LPM_MASK) == (CPM_LPCR_LPM_SLEEP)) ? "sleep" : "hibernate")); ++ len += sprintf (page+len, "Doze Mode : %s\n", ++ (lpcr & CPM_LPCR_DOZE) ? "on" : "off"); ++ if (lpcr & CPM_LPCR_DOZE) ++ len += sprintf (page+len, " duty : %d\n", (int)((lpcr & CPM_LPCR_DUTY_MASK) >> CPM_LPCR_DUTY_BIT)); ++ len += sprintf (page+len, "CKO1 : %s\n", ++ (REG_CPM_CFCR & CPM_CFCR_CKOEN1) ? "enable" : "disable"); ++ len += sprintf (page+len, "UART0 : %s\n", ++ (mscr & CPM_MSCR_MSTP_UART0) ? "stopped" : "running"); ++ len += sprintf (page+len, "UART1 : %s\n", ++ (mscr & CPM_MSCR_MSTP_UART1) ? "stopped" : "running"); ++ len += sprintf (page+len, "UART2 : %s\n", ++ (mscr & CPM_MSCR_MSTP_UART2) ? "stopped" : "running"); ++ len += sprintf (page+len, "UART3 : %s\n", ++ (mscr & CPM_MSCR_MSTP_UART3) ? "stopped" : "running"); ++ len += sprintf (page+len, "OST : %s\n", ++ (mscr & CPM_MSCR_MSTP_OST) ? "stopped" : "running"); ++ len += sprintf (page+len, "DMAC : %s\n", ++ (mscr & CPM_MSCR_MSTP_DMAC) ? "stopped" : "running"); ++ len += sprintf (page+len, "ETH : %s\n", ++ (mscr & CPM_MSCR_MSTP_ETH) ? "stopped" : "running"); ++ len += sprintf (page+len, "UHC/UDC : %s\n", ++ (mscr & CPM_MSCR_MSTP_UHC) ? "stopped" : "running"); ++ len += sprintf (page+len, "PWM0 : %s\n", ++ (mscr & CPM_MSCR_MSTP_PWM0) ? "stopped" : "running"); ++ len += sprintf (page+len, "PWM1 : %s\n", ++ (mscr & CPM_MSCR_MSTP_PWM1) ? "stopped" : "running"); ++ len += sprintf (page+len, "I2C : %s\n", ++ (mscr & CPM_MSCR_MSTP_I2C) ? "stopped" : "running"); ++ len += sprintf (page+len, "SSI : %s\n", ++ (mscr & CPM_MSCR_MSTP_SSI) ? "stopped" : "running"); ++ len += sprintf (page+len, "SCC : %s\n", ++ (mscr & CPM_MSCR_MSTP_SCC) ? "stopped" : "running"); ++ return len; ++} ++ ++static int pmc_write_proc(struct file *file, const char __user *buffer, unsigned long count, void *data) ++{ ++ REG_CPM_MSCR = simple_strtoul(buffer, 0, 16); ++ return count; ++} ++ ++/* ++ * Clock Generation Module ++ */ ++static int cgm_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ unsigned int cfcr = REG_CPM_CFCR; ++ unsigned int plcr1 = REG_CPM_PLCR1; ++ unsigned int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ unsigned int od[4] = {1, 2, 2, 4}; ++ ++ ++ len += sprintf (page+len, "PLCR1 : 0x%08x\n", plcr1); ++ len += sprintf (page+len, "CFCR : 0x%08x\n", cfcr); ++ len += sprintf (page+len, "PLL : %s\n", ++ (plcr1 & CPM_PLCR1_PLL1EN) ? "ON" : "OFF"); ++ len += sprintf (page+len, "NF:NR:NO : %d:%d:%d\n", ++ __cpm_plcr1_fd() + 2, ++ __cpm_plcr1_rd() + 2, ++ od[__cpm_plcr1_od()] ++ ); ++ len += sprintf (page+len, "I:S:M:P : %d:%d:%d:%d\n", ++ div[(cfcr & CPM_CFCR_IFR_MASK) >> CPM_CFCR_IFR_BIT], ++ div[(cfcr & CPM_CFCR_SFR_MASK) >> CPM_CFCR_SFR_BIT], ++ div[(cfcr & CPM_CFCR_MFR_MASK) >> CPM_CFCR_MFR_BIT], ++ div[(cfcr & CPM_CFCR_PFR_MASK) >> CPM_CFCR_PFR_BIT] ++ ); ++ len += sprintf (page+len, "PLL Freq : %d MHz\n", __cpm_get_pllout()/1000000); ++ len += sprintf (page+len, "ICLK : %d MHz\n", __cpm_get_iclk()/1000000); ++ len += sprintf (page+len, "SCLK : %d MHz\n", __cpm_get_sclk()/1000000); ++ len += sprintf (page+len, "MCLK : %d MHz\n", __cpm_get_mclk()/1000000); ++ len += sprintf (page+len, "PCLK : %d MHz\n", __cpm_get_pclk()/1000000); ++ len += sprintf (page+len, "DEVCLK : %d MHz\n", __cpm_get_devclk()/1000000); ++ len += sprintf (page+len, "RTCCLK : %d KHz\n", __cpm_get_rtcclk()/1000); ++ len += sprintf (page+len, "USBCLK : %d MHz\n", __cpm_get_usbclk()/1000000); ++#if defined(CONFIG_FB_JZ) ++ len += sprintf (page+len, "LCDCLK : %d MHz\n", __cpm_get_lcdclk()/1000000); ++ len += sprintf (page+len, "PIXCLK : %d MHz\n", __cpm_get_pixclk()/1000000); ++#endif ++ return len; ++} ++ ++static int cgm_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ REG_CPM_CFCR = simple_strtoul(buffer, 0, 16); ++ return count; ++} ++ ++/* ++ * WDT ++ */ ++static int wdt_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ ++ len += sprintf (page+len, "WDT_WTCSR : 0x%08x\n", REG_WDT_WTCSR); ++ len += sprintf (page+len, "WDT_WTCNT : 0x%08x\n", REG_WDT_WTCNT); ++ ++ return len; ++} ++ ++static int wdt_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ unsigned long cnt = simple_strtoul(buffer, 0, 16); ++ ++ REG_WDT_WTCNT = cnt; ++ REG_WDT_WTCSR = WDT_WTCSR_START; ++ ++ return count; ++} ++ ++/* ++ * PWM ++ */ ++ ++static int proc_jz_pwm_read_byte(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ return sprintf (page, "0x%02x\n", REG8(data)); ++} ++ ++static int proc_jz_pwm_read_word(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ return sprintf (page, "0x%04x\n", REG16(data)); ++} ++ ++static int proc_jz_pwm_write_byte(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ REG8(data) = simple_strtoul(buffer, 0, 16); ++ return count; ++} ++ ++static int proc_jz_pwm_write_word(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ REG16(data) = simple_strtoul(buffer, 0, 16); ++ return count; ++} ++ ++#define PWM_NUM 2 ++ ++static int jz_pwm_proc_init(void) ++{ ++ struct proc_dir_entry *proc_jz_pwm, *res; ++ char name[16]; ++ unsigned char i; ++ ++ for (i = 0; i < PWM_NUM; i++) { ++ sprintf(name, "pwm%d", i); ++ proc_jz_pwm = proc_mkdir(name, proc_jz_root); ++ res = create_proc_entry("control", 0600, proc_jz_pwm); ++ if ( res) { ++ res->read_proc = proc_jz_pwm_read_byte; ++ res->write_proc = proc_jz_pwm_write_byte; ++ if (i) ++ res->data = (void * )PWM_CTR(1); ++ else ++ res->data = (void * )PWM_CTR(0); ++ } ++ res = create_proc_entry("period", 0600, proc_jz_pwm); ++ if ( res) { ++ res->read_proc = proc_jz_pwm_read_word; ++ res->write_proc = proc_jz_pwm_write_word; ++ if (i) ++ res->data = (void *)PWM_PER(1); ++ else ++ res->data = (void *)PWM_PER(0); ++ } ++ res = create_proc_entry("duty", 0600, proc_jz_pwm); ++ if ( res) { ++ res->read_proc = proc_jz_pwm_read_word; ++ res->write_proc = proc_jz_pwm_write_word; ++ if (i) ++ res->data = (void * )PWM_DUT(1); ++ else ++ res->data = (void * )PWM_DUT(0); ++ } ++ } ++ return 0; ++} ++ ++/* ++ * /proc/jz/xxx entry ++ * ++ */ ++static int __init jz_proc_init(void) ++{ ++ struct proc_dir_entry *entry; ++ ++ /* create /proc/jz */ ++ proc_jz_root = proc_mkdir("jz", 0); ++ ++ /* create /proc/jz/emc */ ++ entry = create_proc_entry("emc", 0644, proc_jz_root); ++ if (entry) { ++ entry->read_proc = emc_read_proc; ++ entry->write_proc = NULL; ++ entry->data = NULL; ++ } ++ ++ /* create /proc/jz/pmc */ ++ entry = create_proc_entry("pmc", 0644, proc_jz_root); ++ if (entry) { ++ entry->read_proc = pmc_read_proc; ++ entry->write_proc = pmc_write_proc; ++ entry->data = NULL; ++ } ++ ++ /* create /proc/jz/cgm */ ++ entry = create_proc_entry("cgm", 0644, proc_jz_root); ++ if (entry) { ++ entry->read_proc = cgm_read_proc; ++ entry->write_proc = cgm_write_proc; ++ entry->data = NULL; ++ } ++ ++ /* create /proc/jz/wdt */ ++ entry = create_proc_entry("wdt", 0644, proc_jz_root); ++ if (entry) { ++ entry->read_proc = wdt_read_proc; ++ entry->write_proc = wdt_write_proc; ++ entry->data = NULL; ++ } ++ ++ /* PWM */ ++ jz_pwm_proc_init(); ++ ++ return 0; ++} ++ ++__initcall(jz_proc_init); +diff -urN linux-2.6.24.7.old/arch/mips/jz4730/prom.c linux-2.6.24.7/arch/mips/jz4730/prom.c +--- linux-2.6.24.7.old/arch/mips/jz4730/prom.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4730/prom.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,198 @@ ++/* ++ * ++ * BRIEF MODULE DESCRIPTION ++ * PROM library initialisation code, supports YAMON and U-Boot. ++ * ++ * Copyright 2000, 2001, 2006 MontaVista Software Inc. ++ * Author: MontaVista Software, Inc. ++ * ppopov@mvista.com or source@mvista.com ++ * ++ * This file was derived from Carsten Langgaard's ++ * arch/mips/mips-boards/xx files. ++ * ++ * Carsten Langgaard, carstenl@mips.com ++ * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN ++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, 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 DAMAGE. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* #define DEBUG_CMDLINE */ ++ ++int prom_argc; ++char **prom_argv, **prom_envp; ++ ++char * prom_getcmdline(void) ++{ ++ return &(arcs_cmdline[0]); ++} ++ ++void prom_init_cmdline(void) ++{ ++ char *cp; ++ int actr; ++ ++ actr = 1; /* Always ignore argv[0] */ ++ ++ cp = &(arcs_cmdline[0]); ++ while(actr < prom_argc) { ++ strcpy(cp, prom_argv[actr]); ++ cp += strlen(prom_argv[actr]); ++ *cp++ = ' '; ++ actr++; ++ } ++ if (cp != &(arcs_cmdline[0])) /* get rid of trailing space */ ++ --cp; ++ if (prom_argc > 1) ++ *cp = '\0'; ++ ++} ++ ++ ++char *prom_getenv(char *envname) ++{ ++#if 0 ++ /* ++ * Return a pointer to the given environment variable. ++ * YAMON uses "name", "value" pairs, while U-Boot uses "name=value". ++ */ ++ ++ char **env = prom_envp; ++ int i = strlen(envname); ++ int yamon = (*env && strchr(*env, '=') == NULL); ++ ++ while (*env) { ++ if (yamon) { ++ if (strcmp(envname, *env++) == 0) ++ return *env; ++ } else { ++ if (strncmp(envname, *env, i) == 0 && (*env)[i] == '=') ++ return *env + i + 1; ++ } ++ env++; ++ } ++#endif ++ return NULL; ++} ++ ++inline unsigned char str2hexnum(unsigned char c) ++{ ++ if(c >= '0' && c <= '9') ++ return c - '0'; ++ if(c >= 'a' && c <= 'f') ++ return c - 'a' + 10; ++ if(c >= 'A' && c <= 'F') ++ return c - 'A' + 10; ++ return 0; /* foo */ ++} ++ ++inline void str2eaddr(unsigned char *ea, unsigned char *str) ++{ ++ int i; ++ ++ for(i = 0; i < 6; i++) { ++ unsigned char num; ++ ++ if((*str == '.') || (*str == ':')) ++ str++; ++ num = str2hexnum(*str++) << 4; ++ num |= (str2hexnum(*str++)); ++ ea[i] = num; ++ } ++} ++ ++int get_ethernet_addr(char *ethernet_addr) ++{ ++ char *ethaddr_str; ++ ++ ethaddr_str = prom_getenv("ethaddr"); ++ if (!ethaddr_str) { ++ printk("ethaddr not set in boot prom\n"); ++ return -1; ++ } ++ str2eaddr(ethernet_addr, ethaddr_str); ++ ++#if 0 ++ { ++ int i; ++ ++ printk("get_ethernet_addr: "); ++ for (i=0; i<5; i++) ++ printk("%02x:", (unsigned char)*(ethernet_addr+i)); ++ printk("%02x\n", *(ethernet_addr+i)); ++ } ++#endif ++ ++ return 0; ++} ++ ++void __init prom_free_prom_memory(void) ++{ ++} ++ ++void __init prom_init(void) ++{ ++ unsigned char *memsize_str; ++ unsigned long memsize; ++ ++ prom_argc = (int) fw_arg0; ++ prom_argv = (char **) fw_arg1; ++ prom_envp = (char **) fw_arg2; ++ ++ mips_machtype = MACH_INGENIC_JZ4730; ++ ++ prom_init_cmdline(); ++ memsize_str = prom_getenv("memsize"); ++ if (!memsize_str) { ++ memsize = 0x04000000; ++ } else { ++ memsize = simple_strtol(memsize_str, NULL, 0); ++ } ++ add_memory_region(0, memsize, BOOT_MEM_RAM); ++} ++ ++/* used by early printk */ ++void prom_putchar(char c) ++{ ++ volatile u8 *uart_lsr = (volatile u8 *)(UART3_BASE + OFF_LSR); ++ volatile u8 *uart_tdr = (volatile u8 *)(UART3_BASE + OFF_TDR); ++ ++ /* Wait for fifo to shift out some bytes */ ++ while ( !((*uart_lsr & (UARTLSR_TDRQ | UARTLSR_TEMT)) == 0x60) ); ++ ++ *uart_tdr = (u8)c; ++} ++ ++const char *get_system_type(void) ++{ ++ return "JZ4730"; ++} ++ ++EXPORT_SYMBOL(prom_getcmdline); ++EXPORT_SYMBOL(get_ethernet_addr); ++EXPORT_SYMBOL(str2eaddr); +diff -urN linux-2.6.24.7.old/arch/mips/jz4730/reset.c linux-2.6.24.7/arch/mips/jz4730/reset.c +--- linux-2.6.24.7.old/arch/mips/jz4730/reset.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4730/reset.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,40 @@ ++/* ++ * linux/arch/mips/jz4730/reset.c ++ * ++ * JZ4730 reset routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++void jz_restart(char *command) ++{ ++ __wdt_set_count(0xffffffff-32); /* reset after 1/1024 s */ ++ __wdt_start(); ++ while (1); ++} ++ ++void jz_halt(void) ++{ ++ __wdt_set_count(0xffffffff-32); /* reset after 1/1024 s */ ++ __wdt_start(); ++ while (1); ++} ++ ++void jz_power_off(void) ++{ ++ jz_halt(); ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4730/setup.c linux-2.6.24.7/arch/mips/jz4730/setup.c +--- linux-2.6.24.7.old/arch/mips/jz4730/setup.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4730/setup.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,182 @@ ++/* ++ * linux/arch/mips/jz4730/setup.c ++ * ++ * JZ4730 CPU common setup routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_PC_KEYB ++#include ++#endif ++ ++jz_clocks_t jz_clocks; ++ ++extern char * __init prom_getcmdline(void); ++extern void __init jz_board_setup(void); ++extern void jz_restart(char *); ++extern void jz_halt(void); ++extern void jz_power_off(void); ++extern void jz_time_init(void); ++ ++static void __init sysclocks_setup(void) ++{ ++#ifndef CONFIG_JZ4730_URANUS ++ jz_clocks.iclk = __cpm_get_iclk(); ++ jz_clocks.sclk = __cpm_get_sclk(); ++ jz_clocks.mclk = __cpm_get_mclk(); ++ jz_clocks.pclk = __cpm_get_pclk(); ++ jz_clocks.devclk = __cpm_get_devclk(); ++ jz_clocks.rtcclk = __cpm_get_rtcclk(); ++ jz_clocks.uartclk = __cpm_get_uartclk(); ++ jz_clocks.lcdclk = __cpm_get_lcdclk(); ++ jz_clocks.pixclk = __cpm_get_pixclk(); ++ jz_clocks.usbclk = __cpm_get_usbclk(); ++ jz_clocks.i2sclk = __cpm_get_i2sclk(); ++ jz_clocks.mscclk = __cpm_get_mscclk(); ++#else /* URANUS FPGA */ ++ ++#define FPGACLK 8000000 ++ ++ jz_clocks.iclk = FPGACLK; ++ jz_clocks.sclk = FPGACLK; ++ jz_clocks.mclk = FPGACLK; ++ jz_clocks.devclk = FPGACLK; ++ jz_clocks.rtcclk = FPGACLK; ++ jz_clocks.uartclk = FPGACLK; ++ jz_clocks.pixclk = FPGACLK; ++ jz_clocks.lcdclk = FPGACLK; ++ jz_clocks.usbclk = FPGACLK; ++ jz_clocks.i2sclk = FPGACLK; ++ jz_clocks.mscclk = FPGACLK; ++#endif ++ ++ printk("CPU clock: %dMHz, System clock: %dMHz, Memory clock: %dMHz, Peripheral clock: %dMHz\n", ++ (jz_clocks.iclk + 500000) / 1000000, ++ (jz_clocks.sclk + 500000) / 1000000, ++ (jz_clocks.mclk + 500000) / 1000000, ++ (jz_clocks.pclk + 500000) / 1000000); ++} ++ ++static void __init soc_cpm_setup(void) ++{ ++ __cpm_idle_mode(); ++ __cpm_enable_cko1(); ++ __cpm_start_all(); ++ ++ /* get system clocks */ ++ sysclocks_setup(); ++} ++ ++static void __init soc_harb_setup(void) ++{ ++// __harb_set_priority(0x00); /* CIM>LCD>DMA>ETH>PCI>USB>CBB */ ++// __harb_set_priority(0x03); /* LCD>CIM>DMA>ETH>PCI>USB>CBB */ ++ __harb_set_priority(0x08); /* DMAC>LCD>CIM>ETH>USB>CIM */ ++// __harb_set_priority(0x0a); /* ETH>LCD>CIM>DMA>PCI>USB>CBB */ ++} ++ ++static void __init soc_emc_setup(void) ++{ ++} ++ ++static void __init soc_dmac_setup(void) ++{ ++ __dmac_enable_all_channels(); ++} ++ ++static void __init jz_soc_setup(void) ++{ ++ soc_cpm_setup(); ++ soc_harb_setup(); ++ soc_emc_setup(); ++ soc_dmac_setup(); ++} ++ ++static void __init jz_serial_setup(void) ++{ ++#ifdef CONFIG_SERIAL_8250 ++ struct uart_port s; ++ ++ memset(&s, 0, sizeof(s)); ++ ++ s.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST; ++ s.iotype = UPIO_MEM; ++ s.regshift = 2; ++ s.uartclk = jz_clocks.uartclk; ++ ++ s.line = 0; ++ s.membase = (u8 *)UART0_BASE; ++ s.irq = IRQ_UART0; ++ if (early_serial_setup(&s) != 0) { ++ printk(KERN_ERR "Serial ttyS0 setup failed!\n"); ++ } ++ ++ s.line = 1; ++ s.membase = (u8 *)UART1_BASE; ++ s.irq = IRQ_UART1; ++ if (early_serial_setup(&s) != 0) { ++ printk(KERN_ERR "Serial ttyS1 setup failed!\n"); ++ } ++ ++ s.line = 2; ++ s.membase = (u8 *)UART2_BASE; ++ s.irq = IRQ_UART2; ++ if (early_serial_setup(&s) != 0) { ++ printk(KERN_ERR "Serial ttyS2 setup failed!\n"); ++ } ++ ++ s.line = 3; ++ s.membase = (u8 *)UART3_BASE; ++ s.irq = IRQ_UART3; ++ if (early_serial_setup(&s) != 0) { ++ printk(KERN_ERR "Serial ttyS3 setup failed!\n"); ++ } ++#endif ++} ++ ++void __init plat_mem_setup(void) ++{ ++ char *argptr; ++ ++ argptr = prom_getcmdline(); ++ ++ /* IO/MEM resources. */ ++ set_io_port_base(0); ++ ioport_resource.start = 0x00000000; ++ ioport_resource.end = 0xffffffff; ++ iomem_resource.start = 0x00000000; ++ iomem_resource.end = 0xffffffff; ++ ++ _machine_restart = jz_restart; ++ _machine_halt = jz_halt; ++ pm_power_off = jz_power_off; ++ ++ jz_soc_setup(); /* soc specific setup */ ++ jz_serial_setup(); /* serial port setup */ ++ jz_board_setup(); /* board specific setup */ ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4730/sleep.S linux-2.6.24.7/arch/mips/jz4730/sleep.S +--- linux-2.6.24.7.old/arch/mips/jz4730/sleep.S 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4730/sleep.S 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,307 @@ ++/* ++ * linux/arch/mips/jz4730/sleep.S ++ * ++ * jz4730 Assembler Sleep/WakeUp Management Routines ++ * ++ * Copyright (C) 2005 Ingenic Semiconductor ++ * Author: ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++ ++ .text ++ .set noreorder ++ .set noat ++ ++ .extern jz_flush_cache_all ++ ++/* ++ * jz_cpu_suspend() ++ * ++ * Forces CPU into hibernate mode ++ */ ++ ++ .globl jz_cpu_suspend ++jz_cpu_suspend: ++ ++ /* save hi, lo and general registers except k0($26) and k1($27) (total 32) */ ++ move k0, sp ++ addiu k0, k0, -(32*4) ++ mfhi k1 ++ sw $0, 0(k0) ++ sw $1, 4(k0) ++ sw k1, 120(k0) /* hi */ ++ mflo k1 ++ sw $2, 8(k0) ++ sw $3, 12(k0) ++ sw k1, 124(k0) /* lo */ ++ sw $4, 16(k0) ++ sw $5, 20(k0) ++ sw $6, 24(k0) ++ sw $7, 28(k0) ++ sw $8, 32(k0) ++ sw $9, 36(k0) ++ sw $10, 40(k0) ++ sw $11, 44(k0) ++ sw $12, 48(k0) ++ sw $13, 52(k0) ++ sw $14, 56(k0) ++ sw $15, 60(k0) ++ sw $16, 64(k0) ++ sw $17, 68(k0) ++ sw $18, 72(k0) ++ sw $19, 76(k0) ++ sw $20, 80(k0) ++ sw $21, 84(k0) ++ sw $22, 88(k0) ++ sw $23, 92(k0) ++ sw $24, 96(k0) ++ sw $25, 100(k0) ++ sw $28, 104(k0) ++ sw $29, 108(k0) /* saved sp */ ++ sw $30, 112(k0) ++ sw $31, 116(k0) /* saved ra */ ++ move sp, k0 ++ ++ /* save CP0 registers and sp (total 26) */ ++ move k0, sp ++ addiu k0, k0, -(26*4) ++ ++ mfc0 $1, CP0_INDEX ++ mfc0 $2, CP0_RANDOM ++ mfc0 $3, CP0_ENTRYLO0 ++ mfc0 $4, CP0_ENTRYLO1 ++ mfc0 $5, CP0_CONTEXT ++ mfc0 $6, CP0_PAGEMASK ++ mfc0 $7, CP0_WIRED ++ mfc0 $8, CP0_BADVADDR ++ mfc0 $9, CP0_ENTRYHI ++ mfc0 $10, CP0_STATUS ++/* mfc0 $11, $12, 1*/ /* IntCtl */ ++ mfc0 $12, CP0_CAUSE ++ mfc0 $13, CP0_EPC ++/* mfc0 $14, $15, 1*/ /* EBase */ ++ mfc0 $15, CP0_CONFIG ++/* mfc0 $16, CP0_CONFIG, 7*/ /* Config 7 */ ++ mfc0 $17, CP0_LLADDR ++ mfc0 $18, CP0_WATCHLO ++ mfc0 $19, CP0_WATCHHI ++ mfc0 $20, CP0_DEBUG ++ mfc0 $21, CP0_DEPC ++ mfc0 $22, CP0_ECC ++ mfc0 $23, CP0_TAGLO ++ mfc0 $24, CP0_ERROREPC ++ mfc0 $25, CP0_DESAVE ++ ++ sw $1, 0(k0) ++ sw $2, 4(k0) ++ sw $3, 8(k0) ++ sw $4, 12(k0) ++ sw $5, 16(k0) ++ sw $6, 20(k0) ++ sw $7, 24(k0) ++ sw $8, 28(k0) ++ sw $9, 32(k0) ++ sw $10, 36(k0) ++ sw $11, 40(k0) ++ sw $12, 44(k0) ++ sw $13, 48(k0) ++ sw $14, 52(k0) ++ sw $15, 56(k0) ++ sw $16, 60(k0) ++ sw $17, 64(k0) ++ sw $18, 68(k0) ++ sw $19, 72(k0) ++ sw $20, 76(k0) ++ sw $21, 80(k0) ++ sw $22, 84(k0) ++ sw $23, 88(k0) ++ sw $24, 92(k0) ++ sw $25, 96(k0) ++ sw $29, 100(k0) /* saved sp */ ++ move sp, k0 ++ ++ /* preserve virtual address of stack */ ++ la k0, suspend_save_sp ++ sw sp, 0(k0) ++ ++ /* flush caches and write buffers */ ++ jal jz_flush_cache_all ++ nop ++ ++ /* set new sdram refresh constant */ ++ li t0, 1 ++ la t1, EMC_RTCOR ++ sh t0, 0(t1) ++ ++ /* disable PLL */ ++ la t0, CPM_PLCR1 ++ sw $0, 0(t0) ++ ++ /* put CPU to hibernate mode */ ++ la t0, CPM_LPCR ++ lw t1, 0(t0) ++ li t2, ~CPM_LPCR_LPM_MASK ++ and t1, t2 ++ ori t1, CPM_LPCR_LPM_HIBERNATE ++ ++ .align 5 ++ /* align execution to a cache line */ ++ j 1f ++ ++ .align 5 ++1: ++ /* all needed values are now in registers. ++ * These last instructions should be in cache ++ */ ++ nop ++ nop ++ ++ /* set hibernate mode */ ++ sw t1, 0(t0) ++ nop ++ ++ /* enter hibernate mode */ ++ .set mips3 ++ wait ++ nop ++ .set mips2 ++ ++2: j 2b /* loop waiting for suspended */ ++ nop ++ ++/* ++ * jz_cpu_resume() ++ * ++ * entry point from bootloader into kernel during resume ++ */ ++ ++ .align 5 ++ .globl jz_cpu_resume ++jz_cpu_resume: ++ /* clear SCR.HGP */ ++ la t0, CPM_SCR ++ lw t1, 0(t0) ++ li t2, ~CPM_SCR_HGP ++ and t1, t2 ++ sw t1, 0(t0) ++ ++ /* restore LPCR.LPM to IDLE mode */ ++ la t0, CPM_LPCR ++ lw t1, 0(t0) ++ li t2, ~CPM_LPCR_LPM_MASK ++ and t1, t2 ++ ori t1, CPM_LPCR_LPM_IDLE ++ sw t1, 0(t0) ++ ++ /* restore saved sp */ ++ la t0, suspend_save_sp ++ lw sp, 0(t0) ++ ++ /* restore CP0 registers */ ++ move k0, sp ++ lw $1, 0(k0) ++ lw $2, 4(k0) ++ lw $3, 8(k0) ++ lw $4, 12(k0) ++ lw $5, 16(k0) ++ lw $6, 20(k0) ++ lw $7, 24(k0) ++ lw $8, 28(k0) ++ lw $9, 32(k0) ++ lw $10, 36(k0) ++ lw $11, 40(k0) ++ lw $12, 44(k0) ++ lw $13, 48(k0) ++ lw $14, 52(k0) ++ lw $15, 56(k0) ++ lw $16, 60(k0) ++ lw $17, 64(k0) ++ lw $18, 68(k0) ++ lw $19, 72(k0) ++ lw $20, 76(k0) ++ lw $21, 80(k0) ++ lw $22, 84(k0) ++ lw $23, 88(k0) ++ lw $24, 92(k0) ++ lw $25, 96(k0) ++ lw $29, 100(k0) /* saved sp */ ++ ++ mtc0 $1, CP0_INDEX ++ mtc0 $2, CP0_RANDOM ++ mtc0 $3, CP0_ENTRYLO0 ++ mtc0 $4, CP0_ENTRYLO1 ++ mtc0 $5, CP0_CONTEXT ++ mtc0 $6, CP0_PAGEMASK ++ mtc0 $7, CP0_WIRED ++ mtc0 $8, CP0_BADVADDR ++ mtc0 $9, CP0_ENTRYHI ++ mtc0 $10, CP0_STATUS ++/* mtc0 $11, $12, 1*/ /* IntCtl */ ++ mtc0 $12, CP0_CAUSE ++ mtc0 $13, CP0_EPC ++/* mtc0 $14, $15, 1*/ /* EBase */ ++ mtc0 $15, CP0_CONFIG ++/* mtc0 $16, CP0_CONFIG, 7*/ /* Config 7 */ ++ mtc0 $17, CP0_LLADDR ++ mtc0 $18, CP0_WATCHLO ++ mtc0 $19, CP0_WATCHHI ++ mtc0 $20, CP0_DEBUG ++ mtc0 $21, CP0_DEPC ++ mtc0 $22, CP0_ECC ++ mtc0 $23, CP0_TAGLO ++ mtc0 $24, CP0_ERROREPC ++ mtc0 $25, CP0_DESAVE ++ ++ /* restore general registers */ ++ move k0, sp ++ lw k1, 120(k0) /* hi */ ++ lw $0, 0(k0) ++ lw $1, 4(k0) ++ mthi k1 ++ lw k1, 124(k0) /* lo */ ++ lw $2, 8(k0) ++ lw $3, 12(k0) ++ mtlo k1 ++ lw $4, 16(k0) ++ lw $5, 20(k0) ++ lw $6, 24(k0) ++ lw $7, 28(k0) ++ lw $8, 32(k0) ++ lw $9, 36(k0) ++ lw $10, 40(k0) ++ lw $11, 44(k0) ++ lw $12, 48(k0) ++ lw $13, 52(k0) ++ lw $14, 56(k0) ++ lw $15, 60(k0) ++ lw $16, 64(k0) ++ lw $17, 68(k0) ++ lw $18, 72(k0) ++ lw $19, 76(k0) ++ lw $20, 80(k0) ++ lw $21, 84(k0) ++ lw $22, 88(k0) ++ lw $23, 92(k0) ++ lw $24, 96(k0) ++ lw $25, 100(k0) ++ lw $28, 104(k0) ++ lw $29, 108(k0) /* saved sp */ ++ lw $30, 112(k0) ++ lw $31, 116(k0) /* saved ra */ ++ ++ /* return to caller */ ++ jr ra ++ nop ++ ++suspend_save_sp: ++ .word 0 /* preserve sp here */ ++ ++ .set reorder +diff -urN linux-2.6.24.7.old/arch/mips/jz4730/time.c linux-2.6.24.7/arch/mips/jz4730/time.c +--- linux-2.6.24.7.old/arch/mips/jz4730/time.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4730/time.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,129 @@ ++/* ++ * linux/arch/mips/jz4730/time.c ++ * ++ * Setting up the clock on the JZ4730 boards. ++ * ++ * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define JZ_TIMER_CHAN 0 ++#define JZ_TIMER_IRQ IRQ_OST0 ++#define JZ_TIMER_CLOCK JZ_EXTAL ++ ++static unsigned int timer_latch; ++ ++void (*jz_timer_callback)(void); ++ ++static void jz_set_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt) ++{ ++ switch (mode) { ++ case CLOCK_EVT_MODE_PERIODIC: ++ break; ++ case CLOCK_EVT_MODE_ONESHOT: ++ case CLOCK_EVT_MODE_UNUSED: ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ break; ++ case CLOCK_EVT_MODE_RESUME: ++ break; ++ } ++} ++ ++static struct clock_event_device jz_clockevent_device = { ++ .name = "jz-timer", ++ .features = CLOCK_EVT_FEAT_PERIODIC, ++ ++ /* .mult, .shift, .max_delta_ns and .min_delta_ns left uninitialized */ ++ ++ .rating = 300, ++ .irq = JZ_TIMER_IRQ, ++ .set_mode = jz_set_mode, ++}; ++ ++static irqreturn_t jz_timer_interrupt(int irq, void *dev_id) ++{ ++ struct clock_event_device *cd = dev_id; ++ ++ __ost_clear_uf(JZ_TIMER_CHAN); /* ACK timer */ ++ ++ if (jz_timer_callback) ++ jz_timer_callback(); ++ ++ cd->event_handler(cd); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct irqaction jz_irqaction = { ++ .handler = jz_timer_interrupt, ++ .flags = IRQF_DISABLED | IRQF_PERCPU, ++ .name = "jz-timer", ++}; ++ ++cycle_t jz_get_cycles(void) ++{ ++ unsigned int jz_timer_cnt; ++#if 0 /* clock source use pll, read directly */ ++ jz_timer_cnt = timer_latch - REG_OST_TCNT(JZ_TIMER_CHAN); ++#else /* clock source use RTCClock or Extall Clock, wait read ready */ ++ jz_timer_cnt = REG_OST_TCNT(JZ_TIMER_CHAN); /* dummy read */ ++ while ( __ost_is_busy(JZ_TIMER_CHAN) ) ; /* wait read ready */ ++ jz_timer_cnt = timer_latch - REG_OST_TCRB(JZ_TIMER_CHAN); ++#endif ++ ++ /* convert jiffes to jz timer cycles */ ++ return (cycle_t)( jiffies*((JZ_TIMER_CLOCK)/HZ) + jz_timer_cnt); ++} ++ ++static struct clocksource clocksource_jz = { ++ .name = "jz_clocksource", ++ .rating = 300, ++ .read = jz_get_cycles, ++ .mask = 0xFFFFFFFF, ++ .shift = 10, /* control clocksource.mult's accuracy */ ++ .flags = CLOCK_SOURCE_WATCHDOG, ++}; ++ ++static int __init jz_clocksource_init(void) ++{ ++ clocksource_jz.mult = clocksource_hz2mult(JZ_TIMER_CLOCK, clocksource_jz.shift); ++ clocksource_register(&clocksource_jz); ++ return 0; ++} ++ ++static void __init jz_timer_setup(void) ++{ ++ struct clock_event_device *cd = &jz_clockevent_device; ++ struct irqaction *action = &jz_irqaction; ++ unsigned int cpu = smp_processor_id(); ++ ++ jz_clocksource_init(); ++ cd->cpumask = cpumask_of_cpu(cpu); ++ clockevents_register_device(cd); ++ action->dev_id = cd; ++ setup_irq(JZ_TIMER_IRQ, &jz_irqaction); ++} ++ ++void __init plat_time_init(void) ++{ ++ /* Init timer, timer clock soure use extal clock */ ++ timer_latch = (JZ_TIMER_CLOCK + (HZ>>1)) / HZ; ++ __ost_set_mode(JZ_TIMER_CHAN, OST_TCSR_UIE | OST_TCSR_CKS_EXTAL); ++ __ost_set_reload(JZ_TIMER_CHAN, timer_latch); ++ __ost_set_count(JZ_TIMER_CHAN, timer_latch); ++ __ost_enable_channel(JZ_TIMER_CHAN); ++ ++ jz_timer_setup(); ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/Makefile linux-2.6.24.7/arch/mips/jz4740/Makefile +--- linux-2.6.24.7.old/arch/mips/jz4740/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,26 @@ ++# ++# Makefile for the Ingenic JZ4740. ++# ++ ++# Object file lists. ++ ++obj-y += prom.o irq.o time.o reset.o setup.o dma.o \ ++ platform.o i2c.o ++ ++obj-$(CONFIG_PROC_FS) += proc.o ++ ++# board specific support ++ ++obj-$(CONFIG_JZ4740_PAVO) += board-pavo.o ++obj-$(CONFIG_JZ4740_LEO) += board-leo.o ++obj-$(CONFIG_JZ4740_LYRA) += board-lyra.o ++obj-$(CONFIG_JZ4725_DIPPER) += board-dipper.o ++obj-$(CONFIG_JZ4720_VIRGO) += board-virgo.o ++ ++# PM support ++ ++obj-$(CONFIG_PM_LEGACY) +=pm.o ++ ++# CPU Frequency scaling support ++ ++obj-$(CONFIG_CPU_FREQ_JZ) +=cpufreq.o +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/board-dipper.c linux-2.6.24.7/arch/mips/jz4740/board-dipper.c +--- linux-2.6.24.7.old/arch/mips/jz4740/board-dipper.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/board-dipper.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,117 @@ ++/* ++ * linux/arch/mips/jz4740/board-dipper.c ++ * ++ * JZ4725 Dipper board setup routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++extern void (*jz_timer_callback)(void); ++ ++#if 0 ++static void dancing(void) ++{ ++ static unsigned int count = 0; ++ ++ count ++; ++ count &= 1; ++ ++ if (count) ++ __gpio_set_pin(GPIO_LED_EN); ++ else ++ __gpio_clear_pin(GPIO_LED_EN); ++} ++#endif ++ ++static void dipper_timer_callback(void) ++{ ++ static unsigned long count = 0; ++ ++ if ((++count) % 50 == 0) { ++// dancing(); ++ count = 0; ++ } ++} ++ ++static void __init board_cpm_setup(void) ++{ ++ /* Stop unused module clocks here. ++ * We have started all module clocks at arch/mips/jz4740/setup.c. ++ */ ++} ++ ++static void __init board_gpio_setup(void) ++{ ++ /* ++ * Most of the GPIO pins should have been initialized by the boot-loader ++ */ ++ ++ /* ++ * Initialize MSC pins ++ */ ++ __gpio_as_msc(); ++ ++ /* ++ * Initialize Smart LCD pins ++ */ ++// __gpio_as_slcd_18bit(); ++ ++ /* ++ * Initialize SSI pins ++ */ ++ __gpio_as_ssi(); ++ ++ /* ++ * Initialize I2C pins ++ */ ++ __gpio_as_i2c(); ++ ++ /* ++ * Initialize Other pins ++ */ ++ __gpio_as_output(GPIO_SD_VCC_EN_N); ++ __gpio_clear_pin(GPIO_SD_VCC_EN_N); ++ ++ __gpio_as_input(GPIO_SD_CD_N); ++ __gpio_disable_pull(GPIO_SD_CD_N); ++ ++ __gpio_as_input(GPIO_SD_WP); ++ __gpio_disable_pull(GPIO_SD_WP); ++ ++ __gpio_as_input(GPIO_DC_DETE_N); ++ __gpio_as_input(GPIO_CHARG_STAT_N); ++ __gpio_as_input(GPIO_USB_DETE); ++ ++ __gpio_as_output(GPIO_DISP_OFF_N); ++ ++// __gpio_as_output(GPIO_LED_EN); ++} ++ ++void __init jz_board_setup(void) ++{ ++ printk("JZ4725 DIPPER board setup\n"); ++ ++ board_cpm_setup(); ++ board_gpio_setup(); ++ ++ jz_timer_callback = dipper_timer_callback; ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/board-leo.c linux-2.6.24.7/arch/mips/jz4740/board-leo.c +--- linux-2.6.24.7.old/arch/mips/jz4740/board-leo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/board-leo.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,67 @@ ++/* ++ * linux/arch/mips/jz4740/board-leo.c ++ * ++ * JZ4740 LEO board setup routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++extern void (*jz_timer_callback)(void); ++ ++static void dancing(void) ++{ ++ static unsigned char slash[] = "\\|/-"; ++ static volatile unsigned char *p = (unsigned char *)0xb6000016; ++ static unsigned int count = 0; ++ *p = slash[count++]; ++ count &= 3; ++} ++ ++static void leo_timer_callback(void) ++{ ++ static unsigned long count = 0; ++ ++ if ((++count) % 10 == 0) { ++ dancing(); ++ count = 0; ++ } ++} ++ ++static void __init board_cpm_setup(void) ++{ ++ /* Stop unused module clocks here. ++ * We have started all module clocks at arch/mips/jz4740/setup.c. ++ */ ++} ++ ++static void __init board_gpio_setup(void) ++{ ++ /* All GPIO pins should have been initialized by the boot-loader */ ++} ++ ++void __init jz_board_setup(void) ++{ ++ board_cpm_setup(); ++ board_gpio_setup(); ++ printk(" BOARD SETUP"); ++ jz_timer_callback = leo_timer_callback; ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/board-lyra.c linux-2.6.24.7/arch/mips/jz4740/board-lyra.c +--- linux-2.6.24.7.old/arch/mips/jz4740/board-lyra.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/board-lyra.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,114 @@ ++/* ++ * linux/arch/mips/jz4740/board-lyra.c ++ * ++ * JZ4740 LYRA board setup routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++extern void (*jz_timer_callback)(void); ++ ++static void dancing(void) ++{ ++ static unsigned int count = 0; ++ ++ count ++; ++ count &= 1; ++ if (count) ++ __gpio_set_pin(GPIO_LED_EN); ++ else ++ __gpio_clear_pin(GPIO_LED_EN); ++} ++ ++static void lyra_timer_callback(void) ++{ ++ static unsigned long count = 0; ++ ++ if ((++count) % 50 == 0) { ++ dancing(); ++ count = 0; ++ } ++} ++ ++static void __init board_cpm_setup(void) ++{ ++ /* Stop unused module clocks here. ++ * We have started all module clocks at arch/mips/jz4740/setup.c. ++ */ ++} ++ ++static void __init board_gpio_setup(void) ++{ ++ /* ++ * Most of the GPIO pins should have been initialized by the boot-loader ++ */ ++ ++ /* ++ * Initialize MSC pins ++ */ ++ __gpio_as_msc(); ++ ++ /* ++ * Initialize LCD pins ++ */ ++ __gpio_as_lcd_18bit(); ++ ++ /* ++ * Initialize SSI pins ++ */ ++ __gpio_as_ssi(); ++ ++ /* ++ * Initialize I2C pins ++ */ ++ __gpio_as_i2c(); ++ ++ /* ++ * Initialize Other pins ++ */ ++ __gpio_as_output(GPIO_SD_VCC_EN_N); ++ __gpio_clear_pin(GPIO_SD_VCC_EN_N); ++ ++ __gpio_as_input(GPIO_SD_CD_N); ++ __gpio_disable_pull(GPIO_SD_CD_N); ++ ++ __gpio_as_input(GPIO_SD_WP); ++ __gpio_disable_pull(GPIO_SD_WP); ++ ++ __gpio_as_input(GPIO_DC_DETE_N); ++ __gpio_as_input(GPIO_CHARG_STAT_N); ++ __gpio_as_input(GPIO_USB_DETE); ++ ++ __gpio_as_output(GPIO_DISP_OFF_N); ++ ++ __gpio_as_output(GPIO_LED_EN); ++} ++ ++void __init jz_board_setup(void) ++{ ++ printk("JZ4740 LYRA board setup\n"); ++ ++ board_cpm_setup(); ++ board_gpio_setup(); ++ ++ jz_timer_callback = lyra_timer_callback; ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/board-pavo.c linux-2.6.24.7/arch/mips/jz4740/board-pavo.c +--- linux-2.6.24.7.old/arch/mips/jz4740/board-pavo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/board-pavo.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,114 @@ ++/* ++ * linux/arch/mips/jz4740/board-pavo.c ++ * ++ * JZ4740 PAVO board setup routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++extern void (*jz_timer_callback)(void); ++ ++static void dancing(void) ++{ ++ static unsigned int count = 0; ++ ++ count ++; ++ count &= 1; ++ if (count) ++ __gpio_set_pin(GPIO_LED_EN); ++ else ++ __gpio_clear_pin(GPIO_LED_EN); ++} ++ ++static void pavo_timer_callback(void) ++{ ++ static unsigned long count = 0; ++ ++ if ((++count) % 50 == 0) { ++ dancing(); ++ count = 0; ++ } ++} ++ ++static void __init board_cpm_setup(void) ++{ ++ /* Stop unused module clocks here. ++ * We have started all module clocks at arch/mips/jz4740/setup.c. ++ */ ++} ++ ++static void __init board_gpio_setup(void) ++{ ++ /* ++ * Most of the GPIO pins should have been initialized by the boot-loader ++ */ ++ ++ /* ++ * Initialize MSC pins ++ */ ++ __gpio_as_msc(); ++ ++ /* ++ * Initialize LCD pins ++ */ ++ __gpio_as_lcd_18bit(); ++ ++ /* ++ * Initialize SSI pins ++ */ ++ __gpio_as_ssi(); ++ ++ /* ++ * Initialize I2C pins ++ */ ++ __gpio_as_i2c(); ++ ++ /* ++ * Initialize Other pins ++ */ ++ __gpio_as_output(GPIO_SD_VCC_EN_N); ++ __gpio_clear_pin(GPIO_SD_VCC_EN_N); ++ ++ __gpio_as_input(GPIO_SD_CD_N); ++ __gpio_disable_pull(GPIO_SD_CD_N); ++ ++ __gpio_as_input(GPIO_SD_WP); ++ __gpio_disable_pull(GPIO_SD_WP); ++ ++ __gpio_as_input(GPIO_DC_DETE_N); ++ __gpio_as_input(GPIO_CHARG_STAT_N); ++ __gpio_as_input(GPIO_USB_DETE); ++ ++ __gpio_as_output(GPIO_DISP_OFF_N); ++ ++ __gpio_as_output(GPIO_LED_EN); ++} ++ ++void __init jz_board_setup(void) ++{ ++ printk("JZ4740 PAVO board setup\n"); ++ ++ board_cpm_setup(); ++ board_gpio_setup(); ++ ++ jz_timer_callback = pavo_timer_callback; ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/board-virgo.c linux-2.6.24.7/arch/mips/jz4740/board-virgo.c +--- linux-2.6.24.7.old/arch/mips/jz4740/board-virgo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/board-virgo.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,114 @@ ++/* ++ * linux/arch/mips/jz4740/board-virgo.c ++ * ++ * JZ4720 VIRGO board setup routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++extern void (*jz_timer_callback)(void); ++ ++static void dancing(void) ++{ ++ static unsigned int count = 0; ++ ++ count ++; ++ count &= 1; ++ if (count) ++ __gpio_set_pin(GPIO_LED_EN); ++ else ++ __gpio_clear_pin(GPIO_LED_EN); ++} ++ ++static void virgo_timer_callback(void) ++{ ++ static unsigned long count = 0; ++ ++ if ((++count) % 50 == 0) { ++ dancing(); ++ count = 0; ++ } ++} ++ ++static void __init board_cpm_setup(void) ++{ ++ /* Stop unused module clocks here. ++ * We have started all module clocks at arch/mips/jz4740/setup.c. ++ */ ++} ++ ++static void __init board_gpio_setup(void) ++{ ++ /* ++ * Most of the GPIO pins should have been initialized by the boot-loader ++ */ ++ ++ /* ++ * Initialize MSC pins ++ */ ++ __gpio_as_msc(); ++ ++ /* ++ * Initialize LCD pins ++ */ ++// __gpio_as_lcd_18bit(); ++ ++ /* ++ * Initialize SSI pins ++ */ ++ __gpio_as_ssi(); ++ ++ /* ++ * Initialize I2C pins ++ */ ++ __gpio_as_i2c(); ++ ++ /* ++ * Initialize Other pins ++ */ ++ __gpio_as_output(GPIO_SD_VCC_EN_N); ++ __gpio_clear_pin(GPIO_SD_VCC_EN_N); ++ ++ __gpio_as_input(GPIO_SD_CD_N); ++ __gpio_disable_pull(GPIO_SD_CD_N); ++ ++// __gpio_as_input(GPIO_SD_WP); ++// __gpio_disable_pull(GPIO_SD_WP); ++ ++ __gpio_as_input(GPIO_DC_DETE_N); ++// __gpio_as_input(GPIO_CHARG_STAT_N); ++ __gpio_as_input(GPIO_USB_DETE); ++ ++ __gpio_as_output(GPIO_DISP_OFF_N); ++ ++// __gpio_as_output(GPIO_LED_EN); ++} ++ ++void __init jz_board_setup(void) ++{ ++ printk("JZ4720 VIRGO board setup\n"); ++ ++ board_cpm_setup(); ++ board_gpio_setup(); ++ ++ jz_timer_callback = virgo_timer_callback; ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/cpufreq.c linux-2.6.24.7/arch/mips/jz4740/cpufreq.c +--- linux-2.6.24.7.old/arch/mips/jz4740/cpufreq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/cpufreq.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,602 @@ ++/* ++ * linux/arch/mips/jz4740/cpufreq.c ++ * ++ * cpufreq driver for JZ4740 ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ ++ "cpufreq-jz4740", msg) ++ ++#undef CHANGE_PLL ++ ++#define PLL_UNCHANGED 0 ++#define PLL_GOES_UP 1 ++#define PLL_GOES_DOWN 2 ++ ++#define PLL_WAIT_500NS (500*(__cpm_get_cclk()/1000000000)) ++ ++/* Saved the boot-time parameters */ ++static struct { ++ /* SDRAM parameters */ ++ unsigned int mclk; /* memory clock, KHz */ ++ unsigned int tras; /* RAS pulse width, cycles of mclk */ ++ unsigned int rcd; /* RAS to CAS Delay, cycles of mclk */ ++ unsigned int tpc; /* RAS Precharge time, cycles of mclk */ ++ unsigned int trwl; /* Write Precharge Time, cycles of mclk */ ++ unsigned int trc; /* RAS Cycle Time, cycles of mclk */ ++ unsigned int rtcor; /* Refresh Time Constant */ ++ unsigned int sdram_initialized; ++ ++ /* LCD parameters */ ++ unsigned int lcd_clk; /* LCD clock, Hz */ ++ unsigned int lcdpix_clk; /* LCD Pixel clock, Hz */ ++ unsigned int lcd_clks_initialized; ++} boot_config; ++ ++struct jz4740_freq_percpu_info { ++ struct cpufreq_frequency_table table[7]; ++}; ++ ++static struct jz4740_freq_percpu_info jz4740_freq_table; ++ ++/* ++ * This contains the registers value for an operating point. ++ * If only part of a register needs to change then there is ++ * a mask value for that register. ++ * When going to a new operating point the current register ++ * value is ANDed with the ~mask and ORed with the new value. ++ */ ++struct dpm_regs { ++ u32 cpccr; /* Clock Freq Control Register */ ++ u32 cpccr_mask; /* Clock Freq Control Register mask */ ++ u32 cppcr; /* PLL1 Control Register */ ++ u32 cppcr_mask; /* PLL1 Control Register mask */ ++ u32 pll_up_flag; /* New PLL freq is higher than current or not */ ++}; ++ ++extern jz_clocks_t jz_clocks; ++ ++static void jz_update_clocks(void) ++{ ++ /* Next clocks must be updated if we have changed ++ * the PLL or divisors. ++ */ ++ jz_clocks.cclk = __cpm_get_cclk(); ++ jz_clocks.hclk = __cpm_get_hclk(); ++ jz_clocks.mclk = __cpm_get_mclk(); ++ jz_clocks.pclk = __cpm_get_pclk(); ++ jz_clocks.lcdclk = __cpm_get_lcdclk(); ++ jz_clocks.pixclk = __cpm_get_pixclk(); ++ jz_clocks.i2sclk = __cpm_get_i2sclk(); ++ jz_clocks.usbclk = __cpm_get_usbclk(); ++ jz_clocks.mscclk = __cpm_get_mscclk(); ++} ++ ++static void ++jz_init_boot_config(void) ++{ ++ if (!boot_config.lcd_clks_initialized) { ++ /* the first time to scale pll */ ++ boot_config.lcd_clk = __cpm_get_lcdclk(); ++ boot_config.lcdpix_clk = __cpm_get_pixclk(); ++ boot_config.lcd_clks_initialized = 1; ++ } ++ ++ if (!boot_config.sdram_initialized) { ++ /* the first time to scale frequencies */ ++ unsigned int dmcr, rtcor; ++ unsigned int tras, rcd, tpc, trwl, trc; ++ ++ dmcr = REG_EMC_DMCR; ++ rtcor = REG_EMC_RTCOR; ++ ++ tras = (dmcr >> 13) & 0x7; ++ rcd = (dmcr >> 11) & 0x3; ++ tpc = (dmcr >> 8) & 0x7; ++ trwl = (dmcr >> 5) & 0x3; ++ trc = (dmcr >> 2) & 0x7; ++ ++ boot_config.mclk = __cpm_get_mclk() / 1000; ++ boot_config.tras = tras + 4; ++ boot_config.rcd = rcd + 1; ++ boot_config.tpc = tpc + 1; ++ boot_config.trwl = trwl + 1; ++ boot_config.trc = trc * 2 + 1; ++ boot_config.rtcor = rtcor; ++ ++ boot_config.sdram_initialized = 1; ++ } ++} ++ ++static void jz_update_dram_rtcor(unsigned int new_mclk) ++{ ++ unsigned int rtcor; ++ ++ new_mclk /= 1000; ++ rtcor = boot_config.rtcor * new_mclk / boot_config.mclk; ++ rtcor--; ++ ++ if (rtcor < 1) rtcor = 1; ++ if (rtcor > 255) rtcor = 255; ++ ++ REG_EMC_RTCOR = rtcor; ++ REG_EMC_RTCNT = rtcor; ++} ++ ++static void jz_update_dram_dmcr(unsigned int new_mclk) ++{ ++ unsigned int dmcr; ++ unsigned int tras, rcd, tpc, trwl, trc; ++ unsigned int valid_time, new_time; /* ns */ ++ ++ new_mclk /= 1000; ++ tras = boot_config.tras * new_mclk / boot_config.mclk; ++ rcd = boot_config.rcd * new_mclk / boot_config.mclk; ++ tpc = boot_config.tpc * new_mclk / boot_config.mclk; ++ trwl = boot_config.trwl * new_mclk / boot_config.mclk; ++ trc = boot_config.trc * new_mclk / boot_config.mclk; ++ ++ /* Validation checking */ ++ valid_time = (boot_config.tras * 1000000) / boot_config.mclk; ++ new_time = (tras * 1000000) / new_mclk; ++ if (new_time < valid_time) tras += 1; ++ ++ valid_time = (boot_config.rcd * 1000000) / boot_config.mclk; ++ new_time = (rcd * 1000000) / new_mclk; ++ if (new_time < valid_time) rcd += 1; ++ ++ valid_time = (boot_config.tpc * 1000000) / boot_config.mclk; ++ new_time = (tpc * 1000000) / new_mclk; ++ if (new_time < valid_time) tpc += 1; ++ ++ valid_time = (boot_config.trwl * 1000000) / boot_config.mclk; ++ new_time = (trwl * 1000000) / new_mclk; ++ if (new_time < valid_time) trwl += 1; ++ ++ valid_time = (boot_config.trc * 1000000) / boot_config.mclk; ++ new_time = (trc * 1000000) / new_mclk; ++ if (new_time < valid_time) trc += 2; ++ ++ tras = (tras < 4) ? 4: tras; ++ tras = (tras > 11) ? 11: tras; ++ tras -= 4; ++ ++ rcd = (rcd < 1) ? 1: rcd; ++ rcd = (rcd > 4) ? 4: rcd; ++ rcd -= 1; ++ ++ tpc = (tpc < 1) ? 1: tpc; ++ tpc = (tpc > 8) ? 8: tpc; ++ tpc -= 1; ++ ++ trwl = (trwl < 1) ? 1: trwl; ++ trwl = (trwl > 4) ? 4: trwl; ++ trwl -= 1; ++ ++ trc = (trc < 1) ? 1: trc; ++ trc = (trc > 15) ? 15: trc; ++ trc /= 2; ++ ++ dmcr = REG_EMC_DMCR; ++ ++ dmcr &= ~(EMC_DMCR_TRAS_MASK | EMC_DMCR_RCD_MASK | EMC_DMCR_TPC_MASK | EMC_DMCR_TRWL_MASK | EMC_DMCR_TRC_MASK); ++ dmcr |= ((tras << EMC_DMCR_TRAS_BIT) | (rcd << EMC_DMCR_RCD_BIT) | (tpc << EMC_DMCR_TPC_BIT) | (trwl << EMC_DMCR_TRWL_BIT) | (trc << EMC_DMCR_TRC_BIT)); ++ ++ REG_EMC_DMCR = dmcr; ++} ++ ++static void jz_update_dram_prev(unsigned int cur_mclk, unsigned int new_mclk) ++{ ++ /* No risk, no fun: run with interrupts on! */ ++ if (new_mclk > cur_mclk) { ++ /* We're going FASTER, so first update TRAS, RCD, TPC, TRWL ++ * and TRC of DMCR before changing the frequency. ++ */ ++ jz_update_dram_dmcr(new_mclk); ++ } else { ++ /* We're going SLOWER: first update RTCOR value ++ * before changing the frequency. ++ */ ++ jz_update_dram_rtcor(new_mclk); ++ } ++} ++ ++static void jz_update_dram_post(unsigned int cur_mclk, unsigned int new_mclk) ++{ ++ /* No risk, no fun: run with interrupts on! */ ++ if (new_mclk > cur_mclk) { ++ /* We're going FASTER, so update RTCOR ++ * after changing the frequency ++ */ ++ jz_update_dram_rtcor(new_mclk); ++ } else { ++ /* We're going SLOWER: so update TRAS, RCD, TPC, TRWL ++ * and TRC of DMCR after changing the frequency. ++ */ ++ jz_update_dram_dmcr(new_mclk); ++ } ++} ++ ++static void jz_scale_divisors(struct dpm_regs *regs) ++{ ++ unsigned int cpccr; ++ unsigned int cur_mclk, new_mclk; ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ unsigned int tmp = 0, wait = PLL_WAIT_500NS; ++ ++ cpccr = REG_CPM_CPCCR; ++ cpccr &= ~((unsigned long)regs->cpccr_mask); ++ cpccr |= regs->cpccr; ++ cpccr |= CPM_CPCCR_CE; /* update immediately */ ++ ++ cur_mclk = __cpm_get_mclk(); ++ new_mclk = __cpm_get_pllout() / div[(cpccr & CPM_CPCCR_MDIV_MASK) >> CPM_CPCCR_MDIV_BIT]; ++ ++ /* Update some DRAM parameters before changing frequency */ ++ jz_update_dram_prev(cur_mclk, new_mclk); ++ ++ /* update register to change the clocks. ++ * align this code to a cache line. ++ */ ++ __asm__ __volatile__( ++ ".set noreorder\n\t" ++ ".align 5\n" ++ "sw %1,0(%0)\n\t" ++ "li %3,0\n\t" ++ "1:\n\t" ++ "bne %3,%2,1b\n\t" ++ "addi %3, 1\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ ".set reorder\n\t" ++ : ++ : "r" (CPM_CPCCR), "r" (cpccr), "r" (wait), "r" (tmp)); ++ ++ /* Update some other DRAM parameters after changing frequency */ ++ jz_update_dram_post(cur_mclk, new_mclk); ++} ++ ++#ifdef CHANGE_PLL ++/* Maintain the LCD clock and pixel clock */ ++static void jz_scale_lcd_divisors(struct dpm_regs *regs) ++{ ++ unsigned int new_pll, new_lcd_div, new_lcdpix_div; ++ unsigned int cpccr; ++ unsigned int tmp = 0, wait = PLL_WAIT_500NS; ++ ++ if (!boot_config.lcd_clks_initialized) return; ++ ++ new_pll = __cpm_get_pllout(); ++ new_lcd_div = new_pll / boot_config.lcd_clk; ++ new_lcdpix_div = new_pll / boot_config.lcdpix_clk; ++ ++ if (new_lcd_div < 1) ++ new_lcd_div = 1; ++ if (new_lcd_div > 16) ++ new_lcd_div = 16; ++ ++ if (new_lcdpix_div < 1) ++ new_lcdpix_div = 1; ++ if (new_lcdpix_div > 512) ++ new_lcdpix_div = 512; ++ ++// REG_CPM_CPCCR2 = new_lcdpix_div - 1; ++ ++ cpccr = REG_CPM_CPCCR; ++ cpccr &= ~CPM_CPCCR_LDIV_MASK; ++ cpccr |= ((new_lcd_div - 1) << CPM_CPCCR_LDIV_BIT); ++ cpccr |= CPM_CPCCR_CE; /* update immediately */ ++ ++ /* update register to change the clocks. ++ * align this code to a cache line. ++ */ ++ __asm__ __volatile__( ++ ".set noreorder\n\t" ++ ".align 5\n" ++ "sw %1,0(%0)\n\t" ++ "li %3,0\n\t" ++ "1:\n\t" ++ "bne %3,%2,1b\n\t" ++ "addi %3, 1\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ ".set reorder\n\t" ++ : ++ : "r" (CPM_CPCCR), "r" (cpccr), "r" (wait), "r" (tmp)); ++} ++ ++static void jz_scale_pll(struct dpm_regs *regs) ++{ ++ unsigned int cppcr; ++ unsigned int cur_mclk, new_mclk, new_pll; ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ int od[] = {1, 2, 2, 4}; ++ ++ cppcr = REG_CPM_CPPCR; ++ cppcr &= ~(regs->cppcr_mask | CPM_CPPCR_PLLS | CPM_CPPCR_PLLEN | CPM_CPPCR_PLLST_MASK); ++ regs->cppcr &= ~CPM_CPPCR_PLLEN; ++ cppcr |= (regs->cppcr | 0xff); ++ ++ /* Update some DRAM parameters before changing frequency */ ++ new_pll = JZ_EXTAL * ((cppcr>>23)+2) / ((((cppcr>>18)&0x1f)+2) * od[(cppcr>>16)&0x03]); ++ cur_mclk = __cpm_get_mclk(); ++ new_mclk = new_pll / div[(REG_CPM_CPCCR>>CPM_CPCCR_MDIV_BIT) & 0xf]; ++ ++ /* ++ * Update some SDRAM parameters ++ */ ++ jz_update_dram_prev(cur_mclk, new_mclk); ++ ++ /* ++ * Update PLL, align code to cache line. ++ */ ++ cppcr |= CPM_CPPCR_PLLEN; ++ __asm__ __volatile__( ++ ".set noreorder\n\t" ++ ".align 5\n" ++ "sw %1,0(%0)\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ ".set reorder\n\t" ++ : ++ : "r" (CPM_CPPCR), "r" (cppcr)); ++ ++ /* Update some other DRAM parameters after changing frequency */ ++ jz_update_dram_post(cur_mclk, new_mclk); ++} ++#endif ++ ++static void jz4740_transition(struct dpm_regs *regs) ++{ ++ /* ++ * Get and save some boot-time conditions. ++ */ ++ jz_init_boot_config(); ++ ++#ifdef CHANGE_PLL ++ /* ++ * Disable LCD before scaling pll. ++ * LCD and LCD pixel clocks should not be changed even if the PLL ++ * output frequency has been changed. ++ */ ++ REG_LCD_CTRL &= ~LCD_CTRL_ENA; ++ ++ /* ++ * Stop module clocks before scaling PLL ++ */ ++ __cpm_stop_eth(); ++ __cpm_stop_aic(1); ++ __cpm_stop_aic(2); ++#endif ++ ++ /* ... add more as necessary */ ++ ++ if (regs->pll_up_flag == PLL_GOES_UP) { ++ /* the pll frequency is going up, so change dividors first */ ++ jz_scale_divisors(regs); ++#ifdef CHANGE_PLL ++ jz_scale_pll(regs); ++#endif ++ } ++ else if (regs->pll_up_flag == PLL_GOES_DOWN) { ++ /* the pll frequency is going down, so change pll first */ ++#ifdef CHANGE_PLL ++ jz_scale_pll(regs); ++#endif ++ jz_scale_divisors(regs); ++ } ++ else { ++ /* the pll frequency is unchanged, so change divisors only */ ++ jz_scale_divisors(regs); ++ } ++ ++#ifdef CHANGE_PLL ++ /* ++ * Restart module clocks before scaling PLL ++ */ ++ __cpm_start_eth(); ++ __cpm_start_aic(1); ++ __cpm_start_aic(2); ++ ++ /* ... add more as necessary */ ++ ++ /* Scale the LCD divisors after scaling pll */ ++ if (regs->pll_up_flag != PLL_UNCHANGED) { ++ jz_scale_lcd_divisors(regs); ++ } ++ ++ /* Enable LCD controller */ ++ REG_LCD_CTRL &= ~LCD_CTRL_DIS; ++ REG_LCD_CTRL |= LCD_CTRL_ENA; ++#endif ++ ++ /* Update system clocks */ ++ jz_update_clocks(); ++} ++ ++extern unsigned int idle_times; ++static unsigned int jz4740_freq_get(unsigned int cpu) ++{ ++ return (__cpm_get_cclk() / 1000); ++} ++ ++static unsigned int index_to_divisor(unsigned int index, struct dpm_regs *regs) ++{ ++ int n2FR[33] = { ++ 0, 0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 0, 6, 0, 0, 0, ++ 7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, ++ 9 ++ }; ++ int div[4] = {1, 2, 2, 2}; /* divisors of I:S:P:M */ ++ unsigned int div_of_cclk, new_freq, i; ++ ++ regs->pll_up_flag = PLL_UNCHANGED; ++ regs->cpccr_mask = CPM_CPCCR_CDIV_MASK | CPM_CPCCR_HDIV_MASK | CPM_CPCCR_PDIV_MASK | CPM_CPCCR_MDIV_MASK; ++ ++ new_freq = jz4740_freq_table.table[index].frequency; ++ ++ do { ++ div_of_cclk = __cpm_get_pllout() / (1000 * new_freq); ++ } while (div_of_cclk==0); ++ ++ if(div_of_cclk == 1 || div_of_cclk == 2 || div_of_cclk == 4) { ++ for(i = 1; i<4; i++) { ++ div[i] = 3; ++ } ++ } else { ++ for(i = 1; i<4; i++) { ++ div[i] = 2; ++ } ++ } ++ ++ for(i = 0; i<4; i++) { ++ div[i] *= div_of_cclk; ++ } ++ ++ dprintk("divisors of I:S:P:M = %d:%d:%d:%d\n", div[0], div[1], div[2], div[3]); ++ ++ regs->cpccr = ++ (n2FR[div[0]] << CPM_CPCCR_CDIV_BIT) | ++ (n2FR[div[1]] << CPM_CPCCR_HDIV_BIT) | ++ (n2FR[div[2]] << CPM_CPCCR_PDIV_BIT) | ++ (n2FR[div[3]] << CPM_CPCCR_MDIV_BIT); ++ ++ return div_of_cclk; ++} ++ ++static void jz4740_set_cpu_divider_index(unsigned int cpu, unsigned int index) ++{ ++ unsigned long divisor, old_divisor; ++ struct cpufreq_freqs freqs; ++ struct dpm_regs regs; ++ ++ old_divisor = __cpm_get_pllout() / __cpm_get_cclk(); ++ divisor = index_to_divisor(index, ®s); ++ ++ freqs.old = __cpm_get_cclk() / 1000; ++ freqs.new = __cpm_get_pllout() / (1000 * divisor); ++ freqs.cpu = cpu; ++ ++ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); ++ ++ if (old_divisor != divisor) ++ jz4740_transition(®s); ++ ++ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); ++} ++ ++static int jz4740_freq_target(struct cpufreq_policy *policy, ++ unsigned int target_freq, ++ unsigned int relation) ++{ ++ unsigned int new_index = 0; ++ ++ if (cpufreq_frequency_table_target(policy, ++ &jz4740_freq_table.table[0], ++ target_freq, relation, &new_index)) ++ return -EINVAL; ++ ++ jz4740_set_cpu_divider_index(policy->cpu, new_index); ++ ++ dprintk("new frequency is %d KHz (REG_CPM_CPCCR:0x%x)\n", __cpm_get_cclk() / 1000, REG_CPM_CPCCR); ++ ++ return 0; ++} ++ ++static int jz4740_freq_verify(struct cpufreq_policy *policy) ++{ ++ return cpufreq_frequency_table_verify(policy, ++ &jz4740_freq_table.table[0]); ++} ++ ++static int __init jz4740_cpufreq_driver_init(struct cpufreq_policy *policy) ++{ ++ ++ struct cpufreq_frequency_table *table = &jz4740_freq_table.table[0]; ++ unsigned int MAX_FREQ; ++ ++ dprintk(KERN_INFO "Jz4740 cpufreq driver\n"); ++ ++ if (policy->cpu != 0) ++ return -EINVAL; ++ ++ policy->cur = MAX_FREQ = __cpm_get_cclk() / 1000; /* in kHz. Current and max frequency is determined by u-boot */ ++ policy->governor = CPUFREQ_DEFAULT_GOVERNOR; ++ ++ policy->cpuinfo.min_freq = MAX_FREQ/8; ++ policy->cpuinfo.max_freq = MAX_FREQ; ++ policy->cpuinfo.transition_latency = 100000; /* in 10^(-9) s = nanoseconds */ ++ ++ table[0].index = 0; ++ table[0].frequency = MAX_FREQ/8; ++ table[1].index = 1; ++ table[1].frequency = MAX_FREQ/6; ++ table[2].index = 2; ++ table[2].frequency = MAX_FREQ/4; ++ table[3].index = 3; ++ table[3].frequency = MAX_FREQ/3; ++ table[4].index = 4; ++ table[4].frequency = MAX_FREQ/2; ++ table[5].index = 5; ++ table[5].frequency = MAX_FREQ; ++ table[6].index = 6; ++ table[6].frequency = CPUFREQ_TABLE_END; ++ ++#ifdef CONFIG_CPU_FREQ_STAT_DETAILS ++ cpufreq_frequency_table_get_attr(table, policy->cpu); /* for showing /sys/devices/system/cpu/cpuX/cpufreq/stats/ */ ++#endif ++ ++ return cpufreq_frequency_table_cpuinfo(policy, table); ++} ++ ++static struct cpufreq_driver cpufreq_jz4740_driver = { ++// .flags = CPUFREQ_STICKY, ++ .init = jz4740_cpufreq_driver_init, ++ .verify = jz4740_freq_verify, ++ .target = jz4740_freq_target, ++ .get = jz4740_freq_get, ++ .name = "jz4740", ++}; ++ ++static int __init jz4740_cpufreq_init(void) ++{ ++ return cpufreq_register_driver(&cpufreq_jz4740_driver); ++} ++ ++static void __exit jz4740_cpufreq_exit(void) ++{ ++ cpufreq_unregister_driver(&cpufreq_jz4740_driver); ++} ++ ++module_init(jz4740_cpufreq_init); ++module_exit(jz4740_cpufreq_exit); ++ ++MODULE_AUTHOR("Regen "); ++MODULE_DESCRIPTION("cpufreq driver for Jz4740"); ++MODULE_LICENSE("GPL"); ++ +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/dma.c linux-2.6.24.7/arch/mips/jz4740/dma.c +--- linux-2.6.24.7.old/arch/mips/jz4740/dma.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/dma.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,768 @@ ++/* ++ * linux/arch/mips/jz4740/dma.c ++ * ++ * Support functions for the JZ4740 internal DMA channels. ++ * No-descriptor transfer only. ++ * Descriptor transfer should also call jz_request_dma() to get a free ++ * channel and call jz_free_dma() to free the channel. And driver should ++ * build the DMA descriptor and setup the DMA channel by itself. ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* ++ * A note on resource allocation: ++ * ++ * All drivers needing DMA channels, should allocate and release them ++ * through the public routines `jz_request_dma()' and `jz_free_dma()'. ++ * ++ * In order to avoid problems, all processes should allocate resources in ++ * the same sequence and release them in the reverse order. ++ * ++ * So, when allocating DMAs and IRQs, first allocate the DMA, then the IRQ. ++ * When releasing them, first release the IRQ, then release the DMA. The ++ * main reason for this order is that, if you are requesting the DMA buffer ++ * done interrupt, you won't know the irq number until the DMA channel is ++ * returned from jz_request_dma(). ++ */ ++ ++struct jz_dma_chan jz_dma_table[MAX_DMA_NUM] = { ++ {dev_id:-1,}, ++ {dev_id:-1,}, ++ {dev_id:-1,}, ++ {dev_id:-1,}, ++ {dev_id:-1,}, ++ {dev_id:-1,}, ++}; ++ ++// Device FIFO addresses and default DMA modes ++static const struct { ++ unsigned int fifo_addr; ++ unsigned int dma_mode; ++ unsigned int dma_source; ++} dma_dev_table[DMA_ID_MAX] = { ++ {CPHYSADDR(UART0_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART0OUT}, ++ {CPHYSADDR(UART0_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART0IN}, ++ {CPHYSADDR(SSI_DR), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_SSIOUT}, ++ {CPHYSADDR(SSI_DR), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SSIIN}, ++ {CPHYSADDR(AIC_DR), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_AICOUT}, ++ {CPHYSADDR(AIC_DR), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_AICIN}, ++ {CPHYSADDR(MSC_TXFIFO), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_MSCOUT}, ++ {CPHYSADDR(MSC_RXFIFO), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_MSCIN}, ++ {0, DMA_AUTOINIT, DMAC_DRSR_RS_TCU}, ++ {0, DMA_AUTOINIT, DMAC_DRSR_RS_AUTO}, ++ {}, ++}; ++ ++ ++int jz_dma_read_proc(char *buf, char **start, off_t fpos, ++ int length, int *eof, void *data) ++{ ++ int i, len = 0; ++ struct jz_dma_chan *chan; ++ ++ for (i = 0; i < MAX_DMA_NUM; i++) { ++ if ((chan = get_dma_chan(i)) != NULL) { ++ len += sprintf(buf + len, "%2d: %s\n", ++ i, chan->dev_str); ++ } ++ } ++ ++ if (fpos >= len) { ++ *start = buf; ++ *eof = 1; ++ return 0; ++ } ++ *start = buf + fpos; ++ if ((len -= fpos) > length) ++ return length; ++ *eof = 1; ++ return len; ++} ++ ++ ++void dump_jz_dma_channel(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan; ++ ++ if (dmanr > MAX_DMA_NUM) ++ return; ++ chan = &jz_dma_table[dmanr]; ++ ++ printk("DMA%d Registers:\n", dmanr); ++ printk(" DMACR = 0x%08x\n", REG_DMAC_DMACR); ++ printk(" DSAR = 0x%08x\n", REG_DMAC_DSAR(dmanr)); ++ printk(" DTAR = 0x%08x\n", REG_DMAC_DTAR(dmanr)); ++ printk(" DTCR = 0x%08x\n", REG_DMAC_DTCR(dmanr)); ++ printk(" DRSR = 0x%08x\n", REG_DMAC_DRSR(dmanr)); ++ printk(" DCCSR = 0x%08x\n", REG_DMAC_DCCSR(dmanr)); ++ printk(" DCMD = 0x%08x\n", REG_DMAC_DCMD(dmanr)); ++ printk(" DDA = 0x%08x\n", REG_DMAC_DDA(dmanr)); ++ printk(" DMADBR = 0x%08x\n", REG_DMAC_DMADBR); ++} ++ ++ ++/** ++ * jz_request_dma - dynamically allcate an idle DMA channel to return ++ * @dev_id: the specified dma device id or DMA_ID_RAW_SET ++ * @dev_str: the specified dma device string name ++ * @irqhandler: the irq handler, or NULL ++ * @irqflags: the irq handler flags ++ * @irq_dev_id: the irq handler device id for shared irq ++ * ++ * Finds a free channel, and binds the requested device to it. ++ * Returns the allocated channel number, or negative on error. ++ * Requests the DMA done IRQ if irqhandler != NULL. ++ * ++*/ ++/*int jz_request_dma(int dev_id, const char *dev_str, ++ void (*irqhandler)(int, void *, struct pt_regs *), ++ unsigned long irqflags, ++ void *irq_dev_id) ++*/ ++ ++int jz_request_dma(int dev_id, const char *dev_str, ++ irqreturn_t (*irqhandler)(int, void *), ++ unsigned long irqflags, ++ void *irq_dev_id) ++{ ++ struct jz_dma_chan *chan; ++ int i, ret; ++ ++ if (dev_id < 0 || dev_id >= DMA_ID_MAX) ++ return -EINVAL; ++ ++ for (i = 0; i < MAX_DMA_NUM; i++) { ++ if (jz_dma_table[i].dev_id < 0) ++ break; ++ } ++ if (i == MAX_DMA_NUM) /* no free channel */ ++ return -ENODEV; ++ ++ /* we got a free channel */ ++ chan = &jz_dma_table[i]; ++ ++ if (irqhandler) { ++ chan->irq = IRQ_DMA_0 + i; // allocate irq number ++ chan->irq_dev = irq_dev_id; ++ if ((ret = request_irq(chan->irq, irqhandler, irqflags, ++ dev_str, chan->irq_dev))) { ++ chan->irq = -1; ++ chan->irq_dev = NULL; ++ return ret; ++ } ++ } else { ++ chan->irq = -1; ++ chan->irq_dev = NULL; ++ } ++ ++ // fill it in ++ chan->io = i; ++ chan->dev_id = dev_id; ++ chan->dev_str = dev_str; ++ chan->fifo_addr = dma_dev_table[dev_id].fifo_addr; ++ chan->mode = dma_dev_table[dev_id].dma_mode; ++ chan->source = dma_dev_table[dev_id].dma_source; ++ ++ return i; ++} ++ ++void jz_free_dma(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) { ++ printk("Trying to free DMA%d\n", dmanr); ++ return; ++ } ++ ++ disable_dma(dmanr); ++ if (chan->irq) ++ free_irq(chan->irq, chan->irq_dev); ++ ++ chan->irq = -1; ++ chan->irq_dev = NULL; ++ chan->dev_id = -1; ++} ++ ++void jz_set_dma_dest_width(int dmanr, int nbit) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ chan->mode &= ~DMAC_DCMD_DWDH_MASK; ++ switch (nbit) { ++ case 8: ++ chan->mode |= DMAC_DCMD_DWDH_8; ++ break; ++ case 16: ++ chan->mode |= DMAC_DCMD_DWDH_16; ++ break; ++ case 32: ++ chan->mode |= DMAC_DCMD_DWDH_32; ++ break; ++ } ++} ++ ++void jz_set_dma_src_width(int dmanr, int nbit) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ chan->mode &= ~DMAC_DCMD_SWDH_MASK; ++ switch (nbit) { ++ case 8: ++ chan->mode |= DMAC_DCMD_SWDH_8; ++ break; ++ case 16: ++ chan->mode |= DMAC_DCMD_SWDH_16; ++ break; ++ case 32: ++ chan->mode |= DMAC_DCMD_SWDH_32; ++ break; ++ } ++} ++ ++void jz_set_dma_block_size(int dmanr, int nbyte) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ chan->mode &= ~DMAC_DCMD_DS_MASK; ++ switch (nbyte) { ++ case 1: ++ chan->mode |= DMAC_DCMD_DS_8BIT; ++ break; ++ case 2: ++ chan->mode |= DMAC_DCMD_DS_16BIT; ++ break; ++ case 4: ++ chan->mode |= DMAC_DCMD_DS_32BIT; ++ break; ++ case 16: ++ chan->mode |= DMAC_DCMD_DS_16BYTE; ++ break; ++ case 32: ++ chan->mode |= DMAC_DCMD_DS_32BYTE; ++ break; ++ } ++} ++ ++unsigned int jz_get_dma_command(int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ return chan->mode; ++} ++ ++/** ++ * jz_set_dma_mode - do the raw settings for the specified DMA channel ++ * @dmanr: the specified DMA channel ++ * @mode: dma operate mode, DMA_MODE_READ or DMA_MODE_WRITE ++ * @dma_mode: dma raw mode ++ * @dma_source: dma raw request source ++ * @fifo_addr: dma raw device fifo address ++ * ++ * Ensure call jz_request_dma(DMA_ID_RAW_SET, ...) first, then call ++ * jz_set_dma_mode() rather than set_dma_mode() if you work with ++ * and external request dma device. ++ * ++ * NOTE: Don not dynamically allocate dma channel if one external request ++ * dma device will occupy this channel. ++*/ ++int jz_set_dma_mode(unsigned int dmanr, unsigned int mode, ++ unsigned int dma_mode, unsigned int dma_source, ++ unsigned int fifo_addr) ++{ ++ int dev_id, i; ++ struct jz_dma_chan *chan; ++ ++ if (dmanr > MAX_DMA_NUM) ++ return -ENODEV; ++ for (i = 0; i < MAX_DMA_NUM; i++) { ++ if (jz_dma_table[i].dev_id < 0) ++ break; ++ } ++ if (i == MAX_DMA_NUM) ++ return -ENODEV; ++ ++ chan = &jz_dma_table[dmanr]; ++ dev_id = chan->dev_id; ++ if (dev_id > 0) { ++ printk(KERN_DEBUG "%s sets the allocated DMA channel %d!\n", ++ __FUNCTION__, dmanr); ++ return -ENODEV; ++ } ++ ++ /* clone it from the dynamically allocated. */ ++ if (i != dmanr) { ++ chan->irq = jz_dma_table[i].irq; ++ chan->irq_dev = jz_dma_table[i].irq_dev; ++ chan->dev_str = jz_dma_table[i].dev_str; ++ jz_dma_table[i].irq = 0; ++ jz_dma_table[i].irq_dev = NULL; ++ jz_dma_table[i].dev_id = -1; ++ } ++ chan->dev_id = DMA_ID_RAW_SET; ++ chan->io = dmanr; ++ chan->fifo_addr = fifo_addr; ++ chan->mode = dma_mode; ++ chan->source = dma_source; ++ ++ set_dma_mode(dmanr, dma_mode); ++ ++ return dmanr; ++} ++ ++void enable_dma(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ REG_DMAC_DCCSR(dmanr) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); ++ REG_DMAC_DCCSR(dmanr) |= DMAC_DCCSR_NDES; /* No-descriptor transfer */ ++ __dmac_enable_channel(dmanr); ++ if (chan->irq) ++ __dmac_channel_enable_irq(dmanr); ++} ++ ++#define DMA_DISABLE_POLL 0x10000 ++ ++void disable_dma(unsigned int dmanr) ++{ ++ int i; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ if (!__dmac_channel_enabled(dmanr)) ++ return; ++ ++ for (i = 0; i < DMA_DISABLE_POLL; i++) ++ if (__dmac_channel_transmit_end_detected(dmanr)) ++ break; ++#if 0 ++ if (i == DMA_DISABLE_POLL) ++ printk(KERN_INFO "disable_dma: poll expired!\n"); ++#endif ++ ++ __dmac_disable_channel(dmanr); ++ if (chan->irq) ++ __dmac_channel_disable_irq(dmanr); ++} ++ ++/* Note: DMA_MODE_MASK is simulated by sw */ ++void set_dma_mode(unsigned int dmanr, unsigned int mode) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ if (mode == DMA_MODE_READ) { ++ chan->mode |= DMAC_DCMD_DAI; ++ chan->mode &= ~DMAC_DCMD_SAI; ++ } else if (mode == DMA_MODE_WRITE) { ++ chan->mode |= DMAC_DCMD_SAI; ++ chan->mode &= ~DMAC_DCMD_DAI; ++ } else { ++ printk(KERN_DEBUG "set_dma_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); ++ } ++ REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK; ++ REG_DMAC_DRSR(chan->io) = chan->source; ++} ++ ++void set_dma_addr(unsigned int dmanr, unsigned int phyaddr) ++{ ++ unsigned int mode; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ mode = chan->mode & DMA_MODE_MASK; ++ if (mode == DMA_MODE_READ) { ++ REG_DMAC_DSAR(chan->io) = chan->fifo_addr; ++ REG_DMAC_DTAR(chan->io) = phyaddr; ++ } else if (mode == DMA_MODE_WRITE) { ++ REG_DMAC_DSAR(chan->io) = phyaddr; ++ REG_DMAC_DTAR(chan->io) = chan->fifo_addr; ++ } else ++ printk(KERN_DEBUG "Driver should call set_dma_mode() ahead set_dma_addr()!\n"); ++} ++ ++void set_dma_count(unsigned int dmanr, unsigned int bytecnt) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ int dma_ds[] = {4, 1, 2, 16, 32}; ++ unsigned int ds; ++ ++ if (!chan) ++ return; ++ ++ ds = (chan->mode & DMAC_DCMD_DS_MASK) >> DMAC_DCMD_DS_BIT; ++ REG_DMAC_DTCR(chan->io) = bytecnt / dma_ds[ds]; // transfer count ++} ++ ++unsigned int get_dma_residue(unsigned int dmanr) ++{ ++ unsigned int count, ds; ++ int dma_ds[] = {4, 1, 2, 16, 32}; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 0; ++ ++ ds = (chan->mode & DMAC_DCMD_DS_MASK) >> DMAC_DCMD_DS_BIT; ++ count = REG_DMAC_DTCR(chan->io); ++ count = count * dma_ds[ds]; ++ ++ return count; ++} ++ ++void jz_set_oss_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ switch (audio_fmt) { ++ case AFMT_U8: ++ /* burst mode : 32BIT */ ++ break; ++ case AFMT_S16_LE: ++ /* burst mode : 16BYTE */ ++ if (mode == DMA_MODE_READ) { ++ chan->mode = DMA_AIC_32_16BYTE_RX_CMD | DMA_MODE_READ; ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCMD_DAI; ++ chan->mode &= ~DMAC_DCMD_SAI; ++ } else if (mode == DMA_MODE_WRITE) { ++ chan->mode = DMA_AIC_32_16BYTE_TX_CMD | DMA_MODE_WRITE; ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCMD_SAI; ++ chan->mode &= ~DMAC_DCMD_DAI; ++ } else ++ printk("oss_dma_burst_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); ++ ++ REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK; ++ REG_DMAC_DRSR(chan->io) = chan->source; ++ break; ++ } ++} ++ ++void jz_set_alsa_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ switch (audio_fmt) { ++ case 8: ++ /* SNDRV_PCM_FORMAT_S8 burst mode : 32BIT */ ++ break; ++ case 16: ++ /* SNDRV_PCM_FORMAT_S16_LE burst mode : 16BYTE */ ++ if (mode == DMA_MODE_READ) { ++ chan->mode = DMA_AIC_16BYTE_RX_CMD | DMA_MODE_READ; ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCMD_DAI; ++ chan->mode &= ~DMAC_DCMD_SAI; ++ } else if (mode == DMA_MODE_WRITE) { ++ chan->mode = DMA_AIC_16BYTE_TX_CMD | DMA_MODE_WRITE; ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCMD_SAI; ++ chan->mode &= ~DMAC_DCMD_DAI; ++ } else ++ printk("alsa_dma_burst_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); ++ ++ REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK; ++ REG_DMAC_DRSR(chan->io) = chan->source; ++ break; ++ } ++} ++ ++#undef JZ4740_DMAC_TEST_ENABLE ++ ++#ifdef JZ4740_DMAC_TEST_ENABLE ++ ++/* ++ * DMA test: external address <--> external address ++ */ ++#define TEST_DMA_SIZE 16*1024 ++ ++static jz_dma_desc *dma_desc; ++ ++static int dma_chan; ++static dma_addr_t dma_desc_phys_addr; ++static unsigned int dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr; ++ ++static int dma_check_result(void *src, void *dst, int size) ++{ ++ unsigned int addr1, addr2, i, err = 0; ++ ++ addr1 = (unsigned int)src; ++ addr2 = (unsigned int)dst; ++ ++ for (i = 0; i < size; i += 4) { ++ if (*(volatile unsigned int *)addr1 != *(volatile unsigned int *)addr2) { ++ err++; ++ printk("wrong data at 0x%08x: src 0x%08x dst 0x%08x\n", addr2, *(volatile unsigned int *)addr1, *(volatile unsigned int *)addr2); ++ } ++ addr1 += 4; ++ addr2 += 4; ++ } ++ printk("check DMA result err=%d\n", err); ++ return err; ++} ++ ++static void jz4740_dma_irq(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ printk("jz4740_dma_irq %d\n", irq); ++ ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ ++ if (__dmac_channel_transmit_halt_detected(dma_chan)) { ++ printk("DMA HALT\n"); ++ __dmac_channel_clear_transmit_halt(dma_chan); ++ } ++ ++ if (__dmac_channel_address_error_detected(dma_chan)) { ++ printk("DMA ADDR ERROR\n"); ++ __dmac_channel_clear_address_error(dma_chan); ++ } ++ ++ if (__dmac_channel_descriptor_invalid_detected(dma_chan)) { ++ printk("DMA DESC INVALID\n"); ++ __dmac_channel_clear_descriptor_invalid(dma_chan); ++ } ++ ++ if (__dmac_channel_count_terminated_detected(dma_chan)) { ++ printk("DMA CT\n"); ++ __dmac_channel_clear_count_terminated(dma_chan); ++ } ++ ++ if (__dmac_channel_transmit_end_detected(dma_chan)) { ++ printk("DMA TT\n"); ++ __dmac_channel_clear_transmit_end(dma_chan); ++ dump_jz_dma_channel(dma_chan); ++ dma_check_result((void *)dma_src_addr, (void *)dma_dst_addr, TEST_DMA_SIZE); ++ } ++ ++ /* free buffers */ ++ printk("free DMA buffers\n"); ++ free_pages(dma_src_addr, 2); ++ free_pages(dma_dst_addr, 2); ++ ++ if (dma_desc) ++ free_pages((unsigned int)dma_desc, 0); ++ ++ /* free dma */ ++ jz_free_dma(dma_chan); ++} ++ ++void dma_nodesc_test(void) ++{ ++ unsigned int addr, i; ++ ++ printk("dma_nodesc_test\n"); ++ ++ /* Request DMA channel and setup irq handler */ ++ dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", jz4740_dma_irq, ++ SA_INTERRUPT, NULL); ++ if (dma_chan < 0) { ++ printk("Setup irq failed\n"); ++ return; ++ } ++ ++ printk("Requested DMA channel = %d\n", dma_chan); ++ ++ /* Allocate DMA buffers */ ++ dma_src_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ ++ dma_dst_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ ++ ++ dma_src_phys_addr = CPHYSADDR(dma_src_addr); ++ dma_dst_phys_addr = CPHYSADDR(dma_dst_addr); ++ ++ printk("Buffer addresses: 0x%08x 0x%08x 0x%08x 0x%08x\n", ++ dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr); ++ ++ /* Prepare data for source buffer */ ++ addr = (unsigned int)dma_src_addr; ++ for (i = 0; i < TEST_DMA_SIZE; i += 4) { ++ *(volatile unsigned int *)addr = addr; ++ addr += 4; ++ } ++ dma_cache_wback((unsigned long)dma_src_addr, TEST_DMA_SIZE); ++ ++ /* Init target buffer */ ++ memset((void *)dma_dst_addr, 0, TEST_DMA_SIZE); ++ dma_cache_wback((unsigned long)dma_dst_addr, TEST_DMA_SIZE); ++ ++ /* Init DMA module */ ++ printk("Starting DMA\n"); ++ REG_DMAC_DMACR = 0; ++ REG_DMAC_DCCSR(dma_chan) = 0; ++ REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO; ++ REG_DMAC_DSAR(dma_chan) = dma_src_phys_addr; ++ REG_DMAC_DTAR(dma_chan) = dma_dst_phys_addr; ++ REG_DMAC_DTCR(dma_chan) = 512; ++ REG_DMAC_DCMD(dma_chan) = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_TIE; ++ REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; ++ REG_DMAC_DMACR = DMAC_DMACR_DMAE; /* global DMA enable bit */ ++ ++ printk("DMA started. IMR=%08x\n", REG_INTC_IMR); ++} ++ ++void dma_desc_test(void) ++{ ++ unsigned int next, addr, i; ++ static jz_dma_desc *desc; ++ ++ printk("dma_desc_test\n"); ++ ++ /* Request DMA channel and setup irq handler */ ++ dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", jz4740_dma_irq, ++ SA_INTERRUPT, NULL); ++ if (dma_chan < 0) { ++ printk("Setup irq failed\n"); ++ return; ++ } ++ ++ printk("Requested DMA channel = %d\n", dma_chan); ++ ++ /* Allocate DMA buffers */ ++ dma_src_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ ++ dma_dst_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ ++ ++ dma_src_phys_addr = CPHYSADDR(dma_src_addr); ++ dma_dst_phys_addr = CPHYSADDR(dma_dst_addr); ++ ++ printk("Buffer addresses: 0x%08x 0x%08x 0x%08x 0x%08x\n", ++ dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr); ++ ++ /* Prepare data for source buffer */ ++ addr = (unsigned int)dma_src_addr; ++ for (i = 0; i < TEST_DMA_SIZE; i += 4) { ++ *(volatile unsigned int *)addr = addr; ++ addr += 4; ++ } ++ dma_cache_wback((unsigned long)dma_src_addr, TEST_DMA_SIZE); ++ ++ /* Init target buffer */ ++ memset((void *)dma_dst_addr, 0, TEST_DMA_SIZE); ++ dma_cache_wback((unsigned long)dma_dst_addr, TEST_DMA_SIZE); ++ ++ /* Allocate DMA descriptors */ ++ dma_desc = (jz_dma_desc *)__get_free_pages(GFP_KERNEL, 0); ++ dma_desc_phys_addr = CPHYSADDR((unsigned long)dma_desc); ++ ++ printk("DMA descriptor address: 0x%08x 0x%08x\n", (u32)dma_desc, dma_desc_phys_addr); ++ ++ /* Setup DMA descriptors */ ++ desc = dma_desc; ++ next = (dma_desc_phys_addr + (sizeof(jz_dma_desc))) >> 4; ++ ++ desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_TM | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK; ++ desc->dsadr = dma_src_phys_addr; /* DMA source address */ ++ desc->dtadr = dma_dst_phys_addr; /* DMA target address */ ++ desc->ddadr = (next << 24) + 128; /* size: 128*32 bytes = 4096 bytes */ ++ ++ desc++; ++ next = (dma_desc_phys_addr + 2*(sizeof(jz_dma_desc))) >> 4; ++ ++ desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK; ++ desc->dsadr = dma_src_phys_addr + 4096; /* DMA source address */ ++ desc->dtadr = dma_dst_phys_addr + 4096; /* DMA target address */ ++ desc->ddadr = (next << 24) + 256; /* size: 256*16 bytes = 4096 bytes */ ++ ++ desc++; ++ next = (dma_desc_phys_addr + 3*(sizeof(jz_dma_desc))) >> 4; ++ ++ desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK; ++ desc->dsadr = dma_src_phys_addr + 8192; /* DMA source address */ ++ desc->dtadr = dma_dst_phys_addr + 8192; /* DMA target address */ ++ desc->ddadr = (next << 24) + 256; /* size: 256*16 bytes = 4096 bytes */ ++ ++ desc++; ++ next = (dma_desc_phys_addr + 4*(sizeof(jz_dma_desc))) >> 4; ++ ++ desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE; ++ desc->dsadr = dma_src_phys_addr + 12*1024; /* DMA source address */ ++ desc->dtadr = dma_dst_phys_addr + 12*1024; /* DMA target address */ ++ desc->ddadr = (next << 24) + 1024; /* size: 1024*4 bytes = 4096 bytes */ ++ ++ dma_cache_wback((unsigned long)dma_desc, 4*(sizeof(jz_dma_desc))); ++ ++ /* Setup DMA descriptor address */ ++ REG_DMAC_DDA(dma_chan) = dma_desc_phys_addr; ++ ++ /* Setup request source */ ++ REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO; ++ ++ /* Setup DMA channel control/status register */ ++ REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_EN; /* descriptor transfer, clear status, start channel */ ++ ++ /* Enable DMA */ ++ REG_DMAC_DMACR = DMAC_DMACR_DMAE; ++ ++ /* DMA doorbell set -- start DMA now ... */ ++ REG_DMAC_DMADBSR = 1 << dma_chan; ++ ++ printk("DMA started. IMR=%08x\n", REG_INTC_IMR); ++} ++ ++#endif ++ ++//EXPORT_SYMBOL_NOVERS(jz_dma_table); ++EXPORT_SYMBOL(jz_dma_table); ++EXPORT_SYMBOL(jz_request_dma); ++EXPORT_SYMBOL(jz_free_dma); ++EXPORT_SYMBOL(jz_set_dma_src_width); ++EXPORT_SYMBOL(jz_set_dma_dest_width); ++EXPORT_SYMBOL(jz_set_dma_block_size); ++EXPORT_SYMBOL(jz_set_dma_mode); ++EXPORT_SYMBOL(set_dma_mode); ++EXPORT_SYMBOL(jz_set_oss_dma); ++EXPORT_SYMBOL(jz_set_alsa_dma); ++EXPORT_SYMBOL(set_dma_addr); ++EXPORT_SYMBOL(set_dma_count); ++EXPORT_SYMBOL(get_dma_residue); ++EXPORT_SYMBOL(enable_dma); ++EXPORT_SYMBOL(disable_dma); ++EXPORT_SYMBOL(dump_jz_dma_channel); +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/i2c.c linux-2.6.24.7/arch/mips/jz4740/i2c.c +--- linux-2.6.24.7.old/arch/mips/jz4740/i2c.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/i2c.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,273 @@ ++/* ++ * linux/arch/mips/jz4740/i2c.c ++ * ++ * Jz4740 I2C routines. ++ * ++ * Copyright (C) 2005,2006 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* I2C protocol */ ++#define I2C_READ 1 ++#define I2C_WRITE 0 ++ ++#define TIMEOUT 1000 ++ ++/* ++ * I2C bus protocol basic routines ++ */ ++static int i2c_put_data(unsigned char data) ++{ ++ unsigned int timeout = TIMEOUT*10; ++ ++ __i2c_write(data); ++ __i2c_set_drf(); ++ while (__i2c_check_drf() != 0); ++ while (!__i2c_transmit_ended()); ++ while (!__i2c_received_ack() && timeout) ++ timeout--; ++ ++ if (timeout) ++ return 0; ++ else ++ return -ETIMEDOUT; ++} ++ ++#ifdef CONFIG_JZ_TPANEL_ATA2508 ++static int i2c_put_data_nack(unsigned char data) ++{ ++ unsigned int timeout = TIMEOUT*10; ++ ++ __i2c_write(data); ++ __i2c_set_drf(); ++ while (__i2c_check_drf() != 0); ++ while (!__i2c_transmit_ended()); ++ while (timeout--); ++ return 0; ++} ++#endif ++ ++static int i2c_get_data(unsigned char *data, int ack) ++{ ++ int timeout = TIMEOUT*10; ++ ++ if (!ack) ++ __i2c_send_nack(); ++ else ++ __i2c_send_ack(); ++ ++ while (__i2c_check_drf() == 0 && timeout) ++ timeout--; ++ ++ if (timeout) { ++ if (!ack) ++ __i2c_send_stop(); ++ *data = __i2c_read(); ++ __i2c_clear_drf(); ++ return 0; ++ } else ++ return -ETIMEDOUT; ++} ++ ++/* ++ * I2C interface ++ */ ++void i2c_open(void) ++{ ++ __i2c_set_clk(jz_clocks.extalclk, 10000); /* default 10 KHz */ ++ __i2c_enable(); ++} ++ ++void i2c_close(void) ++{ ++ udelay(300); /* wait for STOP goes over. */ ++ __i2c_disable(); ++} ++ ++void i2c_setclk(unsigned int i2cclk) ++{ ++ __i2c_set_clk(jz_clocks.extalclk, i2cclk); ++} ++ ++int i2c_lseek(unsigned char device, unsigned char offset) ++{ ++ __i2c_send_nack(); /* Master does not send ACK, slave sends it */ ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) ++ goto device_err; ++ if (i2c_put_data(offset) < 0) ++ goto address_err; ++ return 0; ++ device_err: ++ printk(KERN_DEBUG "No I2C device (0x%02x) installed.\n", device); ++ __i2c_send_stop(); ++ return -ENODEV; ++ address_err: ++ printk(KERN_DEBUG "No I2C device (0x%02x) response.\n", device); ++ __i2c_send_stop(); ++ return -EREMOTEIO; ++} ++ ++int i2c_read(unsigned char device, unsigned char *buf, ++ unsigned char address, int count) ++{ ++ int cnt = count; ++ int timeout = 5; ++ ++L_try_again: ++ ++ if (timeout < 0) ++ goto L_timeout; ++ ++ __i2c_send_nack(); /* Master does not send ACK, slave sends it */ ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) ++ goto device_werr; ++ if (i2c_put_data(address) < 0) ++ goto address_err; ++ ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_READ ) < 0) ++ goto device_rerr; ++ __i2c_send_ack(); /* Master sends ACK for continue reading */ ++ while (cnt) { ++ if (cnt == 1) { ++ if (i2c_get_data(buf, 0) < 0) ++ break; ++ } else { ++ if (i2c_get_data(buf, 1) < 0) ++ break; ++ } ++ cnt--; ++ buf++; ++ } ++ ++ __i2c_send_stop(); ++ return count - cnt; ++ device_rerr: ++ device_werr: ++ address_err: ++ timeout --; ++ __i2c_send_stop(); ++ goto L_try_again; ++ ++L_timeout: ++ __i2c_send_stop(); ++ printk("Read I2C device 0x%2x failed.\n", device); ++ return -ENODEV; ++} ++ ++int i2c_write(unsigned char device, unsigned char *buf, ++ unsigned char address, int count) ++{ ++ int cnt = count; ++ int cnt_in_pg; ++ int timeout = 5; ++ unsigned char *tmpbuf; ++ unsigned char tmpaddr; ++ ++ __i2c_send_nack(); /* Master does not send ACK, slave sends it */ ++ ++ W_try_again: ++ if (timeout < 0) ++ goto W_timeout; ++ ++ cnt = count; ++ tmpbuf = (unsigned char *)buf; ++ tmpaddr = address; ++ ++ start_write_page: ++ cnt_in_pg = 0; ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) ++ goto device_err; ++#ifdef CONFIG_JZ_TPANEL_ATA2508 ++ if (address == 0xff) { ++ if (i2c_put_data_nack(tmpaddr) < 0) ++ goto address_err; ++ while (cnt) { ++ if (++cnt_in_pg > 8) { ++ __i2c_send_stop(); ++ mdelay(1); ++ tmpaddr += 8; ++ goto start_write_page; ++ } ++ if (i2c_put_data_nack(*tmpbuf) < 0) ++ break; ++ cnt--; ++ tmpbuf++; ++ } ++ } ++ else { ++ ++ if (i2c_put_data(tmpaddr) < 0) ++ goto address_err; ++ while (cnt) { ++ if (++cnt_in_pg > 8) { ++ __i2c_send_stop(); ++ mdelay(1); ++ tmpaddr += 8; ++ goto start_write_page; ++ } ++ if (i2c_put_data(*tmpbuf) < 0) ++ break; ++ cnt--; ++ tmpbuf++; ++ } ++ } ++#else ++ if (i2c_put_data(tmpaddr) < 0) ++ goto address_err; ++ while (cnt) { ++ if (++cnt_in_pg > 8) { ++ __i2c_send_stop(); ++ mdelay(1); ++ tmpaddr += 8; ++ goto start_write_page; ++ } ++ if (i2c_put_data(*tmpbuf) < 0) ++ break; ++ cnt--; ++ tmpbuf++; ++ } ++#endif ++ __i2c_send_stop(); ++ return count - cnt; ++ device_err: ++ address_err: ++ timeout--; ++ __i2c_send_stop(); ++ goto W_try_again; ++ ++ W_timeout: ++ printk(KERN_DEBUG "Write I2C device 0x%2x failed.\n", device); ++ __i2c_send_stop(); ++ return -ENODEV; ++} ++ ++EXPORT_SYMBOL(i2c_open); ++EXPORT_SYMBOL(i2c_close); ++EXPORT_SYMBOL(i2c_setclk); ++EXPORT_SYMBOL(i2c_read); ++EXPORT_SYMBOL(i2c_write); +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/irq.c linux-2.6.24.7/arch/mips/jz4740/irq.c +--- linux-2.6.24.7.old/arch/mips/jz4740/irq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/irq.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,265 @@ ++/* ++ * linux/arch/mips/jz4740/irq.c ++ * ++ * JZ4740 interrupt routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * INTC irq type ++ */ ++ ++static void enable_intc_irq(unsigned int irq) ++{ ++ __intc_unmask_irq(irq); ++} ++ ++static void disable_intc_irq(unsigned int irq) ++{ ++ __intc_mask_irq(irq); ++} ++ ++static void mask_and_ack_intc_irq(unsigned int irq) ++{ ++ __intc_mask_irq(irq); ++ __intc_ack_irq(irq); ++} ++ ++static void end_intc_irq(unsigned int irq) ++{ ++ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { ++ enable_intc_irq(irq); ++ } ++} ++ ++static unsigned int startup_intc_irq(unsigned int irq) ++{ ++ enable_intc_irq(irq); ++ return 0; ++} ++ ++static void shutdown_intc_irq(unsigned int irq) ++{ ++ disable_intc_irq(irq); ++} ++ ++static struct irq_chip intc_irq_type = { ++ .typename = "INTC", ++ .startup = startup_intc_irq, ++ .shutdown = shutdown_intc_irq, ++ .enable = enable_intc_irq, ++ .disable = disable_intc_irq, ++ .ack = mask_and_ack_intc_irq, ++ .end = end_intc_irq, ++}; ++ ++/* ++ * GPIO irq type ++ */ ++ ++static void enable_gpio_irq(unsigned int irq) ++{ ++ unsigned int intc_irq; ++ ++ if (irq < (IRQ_GPIO_0 + 32)) { ++ intc_irq = IRQ_GPIO0; ++ } ++ else if (irq < (IRQ_GPIO_0 + 64)) { ++ intc_irq = IRQ_GPIO1; ++ } ++ else if (irq < (IRQ_GPIO_0 + 96)) { ++ intc_irq = IRQ_GPIO2; ++ } ++ else { ++ intc_irq = IRQ_GPIO3; ++ } ++ ++ enable_intc_irq(intc_irq); ++ __gpio_unmask_irq(irq - IRQ_GPIO_0); ++} ++ ++static void disable_gpio_irq(unsigned int irq) ++{ ++ __gpio_mask_irq(irq - IRQ_GPIO_0); ++} ++ ++static void mask_and_ack_gpio_irq(unsigned int irq) ++{ ++ __gpio_mask_irq(irq - IRQ_GPIO_0); ++ __gpio_ack_irq(irq - IRQ_GPIO_0); ++} ++ ++static void end_gpio_irq(unsigned int irq) ++{ ++ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { ++ enable_gpio_irq(irq); ++ } ++} ++ ++static unsigned int startup_gpio_irq(unsigned int irq) ++{ ++ enable_gpio_irq(irq); ++ return 0; ++} ++ ++static void shutdown_gpio_irq(unsigned int irq) ++{ ++ disable_gpio_irq(irq); ++} ++ ++static struct irq_chip gpio_irq_type = { ++ .typename = "GPIO", ++ .startup = startup_gpio_irq, ++ .shutdown = shutdown_gpio_irq, ++ .enable = enable_gpio_irq, ++ .disable = disable_gpio_irq, ++ .ack = mask_and_ack_gpio_irq, ++ .end = end_gpio_irq, ++}; ++ ++/* ++ * DMA irq type ++ */ ++ ++static void enable_dma_irq(unsigned int irq) ++{ ++ __intc_unmask_irq(IRQ_DMAC); ++ __dmac_channel_enable_irq(irq - IRQ_DMA_0); ++} ++ ++static void disable_dma_irq(unsigned int irq) ++{ ++ __dmac_channel_disable_irq(irq - IRQ_DMA_0); ++} ++ ++static void mask_and_ack_dma_irq(unsigned int irq) ++{ ++ __intc_ack_irq(IRQ_DMAC); ++ __dmac_channel_disable_irq(irq - IRQ_DMA_0); ++} ++ ++static void end_dma_irq(unsigned int irq) ++{ ++ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { ++ enable_dma_irq(irq); ++ } ++} ++ ++static unsigned int startup_dma_irq(unsigned int irq) ++{ ++ enable_dma_irq(irq); ++ return 0; ++} ++ ++static void shutdown_dma_irq(unsigned int irq) ++{ ++ disable_dma_irq(irq); ++} ++ ++static struct irq_chip dma_irq_type = { ++ .typename = "DMA", ++ .startup = startup_dma_irq, ++ .shutdown = shutdown_dma_irq, ++ .enable = enable_dma_irq, ++ .disable = disable_dma_irq, ++ .ack = mask_and_ack_dma_irq, ++ .end = end_dma_irq, ++}; ++ ++//---------------------------------------------------------------------- ++ ++void __init arch_init_irq(void) ++{ ++ int i; ++ ++ clear_c0_status(0xff04); /* clear ERL */ ++ set_c0_status(0x0400); /* set IP2 */ ++ ++ /* Set up INTC irq ++ */ ++ for (i = 0; i < 32; i++) { ++ disable_intc_irq(i); ++ irq_desc[i].chip = &intc_irq_type; ++ } ++ ++ /* Set up DMAC irq ++ */ ++ for (i = 0; i < NUM_DMA; i++) { ++ disable_dma_irq(IRQ_DMA_0 + i); ++ irq_desc[IRQ_DMA_0 + i].chip = &dma_irq_type; ++ } ++ ++ /* Set up GPIO irq ++ */ ++ for (i = 0; i < NUM_GPIO; i++) { ++ disable_gpio_irq(IRQ_GPIO_0 + i); ++ irq_desc[IRQ_GPIO_0 + i].chip = &gpio_irq_type; ++ } ++} ++ ++static int plat_real_irq(int irq) ++{ ++ switch (irq) { ++ case IRQ_GPIO0: ++ irq = __gpio_group_irq(0) + IRQ_GPIO_0; ++ break; ++ case IRQ_GPIO1: ++ irq = __gpio_group_irq(1) + IRQ_GPIO_0 + 32; ++ break; ++ case IRQ_GPIO2: ++ irq = __gpio_group_irq(2) + IRQ_GPIO_0 + 64; ++ break; ++ case IRQ_GPIO3: ++ irq = __gpio_group_irq(3) + IRQ_GPIO_0 + 96; ++ break; ++ case IRQ_DMAC: ++ irq = __dmac_get_irq() + IRQ_DMA_0; ++ break; ++ } ++ ++ return irq; ++} ++ ++asmlinkage void plat_irq_dispatch(void) ++{ ++ int irq = 0; ++ static unsigned long intc_ipr = 0; ++ ++ intc_ipr |= REG_INTC_IPR; ++ ++ if (!intc_ipr) return; ++ ++ irq = ffs(intc_ipr) - 1; ++ intc_ipr &= ~(1< ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* OHCI (USB full speed host controller) */ ++static struct resource jz_usb_ohci_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(UHC_BASE), // phys addr for ioremap ++ .end = CPHYSADDR(UHC_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_UHC, ++ .end = IRQ_UHC, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++/* The dmamask must be set for OHCI to work */ ++static u64 ohci_dmamask = ~(u32)0; ++ ++static struct platform_device jz_usb_ohci_device = { ++ .name = "jz-ohci", ++ .id = 0, ++ .dev = { ++ .dma_mask = &ohci_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_usb_ohci_resources), ++ .resource = jz_usb_ohci_resources, ++}; ++ ++/*** LCD controller ***/ ++static struct resource jz_lcd_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(LCD_BASE), ++ .end = CPHYSADDR(LCD_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_LCD, ++ .end = IRQ_LCD, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static u64 jz_lcd_dmamask = ~(u32)0; ++ ++static struct platform_device jz_lcd_device = { ++ .name = "jz-lcd", ++ .id = 0, ++ .dev = { ++ .dma_mask = &jz_lcd_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_lcd_resources), ++ .resource = jz_lcd_resources, ++}; ++ ++/* UDC (USB gadget controller) */ ++static struct resource jz_usb_gdt_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(UDC_BASE), ++ .end = CPHYSADDR(UDC_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_UDC, ++ .end = IRQ_UDC, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static u64 udc_dmamask = ~(u32)0; ++ ++static struct platform_device jz_usb_gdt_device = { ++ .name = "jz-udc", ++ .id = 0, ++ .dev = { ++ .dma_mask = &udc_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_usb_gdt_resources), ++ .resource = jz_usb_gdt_resources, ++}; ++ ++/** MMC/SD controller **/ ++static struct resource jz_mmc_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(MSC_BASE), ++ .end = CPHYSADDR(MSC_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_MSC, ++ .end = IRQ_MSC, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static u64 jz_mmc_dmamask = ~(u32)0; ++ ++static struct platform_device jz_mmc_device = { ++ .name = "jz-mmc", ++ .id = 0, ++ .dev = { ++ .dma_mask = &jz_mmc_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_mmc_resources), ++ .resource = jz_mmc_resources, ++}; ++ ++/** I2C controller **/ ++static struct resource jz_i2c_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(I2C_BASE), ++ .end = CPHYSADDR(I2C_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_I2C, ++ .end = IRQ_I2C, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static u64 jz_i2c_dmamask = ~(u32)0; ++ ++static struct platform_device jz_i2c_device = { ++ .name = "jz_i2c", ++ .id = 0, ++ .dev = { ++ .dma_mask = &jz_i2c_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_i2c_resources), ++ .resource = jz_i2c_resources, ++}; ++ ++/* All */ ++static struct platform_device *jz_platform_devices[] __initdata = { ++ &jz_usb_ohci_device, ++ &jz_lcd_device, ++ &jz_usb_gdt_device, ++ &jz_mmc_device, ++ &jz_i2c_device, ++}; ++ ++static int __init jz_platform_init(void) ++{ ++ return platform_add_devices(jz_platform_devices, ARRAY_SIZE(jz_platform_devices)); ++} ++ ++arch_initcall(jz_platform_init); +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/pm.c linux-2.6.24.7/arch/mips/jz4740/pm.c +--- linux-2.6.24.7.old/arch/mips/jz4740/pm.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/pm.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,462 @@ ++/* ++ * linux/arch/mips/jz4740/common/pm.c ++ * ++ * JZ4740 Power Management Routines ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#undef DEBUG ++//#define DEBUG ++#ifdef DEBUG ++#define dprintk(x...) printk(x) ++#else ++#define dprintk(x...) ++#endif ++ ++#define GPIO_WAKEUP 125 /* set SW7(GPIO 125) as WAKEUP key */ ++ ++/* ++ * __gpio_as_sleep set all pins to pull-disable, and set all pins as input ++ * except sdram, nand flash pins and the pins which can be used as CS1_N ++ * to CS4_N for chip select. ++ */ ++#define __gpio_as_sleep() \ ++do { \ ++ REG_GPIO_PXFUNC(1) = ~0x9ff9ffff; \ ++ REG_GPIO_PXSELC(1) = ~0x9ff9ffff; \ ++ REG_GPIO_PXDIRC(1) = ~0x9ff9ffff; \ ++ REG_GPIO_PXPES(1) = 0xffffffff; \ ++ REG_GPIO_PXFUNC(2) = ~0x37000000; \ ++ REG_GPIO_PXSELC(2) = ~0x37000000; \ ++ REG_GPIO_PXDIRC(2) = ~0x37000000; \ ++ REG_GPIO_PXPES(2) = 0xffffffff; \ ++ REG_GPIO_PXFUNC(3) = 0xffffffff; \ ++ REG_GPIO_PXSELC(3) = 0xffffffff; \ ++ REG_GPIO_PXDIRC(3) = 0xffffffff; \ ++ REG_GPIO_PXPES(3) = 0xffffffff; \ ++} while (0) ++ ++static int jz_pm_do_hibernate(void) ++{ ++ printk("Put CPU into hibernate mode.\n"); ++ ++ /* Mask all interrupts */ ++ REG_INTC_IMSR = 0xffffffff; ++ ++ /* ++ * RTC Wakeup or 1Hz interrupt can be enabled or disabled ++ * through RTC driver's ioctl (linux/driver/char/rtc_jz.c). ++ */ ++ ++ /* Set minimum wakeup_n pin low-level assertion time for wakeup: 100ms */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HWFCR = (100 << RTC_HWFCR_BIT); ++ ++ /* Set reset pin low-level assertion time after wakeup: must > 60ms */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HRCR = (60 << RTC_HRCR_BIT); /* 60 ms */ ++ ++ /* Scratch pad register to be reserved */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HSPR = 0x12345678; ++ ++ /* clear wakeup status register */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HWRSR = 0x0; ++ ++ /* Put CPU to power down mode */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HCR = RTC_HCR_PD; ++ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ while(1); ++ ++ /* We can't get here */ ++ return 0; ++} ++ ++/* NOTES: ++ * 1: Pins that are floated (NC) should be set as input and pull-enable. ++ * 2: Pins that are pull-up or pull-down by outside should be set as input ++ * and pull-disable. ++ * 3: Pins that are connected to a chip except sdram and nand flash ++ * should be set as input and pull-disable, too. ++ */ ++static void jz_board_do_sleep(unsigned long *ptr) ++{ ++ unsigned char i; ++ ++ /* Print messages of GPIO registers for debug */ ++ for(i=0;i<4;i++) { ++ dprintk("run dat:%x pin:%x fun:%x sel:%x dir:%x pull:%x msk:%x trg:%x\n", \ ++ REG_GPIO_PXDAT(i),REG_GPIO_PXPIN(i),REG_GPIO_PXFUN(i),REG_GPIO_PXSEL(i), \ ++ REG_GPIO_PXDIR(i),REG_GPIO_PXPE(i),REG_GPIO_PXIM(i),REG_GPIO_PXTRG(i)); ++ } ++ ++ /* Save GPIO registers */ ++ for(i = 1; i < 4; i++) { ++ *ptr++ = REG_GPIO_PXFUN(i); ++ *ptr++ = REG_GPIO_PXSEL(i); ++ *ptr++ = REG_GPIO_PXDIR(i); ++ *ptr++ = REG_GPIO_PXPE(i); ++ *ptr++ = REG_GPIO_PXIM(i); ++ *ptr++ = REG_GPIO_PXDAT(i); ++ *ptr++ = REG_GPIO_PXTRG(i); ++ } ++ ++ /* ++ * Set all pins to pull-disable, and set all pins as input except ++ * sdram, nand flash pins and the pins which can be used as CS1_N ++ * to CS4_N for chip select. ++ */ ++ __gpio_as_sleep(); ++ ++ /* ++ * Set proper status for GPB25 to GPB28 which can be used as CS1_N to CS4_N. ++ * Keep the pins' function used for chip select(CS) here according to your ++ * system to avoid chip select crashing with sdram when resuming from sleep mode. ++ */ ++ ++#if defined(CONFIG_JZ4740_PAVO) ++ /* GPB25/CS1_N is used as chip select for nand flash, shouldn't be change. */ ++ ++ /* GPB26/CS2_N is connected to nand flash, needn't be changed. */ ++ ++ /* GPB27/CS3_N is used as EXT_INT for CS8900 on debug board, it should be set as input.*/ ++ __gpio_as_input(32+27); ++ ++ /* GPB28/CS4_N is used as cs8900's chip select, shouldn't be changed. */ ++#endif ++ ++ /* ++ * Enable pull for NC pins here according to your system ++ */ ++ ++#if defined(CONFIG_JZ4740_PAVO) ++ /* GPB30-27 <-> J1: WE_N RD_N CS4_N EXT_INT */ ++ for(i=27;i<31;i++) { ++ __gpio_enable_pull(32+i); ++ } ++ ++ /* GPC27<-> WAIT_N */ ++ __gpio_enable_pull(32*2+27); ++ ++ /* GPD16<->SD_WP; GPD13-10<->MSC_D0-3; GPD9<->MSC_CMD; GPD8<->MSC_CLK */ ++ __gpio_enable_pull(32*3+16); ++ for(i=8;i<14;i++) { ++ __gpio_enable_pull(32*3+i); ++ } ++#endif ++ ++ /* ++ * If you must set some GPIOs as output to high level or low level, ++ * you can set them here, using: ++ * __gpio_as_output(n); ++ * __gpio_set_pin(n); or __gpio_clear_pin(n); ++ */ ++ ++#if defined(CONFIG_JZ4740_PAVO) ++ /* GPD16 which is used as AMPEN_N should be set to high to disable audio amplifier */ ++ __gpio_set_pin(32*3+4); ++#endif ++ ++#ifdef DEBUG ++ /* Keep uart0 function for printing debug message */ ++ __gpio_as_uart0(); ++ ++ /* Print messages of GPIO registers for debug */ ++ for(i=0;i<4;i++) { ++ dprintk("sleep dat:%x pin:%x fun:%x sel:%x dir:%x pull:%x msk:%x trg:%x\n", \ ++ REG_GPIO_PXDAT(i),REG_GPIO_PXPIN(i),REG_GPIO_PXFUN(i),REG_GPIO_PXSEL(i), \ ++ REG_GPIO_PXDIR(i),REG_GPIO_PXPE(i),REG_GPIO_PXIM(i),REG_GPIO_PXTRG(i)); ++ } ++#endif ++} ++ ++static void jz_board_do_resume(unsigned long *ptr) ++{ ++ unsigned char i; ++ ++ /* Restore GPIO registers */ ++ for(i = 1; i < 4; i++) { ++ REG_GPIO_PXFUNS(i) = *ptr; ++ REG_GPIO_PXFUNC(i) = ~(*ptr++); ++ ++ REG_GPIO_PXSELS(i) = *ptr; ++ REG_GPIO_PXSELC(i) = ~(*ptr++); ++ ++ REG_GPIO_PXDIRS(i) = *ptr; ++ REG_GPIO_PXDIRC(i) = ~(*ptr++); ++ ++ REG_GPIO_PXPES(i) = *ptr; ++ REG_GPIO_PXPEC(i) = ~(*ptr++); ++ ++ REG_GPIO_PXIMS(i)=*ptr; ++ REG_GPIO_PXIMC(i)=~(*ptr++); ++ ++ REG_GPIO_PXDATS(i)=*ptr; ++ REG_GPIO_PXDATC(i)=~(*ptr++); ++ ++ REG_GPIO_PXTRGS(i)=*ptr; ++ REG_GPIO_PXTRGC(i)=~(*ptr++); ++ } ++ ++ /* Print messages of GPIO registers for debug */ ++ for(i=0;i<4;i++) { ++ dprintk("resume dat:%x pin:%x fun:%x sel:%x dir:%x pull:%x msk:%x trg:%x\n", \ ++ REG_GPIO_PXDAT(i),REG_GPIO_PXPIN(i),REG_GPIO_PXFUN(i),REG_GPIO_PXSEL(i), \ ++ REG_GPIO_PXDIR(i),REG_GPIO_PXPE(i),REG_GPIO_PXIM(i),REG_GPIO_PXTRG(i)); ++ } ++} ++ ++ ++ ++static int jz_pm_do_sleep(void) ++{ ++ unsigned long delta; ++ unsigned long nfcsr = REG_EMC_NFCSR; ++ unsigned long scr = REG_CPM_SCR; ++ unsigned long imr = REG_INTC_IMR; ++ unsigned long sadc = REG_SADC_ENA; ++ unsigned long sleep_gpio_save[7*3]; ++ ++ printk("Put CPU into sleep mode.\n"); ++ ++ /* Preserve current time */ ++ delta = xtime.tv_sec - REG_RTC_RSR; ++ ++ /* Disable nand flash */ ++ REG_EMC_NFCSR = ~0xff; ++ ++ /* stop sadc */ ++ REG_SADC_ENA &= ~0x7; ++ while((REG_SADC_ENA & 0x7) != 0); ++ udelay(100); ++ ++ /*stop udc and usb*/ ++ REG_CPM_SCR &= ~( 1<<6 | 1<<7); ++ REG_CPM_SCR |= 0<<6 | 1<<7; ++ ++ /* Sleep on-board modules */ ++ jz_board_do_sleep(sleep_gpio_save); ++ ++ /* Mask all interrupts */ ++ REG_INTC_IMSR = 0xffffffff; ++ ++ /* Just allow following interrupts to wakeup the system. ++ * Note: modify this according to your system. ++ */ ++ ++ /* enable RTC alarm */ ++ __intc_unmask_irq(IRQ_RTC); ++#if 0 ++ /* make system wake up after n seconds by RTC alarm */ ++ unsigned int v, n; ++ n = 10; ++ while (!__rtc_write_ready()); ++ __rtc_enable_alarm(); ++ while (!__rtc_write_ready()); ++ __rtc_enable_alarm_irq(); ++ while (!__rtc_write_ready()); ++ v = __rtc_get_second(); ++ while (!__rtc_write_ready()); ++ __rtc_set_alarm_second(v+n); ++#endif ++ ++ /* WAKEUP key */ ++ __gpio_as_irq_rise_edge(GPIO_WAKEUP); ++ __gpio_unmask_irq(GPIO_WAKEUP); ++ __intc_unmask_irq(IRQ_GPIO3); /* IRQ_GPIOn depends on GPIO_WAKEUP */ ++ ++ /* Enter SLEEP mode */ ++ REG_CPM_LCR &= ~CPM_LCR_LPM_MASK; ++ REG_CPM_LCR |= CPM_LCR_LPM_SLEEP; ++ __asm__(".set\tmips3\n\t" ++ "wait\n\t" ++ ".set\tmips0"); ++ ++ /* Restore to IDLE mode */ ++ REG_CPM_LCR &= ~CPM_LCR_LPM_MASK; ++ REG_CPM_LCR |= CPM_LCR_LPM_IDLE; ++ ++ /* Restore nand flash control register */ ++ REG_EMC_NFCSR = nfcsr; ++ ++ /* Restore interrupts */ ++ REG_INTC_IMSR = imr; ++ REG_INTC_IMCR = ~imr; ++ ++ /* Restore sadc */ ++ REG_SADC_ENA = sadc; ++ ++ /* Resume on-board modules */ ++ jz_board_do_resume(sleep_gpio_save); ++ ++ /* Restore sleep control register */ ++ REG_CPM_SCR = scr; ++ ++ /* Restore current time */ ++ xtime.tv_sec = REG_RTC_RSR + delta; ++ ++ return 0; ++} ++ ++/* Put CPU to HIBERNATE mode */ ++int jz_pm_hibernate(void) ++{ ++ return jz_pm_do_hibernate(); ++} ++ ++#ifndef CONFIG_JZ_POWEROFF ++static irqreturn_t pm_irq_handler (int irq, void *dev_id) ++{ ++ return IRQ_HANDLED; ++} ++#endif ++ ++/* Put CPU to SLEEP mode */ ++int jz_pm_sleep(void) ++{ ++ int retval; ++ ++#ifndef CONFIG_JZ_POWEROFF ++ if ((retval = request_irq (IRQ_GPIO_0 + GPIO_WAKEUP, pm_irq_handler, IRQF_DISABLED, ++ "PM", NULL))) { ++ printk ("PM could not get IRQ for GPIO_WAKEUP\n"); ++ return retval; ++ } ++#endif ++ ++ pm_send_all(PM_SUSPEND, (void *)3); ++ retval = jz_pm_do_sleep(); ++ pm_send_all(PM_RESUME, (void *)0); ++ ++#ifndef CONFIG_JZ_POWEROFF ++ free_irq (IRQ_GPIO_0 + GPIO_WAKEUP, NULL); ++#endif ++ ++ return retval; ++} ++ ++#if 0 ++/* Deprecated ,was used by dpm */ ++void jz_pm_idle(void) ++{ ++ local_irq_disable(); ++ if (!need_resched()) { ++ local_irq_enable(); ++ cpu_wait(); ++ } ++} ++#endif ++ ++#ifdef CONFIG_SYSCTL ++ ++/* ++ * Use a temporary sysctl number. Horrid, but will be cleaned up in 2.6 ++ * when all the PM interfaces exist nicely. ++ */ ++#define CTL_PM_SUSPEND 1 ++#define CTL_PM_HIBERNATE 2 ++ ++/*---------------------------------------------------------------------------- ++ * Power Management sleep sysctl proc interface ++ * ++ * A write to /proc/sys/pm/suspend invokes this function ++ * which initiates a sleep. ++ *--------------------------------------------------------------------------*/ ++static int sysctl_jz_pm_sleep(struct ctl_table *ctl, int write, struct file * filp, ++ void __user *buffer, size_t *lenp, loff_t *ppos) ++{ ++ return jz_pm_sleep(); ++} ++ ++/*---------------------------------------------------------------------------- ++ * Power Management sleep sysctl proc interface ++ * ++ * A write to /proc/sys/pm/hibernate invokes this function ++ * which initiates a poweroff. ++ *--------------------------------------------------------------------------*/ ++static int sysctl_jz_pm_hibernate(struct ctl_table *ctl, int write, struct file * filp, ++ void __user *buffer, size_t *lenp, loff_t *ppos) ++{ ++ return jz_pm_hibernate(); ++} ++ ++static struct ctl_table pm_table[] = ++{ ++ { ++ .ctl_name = CTL_UNNUMBERED, ++ .procname = "suspend", ++ .data = NULL, ++ .maxlen = 0, ++ .mode = 0600, ++ .proc_handler = &sysctl_jz_pm_sleep, ++ }, ++ { ++ .ctl_name = CTL_UNNUMBERED, ++ .procname = "hibernate", ++ .data = NULL, ++ .maxlen = 0, ++ .mode = 0600, ++ .proc_handler = &sysctl_jz_pm_hibernate, ++ }, ++ { .ctl_name = 0} ++}; ++ ++static struct ctl_table pm_dir_table[] = ++{ ++ { ++ .ctl_name = CTL_UNNUMBERED, ++ .procname = "pm", ++ .mode = 0555, ++ .child = pm_table, ++ }, ++ { .ctl_name = 0} ++}; ++ ++#endif /* CONFIG_SYSCTL */ ++ ++/* ++ * Initialize power interface ++ */ ++static int __init jz_pm_init(void) ++{ ++ printk("Power Management for JZ\n"); ++ ++#ifdef CONFIG_SYSCTL ++ register_sysctl_table(pm_dir_table); ++#endif ++ ++ return 0; ++} ++ ++module_init(jz_pm_init); ++ +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/proc.c linux-2.6.24.7/arch/mips/jz4740/proc.c +--- linux-2.6.24.7.old/arch/mips/jz4740/proc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/proc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,887 @@ ++/* ++ * linux/arch/mips/jz4740/proc.c ++ * ++ * /proc/jz/ procfs for jz4740 on-chip modules. ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++//#define DEBUG 1 ++#undef DEBUG ++ ++/* Define this to reserve total 4MB contineous physical memory for IPU. ++ * MPlayer will use IPU to optimize the decoding process. ++ * ++ * If you do not want to run the MPlayer, you can comment it. ++ */ ++#define CONFIG_RESERVE_IPU_MEM 1 ++ ++struct proc_dir_entry *proc_jz_root; ++ ++ ++/* ++ * EMC Modules ++ */ ++static int emc_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ ++ len += sprintf (page+len, "SMCR(0-5): 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", REG_EMC_SMCR0, REG_EMC_SMCR1, REG_EMC_SMCR2, REG_EMC_SMCR3, REG_EMC_SMCR4); ++ len += sprintf (page+len, "SACR(0-5): 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", REG_EMC_SACR0, REG_EMC_SACR1, REG_EMC_SACR2, REG_EMC_SACR3, REG_EMC_SACR4); ++ len += sprintf (page+len, "DMCR: 0x%08x\n", REG_EMC_DMCR); ++ len += sprintf (page+len, "RTCSR: 0x%04x\n", REG_EMC_RTCSR); ++ len += sprintf (page+len, "RTCOR: 0x%04x\n", REG_EMC_RTCOR); ++ return len; ++} ++ ++/* ++ * Power Manager Module ++ */ ++static int pmc_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ unsigned long lcr = REG_CPM_LCR; ++ unsigned long clkgr = REG_CPM_CLKGR; ++ ++ len += sprintf (page+len, "Low Power Mode : %s\n", ++ ((lcr & CPM_LCR_LPM_MASK) == (CPM_LCR_LPM_IDLE)) ? ++ "IDLE" : (((lcr & CPM_LCR_LPM_MASK) == (CPM_LCR_LPM_SLEEP)) ? ++ "SLEEP" : "HIBERNATE")); ++ len += sprintf (page+len, "Doze Mode : %s\n", ++ (lcr & CPM_LCR_DOZE_ON) ? "on" : "off"); ++ if (lcr & CPM_LCR_DOZE_ON) ++ len += sprintf (page+len, " duty : %d\n", (int)((lcr & CPM_LCR_DOZE_DUTY_MASK) >> CPM_LCR_DOZE_DUTY_BIT)); ++ len += sprintf (page+len, "IPU : %s\n", ++ (clkgr & CPM_CLKGR_IPU) ? "stopped" : "running"); ++ len += sprintf (page+len, "DMAC : %s\n", ++ (clkgr & CPM_CLKGR_DMAC) ? "stopped" : "running"); ++ len += sprintf (page+len, "UHC : %s\n", ++ (clkgr & CPM_CLKGR_UHC) ? "stopped" : "running"); ++ len += sprintf (page+len, "UDC : %s\n", ++ (clkgr & CPM_CLKGR_UDC) ? "stopped" : "running"); ++ len += sprintf (page+len, "LCD : %s\n", ++ (clkgr & CPM_CLKGR_LCD) ? "stopped" : "running"); ++ len += sprintf (page+len, "CIM : %s\n", ++ (clkgr & CPM_CLKGR_CIM) ? "stopped" : "running"); ++ len += sprintf (page+len, "SADC : %s\n", ++ (clkgr & CPM_CLKGR_SADC) ? "stopped" : "running"); ++ len += sprintf (page+len, "MSC : %s\n", ++ (clkgr & CPM_CLKGR_MSC) ? "stopped" : "running"); ++ len += sprintf (page+len, "AIC1 : %s\n", ++ (clkgr & CPM_CLKGR_AIC1) ? "stopped" : "running"); ++ len += sprintf (page+len, "AIC2 : %s\n", ++ (clkgr & CPM_CLKGR_AIC2) ? "stopped" : "running"); ++ len += sprintf (page+len, "SSI : %s\n", ++ (clkgr & CPM_CLKGR_SSI) ? "stopped" : "running"); ++ len += sprintf (page+len, "I2C : %s\n", ++ (clkgr & CPM_CLKGR_I2C) ? "stopped" : "running"); ++ len += sprintf (page+len, "RTC : %s\n", ++ (clkgr & CPM_CLKGR_RTC) ? "stopped" : "running"); ++ len += sprintf (page+len, "TCU : %s\n", ++ (clkgr & CPM_CLKGR_TCU) ? "stopped" : "running"); ++ len += sprintf (page+len, "UART1 : %s\n", ++ (clkgr & CPM_CLKGR_UART1) ? "stopped" : "running"); ++ len += sprintf (page+len, "UART0 : %s\n", ++ (clkgr & CPM_CLKGR_UART0) ? "stopped" : "running"); ++ return len; ++} ++ ++static int pmc_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ REG_CPM_CLKGR = simple_strtoul(buffer, 0, 16); ++ return count; ++} ++ ++/* ++ * Clock Generation Module ++ */ ++#define TO_MHZ(x) (x/1000000),(x%1000000)/10000 ++#define TO_KHZ(x) (x/1000),(x%1000)/10 ++ ++static int cgm_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ unsigned int cppcr = REG_CPM_CPPCR; /* PLL Control Register */ ++ unsigned int cpccr = REG_CPM_CPCCR; /* Clock Control Register */ ++ unsigned int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ unsigned int od[4] = {1, 2, 2, 4}; ++ ++ len += sprintf (page+len, "CPPCR : 0x%08x\n", cppcr); ++ len += sprintf (page+len, "CPCCR : 0x%08x\n", cpccr); ++ len += sprintf (page+len, "PLL : %s\n", ++ (cppcr & CPM_CPPCR_PLLEN) ? "ON" : "OFF"); ++ len += sprintf (page+len, "m:n:o : %d:%d:%d\n", ++ __cpm_get_pllm() + 2, ++ __cpm_get_plln() + 2, ++ od[__cpm_get_pllod()] ++ ); ++ len += sprintf (page+len, "C:H:M:P : %d:%d:%d:%d\n", ++ div[__cpm_get_cdiv()], ++ div[__cpm_get_hdiv()], ++ div[__cpm_get_mdiv()], ++ div[__cpm_get_pdiv()] ++ ); ++ len += sprintf (page+len, "PLL Freq : %3d.%02d MHz\n", TO_MHZ(__cpm_get_pllout())); ++ len += sprintf (page+len, "CCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_cclk())); ++ len += sprintf (page+len, "HCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_hclk())); ++ len += sprintf (page+len, "MCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_mclk())); ++ len += sprintf (page+len, "PCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_pclk())); ++ len += sprintf (page+len, "LCDCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_lcdclk())); ++ len += sprintf (page+len, "PIXCLK : %3d.%02d KHz\n", TO_KHZ(__cpm_get_pixclk())); ++ len += sprintf (page+len, "I2SCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_i2sclk())); ++ len += sprintf (page+len, "USBCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_usbclk())); ++ len += sprintf (page+len, "MSCCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_mscclk())); ++ len += sprintf (page+len, "EXTALCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_extalclk())); ++ len += sprintf (page+len, "RTCCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_rtcclk())); ++ ++ return len; ++} ++ ++static int cgm_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ REG_CPM_CPCCR = simple_strtoul(buffer, 0, 16); ++ return count; ++} ++ ++ ++/* ++ * UDC hotplug ++ */ ++#ifdef CONFIG_JZ_UDC_HOTPLUG ++extern int jz_udc_active; /* defined in drivers/char/jzchar/jz_udc_hotplug.c */ ++#endif ++ ++#ifndef GPIO_UDC_HOTPLUG ++#define GPIO_UDC_HOTPLUG 86 ++#endif ++ ++static int udc_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ ++ if (__gpio_get_pin(GPIO_UDC_HOTPLUG)) { ++ ++#ifdef CONFIG_JZ_UDC_HOTPLUG ++ ++ /* Cable has connected, wait for disconnection. */ ++ __gpio_as_irq_fall_edge(GPIO_UDC_HOTPLUG); ++ ++ if (jz_udc_active) ++ len += sprintf (page+len, "CONNECT_CABLE\n"); ++ else ++ len += sprintf (page+len, "CONNECT_POWER\n"); ++#else ++ len += sprintf (page+len, "CONNECT\n"); ++#endif ++ } ++ else { ++ ++#ifdef CONFIG_JZ_UDC_HOTPLUG ++ /* Cable has disconnected, wait for connection. */ ++ __gpio_as_irq_rise_edge(GPIO_UDC_HOTPLUG); ++#endif ++ ++ len += sprintf (page+len, "REMOVE\n"); ++ } ++ ++ return len; ++} ++ ++/* ++ * MMC/SD hotplug ++ */ ++ ++#ifndef MSC_HOTPLUG_PIN ++#define MSC_HOTPLUG_PIN 90 ++#endif ++ ++static int mmc_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ ++#if defined(CONFIG_JZ4740_LYRA) ++ if (!(__gpio_get_pin(MSC_HOTPLUG_PIN))) ++#else ++ if (__gpio_get_pin(MSC_HOTPLUG_PIN)) ++#endif ++ len += sprintf (page+len, "REMOVE\n"); ++ else ++ len += sprintf (page+len, "INSERT\n"); ++ ++ return len; ++} ++ ++#ifdef CONFIG_RESERVE_IPU_MEM ++ ++/* USAGE: ++ * echo n > /proc/jz/ipu // n = [1,...,9], alloc mem, 2^n pages. ++ * echo FF > /proc/jz/ipu // 255, free all buffer ++ * echo xxxx > /proc/jz/ipu // free buffer which addr is xxxx ++ * echo llll > /proc/jz/ipu // add_wired_entry(l,l,l,l) ++ * echo 0 > /proc/jz/ipu // debug, print ipu_buf ++ * od -X /proc/jz/ipu // read mem addr ++ */ ++ ++typedef struct _ipu_buf { ++ unsigned int addr; /* phys addr */ ++ unsigned int page_shift; ++} ipu_buf_t; ++ ++#define IPU_BUF_MAX 4 /* 4 buffers */ ++ ++static struct _ipu_buf ipu_buf[IPU_BUF_MAX]; ++static int ipu_buf_cnt = 0; ++static unsigned char g_asid=0; ++ ++extern void local_flush_tlb_all(void); ++ ++/* CP0 hazard avoidance. */ ++#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ ++ "nop; nop; nop; nop; nop; nop;\n\t" \ ++ ".set reorder\n\t") ++void show_tlb(void) ++{ ++#define ASID_MASK 0xFF ++ ++ unsigned long flags; ++ unsigned int old_ctx; ++ unsigned int entry; ++ unsigned int entrylo0, entrylo1, entryhi; ++ unsigned int pagemask; ++ ++ local_irq_save(flags); ++ ++ /* Save old context */ ++ old_ctx = (read_c0_entryhi() & 0xff); ++ ++ printk("TLB content:\n"); ++ entry = 0; ++ while(entry < 32) { ++ write_c0_index(entry); ++ BARRIER; ++ tlb_read(); ++ BARRIER; ++ entryhi = read_c0_entryhi(); ++ entrylo0 = read_c0_entrylo0(); ++ entrylo1 = read_c0_entrylo1(); ++ pagemask = read_c0_pagemask(); ++ printk("%02d: ASID=%02d%s VA=0x%08x ", entry, entryhi & ASID_MASK, (entrylo0 & entrylo1 & 1) ? "(G)" : " ", entryhi & ~ASID_MASK); ++ printk("PA0=0x%08x C0=%x %s%s%s\n", (entrylo0>>6)<<12, (entrylo0>>3) & 7, (entrylo0 & 4) ? "Dirty " : "", (entrylo0 & 2) ? "Valid " : "Invalid ", (entrylo0 & 1) ? "Global" : ""); ++ printk("\t\t\t PA1=0x%08x C1=%x %s%s%s\n", (entrylo1>>6)<<12, (entrylo1>>3) & 7, (entrylo1 & 4) ? "Dirty " : "", (entrylo1 & 2) ? "Valid " : "Invalid ", (entrylo1 & 1) ? "Global" : ""); ++ ++ printk("\t\tpagemask=0x%08x", pagemask); ++ printk("\tentryhi=0x%08x\n", entryhi); ++ printk("\t\tentrylo0=0x%08x", entrylo0); ++ printk("\tentrylo1=0x%08x\n", entrylo1); ++ ++ entry++; ++ } ++ BARRIER; ++ write_c0_entryhi(old_ctx); ++ ++ local_irq_restore(flags); ++} ++ ++static void ipu_add_wired_entry(unsigned long pid, ++ unsigned long entrylo0, unsigned long entrylo1, ++ unsigned long entryhi, unsigned long pagemask) ++{ ++ unsigned long flags; ++ unsigned long wired; ++ unsigned long old_pagemask; ++ unsigned long old_ctx; ++ struct task_struct *g, *p; ++ ++ /* We will lock an 4MB page size entry to map the 4MB reserved IPU memory */ ++ wired = read_c0_wired(); ++ if (wired) return; ++ ++ do_each_thread(g, p) { ++ if (p->pid == pid ) ++ g_asid = p->mm->context[0]; ++ } while_each_thread(g, p); ++ ++ local_irq_save(flags); ++ ++ entrylo0 = entrylo0 >> 6; /* PFN */ ++ entrylo0 |= 0x6 | (0 << 3); /* Write-through cacheable, dirty, valid */ ++ ++ /* Save old context and create impossible VPN2 value */ ++ old_ctx = read_c0_entryhi() & 0xff; ++ old_pagemask = read_c0_pagemask(); ++ write_c0_wired(wired + 1); ++ write_c0_index(wired); ++ BARRIER; ++ entryhi &= ~0xff; /* new add, 20070906 */ ++ entryhi |= g_asid; /* new add, 20070906 */ ++// entryhi |= old_ctx; /* new add, 20070906 */ ++ write_c0_pagemask(pagemask); ++ write_c0_entryhi(entryhi); ++ write_c0_entrylo0(entrylo0); ++ write_c0_entrylo1(entrylo1); ++ BARRIER; ++ tlb_write_indexed(); ++ BARRIER; ++ ++ write_c0_entryhi(old_ctx); ++ BARRIER; ++ write_c0_pagemask(old_pagemask); ++ local_flush_tlb_all(); ++ local_irq_restore(flags); ++#if defined(DEBUG) ++ printk("\nold_ctx=%03d\n", old_ctx); ++ ++ show_tlb(); ++#endif ++} ++ ++static void ipu_del_wired_entry( void ) ++{ ++ unsigned long flags; ++ unsigned long wired; ++ ++ /* Free all lock entry */ ++ local_irq_save(flags); ++ wired = read_c0_wired(); ++ if (wired) ++ write_c0_wired(0); ++ local_irq_restore(flags); ++} ++ ++static inline void ipu_buf_get( unsigned int page_shift ) ++{ ++ unsigned char * virt_addr; ++ int i; ++ for ( i=0; i< IPU_BUF_MAX; ++i ) { ++ if ( ipu_buf[i].addr == 0 ) { ++ break; ++ } ++ } ++ ++ if ( (ipu_buf_cnt = i) == IPU_BUF_MAX ) { ++ printk("Error, no free ipu buffer.\n"); ++ return ; ++ } ++ ++ virt_addr = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); ++ ++ if ( virt_addr ) { ++ ipu_buf[ipu_buf_cnt].addr = (unsigned int)virt_to_phys((void *)virt_addr); ++ ipu_buf[ipu_buf_cnt].page_shift = page_shift; ++ ++ for (i = 0; i < (1<= IPU_BUF_MAX ) { /* failed alloc mem, rturn 0 */ ++ printk("no free buffer.\n"); ++ *pint = 0; ++ } ++ else ++ *pint = (unsigned int )ipu_buf[ipu_buf_cnt].addr; /* phys addr */ ++ len += sizeof(unsigned int); ++ ++#if defined(DEBUG) ++ show_tlb(); ++#endif ++ return len; ++ ++} ++ ++static int ipu_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ unsigned int val ; ++ int cnt,i; ++ char buf[12]; ++ unsigned long pid, entrylo0, entrylo1, entryhi, pagemask; ++#if defined(DEBUG) ++ printk("ipu write count=%u\n", count); ++#endif ++ if (count == (8*5+1)) { ++ for (i=0;i<12;i++) buf[i]=0; ++ strncpy(buf, buffer+8*0, 8); ++ pid = simple_strtoul(buf, 0, 16); ++ for (i=0;i<12;i++) buf[i]=0; ++ strncpy(buf, buffer+8*1, 8); ++ entrylo0 = simple_strtoul(buf, 0, 16); ++ for (i=0;i<12;i++) buf[i]=0; ++ strncpy(buf, buffer+8*2, 8); ++ entrylo1 = simple_strtoul(buf, 0, 16); ++ for (i=0;i<12;i++) buf[i]=0; ++ strncpy(buf, buffer+8*3, 8); ++ entryhi = simple_strtoul(buf, 0, 16); ++ for (i=0;i<12;i++) buf[i]=0; ++ strncpy(buf, buffer+8*4, 8); ++ pagemask = simple_strtoul(buf, 0, 16); ++ ++#if defined(DEBUG) ++ printk("pid=0x%08x, entrylo0=0x%08x, entrylo1=0x%08x, entryhi=0x%08x, pagemask=0x%08x\n", ++ pid, entrylo0, entrylo1, entryhi, pagemask); ++#endif ++ ipu_add_wired_entry( pid, entrylo0, entrylo1, entryhi, pagemask); ++ return 41; ++ } else if ( count <= 8+1 ) { ++ for (i=0;i<12;i++) buf[i]=0; ++ strncpy(buf, buffer, 8); ++ val = simple_strtoul(buf, 0, 16); ++ } else if (count == 44) { ++ for (i = 0; i < 12; i++) ++ buf[i] = 0; ++ strncpy(buf, buffer, 10); ++ pid = simple_strtoul(buf, 0, 16); ++ for (i = 0; i < 12; i++) ++ buf[i] = 0; ++ strncpy(buf, buffer + 11, 10); ++ entryhi = simple_strtoul(buf, 0, 16);//vaddr ++ for (i = 0; i < 12; i++) ++ buf[i] = 0; ++ strncpy(buf, buffer + 22, 10); ++ entrylo0 = simple_strtoul(buf, 0, 16);//paddr ++ for (i = 0; i < 12; i++) ++ buf[i] = 0; ++ strncpy(buf, buffer + 33, 10); ++ pagemask = simple_strtoul(buf, 0, 16); ++ pagemask = 0x3ff << 13; /* Fixed to 4MB page size */ ++ ipu_add_wired_entry(pid, entrylo0, 0, entryhi, pagemask); ++ return 44; ++ } else { ++ printk("ipu write count error, count=%d\n.", (unsigned int)count); ++ return -1; ++ } ++ ++ /* val: 1-9, page_shift, val>= 10: ipu_buf.addr */ ++ if ( val == 0 ) { /* debug, print ipu_buf info */ ++ for ( cnt=0; cnt /proc/jz/imem // n = [0,...,10], allocate memory, 2^n pages ++ * echo xxxxxxxx > /proc/jz/imem // free buffer which addr is xxxxxxxx ++ * echo FF > /proc/jz/ipu // FF, free all buffers ++ * od -X /proc/jz/imem // return the allocated buffer address and the max order of free buffer ++ */ ++ ++//#define DEBUG_IMEM 1 ++ ++#define IMEM_MAX_ORDER 10 /* max 2^10 * 4096 = 4MB */ ++ ++static unsigned int jz_imem_base; /* physical base address of ipu memory */ ++ ++static unsigned int allocated_phys_addr = 0; ++ ++/* ++ * Allocated buffer list ++ */ ++typedef struct imem_list { ++ unsigned int phys_start; /* physical start addr */ ++ unsigned int phys_end; /* physical end addr */ ++ struct imem_list *next; ++} imem_list_t; ++ ++static struct imem_list *imem_list_head = NULL; /* up sorted by phys_start */ ++ ++#ifdef DEBUG_IMEM ++static void dump_imem_list(void) ++{ ++ struct imem_list *imem; ++ ++ printk("*** dump_imem_list 0x%x ***\n", (u32)imem_list_head); ++ imem = imem_list_head; ++ while (imem) { ++ printk("imem=0x%x phys_start=0x%x phys_end=0x%x next=0x%x\n", (u32)imem, imem->phys_start, imem->phys_end, (u32)imem->next); ++ imem = imem->next; ++ } ++} ++#endif ++ ++/* allocate 2^order pages inside the 4MB memory */ ++static int imem_alloc(unsigned int order) ++{ ++ int alloc_ok = 0; ++ unsigned int start, end; ++ unsigned int size = (1 << order) * PAGE_SIZE; ++ struct imem_list *imem, *imemn, *imemp; ++ ++ allocated_phys_addr = 0; ++ ++ start = jz_imem_base; ++ end = start + (1 << IMEM_MAX_ORDER) * PAGE_SIZE; ++ ++ imem = imem_list_head; ++ while (imem) { ++ if ((imem->phys_start - start) >= size) { ++ /* we got a valid address range */ ++ alloc_ok = 1; ++ break; ++ } ++ ++ start = imem->phys_end + 1; ++ imem = imem->next; ++ } ++ ++ if (!alloc_ok) { ++ if ((end - start) >= size) ++ alloc_ok = 1; ++ } ++ ++ if (alloc_ok) { ++ end = start + size - 1; ++ allocated_phys_addr = start; ++ ++ /* add to imem_list, up sorted by phys_start */ ++ imemn = kmalloc(sizeof(struct imem_list), GFP_KERNEL); ++ if (!imemn) { ++ return -ENOMEM; ++ } ++ imemn->phys_start = start; ++ imemn->phys_end = end; ++ imemn->next = NULL; ++ ++ if (!imem_list_head) ++ imem_list_head = imemn; ++ else { ++ imem = imemp = imem_list_head; ++ while (imem) { ++ if (start < imem->phys_start) { ++ break; ++ } ++ ++ imemp = imem; ++ imem = imem->next; ++ } ++ ++ if (imem == imem_list_head) { ++ imem_list_head = imemn; ++ imemn->next = imem; ++ } ++ else { ++ imemn->next = imemp->next; ++ imemp->next = imemn; ++ } ++ } ++ } ++ ++#ifdef DEBUG_IMEM ++ dump_imem_list(); ++#endif ++ return 0; ++} ++ ++static void imem_free(unsigned int phys_addr) ++{ ++ struct imem_list *imem, *imemp; ++ ++ imem = imemp = imem_list_head; ++ while (imem) { ++ if (phys_addr == imem->phys_start) { ++ if (imem == imem_list_head) { ++ imem_list_head = imem->next; ++ } ++ else { ++ imemp->next = imem->next; ++ } ++ ++ kfree(imem); ++ break; ++ } ++ ++ imemp = imem; ++ imem = imem->next; ++ } ++ ++#ifdef DEBUG_IMEM ++ dump_imem_list(); ++#endif ++} ++ ++static void imem_free_all(void) ++{ ++ struct imem_list *imem; ++ ++ imem = imem_list_head; ++ while (imem) { ++ kfree(imem); ++ imem = imem->next; ++ } ++ ++ imem_list_head = NULL; ++ ++ allocated_phys_addr = 0; ++ ++#ifdef DEBUG_IMEM ++ dump_imem_list(); ++#endif ++} ++ ++/* ++ * Return the allocated buffer address and the max order of free buffer ++ */ ++static int imem_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ unsigned int start_addr, end_addr, max_order, max_size; ++ struct imem_list *imem; ++ ++ unsigned int *tmp = (unsigned int *)(page + len); ++ ++ start_addr = jz_imem_base; ++ end_addr = start_addr + (1 << IMEM_MAX_ORDER) * PAGE_SIZE; ++ ++ if (!imem_list_head) ++ max_size = end_addr - start_addr; ++ else { ++ max_size = 0; ++ imem = imem_list_head; ++ while (imem) { ++ if (max_size < (imem->phys_start - start_addr)) ++ max_size = imem->phys_start - start_addr; ++ ++ start_addr = imem->phys_end + 1; ++ imem = imem->next; ++ } ++ ++ if (max_size < (end_addr - start_addr)) ++ max_size = end_addr - start_addr; ++ } ++ ++ if (max_size > 0) { ++ max_order = get_order(max_size); ++ if (((1 << max_order) * PAGE_SIZE) > max_size) ++ max_order--; ++ } ++ else { ++ max_order = 0xffffffff; /* No any free buffer */ ++ } ++ ++ *tmp++ = allocated_phys_addr; /* address allocated by 'echo n > /proc/jz/imem' */ ++ *tmp = max_order; /* max order of current free buffers */ ++ ++ len += 2 * sizeof(unsigned int); ++ ++ return len; ++} ++ ++static int imem_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ unsigned int val; ++ ++ val = simple_strtoul(buffer, 0, 16); ++ ++ if (val == 0xff) { ++ /* free all memory */ ++ imem_free_all(); ++ ipu_del_wired_entry(); ++ } ++ else if ((val >= 0) && (val <= IMEM_MAX_ORDER)) { ++ /* allocate 2^val pages */ ++ imem_alloc(val); ++ } ++ else { ++ /* free buffer which phys_addr is val */ ++ imem_free(val); ++ } ++ ++ return count; ++} ++ ++#endif /* CONFIG_RESERVE_IPU_MEM */ ++ ++/* ++ * /proc/jz/xxx entry ++ * ++ */ ++static int __init jz_proc_init(void) ++{ ++ struct proc_dir_entry *res; ++#ifdef CONFIG_RESERVE_IPU_MEM ++ unsigned int virt_addr, i; ++#endif ++ ++ proc_jz_root = proc_mkdir("jz", 0); ++ ++ /* External Memory Controller */ ++ res = create_proc_entry("emc", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = emc_read_proc; ++ res->write_proc = NULL; ++ res->data = NULL; ++ } ++ ++ /* Power Management Controller */ ++ res = create_proc_entry("pmc", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = pmc_read_proc; ++ res->write_proc = pmc_write_proc; ++ res->data = NULL; ++ } ++ ++ /* Clock Generation Module */ ++ res = create_proc_entry("cgm", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = cgm_read_proc; ++ res->write_proc = cgm_write_proc; ++ res->data = NULL; ++ } ++ ++ /* udc hotplug */ ++ res = create_proc_entry("udc", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = udc_read_proc; ++ res->write_proc = NULL; ++ res->data = NULL; ++ } ++ ++ /* mmc hotplug */ ++ res = create_proc_entry("mmc", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = mmc_read_proc; ++ res->write_proc = NULL; ++ res->data = NULL; ++ } ++ ++#ifdef CONFIG_RESERVE_IPU_MEM ++ /* Image process unit */ ++ res = create_proc_entry("ipu", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = ipu_read_proc; ++ res->write_proc = ipu_write_proc; ++ res->data = NULL; ++ } ++ ++ /* ++ * Reserve a 4MB memory for IPU on JZ4740. ++ */ ++ jz_imem_base = (unsigned int)__get_free_pages(GFP_KERNEL, IMEM_MAX_ORDER); ++ if (jz_imem_base) { ++ /* imem (IPU memory management) */ ++ res = create_proc_entry("imem", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = imem_read_proc; ++ res->write_proc = imem_write_proc; ++ res->data = NULL; ++ } ++ ++ /* Set page reserved */ ++ virt_addr = jz_imem_base; ++ for (i = 0; i < (1 << IMEM_MAX_ORDER); i++) { ++ SetPageReserved(virt_to_page((void *)virt_addr)); ++ virt_addr += PAGE_SIZE; ++ } ++ ++ /* Convert to physical address */ ++ jz_imem_base = virt_to_phys((void *)jz_imem_base); ++ ++ printk("Total %dMB memory at 0x%x was reserved for IPU\n", ++ (unsigned int)((1 << IMEM_MAX_ORDER) * PAGE_SIZE)/1000000, jz_imem_base); ++ } ++#endif ++ ++ return 0; ++} ++ ++__initcall(jz_proc_init); +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/prom.c linux-2.6.24.7/arch/mips/jz4740/prom.c +--- linux-2.6.24.7.old/arch/mips/jz4740/prom.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/prom.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,198 @@ ++/* ++ * ++ * BRIEF MODULE DESCRIPTION ++ * PROM library initialisation code, supports YAMON and U-Boot. ++ * ++ * Copyright 2000, 2001, 2006 MontaVista Software Inc. ++ * Author: MontaVista Software, Inc. ++ * ppopov@mvista.com or source@mvista.com ++ * ++ * This file was derived from Carsten Langgaard's ++ * arch/mips/mips-boards/xx files. ++ * ++ * Carsten Langgaard, carstenl@mips.com ++ * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN ++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, 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 DAMAGE. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* #define DEBUG_CMDLINE */ ++ ++int prom_argc; ++char **prom_argv, **prom_envp; ++ ++char * prom_getcmdline(void) ++{ ++ return &(arcs_cmdline[0]); ++} ++ ++void prom_init_cmdline(void) ++{ ++ char *cp; ++ int actr; ++ ++ actr = 1; /* Always ignore argv[0] */ ++ ++ cp = &(arcs_cmdline[0]); ++ while(actr < prom_argc) { ++ strcpy(cp, prom_argv[actr]); ++ cp += strlen(prom_argv[actr]); ++ *cp++ = ' '; ++ actr++; ++ } ++ if (cp != &(arcs_cmdline[0])) /* get rid of trailing space */ ++ --cp; ++ if (prom_argc > 1) ++ *cp = '\0'; ++ ++} ++ ++ ++char *prom_getenv(char *envname) ++{ ++#if 0 ++ /* ++ * Return a pointer to the given environment variable. ++ * YAMON uses "name", "value" pairs, while U-Boot uses "name=value". ++ */ ++ ++ char **env = prom_envp; ++ int i = strlen(envname); ++ int yamon = (*env && strchr(*env, '=') == NULL); ++ ++ while (*env) { ++ if (yamon) { ++ if (strcmp(envname, *env++) == 0) ++ return *env; ++ } else { ++ if (strncmp(envname, *env, i) == 0 && (*env)[i] == '=') ++ return *env + i + 1; ++ } ++ env++; ++ } ++#endif ++ return NULL; ++} ++ ++inline unsigned char str2hexnum(unsigned char c) ++{ ++ if(c >= '0' && c <= '9') ++ return c - '0'; ++ if(c >= 'a' && c <= 'f') ++ return c - 'a' + 10; ++ if(c >= 'A' && c <= 'F') ++ return c - 'A' + 10; ++ return 0; /* foo */ ++} ++ ++inline void str2eaddr(unsigned char *ea, unsigned char *str) ++{ ++ int i; ++ ++ for(i = 0; i < 6; i++) { ++ unsigned char num; ++ ++ if((*str == '.') || (*str == ':')) ++ str++; ++ num = str2hexnum(*str++) << 4; ++ num |= (str2hexnum(*str++)); ++ ea[i] = num; ++ } ++} ++ ++int get_ethernet_addr(char *ethernet_addr) ++{ ++ char *ethaddr_str; ++ ++ ethaddr_str = prom_getenv("ethaddr"); ++ if (!ethaddr_str) { ++ printk("ethaddr not set in boot prom\n"); ++ return -1; ++ } ++ str2eaddr(ethernet_addr, ethaddr_str); ++ ++#if 0 ++ { ++ int i; ++ ++ printk("get_ethernet_addr: "); ++ for (i=0; i<5; i++) ++ printk("%02x:", (unsigned char)*(ethernet_addr+i)); ++ printk("%02x\n", *(ethernet_addr+i)); ++ } ++#endif ++ ++ return 0; ++} ++ ++void __init prom_free_prom_memory(void) ++{ ++} ++ ++void __init prom_init(void) ++{ ++ unsigned char *memsize_str; ++ unsigned long memsize; ++ ++ prom_argc = (int) fw_arg0; ++ prom_argv = (char **) fw_arg1; ++ prom_envp = (char **) fw_arg2; ++ ++ mips_machtype = MACH_INGENIC_JZ4740; ++ ++ prom_init_cmdline(); ++ memsize_str = prom_getenv("memsize"); ++ if (!memsize_str) { ++ memsize = 0x04000000; ++ } else { ++ memsize = simple_strtol(memsize_str, NULL, 0); ++ } ++ add_memory_region(0, memsize, BOOT_MEM_RAM); ++} ++ ++/* used by early printk */ ++void prom_putchar(char c) ++{ ++ volatile u8 *uart_lsr = (volatile u8 *)(UART0_BASE + OFF_LSR); ++ volatile u8 *uart_tdr = (volatile u8 *)(UART0_BASE + OFF_TDR); ++ ++ /* Wait for fifo to shift out some bytes */ ++ while ( !((*uart_lsr & (UARTLSR_TDRQ | UARTLSR_TEMT)) == 0x60) ); ++ ++ *uart_tdr = (u8)c; ++} ++ ++const char *get_system_type(void) ++{ ++ return "JZ4740"; ++} ++ ++EXPORT_SYMBOL(prom_getcmdline); ++EXPORT_SYMBOL(get_ethernet_addr); ++EXPORT_SYMBOL(str2eaddr); +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/reset.c linux-2.6.24.7/arch/mips/jz4740/reset.c +--- linux-2.6.24.7.old/arch/mips/jz4740/reset.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/reset.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,46 @@ ++/* ++ * linux/arch/mips/jz4740/reset.c ++ * ++ * JZ4740 reset routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++void jz_restart(char *command) ++{ ++ printk("Restarting after 4 ms\n"); ++ REG_WDT_TCSR = WDT_TCSR_PRESCALE4 | WDT_TCSR_EXT_EN; ++ REG_WDT_TCNT = 0; ++ REG_WDT_TDR = JZ_EXTAL/1000; /* reset after 4ms */ ++ REG_TCU_TSCR = TCU_TSSR_WDTSC; /* enable wdt clock */ ++ REG_WDT_TCER = WDT_TCER_TCEN; /* wdt start */ ++ while (1); ++} ++ ++void jz_halt(void) ++{ ++ printk(KERN_NOTICE "\n** You can safely turn off the power\n"); ++ ++ while (1) ++ __asm__(".set\tmips3\n\t" ++ "wait\n\t" ++ ".set\tmips0"); ++} ++ ++void jz_power_off(void) ++{ ++ jz_halt(); ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/setup.c linux-2.6.24.7/arch/mips/jz4740/setup.c +--- linux-2.6.24.7.old/arch/mips/jz4740/setup.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/setup.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,182 @@ ++/* ++ * linux/arch/mips/jz4740/common/setup.c ++ * ++ * JZ4740 common setup routines. ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_PC_KEYB ++#include ++#endif ++ ++jz_clocks_t jz_clocks; ++ ++extern char * __init prom_getcmdline(void); ++extern void __init jz_board_setup(void); ++extern void jz_restart(char *); ++extern void jz_halt(void); ++extern void jz_power_off(void); ++extern void jz_time_init(void); ++ ++static void __init sysclocks_setup(void) ++{ ++#ifndef CONFIG_MIPS_JZ_EMURUS /* FPGA */ ++ jz_clocks.cclk = __cpm_get_cclk(); ++ jz_clocks.hclk = __cpm_get_hclk(); ++ jz_clocks.pclk = __cpm_get_pclk(); ++ jz_clocks.mclk = __cpm_get_mclk(); ++ jz_clocks.lcdclk = __cpm_get_lcdclk(); ++ jz_clocks.pixclk = __cpm_get_pixclk(); ++ jz_clocks.i2sclk = __cpm_get_i2sclk(); ++ jz_clocks.usbclk = __cpm_get_usbclk(); ++ jz_clocks.mscclk = __cpm_get_mscclk(); ++ jz_clocks.extalclk = __cpm_get_extalclk(); ++ jz_clocks.rtcclk = __cpm_get_rtcclk(); ++#else ++ ++#define FPGACLK 8000000 ++ ++ jz_clocks.cclk = FPGACLK; ++ jz_clocks.hclk = FPGACLK; ++ jz_clocks.pclk = FPGACLK; ++ jz_clocks.mclk = FPGACLK; ++ jz_clocks.lcdclk = FPGACLK; ++ jz_clocks.pixclk = FPGACLK; ++ jz_clocks.i2sclk = FPGACLK; ++ jz_clocks.usbclk = FPGACLK; ++ jz_clocks.mscclk = FPGACLK; ++ jz_clocks.extalclk = FPGACLK; ++ jz_clocks.rtcclk = FPGACLK; ++#endif ++ ++ printk("CPU clock: %dMHz, System clock: %dMHz, Peripheral clock: %dMHz, Memory clock: %dMHz\n", ++ (jz_clocks.cclk + 500000) / 1000000, ++ (jz_clocks.hclk + 500000) / 1000000, ++ (jz_clocks.pclk + 500000) / 1000000, ++ (jz_clocks.mclk + 500000) / 1000000); ++} ++ ++static void __init soc_cpm_setup(void) ++{ ++ /* Start all module clocks ++ */ ++ __cpm_start_all(); ++ ++ /* Enable CKO to external memory */ ++ __cpm_enable_cko(); ++ ++ /* CPU enters IDLE mode when executing 'wait' instruction */ ++ __cpm_idle_mode(); ++ ++ /* Setup system clocks */ ++ sysclocks_setup(); ++} ++ ++static void __init soc_harb_setup(void) ++{ ++// __harb_set_priority(0x00); /* CIM>LCD>DMA>ETH>PCI>USB>CBB */ ++// __harb_set_priority(0x03); /* LCD>CIM>DMA>ETH>PCI>USB>CBB */ ++// __harb_set_priority(0x0a); /* ETH>LCD>CIM>DMA>PCI>USB>CBB */ ++} ++ ++static void __init soc_emc_setup(void) ++{ ++} ++ ++static void __init soc_dmac_setup(void) ++{ ++ __dmac_enable_module(); ++} ++ ++static void __init jz_soc_setup(void) ++{ ++ soc_cpm_setup(); ++ soc_harb_setup(); ++ soc_emc_setup(); ++ soc_dmac_setup(); ++} ++ ++static void __init jz_serial_setup(void) ++{ ++#ifdef CONFIG_SERIAL_8250 ++ struct uart_port s; ++ REG8(UART0_FCR) |= UARTFCR_UUE; /* enable UART module */ ++ memset(&s, 0, sizeof(s)); ++ s.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST; ++ s.iotype = SERIAL_IO_MEM; ++ s.regshift = 2; ++ s.uartclk = jz_clocks.extalclk ; ++ ++ s.line = 0; ++ s.membase = (u8 *)UART0_BASE; ++ s.irq = IRQ_UART0; ++ if (early_serial_setup(&s) != 0) { ++ printk(KERN_ERR "Serial ttyS0 setup failed!\n"); ++ } ++ ++ s.line = 1; ++ s.membase = (u8 *)UART1_BASE; ++ s.irq = IRQ_UART1; ++ if (early_serial_setup(&s) != 0) { ++ printk(KERN_ERR "Serial ttyS1 setup failed!\n"); ++ } ++#endif ++} ++ ++void __init plat_mem_setup(void) ++{ ++ char *argptr; ++ ++ argptr = prom_getcmdline(); ++ ++ /* IO/MEM resources. Which will be the addtion value in `inX' and ++ * `outX' macros defined in asm/io.h */ ++ set_io_port_base(0); ++ ioport_resource.start = 0x00000000; ++ ioport_resource.end = 0xffffffff; ++ iomem_resource.start = 0x00000000; ++ iomem_resource.end = 0xffffffff; ++ ++ _machine_restart = jz_restart; ++ _machine_halt = jz_halt; ++ pm_power_off = jz_power_off; ++ ++ jz_soc_setup(); ++ jz_serial_setup(); ++ jz_board_setup(); ++} ++ +diff -urN linux-2.6.24.7.old/arch/mips/jz4740/time.c linux-2.6.24.7/arch/mips/jz4740/time.c +--- linux-2.6.24.7.old/arch/mips/jz4740/time.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4740/time.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,158 @@ ++/* ++ * linux/arch/mips/jz4740/time.c ++ * ++ * Setting up the clock on the JZ4740 boards. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* This is for machines which generate the exact clock. */ ++ ++#define JZ_TIMER_CHAN 0 ++#define JZ_TIMER_IRQ IRQ_TCU0 ++ ++#define JZ_TIMER_CLOCK (JZ_EXTAL>>4) /* Jz timer clock frequency */ ++ ++static struct clocksource clocksource_jz; /* Jz clock source */ ++static struct clock_event_device jz_clockevent_device; /* Jz clock event */ ++ ++void (*jz_timer_callback)(void); ++ ++static irqreturn_t jz_timer_interrupt(int irq, void *dev_id) ++{ ++ struct clock_event_device *cd = dev_id; ++ ++ REG_TCU_TFCR = 1 << JZ_TIMER_CHAN; /* ACK timer */ ++ ++ if (jz_timer_callback) ++ jz_timer_callback(); ++ ++ cd->event_handler(cd); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct irqaction jz_irqaction = { ++ .handler = jz_timer_interrupt, ++ .flags = IRQF_DISABLED | IRQF_PERCPU | IRQF_TIMER, ++ .name = "jz-timerirq", ++}; ++ ++ ++cycle_t jz_get_cycles(void) ++{ ++ /* convert jiffes to jz timer cycles */ ++ return (cycle_t)( jiffies*((JZ_TIMER_CLOCK)/HZ) + REG_TCU_TCNT(JZ_TIMER_CHAN)); ++} ++ ++static struct clocksource clocksource_jz = { ++ .name = "jz_clocksource", ++ .rating = 300, ++ .read = jz_get_cycles, ++ .mask = 0xFFFF, ++ .shift = 10, ++ .flags = CLOCK_SOURCE_WATCHDOG, ++}; ++ ++static int __init jz_clocksource_init(void) ++{ ++ clocksource_jz.mult = clocksource_hz2mult(JZ_TIMER_CLOCK, clocksource_jz.shift); ++ clocksource_register(&clocksource_jz); ++ return 0; ++} ++ ++static int jz_set_next_event(unsigned long evt, ++ struct clock_event_device *unused) ++{ ++ return 0; ++} ++ ++static void jz_set_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt) ++{ ++ switch (mode) { ++ case CLOCK_EVT_MODE_PERIODIC: ++ break; ++ case CLOCK_EVT_MODE_ONESHOT: ++ case CLOCK_EVT_MODE_UNUSED: ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ break; ++ case CLOCK_EVT_MODE_RESUME: ++ break; ++ } ++} ++ ++static struct clock_event_device jz_clockevent_device = { ++ .name = "jz-clockenvent", ++ .features = CLOCK_EVT_FEAT_PERIODIC, ++// .features = CLOCK_EVT_FEAT_ONESHOT, /* Jz4740 not support dynamic clock now */ ++ ++ /* .mult, .shift, .max_delta_ns and .min_delta_ns left uninitialized */ ++ .rating = 300, ++ .irq = JZ_TIMER_IRQ, ++ .set_mode = jz_set_mode, ++ .set_next_event = jz_set_next_event, ++}; ++ ++static void __init jz_clockevent_init(void) ++{ ++ struct clock_event_device *cd = &jz_clockevent_device; ++ unsigned int cpu = smp_processor_id(); ++ ++ cd->cpumask = cpumask_of_cpu(cpu); ++ clockevents_register_device(cd); ++} ++ ++static void __init jz_timer_setup(void) ++{ ++ jz_clocksource_init(); /* init jz clock source */ ++ jz_clockevent_init(); /* init jz clock event */ ++ ++ /* ++ * Make irqs happen for the system timer ++ */ ++ jz_irqaction.dev_id = &jz_clockevent_device; ++ setup_irq(JZ_TIMER_IRQ, &jz_irqaction); ++} ++ ++ ++void __init plat_time_init(void) ++{ ++ unsigned int latch; ++ /* Init timer */ ++ latch = ( JZ_TIMER_CLOCK + (HZ>>1)) / HZ; ++ ++ REG_TCU_TCSR(JZ_TIMER_CHAN) = TCU_TCSR_PRESCALE16 | TCU_TCSR_EXT_EN; ++ REG_TCU_TCNT(JZ_TIMER_CHAN) = 0; ++ REG_TCU_TDHR(JZ_TIMER_CHAN) = 0; ++ REG_TCU_TDFR(JZ_TIMER_CHAN) = latch; ++ ++ REG_TCU_TMSR = (1 << (JZ_TIMER_CHAN + 16)); /* mask half irq */ ++ REG_TCU_TMCR = (1 << JZ_TIMER_CHAN); /* unmask full irq */ ++ REG_TCU_TSCR = (1 << JZ_TIMER_CHAN); /* enable timer clock */ ++ REG_TCU_TESR = (1 << JZ_TIMER_CHAN); /* start counting up */ ++ ++ jz_timer_setup(); ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4750/Makefile linux-2.6.24.7/arch/mips/jz4750/Makefile +--- linux-2.6.24.7.old/arch/mips/jz4750/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,23 @@ ++# ++# Makefile for the Ingenic JZ4750. ++# ++ ++# Object file lists. ++ ++obj-y += prom.o irq.o time.o reset.o setup.o dma.o \ ++ platform.o i2c.o ++ ++obj-$(CONFIG_PROC_FS) += proc.o ++ ++# board specific support ++ ++obj-$(CONFIG_JZ4750_FUWA) += board-fuwa.o ++obj-$(CONFIG_JZ4750_APUS) += board-apus.o ++ ++# PM support ++ ++obj-$(CONFIG_PM_LEGACY) +=pm.o ++ ++# CPU Frequency scaling support ++ ++obj-$(CONFIG_CPU_FREQ_JZ) +=cpufreq.o +diff -urN linux-2.6.24.7.old/arch/mips/jz4750/board-apus.c linux-2.6.24.7/arch/mips/jz4750/board-apus.c +--- linux-2.6.24.7.old/arch/mips/jz4750/board-apus.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750/board-apus.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,64 @@ ++/* ++ * linux/arch/mips/jz4750/board-apus.c ++ * ++ * JZ4750 APUS board setup routines. ++ * ++ * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++extern void (*jz_timer_callback)(void); ++ ++static void dancing(void) ++{ ++} ++ ++static void apus_timer_callback(void) ++{ ++ static unsigned long count = 0; ++ ++ if ((++count) % 50 == 0) { ++ dancing(); ++ count = 0; ++ } ++} ++ ++static void __init board_cpm_setup(void) ++{ ++ /* Stop unused module clocks here. ++ * We have started all module clocks at arch/mips/jz4750/setup.c. ++ */ ++} ++ ++static void __init board_gpio_setup(void) ++{ ++ __gpio_as_pcm(); ++} ++ ++void __init jz_board_setup(void) ++{ ++ printk("JZ4750 APUS board setup\n"); ++ ++ board_cpm_setup(); ++ board_gpio_setup(); ++ ++ jz_timer_callback = apus_timer_callback; ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4750/board-fuwa.c linux-2.6.24.7/arch/mips/jz4750/board-fuwa.c +--- linux-2.6.24.7.old/arch/mips/jz4750/board-fuwa.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750/board-fuwa.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,105 @@ ++/* ++ * linux/arch/mips/jz4750/board-fuwa.c ++ * ++ * JZ4750 FUWA board setup routines. ++ * ++ * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++extern void (*jz_timer_callback)(void); ++ ++static void dancing(void) ++{ ++ static unsigned char slash[] = "\\|/-"; ++// static volatile unsigned char *p = (unsigned char *)0xb6000058; ++ static volatile unsigned char *p = (unsigned char *)0xb6000016; ++ static unsigned int count = 0; ++ *p = slash[count++]; ++ count &= 3; ++} ++ ++static void fuwa_timer_callback(void) ++{ ++ static unsigned long count = 0; ++ ++ if ((++count) % 50 == 0) { ++ dancing(); ++ count = 0; ++ } ++} ++ ++static void __init board_cpm_setup(void) ++{ ++ /* Stop unused module clocks here. ++ * We have started all module clocks at arch/mips/jz4750/setup.c. ++ */ ++} ++ ++static void __init board_gpio_setup(void) ++{ ++ /* ++ * Initialize SDRAM pins ++ */ ++ ++ /* PORT A: D0 ~ D31 */ ++ REG_GPIO_PXFUNS(0) = 0xffffffff; ++ REG_GPIO_PXSELC(0) = 0xffffffff; ++ ++ /* PORT B: A0 ~ A16, DCS#, RAS#, CAS#, CKE#, RDWE#, CKO#, WE0# */ ++ REG_GPIO_PXFUNS(1) = 0x81f9ffff; ++ REG_GPIO_PXSELC(1) = 0x81f9ffff; ++ ++ /* PORT C: WE1#, WE2#, WE3# */ ++ REG_GPIO_PXFUNS(2) = 0x07000000; ++ REG_GPIO_PXSELC(2) = 0x07000000; ++ ++ ++ /* ++ * Initialize UART0 pins ++ */ ++ ++ /* PORT D: TXD/RXD */ ++ REG_GPIO_PXFUNS(3) = 0x06000000; ++ REG_GPIO_PXSELS(3) = 0x06000000; ++ ++ ++ /* ++ * Initialize LED pins ++ */ ++ __gpio_as_lcd_18bit(); ++ ++ /* CS2# */ ++ REG_GPIO_PXFUNS(1) = 0x04000000; ++ REG_GPIO_PXSELC(1) = 0x04000000; ++ ++ __gpio_as_pcm(); ++} ++ ++void __init jz_board_setup(void) ++{ ++ printk("JZ4750 FUWA board setup\n"); ++ ++ board_cpm_setup(); ++ board_gpio_setup(); ++ ++ jz_timer_callback = fuwa_timer_callback; ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4750/board-slt50.c linux-2.6.24.7/arch/mips/jz4750/board-slt50.c +--- linux-2.6.24.7.old/arch/mips/jz4750/board-slt50.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750/board-slt50.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,64 @@ ++/* ++ * linux/arch/mips/jz4750/board-apus.c ++ * ++ * JZ4750 APUS board setup routines. ++ * ++ * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++extern void (*jz_timer_callback)(void); ++ ++static void dancing(void) ++{ ++} ++ ++static void apus_timer_callback(void) ++{ ++ static unsigned long count = 0; ++ ++ if ((++count) % 50 == 0) { ++ dancing(); ++ count = 0; ++ } ++} ++ ++static void __init board_cpm_setup(void) ++{ ++ /* Stop unused module clocks here. ++ * We have started all module clocks at arch/mips/jz4750/setup.c. ++ */ ++} ++ ++static void __init board_gpio_setup(void) ++{ ++ __gpio_as_pcm(); ++} ++ ++void __init jz_board_setup(void) ++{ ++ printk("JZ4750 SLT_50 board setup\n"); ++ ++ board_cpm_setup(); ++ board_gpio_setup(); ++ ++ jz_timer_callback = apus_timer_callback; ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4750/cpufreq.c linux-2.6.24.7/arch/mips/jz4750/cpufreq.c +--- linux-2.6.24.7.old/arch/mips/jz4750/cpufreq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750/cpufreq.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,601 @@ ++/* ++ * linux/arch/mips/jz4750/cpufreq.c ++ * ++ * cpufreq driver for JZ4750 ++ * ++ * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ ++ "cpufreq-jz4750", msg) ++ ++#undef CHANGE_PLL ++ ++#define PLL_UNCHANGED 0 ++#define PLL_GOES_UP 1 ++#define PLL_GOES_DOWN 2 ++ ++#define PLL_WAIT_500NS (500*(__cpm_get_cclk()/1000000000)) ++ ++/* Saved the boot-time parameters */ ++static struct { ++ /* SDRAM parameters */ ++ unsigned int mclk; /* memory clock, KHz */ ++ unsigned int tras; /* RAS pulse width, cycles of mclk */ ++ unsigned int rcd; /* RAS to CAS Delay, cycles of mclk */ ++ unsigned int tpc; /* RAS Precharge time, cycles of mclk */ ++ unsigned int trwl; /* Write Precharge Time, cycles of mclk */ ++ unsigned int trc; /* RAS Cycle Time, cycles of mclk */ ++ unsigned int rtcor; /* Refresh Time Constant */ ++ unsigned int sdram_initialized; ++ ++ /* LCD parameters */ ++ unsigned int lcd_clk; /* LCD clock, Hz */ ++ unsigned int lcdpix_clk; /* LCD Pixel clock, Hz */ ++ unsigned int lcd_clks_initialized; ++} boot_config; ++ ++struct jz4750_freq_percpu_info { ++ struct cpufreq_frequency_table table[7]; ++}; ++ ++static struct jz4750_freq_percpu_info jz4750_freq_table; ++ ++/* ++ * This contains the registers value for an operating point. ++ * If only part of a register needs to change then there is ++ * a mask value for that register. ++ * When going to a new operating point the current register ++ * value is ANDed with the ~mask and ORed with the new value. ++ */ ++struct dpm_regs { ++ u32 cpccr; /* Clock Freq Control Register */ ++ u32 cpccr_mask; /* Clock Freq Control Register mask */ ++ u32 cppcr; /* PLL1 Control Register */ ++ u32 cppcr_mask; /* PLL1 Control Register mask */ ++ u32 pll_up_flag; /* New PLL freq is higher than current or not */ ++}; ++ ++extern jz_clocks_t jz_clocks; ++ ++static void jz_update_clocks(void) ++{ ++ /* Next clocks must be updated if we have changed ++ * the PLL or divisors. ++ */ ++ jz_clocks.cclk = __cpm_get_cclk(); ++ jz_clocks.hclk = __cpm_get_hclk(); ++ jz_clocks.mclk = __cpm_get_mclk(); ++ jz_clocks.pclk = __cpm_get_pclk(); ++ jz_clocks.lcdclk = __cpm_get_lcdclk(); ++ jz_clocks.pixclk = __cpm_get_pixclk(); ++ jz_clocks.i2sclk = __cpm_get_i2sclk(); ++ jz_clocks.usbclk = __cpm_get_usbclk(); ++ jz_clocks.mscclk = __cpm_get_mscclk(0); ++} ++ ++static void ++jz_init_boot_config(void) ++{ ++ if (!boot_config.lcd_clks_initialized) { ++ /* the first time to scale pll */ ++ boot_config.lcd_clk = __cpm_get_lcdclk(); ++ boot_config.lcdpix_clk = __cpm_get_pixclk(); ++ boot_config.lcd_clks_initialized = 1; ++ } ++ ++ if (!boot_config.sdram_initialized) { ++ /* the first time to scale frequencies */ ++ unsigned int dmcr, rtcor; ++ unsigned int tras, rcd, tpc, trwl, trc; ++ ++ dmcr = REG_EMC_DMCR; ++ rtcor = REG_EMC_RTCOR; ++ ++ tras = (dmcr >> 13) & 0x7; ++ rcd = (dmcr >> 11) & 0x3; ++ tpc = (dmcr >> 8) & 0x7; ++ trwl = (dmcr >> 5) & 0x3; ++ trc = (dmcr >> 2) & 0x7; ++ ++ boot_config.mclk = __cpm_get_mclk() / 1000; ++ boot_config.tras = tras + 4; ++ boot_config.rcd = rcd + 1; ++ boot_config.tpc = tpc + 1; ++ boot_config.trwl = trwl + 1; ++ boot_config.trc = trc * 2 + 1; ++ boot_config.rtcor = rtcor; ++ ++ boot_config.sdram_initialized = 1; ++ } ++} ++ ++static void jz_update_dram_rtcor(unsigned int new_mclk) ++{ ++ unsigned int rtcor; ++ ++ new_mclk /= 1000; ++ rtcor = boot_config.rtcor * new_mclk / boot_config.mclk; ++ rtcor--; ++ ++ if (rtcor < 1) rtcor = 1; ++ if (rtcor > 255) rtcor = 255; ++ ++ REG_EMC_RTCOR = rtcor; ++ REG_EMC_RTCNT = rtcor; ++} ++ ++static void jz_update_dram_dmcr(unsigned int new_mclk) ++{ ++ unsigned int dmcr; ++ unsigned int tras, rcd, tpc, trwl, trc; ++ unsigned int valid_time, new_time; /* ns */ ++ ++ new_mclk /= 1000; ++ tras = boot_config.tras * new_mclk / boot_config.mclk; ++ rcd = boot_config.rcd * new_mclk / boot_config.mclk; ++ tpc = boot_config.tpc * new_mclk / boot_config.mclk; ++ trwl = boot_config.trwl * new_mclk / boot_config.mclk; ++ trc = boot_config.trc * new_mclk / boot_config.mclk; ++ ++ /* Validation checking */ ++ valid_time = (boot_config.tras * 1000000) / boot_config.mclk; ++ new_time = (tras * 1000000) / new_mclk; ++ if (new_time < valid_time) tras += 1; ++ ++ valid_time = (boot_config.rcd * 1000000) / boot_config.mclk; ++ new_time = (rcd * 1000000) / new_mclk; ++ if (new_time < valid_time) rcd += 1; ++ ++ valid_time = (boot_config.tpc * 1000000) / boot_config.mclk; ++ new_time = (tpc * 1000000) / new_mclk; ++ if (new_time < valid_time) tpc += 1; ++ ++ valid_time = (boot_config.trwl * 1000000) / boot_config.mclk; ++ new_time = (trwl * 1000000) / new_mclk; ++ if (new_time < valid_time) trwl += 1; ++ ++ valid_time = (boot_config.trc * 1000000) / boot_config.mclk; ++ new_time = (trc * 1000000) / new_mclk; ++ if (new_time < valid_time) trc += 2; ++ ++ tras = (tras < 4) ? 4: tras; ++ tras = (tras > 11) ? 11: tras; ++ tras -= 4; ++ ++ rcd = (rcd < 1) ? 1: rcd; ++ rcd = (rcd > 4) ? 4: rcd; ++ rcd -= 1; ++ ++ tpc = (tpc < 1) ? 1: tpc; ++ tpc = (tpc > 8) ? 8: tpc; ++ tpc -= 1; ++ ++ trwl = (trwl < 1) ? 1: trwl; ++ trwl = (trwl > 4) ? 4: trwl; ++ trwl -= 1; ++ ++ trc = (trc < 1) ? 1: trc; ++ trc = (trc > 15) ? 15: trc; ++ trc /= 2; ++ ++ dmcr = REG_EMC_DMCR; ++ ++ dmcr &= ~(EMC_DMCR_TRAS_MASK | EMC_DMCR_RCD_MASK | EMC_DMCR_TPC_MASK | EMC_DMCR_TRWL_MASK | EMC_DMCR_TRC_MASK); ++ dmcr |= ((tras << EMC_DMCR_TRAS_BIT) | (rcd << EMC_DMCR_RCD_BIT) | (tpc << EMC_DMCR_TPC_BIT) | (trwl << EMC_DMCR_TRWL_BIT) | (trc << EMC_DMCR_TRC_BIT)); ++ ++ REG_EMC_DMCR = dmcr; ++} ++ ++static void jz_update_dram_prev(unsigned int cur_mclk, unsigned int new_mclk) ++{ ++ /* No risk, no fun: run with interrupts on! */ ++ if (new_mclk > cur_mclk) { ++ /* We're going FASTER, so first update TRAS, RCD, TPC, TRWL ++ * and TRC of DMCR before changing the frequency. ++ */ ++ jz_update_dram_dmcr(new_mclk); ++ } else { ++ /* We're going SLOWER: first update RTCOR value ++ * before changing the frequency. ++ */ ++ jz_update_dram_rtcor(new_mclk); ++ } ++} ++ ++static void jz_update_dram_post(unsigned int cur_mclk, unsigned int new_mclk) ++{ ++ /* No risk, no fun: run with interrupts on! */ ++ if (new_mclk > cur_mclk) { ++ /* We're going FASTER, so update RTCOR ++ * after changing the frequency ++ */ ++ jz_update_dram_rtcor(new_mclk); ++ } else { ++ /* We're going SLOWER: so update TRAS, RCD, TPC, TRWL ++ * and TRC of DMCR after changing the frequency. ++ */ ++ jz_update_dram_dmcr(new_mclk); ++ } ++} ++ ++static void jz_scale_divisors(struct dpm_regs *regs) ++{ ++ unsigned int cpccr; ++ unsigned int cur_mclk, new_mclk; ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ unsigned int tmp = 0, wait = PLL_WAIT_500NS; ++ ++ cpccr = REG_CPM_CPCCR; ++ cpccr &= ~((unsigned long)regs->cpccr_mask); ++ cpccr |= regs->cpccr; ++ cpccr |= CPM_CPCCR_CE; /* update immediately */ ++ ++ cur_mclk = __cpm_get_mclk(); ++ new_mclk = __cpm_get_pllout() / div[(cpccr & CPM_CPCCR_MDIV_MASK) >> CPM_CPCCR_MDIV_BIT]; ++ ++ /* Update some DRAM parameters before changing frequency */ ++ jz_update_dram_prev(cur_mclk, new_mclk); ++ ++ /* update register to change the clocks. ++ * align this code to a cache line. ++ */ ++ __asm__ __volatile__( ++ ".set noreorder\n\t" ++ ".align 5\n" ++ "sw %1,0(%0)\n\t" ++ "li %3,0\n\t" ++ "1:\n\t" ++ "bne %3,%2,1b\n\t" ++ "addi %3, 1\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ ".set reorder\n\t" ++ : ++ : "r" (CPM_CPCCR), "r" (cpccr), "r" (wait), "r" (tmp)); ++ ++ /* Update some other DRAM parameters after changing frequency */ ++ jz_update_dram_post(cur_mclk, new_mclk); ++} ++ ++#ifdef CHANGE_PLL ++/* Maintain the LCD clock and pixel clock */ ++static void jz_scale_lcd_divisors(struct dpm_regs *regs) ++{ ++ unsigned int new_pll, new_lcd_div, new_lcdpix_div; ++ unsigned int cpccr; ++ unsigned int tmp = 0, wait = PLL_WAIT_500NS; ++ ++ if (!boot_config.lcd_clks_initialized) return; ++ ++ new_pll = __cpm_get_pllout(); ++ new_lcd_div = new_pll / boot_config.lcd_clk; ++ new_lcdpix_div = new_pll / boot_config.lcdpix_clk; ++ ++ if (new_lcd_div < 1) ++ new_lcd_div = 1; ++ if (new_lcd_div > 16) ++ new_lcd_div = 16; ++ ++ if (new_lcdpix_div < 1) ++ new_lcdpix_div = 1; ++ if (new_lcdpix_div > 512) ++ new_lcdpix_div = 512; ++ ++// REG_CPM_CPCCR2 = new_lcdpix_div - 1; ++ ++ cpccr = REG_CPM_CPCCR; ++ cpccr &= ~CPM_CPCCR_LDIV_MASK; ++ cpccr |= ((new_lcd_div - 1) << CPM_CPCCR_LDIV_BIT); ++ cpccr |= CPM_CPCCR_CE; /* update immediately */ ++ ++ /* update register to change the clocks. ++ * align this code to a cache line. ++ */ ++ __asm__ __volatile__( ++ ".set noreorder\n\t" ++ ".align 5\n" ++ "sw %1,0(%0)\n\t" ++ "li %3,0\n\t" ++ "1:\n\t" ++ "bne %3,%2,1b\n\t" ++ "addi %3, 1\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ ".set reorder\n\t" ++ : ++ : "r" (CPM_CPCCR), "r" (cpccr), "r" (wait), "r" (tmp)); ++} ++ ++static void jz_scale_pll(struct dpm_regs *regs) ++{ ++ unsigned int cppcr; ++ unsigned int cur_mclk, new_mclk, new_pll; ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ int od[] = {1, 2, 2, 4}; ++ ++ cppcr = REG_CPM_CPPCR; ++ cppcr &= ~(regs->cppcr_mask | CPM_CPPCR_PLLS | CPM_CPPCR_PLLEN | CPM_CPPCR_PLLST_MASK); ++ regs->cppcr &= ~CPM_CPPCR_PLLEN; ++ cppcr |= (regs->cppcr | 0xff); ++ ++ /* Update some DRAM parameters before changing frequency */ ++ new_pll = JZ_EXTAL * ((cppcr>>23)+2) / ((((cppcr>>18)&0x1f)+2) * od[(cppcr>>16)&0x03]); ++ cur_mclk = __cpm_get_mclk(); ++ new_mclk = new_pll / div[(REG_CPM_CPCCR>>16) & 0xf]; ++ ++ /* ++ * Update some SDRAM parameters ++ */ ++ jz_update_dram_prev(cur_mclk, new_mclk); ++ ++ /* ++ * Update PLL, align code to cache line. ++ */ ++ cppcr |= CPM_CPPCR_PLLEN; ++ __asm__ __volatile__( ++ ".set noreorder\n\t" ++ ".align 5\n" ++ "sw %1,0(%0)\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ ".set reorder\n\t" ++ : ++ : "r" (CPM_CPPCR), "r" (cppcr)); ++ ++ /* Update some other DRAM parameters after changing frequency */ ++ jz_update_dram_post(cur_mclk, new_mclk); ++} ++#endif ++ ++static void jz4750_transition(struct dpm_regs *regs) ++{ ++ /* ++ * Get and save some boot-time conditions. ++ */ ++ jz_init_boot_config(); ++ ++#ifdef CHANGE_PLL ++ /* ++ * Disable LCD before scaling pll. ++ * LCD and LCD pixel clocks should not be changed even if the PLL ++ * output frequency has been changed. ++ */ ++ REG_LCD_CTRL &= ~LCD_CTRL_ENA; ++ ++ /* ++ * Stop module clocks before scaling PLL ++ */ ++ __cpm_stop_eth(); ++ __cpm_stop_aic(1); ++ __cpm_stop_aic(2); ++#endif ++ ++ /* ... add more as necessary */ ++ ++ if (regs->pll_up_flag == PLL_GOES_UP) { ++ /* the pll frequency is going up, so change dividors first */ ++ jz_scale_divisors(regs); ++#ifdef CHANGE_PLL ++ jz_scale_pll(regs); ++#endif ++ } ++ else if (regs->pll_up_flag == PLL_GOES_DOWN) { ++ /* the pll frequency is going down, so change pll first */ ++#ifdef CHANGE_PLL ++ jz_scale_pll(regs); ++#endif ++ jz_scale_divisors(regs); ++ } ++ else { ++ /* the pll frequency is unchanged, so change divisors only */ ++ jz_scale_divisors(regs); ++ } ++ ++#ifdef CHANGE_PLL ++ /* ++ * Restart module clocks before scaling PLL ++ */ ++ __cpm_start_eth(); ++ __cpm_start_aic(1); ++ __cpm_start_aic(2); ++ ++ /* ... add more as necessary */ ++ ++ /* Scale the LCD divisors after scaling pll */ ++ if (regs->pll_up_flag != PLL_UNCHANGED) { ++ jz_scale_lcd_divisors(regs); ++ } ++ ++ /* Enable LCD controller */ ++ REG_LCD_CTRL &= ~LCD_CTRL_DIS; ++ REG_LCD_CTRL |= LCD_CTRL_ENA; ++#endif ++ ++ /* Update system clocks */ ++ jz_update_clocks(); ++} ++ ++extern unsigned int idle_times; ++static unsigned int jz4750_freq_get(unsigned int cpu) ++{ ++ return (__cpm_get_cclk() / 1000); ++} ++ ++static unsigned int index_to_divisor(unsigned int index, struct dpm_regs *regs) ++{ ++ int n2FR[33] = { ++ 0, 0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 0, 6, 0, 0, 0, ++ 7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, ++ 9 ++ }; ++ int div[4] = {1, 2, 2, 2}; /* divisors of I:S:P:M */ ++ unsigned int div_of_cclk, new_freq, i; ++ ++ regs->pll_up_flag = PLL_UNCHANGED; ++ regs->cpccr_mask = CPM_CPCCR_CDIV_MASK | CPM_CPCCR_HDIV_MASK | CPM_CPCCR_PDIV_MASK | CPM_CPCCR_MDIV_MASK; ++ ++ new_freq = jz4750_freq_table.table[index].frequency; ++ ++ do { ++ div_of_cclk = __cpm_get_pllout() / (1000 * new_freq); ++ } while (div_of_cclk==0); ++ ++ if(div_of_cclk == 1 || div_of_cclk == 2 || div_of_cclk == 4) { ++ for(i = 1; i<4; i++) { ++ div[i] = 3; ++ } ++ } else { ++ for(i = 1; i<4; i++) { ++ div[i] = 2; ++ } ++ } ++ ++ for(i = 0; i<4; i++) { ++ div[i] *= div_of_cclk; ++ } ++ ++ dprintk("divisors of I:S:P:M = %d:%d:%d:%d\n", div[0], div[1], div[2], div[3]); ++ ++ regs->cpccr = ++ (n2FR[div[0]] << CPM_CPCCR_CDIV_BIT) | ++ (n2FR[div[1]] << CPM_CPCCR_HDIV_BIT) | ++ (n2FR[div[2]] << CPM_CPCCR_PDIV_BIT) | ++ (n2FR[div[3]] << CPM_CPCCR_MDIV_BIT); ++ ++ return div_of_cclk; ++} ++ ++static void jz4750_set_cpu_divider_index(unsigned int cpu, unsigned int index) ++{ ++ unsigned long divisor, old_divisor; ++ struct cpufreq_freqs freqs; ++ struct dpm_regs regs; ++ ++ old_divisor = __cpm_get_pllout() / __cpm_get_cclk(); ++ divisor = index_to_divisor(index, ®s); ++ ++ freqs.old = __cpm_get_cclk() / 1000; ++ freqs.new = __cpm_get_pllout() / (1000 * divisor); ++ freqs.cpu = cpu; ++ ++ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); ++ ++ if (old_divisor != divisor) ++ jz4750_transition(®s); ++ ++ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); ++} ++ ++static int jz4750_freq_target(struct cpufreq_policy *policy, ++ unsigned int target_freq, ++ unsigned int relation) ++{ ++ unsigned int new_index = 0; ++ ++ if (cpufreq_frequency_table_target(policy, ++ &jz4750_freq_table.table[0], ++ target_freq, relation, &new_index)) ++ return -EINVAL; ++ ++ jz4750_set_cpu_divider_index(policy->cpu, new_index); ++ ++ dprintk("new frequency is %d KHz (REG_CPM_CPCCR:0x%x)\n", __cpm_get_cclk() / 1000, REG_CPM_CPCCR); ++ ++ return 0; ++} ++ ++static int jz4750_freq_verify(struct cpufreq_policy *policy) ++{ ++ return cpufreq_frequency_table_verify(policy, ++ &jz4750_freq_table.table[0]); ++} ++ ++static int __init jz4750_cpufreq_driver_init(struct cpufreq_policy *policy) ++{ ++ ++ struct cpufreq_frequency_table *table = &jz4750_freq_table.table[0]; ++ unsigned int MAX_FREQ; ++ ++ dprintk(KERN_INFO "Jz4750 cpufreq driver\n"); ++ ++ if (policy->cpu != 0) ++ return -EINVAL; ++ ++ policy->cur = MAX_FREQ = __cpm_get_cclk() / 1000; /* in kHz. Current and max frequency is determined by u-boot */ ++ policy->governor = CPUFREQ_DEFAULT_GOVERNOR; ++ ++ policy->cpuinfo.min_freq = MAX_FREQ/8; ++ policy->cpuinfo.max_freq = MAX_FREQ; ++ policy->cpuinfo.transition_latency = 100000; /* in 10^(-9) s = nanoseconds */ ++ ++ table[0].index = 0; ++ table[0].frequency = MAX_FREQ/8; ++ table[1].index = 1; ++ table[1].frequency = MAX_FREQ/6; ++ table[2].index = 2; ++ table[2].frequency = MAX_FREQ/4; ++ table[3].index = 3; ++ table[3].frequency = MAX_FREQ/3; ++ table[4].index = 4; ++ table[4].frequency = MAX_FREQ/2; ++ table[5].index = 5; ++ table[5].frequency = MAX_FREQ; ++ table[6].index = 6; ++ table[6].frequency = CPUFREQ_TABLE_END; ++ ++#ifdef CONFIG_CPU_FREQ_STAT_DETAILS ++ cpufreq_frequency_table_get_attr(table, policy->cpu); /* for showing /sys/devices/system/cpu/cpuX/cpufreq/stats/ */ ++#endif ++ ++ return cpufreq_frequency_table_cpuinfo(policy, table); ++} ++ ++static struct cpufreq_driver cpufreq_jz4750_driver = { ++// .flags = CPUFREQ_STICKY, ++ .init = jz4750_cpufreq_driver_init, ++ .verify = jz4750_freq_verify, ++ .target = jz4750_freq_target, ++ .get = jz4750_freq_get, ++ .name = "jz4750", ++}; ++ ++static int __init jz4750_cpufreq_init(void) ++{ ++ return cpufreq_register_driver(&cpufreq_jz4750_driver); ++} ++ ++static void __exit jz4750_cpufreq_exit(void) ++{ ++ cpufreq_unregister_driver(&cpufreq_jz4750_driver); ++} ++ ++module_init(jz4750_cpufreq_init); ++module_exit(jz4750_cpufreq_exit); ++ ++MODULE_AUTHOR("Regen "); ++MODULE_DESCRIPTION("cpufreq driver for Jz4750"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/arch/mips/jz4750/dma.c linux-2.6.24.7/arch/mips/jz4750/dma.c +--- linux-2.6.24.7.old/arch/mips/jz4750/dma.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750/dma.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,836 @@ ++/* ++ * linux/arch/mips/jz4750/dma.c ++ * ++ * Support functions for the JZ4750 internal DMA channels. ++ * No-descriptor transfer only. ++ * Descriptor transfer should also call jz_request_dma() to get a free ++ * channel and call jz_free_dma() to free the channel. And driver should ++ * build the DMA descriptor and setup the DMA channel by itself. ++ * ++ * Copyright (C) 2006 - 2008 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* ++ * A note on resource allocation: ++ * ++ * All drivers needing DMA channels, should allocate and release them ++ * through the public routines `jz_request_dma()' and `jz_free_dma()'. ++ * ++ * In order to avoid problems, all processes should allocate resources in ++ * the same sequence and release them in the reverse order. ++ * ++ * So, when allocating DMAs and IRQs, first allocate the DMA, then the IRQ. ++ * When releasing them, first release the IRQ, then release the DMA. The ++ * main reason for this order is that, if you are requesting the DMA buffer ++ * done interrupt, you won't know the irq number until the DMA channel is ++ * returned from jz_request_dma(). ++ */ ++ ++struct jz_dma_chan jz_dma_table[MAX_DMA_NUM] = { ++ {dev_id:DMA_ID_BCH_ENC,}, /* DMAC0 channel 0, reserved for BCH */ ++ {dev_id:-1,}, /* DMAC0 channel 1 */ ++ {dev_id:-1,}, /* DMAC0 channel 2 */ ++ {dev_id:-1,}, /* DMAC0 channel 3 */ ++ {dev_id:-1,}, /* DMAC0 channel 4 */ ++ {dev_id:-1,}, /* DMAC0 channel 5 */ ++ {dev_id:-1,}, /* DMAC1 channel 0 */ ++ {dev_id:-1,}, /* DMAC1 channel 1 */ ++ {dev_id:-1,}, /* DMAC1 channel 2 */ ++ {dev_id:-1,}, /* DMAC1 channel 3 */ ++ {dev_id:-1,}, /* DMAC1 channel 4 */ ++ {dev_id:-1,}, /* DMAC1 channel 5 */ ++}; ++ ++// Device FIFO addresses and default DMA modes ++static const struct { ++ unsigned int fifo_addr; ++ unsigned int dma_mode; ++ unsigned int dma_source; ++} dma_dev_table[DMA_ID_MAX] = { ++ {0, DMA_AUTOINIT, DMAC_DRSR_RS_EXT}, /* External request with DREQn */ ++ {0x18000000, DMA_AUTOINIT, DMAC_DRSR_RS_NAND}, /* NAND request */ ++ {CPHYSADDR(BCH_DR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_BCH_ENC}, ++ {CPHYSADDR(BCH_DR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_BCH_DEC}, ++ {0, DMA_AUTOINIT, DMAC_DRSR_RS_AUTO}, ++// {CPHYSADDR(TSSI_FIFO), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_TSSIIN}, ++ {CPHYSADDR(UART3_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART3OUT}, ++ {CPHYSADDR(UART3_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART3IN}, ++ {CPHYSADDR(UART2_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART2OUT}, ++ {CPHYSADDR(UART2_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART2IN}, ++ {CPHYSADDR(UART1_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART1OUT}, ++ {CPHYSADDR(UART1_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART1IN}, ++ {CPHYSADDR(UART0_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART0OUT}, ++ {CPHYSADDR(UART0_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART0IN}, ++ {CPHYSADDR(SSI_DR(0)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_SSI0OUT}, ++ {CPHYSADDR(SSI_DR(0)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SSI0IN}, ++ {CPHYSADDR(AIC_DR), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_AICOUT}, ++ {CPHYSADDR(AIC_DR), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_AICIN}, ++ {CPHYSADDR(MSC_TXFIFO(0)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_MSC0OUT}, ++ {CPHYSADDR(MSC_RXFIFO(0)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_MSC0IN}, ++ {0, DMA_AUTOINIT, DMAC_DRSR_RS_TCU}, ++ {SADC_TSDAT, DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SADC},/* Touch Screen Data Register */ ++ {CPHYSADDR(MSC_TXFIFO(1)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_MSC1OUT}, /* SSC1 TX */ ++ {CPHYSADDR(MSC_RXFIFO(1)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_MSC1IN}, /* SSC1 RX */ ++ {CPHYSADDR(SSI_DR(1)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_SSI1OUT}, ++ {CPHYSADDR(SSI_DR(1)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SSI1IN}, ++ {CPHYSADDR(PCM_DP), DMA_16BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_PMOUT}, ++ {CPHYSADDR(PCM_DP), DMA_16BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_PMIN}, ++ {}, ++}; ++ ++ ++int jz_dma_read_proc(char *buf, char **start, off_t fpos, ++ int length, int *eof, void *data) ++{ ++ int i, len = 0; ++ struct jz_dma_chan *chan; ++ ++ for (i = 0; i < MAX_DMA_NUM; i++) { ++ if ((chan = get_dma_chan(i)) != NULL) { ++ len += sprintf(buf + len, "%2d: %s\n", ++ i, chan->dev_str); ++ } ++ } ++ ++ if (fpos >= len) { ++ *start = buf; ++ *eof = 1; ++ return 0; ++ } ++ *start = buf + fpos; ++ if ((len -= fpos) > length) ++ return length; ++ *eof = 1; ++ return len; ++} ++ ++ ++void dump_jz_dma_channel(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan; ++ ++ if (dmanr > MAX_DMA_NUM) ++ return; ++ chan = &jz_dma_table[dmanr]; ++ ++ printk("DMA%d Registers:\n", dmanr); ++ printk(" DMACR = 0x%08x\n", REG_DMAC_DMACR(chan->io/HALF_DMA_NUM)); ++ printk(" DSAR = 0x%08x\n", REG_DMAC_DSAR(dmanr)); ++ printk(" DTAR = 0x%08x\n", REG_DMAC_DTAR(dmanr)); ++ printk(" DTCR = 0x%08x\n", REG_DMAC_DTCR(dmanr)); ++ printk(" DRSR = 0x%08x\n", REG_DMAC_DRSR(dmanr)); ++ printk(" DCCSR = 0x%08x\n", REG_DMAC_DCCSR(dmanr)); ++ printk(" DCMD = 0x%08x\n", REG_DMAC_DCMD(dmanr)); ++ printk(" DDA = 0x%08x\n", REG_DMAC_DDA(dmanr)); ++ printk(" DMADBR = 0x%08x\n", REG_DMAC_DMADBR(chan->io/HALF_DMA_NUM)); ++} ++ ++ ++/** ++ * jz_request_dma - dynamically allcate an idle DMA channel to return ++ * @dev_id: the specified dma device id or DMA_ID_RAW_SET ++ * @dev_str: the specified dma device string name ++ * @irqhandler: the irq handler, or NULL ++ * @irqflags: the irq handler flags ++ * @irq_dev_id: the irq handler device id for shared irq ++ * ++ * Finds a free channel, and binds the requested device to it. ++ * Returns the allocated channel number, or negative on error. ++ * Requests the DMA done IRQ if irqhandler != NULL. ++ * ++*/ ++/*int jz_request_dma(int dev_id, const char *dev_str, ++ void (*irqhandler)(int, void *, struct pt_regs *), ++ unsigned long irqflags, ++ void *irq_dev_id) ++*/ ++ ++int jz_request_dma(int dev_id, const char *dev_str, ++ irqreturn_t (*irqhandler)(int, void *), ++ unsigned long irqflags, ++ void *irq_dev_id) ++{ ++ struct jz_dma_chan *chan; ++ int i, ret, chan0; ++ ++ if (dev_id < 0 || dev_id >= DMA_ID_MAX) ++ return -EINVAL; ++ ++ /* Because of a bug in DMA controller of jz4750, which causes auto ++ request and device request can't be allocated in a same DMA ++ controller, all device requests should be put in the second DMA ++ controller ++ */ ++ if (dev_id > DMA_ID_AUTO) ++ chan0 = 6; ++ else ++ chan0 = 0; ++ ++ for (i = chan0; i < MAX_DMA_NUM; i++) { ++ if (jz_dma_table[i].dev_id < 0) ++ break; ++ } ++ if (i == MAX_DMA_NUM) /* no free channel */ ++ return -ENODEV; ++ ++ /* we got a free channel */ ++ chan = &jz_dma_table[i]; ++ ++ if (irqhandler) { ++ chan->irq = IRQ_DMA_0 + i; // allocate irq number ++ chan->irq_dev = irq_dev_id; ++ if ((ret = request_irq(chan->irq, irqhandler, irqflags, ++ dev_str, chan->irq_dev))) { ++ chan->irq = -1; ++ chan->irq_dev = NULL; ++ return ret; ++ } ++ } else { ++ chan->irq = -1; ++ chan->irq_dev = NULL; ++ } ++ ++ // fill it in ++ chan->io = i; ++ chan->dev_id = dev_id; ++ chan->dev_str = dev_str; ++ chan->fifo_addr = dma_dev_table[dev_id].fifo_addr; ++ chan->mode = dma_dev_table[dev_id].dma_mode; ++ chan->source = dma_dev_table[dev_id].dma_source; ++ ++ if (i < 6) ++ REG_DMAC_DMACKE(0) = 1 << i; ++ else ++ REG_DMAC_DMACKE(1) = 1 << (i - 6); ++ ++ return i; ++} ++ ++void jz_free_dma(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) { ++ printk("Trying to free DMA%d\n", dmanr); ++ return; ++ } ++ ++ disable_dma(dmanr); ++ if (chan->irq) ++ free_irq(chan->irq, chan->irq_dev); ++ ++ chan->irq = -1; ++ chan->irq_dev = NULL; ++ chan->dev_id = -1; ++} ++ ++void jz_set_dma_dest_width(int dmanr, int nbit) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ chan->mode &= ~DMAC_DCMD_DWDH_MASK; ++ switch (nbit) { ++ case 8: ++ chan->mode |= DMAC_DCMD_DWDH_8; ++ break; ++ case 16: ++ chan->mode |= DMAC_DCMD_DWDH_16; ++ break; ++ case 32: ++ chan->mode |= DMAC_DCMD_DWDH_32; ++ break; ++ } ++} ++ ++void jz_set_dma_src_width(int dmanr, int nbit) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ chan->mode &= ~DMAC_DCMD_SWDH_MASK; ++ switch (nbit) { ++ case 8: ++ chan->mode |= DMAC_DCMD_SWDH_8; ++ break; ++ case 16: ++ chan->mode |= DMAC_DCMD_SWDH_16; ++ break; ++ case 32: ++ chan->mode |= DMAC_DCMD_SWDH_32; ++ break; ++ } ++} ++ ++void jz_set_dma_block_size(int dmanr, int nbyte) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ chan->mode &= ~DMAC_DCMD_DS_MASK; ++ switch (nbyte) { ++ case 1: ++ chan->mode |= DMAC_DCMD_DS_8BIT; ++ break; ++ case 2: ++ chan->mode |= DMAC_DCMD_DS_16BIT; ++ break; ++ case 4: ++ chan->mode |= DMAC_DCMD_DS_32BIT; ++ break; ++ case 16: ++ chan->mode |= DMAC_DCMD_DS_16BYTE; ++ break; ++ case 32: ++ chan->mode |= DMAC_DCMD_DS_32BYTE; ++ break; ++ } ++} ++ ++unsigned int jz_get_dma_command(int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ return chan->mode; ++} ++ ++/** ++ * jz_set_dma_mode - do the raw settings for the specified DMA channel ++ * @dmanr: the specified DMA channel ++ * @mode: dma operate mode, DMA_MODE_READ or DMA_MODE_WRITE ++ * @dma_mode: dma raw mode ++ * @dma_source: dma raw request source ++ * @fifo_addr: dma raw device fifo address ++ * ++ * Ensure call jz_request_dma(DMA_ID_RAW_SET, ...) first, then call ++ * jz_set_dma_mode() rather than set_dma_mode() if you work with ++ * and external request dma device. ++ * ++ * NOTE: Don not dynamically allocate dma channel if one external request ++ * dma device will occupy this channel. ++*/ ++int jz_set_dma_mode(unsigned int dmanr, unsigned int mode, ++ unsigned int dma_mode, unsigned int dma_source, ++ unsigned int fifo_addr) ++{ ++ int dev_id, i; ++ struct jz_dma_chan *chan; ++ ++ if (dmanr > MAX_DMA_NUM) ++ return -ENODEV; ++ for (i = 0; i < MAX_DMA_NUM; i++) { ++ if (jz_dma_table[i].dev_id < 0) ++ break; ++ } ++ if (i == MAX_DMA_NUM) ++ return -ENODEV; ++ ++ chan = &jz_dma_table[dmanr]; ++ dev_id = chan->dev_id; ++ if (dev_id > 0) { ++ printk(KERN_DEBUG "%s sets the allocated DMA channel %d!\n", ++ __FUNCTION__, dmanr); ++ return -ENODEV; ++ } ++ ++ /* clone it from the dynamically allocated. */ ++ if (i != dmanr) { ++ chan->irq = jz_dma_table[i].irq; ++ chan->irq_dev = jz_dma_table[i].irq_dev; ++ chan->dev_str = jz_dma_table[i].dev_str; ++ jz_dma_table[i].irq = 0; ++ jz_dma_table[i].irq_dev = NULL; ++ jz_dma_table[i].dev_id = -1; ++ } ++ chan->dev_id = DMA_ID_RAW_SET; ++ chan->io = dmanr; ++ chan->fifo_addr = fifo_addr; ++ chan->mode = dma_mode; ++ chan->source = dma_source; ++ ++ set_dma_mode(dmanr, dma_mode); ++ ++ return dmanr; ++} ++ ++void enable_dma(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ REG_DMAC_DCCSR(dmanr) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); ++ REG_DMAC_DCCSR(dmanr) |= DMAC_DCCSR_NDES; /* No-descriptor transfer */ ++ __dmac_enable_channel(dmanr); ++ if (chan->irq) ++ __dmac_channel_enable_irq(dmanr); ++} ++ ++#define DMA_DISABLE_POLL 0x10000 ++ ++void disable_dma(unsigned int dmanr) ++{ ++ int i; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ if (!__dmac_channel_enabled(dmanr)) ++ return; ++ ++ for (i = 0; i < DMA_DISABLE_POLL; i++) ++ if (__dmac_channel_transmit_end_detected(dmanr)) ++ break; ++#if 0 ++ if (i == DMA_DISABLE_POLL) ++ printk(KERN_INFO "disable_dma: poll expired!\n"); ++#endif ++ ++ __dmac_disable_channel(dmanr); ++ if (chan->irq) ++ __dmac_channel_disable_irq(dmanr); ++} ++ ++/* Note: DMA_MODE_MASK is simulated by sw */ ++void set_dma_mode(unsigned int dmanr, unsigned int mode) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ if (mode == DMA_MODE_READ) { ++ chan->mode |= DMAC_DCMD_DAI; ++ chan->mode &= ~DMAC_DCMD_SAI; ++ } else if (mode == DMA_MODE_WRITE) { ++ chan->mode |= DMAC_DCMD_SAI; ++ chan->mode &= ~DMAC_DCMD_DAI; ++ } else { ++ printk(KERN_DEBUG "set_dma_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); ++ } ++ REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK; ++ REG_DMAC_DRSR(chan->io) = chan->source; ++} ++ ++void set_dma_addr(unsigned int dmanr, unsigned int phyaddr) ++{ ++ unsigned int mode; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ mode = chan->mode & DMA_MODE_MASK; ++ if (mode == DMA_MODE_READ) { ++ REG_DMAC_DSAR(chan->io) = chan->fifo_addr; ++ REG_DMAC_DTAR(chan->io) = phyaddr; ++ } else if (mode == DMA_MODE_WRITE) { ++ REG_DMAC_DSAR(chan->io) = phyaddr; ++ REG_DMAC_DTAR(chan->io) = chan->fifo_addr; ++ } else ++ printk(KERN_DEBUG "Driver should call set_dma_mode() ahead set_dma_addr()!\n"); ++} ++ ++void set_dma_count(unsigned int dmanr, unsigned int bytecnt) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ int dma_ds[] = {4, 1, 2, 16, 32}; ++ unsigned int ds; ++ ++ if (!chan) ++ return; ++ ++ ds = (chan->mode & DMAC_DCMD_DS_MASK) >> DMAC_DCMD_DS_BIT; ++ REG_DMAC_DTCR(chan->io) = bytecnt / dma_ds[ds]; // transfer count ++} ++ ++unsigned int get_dma_residue(unsigned int dmanr) ++{ ++ unsigned int count, ds; ++ int dma_ds[] = {4, 1, 2, 16, 32}; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 0; ++ ++ ds = (chan->mode & DMAC_DCMD_DS_MASK) >> DMAC_DCMD_DS_BIT; ++ count = REG_DMAC_DTCR(chan->io); ++ count = count * dma_ds[ds]; ++ ++ return count; ++} ++ ++void jz_set_oss_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ switch (audio_fmt) { ++ case AFMT_U8: ++ /* burst mode : 32BIT */ ++ break; ++ case AFMT_S16_LE: ++ /* burst mode : 16BYTE */ ++ if (mode == DMA_MODE_READ) { ++ chan->mode = DMA_AIC_32_16BYTE_RX_CMD | DMA_MODE_READ; ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCMD_DAI; ++ chan->mode &= ~DMAC_DCMD_SAI; ++ } else if (mode == DMA_MODE_WRITE) { ++ chan->mode = DMA_AIC_32_16BYTE_TX_CMD | DMA_MODE_WRITE; ++ //chan->mode = DMA_AIC_16BYTE_TX_CMD | DMA_MODE_WRITE; ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCMD_SAI; ++ chan->mode &= ~DMAC_DCMD_DAI; ++ } else ++ printk("oss_dma_burst_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); ++ ++ REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK; ++ REG_DMAC_DRSR(chan->io) = chan->source; ++ break; ++ } ++} ++ ++void jz_set_alsa_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ switch (audio_fmt) { ++ case 8: ++ /* SNDRV_PCM_FORMAT_S8 burst mode : 32BIT */ ++ break; ++ case 16: ++ /* SNDRV_PCM_FORMAT_S16_LE burst mode : 16BYTE */ ++ if (mode == DMA_MODE_READ) { ++ chan->mode = DMA_AIC_16BYTE_RX_CMD | DMA_MODE_READ; ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCMD_DAI; ++ chan->mode &= ~DMAC_DCMD_SAI; ++ } else if (mode == DMA_MODE_WRITE) { ++ chan->mode = DMA_AIC_16BYTE_TX_CMD | DMA_MODE_WRITE; ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCMD_SAI; ++ chan->mode &= ~DMAC_DCMD_DAI; ++ } else ++ printk("alsa_dma_burst_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); ++ ++ REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK; ++ REG_DMAC_DRSR(chan->io) = chan->source; ++ break; ++ } ++} ++ ++//#define JZ4750_DMAC_TEST_ENABLE ++#undef JZ4750_DMAC_TEST_ENABLE ++ ++#ifdef JZ4750_DMAC_TEST_ENABLE ++ ++/* ++ * DMA test: external address <--> external address ++ */ ++#define TEST_DMA_SIZE 16*1024 ++ ++static jz_dma_desc *dma_desc; ++ ++static int dma_chan; ++static dma_addr_t dma_desc_phys_addr; ++static unsigned int dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr; ++ ++static int dma_check_result(void *src, void *dst, int size) ++{ ++ unsigned int addr1, addr2, i, err = 0; ++ ++ addr1 = (unsigned int)src; ++ addr2 = (unsigned int)dst; ++ ++ for (i = 0; i < size; i += 4) { ++ if (*(volatile unsigned int *)addr1 != *(volatile unsigned int *)addr2) { ++ err++; ++ printk("wrong data at 0x%08x: src 0x%08x dst 0x%08x\n", addr2, *(volatile unsigned int *)addr1, *(volatile unsigned int *)addr2); ++ } ++ addr1 += 4; ++ addr2 += 4; ++ } ++ printk("check DMA result err=%d\n", err); ++ return err; ++} ++ ++static irqreturn_t jz4750_dma_irq(int irq, void *dev_id) ++{ ++ printk("jz4750_dma_irq %d\n", irq); ++ ++ ++ if (__dmac_channel_transmit_halt_detected(dma_chan)) { ++ printk("DMA HALT\n"); ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ __dmac_channel_clear_transmit_halt(dma_chan); ++ } ++ ++ if (__dmac_channel_address_error_detected(dma_chan)) { ++ printk("DMA ADDR ERROR\n"); ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ REG_DMAC_DSAR(dma_chan) = 0; /* clear source address register */ ++ REG_DMAC_DTAR(dma_chan) = 0; /* clear target address register */ ++ __dmac_channel_clear_address_error(dma_chan); ++ } ++ ++ if (__dmac_channel_descriptor_invalid_detected(dma_chan)) { ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ printk("DMA DESC INVALID\n"); ++ __dmac_channel_clear_descriptor_invalid(dma_chan); ++ } ++ ++ if (__dmac_channel_count_terminated_detected(dma_chan)) { ++ printk("DMA CT\n"); ++ __dmac_channel_clear_count_terminated(dma_chan); ++ } ++ ++ if (__dmac_channel_transmit_end_detected(dma_chan)) { ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ printk("DMA TT\n"); ++ __dmac_channel_clear_transmit_end(dma_chan); ++ dump_jz_dma_channel(dma_chan); ++ dma_check_result((void *)dma_src_addr, (void *)dma_dst_addr, TEST_DMA_SIZE); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++void dma_nodesc_test(void) ++{ ++ unsigned int addr, i; ++ ++ printk("dma_nodesc_test\n"); ++ ++ /* Request DMA channel and setup irq handler */ ++ dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", jz4750_dma_irq, ++ IRQF_DISABLED, NULL); ++ if (dma_chan < 0) { ++ printk("Setup irq failed\n"); ++ return; ++ } ++ ++ printk("Requested DMA channel = %d\n", dma_chan); ++ ++ /* Allocate DMA buffers */ ++ dma_src_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ ++ dma_dst_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ ++ ++ dma_src_phys_addr = CPHYSADDR(dma_src_addr); ++ dma_dst_phys_addr = CPHYSADDR(dma_dst_addr); ++ ++ printk("Buffer addresses: 0x%08x 0x%08x 0x%08x 0x%08x\n", ++ dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr); ++ ++ /* Prepare data for source buffer */ ++ addr = (unsigned int)dma_src_addr; ++ for (i = 0; i < TEST_DMA_SIZE; i += 4) { ++ *(volatile unsigned int *)addr = addr; ++ addr += 4; ++ } ++ dma_cache_wback((unsigned long)dma_src_addr, TEST_DMA_SIZE); ++ ++ /* Init target buffer */ ++ memset((void *)dma_dst_addr, 0, TEST_DMA_SIZE); ++ dma_cache_wback((unsigned long)dma_dst_addr, TEST_DMA_SIZE); ++ ++ /* Init DMA module */ ++ printk("Starting DMA\n"); ++ REG_DMAC_DMACR(dma_chan/HALF_DMA_NUM) = 0; ++ REG_DMAC_DCCSR(dma_chan) = 0; ++ REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO; ++ REG_DMAC_DSAR(dma_chan) = dma_src_phys_addr; ++ REG_DMAC_DTAR(dma_chan) = dma_dst_phys_addr; ++ REG_DMAC_DTCR(dma_chan) = 512; ++ REG_DMAC_DCMD(dma_chan) = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_TIE; ++ REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; ++ REG_DMAC_DMACR(dma_chan/HALF_DMA_NUM) = DMAC_DMACR_DMAE; /* global DMA enable bit */ ++ ++ printk("DMA started. IMR=%08x\n", REG_INTC_IMR); ++ ++ /* wait a long time, ensure transfer end */ ++ printk("wait 3s...\n"); ++ mdelay(3000); /* wait 3s */ ++ ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ /* free buffers */ ++ printk("free DMA buffers\n"); ++ free_pages(dma_src_addr, 2); ++ free_pages(dma_dst_addr, 2); ++ ++ if (dma_desc) ++ free_pages((unsigned int)dma_desc, 0); ++ ++ /* free dma */ ++ jz_free_dma(dma_chan); ++} ++ ++void dma_desc_test(void) ++{ ++ unsigned int next, addr, i; ++ static jz_dma_desc *desc; ++ ++ printk("dma_desc_test\n"); ++ ++ /* Request DMA channel and setup irq handler */ ++ dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", jz4750_dma_irq, ++ IRQF_DISABLED, NULL); ++ if (dma_chan < 0) { ++ printk("Setup irq failed\n"); ++ return; ++ } ++ ++ printk("Requested DMA channel = %d\n", dma_chan); ++ ++ /* Allocate DMA buffers */ ++ dma_src_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ ++ dma_dst_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ ++ ++ dma_src_phys_addr = CPHYSADDR(dma_src_addr); ++ dma_dst_phys_addr = CPHYSADDR(dma_dst_addr); ++ ++ printk("Buffer addresses: 0x%08x 0x%08x 0x%08x 0x%08x\n", ++ dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr); ++ ++ /* Prepare data for source buffer */ ++ addr = (unsigned int)dma_src_addr; ++ for (i = 0; i < TEST_DMA_SIZE; i += 4) { ++ *(volatile unsigned int *)addr = addr; ++ addr += 4; ++ } ++ dma_cache_wback((unsigned long)dma_src_addr, TEST_DMA_SIZE); ++ ++ /* Init target buffer */ ++ memset((void *)dma_dst_addr, 0, TEST_DMA_SIZE); ++ dma_cache_wback((unsigned long)dma_dst_addr, TEST_DMA_SIZE); ++ ++ /* Allocate DMA descriptors */ ++ dma_desc = (jz_dma_desc *)__get_free_pages(GFP_KERNEL, 0); ++ dma_desc_phys_addr = CPHYSADDR((unsigned long)dma_desc); ++ ++ printk("DMA descriptor address: 0x%08x 0x%08x\n", (u32)dma_desc, dma_desc_phys_addr); ++ ++ /* Setup DMA descriptors */ ++ desc = dma_desc; ++ next = (dma_desc_phys_addr + (sizeof(jz_dma_desc))) >> 4; ++ ++ desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK; ++ desc->dsadr = dma_src_phys_addr; /* DMA source address */ ++ desc->dtadr = dma_dst_phys_addr; /* DMA target address */ ++ desc->ddadr = (next << 24) + 128; /* size: 128*32 bytes = 4096 bytes */ ++ ++ desc++; ++ next = (dma_desc_phys_addr + 2*(sizeof(jz_dma_desc))) >> 4; ++ ++ desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK; ++ desc->dsadr = dma_src_phys_addr + 4096; /* DMA source address */ ++ desc->dtadr = dma_dst_phys_addr + 4096; /* DMA target address */ ++ desc->ddadr = (next << 24) + 256; /* size: 256*16 bytes = 4096 bytes */ ++ ++ desc++; ++ next = (dma_desc_phys_addr + 3*(sizeof(jz_dma_desc))) >> 4; ++ ++ desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK; ++ desc->dsadr = dma_src_phys_addr + 8192; /* DMA source address */ ++ desc->dtadr = dma_dst_phys_addr + 8192; /* DMA target address */ ++ desc->ddadr = (next << 24) + 256; /* size: 256*16 bytes = 4096 bytes */ ++ ++ desc++; ++ next = (dma_desc_phys_addr + 4*(sizeof(jz_dma_desc))) >> 4; ++ ++ desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE; ++ desc->dsadr = dma_src_phys_addr + 12*1024; /* DMA source address */ ++ desc->dtadr = dma_dst_phys_addr + 12*1024; /* DMA target address */ ++ desc->ddadr = (next << 24) + 1024; /* size: 1024*4 bytes = 4096 bytes */ ++ ++ dma_cache_wback((unsigned long)dma_desc, 4*(sizeof(jz_dma_desc))); ++ ++ /* Setup DMA descriptor address */ ++ REG_DMAC_DDA(dma_chan) = dma_desc_phys_addr; ++ ++ /* Setup request source */ ++ REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO; ++ ++ /* Setup DMA channel control/status register */ ++ REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_EN; /* descriptor transfer, clear status, start channel */ ++ ++ /* Enable DMA */ ++ REG_DMAC_DMACR(dma_chan/HALF_DMA_NUM) = DMAC_DMACR_DMAE; ++ ++ /* DMA doorbell set -- start DMA now ... */ ++ REG_DMAC_DMADBSR(dma_chan/HALF_DMA_NUM) = 1 << dma_chan; ++ ++ printk("DMA started. IMR=%08x\n", REG_INTC_IMR); ++ /* wait a long time, ensure transfer end */ ++ printk("wait 3s...\n"); ++ mdelay(3000); /* wait 3s */ ++ ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ /* free buffers */ ++ printk("free DMA buffers\n"); ++ free_pages(dma_src_addr, 2); ++ free_pages(dma_dst_addr, 2); ++ ++ if (dma_desc) ++ free_pages((unsigned int)dma_desc, 0); ++ ++ /* free dma */ ++ jz_free_dma(dma_chan); ++} ++ ++#endif ++ ++//EXPORT_SYMBOL_NOVERS(jz_dma_table); ++EXPORT_SYMBOL(jz_dma_table); ++EXPORT_SYMBOL(jz_request_dma); ++EXPORT_SYMBOL(jz_free_dma); ++EXPORT_SYMBOL(jz_set_dma_src_width); ++EXPORT_SYMBOL(jz_set_dma_dest_width); ++EXPORT_SYMBOL(jz_set_dma_block_size); ++EXPORT_SYMBOL(jz_set_dma_mode); ++EXPORT_SYMBOL(set_dma_mode); ++EXPORT_SYMBOL(jz_set_oss_dma); ++EXPORT_SYMBOL(jz_set_alsa_dma); ++EXPORT_SYMBOL(set_dma_addr); ++EXPORT_SYMBOL(set_dma_count); ++EXPORT_SYMBOL(get_dma_residue); ++EXPORT_SYMBOL(enable_dma); ++EXPORT_SYMBOL(disable_dma); ++EXPORT_SYMBOL(dump_jz_dma_channel); +diff -urN linux-2.6.24.7.old/arch/mips/jz4750/i2c.c linux-2.6.24.7/arch/mips/jz4750/i2c.c +--- linux-2.6.24.7.old/arch/mips/jz4750/i2c.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750/i2c.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,273 @@ ++/* ++ * linux/arch/mips/jz4750/i2c.c ++ * ++ * Jz4750 I2C routines. ++ * ++ * Copyright (C) 2005,2006 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* I2C protocol */ ++#define I2C_READ 1 ++#define I2C_WRITE 0 ++ ++#define TIMEOUT 1000 ++ ++/* ++ * I2C bus protocol basic routines ++ */ ++static int i2c_put_data(unsigned char data) ++{ ++ unsigned int timeout = TIMEOUT*10; ++ ++ __i2c_write(data); ++ __i2c_set_drf(); ++ while (__i2c_check_drf() != 0); ++ while (!__i2c_transmit_ended()); ++ while (!__i2c_received_ack() && timeout) ++ timeout--; ++ ++ if (timeout) ++ return 0; ++ else ++ return -ETIMEDOUT; ++} ++ ++#ifdef CONFIG_JZ_TPANEL_ATA2508 ++static int i2c_put_data_nack(unsigned char data) ++{ ++ unsigned int timeout = TIMEOUT*10; ++ ++ __i2c_write(data); ++ __i2c_set_drf(); ++ while (__i2c_check_drf() != 0); ++ while (!__i2c_transmit_ended()); ++ while (timeout--); ++ return 0; ++} ++#endif ++ ++static int i2c_get_data(unsigned char *data, int ack) ++{ ++ int timeout = TIMEOUT*10; ++ ++ if (!ack) ++ __i2c_send_nack(); ++ else ++ __i2c_send_ack(); ++ ++ while (__i2c_check_drf() == 0 && timeout) ++ timeout--; ++ ++ if (timeout) { ++ if (!ack) ++ __i2c_send_stop(); ++ *data = __i2c_read(); ++ __i2c_clear_drf(); ++ return 0; ++ } else ++ return -ETIMEDOUT; ++} ++ ++/* ++ * I2C interface ++ */ ++void i2c_open(void) ++{ ++ __i2c_set_clk(jz_clocks.extalclk, 10000); /* default 10 KHz */ ++ __i2c_enable(); ++} ++ ++void i2c_close(void) ++{ ++ udelay(300); /* wait for STOP goes over. */ ++ __i2c_disable(); ++} ++ ++void i2c_setclk(unsigned int i2cclk) ++{ ++ __i2c_set_clk(jz_clocks.extalclk, i2cclk); ++} ++ ++int i2c_lseek(unsigned char device, unsigned char offset) ++{ ++ __i2c_send_nack(); /* Master does not send ACK, slave sends it */ ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) ++ goto device_err; ++ if (i2c_put_data(offset) < 0) ++ goto address_err; ++ return 0; ++ device_err: ++ printk(KERN_DEBUG "No I2C device (0x%02x) installed.\n", device); ++ __i2c_send_stop(); ++ return -ENODEV; ++ address_err: ++ printk(KERN_DEBUG "No I2C device (0x%02x) response.\n", device); ++ __i2c_send_stop(); ++ return -EREMOTEIO; ++} ++ ++int i2c_read(unsigned char device, unsigned char *buf, ++ unsigned char address, int count) ++{ ++ int cnt = count; ++ int timeout = 5; ++ ++L_try_again: ++ ++ if (timeout < 0) ++ goto L_timeout; ++ ++ __i2c_send_nack(); /* Master does not send ACK, slave sends it */ ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) ++ goto device_werr; ++ if (i2c_put_data(address) < 0) ++ goto address_err; ++ ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_READ ) < 0) ++ goto device_rerr; ++ __i2c_send_ack(); /* Master sends ACK for continue reading */ ++ while (cnt) { ++ if (cnt == 1) { ++ if (i2c_get_data(buf, 0) < 0) ++ break; ++ } else { ++ if (i2c_get_data(buf, 1) < 0) ++ break; ++ } ++ cnt--; ++ buf++; ++ } ++ ++ __i2c_send_stop(); ++ return count - cnt; ++ device_rerr: ++ device_werr: ++ address_err: ++ timeout --; ++ __i2c_send_stop(); ++ goto L_try_again; ++ ++L_timeout: ++ __i2c_send_stop(); ++ printk("Read I2C device 0x%2x failed.\n", device); ++ return -ENODEV; ++} ++ ++int i2c_write(unsigned char device, unsigned char *buf, ++ unsigned char address, int count) ++{ ++ int cnt = count; ++ int cnt_in_pg; ++ int timeout = 5; ++ unsigned char *tmpbuf; ++ unsigned char tmpaddr; ++ ++ __i2c_send_nack(); /* Master does not send ACK, slave sends it */ ++ ++ W_try_again: ++ if (timeout < 0) ++ goto W_timeout; ++ ++ cnt = count; ++ tmpbuf = (unsigned char *)buf; ++ tmpaddr = address; ++ ++ start_write_page: ++ cnt_in_pg = 0; ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) ++ goto device_err; ++#ifdef CONFIG_JZ_TPANEL_ATA2508 ++ if (address == 0xff) { ++ if (i2c_put_data_nack(tmpaddr) < 0) ++ goto address_err; ++ while (cnt) { ++ if (++cnt_in_pg > 8) { ++ __i2c_send_stop(); ++ mdelay(1); ++ tmpaddr += 8; ++ goto start_write_page; ++ } ++ if (i2c_put_data_nack(*tmpbuf) < 0) ++ break; ++ cnt--; ++ tmpbuf++; ++ } ++ } ++ else { ++ ++ if (i2c_put_data(tmpaddr) < 0) ++ goto address_err; ++ while (cnt) { ++ if (++cnt_in_pg > 8) { ++ __i2c_send_stop(); ++ mdelay(1); ++ tmpaddr += 8; ++ goto start_write_page; ++ } ++ if (i2c_put_data(*tmpbuf) < 0) ++ break; ++ cnt--; ++ tmpbuf++; ++ } ++ } ++#else ++ if (i2c_put_data(tmpaddr) < 0) ++ goto address_err; ++ while (cnt) { ++ if (++cnt_in_pg > 8) { ++ __i2c_send_stop(); ++ mdelay(1); ++ tmpaddr += 8; ++ goto start_write_page; ++ } ++ if (i2c_put_data(*tmpbuf) < 0) ++ break; ++ cnt--; ++ tmpbuf++; ++ } ++#endif ++ __i2c_send_stop(); ++ return count - cnt; ++ device_err: ++ address_err: ++ timeout--; ++ __i2c_send_stop(); ++ goto W_try_again; ++ ++ W_timeout: ++ printk(KERN_DEBUG "Write I2C device 0x%2x failed.\n", device); ++ __i2c_send_stop(); ++ return -ENODEV; ++} ++ ++EXPORT_SYMBOL(i2c_open); ++EXPORT_SYMBOL(i2c_close); ++EXPORT_SYMBOL(i2c_setclk); ++EXPORT_SYMBOL(i2c_read); ++EXPORT_SYMBOL(i2c_write); +diff -urN linux-2.6.24.7.old/arch/mips/jz4750/irq.c linux-2.6.24.7/arch/mips/jz4750/irq.c +--- linux-2.6.24.7.old/arch/mips/jz4750/irq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750/irq.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,299 @@ ++/* ++ * linux/arch/mips/jz4750/irq.c ++ * ++ * JZ4750 interrupt routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * INTC irq type ++ */ ++ ++static void enable_intc_irq(unsigned int irq) ++{ ++ __intc_unmask_irq(irq); ++} ++ ++static void disable_intc_irq(unsigned int irq) ++{ ++ __intc_mask_irq(irq); ++} ++ ++static void mask_and_ack_intc_irq(unsigned int irq) ++{ ++ __intc_mask_irq(irq); ++ __intc_ack_irq(irq); ++} ++ ++static void end_intc_irq(unsigned int irq) ++{ ++ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { ++ enable_intc_irq(irq); ++ } ++} ++ ++static unsigned int startup_intc_irq(unsigned int irq) ++{ ++ enable_intc_irq(irq); ++ return 0; ++} ++ ++static void shutdown_intc_irq(unsigned int irq) ++{ ++ disable_intc_irq(irq); ++} ++ ++static struct irq_chip intc_irq_type = { ++ .typename = "INTC", ++ .startup = startup_intc_irq, ++ .shutdown = shutdown_intc_irq, ++ .enable = enable_intc_irq, ++ .disable = disable_intc_irq, ++ .ack = mask_and_ack_intc_irq, ++ .end = end_intc_irq, ++}; ++ ++/* ++ * GPIO irq type ++ */ ++ ++static void enable_gpio_irq(unsigned int irq) ++{ ++ unsigned int intc_irq; ++ ++ if (irq < (IRQ_GPIO_0 + 32)) { ++ intc_irq = IRQ_GPIO0; ++ } ++ else if (irq < (IRQ_GPIO_0 + 64)) { ++ intc_irq = IRQ_GPIO1; ++ } ++ else if (irq < (IRQ_GPIO_0 + 96)) { ++ intc_irq = IRQ_GPIO2; ++ } ++ else if (irq < (IRQ_GPIO_0 + 128)) { ++ intc_irq = IRQ_GPIO3; ++ } ++ else if (irq < (IRQ_GPIO_0 + 160)) { ++ intc_irq = IRQ_GPIO4; ++ } ++ else { ++ intc_irq = IRQ_GPIO5; ++ } ++ ++ enable_intc_irq(intc_irq); ++ __gpio_unmask_irq(irq - IRQ_GPIO_0); ++} ++ ++static void disable_gpio_irq(unsigned int irq) ++{ ++ __gpio_mask_irq(irq - IRQ_GPIO_0); ++} ++ ++static void mask_and_ack_gpio_irq(unsigned int irq) ++{ ++ __gpio_mask_irq(irq - IRQ_GPIO_0); ++ __gpio_ack_irq(irq - IRQ_GPIO_0); ++} ++ ++static void end_gpio_irq(unsigned int irq) ++{ ++ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { ++ enable_gpio_irq(irq); ++ } ++} ++ ++static unsigned int startup_gpio_irq(unsigned int irq) ++{ ++ enable_gpio_irq(irq); ++ return 0; ++} ++ ++static void shutdown_gpio_irq(unsigned int irq) ++{ ++ disable_gpio_irq(irq); ++} ++ ++static struct irq_chip gpio_irq_type = { ++ .typename = "GPIO", ++ .startup = startup_gpio_irq, ++ .shutdown = shutdown_gpio_irq, ++ .enable = enable_gpio_irq, ++ .disable = disable_gpio_irq, ++ .ack = mask_and_ack_gpio_irq, ++ .end = end_gpio_irq, ++}; ++ ++/* ++ * DMA irq type ++ */ ++ ++static void enable_dma_irq(unsigned int irq) ++{ ++ unsigned int intc_irq; ++ ++ if ( irq < (IRQ_DMA_0 + HALF_DMA_NUM) ) /* DMAC Group 0 irq */ ++ intc_irq = IRQ_DMAC0; ++ else if ( irq < (IRQ_DMA_0 + MAX_DMA_NUM) ) /* DMAC Group 1 irq */ ++ intc_irq = IRQ_DMAC1; ++ else { ++ printk("%s, unexpected dma irq #%d\n", __FILE__, irq); ++ return; ++ } ++ __intc_unmask_irq(intc_irq); ++ __dmac_channel_enable_irq(irq - IRQ_DMA_0); ++} ++ ++static void disable_dma_irq(unsigned int irq) ++{ ++ __dmac_channel_disable_irq(irq - IRQ_DMA_0); ++} ++ ++static void mask_and_ack_dma_irq(unsigned int irq) ++{ ++ unsigned int intc_irq; ++ ++ if ( irq < (IRQ_DMA_0 + HALF_DMA_NUM) ) /* DMAC Group 0 irq */ ++ intc_irq = IRQ_DMAC0; ++ else if ( irq < (IRQ_DMA_0 + MAX_DMA_NUM) ) /* DMAC Group 1 irq */ ++ intc_irq = IRQ_DMAC1; ++ else { ++ printk("%s, unexpected dma irq #%d\n", __FILE__, irq); ++ return ; ++ } ++ __intc_ack_irq(intc_irq); ++ __dmac_channel_ack_irq(irq-IRQ_DMA_0); /* needed?? add 20080506, Wolfgang */ ++ __dmac_channel_disable_irq(irq - IRQ_DMA_0); ++} ++ ++static void end_dma_irq(unsigned int irq) ++{ ++ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { ++ enable_dma_irq(irq); ++ } ++} ++ ++static unsigned int startup_dma_irq(unsigned int irq) ++{ ++ enable_dma_irq(irq); ++ return 0; ++} ++ ++static void shutdown_dma_irq(unsigned int irq) ++{ ++ disable_dma_irq(irq); ++} ++ ++static struct irq_chip dma_irq_type = { ++ .typename = "DMA", ++ .startup = startup_dma_irq, ++ .shutdown = shutdown_dma_irq, ++ .enable = enable_dma_irq, ++ .disable = disable_dma_irq, ++ .ack = mask_and_ack_dma_irq, ++ .end = end_dma_irq, ++}; ++ ++//---------------------------------------------------------------------- ++ ++void __init arch_init_irq(void) ++{ ++ int i; ++ ++ clear_c0_status(0xff04); /* clear ERL */ ++ set_c0_status(0x0400); /* set IP2 */ ++ ++ /* Set up INTC irq ++ */ ++ for (i = 0; i < 32; i++) { ++ disable_intc_irq(i); ++ irq_desc[i].chip = &intc_irq_type; ++ } ++ ++ /* Set up DMAC irq ++ */ ++ for (i = 0; i < NUM_DMA; i++) { ++ disable_dma_irq(IRQ_DMA_0 + i); ++ irq_desc[IRQ_DMA_0 + i].chip = &dma_irq_type; ++ } ++ ++ /* Set up GPIO irq ++ */ ++ for (i = 0; i < NUM_GPIO; i++) { ++ disable_gpio_irq(IRQ_GPIO_0 + i); ++ irq_desc[IRQ_GPIO_0 + i].chip = &gpio_irq_type; ++ } ++} ++ ++static int plat_real_irq(int irq) ++{ ++ switch (irq) { ++ case IRQ_GPIO0: ++ irq = __gpio_group_irq(0) + IRQ_GPIO_0; ++ break; ++ case IRQ_GPIO1: ++ irq = __gpio_group_irq(1) + IRQ_GPIO_0 + 32; ++ break; ++ case IRQ_GPIO2: ++ irq = __gpio_group_irq(2) + IRQ_GPIO_0 + 64; ++ break; ++ case IRQ_GPIO3: ++ irq = __gpio_group_irq(3) + IRQ_GPIO_0 + 96; ++ break; ++ case IRQ_GPIO4: ++ irq = __gpio_group_irq(4) + IRQ_GPIO_0 + 128; ++ break; ++ case IRQ_GPIO5: ++ irq = __gpio_group_irq(5) + IRQ_GPIO_0 + 160; ++ break; ++ case IRQ_DMAC0: ++ case IRQ_DMAC1: ++ irq = __dmac_get_irq() + IRQ_DMA_0; ++ break; ++ } ++ ++ return irq; ++} ++ ++asmlinkage void plat_irq_dispatch(void) ++{ ++ int irq = 0; ++ static unsigned long intc_ipr = 0; ++ ++ intc_ipr |= REG_INTC_IPR; ++ ++ if (!intc_ipr) return; ++ ++ irq = ffs(intc_ipr) - 1; ++ intc_ipr &= ~(1< ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* OHCI (USB full speed host controller) */ ++static struct resource jz_usb_ohci_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(UHC_BASE), // phys addr for ioremap ++ .end = CPHYSADDR(UHC_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_UHC, ++ .end = IRQ_UHC, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++/* The dmamask must be set for OHCI to work */ ++static u64 ohci_dmamask = ~(u32)0; ++ ++static struct platform_device jz_usb_ohci_device = { ++ .name = "jz-ohci", ++ .id = 0, ++ .dev = { ++ .dma_mask = &ohci_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_usb_ohci_resources), ++ .resource = jz_usb_ohci_resources, ++}; ++ ++/*** LCD controller ***/ ++static struct resource jz_lcd_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(LCD_BASE), ++ .end = CPHYSADDR(LCD_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_LCD, ++ .end = IRQ_LCD, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static u64 jz_lcd_dmamask = ~(u32)0; ++ ++static struct platform_device jz_lcd_device = { ++ .name = "jz-lcd", ++ .id = 0, ++ .dev = { ++ .dma_mask = &jz_lcd_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_lcd_resources), ++ .resource = jz_lcd_resources, ++}; ++ ++/* UDC (USB gadget controller) */ ++static struct resource jz_usb_gdt_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(UDC_BASE), ++ .end = CPHYSADDR(UDC_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_UDC, ++ .end = IRQ_UDC, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static u64 udc_dmamask = ~(u32)0; ++ ++static struct platform_device jz_usb_gdt_device = { ++ .name = "jz-udc", ++ .id = 0, ++ .dev = { ++ .dma_mask = &udc_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_usb_gdt_resources), ++ .resource = jz_usb_gdt_resources, ++}; ++ ++/** MMC/SD controller **/ ++static struct resource jz_mmc_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(MSC_BASE), ++ .end = CPHYSADDR(MSC_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_MSC0, ++ .end = IRQ_MSC0, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static u64 jz_mmc_dmamask = ~(u32)0; ++ ++static struct platform_device jz_mmc_device = { ++ .name = "jz-mmc", ++ .id = 0, ++ .dev = { ++ .dma_mask = &jz_mmc_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_mmc_resources), ++ .resource = jz_mmc_resources, ++}; ++ ++/* All */ ++static struct platform_device *jz_platform_devices[] __initdata = { ++ &jz_usb_ohci_device, ++ &jz_lcd_device, ++ &jz_usb_gdt_device, ++ &jz_mmc_device, ++}; ++ ++static int __init jz_platform_init(void) ++{ ++ return platform_add_devices(jz_platform_devices, ARRAY_SIZE(jz_platform_devices)); ++} ++ ++arch_initcall(jz_platform_init); +diff -urN linux-2.6.24.7.old/arch/mips/jz4750/pm.c linux-2.6.24.7/arch/mips/jz4750/pm.c +--- linux-2.6.24.7.old/arch/mips/jz4750/pm.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750/pm.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,461 @@ ++/* ++ * linux/arch/mips/jz4750/common/pm.c ++ * ++ * JZ4750 Power Management Routines ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#undef DEBUG ++//#define DEBUG ++#ifdef DEBUG ++#define dprintk(x...) printk(x) ++#else ++#define dprintk(x...) ++#endif ++ ++#define GPIO_PORT_NUM 6 ++ ++/* ++ * __gpio_as_sleep set all pins to pull-disable, and set all pins as input ++ * except sdram and the pins which can be used as CS1_N to CS4_N for chip select. ++ */ ++#define __gpio_as_sleep() \ ++do { \ ++ REG_GPIO_PXFUNC(1) = ~0x03ff7fff; \ ++ REG_GPIO_PXSELC(1) = ~0x03ff7fff; \ ++ REG_GPIO_PXDIRC(1) = ~0x03ff7fff; \ ++ REG_GPIO_PXPES(1) = 0xffffffff; \ ++ REG_GPIO_PXFUNC(2) = ~0x01e00000; \ ++ REG_GPIO_PXSELC(2) = ~0x01e00000; \ ++ REG_GPIO_PXDIRC(2) = ~0x01e00000; \ ++ REG_GPIO_PXPES(2) = 0xffffffff; \ ++ REG_GPIO_PXFUNC(3) = 0xffffffff; \ ++ REG_GPIO_PXSELC(3) = 0xffffffff; \ ++ REG_GPIO_PXDIRC(3) = 0xffffffff; \ ++ REG_GPIO_PXPES(3) = 0xffffffff; \ ++ REG_GPIO_PXFUNC(4) = 0xffffffff; \ ++ REG_GPIO_PXSELC(4) = 0xffffffff; \ ++ REG_GPIO_PXDIRC(4) = 0xffffffff; \ ++ REG_GPIO_PXPES(4) = 0xffffffff; \ ++ REG_GPIO_PXFUNC(5) = 0xffffffff; \ ++ REG_GPIO_PXSELC(5) = 0xffffffff; \ ++ REG_GPIO_PXDIRC(5) = 0xffffffff; \ ++ REG_GPIO_PXPES(5) = 0xffffffff; \ ++} while (0) ++ ++static int jz_pm_do_hibernate(void) ++{ ++ printk("Put CPU into hibernate mode.\n"); ++ ++ /* Mask all interrupts */ ++ REG_INTC_IMSR = 0xffffffff; ++ ++ /* ++ * RTC Wakeup or 1Hz interrupt can be enabled or disabled ++ * through RTC driver's ioctl (linux/driver/char/rtc_jz.c). ++ */ ++ ++ /* Set minimum wakeup_n pin low-level assertion time for wakeup: 100ms */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HWFCR = (100 << RTC_HWFCR_BIT); ++ ++ /* Set reset pin low-level assertion time after wakeup: must > 60ms */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HRCR = (60 << RTC_HRCR_BIT); /* 60 ms */ ++ ++ /* Scratch pad register to be reserved */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HSPR = 0x12345678; ++ ++ /* clear wakeup status register */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HWRSR = 0x0; ++ ++ /* Put CPU to power down mode */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HCR = RTC_HCR_PD; ++ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ while(1); ++ ++ /* We can't get here */ ++ return 0; ++} ++ ++/* NOTES: ++ * 1: Pins that are floated (NC) should be set as input and pull-enable. ++ * 2: Pins that are pull-up or pull-down by outside should be set as input ++ * and pull-disable. ++ * 3: Pins that are connected to a chip except sdram and nand flash ++ * should be set as input and pull-disable, too. ++ */ ++static void jz_board_do_sleep(unsigned long *ptr) ++{ ++ unsigned char i; ++ ++ /* Print messages of GPIO registers for debug */ ++ for(i=0;i ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++//#define DEBUG 1 ++#undef DEBUG ++ ++ ++struct proc_dir_entry *proc_jz_root; ++ ++ ++/* ++ * EMC Modules ++ */ ++static int emc_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ ++ len += sprintf (page+len, "SMCR(0-5): 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", REG_EMC_SMCR0, REG_EMC_SMCR1, REG_EMC_SMCR2, REG_EMC_SMCR3, REG_EMC_SMCR4); ++ len += sprintf (page+len, "SACR(0-5): 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", REG_EMC_SACR0, REG_EMC_SACR1, REG_EMC_SACR2, REG_EMC_SACR3, REG_EMC_SACR4); ++ len += sprintf (page+len, "DMCR: 0x%08x\n", REG_EMC_DMCR); ++ len += sprintf (page+len, "RTCSR: 0x%04x\n", REG_EMC_RTCSR); ++ len += sprintf (page+len, "RTCOR: 0x%04x\n", REG_EMC_RTCOR); ++ return len; ++} ++ ++/* ++ * Power Manager Module ++ */ ++static int pmc_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ unsigned long lcr = REG_CPM_LCR; ++ unsigned long clkgr = REG_CPM_CLKGR; ++ ++ len += sprintf (page+len, "Low Power Mode : %s\n", ++ ((lcr & CPM_LCR_LPM_MASK) == (CPM_LCR_LPM_IDLE)) ? ++ "IDLE" : (((lcr & CPM_LCR_LPM_MASK) == (CPM_LCR_LPM_SLEEP)) ? ++ "SLEEP" : "HIBERNATE")); ++ len += sprintf (page+len, "Doze Mode : %s\n", ++ (lcr & CPM_LCR_DOZE_ON) ? "on" : "off"); ++ if (lcr & CPM_LCR_DOZE_ON) ++ len += sprintf (page+len, " duty : %d\n", (int)((lcr & CPM_LCR_DOZE_DUTY_MASK) >> CPM_LCR_DOZE_DUTY_BIT)); ++ len += sprintf (page+len, "IPU : %s\n", ++ (clkgr & CPM_CLKGR_IPU) ? "stopped" : "running"); ++ len += sprintf (page+len, "DMAC : %s\n", ++ (clkgr & CPM_CLKGR_DMAC) ? "stopped" : "running"); ++ len += sprintf (page+len, "UHC : %s\n", ++ (clkgr & CPM_CLKGR_UHC) ? "stopped" : "running"); ++ len += sprintf (page+len, "UDC : %s\n", ++ (clkgr & CPM_CLKGR_UDC) ? "stopped" : "running"); ++ len += sprintf (page+len, "LCD : %s\n", ++ (clkgr & CPM_CLKGR_LCD) ? "stopped" : "running"); ++ len += sprintf (page+len, "CIM : %s\n", ++ (clkgr & CPM_CLKGR_CIM) ? "stopped" : "running"); ++ len += sprintf (page+len, "SADC : %s\n", ++ (clkgr & CPM_CLKGR_SADC) ? "stopped" : "running"); ++ len += sprintf (page+len, "MSC0 : %s\n", ++ (clkgr & CPM_CLKGR_MSC0) ? "stopped" : "running"); ++ len += sprintf (page+len, "MSC1 : %s\n", ++ (clkgr & CPM_CLKGR_MSC1) ? "stopped" : "running"); ++ len += sprintf (page+len, "AIC1 : %s\n", ++ (clkgr & CPM_CLKGR_AIC1) ? "stopped" : "running"); ++ len += sprintf (page+len, "AIC2 : %s\n", ++ (clkgr & CPM_CLKGR_AIC2) ? "stopped" : "running"); ++ len += sprintf (page+len, "SSI0 : %s\n", ++ (clkgr & CPM_CLKGR_SSI0) ? "stopped" : "running"); ++ len += sprintf (page+len, "SSI1 : %s\n", ++ (clkgr & CPM_CLKGR_SSI1) ? "stopped" : "running"); ++ len += sprintf (page+len, "I2C : %s\n", ++ (clkgr & CPM_CLKGR_I2C) ? "stopped" : "running"); ++ len += sprintf (page+len, "RTC : %s\n", ++ (clkgr & CPM_CLKGR_RTC) ? "stopped" : "running"); ++ len += sprintf (page+len, "TCU : %s\n", ++ (clkgr & CPM_CLKGR_TCU) ? "stopped" : "running"); ++ len += sprintf (page+len, "UART1 : %s\n", ++ (clkgr & CPM_CLKGR_UART1) ? "stopped" : "running"); ++ len += sprintf (page+len, "UART0 : %s\n", ++ (clkgr & CPM_CLKGR_UART0) ? "stopped" : "running"); ++ return len; ++} ++ ++static int pmc_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ REG_CPM_CLKGR = simple_strtoul(buffer, 0, 16); ++ return count; ++} ++ ++/* ++ * Clock Generation Module ++ */ ++#define TO_MHZ(x) (x/1000000),(x%1000000)/10000 ++#define TO_KHZ(x) (x/1000),(x%1000)/10 ++ ++static int cgm_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ unsigned int cppcr = REG_CPM_CPPCR; /* PLL Control Register */ ++ unsigned int cpccr = REG_CPM_CPCCR; /* Clock Control Register */ ++ unsigned int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ unsigned int od[4] = {1, 2, 2, 4}; ++ ++ len += sprintf (page+len, "CPPCR : 0x%08x\n", cppcr); ++ len += sprintf (page+len, "CPCCR : 0x%08x\n", cpccr); ++ len += sprintf (page+len, "PLL : %s\n", ++ (cppcr & CPM_CPPCR_PLLEN) ? "ON" : "OFF"); ++ len += sprintf (page+len, "m:n:o : %d:%d:%d\n", ++ __cpm_get_pllm() + 2, ++ __cpm_get_plln() + 2, ++ od[__cpm_get_pllod()] ++ ); ++ len += sprintf (page+len, "C:H:M:P : %d:%d:%d:%d\n", ++ div[__cpm_get_cdiv()], ++ div[__cpm_get_hdiv()], ++ div[__cpm_get_mdiv()], ++ div[__cpm_get_pdiv()] ++ ); ++ len += sprintf (page+len, "PLL Freq : %3d.%02d MHz\n", TO_MHZ(__cpm_get_pllout())); ++ len += sprintf (page+len, "CCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_cclk())); ++ len += sprintf (page+len, "HCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_hclk())); ++ len += sprintf (page+len, "MCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_mclk())); ++ len += sprintf (page+len, "PCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_pclk())); ++ len += sprintf (page+len, "LCDCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_lcdclk())); ++ len += sprintf (page+len, "PIXCLK : %3d.%02d KHz\n", TO_KHZ(__cpm_get_pixclk())); ++ len += sprintf (page+len, "I2SCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_i2sclk())); ++ len += sprintf (page+len, "USBCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_usbclk())); ++ len += sprintf (page+len, "MSC0CLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_mscclk(0))); ++ len += sprintf (page+len, "MSC1CLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_mscclk(1))); ++ len += sprintf (page+len, "EXTALCLK0 : %3d.%02d MHz\n", TO_MHZ(__cpm_get_extalclk0())); ++ len += sprintf (page+len, "EXTALCLK(by CPM): %3d.%02d MHz\n", TO_MHZ(__cpm_get_extalclk())); ++ len += sprintf (page+len, "RTCCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_rtcclk())); ++ ++ return len; ++} ++ ++static int cgm_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ REG_CPM_CPCCR = simple_strtoul(buffer, 0, 16); ++ return count; ++} ++ ++ ++/* USAGE: ++ * echo n > /proc/jz/ipu // n = [1,...,9], alloc mem, 2^n pages. ++ * echo FF > /proc/jz/ipu // 255, free all buffer ++ * echo xxxx > /proc/jz/ipu // free buffer which addr is xxxx ++ * echo llll > /proc/jz/ipu // add_wired_entry(l,l,l,l) ++ * echo 0 > /proc/jz/ipu // debug, print ipu_buf ++ * od -X /proc/jz/ipu // read mem addr ++ */ ++ ++typedef struct _ipu_buf { ++ unsigned int addr; /* phys addr */ ++ unsigned int page_shift; ++} ipu_buf_t; ++ ++#define IPU_BUF_MAX 4 /* 4 buffers */ ++ ++static struct _ipu_buf ipu_buf[IPU_BUF_MAX]; ++static int ipu_buf_cnt = 0; ++static unsigned char g_asid=0; ++ ++extern void local_flush_tlb_all(void); ++ ++/* CP0 hazard avoidance. */ ++#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ ++ "nop; nop; nop; nop; nop; nop;\n\t" \ ++ ".set reorder\n\t") ++void show_tlb(void) ++{ ++#define ASID_MASK 0xFF ++ ++ unsigned long flags; ++ unsigned int old_ctx; ++ unsigned int entry; ++ unsigned int entrylo0, entrylo1, entryhi; ++ unsigned int pagemask; ++ ++ local_irq_save(flags); ++ ++ /* Save old context */ ++ old_ctx = (read_c0_entryhi() & 0xff); ++ ++ printk("TLB content:\n"); ++ entry = 0; ++ while(entry < 32) { ++ write_c0_index(entry); ++ BARRIER; ++ tlb_read(); ++ BARRIER; ++ entryhi = read_c0_entryhi(); ++ entrylo0 = read_c0_entrylo0(); ++ entrylo1 = read_c0_entrylo1(); ++ pagemask = read_c0_pagemask(); ++ printk("%02d: ASID=%02d%s VA=0x%08x ", entry, entryhi & ASID_MASK, (entrylo0 & entrylo1 & 1) ? "(G)" : " ", entryhi & ~ASID_MASK); ++ printk("PA0=0x%08x C0=%x %s%s%s\n", (entrylo0>>6)<<12, (entrylo0>>3) & 7, (entrylo0 & 4) ? "Dirty " : "", (entrylo0 & 2) ? "Valid " : "Invalid ", (entrylo0 & 1) ? "Global" : ""); ++ printk("\t\t\t PA1=0x%08x C1=%x %s%s%s\n", (entrylo1>>6)<<12, (entrylo1>>3) & 7, (entrylo1 & 4) ? "Dirty " : "", (entrylo1 & 2) ? "Valid " : "Invalid ", (entrylo1 & 1) ? "Global" : ""); ++ ++ printk("\t\tpagemask=0x%08x", pagemask); ++ printk("\tentryhi=0x%08x\n", entryhi); ++ printk("\t\tentrylo0=0x%08x", entrylo0); ++ printk("\tentrylo1=0x%08x\n", entrylo1); ++ ++ entry++; ++ } ++ BARRIER; ++ write_c0_entryhi(old_ctx); ++ ++ local_irq_restore(flags); ++} ++ ++static void ipu_add_wired_entry(unsigned long pid, ++ unsigned long entrylo0, unsigned long entrylo1, ++ unsigned long entryhi, unsigned long pagemask) ++{ ++ unsigned long flags; ++ unsigned long wired; ++ unsigned long old_pagemask; ++ unsigned long old_ctx; ++ struct task_struct *g, *p; ++ ++ /* We will lock an 4MB page size entry to map the 4MB reserved IPU memory */ ++ entrylo0 = entrylo0 >> 6; ++ entrylo0 |= 0x6 | (0 << 3); ++ /*entrylo0 |= 0x6 | (1 << 3);*/ ++ ++ do_each_thread(g, p) { ++ if (p->pid == pid ) ++ g_asid = p->mm->context[0]; ++ } while_each_thread(g, p); ++ ++ local_irq_save(flags); ++ /* Save old context and create impossible VPN2 value */ ++ old_ctx = read_c0_entryhi() & 0xff; ++ old_pagemask = read_c0_pagemask(); ++ wired = read_c0_wired(); ++ write_c0_wired(wired + 1); ++ write_c0_index(wired); ++ BARRIER; ++ entryhi &= ~0xff; /* new add, 20070906 */ ++ entryhi |= g_asid; /* new add, 20070906 */ ++// entryhi |= old_ctx; /* new add, 20070906 */ ++ write_c0_pagemask(pagemask); ++ write_c0_entryhi(entryhi); ++ write_c0_entrylo0(entrylo0); ++ write_c0_entrylo1(entrylo1); ++ BARRIER; ++ tlb_write_indexed(); ++ BARRIER; ++ ++ write_c0_entryhi(old_ctx); ++ BARRIER; ++ write_c0_pagemask(old_pagemask); ++ local_flush_tlb_all(); ++ local_irq_restore(flags); ++#if defined(DEBUG) ++ printk("\nold_ctx=%03d\n", old_ctx); ++ ++ show_tlb(); ++#endif ++} ++ ++static void ipu_del_wired_entry( void ) ++{ ++ unsigned long flags; ++ unsigned long wired; ++ ++ local_irq_save(flags); ++ wired = read_c0_wired(); ++ if (wired) { ++ write_c0_wired(0); ++ } ++ local_irq_restore(flags); ++} ++ ++static inline void ipu_buf_get( unsigned int page_shift ) ++{ ++ unsigned char * virt_addr; ++ int i; ++ for ( i=0; i< IPU_BUF_MAX; ++i ) { ++ if ( ipu_buf[i].addr == 0 ) { ++ break; ++ } ++ } ++ ++ if ( (ipu_buf_cnt = i) == IPU_BUF_MAX ) { ++ printk("Error, no free ipu buffer.\n"); ++ return ; ++ } ++ ++ virt_addr = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); ++ ++ if ( virt_addr ) { ++ ipu_buf[ipu_buf_cnt].addr = (unsigned int)virt_to_phys((void *)virt_addr); ++ ipu_buf[ipu_buf_cnt].page_shift = page_shift; ++ ++ for (i = 0; i < (1<= IPU_BUF_MAX ) { /* failed alloc mem, rturn 0 */ ++ printk("no free buffer.\n"); ++ *pint = 0; ++ } ++ else ++ *pint = (unsigned int )ipu_buf[ipu_buf_cnt].addr; /* phys addr */ ++ len += sizeof(unsigned int); ++ ++#if defined(DEBUG) ++ show_tlb(); ++#endif ++ return len; ++ ++} ++ ++static int ipu_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ unsigned int val ; ++ int cnt,i; ++ char buf[12]; ++ unsigned long pid, entrylo0, entrylo1, entryhi, pagemask; ++#if defined(DEBUG) ++ printk("ipu write count=%u\n", count); ++#endif ++ if (count == 41) { ++ for (i=0;i<12;i++) ++ buf[i]=0; ++ strncpy(buf, buffer+8*0, 8); ++ pid = simple_strtoul(buf, 0, 16); ++ for (i=0;i<12;i++) ++ buf[i]=0; ++ strncpy(buf, buffer+8*1, 8); ++ entrylo0 = simple_strtoul(buf, 0, 16); ++ for (i=0;i<12;i++) ++ buf[i]=0; ++ strncpy(buf, buffer+8*2, 8); ++ entrylo1 = simple_strtoul(buf, 0, 16); ++ for (i=0;i<12;i++) ++ buf[i]=0; ++ strncpy(buf, buffer+8*3, 8); ++ entryhi = simple_strtoul(buf, 0, 16); ++ for (i=0;i<12;i++) ++ buf[i]=0; ++ strncpy(buf, buffer+8*4, 8); ++ pagemask = simple_strtoul(buf, 0, 16); ++ ++#if defined(DEBUG) ++ printk("pid=0x%08x, entrylo0=0x%08x, entrylo1=0x%08x, entryhi=0x%08x, pagemask=0x%08x\n", ++ pid, entrylo0, entrylo1, entryhi, pagemask); ++#endif ++ ipu_add_wired_entry( pid, entrylo0, entrylo1, entryhi, pagemask); ++ return 41; ++ } else if ( count <= 9 ) { ++ for (i=0;i<12;i++) buf[i]=0; ++ strncpy(buf, buffer, 8); ++ val = simple_strtoul(buf, 0, 16); ++ } else if (count == 44) { ++ for (i = 0; i < 12; i++) ++ buf[i] = 0; ++ strncpy(buf, buffer, 10); ++ pid = simple_strtoul(buf, 0, 16); ++ ++ for (i = 0; i < 12; i++) ++ buf[i] = 0; ++ strncpy(buf, buffer + 11, 10); ++ entryhi = simple_strtoul(buf, 0, 16);//vaddr ++ ++ for (i = 0; i < 12; i++) ++ buf[i] = 0; ++ strncpy(buf, buffer + 22, 10); ++ entrylo0 = simple_strtoul(buf, 0, 16);//paddr ++ ++ for (i = 0; i < 12; i++) ++ buf[i] = 0; ++ strncpy(buf, buffer + 33, 10); ++ pagemask = simple_strtoul(buf, 0, 16); ++ pagemask = 0x3ff << 13; /* Fixed to 4MB page size */ ++ //pagemask = 0xfff << 13; /* Fixed to 16MB page size */ ++ ++ ipu_add_wired_entry(pid, entrylo0, 0, entryhi, pagemask); ++ return 44; ++ } else if (count == 12) { ++ printk("\necho release tlb > /proc/jz/ipu\n"); ++ ipu_del_wired_entry(); ++ return 12; ++ } else { ++ printk("ipu write count error, count=%d\n.", (unsigned int)count); ++ return -1; ++ } ++ ++ /* val: 1-9, page_shift, val>= 10: ipu_buf.addr */ ++ if ( val == 0 ) { /* debug, print ipu_buf info */ ++ for ( cnt=0; cnt /proc/jz/imem // n = [0,...,10], allocate memory, 2^n pages ++ * echo xxxxxxxx > /proc/jz/imem // free buffer which addr is xxxxxxxx ++ * echo FF > /proc/jz/ipu // FF, free all buffers ++ * od -X /proc/jz/imem // return the allocated buffer address and the max order of free buffer ++ */ ++ ++//#define DEBUG_IMEM 1 ++ ++#define IMEM_MAX_ORDER 10 /* max 2^10 * 4096 = 4MB */ ++ ++static unsigned int jz_imem_base; /* physical base address of ipu memory */ ++ ++static unsigned int allocated_phys_addr = 0; ++ ++/* ++ * Allocated buffer list ++ */ ++typedef struct imem_list { ++ unsigned int phys_start; /* physical start addr */ ++ unsigned int phys_end; /* physical end addr */ ++ struct imem_list *next; ++} imem_list_t; ++ ++static struct imem_list *imem_list_head = NULL; /* up sorted by phys_start */ ++ ++#ifdef DEBUG_IMEM ++static void dump_imem_list(void) ++{ ++ struct imem_list *imem; ++ ++ printk("*** dump_imem_list 0x%x ***\n", (u32)imem_list_head); ++ imem = imem_list_head; ++ while (imem) { ++ printk("imem=0x%x phys_start=0x%x phys_end=0x%x next=0x%x\n", (u32)imem, imem->phys_start, imem->phys_end, (u32)imem->next); ++ imem = imem->next; ++ } ++} ++#endif ++ ++/* allocate 2^order pages inside the 4MB memory */ ++static int imem_alloc(unsigned int order) ++{ ++ int alloc_ok = 0; ++ unsigned int start, end; ++ unsigned int size = (1 << order) * PAGE_SIZE; ++ struct imem_list *imem, *imemn, *imemp; ++ ++ allocated_phys_addr = 0; ++ ++ start = jz_imem_base; ++ end = start + (1 << IMEM_MAX_ORDER) * PAGE_SIZE; ++ ++ imem = imem_list_head; ++ while (imem) { ++ if ((imem->phys_start - start) >= size) { ++ /* we got a valid address range */ ++ alloc_ok = 1; ++ break; ++ } ++ ++ start = imem->phys_end + 1; ++ imem = imem->next; ++ } ++ ++ if (!alloc_ok) { ++ if ((end - start) >= size) ++ alloc_ok = 1; ++ } ++ ++ if (alloc_ok) { ++ end = start + size - 1; ++ allocated_phys_addr = start; ++ ++ /* add to imem_list, up sorted by phys_start */ ++ imemn = kmalloc(sizeof(struct imem_list), GFP_KERNEL); ++ if (!imemn) { ++ return -ENOMEM; ++ } ++ imemn->phys_start = start; ++ imemn->phys_end = end; ++ imemn->next = NULL; ++ ++ if (!imem_list_head) ++ imem_list_head = imemn; ++ else { ++ imem = imemp = imem_list_head; ++ while (imem) { ++ if (start < imem->phys_start) { ++ break; ++ } ++ ++ imemp = imem; ++ imem = imem->next; ++ } ++ ++ if (imem == imem_list_head) { ++ imem_list_head = imemn; ++ imemn->next = imem; ++ } ++ else { ++ imemn->next = imemp->next; ++ imemp->next = imemn; ++ } ++ } ++ } ++ ++#ifdef DEBUG_IMEM ++ dump_imem_list(); ++#endif ++ return 0; ++} ++ ++static void imem_free(unsigned int phys_addr) ++{ ++ struct imem_list *imem, *imemp; ++ ++ imem = imemp = imem_list_head; ++ while (imem) { ++ if (phys_addr == imem->phys_start) { ++ if (imem == imem_list_head) { ++ imem_list_head = imem->next; ++ } ++ else { ++ imemp->next = imem->next; ++ } ++ ++ kfree(imem); ++ break; ++ } ++ ++ imemp = imem; ++ imem = imem->next; ++ } ++ ++#ifdef DEBUG_IMEM ++ dump_imem_list(); ++#endif ++} ++ ++static void imem_free_all(void) ++{ ++ struct imem_list *imem; ++ ++ imem = imem_list_head; ++ while (imem) { ++ kfree(imem); ++ imem = imem->next; ++ } ++ ++ imem_list_head = NULL; ++ ++ allocated_phys_addr = 0; ++ ++#ifdef DEBUG_IMEM ++ dump_imem_list(); ++#endif ++} ++ ++/* ++ * Return the allocated buffer address and the max order of free buffer ++ */ ++static int imem_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ unsigned int start_addr, end_addr, max_order, max_size; ++ struct imem_list *imem; ++ ++ unsigned int *tmp = (unsigned int *)(page + len); ++ ++ start_addr = jz_imem_base; ++ end_addr = start_addr + (1 << IMEM_MAX_ORDER) * PAGE_SIZE; ++ ++ if (!imem_list_head) ++ max_size = end_addr - start_addr; ++ else { ++ max_size = 0; ++ imem = imem_list_head; ++ while (imem) { ++ if (max_size < (imem->phys_start - start_addr)) ++ max_size = imem->phys_start - start_addr; ++ ++ start_addr = imem->phys_end + 1; ++ imem = imem->next; ++ } ++ ++ if (max_size < (end_addr - start_addr)) ++ max_size = end_addr - start_addr; ++ } ++ ++ if (max_size > 0) { ++ max_order = get_order(max_size); ++ if (((1 << max_order) * PAGE_SIZE) > max_size) ++ max_order--; ++ } ++ else { ++ max_order = 0xffffffff; /* No any free buffer */ ++ } ++ ++ *tmp++ = allocated_phys_addr; /* address allocated by 'echo n > /proc/jz/imem' */ ++ *tmp = max_order; /* max order of current free buffers */ ++ ++ len += 2 * sizeof(unsigned int); ++ ++ return len; ++} ++ ++static int imem_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ unsigned int val; ++ ++ val = simple_strtoul(buffer, 0, 16); ++ ++ if (val == 0xff) { ++ /* free all memory */ ++ imem_free_all(); ++ ipu_del_wired_entry(); ++ } else if ((val >= 0) && (val <= IMEM_MAX_ORDER)) { ++ /* allocate 2^val pages */ ++ imem_alloc(val); ++ } else { ++ /* free buffer which phys_addr is val */ ++ imem_free(val); ++ } ++ ++ return count; ++} ++ ++/* ++ * /proc/jz/xxx entry ++ * ++ */ ++static int __init jz_proc_init(void) ++{ ++ struct proc_dir_entry *res; ++ unsigned int virt_addr, i; ++ ++ proc_jz_root = proc_mkdir("jz", 0); ++ ++ /* External Memory Controller */ ++ res = create_proc_entry("emc", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = emc_read_proc; ++ res->write_proc = NULL; ++ res->data = NULL; ++ } ++ ++ /* Power Management Controller */ ++ res = create_proc_entry("pmc", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = pmc_read_proc; ++ res->write_proc = pmc_write_proc; ++ res->data = NULL; ++ } ++ ++ /* Clock Generation Module */ ++ res = create_proc_entry("cgm", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = cgm_read_proc; ++ res->write_proc = cgm_write_proc; ++ res->data = NULL; ++ } ++ ++ /* Image process unit */ ++ res = create_proc_entry("ipu", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = ipu_read_proc; ++ res->write_proc = ipu_write_proc; ++ res->data = NULL; ++ } ++ ++ /* udc hotplug */ ++ res = create_proc_entry("udc", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = udc_read_proc; ++ res->write_proc = NULL; ++ res->data = NULL; ++ } ++ ++ /* mmc hotplug */ ++ res = create_proc_entry("mmc", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = mmc_read_proc; ++ res->write_proc = NULL; ++ res->data = NULL; ++ } ++ ++ /* show tlb */ ++ res = create_proc_entry("tlb", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = tlb_read_proc; ++ res->write_proc = NULL; ++ res->data = NULL; ++ } ++ ++ /* ++ * Reserve a 4MB memory for IPU on JZ4750. ++ */ ++ jz_imem_base = (unsigned int)__get_free_pages(GFP_KERNEL, IMEM_MAX_ORDER); ++ if (jz_imem_base) { ++ /* imem (IPU memory management) */ ++ res = create_proc_entry("imem", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = imem_read_proc; ++ res->write_proc = imem_write_proc; ++ res->data = NULL; ++ } ++ ++ /* Set page reserved */ ++ virt_addr = jz_imem_base; ++ for (i = 0; i < (1 << IMEM_MAX_ORDER); i++) { ++ SetPageReserved(virt_to_page((void *)virt_addr)); ++ virt_addr += PAGE_SIZE; ++ } ++ ++ /* Convert to physical address */ ++ jz_imem_base = virt_to_phys((void *)jz_imem_base); ++ ++ printk("Total %dMB memory at 0x%x was reserved for IPU\n", ++ (unsigned int)((1 << IMEM_MAX_ORDER) * PAGE_SIZE)/1000000, jz_imem_base); ++ } else ++ printk("NOT enough memory for imem\n"); ++ ++ return 0; ++} ++ ++__initcall(jz_proc_init); +diff -urN linux-2.6.24.7.old/arch/mips/jz4750/prom.c linux-2.6.24.7/arch/mips/jz4750/prom.c +--- linux-2.6.24.7.old/arch/mips/jz4750/prom.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750/prom.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,198 @@ ++/* ++ * ++ * BRIEF MODULE DESCRIPTION ++ * PROM library initialisation code, supports YAMON and U-Boot. ++ * ++ * Copyright 2000, 2001, 2006 MontaVista Software Inc. ++ * Author: MontaVista Software, Inc. ++ * ppopov@mvista.com or source@mvista.com ++ * ++ * This file was derived from Carsten Langgaard's ++ * arch/mips/mips-boards/xx files. ++ * ++ * Carsten Langgaard, carstenl@mips.com ++ * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN ++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, 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 DAMAGE. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* #define DEBUG_CMDLINE */ ++ ++int prom_argc; ++char **prom_argv, **prom_envp; ++ ++char * prom_getcmdline(void) ++{ ++ return &(arcs_cmdline[0]); ++} ++ ++void prom_init_cmdline(void) ++{ ++ char *cp; ++ int actr; ++ ++ actr = 1; /* Always ignore argv[0] */ ++ ++ cp = &(arcs_cmdline[0]); ++ while(actr < prom_argc) { ++ strcpy(cp, prom_argv[actr]); ++ cp += strlen(prom_argv[actr]); ++ *cp++ = ' '; ++ actr++; ++ } ++ if (cp != &(arcs_cmdline[0])) /* get rid of trailing space */ ++ --cp; ++ if (prom_argc > 1) ++ *cp = '\0'; ++ ++} ++ ++ ++char *prom_getenv(char *envname) ++{ ++#if 0 ++ /* ++ * Return a pointer to the given environment variable. ++ * YAMON uses "name", "value" pairs, while U-Boot uses "name=value". ++ */ ++ ++ char **env = prom_envp; ++ int i = strlen(envname); ++ int yamon = (*env && strchr(*env, '=') == NULL); ++ ++ while (*env) { ++ if (yamon) { ++ if (strcmp(envname, *env++) == 0) ++ return *env; ++ } else { ++ if (strncmp(envname, *env, i) == 0 && (*env)[i] == '=') ++ return *env + i + 1; ++ } ++ env++; ++ } ++#endif ++ return NULL; ++} ++ ++inline unsigned char str2hexnum(unsigned char c) ++{ ++ if(c >= '0' && c <= '9') ++ return c - '0'; ++ if(c >= 'a' && c <= 'f') ++ return c - 'a' + 10; ++ if(c >= 'A' && c <= 'F') ++ return c - 'A' + 10; ++ return 0; /* foo */ ++} ++ ++inline void str2eaddr(unsigned char *ea, unsigned char *str) ++{ ++ int i; ++ ++ for(i = 0; i < 6; i++) { ++ unsigned char num; ++ ++ if((*str == '.') || (*str == ':')) ++ str++; ++ num = str2hexnum(*str++) << 4; ++ num |= (str2hexnum(*str++)); ++ ea[i] = num; ++ } ++} ++ ++int get_ethernet_addr(char *ethernet_addr) ++{ ++ char *ethaddr_str; ++ ++ ethaddr_str = prom_getenv("ethaddr"); ++ if (!ethaddr_str) { ++ printk("ethaddr not set in boot prom\n"); ++ return -1; ++ } ++ str2eaddr(ethernet_addr, ethaddr_str); ++ ++#if 0 ++ { ++ int i; ++ ++ printk("get_ethernet_addr: "); ++ for (i=0; i<5; i++) ++ printk("%02x:", (unsigned char)*(ethernet_addr+i)); ++ printk("%02x\n", *(ethernet_addr+i)); ++ } ++#endif ++ ++ return 0; ++} ++ ++void __init prom_free_prom_memory(void) ++{ ++} ++ ++void __init prom_init(void) ++{ ++ unsigned char *memsize_str; ++ unsigned long memsize; ++ ++ prom_argc = (int) fw_arg0; ++ prom_argv = (char **) fw_arg1; ++ prom_envp = (char **) fw_arg2; ++ ++ mips_machtype = MACH_INGENIC_JZ4750; ++ ++ prom_init_cmdline(); ++ memsize_str = prom_getenv("memsize"); ++ if (!memsize_str) { ++ memsize = 0x04000000; ++ } else { ++ memsize = simple_strtol(memsize_str, NULL, 0); ++ } ++ add_memory_region(0, memsize, BOOT_MEM_RAM); ++} ++ ++/* used by early printk */ ++void prom_putchar(char c) ++{ ++ volatile u8 *uart_lsr = (volatile u8 *)(UART0_BASE + OFF_LSR); ++ volatile u8 *uart_tdr = (volatile u8 *)(UART0_BASE + OFF_TDR); ++ ++ /* Wait for fifo to shift out some bytes */ ++ while ( !((*uart_lsr & (UARTLSR_TDRQ | UARTLSR_TEMT)) == 0x60) ); ++ ++ *uart_tdr = (u8)c; ++} ++ ++const char *get_system_type(void) ++{ ++ return "JZ4750"; ++} ++ ++EXPORT_SYMBOL(prom_getcmdline); ++EXPORT_SYMBOL(get_ethernet_addr); ++EXPORT_SYMBOL(str2eaddr); +diff -urN linux-2.6.24.7.old/arch/mips/jz4750/reset.c linux-2.6.24.7/arch/mips/jz4750/reset.c +--- linux-2.6.24.7.old/arch/mips/jz4750/reset.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750/reset.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,46 @@ ++/* ++ * linux/arch/mips/jz4750/reset.c ++ * ++ * JZ4750 reset routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++void jz_restart(char *command) ++{ ++ printk("Restarting after 4 ms\n"); ++ REG_WDT_TCSR = WDT_TCSR_PRESCALE4 | WDT_TCSR_EXT_EN; ++ REG_WDT_TCNT = 0; ++ REG_WDT_TDR = JZ_EXTAL/1000; /* reset after 4ms */ ++ REG_TCU_TSCR = TCU_TSCR_WDTSC; /* enable wdt clock */ ++ REG_WDT_TCER = WDT_TCER_TCEN; /* wdt start */ ++ while (1); ++} ++ ++void jz_halt(void) ++{ ++ printk(KERN_NOTICE "\n** You can safely turn off the power\n"); ++ ++ while (1) ++ __asm__(".set\tmips3\n\t" ++ "wait\n\t" ++ ".set\tmips0"); ++} ++ ++void jz_power_off(void) ++{ ++ jz_halt(); ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4750/setup.c linux-2.6.24.7/arch/mips/jz4750/setup.c +--- linux-2.6.24.7.old/arch/mips/jz4750/setup.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750/setup.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,197 @@ ++/* ++ * linux/arch/mips/jz4750/common/setup.c ++ * ++ * JZ4750 common setup routines. ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_PC_KEYB ++#include ++#endif ++ ++jz_clocks_t jz_clocks; ++ ++extern char * __init prom_getcmdline(void); ++extern void __init jz_board_setup(void); ++extern void jz_restart(char *); ++extern void jz_halt(void); ++extern void jz_power_off(void); ++extern void jz_time_init(void); ++ ++static void __init sysclocks_setup(void) ++{ ++#ifndef CONFIG_MIPS_JZ_EMURUS /* FPGA */ ++ jz_clocks.cclk = __cpm_get_cclk(); ++ jz_clocks.hclk = __cpm_get_hclk(); ++ jz_clocks.pclk = __cpm_get_pclk(); ++ jz_clocks.mclk = __cpm_get_mclk(); ++ jz_clocks.lcdclk = __cpm_get_lcdclk(); ++ jz_clocks.pixclk = __cpm_get_pixclk(); ++ jz_clocks.i2sclk = __cpm_get_i2sclk(); ++ jz_clocks.usbclk = __cpm_get_usbclk(); ++ jz_clocks.mscclk = __cpm_get_mscclk(0); ++ jz_clocks.extalclk = __cpm_get_extalclk(); ++ jz_clocks.rtcclk = __cpm_get_rtcclk(); ++#else ++ ++#define FPGACLK 8000000 ++ ++ jz_clocks.cclk = FPGACLK; ++ jz_clocks.hclk = FPGACLK; ++ jz_clocks.pclk = FPGACLK; ++ jz_clocks.mclk = FPGACLK; ++ jz_clocks.lcdclk = FPGACLK; ++ jz_clocks.pixclk = FPGACLK; ++ jz_clocks.i2sclk = FPGACLK; ++ jz_clocks.usbclk = FPGACLK; ++ jz_clocks.mscclk = FPGACLK; ++ jz_clocks.extalclk = FPGACLK; ++ jz_clocks.rtcclk = FPGACLK; ++#endif ++ ++ printk("CPU clock: %dMHz, System clock: %dMHz, Peripheral clock: %dMHz, Memory clock: %dMHz\n", ++ (jz_clocks.cclk + 500000) / 1000000, ++ (jz_clocks.hclk + 500000) / 1000000, ++ (jz_clocks.pclk + 500000) / 1000000, ++ (jz_clocks.mclk + 500000) / 1000000); ++} ++ ++static void __init soc_cpm_setup(void) ++{ ++ /* Start all module clocks ++ */ ++ __cpm_start_all(); ++ ++ /* Enable CKO to external memory */ ++ __cpm_enable_cko(); ++ ++ /* CPU enters IDLE mode when executing 'wait' instruction */ ++ __cpm_idle_mode(); ++ ++ /* Setup system clocks */ ++ sysclocks_setup(); ++} ++ ++static void __init soc_harb_setup(void) ++{ ++// __harb_set_priority(0x00); /* CIM>LCD>DMA>ETH>PCI>USB>CBB */ ++// __harb_set_priority(0x03); /* LCD>CIM>DMA>ETH>PCI>USB>CBB */ ++// __harb_set_priority(0x0a); /* ETH>LCD>CIM>DMA>PCI>USB>CBB */ ++} ++ ++static void __init soc_emc_setup(void) ++{ ++} ++ ++static void __init soc_dmac_setup(void) ++{ ++ __dmac_enable_module(0); ++ __dmac_enable_module(1); ++} ++ ++static void __init jz_soc_setup(void) ++{ ++ soc_cpm_setup(); ++ soc_harb_setup(); ++ soc_emc_setup(); ++ soc_dmac_setup(); ++} ++ ++static void __init jz_serial_setup(void) ++{ ++#ifdef CONFIG_SERIAL_8250 ++ struct uart_port s; ++ REG8(UART0_FCR) |= UARTFCR_UUE; /* enable UART module */ ++ memset(&s, 0, sizeof(s)); ++ s.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST; ++ s.iotype = SERIAL_IO_MEM; ++ s.regshift = 2; ++ s.uartclk = jz_clocks.extalclk ; ++ ++ s.line = 0; ++ s.membase = (u8 *)UART0_BASE; ++ s.irq = IRQ_UART0; ++ if (early_serial_setup(&s) != 0) { ++ printk(KERN_ERR "Serial ttyS0 setup failed!\n"); ++ } ++ ++ s.line = 1; ++ s.membase = (u8 *)UART1_BASE; ++ s.irq = IRQ_UART1; ++ if (early_serial_setup(&s) != 0) { ++ printk(KERN_ERR "Serial ttyS1 setup failed!\n"); ++ } ++ ++ s.line = 2; ++ s.membase = (u8 *)UART2_BASE; ++ s.irq = IRQ_UART2; ++ if (early_serial_setup(&s) != 0) { ++ printk(KERN_ERR "Serial ttyS2 setup failed!\n"); ++ } ++ ++ s.line = 3; ++ s.membase = (u8 *)UART3_BASE; ++ s.irq = IRQ_UART3; ++ if (early_serial_setup(&s) != 0) { ++ printk(KERN_ERR "Serial ttyS3 setup failed!\n"); ++ } ++#endif ++} ++ ++void __init plat_mem_setup(void) ++{ ++ char *argptr; ++ ++ argptr = prom_getcmdline(); ++ ++ /* IO/MEM resources. Which will be the addtion value in `inX' and ++ * `outX' macros defined in asm/io.h */ ++ set_io_port_base(0); ++ ioport_resource.start = 0x00000000; ++ ioport_resource.end = 0xffffffff; ++ iomem_resource.start = 0x00000000; ++ iomem_resource.end = 0xffffffff; ++ ++ _machine_restart = jz_restart; ++ _machine_halt = jz_halt; ++ pm_power_off = jz_power_off; ++ ++ jz_soc_setup(); ++ jz_serial_setup(); ++ jz_board_setup(); ++} ++ +diff -urN linux-2.6.24.7.old/arch/mips/jz4750/time.c linux-2.6.24.7/arch/mips/jz4750/time.c +--- linux-2.6.24.7.old/arch/mips/jz4750/time.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750/time.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,156 @@ ++/* ++ * linux/arch/mips/jz4750/time.c ++ * ++ * Setting up the clock on the JZ4750 boards. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* This is for machines which generate the exact clock. */ ++ ++#define JZ_TIMER_IRQ IRQ_TCU0 ++ ++#define JZ_TIMER_CLOCK (JZ_EXTAL>>4) /* Jz timer clock frequency */ ++ ++static struct clocksource clocksource_jz; /* Jz clock source */ ++static struct clock_event_device jz_clockevent_device; /* Jz clock event */ ++ ++void (*jz_timer_callback)(void); ++ ++static irqreturn_t jz_timer_interrupt(int irq, void *dev_id) ++{ ++ struct clock_event_device *cd = dev_id; ++ ++ REG_TCU_TFCR = TCU_TFCR_OSTFCL; /* ACK timer */ ++ ++ if (jz_timer_callback) ++ jz_timer_callback(); ++ ++ cd->event_handler(cd); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct irqaction jz_irqaction = { ++ .handler = jz_timer_interrupt, ++ .flags = IRQF_DISABLED | IRQF_PERCPU | IRQF_TIMER, ++ .name = "jz-timerirq", ++}; ++ ++ ++cycle_t jz_get_cycles(void) ++{ ++ /* convert jiffes to jz timer cycles */ ++ return (cycle_t)( jiffies*((JZ_TIMER_CLOCK)/HZ) + REG_TCU_OSTCNT); ++} ++ ++static struct clocksource clocksource_jz = { ++ .name = "jz_clocksource", ++ .rating = 300, ++ .read = jz_get_cycles, ++ .mask = 0xFFFFFFFF, ++ .shift = 10, ++ .flags = CLOCK_SOURCE_WATCHDOG, ++}; ++ ++static int __init jz_clocksource_init(void) ++{ ++ clocksource_jz.mult = clocksource_hz2mult(JZ_TIMER_CLOCK, clocksource_jz.shift); ++ clocksource_register(&clocksource_jz); ++ return 0; ++} ++ ++static int jz_set_next_event(unsigned long evt, ++ struct clock_event_device *unused) ++{ ++ return 0; ++} ++ ++static void jz_set_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt) ++{ ++ switch (mode) { ++ case CLOCK_EVT_MODE_PERIODIC: ++ break; ++ case CLOCK_EVT_MODE_ONESHOT: ++ case CLOCK_EVT_MODE_UNUSED: ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ break; ++ case CLOCK_EVT_MODE_RESUME: ++ break; ++ } ++} ++ ++static struct clock_event_device jz_clockevent_device = { ++ .name = "jz-clockenvent", ++ .features = CLOCK_EVT_FEAT_PERIODIC, ++// .features = CLOCK_EVT_FEAT_ONESHOT, /* Jz4740 not support dynamic clock now */ ++ ++ /* .mult, .shift, .max_delta_ns and .min_delta_ns left uninitialized */ ++ .rating = 300, ++ .irq = JZ_TIMER_IRQ, ++ .set_mode = jz_set_mode, ++ .set_next_event = jz_set_next_event, ++}; ++ ++static void __init jz_clockevent_init(void) ++{ ++ struct clock_event_device *cd = &jz_clockevent_device; ++ unsigned int cpu = smp_processor_id(); ++ ++ cd->cpumask = cpumask_of_cpu(cpu); ++ clockevents_register_device(cd); ++} ++ ++static void __init jz_timer_setup(void) ++{ ++ jz_clocksource_init(); /* init jz clock source */ ++ jz_clockevent_init(); /* init jz clock event */ ++ ++ /* ++ * Make irqs happen for the system timer ++ */ ++ jz_irqaction.dev_id = &jz_clockevent_device; ++ setup_irq(JZ_TIMER_IRQ, &jz_irqaction); ++} ++ ++ ++void __init plat_time_init(void) ++{ ++ unsigned int latch; ++ ++ /* Init timer */ ++ latch = (JZ_TIMER_CLOCK + (HZ>>1)) / HZ; ++ ++ REG_TCU_OSTCSR = TCU_OSTCSR_PRESCALE16 | TCU_OSTCSR_EXT_EN; ++ REG_TCU_OSTCNT = 0; ++ REG_TCU_OSTDR = latch; ++ ++ REG_TCU_TMCR = TCU_TMCR_OSTMCL; /* unmask match irq */ ++ REG_TCU_TSCR = TCU_TSCR_OSTSC; /* enable timer clock */ ++ REG_TCU_TESR = TCU_TESR_OSTST; /* start counting up */ ++ ++ jz_timer_setup(); ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4750d/Makefile linux-2.6.24.7/arch/mips/jz4750d/Makefile +--- linux-2.6.24.7.old/arch/mips/jz4750d/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750d/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,22 @@ ++# ++# Makefile for the Ingenic JZ4750D. ++# ++ ++# Object file lists. ++ ++obj-y += prom.o irq.o time.o reset.o setup.o dma.o \ ++ platform.o i2c.o ++ ++obj-$(CONFIG_PROC_FS) += proc.o ++ ++# board specific support ++ ++obj-$(CONFIG_JZ4750D_FUWA1) += board-fuwa1.o ++ ++# PM support ++ ++obj-$(CONFIG_PM_LEGACY) +=pm.o ++ ++# CPU Frequency scaling support ++ ++obj-$(CONFIG_CPU_FREQ_JZ) +=cpufreq.o +diff -urN linux-2.6.24.7.old/arch/mips/jz4750d/board-fuwa1.c linux-2.6.24.7/arch/mips/jz4750d/board-fuwa1.c +--- linux-2.6.24.7.old/arch/mips/jz4750d/board-fuwa1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750d/board-fuwa1.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,72 @@ ++/* ++ * linux/arch/mips/jz4750d/board-fuwa1.c ++ * ++ * JZ4750D FUWA1 board setup routines. ++ * ++ * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++extern void (*jz_timer_callback)(void); ++ ++static void dancing(void) ++{ ++ static unsigned char slash[] = "\\|/-"; ++// static volatile unsigned char *p = (unsigned char *)0xb6000058; ++ static volatile unsigned char *p = (unsigned char *)0xb6000016; ++ static unsigned int count = 0; ++ *p = slash[count++]; ++ count &= 3; ++} ++ ++static void fuwa1_timer_callback(void) ++{ ++ static unsigned long count = 0; ++ ++ if ((++count) % 50 == 0) { ++ dancing(); ++ count = 0; ++ } ++} ++ ++static void __init board_cpm_setup(void) ++{ ++ /* Stop unused module clocks here. ++ * We have started all module clocks at arch/mips/jz4750d/setup.c. ++ */ ++} ++ ++static void __init board_gpio_setup(void) ++{ ++ /* ++ * Initialize SDRAM pins ++ */ ++} ++ ++void __init jz_board_setup(void) ++{ ++ printk("JZ4750D FUWA1 board setup\n"); ++ ++ board_cpm_setup(); ++ board_gpio_setup(); ++ ++ jz_timer_callback = fuwa1_timer_callback; ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4750d/cpufreq.c linux-2.6.24.7/arch/mips/jz4750d/cpufreq.c +--- linux-2.6.24.7.old/arch/mips/jz4750d/cpufreq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750d/cpufreq.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,601 @@ ++/* ++ * linux/arch/mips/jz4750d/cpufreq.c ++ * ++ * cpufreq driver for JZ4750D ++ * ++ * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ ++ "cpufreq-jz4750d", msg) ++ ++#undef CHANGE_PLL ++ ++#define PLL_UNCHANGED 0 ++#define PLL_GOES_UP 1 ++#define PLL_GOES_DOWN 2 ++ ++#define PLL_WAIT_500NS (500*(__cpm_get_cclk()/1000000000)) ++ ++/* Saved the boot-time parameters */ ++static struct { ++ /* SDRAM parameters */ ++ unsigned int mclk; /* memory clock, KHz */ ++ unsigned int tras; /* RAS pulse width, cycles of mclk */ ++ unsigned int rcd; /* RAS to CAS Delay, cycles of mclk */ ++ unsigned int tpc; /* RAS Precharge time, cycles of mclk */ ++ unsigned int trwl; /* Write Precharge Time, cycles of mclk */ ++ unsigned int trc; /* RAS Cycle Time, cycles of mclk */ ++ unsigned int rtcor; /* Refresh Time Constant */ ++ unsigned int sdram_initialized; ++ ++ /* LCD parameters */ ++ unsigned int lcd_clk; /* LCD clock, Hz */ ++ unsigned int lcdpix_clk; /* LCD Pixel clock, Hz */ ++ unsigned int lcd_clks_initialized; ++} boot_config; ++ ++struct jz4750d_freq_percpu_info { ++ struct cpufreq_frequency_table table[7]; ++}; ++ ++static struct jz4750d_freq_percpu_info jz4750d_freq_table; ++ ++/* ++ * This contains the registers value for an operating point. ++ * If only part of a register needs to change then there is ++ * a mask value for that register. ++ * When going to a new operating point the current register ++ * value is ANDed with the ~mask and ORed with the new value. ++ */ ++struct dpm_regs { ++ u32 cpccr; /* Clock Freq Control Register */ ++ u32 cpccr_mask; /* Clock Freq Control Register mask */ ++ u32 cppcr; /* PLL1 Control Register */ ++ u32 cppcr_mask; /* PLL1 Control Register mask */ ++ u32 pll_up_flag; /* New PLL freq is higher than current or not */ ++}; ++ ++extern jz_clocks_t jz_clocks; ++ ++static void jz_update_clocks(void) ++{ ++ /* Next clocks must be updated if we have changed ++ * the PLL or divisors. ++ */ ++ jz_clocks.cclk = __cpm_get_cclk(); ++ jz_clocks.hclk = __cpm_get_hclk(); ++ jz_clocks.mclk = __cpm_get_mclk(); ++ jz_clocks.pclk = __cpm_get_pclk(); ++ jz_clocks.lcdclk = __cpm_get_lcdclk(); ++ jz_clocks.pixclk = __cpm_get_pixclk(); ++ jz_clocks.i2sclk = __cpm_get_i2sclk(); ++ jz_clocks.usbclk = __cpm_get_usbclk(); ++ jz_clocks.mscclk = __cpm_get_mscclk(0); ++} ++ ++static void ++jz_init_boot_config(void) ++{ ++ if (!boot_config.lcd_clks_initialized) { ++ /* the first time to scale pll */ ++ boot_config.lcd_clk = __cpm_get_lcdclk(); ++ boot_config.lcdpix_clk = __cpm_get_pixclk(); ++ boot_config.lcd_clks_initialized = 1; ++ } ++ ++ if (!boot_config.sdram_initialized) { ++ /* the first time to scale frequencies */ ++ unsigned int dmcr, rtcor; ++ unsigned int tras, rcd, tpc, trwl, trc; ++ ++ dmcr = REG_EMC_DMCR; ++ rtcor = REG_EMC_RTCOR; ++ ++ tras = (dmcr >> 13) & 0x7; ++ rcd = (dmcr >> 11) & 0x3; ++ tpc = (dmcr >> 8) & 0x7; ++ trwl = (dmcr >> 5) & 0x3; ++ trc = (dmcr >> 2) & 0x7; ++ ++ boot_config.mclk = __cpm_get_mclk() / 1000; ++ boot_config.tras = tras + 4; ++ boot_config.rcd = rcd + 1; ++ boot_config.tpc = tpc + 1; ++ boot_config.trwl = trwl + 1; ++ boot_config.trc = trc * 2 + 1; ++ boot_config.rtcor = rtcor; ++ ++ boot_config.sdram_initialized = 1; ++ } ++} ++ ++static void jz_update_dram_rtcor(unsigned int new_mclk) ++{ ++ unsigned int rtcor; ++ ++ new_mclk /= 1000; ++ rtcor = boot_config.rtcor * new_mclk / boot_config.mclk; ++ rtcor--; ++ ++ if (rtcor < 1) rtcor = 1; ++ if (rtcor > 255) rtcor = 255; ++ ++ REG_EMC_RTCOR = rtcor; ++ REG_EMC_RTCNT = rtcor; ++} ++ ++static void jz_update_dram_dmcr(unsigned int new_mclk) ++{ ++ unsigned int dmcr; ++ unsigned int tras, rcd, tpc, trwl, trc; ++ unsigned int valid_time, new_time; /* ns */ ++ ++ new_mclk /= 1000; ++ tras = boot_config.tras * new_mclk / boot_config.mclk; ++ rcd = boot_config.rcd * new_mclk / boot_config.mclk; ++ tpc = boot_config.tpc * new_mclk / boot_config.mclk; ++ trwl = boot_config.trwl * new_mclk / boot_config.mclk; ++ trc = boot_config.trc * new_mclk / boot_config.mclk; ++ ++ /* Validation checking */ ++ valid_time = (boot_config.tras * 1000000) / boot_config.mclk; ++ new_time = (tras * 1000000) / new_mclk; ++ if (new_time < valid_time) tras += 1; ++ ++ valid_time = (boot_config.rcd * 1000000) / boot_config.mclk; ++ new_time = (rcd * 1000000) / new_mclk; ++ if (new_time < valid_time) rcd += 1; ++ ++ valid_time = (boot_config.tpc * 1000000) / boot_config.mclk; ++ new_time = (tpc * 1000000) / new_mclk; ++ if (new_time < valid_time) tpc += 1; ++ ++ valid_time = (boot_config.trwl * 1000000) / boot_config.mclk; ++ new_time = (trwl * 1000000) / new_mclk; ++ if (new_time < valid_time) trwl += 1; ++ ++ valid_time = (boot_config.trc * 1000000) / boot_config.mclk; ++ new_time = (trc * 1000000) / new_mclk; ++ if (new_time < valid_time) trc += 2; ++ ++ tras = (tras < 4) ? 4: tras; ++ tras = (tras > 11) ? 11: tras; ++ tras -= 4; ++ ++ rcd = (rcd < 1) ? 1: rcd; ++ rcd = (rcd > 4) ? 4: rcd; ++ rcd -= 1; ++ ++ tpc = (tpc < 1) ? 1: tpc; ++ tpc = (tpc > 8) ? 8: tpc; ++ tpc -= 1; ++ ++ trwl = (trwl < 1) ? 1: trwl; ++ trwl = (trwl > 4) ? 4: trwl; ++ trwl -= 1; ++ ++ trc = (trc < 1) ? 1: trc; ++ trc = (trc > 15) ? 15: trc; ++ trc /= 2; ++ ++ dmcr = REG_EMC_DMCR; ++ ++ dmcr &= ~(EMC_DMCR_TRAS_MASK | EMC_DMCR_RCD_MASK | EMC_DMCR_TPC_MASK | EMC_DMCR_TRWL_MASK | EMC_DMCR_TRC_MASK); ++ dmcr |= ((tras << EMC_DMCR_TRAS_BIT) | (rcd << EMC_DMCR_RCD_BIT) | (tpc << EMC_DMCR_TPC_BIT) | (trwl << EMC_DMCR_TRWL_BIT) | (trc << EMC_DMCR_TRC_BIT)); ++ ++ REG_EMC_DMCR = dmcr; ++} ++ ++static void jz_update_dram_prev(unsigned int cur_mclk, unsigned int new_mclk) ++{ ++ /* No risk, no fun: run with interrupts on! */ ++ if (new_mclk > cur_mclk) { ++ /* We're going FASTER, so first update TRAS, RCD, TPC, TRWL ++ * and TRC of DMCR before changing the frequency. ++ */ ++ jz_update_dram_dmcr(new_mclk); ++ } else { ++ /* We're going SLOWER: first update RTCOR value ++ * before changing the frequency. ++ */ ++ jz_update_dram_rtcor(new_mclk); ++ } ++} ++ ++static void jz_update_dram_post(unsigned int cur_mclk, unsigned int new_mclk) ++{ ++ /* No risk, no fun: run with interrupts on! */ ++ if (new_mclk > cur_mclk) { ++ /* We're going FASTER, so update RTCOR ++ * after changing the frequency ++ */ ++ jz_update_dram_rtcor(new_mclk); ++ } else { ++ /* We're going SLOWER: so update TRAS, RCD, TPC, TRWL ++ * and TRC of DMCR after changing the frequency. ++ */ ++ jz_update_dram_dmcr(new_mclk); ++ } ++} ++ ++static void jz_scale_divisors(struct dpm_regs *regs) ++{ ++ unsigned int cpccr; ++ unsigned int cur_mclk, new_mclk; ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ unsigned int tmp = 0, wait = PLL_WAIT_500NS; ++ ++ cpccr = REG_CPM_CPCCR; ++ cpccr &= ~((unsigned long)regs->cpccr_mask); ++ cpccr |= regs->cpccr; ++ cpccr |= CPM_CPCCR_CE; /* update immediately */ ++ ++ cur_mclk = __cpm_get_mclk(); ++ new_mclk = __cpm_get_pllout() / div[(cpccr & CPM_CPCCR_MDIV_MASK) >> CPM_CPCCR_MDIV_BIT]; ++ ++ /* Update some DRAM parameters before changing frequency */ ++ jz_update_dram_prev(cur_mclk, new_mclk); ++ ++ /* update register to change the clocks. ++ * align this code to a cache line. ++ */ ++ __asm__ __volatile__( ++ ".set noreorder\n\t" ++ ".align 5\n" ++ "sw %1,0(%0)\n\t" ++ "li %3,0\n\t" ++ "1:\n\t" ++ "bne %3,%2,1b\n\t" ++ "addi %3, 1\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ ".set reorder\n\t" ++ : ++ : "r" (CPM_CPCCR), "r" (cpccr), "r" (wait), "r" (tmp)); ++ ++ /* Update some other DRAM parameters after changing frequency */ ++ jz_update_dram_post(cur_mclk, new_mclk); ++} ++ ++#ifdef CHANGE_PLL ++/* Maintain the LCD clock and pixel clock */ ++static void jz_scale_lcd_divisors(struct dpm_regs *regs) ++{ ++ unsigned int new_pll, new_lcd_div, new_lcdpix_div; ++ unsigned int cpccr; ++ unsigned int tmp = 0, wait = PLL_WAIT_500NS; ++ ++ if (!boot_config.lcd_clks_initialized) return; ++ ++ new_pll = __cpm_get_pllout(); ++ new_lcd_div = new_pll / boot_config.lcd_clk; ++ new_lcdpix_div = new_pll / boot_config.lcdpix_clk; ++ ++ if (new_lcd_div < 1) ++ new_lcd_div = 1; ++ if (new_lcd_div > 16) ++ new_lcd_div = 16; ++ ++ if (new_lcdpix_div < 1) ++ new_lcdpix_div = 1; ++ if (new_lcdpix_div > 512) ++ new_lcdpix_div = 512; ++ ++// REG_CPM_CPCCR2 = new_lcdpix_div - 1; ++ ++ cpccr = REG_CPM_CPCCR; ++ cpccr &= ~CPM_CPCCR_LDIV_MASK; ++ cpccr |= ((new_lcd_div - 1) << CPM_CPCCR_LDIV_BIT); ++ cpccr |= CPM_CPCCR_CE; /* update immediately */ ++ ++ /* update register to change the clocks. ++ * align this code to a cache line. ++ */ ++ __asm__ __volatile__( ++ ".set noreorder\n\t" ++ ".align 5\n" ++ "sw %1,0(%0)\n\t" ++ "li %3,0\n\t" ++ "1:\n\t" ++ "bne %3,%2,1b\n\t" ++ "addi %3, 1\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ ".set reorder\n\t" ++ : ++ : "r" (CPM_CPCCR), "r" (cpccr), "r" (wait), "r" (tmp)); ++} ++ ++static void jz_scale_pll(struct dpm_regs *regs) ++{ ++ unsigned int cppcr; ++ unsigned int cur_mclk, new_mclk, new_pll; ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ int od[] = {1, 2, 2, 4}; ++ ++ cppcr = REG_CPM_CPPCR; ++ cppcr &= ~(regs->cppcr_mask | CPM_CPPCR_PLLS | CPM_CPPCR_PLLEN | CPM_CPPCR_PLLST_MASK); ++ regs->cppcr &= ~CPM_CPPCR_PLLEN; ++ cppcr |= (regs->cppcr | 0xff); ++ ++ /* Update some DRAM parameters before changing frequency */ ++ new_pll = JZ_EXTAL * ((cppcr>>23)+2) / ((((cppcr>>18)&0x1f)+2) * od[(cppcr>>16)&0x03]); ++ cur_mclk = __cpm_get_mclk(); ++ new_mclk = new_pll / div[(REG_CPM_CPCCR>>16) & 0xf]; ++ ++ /* ++ * Update some SDRAM parameters ++ */ ++ jz_update_dram_prev(cur_mclk, new_mclk); ++ ++ /* ++ * Update PLL, align code to cache line. ++ */ ++ cppcr |= CPM_CPPCR_PLLEN; ++ __asm__ __volatile__( ++ ".set noreorder\n\t" ++ ".align 5\n" ++ "sw %1,0(%0)\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ "nop\n\t" ++ ".set reorder\n\t" ++ : ++ : "r" (CPM_CPPCR), "r" (cppcr)); ++ ++ /* Update some other DRAM parameters after changing frequency */ ++ jz_update_dram_post(cur_mclk, new_mclk); ++} ++#endif ++ ++static void jz4750d_transition(struct dpm_regs *regs) ++{ ++ /* ++ * Get and save some boot-time conditions. ++ */ ++ jz_init_boot_config(); ++ ++#ifdef CHANGE_PLL ++ /* ++ * Disable LCD before scaling pll. ++ * LCD and LCD pixel clocks should not be changed even if the PLL ++ * output frequency has been changed. ++ */ ++ REG_LCD_CTRL &= ~LCD_CTRL_ENA; ++ ++ /* ++ * Stop module clocks before scaling PLL ++ */ ++ __cpm_stop_eth(); ++ __cpm_stop_aic(1); ++ __cpm_stop_aic(2); ++#endif ++ ++ /* ... add more as necessary */ ++ ++ if (regs->pll_up_flag == PLL_GOES_UP) { ++ /* the pll frequency is going up, so change dividors first */ ++ jz_scale_divisors(regs); ++#ifdef CHANGE_PLL ++ jz_scale_pll(regs); ++#endif ++ } ++ else if (regs->pll_up_flag == PLL_GOES_DOWN) { ++ /* the pll frequency is going down, so change pll first */ ++#ifdef CHANGE_PLL ++ jz_scale_pll(regs); ++#endif ++ jz_scale_divisors(regs); ++ } ++ else { ++ /* the pll frequency is unchanged, so change divisors only */ ++ jz_scale_divisors(regs); ++ } ++ ++#ifdef CHANGE_PLL ++ /* ++ * Restart module clocks before scaling PLL ++ */ ++ __cpm_start_eth(); ++ __cpm_start_aic(1); ++ __cpm_start_aic(2); ++ ++ /* ... add more as necessary */ ++ ++ /* Scale the LCD divisors after scaling pll */ ++ if (regs->pll_up_flag != PLL_UNCHANGED) { ++ jz_scale_lcd_divisors(regs); ++ } ++ ++ /* Enable LCD controller */ ++ REG_LCD_CTRL &= ~LCD_CTRL_DIS; ++ REG_LCD_CTRL |= LCD_CTRL_ENA; ++#endif ++ ++ /* Update system clocks */ ++ jz_update_clocks(); ++} ++ ++extern unsigned int idle_times; ++static unsigned int jz4750d_freq_get(unsigned int cpu) ++{ ++ return (__cpm_get_cclk() / 1000); ++} ++ ++static unsigned int index_to_divisor(unsigned int index, struct dpm_regs *regs) ++{ ++ int n2FR[33] = { ++ 0, 0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 0, 6, 0, 0, 0, ++ 7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, ++ 9 ++ }; ++ int div[4] = {1, 2, 2, 2}; /* divisors of I:S:P:M */ ++ unsigned int div_of_cclk, new_freq, i; ++ ++ regs->pll_up_flag = PLL_UNCHANGED; ++ regs->cpccr_mask = CPM_CPCCR_CDIV_MASK | CPM_CPCCR_HDIV_MASK | CPM_CPCCR_PDIV_MASK | CPM_CPCCR_MDIV_MASK; ++ ++ new_freq = jz4750d_freq_table.table[index].frequency; ++ ++ do { ++ div_of_cclk = __cpm_get_pllout() / (1000 * new_freq); ++ } while (div_of_cclk==0); ++ ++ if(div_of_cclk == 1 || div_of_cclk == 2 || div_of_cclk == 4) { ++ for(i = 1; i<4; i++) { ++ div[i] = 3; ++ } ++ } else { ++ for(i = 1; i<4; i++) { ++ div[i] = 2; ++ } ++ } ++ ++ for(i = 0; i<4; i++) { ++ div[i] *= div_of_cclk; ++ } ++ ++ dprintk("divisors of I:S:P:M = %d:%d:%d:%d\n", div[0], div[1], div[2], div[3]); ++ ++ regs->cpccr = ++ (n2FR[div[0]] << CPM_CPCCR_CDIV_BIT) | ++ (n2FR[div[1]] << CPM_CPCCR_HDIV_BIT) | ++ (n2FR[div[2]] << CPM_CPCCR_PDIV_BIT) | ++ (n2FR[div[3]] << CPM_CPCCR_MDIV_BIT); ++ ++ return div_of_cclk; ++} ++ ++static void jz4750d_set_cpu_divider_index(unsigned int cpu, unsigned int index) ++{ ++ unsigned long divisor, old_divisor; ++ struct cpufreq_freqs freqs; ++ struct dpm_regs regs; ++ ++ old_divisor = __cpm_get_pllout() / __cpm_get_cclk(); ++ divisor = index_to_divisor(index, ®s); ++ ++ freqs.old = __cpm_get_cclk() / 1000; ++ freqs.new = __cpm_get_pllout() / (1000 * divisor); ++ freqs.cpu = cpu; ++ ++ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); ++ ++ if (old_divisor != divisor) ++ jz4750d_transition(®s); ++ ++ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); ++} ++ ++static int jz4750d_freq_target(struct cpufreq_policy *policy, ++ unsigned int target_freq, ++ unsigned int relation) ++{ ++ unsigned int new_index = 0; ++ ++ if (cpufreq_frequency_table_target(policy, ++ &jz4750d_freq_table.table[0], ++ target_freq, relation, &new_index)) ++ return -EINVAL; ++ ++ jz4750d_set_cpu_divider_index(policy->cpu, new_index); ++ ++ dprintk("new frequency is %d KHz (REG_CPM_CPCCR:0x%x)\n", __cpm_get_cclk() / 1000, REG_CPM_CPCCR); ++ ++ return 0; ++} ++ ++static int jz4750d_freq_verify(struct cpufreq_policy *policy) ++{ ++ return cpufreq_frequency_table_verify(policy, ++ &jz4750d_freq_table.table[0]); ++} ++ ++static int __init jz4750d_cpufreq_driver_init(struct cpufreq_policy *policy) ++{ ++ ++ struct cpufreq_frequency_table *table = &jz4750d_freq_table.table[0]; ++ unsigned int MAX_FREQ; ++ ++ dprintk(KERN_INFO "Jz4750d cpufreq driver\n"); ++ ++ if (policy->cpu != 0) ++ return -EINVAL; ++ ++ policy->cur = MAX_FREQ = __cpm_get_cclk() / 1000; /* in kHz. Current and max frequency is determined by u-boot */ ++ policy->governor = CPUFREQ_DEFAULT_GOVERNOR; ++ ++ policy->cpuinfo.min_freq = MAX_FREQ/8; ++ policy->cpuinfo.max_freq = MAX_FREQ; ++ policy->cpuinfo.transition_latency = 100000; /* in 10^(-9) s = nanoseconds */ ++ ++ table[0].index = 0; ++ table[0].frequency = MAX_FREQ/8; ++ table[1].index = 1; ++ table[1].frequency = MAX_FREQ/6; ++ table[2].index = 2; ++ table[2].frequency = MAX_FREQ/4; ++ table[3].index = 3; ++ table[3].frequency = MAX_FREQ/3; ++ table[4].index = 4; ++ table[4].frequency = MAX_FREQ/2; ++ table[5].index = 5; ++ table[5].frequency = MAX_FREQ; ++ table[6].index = 6; ++ table[6].frequency = CPUFREQ_TABLE_END; ++ ++#ifdef CONFIG_CPU_FREQ_STAT_DETAILS ++ cpufreq_frequency_table_get_attr(table, policy->cpu); /* for showing /sys/devices/system/cpu/cpuX/cpufreq/stats/ */ ++#endif ++ ++ return cpufreq_frequency_table_cpuinfo(policy, table); ++} ++ ++static struct cpufreq_driver cpufreq_jz4750d_driver = { ++// .flags = CPUFREQ_STICKY, ++ .init = jz4750d_cpufreq_driver_init, ++ .verify = jz4750d_freq_verify, ++ .target = jz4750d_freq_target, ++ .get = jz4750d_freq_get, ++ .name = "jz4750d", ++}; ++ ++static int __init jz4750d_cpufreq_init(void) ++{ ++ return cpufreq_register_driver(&cpufreq_jz4750d_driver); ++} ++ ++static void __exit jz4750d_cpufreq_exit(void) ++{ ++ cpufreq_unregister_driver(&cpufreq_jz4750d_driver); ++} ++ ++module_init(jz4750d_cpufreq_init); ++module_exit(jz4750d_cpufreq_exit); ++ ++MODULE_AUTHOR("Regen "); ++MODULE_DESCRIPTION("cpufreq driver for Jz4750d"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/arch/mips/jz4750d/dma.c linux-2.6.24.7/arch/mips/jz4750d/dma.c +--- linux-2.6.24.7.old/arch/mips/jz4750d/dma.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750d/dma.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,822 @@ ++/* ++ * linux/arch/mips/jz4750d/dma.c ++ * ++ * Support functions for the JZ4750D internal DMA channels. ++ * No-descriptor transfer only. ++ * Descriptor transfer should also call jz_request_dma() to get a free ++ * channel and call jz_free_dma() to free the channel. And driver should ++ * build the DMA descriptor and setup the DMA channel by itself. ++ * ++ * Copyright (C) 2006 - 2008 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* ++ * A note on resource allocation: ++ * ++ * All drivers needing DMA channels, should allocate and release them ++ * through the public routines `jz_request_dma()' and `jz_free_dma()'. ++ * ++ * In order to avoid problems, all processes should allocate resources in ++ * the same sequence and release them in the reverse order. ++ * ++ * So, when allocating DMAs and IRQs, first allocate the DMA, then the IRQ. ++ * When releasing them, first release the IRQ, then release the DMA. The ++ * main reason for this order is that, if you are requesting the DMA buffer ++ * done interrupt, you won't know the irq number until the DMA channel is ++ * returned from jz_request_dma(). ++ */ ++ ++struct jz_dma_chan jz_dma_table[MAX_DMA_NUM] = { ++ {dev_id:DMA_ID_BCH_ENC,}, /* DMAC0 channel 0, reserved for BCH */ ++ {dev_id:-1,}, /* DMAC0 channel 1 */ ++ {dev_id:-1,}, /* DMAC0 channel 2 */ ++ {dev_id:-1,}, /* DMAC0 channel 3 */ ++ {dev_id:-1,}, /* DMAC1 channel 0 */ ++ {dev_id:-1,}, /* DMAC1 channel 1 */ ++ {dev_id:-1,}, /* DMAC1 channel 2 */ ++ {dev_id:-1,}, /* DMAC1 channel 3 */ ++}; ++ ++// Device FIFO addresses and default DMA modes ++static const struct { ++ unsigned int fifo_addr; ++ unsigned int dma_mode; ++ unsigned int dma_source; ++} dma_dev_table[DMA_ID_MAX] = { ++ {0, DMA_AUTOINIT, DMAC_DRSR_RS_EXT}, /* External request with DREQn */ ++ {0x18000000, DMA_AUTOINIT, DMAC_DRSR_RS_NAND}, /* NAND request */ ++ {CPHYSADDR(BCH_DR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_BCH_ENC}, ++ {CPHYSADDR(BCH_DR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_BCH_DEC}, ++ {0, DMA_AUTOINIT, DMAC_DRSR_RS_AUTO}, ++// {CPHYSADDR(TSSI_FIFO), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_TSSIIN}, ++ {CPHYSADDR(UART3_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART3OUT}, ++ {CPHYSADDR(UART3_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART3IN}, ++ {CPHYSADDR(UART2_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART2OUT}, ++ {CPHYSADDR(UART2_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART2IN}, ++ {CPHYSADDR(UART1_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART1OUT}, ++ {CPHYSADDR(UART1_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART1IN}, ++ {CPHYSADDR(UART0_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART0OUT}, ++ {CPHYSADDR(UART0_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART0IN}, ++ {CPHYSADDR(SSI_DR(0)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_SSI0OUT}, ++ {CPHYSADDR(SSI_DR(0)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SSI0IN}, ++ {CPHYSADDR(AIC_DR), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_AICOUT}, ++ {CPHYSADDR(AIC_DR), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_AICIN}, ++ {CPHYSADDR(MSC_TXFIFO(0)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_MSC0OUT}, ++ {CPHYSADDR(MSC_RXFIFO(0)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_MSC0IN}, ++ {0, DMA_AUTOINIT, DMAC_DRSR_RS_TCU}, ++ {SADC_TSDAT, DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SADC},/* Touch Screen Data Register */ ++ {CPHYSADDR(MSC_TXFIFO(1)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_MSC1OUT}, /* SSC1 TX */ ++ {CPHYSADDR(MSC_RXFIFO(1)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_MSC1IN}, /* SSC1 RX */ ++ {CPHYSADDR(SSI_DR(1)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_SSI1OUT}, ++ {CPHYSADDR(SSI_DR(1)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SSI1IN}, ++ {CPHYSADDR(PCM_DP), DMA_16BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_PMOUT}, ++ {CPHYSADDR(PCM_DP), DMA_16BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_PMIN}, ++ {}, ++}; ++ ++ ++int jz_dma_read_proc(char *buf, char **start, off_t fpos, ++ int length, int *eof, void *data) ++{ ++ int i, len = 0; ++ struct jz_dma_chan *chan; ++ ++ for (i = 0; i < MAX_DMA_NUM; i++) { ++ if ((chan = get_dma_chan(i)) != NULL) { ++ len += sprintf(buf + len, "%2d: %s\n", ++ i, chan->dev_str); ++ } ++ } ++ ++ if (fpos >= len) { ++ *start = buf; ++ *eof = 1; ++ return 0; ++ } ++ *start = buf + fpos; ++ if ((len -= fpos) > length) ++ return length; ++ *eof = 1; ++ return len; ++} ++ ++ ++void dump_jz_dma_channel(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan; ++ ++ if (dmanr > MAX_DMA_NUM) ++ return; ++ chan = &jz_dma_table[dmanr]; ++ ++ printk("DMA%d Registers:\n", dmanr); ++ printk(" DMACR = 0x%08x\n", REG_DMAC_DMACR(chan->io/HALF_DMA_NUM)); ++ printk(" DSAR = 0x%08x\n", REG_DMAC_DSAR(dmanr)); ++ printk(" DTAR = 0x%08x\n", REG_DMAC_DTAR(dmanr)); ++ printk(" DTCR = 0x%08x\n", REG_DMAC_DTCR(dmanr)); ++ printk(" DRSR = 0x%08x\n", REG_DMAC_DRSR(dmanr)); ++ printk(" DCCSR = 0x%08x\n", REG_DMAC_DCCSR(dmanr)); ++ printk(" DCMD = 0x%08x\n", REG_DMAC_DCMD(dmanr)); ++ printk(" DDA = 0x%08x\n", REG_DMAC_DDA(dmanr)); ++ printk(" DMADBR = 0x%08x\n", REG_DMAC_DMADBR(chan->io/HALF_DMA_NUM)); ++} ++ ++ ++/** ++ * jz_request_dma - dynamically allcate an idle DMA channel to return ++ * @dev_id: the specified dma device id or DMA_ID_RAW_SET ++ * @dev_str: the specified dma device string name ++ * @irqhandler: the irq handler, or NULL ++ * @irqflags: the irq handler flags ++ * @irq_dev_id: the irq handler device id for shared irq ++ * ++ * Finds a free channel, and binds the requested device to it. ++ * Returns the allocated channel number, or negative on error. ++ * Requests the DMA done IRQ if irqhandler != NULL. ++ * ++*/ ++/*int jz_request_dma(int dev_id, const char *dev_str, ++ void (*irqhandler)(int, void *, struct pt_regs *), ++ unsigned long irqflags, ++ void *irq_dev_id) ++*/ ++ ++int jz_request_dma(int dev_id, const char *dev_str, ++ irqreturn_t (*irqhandler)(int, void *), ++ unsigned long irqflags, ++ void *irq_dev_id) ++{ ++ struct jz_dma_chan *chan; ++ int i, ret; ++ ++ if (dev_id < 0 || dev_id >= DMA_ID_MAX) ++ return -EINVAL; ++ ++ for (i = 0; i < MAX_DMA_NUM; i++) { ++ if (jz_dma_table[i].dev_id < 0) ++ break; ++ } ++ if (i == MAX_DMA_NUM) /* no free channel */ ++ return -ENODEV; ++ ++ /* we got a free channel */ ++ chan = &jz_dma_table[i]; ++ ++ if (irqhandler) { ++ chan->irq = IRQ_DMA_0 + i; // allocate irq number ++ chan->irq_dev = irq_dev_id; ++ if ((ret = request_irq(chan->irq, irqhandler, irqflags, ++ dev_str, chan->irq_dev))) { ++ chan->irq = -1; ++ chan->irq_dev = NULL; ++ return ret; ++ } ++ } else { ++ chan->irq = -1; ++ chan->irq_dev = NULL; ++ } ++ ++ // fill it in ++ chan->io = i; ++ chan->dev_id = dev_id; ++ chan->dev_str = dev_str; ++ chan->fifo_addr = dma_dev_table[dev_id].fifo_addr; ++ chan->mode = dma_dev_table[dev_id].dma_mode; ++ chan->source = dma_dev_table[dev_id].dma_source; ++ ++ if (i < HALF_DMA_NUM) ++ REG_DMAC_DMACKE(0) = 1 << i; ++ else ++ REG_DMAC_DMACKE(1) = 1 << (i - HALF_DMA_NUM); ++ ++ return i; ++} ++ ++void jz_free_dma(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) { ++ printk("Trying to free DMA%d\n", dmanr); ++ return; ++ } ++ ++ disable_dma(dmanr); ++ if (chan->irq) ++ free_irq(chan->irq, chan->irq_dev); ++ ++ chan->irq = -1; ++ chan->irq_dev = NULL; ++ chan->dev_id = -1; ++} ++ ++void jz_set_dma_dest_width(int dmanr, int nbit) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ chan->mode &= ~DMAC_DCMD_DWDH_MASK; ++ switch (nbit) { ++ case 8: ++ chan->mode |= DMAC_DCMD_DWDH_8; ++ break; ++ case 16: ++ chan->mode |= DMAC_DCMD_DWDH_16; ++ break; ++ case 32: ++ chan->mode |= DMAC_DCMD_DWDH_32; ++ break; ++ } ++} ++ ++void jz_set_dma_src_width(int dmanr, int nbit) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ chan->mode &= ~DMAC_DCMD_SWDH_MASK; ++ switch (nbit) { ++ case 8: ++ chan->mode |= DMAC_DCMD_SWDH_8; ++ break; ++ case 16: ++ chan->mode |= DMAC_DCMD_SWDH_16; ++ break; ++ case 32: ++ chan->mode |= DMAC_DCMD_SWDH_32; ++ break; ++ } ++} ++ ++void jz_set_dma_block_size(int dmanr, int nbyte) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ chan->mode &= ~DMAC_DCMD_DS_MASK; ++ switch (nbyte) { ++ case 1: ++ chan->mode |= DMAC_DCMD_DS_8BIT; ++ break; ++ case 2: ++ chan->mode |= DMAC_DCMD_DS_16BIT; ++ break; ++ case 4: ++ chan->mode |= DMAC_DCMD_DS_32BIT; ++ break; ++ case 16: ++ chan->mode |= DMAC_DCMD_DS_16BYTE; ++ break; ++ case 32: ++ chan->mode |= DMAC_DCMD_DS_32BYTE; ++ break; ++ } ++} ++ ++unsigned int jz_get_dma_command(int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ return chan->mode; ++} ++ ++/** ++ * jz_set_dma_mode - do the raw settings for the specified DMA channel ++ * @dmanr: the specified DMA channel ++ * @mode: dma operate mode, DMA_MODE_READ or DMA_MODE_WRITE ++ * @dma_mode: dma raw mode ++ * @dma_source: dma raw request source ++ * @fifo_addr: dma raw device fifo address ++ * ++ * Ensure call jz_request_dma(DMA_ID_RAW_SET, ...) first, then call ++ * jz_set_dma_mode() rather than set_dma_mode() if you work with ++ * and external request dma device. ++ * ++ * NOTE: Don not dynamically allocate dma channel if one external request ++ * dma device will occupy this channel. ++*/ ++int jz_set_dma_mode(unsigned int dmanr, unsigned int mode, ++ unsigned int dma_mode, unsigned int dma_source, ++ unsigned int fifo_addr) ++{ ++ int dev_id, i; ++ struct jz_dma_chan *chan; ++ ++ if (dmanr > MAX_DMA_NUM) ++ return -ENODEV; ++ for (i = 0; i < MAX_DMA_NUM; i++) { ++ if (jz_dma_table[i].dev_id < 0) ++ break; ++ } ++ if (i == MAX_DMA_NUM) ++ return -ENODEV; ++ ++ chan = &jz_dma_table[dmanr]; ++ dev_id = chan->dev_id; ++ if (dev_id > 0) { ++ printk(KERN_DEBUG "%s sets the allocated DMA channel %d!\n", ++ __FUNCTION__, dmanr); ++ return -ENODEV; ++ } ++ ++ /* clone it from the dynamically allocated. */ ++ if (i != dmanr) { ++ chan->irq = jz_dma_table[i].irq; ++ chan->irq_dev = jz_dma_table[i].irq_dev; ++ chan->dev_str = jz_dma_table[i].dev_str; ++ jz_dma_table[i].irq = 0; ++ jz_dma_table[i].irq_dev = NULL; ++ jz_dma_table[i].dev_id = -1; ++ } ++ chan->dev_id = DMA_ID_RAW_SET; ++ chan->io = dmanr; ++ chan->fifo_addr = fifo_addr; ++ chan->mode = dma_mode; ++ chan->source = dma_source; ++ ++ set_dma_mode(dmanr, dma_mode); ++ ++ return dmanr; ++} ++ ++void enable_dma(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ REG_DMAC_DCCSR(dmanr) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); ++ REG_DMAC_DCCSR(dmanr) |= DMAC_DCCSR_NDES; /* No-descriptor transfer */ ++ __dmac_enable_channel(dmanr); ++ if (chan->irq) ++ __dmac_channel_enable_irq(dmanr); ++} ++ ++#define DMA_DISABLE_POLL 0x10000 ++ ++void disable_dma(unsigned int dmanr) ++{ ++ int i; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ if (!__dmac_channel_enabled(dmanr)) ++ return; ++ ++ for (i = 0; i < DMA_DISABLE_POLL; i++) ++ if (__dmac_channel_transmit_end_detected(dmanr)) ++ break; ++#if 0 ++ if (i == DMA_DISABLE_POLL) ++ printk(KERN_INFO "disable_dma: poll expired!\n"); ++#endif ++ ++ __dmac_disable_channel(dmanr); ++ if (chan->irq) ++ __dmac_channel_disable_irq(dmanr); ++} ++ ++/* Note: DMA_MODE_MASK is simulated by sw */ ++void set_dma_mode(unsigned int dmanr, unsigned int mode) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ if (mode == DMA_MODE_READ) { ++ chan->mode |= DMAC_DCMD_DAI; ++ chan->mode &= ~DMAC_DCMD_SAI; ++ } else if (mode == DMA_MODE_WRITE) { ++ chan->mode |= DMAC_DCMD_SAI; ++ chan->mode &= ~DMAC_DCMD_DAI; ++ } else { ++ printk(KERN_DEBUG "set_dma_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); ++ } ++ REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK; ++ REG_DMAC_DRSR(chan->io) = chan->source; ++} ++ ++void set_dma_addr(unsigned int dmanr, unsigned int phyaddr) ++{ ++ unsigned int mode; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ mode = chan->mode & DMA_MODE_MASK; ++ if (mode == DMA_MODE_READ) { ++ REG_DMAC_DSAR(chan->io) = chan->fifo_addr; ++ REG_DMAC_DTAR(chan->io) = phyaddr; ++ } else if (mode == DMA_MODE_WRITE) { ++ REG_DMAC_DSAR(chan->io) = phyaddr; ++ REG_DMAC_DTAR(chan->io) = chan->fifo_addr; ++ } else ++ printk(KERN_DEBUG "Driver should call set_dma_mode() ahead set_dma_addr()!\n"); ++} ++ ++void set_dma_count(unsigned int dmanr, unsigned int bytecnt) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ int dma_ds[] = {4, 1, 2, 16, 32}; ++ unsigned int ds; ++ ++ if (!chan) ++ return; ++ ++ ds = (chan->mode & DMAC_DCMD_DS_MASK) >> DMAC_DCMD_DS_BIT; ++ REG_DMAC_DTCR(chan->io) = bytecnt / dma_ds[ds]; // transfer count ++} ++ ++unsigned int get_dma_residue(unsigned int dmanr) ++{ ++ unsigned int count, ds; ++ int dma_ds[] = {4, 1, 2, 16, 32}; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 0; ++ ++ ds = (chan->mode & DMAC_DCMD_DS_MASK) >> DMAC_DCMD_DS_BIT; ++ count = REG_DMAC_DTCR(chan->io); ++ count = count * dma_ds[ds]; ++ ++ return count; ++} ++ ++void jz_set_oss_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ switch (audio_fmt) { ++ case AFMT_U8: ++ /* burst mode : 32BIT */ ++ break; ++ case AFMT_S16_LE: ++ /* burst mode : 16BYTE */ ++ if (mode == DMA_MODE_READ) { ++ chan->mode = DMA_AIC_32_16BYTE_RX_CMD | DMA_MODE_READ; ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCMD_DAI; ++ chan->mode &= ~DMAC_DCMD_SAI; ++ } else if (mode == DMA_MODE_WRITE) { ++ chan->mode = DMA_AIC_32_16BYTE_TX_CMD | DMA_MODE_WRITE; ++ //chan->mode = DMA_AIC_16BYTE_TX_CMD | DMA_MODE_WRITE; ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCMD_SAI; ++ chan->mode &= ~DMAC_DCMD_DAI; ++ } else ++ printk("oss_dma_burst_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); ++ ++ REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK; ++ REG_DMAC_DRSR(chan->io) = chan->source; ++ break; ++ } ++} ++ ++void jz_set_alsa_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ ++ if (!chan) ++ return; ++ ++ switch (audio_fmt) { ++ case 8: ++ /* SNDRV_PCM_FORMAT_S8 burst mode : 32BIT */ ++ break; ++ case 16: ++ /* SNDRV_PCM_FORMAT_S16_LE burst mode : 16BYTE */ ++ if (mode == DMA_MODE_READ) { ++ chan->mode = DMA_AIC_16BYTE_RX_CMD | DMA_MODE_READ; ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCMD_DAI; ++ chan->mode &= ~DMAC_DCMD_SAI; ++ } else if (mode == DMA_MODE_WRITE) { ++ chan->mode = DMA_AIC_16BYTE_TX_CMD | DMA_MODE_WRITE; ++ chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI); ++ mode &= DMA_MODE_MASK; ++ chan->mode |= DMAC_DCMD_SAI; ++ chan->mode &= ~DMAC_DCMD_DAI; ++ } else ++ printk("alsa_dma_burst_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); ++ ++ REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK; ++ REG_DMAC_DRSR(chan->io) = chan->source; ++ break; ++ } ++} ++ ++//#define JZ4750D_DMAC_TEST_ENABLE ++#undef JZ4750D_DMAC_TEST_ENABLE ++ ++#ifdef JZ4750D_DMAC_TEST_ENABLE ++ ++/* ++ * DMA test: external address <--> external address ++ */ ++#define TEST_DMA_SIZE 16*1024 ++ ++static jz_dma_desc *dma_desc; ++ ++static int dma_chan; ++static dma_addr_t dma_desc_phys_addr; ++static unsigned int dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr; ++ ++static int dma_check_result(void *src, void *dst, int size) ++{ ++ unsigned int addr1, addr2, i, err = 0; ++ ++ addr1 = (unsigned int)src; ++ addr2 = (unsigned int)dst; ++ ++ for (i = 0; i < size; i += 4) { ++ if (*(volatile unsigned int *)addr1 != *(volatile unsigned int *)addr2) { ++ err++; ++ printk("wrong data at 0x%08x: src 0x%08x dst 0x%08x\n", addr2, *(volatile unsigned int *)addr1, *(volatile unsigned int *)addr2); ++ } ++ addr1 += 4; ++ addr2 += 4; ++ } ++ printk("check DMA result err=%d\n", err); ++ return err; ++} ++ ++static irqreturn_t jz4750d_dma_irq(int irq, void *dev_id) ++{ ++ printk("jz4750d_dma_irq %d\n", irq); ++ ++ ++ if (__dmac_channel_transmit_halt_detected(dma_chan)) { ++ printk("DMA HALT\n"); ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ __dmac_channel_clear_transmit_halt(dma_chan); ++ } ++ ++ if (__dmac_channel_address_error_detected(dma_chan)) { ++ printk("DMA ADDR ERROR\n"); ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ REG_DMAC_DSAR(dma_chan) = 0; /* clear source address register */ ++ REG_DMAC_DTAR(dma_chan) = 0; /* clear target address register */ ++ __dmac_channel_clear_address_error(dma_chan); ++ } ++ ++ if (__dmac_channel_descriptor_invalid_detected(dma_chan)) { ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ printk("DMA DESC INVALID\n"); ++ __dmac_channel_clear_descriptor_invalid(dma_chan); ++ } ++ ++ if (__dmac_channel_count_terminated_detected(dma_chan)) { ++ printk("DMA CT\n"); ++ __dmac_channel_clear_count_terminated(dma_chan); ++ } ++ ++ if (__dmac_channel_transmit_end_detected(dma_chan)) { ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ printk("DMA TT\n"); ++ __dmac_channel_clear_transmit_end(dma_chan); ++ dump_jz_dma_channel(dma_chan); ++ dma_check_result((void *)dma_src_addr, (void *)dma_dst_addr, TEST_DMA_SIZE); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++void dma_nodesc_test(void) ++{ ++ unsigned int addr, i; ++ ++ printk("dma_nodesc_test\n"); ++ ++ /* Request DMA channel and setup irq handler */ ++ dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", jz4750d_dma_irq, ++ IRQF_DISABLED, NULL); ++ if (dma_chan < 0) { ++ printk("Setup irq failed\n"); ++ return; ++ } ++ ++ printk("Requested DMA channel = %d\n", dma_chan); ++ ++ /* Allocate DMA buffers */ ++ dma_src_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ ++ dma_dst_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ ++ ++ dma_src_phys_addr = CPHYSADDR(dma_src_addr); ++ dma_dst_phys_addr = CPHYSADDR(dma_dst_addr); ++ ++ printk("Buffer addresses: 0x%08x 0x%08x 0x%08x 0x%08x\n", ++ dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr); ++ ++ /* Prepare data for source buffer */ ++ addr = (unsigned int)dma_src_addr; ++ for (i = 0; i < TEST_DMA_SIZE; i += 4) { ++ *(volatile unsigned int *)addr = addr; ++ addr += 4; ++ } ++ dma_cache_wback((unsigned long)dma_src_addr, TEST_DMA_SIZE); ++ ++ /* Init target buffer */ ++ memset((void *)dma_dst_addr, 0, TEST_DMA_SIZE); ++ dma_cache_wback((unsigned long)dma_dst_addr, TEST_DMA_SIZE); ++ ++ /* Init DMA module */ ++ printk("Starting DMA\n"); ++ REG_DMAC_DMACR(dma_chan/HALF_DMA_NUM) = 0; ++ REG_DMAC_DCCSR(dma_chan) = 0; ++ REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO; ++ REG_DMAC_DSAR(dma_chan) = dma_src_phys_addr; ++ REG_DMAC_DTAR(dma_chan) = dma_dst_phys_addr; ++ REG_DMAC_DTCR(dma_chan) = 512; ++ REG_DMAC_DCMD(dma_chan) = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_TIE; ++ REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; ++ REG_DMAC_DMACR(dma_chan/HALF_DMA_NUM) = DMAC_DMACR_DMAE; /* global DMA enable bit */ ++ ++ printk("DMA started. IMR=%08x\n", REG_INTC_IMR); ++ ++ /* wait a long time, ensure transfer end */ ++ printk("wait 3s...\n"); ++ mdelay(3000); /* wait 3s */ ++ ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ /* free buffers */ ++ printk("free DMA buffers\n"); ++ free_pages(dma_src_addr, 2); ++ free_pages(dma_dst_addr, 2); ++ ++ if (dma_desc) ++ free_pages((unsigned int)dma_desc, 0); ++ ++ /* free dma */ ++ jz_free_dma(dma_chan); ++} ++ ++void dma_desc_test(void) ++{ ++ unsigned int next, addr, i; ++ static jz_dma_desc *desc; ++ ++ printk("dma_desc_test\n"); ++ ++ /* Request DMA channel and setup irq handler */ ++ dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", jz4750d_dma_irq, ++ IRQF_DISABLED, NULL); ++ if (dma_chan < 0) { ++ printk("Setup irq failed\n"); ++ return; ++ } ++ ++ printk("Requested DMA channel = %d\n", dma_chan); ++ ++ /* Allocate DMA buffers */ ++ dma_src_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ ++ dma_dst_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */ ++ ++ dma_src_phys_addr = CPHYSADDR(dma_src_addr); ++ dma_dst_phys_addr = CPHYSADDR(dma_dst_addr); ++ ++ printk("Buffer addresses: 0x%08x 0x%08x 0x%08x 0x%08x\n", ++ dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr); ++ ++ /* Prepare data for source buffer */ ++ addr = (unsigned int)dma_src_addr; ++ for (i = 0; i < TEST_DMA_SIZE; i += 4) { ++ *(volatile unsigned int *)addr = addr; ++ addr += 4; ++ } ++ dma_cache_wback((unsigned long)dma_src_addr, TEST_DMA_SIZE); ++ ++ /* Init target buffer */ ++ memset((void *)dma_dst_addr, 0, TEST_DMA_SIZE); ++ dma_cache_wback((unsigned long)dma_dst_addr, TEST_DMA_SIZE); ++ ++ /* Allocate DMA descriptors */ ++ dma_desc = (jz_dma_desc *)__get_free_pages(GFP_KERNEL, 0); ++ dma_desc_phys_addr = CPHYSADDR((unsigned long)dma_desc); ++ ++ printk("DMA descriptor address: 0x%08x 0x%08x\n", (u32)dma_desc, dma_desc_phys_addr); ++ ++ /* Setup DMA descriptors */ ++ desc = dma_desc; ++ next = (dma_desc_phys_addr + (sizeof(jz_dma_desc))) >> 4; ++ ++ desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK; ++ desc->dsadr = dma_src_phys_addr; /* DMA source address */ ++ desc->dtadr = dma_dst_phys_addr; /* DMA target address */ ++ desc->ddadr = (next << 24) + 128; /* size: 128*32 bytes = 4096 bytes */ ++ ++ desc++; ++ next = (dma_desc_phys_addr + 2*(sizeof(jz_dma_desc))) >> 4; ++ ++ desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK; ++ desc->dsadr = dma_src_phys_addr + 4096; /* DMA source address */ ++ desc->dtadr = dma_dst_phys_addr + 4096; /* DMA target address */ ++ desc->ddadr = (next << 24) + 256; /* size: 256*16 bytes = 4096 bytes */ ++ ++ desc++; ++ next = (dma_desc_phys_addr + 3*(sizeof(jz_dma_desc))) >> 4; ++ ++ desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK; ++ desc->dsadr = dma_src_phys_addr + 8192; /* DMA source address */ ++ desc->dtadr = dma_dst_phys_addr + 8192; /* DMA target address */ ++ desc->ddadr = (next << 24) + 256; /* size: 256*16 bytes = 4096 bytes */ ++ ++ desc++; ++ next = (dma_desc_phys_addr + 4*(sizeof(jz_dma_desc))) >> 4; ++ ++ desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE; ++ desc->dsadr = dma_src_phys_addr + 12*1024; /* DMA source address */ ++ desc->dtadr = dma_dst_phys_addr + 12*1024; /* DMA target address */ ++ desc->ddadr = (next << 24) + 1024; /* size: 1024*4 bytes = 4096 bytes */ ++ ++ dma_cache_wback((unsigned long)dma_desc, 4*(sizeof(jz_dma_desc))); ++ ++ /* Setup DMA descriptor address */ ++ REG_DMAC_DDA(dma_chan) = dma_desc_phys_addr; ++ ++ /* Setup request source */ ++ REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO; ++ ++ /* Setup DMA channel control/status register */ ++ REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_EN; /* descriptor transfer, clear status, start channel */ ++ ++ /* Enable DMA */ ++ REG_DMAC_DMACR(dma_chan/HALF_DMA_NUM) = DMAC_DMACR_DMAE; ++ ++ /* DMA doorbell set -- start DMA now ... */ ++ REG_DMAC_DMADBSR(dma_chan/HALF_DMA_NUM) = 1 << dma_chan; ++ ++ printk("DMA started. IMR=%08x\n", REG_INTC_IMR); ++ /* wait a long time, ensure transfer end */ ++ printk("wait 3s...\n"); ++ mdelay(3000); /* wait 3s */ ++ ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ /* free buffers */ ++ printk("free DMA buffers\n"); ++ free_pages(dma_src_addr, 2); ++ free_pages(dma_dst_addr, 2); ++ ++ if (dma_desc) ++ free_pages((unsigned int)dma_desc, 0); ++ ++ /* free dma */ ++ jz_free_dma(dma_chan); ++} ++ ++#endif ++ ++//EXPORT_SYMBOL_NOVERS(jz_dma_table); ++EXPORT_SYMBOL(jz_dma_table); ++EXPORT_SYMBOL(jz_request_dma); ++EXPORT_SYMBOL(jz_free_dma); ++EXPORT_SYMBOL(jz_set_dma_src_width); ++EXPORT_SYMBOL(jz_set_dma_dest_width); ++EXPORT_SYMBOL(jz_set_dma_block_size); ++EXPORT_SYMBOL(jz_set_dma_mode); ++EXPORT_SYMBOL(set_dma_mode); ++EXPORT_SYMBOL(jz_set_oss_dma); ++EXPORT_SYMBOL(jz_set_alsa_dma); ++EXPORT_SYMBOL(set_dma_addr); ++EXPORT_SYMBOL(set_dma_count); ++EXPORT_SYMBOL(get_dma_residue); ++EXPORT_SYMBOL(enable_dma); ++EXPORT_SYMBOL(disable_dma); ++EXPORT_SYMBOL(dump_jz_dma_channel); +diff -urN linux-2.6.24.7.old/arch/mips/jz4750d/i2c.c linux-2.6.24.7/arch/mips/jz4750d/i2c.c +--- linux-2.6.24.7.old/arch/mips/jz4750d/i2c.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750d/i2c.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,273 @@ ++/* ++ * linux/arch/mips/jz4750d/i2c.c ++ * ++ * Jz4750D I2C routines. ++ * ++ * Copyright (C) 2005,2006 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* I2C protocol */ ++#define I2C_READ 1 ++#define I2C_WRITE 0 ++ ++#define TIMEOUT 1000 ++ ++/* ++ * I2C bus protocol basic routines ++ */ ++static int i2c_put_data(unsigned char data) ++{ ++ unsigned int timeout = TIMEOUT*10; ++ ++ __i2c_write(data); ++ __i2c_set_drf(); ++ while (__i2c_check_drf() != 0); ++ while (!__i2c_transmit_ended()); ++ while (!__i2c_received_ack() && timeout) ++ timeout--; ++ ++ if (timeout) ++ return 0; ++ else ++ return -ETIMEDOUT; ++} ++ ++#ifdef CONFIG_JZ_TPANEL_ATA2508 ++static int i2c_put_data_nack(unsigned char data) ++{ ++ unsigned int timeout = TIMEOUT*10; ++ ++ __i2c_write(data); ++ __i2c_set_drf(); ++ while (__i2c_check_drf() != 0); ++ while (!__i2c_transmit_ended()); ++ while (timeout--); ++ return 0; ++} ++#endif ++ ++static int i2c_get_data(unsigned char *data, int ack) ++{ ++ int timeout = TIMEOUT*10; ++ ++ if (!ack) ++ __i2c_send_nack(); ++ else ++ __i2c_send_ack(); ++ ++ while (__i2c_check_drf() == 0 && timeout) ++ timeout--; ++ ++ if (timeout) { ++ if (!ack) ++ __i2c_send_stop(); ++ *data = __i2c_read(); ++ __i2c_clear_drf(); ++ return 0; ++ } else ++ return -ETIMEDOUT; ++} ++ ++/* ++ * I2C interface ++ */ ++void i2c_open(void) ++{ ++ __i2c_set_clk(jz_clocks.extalclk, 10000); /* default 10 KHz */ ++ __i2c_enable(); ++} ++ ++void i2c_close(void) ++{ ++ udelay(300); /* wait for STOP goes over. */ ++ __i2c_disable(); ++} ++ ++void i2c_setclk(unsigned int i2cclk) ++{ ++ __i2c_set_clk(jz_clocks.extalclk, i2cclk); ++} ++ ++int i2c_lseek(unsigned char device, unsigned char offset) ++{ ++ __i2c_send_nack(); /* Master does not send ACK, slave sends it */ ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) ++ goto device_err; ++ if (i2c_put_data(offset) < 0) ++ goto address_err; ++ return 0; ++ device_err: ++ printk(KERN_DEBUG "No I2C device (0x%02x) installed.\n", device); ++ __i2c_send_stop(); ++ return -ENODEV; ++ address_err: ++ printk(KERN_DEBUG "No I2C device (0x%02x) response.\n", device); ++ __i2c_send_stop(); ++ return -EREMOTEIO; ++} ++ ++int i2c_read(unsigned char device, unsigned char *buf, ++ unsigned char address, int count) ++{ ++ int cnt = count; ++ int timeout = 5; ++ ++L_try_again: ++ ++ if (timeout < 0) ++ goto L_timeout; ++ ++ __i2c_send_nack(); /* Master does not send ACK, slave sends it */ ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) ++ goto device_werr; ++ if (i2c_put_data(address) < 0) ++ goto address_err; ++ ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_READ ) < 0) ++ goto device_rerr; ++ __i2c_send_ack(); /* Master sends ACK for continue reading */ ++ while (cnt) { ++ if (cnt == 1) { ++ if (i2c_get_data(buf, 0) < 0) ++ break; ++ } else { ++ if (i2c_get_data(buf, 1) < 0) ++ break; ++ } ++ cnt--; ++ buf++; ++ } ++ ++ __i2c_send_stop(); ++ return count - cnt; ++ device_rerr: ++ device_werr: ++ address_err: ++ timeout --; ++ __i2c_send_stop(); ++ goto L_try_again; ++ ++L_timeout: ++ __i2c_send_stop(); ++ printk("Read I2C device 0x%2x failed.\n", device); ++ return -ENODEV; ++} ++ ++int i2c_write(unsigned char device, unsigned char *buf, ++ unsigned char address, int count) ++{ ++ int cnt = count; ++ int cnt_in_pg; ++ int timeout = 5; ++ unsigned char *tmpbuf; ++ unsigned char tmpaddr; ++ ++ __i2c_send_nack(); /* Master does not send ACK, slave sends it */ ++ ++ W_try_again: ++ if (timeout < 0) ++ goto W_timeout; ++ ++ cnt = count; ++ tmpbuf = (unsigned char *)buf; ++ tmpaddr = address; ++ ++ start_write_page: ++ cnt_in_pg = 0; ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) ++ goto device_err; ++#ifdef CONFIG_JZ_TPANEL_ATA2508 ++ if (address == 0xff) { ++ if (i2c_put_data_nack(tmpaddr) < 0) ++ goto address_err; ++ while (cnt) { ++ if (++cnt_in_pg > 8) { ++ __i2c_send_stop(); ++ mdelay(1); ++ tmpaddr += 8; ++ goto start_write_page; ++ } ++ if (i2c_put_data_nack(*tmpbuf) < 0) ++ break; ++ cnt--; ++ tmpbuf++; ++ } ++ } ++ else { ++ ++ if (i2c_put_data(tmpaddr) < 0) ++ goto address_err; ++ while (cnt) { ++ if (++cnt_in_pg > 8) { ++ __i2c_send_stop(); ++ mdelay(1); ++ tmpaddr += 8; ++ goto start_write_page; ++ } ++ if (i2c_put_data(*tmpbuf) < 0) ++ break; ++ cnt--; ++ tmpbuf++; ++ } ++ } ++#else ++ if (i2c_put_data(tmpaddr) < 0) ++ goto address_err; ++ while (cnt) { ++ if (++cnt_in_pg > 8) { ++ __i2c_send_stop(); ++ mdelay(1); ++ tmpaddr += 8; ++ goto start_write_page; ++ } ++ if (i2c_put_data(*tmpbuf) < 0) ++ break; ++ cnt--; ++ tmpbuf++; ++ } ++#endif ++ __i2c_send_stop(); ++ return count - cnt; ++ device_err: ++ address_err: ++ timeout--; ++ __i2c_send_stop(); ++ goto W_try_again; ++ ++ W_timeout: ++ printk(KERN_DEBUG "Write I2C device 0x%2x failed.\n", device); ++ __i2c_send_stop(); ++ return -ENODEV; ++} ++ ++EXPORT_SYMBOL(i2c_open); ++EXPORT_SYMBOL(i2c_close); ++EXPORT_SYMBOL(i2c_setclk); ++EXPORT_SYMBOL(i2c_read); ++EXPORT_SYMBOL(i2c_write); +diff -urN linux-2.6.24.7.old/arch/mips/jz4750d/irq.c linux-2.6.24.7/arch/mips/jz4750d/irq.c +--- linux-2.6.24.7.old/arch/mips/jz4750d/irq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750d/irq.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,299 @@ ++/* ++ * linux/arch/mips/jz4750d/irq.c ++ * ++ * JZ4750D interrupt routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * INTC irq type ++ */ ++ ++static void enable_intc_irq(unsigned int irq) ++{ ++ __intc_unmask_irq(irq); ++} ++ ++static void disable_intc_irq(unsigned int irq) ++{ ++ __intc_mask_irq(irq); ++} ++ ++static void mask_and_ack_intc_irq(unsigned int irq) ++{ ++ __intc_mask_irq(irq); ++ __intc_ack_irq(irq); ++} ++ ++static void end_intc_irq(unsigned int irq) ++{ ++ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { ++ enable_intc_irq(irq); ++ } ++} ++ ++static unsigned int startup_intc_irq(unsigned int irq) ++{ ++ enable_intc_irq(irq); ++ return 0; ++} ++ ++static void shutdown_intc_irq(unsigned int irq) ++{ ++ disable_intc_irq(irq); ++} ++ ++static struct irq_chip intc_irq_type = { ++ .typename = "INTC", ++ .startup = startup_intc_irq, ++ .shutdown = shutdown_intc_irq, ++ .enable = enable_intc_irq, ++ .disable = disable_intc_irq, ++ .ack = mask_and_ack_intc_irq, ++ .end = end_intc_irq, ++}; ++ ++/* ++ * GPIO irq type ++ */ ++ ++static void enable_gpio_irq(unsigned int irq) ++{ ++ unsigned int intc_irq; ++ ++ if (irq < (IRQ_GPIO_0 + 32)) { ++ intc_irq = IRQ_GPIO0; ++ } ++ else if (irq < (IRQ_GPIO_0 + 64)) { ++ intc_irq = IRQ_GPIO1; ++ } ++ else if (irq < (IRQ_GPIO_0 + 96)) { ++ intc_irq = IRQ_GPIO2; ++ } ++ else if (irq < (IRQ_GPIO_0 + 128)) { ++ intc_irq = IRQ_GPIO3; ++ } ++ else if (irq < (IRQ_GPIO_0 + 160)) { ++ intc_irq = IRQ_GPIO4; ++ } ++ else { ++ intc_irq = IRQ_GPIO5; ++ } ++ ++ enable_intc_irq(intc_irq); ++ __gpio_unmask_irq(irq - IRQ_GPIO_0); ++} ++ ++static void disable_gpio_irq(unsigned int irq) ++{ ++ __gpio_mask_irq(irq - IRQ_GPIO_0); ++} ++ ++static void mask_and_ack_gpio_irq(unsigned int irq) ++{ ++ __gpio_mask_irq(irq - IRQ_GPIO_0); ++ __gpio_ack_irq(irq - IRQ_GPIO_0); ++} ++ ++static void end_gpio_irq(unsigned int irq) ++{ ++ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { ++ enable_gpio_irq(irq); ++ } ++} ++ ++static unsigned int startup_gpio_irq(unsigned int irq) ++{ ++ enable_gpio_irq(irq); ++ return 0; ++} ++ ++static void shutdown_gpio_irq(unsigned int irq) ++{ ++ disable_gpio_irq(irq); ++} ++ ++static struct irq_chip gpio_irq_type = { ++ .typename = "GPIO", ++ .startup = startup_gpio_irq, ++ .shutdown = shutdown_gpio_irq, ++ .enable = enable_gpio_irq, ++ .disable = disable_gpio_irq, ++ .ack = mask_and_ack_gpio_irq, ++ .end = end_gpio_irq, ++}; ++ ++/* ++ * DMA irq type ++ */ ++ ++static void enable_dma_irq(unsigned int irq) ++{ ++ unsigned int intc_irq; ++ ++ if ( irq < (IRQ_DMA_0 + HALF_DMA_NUM) ) /* DMAC Group 0 irq */ ++ intc_irq = IRQ_DMAC0; ++ else if ( irq < (IRQ_DMA_0 + MAX_DMA_NUM) ) /* DMAC Group 1 irq */ ++ intc_irq = IRQ_DMAC1; ++ else { ++ printk("%s, unexpected dma irq #%d\n", __FILE__, irq); ++ return; ++ } ++ __intc_unmask_irq(intc_irq); ++ __dmac_channel_enable_irq(irq - IRQ_DMA_0); ++} ++ ++static void disable_dma_irq(unsigned int irq) ++{ ++ __dmac_channel_disable_irq(irq - IRQ_DMA_0); ++} ++ ++static void mask_and_ack_dma_irq(unsigned int irq) ++{ ++ unsigned int intc_irq; ++ ++ if ( irq < (IRQ_DMA_0 + HALF_DMA_NUM) ) /* DMAC Group 0 irq */ ++ intc_irq = IRQ_DMAC0; ++ else if ( irq < (IRQ_DMA_0 + MAX_DMA_NUM) ) /* DMAC Group 1 irq */ ++ intc_irq = IRQ_DMAC1; ++ else { ++ printk("%s, unexpected dma irq #%d\n", __FILE__, irq); ++ return ; ++ } ++ __intc_ack_irq(intc_irq); ++ __dmac_channel_ack_irq(irq-IRQ_DMA_0); /* needed?? add 20080506, Wolfgang */ ++ __dmac_channel_disable_irq(irq - IRQ_DMA_0); ++} ++ ++static void end_dma_irq(unsigned int irq) ++{ ++ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { ++ enable_dma_irq(irq); ++ } ++} ++ ++static unsigned int startup_dma_irq(unsigned int irq) ++{ ++ enable_dma_irq(irq); ++ return 0; ++} ++ ++static void shutdown_dma_irq(unsigned int irq) ++{ ++ disable_dma_irq(irq); ++} ++ ++static struct irq_chip dma_irq_type = { ++ .typename = "DMA", ++ .startup = startup_dma_irq, ++ .shutdown = shutdown_dma_irq, ++ .enable = enable_dma_irq, ++ .disable = disable_dma_irq, ++ .ack = mask_and_ack_dma_irq, ++ .end = end_dma_irq, ++}; ++ ++//---------------------------------------------------------------------- ++ ++void __init arch_init_irq(void) ++{ ++ int i; ++ ++ clear_c0_status(0xff04); /* clear ERL */ ++ set_c0_status(0x0400); /* set IP2 */ ++ ++ /* Set up INTC irq ++ */ ++ for (i = 0; i < 32; i++) { ++ disable_intc_irq(i); ++ irq_desc[i].chip = &intc_irq_type; ++ } ++ ++ /* Set up DMAC irq ++ */ ++ for (i = 0; i < NUM_DMA; i++) { ++ disable_dma_irq(IRQ_DMA_0 + i); ++ irq_desc[IRQ_DMA_0 + i].chip = &dma_irq_type; ++ } ++ ++ /* Set up GPIO irq ++ */ ++ for (i = 0; i < NUM_GPIO; i++) { ++ disable_gpio_irq(IRQ_GPIO_0 + i); ++ irq_desc[IRQ_GPIO_0 + i].chip = &gpio_irq_type; ++ } ++} ++ ++static int plat_real_irq(int irq) ++{ ++ switch (irq) { ++ case IRQ_GPIO0: ++ irq = __gpio_group_irq(0) + IRQ_GPIO_0; ++ break; ++ case IRQ_GPIO1: ++ irq = __gpio_group_irq(1) + IRQ_GPIO_0 + 32; ++ break; ++ case IRQ_GPIO2: ++ irq = __gpio_group_irq(2) + IRQ_GPIO_0 + 64; ++ break; ++ case IRQ_GPIO3: ++ irq = __gpio_group_irq(3) + IRQ_GPIO_0 + 96; ++ break; ++ case IRQ_GPIO4: ++ irq = __gpio_group_irq(4) + IRQ_GPIO_0 + 128; ++ break; ++ case IRQ_GPIO5: ++ irq = __gpio_group_irq(5) + IRQ_GPIO_0 + 160; ++ break; ++ case IRQ_DMAC0: ++ case IRQ_DMAC1: ++ irq = __dmac_get_irq() + IRQ_DMA_0; ++ break; ++ } ++ ++ return irq; ++} ++ ++asmlinkage void plat_irq_dispatch(void) ++{ ++ int irq = 0; ++ static unsigned long intc_ipr = 0; ++ ++ intc_ipr |= REG_INTC_IPR; ++ ++ if (!intc_ipr) return; ++ ++ irq = ffs(intc_ipr) - 1; ++ intc_ipr &= ~(1< ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#include ++#if 0 ++/* OHCI (USB full speed host controller) */ ++static struct resource jz_usb_ohci_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(UHC_BASE), // phys addr for ioremap ++ .end = CPHYSADDR(UHC_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_UHC, ++ .end = IRQ_UHC, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++/* The dmamask must be set for OHCI to work */ ++static u64 ohci_dmamask = ~(u32)0; ++ ++static struct platform_device jz_usb_ohci_device = { ++ .name = "jz-ohci", ++ .id = 0, ++ .dev = { ++ .dma_mask = &ohci_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_usb_ohci_resources), ++ .resource = jz_usb_ohci_resources, ++}; ++#endif ++/*** LCD controller ***/ ++static struct resource jz_lcd_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(LCD_BASE), ++ .end = CPHYSADDR(LCD_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_LCD, ++ .end = IRQ_LCD, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static u64 jz_lcd_dmamask = ~(u32)0; ++ ++static struct platform_device jz_lcd_device = { ++ .name = "jz-lcd", ++ .id = 0, ++ .dev = { ++ .dma_mask = &jz_lcd_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_lcd_resources), ++ .resource = jz_lcd_resources, ++}; ++ ++/* UDC (USB gadget controller) */ ++static struct resource jz_usb_gdt_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(UDC_BASE), ++ .end = CPHYSADDR(UDC_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_UDC, ++ .end = IRQ_UDC, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static u64 udc_dmamask = ~(u32)0; ++ ++static struct platform_device jz_usb_gdt_device = { ++ .name = "jz-udc", ++ .id = 0, ++ .dev = { ++ .dma_mask = &udc_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_usb_gdt_resources), ++ .resource = jz_usb_gdt_resources, ++}; ++ ++/** MMC/SD controller **/ ++static struct resource jz_mmc_resources[] = { ++ [0] = { ++ .start = CPHYSADDR(MSC_BASE), ++ .end = CPHYSADDR(MSC_BASE) + 0x10000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_MSC0, ++ .end = IRQ_MSC0, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static u64 jz_mmc_dmamask = ~(u32)0; ++ ++static struct platform_device jz_mmc_device = { ++ .name = "jz-mmc", ++ .id = 0, ++ .dev = { ++ .dma_mask = &jz_mmc_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(jz_mmc_resources), ++ .resource = jz_mmc_resources, ++}; ++ ++/* All */ ++static struct platform_device *jz_platform_devices[] __initdata = { ++// &jz_usb_ohci_device, ++ &jz_lcd_device, ++ &jz_usb_gdt_device, ++ &jz_mmc_device, ++}; ++ ++static int __init jz_platform_init(void) ++{ ++ return platform_add_devices(jz_platform_devices, ARRAY_SIZE(jz_platform_devices)); ++} ++ ++arch_initcall(jz_platform_init); +diff -urN linux-2.6.24.7.old/arch/mips/jz4750d/pm.c linux-2.6.24.7/arch/mips/jz4750d/pm.c +--- linux-2.6.24.7.old/arch/mips/jz4750d/pm.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750d/pm.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,461 @@ ++/* ++ * linux/arch/mips/jz4750d/common/pm.c ++ * ++ * JZ4750D Power Management Routines ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#undef DEBUG ++//#define DEBUG ++#ifdef DEBUG ++#define dprintk(x...) printk(x) ++#else ++#define dprintk(x...) ++#endif ++ ++#define GPIO_PORT_NUM 6 ++ ++/* ++ * __gpio_as_sleep set all pins to pull-disable, and set all pins as input ++ * except sdram and the pins which can be used as CS1_N to CS4_N for chip select. ++ */ ++#define __gpio_as_sleep() \ ++do { \ ++ REG_GPIO_PXFUNC(1) = ~0x03ff7fff; \ ++ REG_GPIO_PXSELC(1) = ~0x03ff7fff; \ ++ REG_GPIO_PXDIRC(1) = ~0x03ff7fff; \ ++ REG_GPIO_PXPES(1) = 0xffffffff; \ ++ REG_GPIO_PXFUNC(2) = ~0x01e00000; \ ++ REG_GPIO_PXSELC(2) = ~0x01e00000; \ ++ REG_GPIO_PXDIRC(2) = ~0x01e00000; \ ++ REG_GPIO_PXPES(2) = 0xffffffff; \ ++ REG_GPIO_PXFUNC(3) = 0xffffffff; \ ++ REG_GPIO_PXSELC(3) = 0xffffffff; \ ++ REG_GPIO_PXDIRC(3) = 0xffffffff; \ ++ REG_GPIO_PXPES(3) = 0xffffffff; \ ++ REG_GPIO_PXFUNC(4) = 0xffffffff; \ ++ REG_GPIO_PXSELC(4) = 0xffffffff; \ ++ REG_GPIO_PXDIRC(4) = 0xffffffff; \ ++ REG_GPIO_PXPES(4) = 0xffffffff; \ ++ REG_GPIO_PXFUNC(5) = 0xffffffff; \ ++ REG_GPIO_PXSELC(5) = 0xffffffff; \ ++ REG_GPIO_PXDIRC(5) = 0xffffffff; \ ++ REG_GPIO_PXPES(5) = 0xffffffff; \ ++} while (0) ++ ++static int jz_pm_do_hibernate(void) ++{ ++ printk("Put CPU into hibernate mode.\n"); ++ ++ /* Mask all interrupts */ ++ REG_INTC_IMSR = 0xffffffff; ++ ++ /* ++ * RTC Wakeup or 1Hz interrupt can be enabled or disabled ++ * through RTC driver's ioctl (linux/driver/char/rtc_jz.c). ++ */ ++ ++ /* Set minimum wakeup_n pin low-level assertion time for wakeup: 100ms */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HWFCR = (100 << RTC_HWFCR_BIT); ++ ++ /* Set reset pin low-level assertion time after wakeup: must > 60ms */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HRCR = (60 << RTC_HRCR_BIT); /* 60 ms */ ++ ++ /* Scratch pad register to be reserved */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HSPR = 0x12345678; ++ ++ /* clear wakeup status register */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HWRSR = 0x0; ++ ++ /* Put CPU to power down mode */ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_HCR = RTC_HCR_PD; ++ ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ while(1); ++ ++ /* We can't get here */ ++ return 0; ++} ++ ++/* NOTES: ++ * 1: Pins that are floated (NC) should be set as input and pull-enable. ++ * 2: Pins that are pull-up or pull-down by outside should be set as input ++ * and pull-disable. ++ * 3: Pins that are connected to a chip except sdram and nand flash ++ * should be set as input and pull-disable, too. ++ */ ++static void jz_board_do_sleep(unsigned long *ptr) ++{ ++ unsigned char i; ++ ++ /* Print messages of GPIO registers for debug */ ++ for(i=0;i ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++//#define DEBUG 1 ++#undef DEBUG ++ ++ ++struct proc_dir_entry *proc_jz_root; ++ ++ ++/* ++ * EMC Modules ++ */ ++static int emc_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ ++ len += sprintf (page+len, "SMCR(0-5): 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", REG_EMC_SMCR0, REG_EMC_SMCR1, REG_EMC_SMCR2, REG_EMC_SMCR3, REG_EMC_SMCR4); ++ len += sprintf (page+len, "SACR(0-5): 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", REG_EMC_SACR0, REG_EMC_SACR1, REG_EMC_SACR2, REG_EMC_SACR3, REG_EMC_SACR4); ++ len += sprintf (page+len, "DMCR: 0x%08x\n", REG_EMC_DMCR); ++ len += sprintf (page+len, "RTCSR: 0x%04x\n", REG_EMC_RTCSR); ++ len += sprintf (page+len, "RTCOR: 0x%04x\n", REG_EMC_RTCOR); ++ return len; ++} ++ ++/* ++ * Power Manager Module ++ */ ++static int pmc_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ unsigned long lcr = REG_CPM_LCR; ++ unsigned long clkgr = REG_CPM_CLKGR; ++ ++ len += sprintf (page+len, "Low Power Mode : %s\n", ++ ((lcr & CPM_LCR_LPM_MASK) == (CPM_LCR_LPM_IDLE)) ? ++ "IDLE" : (((lcr & CPM_LCR_LPM_MASK) == (CPM_LCR_LPM_SLEEP)) ? ++ "SLEEP" : "HIBERNATE")); ++ len += sprintf (page+len, "Doze Mode : %s\n", ++ (lcr & CPM_LCR_DOZE_ON) ? "on" : "off"); ++ if (lcr & CPM_LCR_DOZE_ON) ++ len += sprintf (page+len, " duty : %d\n", (int)((lcr & CPM_LCR_DOZE_DUTY_MASK) >> CPM_LCR_DOZE_DUTY_BIT)); ++ len += sprintf (page+len, "IPU : %s\n", ++ (clkgr & CPM_CLKGR_IPU) ? "stopped" : "running"); ++ len += sprintf (page+len, "DMAC : %s\n", ++ (clkgr & CPM_CLKGR_DMAC) ? "stopped" : "running"); ++ len += sprintf (page+len, "UHC : %s\n", ++ (clkgr & CPM_CLKGR_UHC) ? "stopped" : "running"); ++ len += sprintf (page+len, "UDC : %s\n", ++ (clkgr & CPM_CLKGR_UDC) ? "stopped" : "running"); ++ len += sprintf (page+len, "LCD : %s\n", ++ (clkgr & CPM_CLKGR_LCD) ? "stopped" : "running"); ++ len += sprintf (page+len, "CIM : %s\n", ++ (clkgr & CPM_CLKGR_CIM) ? "stopped" : "running"); ++ len += sprintf (page+len, "SADC : %s\n", ++ (clkgr & CPM_CLKGR_SADC) ? "stopped" : "running"); ++ len += sprintf (page+len, "MSC0 : %s\n", ++ (clkgr & CPM_CLKGR_MSC0) ? "stopped" : "running"); ++ len += sprintf (page+len, "MSC1 : %s\n", ++ (clkgr & CPM_CLKGR_MSC1) ? "stopped" : "running"); ++ len += sprintf (page+len, "AIC1 : %s\n", ++ (clkgr & CPM_CLKGR_AIC1) ? "stopped" : "running"); ++ len += sprintf (page+len, "AIC2 : %s\n", ++ (clkgr & CPM_CLKGR_AIC2) ? "stopped" : "running"); ++ len += sprintf (page+len, "SSI0 : %s\n", ++ (clkgr & CPM_CLKGR_SSI0) ? "stopped" : "running"); ++ len += sprintf (page+len, "SSI1 : %s\n", ++ (clkgr & CPM_CLKGR_SSI1) ? "stopped" : "running"); ++ len += sprintf (page+len, "I2C : %s\n", ++ (clkgr & CPM_CLKGR_I2C) ? "stopped" : "running"); ++ len += sprintf (page+len, "RTC : %s\n", ++ (clkgr & CPM_CLKGR_RTC) ? "stopped" : "running"); ++ len += sprintf (page+len, "TCU : %s\n", ++ (clkgr & CPM_CLKGR_TCU) ? "stopped" : "running"); ++ len += sprintf (page+len, "UART1 : %s\n", ++ (clkgr & CPM_CLKGR_UART1) ? "stopped" : "running"); ++ len += sprintf (page+len, "UART0 : %s\n", ++ (clkgr & CPM_CLKGR_UART0) ? "stopped" : "running"); ++ return len; ++} ++ ++static int pmc_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ REG_CPM_CLKGR = simple_strtoul(buffer, 0, 16); ++ return count; ++} ++ ++/* ++ * Clock Generation Module ++ */ ++#define TO_MHZ(x) (x/1000000),(x%1000000)/10000 ++#define TO_KHZ(x) (x/1000),(x%1000)/10 ++ ++static int cgm_read_proc (char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ unsigned int cppcr = REG_CPM_CPPCR; /* PLL Control Register */ ++ unsigned int cpccr = REG_CPM_CPCCR; /* Clock Control Register */ ++ unsigned int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ unsigned int od[4] = {1, 2, 2, 4}; ++ ++ len += sprintf (page+len, "CPPCR : 0x%08x\n", cppcr); ++ len += sprintf (page+len, "CPCCR : 0x%08x\n", cpccr); ++ len += sprintf (page+len, "PLL : %s\n", ++ (cppcr & CPM_CPPCR_PLLEN) ? "ON" : "OFF"); ++ len += sprintf (page+len, "m:n:o : %d:%d:%d\n", ++ __cpm_get_pllm() + 2, ++ __cpm_get_plln() + 2, ++ od[__cpm_get_pllod()] ++ ); ++ len += sprintf (page+len, "C:H:M:P : %d:%d:%d:%d\n", ++ div[__cpm_get_cdiv()], ++ div[__cpm_get_hdiv()], ++ div[__cpm_get_mdiv()], ++ div[__cpm_get_pdiv()] ++ ); ++ len += sprintf (page+len, "PLL Freq : %3d.%02d MHz\n", TO_MHZ(__cpm_get_pllout())); ++ len += sprintf (page+len, "CCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_cclk())); ++ len += sprintf (page+len, "HCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_hclk())); ++ len += sprintf (page+len, "MCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_mclk())); ++ len += sprintf (page+len, "PCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_pclk())); ++ len += sprintf (page+len, "LCDCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_lcdclk())); ++ len += sprintf (page+len, "PIXCLK : %3d.%02d KHz\n", TO_KHZ(__cpm_get_pixclk())); ++ len += sprintf (page+len, "I2SCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_i2sclk())); ++ len += sprintf (page+len, "USBCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_usbclk())); ++ len += sprintf (page+len, "MSC0CLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_mscclk(0))); ++ len += sprintf (page+len, "MSC1CLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_mscclk(1))); ++ len += sprintf (page+len, "EXTALCLK0 : %3d.%02d MHz\n", TO_MHZ(__cpm_get_extalclk0())); ++ len += sprintf (page+len, "EXTALCLK(by CPM): %3d.%02d MHz\n", TO_MHZ(__cpm_get_extalclk())); ++ len += sprintf (page+len, "RTCCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_rtcclk())); ++ ++ return len; ++} ++ ++static int cgm_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ REG_CPM_CPCCR = simple_strtoul(buffer, 0, 16); ++ return count; ++} ++ ++ ++/* USAGE: ++ * echo n > /proc/jz/ipu // n = [1,...,9], alloc mem, 2^n pages. ++ * echo FF > /proc/jz/ipu // 255, free all buffer ++ * echo xxxx > /proc/jz/ipu // free buffer which addr is xxxx ++ * echo llll > /proc/jz/ipu // add_wired_entry(l,l,l,l) ++ * echo 0 > /proc/jz/ipu // debug, print ipu_buf ++ * od -X /proc/jz/ipu // read mem addr ++ */ ++ ++typedef struct _ipu_buf { ++ unsigned int addr; /* phys addr */ ++ unsigned int page_shift; ++} ipu_buf_t; ++ ++#define IPU_BUF_MAX 4 /* 4 buffers */ ++ ++static struct _ipu_buf ipu_buf[IPU_BUF_MAX]; ++static int ipu_buf_cnt = 0; ++static unsigned char g_asid=0; ++ ++extern void local_flush_tlb_all(void); ++ ++/* CP0 hazard avoidance. */ ++#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ ++ "nop; nop; nop; nop; nop; nop;\n\t" \ ++ ".set reorder\n\t") ++void show_tlb(void) ++{ ++#define ASID_MASK 0xFF ++ ++ unsigned long flags; ++ unsigned int old_ctx; ++ unsigned int entry; ++ unsigned int entrylo0, entrylo1, entryhi; ++ unsigned int pagemask; ++ ++ local_irq_save(flags); ++ ++ /* Save old context */ ++ old_ctx = (read_c0_entryhi() & 0xff); ++ ++ printk("TLB content:\n"); ++ entry = 0; ++ while(entry < 32) { ++ write_c0_index(entry); ++ BARRIER; ++ tlb_read(); ++ BARRIER; ++ entryhi = read_c0_entryhi(); ++ entrylo0 = read_c0_entrylo0(); ++ entrylo1 = read_c0_entrylo1(); ++ pagemask = read_c0_pagemask(); ++ printk("%02d: ASID=%02d%s VA=0x%08x ", entry, entryhi & ASID_MASK, (entrylo0 & entrylo1 & 1) ? "(G)" : " ", entryhi & ~ASID_MASK); ++ printk("PA0=0x%08x C0=%x %s%s%s\n", (entrylo0>>6)<<12, (entrylo0>>3) & 7, (entrylo0 & 4) ? "Dirty " : "", (entrylo0 & 2) ? "Valid " : "Invalid ", (entrylo0 & 1) ? "Global" : ""); ++ printk("\t\t\t PA1=0x%08x C1=%x %s%s%s\n", (entrylo1>>6)<<12, (entrylo1>>3) & 7, (entrylo1 & 4) ? "Dirty " : "", (entrylo1 & 2) ? "Valid " : "Invalid ", (entrylo1 & 1) ? "Global" : ""); ++ ++ printk("\t\tpagemask=0x%08x", pagemask); ++ printk("\tentryhi=0x%08x\n", entryhi); ++ printk("\t\tentrylo0=0x%08x", entrylo0); ++ printk("\tentrylo1=0x%08x\n", entrylo1); ++ ++ entry++; ++ } ++ BARRIER; ++ write_c0_entryhi(old_ctx); ++ ++ local_irq_restore(flags); ++} ++ ++static void ipu_add_wired_entry(unsigned long pid, ++ unsigned long entrylo0, unsigned long entrylo1, ++ unsigned long entryhi, unsigned long pagemask) ++{ ++ unsigned long flags; ++ unsigned long wired; ++ unsigned long old_pagemask; ++ unsigned long old_ctx; ++ struct task_struct *g, *p; ++ ++ /* We will lock an 4MB page size entry to map the 4MB reserved IPU memory */ ++ wired = read_c0_wired(); ++ if (wired) return; ++ ++ do_each_thread(g, p) { ++ if (p->pid == pid ) ++ g_asid = p->mm->context[0]; ++ } while_each_thread(g, p); ++ ++ ++ local_irq_save(flags); ++ ++ entrylo0 = entrylo0 >> 6; /* PFN */ ++ entrylo0 |= 0x6 | (0 << 3); /* Write-through cacheable, dirty, valid */ ++ ++ /* Save old context and create impossible VPN2 value */ ++ old_ctx = read_c0_entryhi() & 0xff; ++ old_pagemask = read_c0_pagemask(); ++ wired = read_c0_wired(); ++ write_c0_wired(wired + 1); ++ write_c0_index(wired); ++ BARRIER; ++ entryhi &= ~0xff; /* new add, 20070906 */ ++ entryhi |= g_asid; /* new add, 20070906 */ ++// entryhi |= old_ctx; /* new add, 20070906 */ ++ write_c0_pagemask(pagemask); ++ write_c0_entryhi(entryhi); ++ write_c0_entrylo0(entrylo0); ++ write_c0_entrylo1(entrylo1); ++ BARRIER; ++ tlb_write_indexed(); ++ BARRIER; ++ ++ write_c0_entryhi(old_ctx); ++ BARRIER; ++ write_c0_pagemask(old_pagemask); ++ local_flush_tlb_all(); ++ local_irq_restore(flags); ++#if defined(DEBUG) ++ printk("\nold_ctx=%03d\n", old_ctx); ++ ++ show_tlb(); ++#endif ++} ++ ++static void ipu_del_wired_entry( void ) ++{ ++ unsigned long flags; ++ unsigned long wired; ++ ++ local_irq_save(flags); ++ wired = read_c0_wired(); ++ if ( wired > 0 ) { ++ write_c0_wired(wired - 1); ++ } ++ local_irq_restore(flags); ++} ++ ++static inline void ipu_buf_get( unsigned int page_shift ) ++{ ++ unsigned char * virt_addr; ++ int i; ++ for ( i=0; i< IPU_BUF_MAX; ++i ) { ++ if ( ipu_buf[i].addr == 0 ) { ++ break; ++ } ++ } ++ ++ if ( (ipu_buf_cnt = i) == IPU_BUF_MAX ) { ++ printk("Error, no free ipu buffer.\n"); ++ return ; ++ } ++ ++ virt_addr = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); ++ ++ if ( virt_addr ) { ++ ipu_buf[ipu_buf_cnt].addr = (unsigned int)virt_to_phys((void *)virt_addr); ++ ipu_buf[ipu_buf_cnt].page_shift = page_shift; ++ ++ for (i = 0; i < (1<= IPU_BUF_MAX ) { /* failed alloc mem, rturn 0 */ ++ printk("no free buffer.\n"); ++ *pint = 0; ++ } ++ else ++ *pint = (unsigned int )ipu_buf[ipu_buf_cnt].addr; /* phys addr */ ++ len += sizeof(unsigned int); ++ ++#if defined(DEBUG) ++ show_tlb(); ++#endif ++ return len; ++ ++} ++ ++static int ipu_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ unsigned int val ; ++ int cnt,i; ++ char buf[12]; ++ unsigned long pid, entrylo0, entrylo1, entryhi, pagemask; ++#if defined(DEBUG) ++ printk("ipu write count=%u\n", count); ++#endif ++ if (count == (8*5+1)) { ++ for (i=0;i<12;i++) buf[i]=0; ++ strncpy(buf, buffer+8*0, 8); ++ pid = simple_strtoul(buf, 0, 16); ++ for (i=0;i<12;i++) buf[i]=0; ++ strncpy(buf, buffer+8*1, 8); ++ entrylo0 = simple_strtoul(buf, 0, 16); ++ for (i=0;i<12;i++) buf[i]=0; ++ strncpy(buf, buffer+8*2, 8); ++ entrylo1 = simple_strtoul(buf, 0, 16); ++ for (i=0;i<12;i++) buf[i]=0; ++ strncpy(buf, buffer+8*3, 8); ++ entryhi = simple_strtoul(buf, 0, 16); ++ for (i=0;i<12;i++) buf[i]=0; ++ strncpy(buf, buffer+8*4, 8); ++ pagemask = simple_strtoul(buf, 0, 16); ++ ++#if defined(DEBUG) ++ printk("pid=0x%08x, entrylo0=0x%08x, entrylo1=0x%08x, entryhi=0x%08x, pagemask=0x%08x\n", ++ pid, entrylo0, entrylo1, entryhi, pagemask); ++#endif ++ ipu_add_wired_entry( pid, entrylo0, entrylo1, entryhi, pagemask); ++ return 41; ++ } ++ else if ( count <= 8+1 ) { ++ for (i=0;i<12;i++) buf[i]=0; ++ strncpy(buf, buffer, 8); ++ val = simple_strtoul(buf, 0, 16); ++ } else if (count == 44) { ++ for (i = 0; i < 12; i++) ++ buf[i] = 0; ++ strncpy(buf, buffer, 10); ++ pid = simple_strtoul(buf, 0, 16); ++ for (i = 0; i < 12; i++) ++ buf[i] = 0; ++ strncpy(buf, buffer + 11, 10); ++ entryhi = simple_strtoul(buf, 0, 16);//vaddr ++ for (i = 0; i < 12; i++) ++ buf[i] = 0; ++ strncpy(buf, buffer + 22, 10); ++ entrylo0 = simple_strtoul(buf, 0, 16);//paddr ++ for (i = 0; i < 12; i++) ++ buf[i] = 0; ++ strncpy(buf, buffer + 33, 10); ++ pagemask = simple_strtoul(buf, 0, 16); ++ pagemask = 0x3ff << 13; /* Fixed to 4MB page size */ ++ ipu_add_wired_entry(pid, entrylo0, 0, entryhi, pagemask); ++ return 44; ++ } else { ++ printk("ipu write count error, count=%d\n.", (unsigned int)count); ++ return -1; ++ } ++ ++ /* val: 1-9, page_shift, val>= 10: ipu_buf.addr */ ++ if ( val == 0 ) { /* debug, print ipu_buf info */ ++ for ( cnt=0; cnt /proc/jz/imem // n = [0,...,10], allocate memory, 2^n pages ++ * echo xxxxxxxx > /proc/jz/imem // free buffer which addr is xxxxxxxx ++ * echo FF > /proc/jz/ipu // FF, free all buffers ++ * od -X /proc/jz/imem // return the allocated buffer address and the max order of free buffer ++ */ ++ ++//#define DEBUG_IMEM 1 ++ ++#define IMEM_MAX_ORDER 10 /* max 2^10 * 4096 = 4MB */ ++ ++static unsigned int jz_imem_base; /* physical base address of ipu memory */ ++ ++static unsigned int allocated_phys_addr = 0; ++ ++/* ++ * Allocated buffer list ++ */ ++typedef struct imem_list { ++ unsigned int phys_start; /* physical start addr */ ++ unsigned int phys_end; /* physical end addr */ ++ struct imem_list *next; ++} imem_list_t; ++ ++static struct imem_list *imem_list_head = NULL; /* up sorted by phys_start */ ++ ++#ifdef DEBUG_IMEM ++static void dump_imem_list(void) ++{ ++ struct imem_list *imem; ++ ++ printk("*** dump_imem_list 0x%x ***\n", (u32)imem_list_head); ++ imem = imem_list_head; ++ while (imem) { ++ printk("imem=0x%x phys_start=0x%x phys_end=0x%x next=0x%x\n", (u32)imem, imem->phys_start, imem->phys_end, (u32)imem->next); ++ imem = imem->next; ++ } ++} ++#endif ++ ++/* allocate 2^order pages inside the 4MB memory */ ++static int imem_alloc(unsigned int order) ++{ ++ int alloc_ok = 0; ++ unsigned int start, end; ++ unsigned int size = (1 << order) * PAGE_SIZE; ++ struct imem_list *imem, *imemn, *imemp; ++ ++ allocated_phys_addr = 0; ++ ++ start = jz_imem_base; ++ end = start + (1 << IMEM_MAX_ORDER) * PAGE_SIZE; ++ ++ imem = imem_list_head; ++ while (imem) { ++ if ((imem->phys_start - start) >= size) { ++ /* we got a valid address range */ ++ alloc_ok = 1; ++ break; ++ } ++ ++ start = imem->phys_end + 1; ++ imem = imem->next; ++ } ++ ++ if (!alloc_ok) { ++ if ((end - start) >= size) ++ alloc_ok = 1; ++ } ++ ++ if (alloc_ok) { ++ end = start + size - 1; ++ allocated_phys_addr = start; ++ ++ /* add to imem_list, up sorted by phys_start */ ++ imemn = kmalloc(sizeof(struct imem_list), GFP_KERNEL); ++ if (!imemn) { ++ return -ENOMEM; ++ } ++ imemn->phys_start = start; ++ imemn->phys_end = end; ++ imemn->next = NULL; ++ ++ if (!imem_list_head) ++ imem_list_head = imemn; ++ else { ++ imem = imemp = imem_list_head; ++ while (imem) { ++ if (start < imem->phys_start) { ++ break; ++ } ++ ++ imemp = imem; ++ imem = imem->next; ++ } ++ ++ if (imem == imem_list_head) { ++ imem_list_head = imemn; ++ imemn->next = imem; ++ } ++ else { ++ imemn->next = imemp->next; ++ imemp->next = imemn; ++ } ++ } ++ } ++ ++#ifdef DEBUG_IMEM ++ dump_imem_list(); ++#endif ++ return 0; ++} ++ ++static void imem_free(unsigned int phys_addr) ++{ ++ struct imem_list *imem, *imemp; ++ ++ imem = imemp = imem_list_head; ++ while (imem) { ++ if (phys_addr == imem->phys_start) { ++ if (imem == imem_list_head) { ++ imem_list_head = imem->next; ++ } ++ else { ++ imemp->next = imem->next; ++ } ++ ++ kfree(imem); ++ break; ++ } ++ ++ imemp = imem; ++ imem = imem->next; ++ } ++ ++#ifdef DEBUG_IMEM ++ dump_imem_list(); ++#endif ++} ++ ++static void imem_free_all(void) ++{ ++ struct imem_list *imem; ++ ++ imem = imem_list_head; ++ while (imem) { ++ kfree(imem); ++ imem = imem->next; ++ } ++ ++ imem_list_head = NULL; ++ ++ allocated_phys_addr = 0; ++ ++#ifdef DEBUG_IMEM ++ dump_imem_list(); ++#endif ++} ++ ++/* ++ * Return the allocated buffer address and the max order of free buffer ++ */ ++static int imem_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = 0; ++ unsigned int start_addr, end_addr, max_order, max_size; ++ struct imem_list *imem; ++ ++ unsigned int *tmp = (unsigned int *)(page + len); ++ ++ start_addr = jz_imem_base; ++ end_addr = start_addr + (1 << IMEM_MAX_ORDER) * PAGE_SIZE; ++ ++ if (!imem_list_head) ++ max_size = end_addr - start_addr; ++ else { ++ max_size = 0; ++ imem = imem_list_head; ++ while (imem) { ++ if (max_size < (imem->phys_start - start_addr)) ++ max_size = imem->phys_start - start_addr; ++ ++ start_addr = imem->phys_end + 1; ++ imem = imem->next; ++ } ++ ++ if (max_size < (end_addr - start_addr)) ++ max_size = end_addr - start_addr; ++ } ++ ++ if (max_size > 0) { ++ max_order = get_order(max_size); ++ if (((1 << max_order) * PAGE_SIZE) > max_size) ++ max_order--; ++ } ++ else { ++ max_order = 0xffffffff; /* No any free buffer */ ++ } ++ ++ *tmp++ = allocated_phys_addr; /* address allocated by 'echo n > /proc/jz/imem' */ ++ *tmp = max_order; /* max order of current free buffers */ ++ ++ len += 2 * sizeof(unsigned int); ++ ++ return len; ++} ++ ++static int imem_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ unsigned int val; ++ ++ val = simple_strtoul(buffer, 0, 16); ++ ++ if (val == 0xff) { ++ /* free all memory */ ++ imem_free_all(); ++ } ++ else if ((val >= 0) && (val <= IMEM_MAX_ORDER)) { ++ /* allocate 2^val pages */ ++ imem_alloc(val); ++ } ++ else { ++ /* free buffer which phys_addr is val */ ++ imem_free(val); ++ } ++ ++ return count; ++} ++ ++/* ++ * /proc/jz/xxx entry ++ * ++ */ ++static int __init jz_proc_init(void) ++{ ++ struct proc_dir_entry *res; ++ unsigned int virt_addr, i; ++ ++ proc_jz_root = proc_mkdir("jz", 0); ++ ++ /* External Memory Controller */ ++ res = create_proc_entry("emc", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = emc_read_proc; ++ res->write_proc = NULL; ++ res->data = NULL; ++ } ++ ++ /* Power Management Controller */ ++ res = create_proc_entry("pmc", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = pmc_read_proc; ++ res->write_proc = pmc_write_proc; ++ res->data = NULL; ++ } ++ ++ /* Clock Generation Module */ ++ res = create_proc_entry("cgm", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = cgm_read_proc; ++ res->write_proc = cgm_write_proc; ++ res->data = NULL; ++ } ++ ++ /* Image process unit */ ++ res = create_proc_entry("ipu", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = ipu_read_proc; ++ res->write_proc = ipu_write_proc; ++ res->data = NULL; ++ } ++ ++ /* udc hotplug */ ++ res = create_proc_entry("udc", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = udc_read_proc; ++ res->write_proc = NULL; ++ res->data = NULL; ++ } ++ ++ /* mmc hotplug */ ++ res = create_proc_entry("mmc", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = mmc_read_proc; ++ res->write_proc = NULL; ++ res->data = NULL; ++ } ++ ++ /* ++ * Reserve a 4MB memory for IPU on JZ4750D. ++ */ ++ jz_imem_base = (unsigned int)__get_free_pages(GFP_KERNEL, IMEM_MAX_ORDER); ++ if (jz_imem_base) { ++ /* imem (IPU memory management) */ ++ res = create_proc_entry("imem", 0644, proc_jz_root); ++ if (res) { ++ res->read_proc = imem_read_proc; ++ res->write_proc = imem_write_proc; ++ res->data = NULL; ++ } ++ ++ /* Set page reserved */ ++ virt_addr = jz_imem_base; ++ for (i = 0; i < (1 << IMEM_MAX_ORDER); i++) { ++ SetPageReserved(virt_to_page((void *)virt_addr)); ++ virt_addr += PAGE_SIZE; ++ } ++ ++ /* Convert to physical address */ ++ jz_imem_base = virt_to_phys((void *)jz_imem_base); ++ ++ printk("Total %dMB memory at 0x%x was reserved for IPU\n", ++ (unsigned int)((1 << IMEM_MAX_ORDER) * PAGE_SIZE)/1000000, jz_imem_base); ++ } ++ ++ return 0; ++} ++ ++__initcall(jz_proc_init); +diff -urN linux-2.6.24.7.old/arch/mips/jz4750d/prom.c linux-2.6.24.7/arch/mips/jz4750d/prom.c +--- linux-2.6.24.7.old/arch/mips/jz4750d/prom.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750d/prom.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,198 @@ ++/* ++ * ++ * BRIEF MODULE DESCRIPTION ++ * PROM library initialisation code, supports YAMON and U-Boot. ++ * ++ * Copyright 2000, 2001, 2006 MontaVista Software Inc. ++ * Author: MontaVista Software, Inc. ++ * ppopov@mvista.com or source@mvista.com ++ * ++ * This file was derived from Carsten Langgaard's ++ * arch/mips/mips-boards/xx files. ++ * ++ * Carsten Langgaard, carstenl@mips.com ++ * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN ++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, 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 DAMAGE. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* #define DEBUG_CMDLINE */ ++ ++int prom_argc; ++char **prom_argv, **prom_envp; ++ ++char * prom_getcmdline(void) ++{ ++ return &(arcs_cmdline[0]); ++} ++ ++void prom_init_cmdline(void) ++{ ++ char *cp; ++ int actr; ++ ++ actr = 1; /* Always ignore argv[0] */ ++ ++ cp = &(arcs_cmdline[0]); ++ while(actr < prom_argc) { ++ strcpy(cp, prom_argv[actr]); ++ cp += strlen(prom_argv[actr]); ++ *cp++ = ' '; ++ actr++; ++ } ++ if (cp != &(arcs_cmdline[0])) /* get rid of trailing space */ ++ --cp; ++ if (prom_argc > 1) ++ *cp = '\0'; ++ ++} ++ ++ ++char *prom_getenv(char *envname) ++{ ++#if 0 ++ /* ++ * Return a pointer to the given environment variable. ++ * YAMON uses "name", "value" pairs, while U-Boot uses "name=value". ++ */ ++ ++ char **env = prom_envp; ++ int i = strlen(envname); ++ int yamon = (*env && strchr(*env, '=') == NULL); ++ ++ while (*env) { ++ if (yamon) { ++ if (strcmp(envname, *env++) == 0) ++ return *env; ++ } else { ++ if (strncmp(envname, *env, i) == 0 && (*env)[i] == '=') ++ return *env + i + 1; ++ } ++ env++; ++ } ++#endif ++ return NULL; ++} ++ ++inline unsigned char str2hexnum(unsigned char c) ++{ ++ if(c >= '0' && c <= '9') ++ return c - '0'; ++ if(c >= 'a' && c <= 'f') ++ return c - 'a' + 10; ++ if(c >= 'A' && c <= 'F') ++ return c - 'A' + 10; ++ return 0; /* foo */ ++} ++ ++inline void str2eaddr(unsigned char *ea, unsigned char *str) ++{ ++ int i; ++ ++ for(i = 0; i < 6; i++) { ++ unsigned char num; ++ ++ if((*str == '.') || (*str == ':')) ++ str++; ++ num = str2hexnum(*str++) << 4; ++ num |= (str2hexnum(*str++)); ++ ea[i] = num; ++ } ++} ++ ++int get_ethernet_addr(char *ethernet_addr) ++{ ++ char *ethaddr_str; ++ ++ ethaddr_str = prom_getenv("ethaddr"); ++ if (!ethaddr_str) { ++ printk("ethaddr not set in boot prom\n"); ++ return -1; ++ } ++ str2eaddr(ethernet_addr, ethaddr_str); ++ ++#if 0 ++ { ++ int i; ++ ++ printk("get_ethernet_addr: "); ++ for (i=0; i<5; i++) ++ printk("%02x:", (unsigned char)*(ethernet_addr+i)); ++ printk("%02x\n", *(ethernet_addr+i)); ++ } ++#endif ++ ++ return 0; ++} ++ ++void __init prom_free_prom_memory(void) ++{ ++} ++ ++void __init prom_init(void) ++{ ++ unsigned char *memsize_str; ++ unsigned long memsize; ++ ++ prom_argc = (int) fw_arg0; ++ prom_argv = (char **) fw_arg1; ++ prom_envp = (char **) fw_arg2; ++ ++ mips_machtype = MACH_INGENIC_JZ4750D; ++ ++ prom_init_cmdline(); ++ memsize_str = prom_getenv("memsize"); ++ if (!memsize_str) { ++ memsize = 0x04000000; ++ } else { ++ memsize = simple_strtol(memsize_str, NULL, 0); ++ } ++ add_memory_region(0, memsize, BOOT_MEM_RAM); ++} ++ ++/* used by early printk */ ++void prom_putchar(char c) ++{ ++ volatile u8 *uart_lsr = (volatile u8 *)(UART0_BASE + OFF_LSR); ++ volatile u8 *uart_tdr = (volatile u8 *)(UART0_BASE + OFF_TDR); ++ ++ /* Wait for fifo to shift out some bytes */ ++ while ( !((*uart_lsr & (UARTLSR_TDRQ | UARTLSR_TEMT)) == 0x60) ); ++ ++ *uart_tdr = (u8)c; ++} ++ ++const char *get_system_type(void) ++{ ++ return "JZ4750D"; ++} ++ ++EXPORT_SYMBOL(prom_getcmdline); ++EXPORT_SYMBOL(get_ethernet_addr); ++EXPORT_SYMBOL(str2eaddr); +diff -urN linux-2.6.24.7.old/arch/mips/jz4750d/reset.c linux-2.6.24.7/arch/mips/jz4750d/reset.c +--- linux-2.6.24.7.old/arch/mips/jz4750d/reset.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750d/reset.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,46 @@ ++/* ++ * linux/arch/mips/jz4750/reset.c ++ * ++ * JZ4750 reset routines. ++ * ++ * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++void jz_restart(char *command) ++{ ++ printk("Restarting after 4 ms\n"); ++ REG_WDT_TCSR = WDT_TCSR_PRESCALE4 | WDT_TCSR_EXT_EN; ++ REG_WDT_TCNT = 0; ++ REG_WDT_TDR = JZ_EXTAL/1000; /* reset after 4ms */ ++ REG_TCU_TSCR = TCU_TSCR_WDTSC; /* enable wdt clock */ ++ REG_WDT_TCER = WDT_TCER_TCEN; /* wdt start */ ++ while (1); ++} ++ ++void jz_halt(void) ++{ ++ printk(KERN_NOTICE "\n** You can safely turn off the power\n"); ++ ++ while (1) ++ __asm__(".set\tmips3\n\t" ++ "wait\n\t" ++ ".set\tmips0"); ++} ++ ++void jz_power_off(void) ++{ ++ jz_halt(); ++} +diff -urN linux-2.6.24.7.old/arch/mips/jz4750d/setup.c linux-2.6.24.7/arch/mips/jz4750d/setup.c +--- linux-2.6.24.7.old/arch/mips/jz4750d/setup.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750d/setup.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,199 @@ ++/* ++ * linux/arch/mips/jz4750d/common/setup.c ++ * ++ * JZ4750D common setup routines. ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_PC_KEYB ++#include ++#endif ++ ++jz_clocks_t jz_clocks; ++ ++extern char * __init prom_getcmdline(void); ++extern void __init jz_board_setup(void); ++extern void jz_restart(char *); ++extern void jz_halt(void); ++extern void jz_power_off(void); ++extern void jz_time_init(void); ++ ++static void __init sysclocks_setup(void) ++{ ++#ifndef CONFIG_MIPS_JZ_EMURUS /* FPGA */ ++ jz_clocks.cclk = __cpm_get_cclk(); ++ jz_clocks.hclk = __cpm_get_hclk(); ++ jz_clocks.pclk = __cpm_get_pclk(); ++ jz_clocks.mclk = __cpm_get_mclk(); ++ jz_clocks.lcdclk = __cpm_get_lcdclk(); ++ jz_clocks.pixclk = __cpm_get_pixclk(); ++ jz_clocks.i2sclk = __cpm_get_i2sclk(); ++ jz_clocks.usbclk = __cpm_get_usbclk(); ++ jz_clocks.mscclk = __cpm_get_mscclk(0); ++ jz_clocks.extalclk = __cpm_get_extalclk(); ++ jz_clocks.rtcclk = __cpm_get_rtcclk(); ++#else ++ ++#define FPGACLK 8000000 ++ ++ jz_clocks.cclk = FPGACLK; ++ jz_clocks.hclk = FPGACLK; ++ jz_clocks.pclk = FPGACLK; ++ jz_clocks.mclk = FPGACLK; ++ jz_clocks.lcdclk = FPGACLK; ++ jz_clocks.pixclk = FPGACLK; ++ jz_clocks.i2sclk = FPGACLK; ++ jz_clocks.usbclk = FPGACLK; ++ jz_clocks.mscclk = FPGACLK; ++ jz_clocks.extalclk = FPGACLK; ++ jz_clocks.rtcclk = FPGACLK; ++#endif ++ ++ printk("CPU clock: %dMHz, System clock: %dMHz, Peripheral clock: %dMHz, Memory clock: %dMHz\n", ++ (jz_clocks.cclk + 500000) / 1000000, ++ (jz_clocks.hclk + 500000) / 1000000, ++ (jz_clocks.pclk + 500000) / 1000000, ++ (jz_clocks.mclk + 500000) / 1000000); ++} ++ ++static void __init soc_cpm_setup(void) ++{ ++ /* Start all module clocks ++ */ ++ __cpm_start_all(); ++ ++ /* Enable CKO to external memory */ ++ __cpm_enable_cko(); ++ ++ /* CPU enters IDLE mode when executing 'wait' instruction */ ++ __cpm_idle_mode(); ++ ++ /* Setup system clocks */ ++ sysclocks_setup(); ++} ++ ++static void __init soc_harb_setup(void) ++{ ++// __harb_set_priority(0x00); /* CIM>LCD>DMA>ETH>PCI>USB>CBB */ ++// __harb_set_priority(0x03); /* LCD>CIM>DMA>ETH>PCI>USB>CBB */ ++// __harb_set_priority(0x0a); /* ETH>LCD>CIM>DMA>PCI>USB>CBB */ ++} ++ ++static void __init soc_emc_setup(void) ++{ ++} ++ ++static void __init soc_dmac_setup(void) ++{ ++ __dmac_enable_module(0); ++ __dmac_enable_module(1); ++} ++ ++static void __init jz_soc_setup(void) ++{ ++ soc_cpm_setup(); ++ soc_harb_setup(); ++ soc_emc_setup(); ++ soc_dmac_setup(); ++} ++ ++static void __init jz_serial_setup(void) ++{ ++#ifdef CONFIG_SERIAL_8250 ++ struct uart_port s; ++ REG8(UART0_FCR) |= UARTFCR_UUE; /* enable UART module */ ++ memset(&s, 0, sizeof(s)); ++ s.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST; ++ s.iotype = SERIAL_IO_MEM; ++ s.regshift = 2; ++ s.uartclk = jz_clocks.extalclk ; ++ ++ s.line = 0; ++ s.membase = (u8 *)UART0_BASE; ++ s.irq = IRQ_UART0; ++ if (early_serial_setup(&s) != 0) { ++ printk(KERN_ERR "Serial ttyS0 setup failed!\n"); ++ } ++ ++ s.line = 1; ++ s.membase = (u8 *)UART1_BASE; ++ s.irq = IRQ_UART1; ++ if (early_serial_setup(&s) != 0) { ++ printk(KERN_ERR "Serial ttyS1 setup failed!\n"); ++ } ++ ++ s.line = 2; ++ s.membase = (u8 *)UART2_BASE; ++ s.irq = IRQ_UART2; ++ ++ if (early_serial_setup(&s) != 0) { ++ printk(KERN_ERR "Serial ttyS2 setup failed!\n"); ++ } ++/* ++ s.line = 3; ++ s.membase = (u8 *)UART3_BASE; ++ s.irq = IRQ_UART3; ++ if (early_serial_setup(&s) != 0) { ++ printk(KERN_ERR "Serial ttyS3 setup failed!\n"); ++ } ++*/ ++#endif ++} ++ ++void __init plat_mem_setup(void) ++{ ++ char *argptr; ++ ++ argptr = prom_getcmdline(); ++ ++ /* IO/MEM resources. Which will be the addtion value in `inX' and ++ * `outX' macros defined in asm/io.h */ ++ set_io_port_base(0); ++ ioport_resource.start = 0x00000000; ++ ioport_resource.end = 0xffffffff; ++ iomem_resource.start = 0x00000000; ++ iomem_resource.end = 0xffffffff; ++ ++ _machine_restart = jz_restart; ++ _machine_halt = jz_halt; ++ pm_power_off = jz_power_off; ++ ++ jz_soc_setup(); ++ jz_serial_setup(); ++ jz_board_setup(); ++} ++ +diff -urN linux-2.6.24.7.old/arch/mips/jz4750d/time.c linux-2.6.24.7/arch/mips/jz4750d/time.c +--- linux-2.6.24.7.old/arch/mips/jz4750d/time.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/arch/mips/jz4750d/time.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,156 @@ ++/* ++ * linux/arch/mips/jz4750d/time.c ++ * ++ * Setting up the clock on the JZ4750D boards. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ */ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* This is for machines which generate the exact clock. */ ++ ++#define JZ_TIMER_IRQ IRQ_TCU0 ++ ++#define JZ_TIMER_CLOCK (JZ_EXTAL>>4) /* Jz timer clock frequency */ ++ ++static struct clocksource clocksource_jz; /* Jz clock source */ ++static struct clock_event_device jz_clockevent_device; /* Jz clock event */ ++ ++void (*jz_timer_callback)(void); ++ ++static irqreturn_t jz_timer_interrupt(int irq, void *dev_id) ++{ ++ struct clock_event_device *cd = dev_id; ++ ++ REG_TCU_TFCR = TCU_TFCR_OSTFCL; /* ACK timer */ ++ ++ if (jz_timer_callback) ++ jz_timer_callback(); ++ ++ cd->event_handler(cd); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct irqaction jz_irqaction = { ++ .handler = jz_timer_interrupt, ++ .flags = IRQF_DISABLED | IRQF_PERCPU | IRQF_TIMER, ++ .name = "jz-timerirq", ++}; ++ ++ ++cycle_t jz_get_cycles(void) ++{ ++ /* convert jiffes to jz timer cycles */ ++ return (cycle_t)( jiffies*((JZ_TIMER_CLOCK)/HZ) + REG_TCU_OSTCNT); ++} ++ ++static struct clocksource clocksource_jz = { ++ .name = "jz_clocksource", ++ .rating = 300, ++ .read = jz_get_cycles, ++ .mask = 0xFFFFFFFF, ++ .shift = 10, ++ .flags = CLOCK_SOURCE_WATCHDOG, ++}; ++ ++static int __init jz_clocksource_init(void) ++{ ++ clocksource_jz.mult = clocksource_hz2mult(JZ_TIMER_CLOCK, clocksource_jz.shift); ++ clocksource_register(&clocksource_jz); ++ return 0; ++} ++ ++static int jz_set_next_event(unsigned long evt, ++ struct clock_event_device *unused) ++{ ++ return 0; ++} ++ ++static void jz_set_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt) ++{ ++ switch (mode) { ++ case CLOCK_EVT_MODE_PERIODIC: ++ break; ++ case CLOCK_EVT_MODE_ONESHOT: ++ case CLOCK_EVT_MODE_UNUSED: ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ break; ++ case CLOCK_EVT_MODE_RESUME: ++ break; ++ } ++} ++ ++static struct clock_event_device jz_clockevent_device = { ++ .name = "jz-clockenvent", ++ .features = CLOCK_EVT_FEAT_PERIODIC, ++// .features = CLOCK_EVT_FEAT_ONESHOT, /* Jz4740 not support dynamic clock now */ ++ ++ /* .mult, .shift, .max_delta_ns and .min_delta_ns left uninitialized */ ++ .rating = 300, ++ .irq = JZ_TIMER_IRQ, ++ .set_mode = jz_set_mode, ++ .set_next_event = jz_set_next_event, ++}; ++ ++static void __init jz_clockevent_init(void) ++{ ++ struct clock_event_device *cd = &jz_clockevent_device; ++ unsigned int cpu = smp_processor_id(); ++ ++ cd->cpumask = cpumask_of_cpu(cpu); ++ clockevents_register_device(cd); ++} ++ ++static void __init jz_timer_setup(void) ++{ ++ jz_clocksource_init(); /* init jz clock source */ ++ jz_clockevent_init(); /* init jz clock event */ ++ ++ /* ++ * Make irqs happen for the system timer ++ */ ++ jz_irqaction.dev_id = &jz_clockevent_device; ++ setup_irq(JZ_TIMER_IRQ, &jz_irqaction); ++} ++ ++ ++void __init plat_time_init(void) ++{ ++ unsigned int latch; ++ ++ /* Init timer */ ++ latch = (JZ_TIMER_CLOCK + (HZ>>1)) / HZ; ++ ++ REG_TCU_OSTCSR = TCU_OSTCSR_PRESCALE16 | TCU_OSTCSR_EXT_EN; ++ REG_TCU_OSTCNT = 0; ++ REG_TCU_OSTDR = latch; ++ ++ REG_TCU_TMCR = TCU_TMCR_OSTMCL; /* unmask match irq */ ++ REG_TCU_TSCR = TCU_TSCR_OSTSC; /* enable timer clock */ ++ REG_TCU_TESR = TCU_TESR_OSTST; /* start counting up */ ++ ++ jz_timer_setup(); ++} +diff -urN linux-2.6.24.7.old/arch/mips/kernel/cpu-probe.c linux-2.6.24.7/arch/mips/kernel/cpu-probe.c +--- linux-2.6.24.7.old/arch/mips/kernel/cpu-probe.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/arch/mips/kernel/cpu-probe.c 2009-04-12 18:13:57.000000000 +0200 +@@ -160,6 +160,7 @@ + case CPU_25KF: + case CPU_PR4450: + case CPU_BCM3302: ++ case CPU_JZRISC: + cpu_wait = r4k_wait; + break; + +@@ -802,6 +803,22 @@ + } + } + ++static inline void cpu_probe_ingenic(struct cpuinfo_mips *c) ++{ ++ decode_configs(c); ++ c->options &= ~MIPS_CPU_COUNTER; /* JZRISC does not implement the CP0 counter. */ ++ switch (c->processor_id & 0xff00) { ++ case PRID_IMP_JZRISC: ++ c->cputype = CPU_JZRISC; ++ c->isa_level = MIPS_CPU_ISA_M32R1; ++ c->tlbsize = 32; ++ break; ++ default: ++ panic("Unknown Ingenic Processor ID!"); ++ break; ++ } ++} ++ + const char *__cpu_name[NR_CPUS]; + + /* +@@ -880,6 +897,7 @@ + case CPU_BCM4710: name = "Broadcom BCM4710"; break; + case CPU_PR4450: name = "Philips PR4450"; break; + case CPU_LOONGSON2: name = "ICT Loongson-2"; break; ++ case CPU_JZRISC: name = "Ingenic JZRISC"; break; + default: + BUG(); + } +@@ -919,6 +937,9 @@ + case PRID_COMP_PHILIPS: + cpu_probe_philips(c); + break; ++ case PRID_COMP_INGENIC: ++ cpu_probe_ingenic(c); ++ break; + default: + c->cputype = CPU_UNKNOWN; + } +diff -urN linux-2.6.24.7.old/arch/mips/mm/c-r4k.c linux-2.6.24.7/arch/mips/mm/c-r4k.c +--- linux-2.6.24.7.old/arch/mips/mm/c-r4k.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/arch/mips/mm/c-r4k.c 2009-04-12 18:13:57.000000000 +0200 +@@ -874,6 +874,36 @@ + c->dcache.waybit = 0; + break; + ++ case CPU_JZRISC: ++ config1 = read_c0_config1(); ++ config1 = (config1 >> 22) & 0x07; ++ if (config1 == 0x07) ++ config1 = 10; ++ else ++ config1 = config1 + 11; ++ config1 += 2; ++ icache_size = (1 << config1); ++ c->icache.linesz = 32; ++ c->icache.ways = 4; ++ c->icache.waybit = __ffs(icache_size / c->icache.ways); ++ ++ config1 = read_c0_config1(); ++ config1 = (config1 >> 13) & 0x07; ++ if (config1 == 0x07) ++ config1 = 10; ++ else ++ config1 = config1 + 11; ++ config1 += 2; ++ dcache_size = (1 << config1); ++ c->dcache.linesz = 32; ++ c->dcache.ways = 4; ++ c->dcache.waybit = __ffs(dcache_size / c->dcache.ways); ++ ++ c->dcache.flags = 0; ++ c->options |= MIPS_CPU_PREFETCH; ++ ++ break; ++ + default: + if (!(config & MIPS_CONF_M)) + panic("Don't know how to probe P-caches on this cpu."); +diff -urN linux-2.6.24.7.old/arch/mips/mm/cache.c linux-2.6.24.7/arch/mips/mm/cache.c +--- linux-2.6.24.7.old/arch/mips/mm/cache.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/arch/mips/mm/cache.c 2009-04-12 18:13:57.000000000 +0200 +@@ -47,6 +47,8 @@ + void (*_dma_cache_inv)(unsigned long start, unsigned long size); + + EXPORT_SYMBOL(_dma_cache_wback_inv); ++EXPORT_SYMBOL(_dma_cache_wback); ++EXPORT_SYMBOL(_dma_cache_inv); + + #endif /* CONFIG_DMA_NONCOHERENT */ + +diff -urN linux-2.6.24.7.old/arch/mips/mm/tlbex.c linux-2.6.24.7/arch/mips/mm/tlbex.c +--- linux-2.6.24.7.old/arch/mips/mm/tlbex.c 2009-04-12 18:05:07.000000000 +0200 ++++ linux-2.6.24.7/arch/mips/mm/tlbex.c 2009-04-12 18:13:57.000000000 +0200 +@@ -981,6 +981,11 @@ + tlbw(p); + break; + ++ case CPU_JZRISC: ++ tlbw(p); ++ i_nop(p); ++ break; ++ + default: + panic("No TLB refill handler yet (CPU type: %d)", + current_cpu_data.cputype); +diff -urN linux-2.6.24.7.old/crypto/Kconfig linux-2.6.24.7/crypto/Kconfig +--- linux-2.6.24.7.old/crypto/Kconfig 2009-04-12 18:05:07.000000000 +0200 ++++ linux-2.6.24.7/crypto/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -502,6 +502,14 @@ + Authenc: Combined mode wrapper for IPsec. + This is required for IPSec. + ++config CRYPTO_LZO ++ tristate "LZO compression algorithm" ++ select CRYPTO_ALGAPI ++ select LZO_COMPRESS ++ select LZO_DECOMPRESS ++ help ++ This is the LZO algorithm. ++ + source "drivers/crypto/Kconfig" + + endif # if CRYPTO +diff -urN linux-2.6.24.7.old/crypto/Makefile linux-2.6.24.7/crypto/Makefile +--- linux-2.6.24.7.old/crypto/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/crypto/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -51,6 +51,7 @@ + obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o + obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o + obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o ++obj-$(CONFIG_CRYPTO_LZO) += lzo.o + obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o + + obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o +diff -urN linux-2.6.24.7.old/crypto/lzo.c linux-2.6.24.7/crypto/lzo.c +--- linux-2.6.24.7.old/crypto/lzo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/crypto/lzo.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,106 @@ ++/* ++ * Cryptographic API. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct lzo_ctx { ++ void *lzo_comp_mem; ++}; ++ ++static int lzo_init(struct crypto_tfm *tfm) ++{ ++ struct lzo_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ ctx->lzo_comp_mem = vmalloc(LZO1X_MEM_COMPRESS); ++ if (!ctx->lzo_comp_mem) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static void lzo_exit(struct crypto_tfm *tfm) ++{ ++ struct lzo_ctx *ctx = crypto_tfm_ctx(tfm); ++ ++ vfree(ctx->lzo_comp_mem); ++} ++ ++static int lzo_compress(struct crypto_tfm *tfm, const u8 *src, ++ unsigned int slen, u8 *dst, unsigned int *dlen) ++{ ++ struct lzo_ctx *ctx = crypto_tfm_ctx(tfm); ++ size_t tmp_len = *dlen; /* size_t(ulong) <-> uint on 64 bit */ ++ int err; ++ ++ err = lzo1x_1_compress(src, slen, dst, &tmp_len, ctx->lzo_comp_mem); ++ ++ if (err != LZO_E_OK) ++ return -EINVAL; ++ ++ *dlen = tmp_len; ++ return 0; ++} ++ ++static int lzo_decompress(struct crypto_tfm *tfm, const u8 *src, ++ unsigned int slen, u8 *dst, unsigned int *dlen) ++{ ++ int err; ++ size_t tmp_len = *dlen; /* size_t(ulong) <-> uint on 64 bit */ ++ ++ err = lzo1x_decompress_safe(src, slen, dst, &tmp_len); ++ ++ if (err != LZO_E_OK) ++ return -EINVAL; ++ ++ *dlen = tmp_len; ++ return 0; ++ ++} ++ ++static struct crypto_alg alg = { ++ .cra_name = "lzo", ++ .cra_flags = CRYPTO_ALG_TYPE_COMPRESS, ++ .cra_ctxsize = sizeof(struct lzo_ctx), ++ .cra_module = THIS_MODULE, ++ .cra_list = LIST_HEAD_INIT(alg.cra_list), ++ .cra_init = lzo_init, ++ .cra_exit = lzo_exit, ++ .cra_u = { .compress = { ++ .coa_compress = lzo_compress, ++ .coa_decompress = lzo_decompress } } ++}; ++ ++static int __init init(void) ++{ ++ return crypto_register_alg(&alg); ++} ++ ++static void __exit fini(void) ++{ ++ crypto_unregister_alg(&alg); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("LZO Compression Algorithm"); +diff -urN linux-2.6.24.7.old/crypto/tcrypt.c linux-2.6.24.7/crypto/tcrypt.c +--- linux-2.6.24.7.old/crypto/tcrypt.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/crypto/tcrypt.c 2009-04-12 18:13:57.000000000 +0200 +@@ -78,7 +78,7 @@ + "twofish", "serpent", "sha384", "sha512", "md4", "aes", "cast6", + "arc4", "michael_mic", "deflate", "crc32c", "tea", "xtea", + "khazad", "wp512", "wp384", "wp256", "tnepres", "xeta", "fcrypt", +- "camellia", "seed", NULL ++ "camellia", "seed", "lzo", NULL + }; + + static void hexdump(unsigned char *buf, unsigned int len) +@@ -800,7 +800,8 @@ + crypto_free_hash(tfm); + } + +-static void test_deflate(void) ++static void test_comp(char *algo, struct comp_testvec *ctemplate, ++ struct comp_testvec *dtemplate, int ctcount, int dtcount) + { + unsigned int i; + char result[COMP_BUF_SIZE]; +@@ -808,25 +809,26 @@ + struct comp_testvec *tv; + unsigned int tsize; + +- printk("\ntesting deflate compression\n"); ++ printk("\ntesting %s compression\n", algo); + +- tsize = sizeof (deflate_comp_tv_template); ++ tsize = sizeof(struct comp_testvec); ++ tsize *= ctcount; + if (tsize > TVMEMSIZE) { + printk("template (%u) too big for tvmem (%u)\n", tsize, + TVMEMSIZE); + return; + } + +- memcpy(tvmem, deflate_comp_tv_template, tsize); ++ memcpy(tvmem, ctemplate, tsize); + tv = (void *)tvmem; + +- tfm = crypto_alloc_comp("deflate", 0, CRYPTO_ALG_ASYNC); ++ tfm = crypto_alloc_comp(algo, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { +- printk("failed to load transform for deflate\n"); ++ printk("failed to load transform for %s\n", algo); + return; + } + +- for (i = 0; i < DEFLATE_COMP_TEST_VECTORS; i++) { ++ for (i = 0; i < ctcount; i++) { + int ilen, ret, dlen = COMP_BUF_SIZE; + + printk("test %u:\n", i + 1); +@@ -845,19 +847,20 @@ + ilen, dlen); + } + +- printk("\ntesting deflate decompression\n"); ++ printk("\ntesting %s decompression\n", algo); + +- tsize = sizeof (deflate_decomp_tv_template); ++ tsize = sizeof(struct comp_testvec); ++ tsize *= dtcount; + if (tsize > TVMEMSIZE) { + printk("template (%u) too big for tvmem (%u)\n", tsize, + TVMEMSIZE); + goto out; + } + +- memcpy(tvmem, deflate_decomp_tv_template, tsize); ++ memcpy(tvmem, dtemplate, tsize); + tv = (void *)tvmem; + +- for (i = 0; i < DEFLATE_DECOMP_TEST_VECTORS; i++) { ++ for (i = 0; i < dtcount; i++) { + int ilen, ret, dlen = COMP_BUF_SIZE; + + printk("test %u:\n", i + 1); +@@ -1057,7 +1060,11 @@ + test_hash("tgr192", tgr192_tv_template, TGR192_TEST_VECTORS); + test_hash("tgr160", tgr160_tv_template, TGR160_TEST_VECTORS); + test_hash("tgr128", tgr128_tv_template, TGR128_TEST_VECTORS); +- test_deflate(); ++ test_comp("deflate", deflate_comp_tv_template, ++ deflate_decomp_tv_template, DEFLATE_COMP_TEST_VECTORS, ++ DEFLATE_DECOMP_TEST_VECTORS); ++ test_comp("lzo", lzo_comp_tv_template, lzo_decomp_tv_template, ++ LZO_COMP_TEST_VECTORS, LZO_DECOMP_TEST_VECTORS); + test_hash("crc32c", crc32c_tv_template, CRC32C_TEST_VECTORS); + test_hash("hmac(md5)", hmac_md5_tv_template, + HMAC_MD5_TEST_VECTORS); +@@ -1167,7 +1174,9 @@ + break; + + case 13: +- test_deflate(); ++ test_comp("deflate", deflate_comp_tv_template, ++ deflate_decomp_tv_template, DEFLATE_COMP_TEST_VECTORS, ++ DEFLATE_DECOMP_TEST_VECTORS); + break; + + case 14: +@@ -1292,6 +1301,11 @@ + CAMELLIA_CBC_DEC_TEST_VECTORS); + break; + ++ case 33: ++ test_comp("lzo", lzo_comp_tv_template, lzo_decomp_tv_template, ++ LZO_COMP_TEST_VECTORS, LZO_DECOMP_TEST_VECTORS); ++ break; ++ + case 100: + test_hash("hmac(md5)", hmac_md5_tv_template, + HMAC_MD5_TEST_VECTORS); +diff -urN linux-2.6.24.7.old/crypto/tcrypt.h linux-2.6.24.7/crypto/tcrypt.h +--- linux-2.6.24.7.old/crypto/tcrypt.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/crypto/tcrypt.h 2009-04-12 18:13:57.000000000 +0200 +@@ -4408,6 +4408,88 @@ + }; + + /* ++ * LZO test vectors (null-terminated strings). ++ */ ++#define LZO_COMP_TEST_VECTORS 2 ++#define LZO_DECOMP_TEST_VECTORS 2 ++ ++static struct comp_testvec lzo_comp_tv_template[] = { ++ { ++ .inlen = 70, ++ .outlen = 46, ++ .input = "Join us now and share the software " ++ "Join us now and share the software ", ++ .output = { 0x00, 0x0d, 0x4a, 0x6f, 0x69, 0x6e, 0x20, 0x75, ++ 0x73, 0x20, 0x6e, 0x6f, 0x77, 0x20, 0x61, 0x6e, ++ 0x64, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x20, ++ 0x74, 0x68, 0x65, 0x20, 0x73, 0x6f, 0x66, 0x74, ++ 0x77, 0x70, 0x01, 0x01, 0x4a, 0x6f, 0x69, 0x6e, ++ 0x3d, 0x88, 0x00, 0x11, 0x00, 0x00 }, ++ }, { ++ .inlen = 159, ++ .outlen = 133, ++ .input = "This document describes a compression method based on the LZO " ++ "compression algorithm. This document defines the application of " ++ "the LZO algorithm used in UBIFS.", ++ .output = { 0x00, 0x2b, 0x54, 0x68, 0x69, 0x73, 0x20, 0x64, ++ 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, ++ 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, ++ 0x73, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x70, ++ 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, ++ 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x62, ++ 0x61, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, ++ 0x74, 0x68, 0x65, 0x20, 0x4c, 0x5a, 0x4f, 0x2b, ++ 0x8c, 0x00, 0x0d, 0x61, 0x6c, 0x67, 0x6f, 0x72, ++ 0x69, 0x74, 0x68, 0x6d, 0x2e, 0x20, 0x20, 0x54, ++ 0x68, 0x69, 0x73, 0x2a, 0x54, 0x01, 0x02, 0x66, ++ 0x69, 0x6e, 0x65, 0x73, 0x94, 0x06, 0x05, 0x61, ++ 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x76, ++ 0x0a, 0x6f, 0x66, 0x88, 0x02, 0x60, 0x09, 0x27, ++ 0xf0, 0x00, 0x0c, 0x20, 0x75, 0x73, 0x65, 0x64, ++ 0x20, 0x69, 0x6e, 0x20, 0x55, 0x42, 0x49, 0x46, ++ 0x53, 0x2e, 0x11, 0x00, 0x00 }, ++ }, ++}; ++ ++static struct comp_testvec lzo_decomp_tv_template[] = { ++ { ++ .inlen = 133, ++ .outlen = 159, ++ .input = { 0x00, 0x2b, 0x54, 0x68, 0x69, 0x73, 0x20, 0x64, ++ 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, ++ 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, ++ 0x73, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x70, ++ 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, ++ 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x62, ++ 0x61, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, ++ 0x74, 0x68, 0x65, 0x20, 0x4c, 0x5a, 0x4f, 0x2b, ++ 0x8c, 0x00, 0x0d, 0x61, 0x6c, 0x67, 0x6f, 0x72, ++ 0x69, 0x74, 0x68, 0x6d, 0x2e, 0x20, 0x20, 0x54, ++ 0x68, 0x69, 0x73, 0x2a, 0x54, 0x01, 0x02, 0x66, ++ 0x69, 0x6e, 0x65, 0x73, 0x94, 0x06, 0x05, 0x61, ++ 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x76, ++ 0x0a, 0x6f, 0x66, 0x88, 0x02, 0x60, 0x09, 0x27, ++ 0xf0, 0x00, 0x0c, 0x20, 0x75, 0x73, 0x65, 0x64, ++ 0x20, 0x69, 0x6e, 0x20, 0x55, 0x42, 0x49, 0x46, ++ 0x53, 0x2e, 0x11, 0x00, 0x00 }, ++ .output = "This document describes a compression method based on the LZO " ++ "compression algorithm. This document defines the application of " ++ "the LZO algorithm used in UBIFS.", ++ }, { ++ .inlen = 46, ++ .outlen = 70, ++ .input = { 0x00, 0x0d, 0x4a, 0x6f, 0x69, 0x6e, 0x20, 0x75, ++ 0x73, 0x20, 0x6e, 0x6f, 0x77, 0x20, 0x61, 0x6e, ++ 0x64, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x20, ++ 0x74, 0x68, 0x65, 0x20, 0x73, 0x6f, 0x66, 0x74, ++ 0x77, 0x70, 0x01, 0x01, 0x4a, 0x6f, 0x69, 0x6e, ++ 0x3d, 0x88, 0x00, 0x11, 0x00, 0x00 }, ++ .output = "Join us now and share the software " ++ "Join us now and share the software ", ++ }, ++}; ++ ++/* + * Michael MIC test vectors from IEEE 802.11i + */ + #define MICHAEL_MIC_TEST_VECTORS 6 +diff -urN linux-2.6.24.7.old/drivers/char/Kconfig linux-2.6.24.7/drivers/char/Kconfig +--- linux-2.6.24.7.old/drivers/char/Kconfig 2009-04-12 18:05:07.000000000 +0200 ++++ linux-2.6.24.7/drivers/char/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -731,6 +731,16 @@ + To compile this driver as a module, choose M here: the + module will be called rtc. + ++config RTC_PCF8563 ++ bool 'Philips PCF8563 Real Time Clock (I2C Bus)' ++ help ++ Philips PCF8563 Real Time Clock (I2C Bus) ++ ++config RTC_JZ ++ bool 'Jz47XX On-Chip Real Time Clock' ++ help ++ Jz47XX On-Chip Real Time Clock ++ + config JS_RTC + tristate "Enhanced Real Time Clock Support" + depends on SPARC32 && PCI +@@ -1046,6 +1056,7 @@ + default y + + source "drivers/s390/char/Kconfig" ++source "drivers/char/jzchar/Kconfig" + + endmenu + +diff -urN linux-2.6.24.7.old/drivers/char/Kconfig.orig linux-2.6.24.7/drivers/char/Kconfig.orig +--- linux-2.6.24.7.old/drivers/char/Kconfig.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/Kconfig.orig 2009-04-12 18:01:55.000000000 +0200 +@@ -0,0 +1,1051 @@ ++# ++# Character device configuration ++# ++ ++menu "Character devices" ++ ++config VT ++ bool "Virtual terminal" if EMBEDDED ++ depends on !S390 ++ select INPUT ++ default y if !VIOCONS ++ ---help--- ++ If you say Y here, you will get support for terminal devices with ++ display and keyboard devices. These are called "virtual" because you ++ can run several virtual terminals (also called virtual consoles) on ++ one physical terminal. This is rather useful, for example one ++ virtual terminal can collect system messages and warnings, another ++ one can be used for a text-mode user session, and a third could run ++ an X session, all in parallel. Switching between virtual terminals ++ is done with certain key combinations, usually Alt-. ++ ++ The setterm command ("man setterm") can be used to change the ++ properties (such as colors or beeping) of a virtual terminal. The ++ man page console_codes(4) ("man console_codes") contains the special ++ character sequences that can be used to change those properties ++ directly. The fonts used on virtual terminals can be changed with ++ the setfont ("man setfont") command and the key bindings are defined ++ with the loadkeys ("man loadkeys") command. ++ ++ You need at least one virtual terminal device in order to make use ++ of your keyboard and monitor. Therefore, only people configuring an ++ embedded system would want to say N here in order to save some ++ memory; the only way to log into such a system is then via a serial ++ or network connection. ++ ++ If unsure, say Y, or else you won't be able to do much with your new ++ shiny Linux system :-) ++ ++config VT_CONSOLE ++ bool "Support for console on virtual terminal" if EMBEDDED ++ depends on VT ++ default y ++ ---help--- ++ The system console is the device which receives all kernel messages ++ and warnings and which allows logins in single user mode. If you ++ answer Y here, a virtual terminal (the device used to interact with ++ a physical terminal) can be used as system console. This is the most ++ common mode of operations, so you should say Y here unless you want ++ the kernel messages be output only to a serial port (in which case ++ you should say Y to "Console on serial port", below). ++ ++ If you do say Y here, by default the currently visible virtual ++ terminal (/dev/tty0) will be used as system console. You can change ++ that with a kernel command line option such as "console=tty3" which ++ would use the third virtual terminal as system console. (Try "man ++ bootparam" or see the documentation of your boot loader (lilo or ++ loadlin) about how to pass options to the kernel at boot time.) ++ ++ If unsure, say Y. ++ ++config HW_CONSOLE ++ bool ++ depends on VT && !S390 && !UML ++ default y ++ ++config VT_HW_CONSOLE_BINDING ++ bool "Support for binding and unbinding console drivers" ++ depends on HW_CONSOLE ++ default n ++ ---help--- ++ The virtual terminal is the device that interacts with the physical ++ terminal through console drivers. On these systems, at least one ++ console driver is loaded. In other configurations, additional console ++ drivers may be enabled, such as the framebuffer console. If more than ++ 1 console driver is enabled, setting this to 'y' will allow you to ++ select the console driver that will serve as the backend for the ++ virtual terminals. ++ ++ See for more ++ information. For framebuffer console users, please refer to ++ . ++ ++config SERIAL_NONSTANDARD ++ bool "Non-standard serial port support" ++ depends on HAS_IOMEM ++ ---help--- ++ Say Y here if you have any non-standard serial boards -- boards ++ which aren't supported using the standard "dumb" serial driver. ++ This includes intelligent serial boards such as Cyclades, ++ Digiboards, etc. These are usually used for systems that need many ++ serial ports because they serve many terminals or dial-in ++ connections. ++ ++ Note that the answer to this question won't directly affect the ++ kernel: saying N will just cause the configurator to skip all ++ the questions about non-standard serial boards. ++ ++ Most people can say N here. ++ ++config COMPUTONE ++ tristate "Computone IntelliPort Plus serial support" ++ depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) ++ ---help--- ++ This driver supports the entire family of Intelliport II/Plus ++ controllers with the exception of the MicroChannel controllers and ++ products previous to the Intelliport II. These are multiport cards, ++ which give you many serial ports. You would need something like this ++ to connect more than two modems to your Linux box, for instance in ++ order to become a dial-in server. If you have a card like that, say ++ Y here and read . ++ ++ To compile this driver as modules, choose M here: the ++ modules will be called ip2 and ip2main. ++ ++config ROCKETPORT ++ tristate "Comtrol RocketPort support" ++ depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) ++ help ++ This driver supports Comtrol RocketPort and RocketModem PCI boards. ++ These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or ++ modems. For information about the RocketPort/RocketModem boards ++ and this driver read . ++ ++ To compile this driver as a module, choose M here: the ++ module will be called rocket. ++ ++ If you want to compile this driver into the kernel, say Y here. If ++ you don't have a Comtrol RocketPort/RocketModem card installed, say N. ++ ++config CYCLADES ++ tristate "Cyclades async mux support" ++ depends on SERIAL_NONSTANDARD && (PCI || ISA) ++ select FW_LOADER ++ ---help--- ++ This driver supports Cyclades Z and Y multiserial boards. ++ You would need something like this to connect more than two modems to ++ your Linux box, for instance in order to become a dial-in server. ++ ++ For information about the Cyclades-Z card, read ++ . ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cyclades. ++ ++ If you haven't heard about it, it's safe to say N. ++ ++config CYZ_INTR ++ bool "Cyclades-Z interrupt mode operation (EXPERIMENTAL)" ++ depends on EXPERIMENTAL && CYCLADES ++ help ++ The Cyclades-Z family of multiport cards allows 2 (two) driver op ++ modes: polling and interrupt. In polling mode, the driver will check ++ the status of the Cyclades-Z ports every certain amount of time ++ (which is called polling cycle and is configurable). In interrupt ++ mode, it will use an interrupt line (IRQ) in order to check the ++ status of the Cyclades-Z ports. The default op mode is polling. If ++ unsure, say N. ++ ++config DIGIEPCA ++ tristate "Digiboard Intelligent Async Support" ++ depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) ++ ---help--- ++ This is a driver for Digi International's Xx, Xeve, and Xem series ++ of cards which provide multiple serial ports. You would need ++ something like this to connect more than two modems to your Linux ++ box, for instance in order to become a dial-in server. This driver ++ supports the original PC (ISA) boards as well as PCI, and EISA. If ++ you have a card like this, say Y here and read the file ++ . ++ ++ To compile this driver as a module, choose M here: the ++ module will be called epca. ++ ++config ESPSERIAL ++ tristate "Hayes ESP serial port support" ++ depends on SERIAL_NONSTANDARD && ISA && ISA_DMA_API ++ help ++ This is a driver which supports Hayes ESP serial ports. Both single ++ port cards and multiport cards are supported. Make sure to read ++ . ++ ++ To compile this driver as a module, choose M here: the ++ module will be called esp. ++ ++ If unsure, say N. ++ ++config MOXA_INTELLIO ++ tristate "Moxa Intellio support" ++ depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) ++ help ++ Say Y here if you have a Moxa Intellio multiport serial card. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called moxa. ++ ++config MOXA_SMARTIO ++ tristate "Moxa SmartIO support (OBSOLETE)" ++ depends on SERIAL_NONSTANDARD ++ help ++ Say Y here if you have a Moxa SmartIO multiport serial card. ++ ++ This driver can also be built as a module ( = code which can be ++ inserted in and removed from the running kernel whenever you want). ++ The module will be called mxser. If you want to do that, say M ++ here. ++ ++config MOXA_SMARTIO_NEW ++ tristate "Moxa SmartIO support v. 2.0" ++ depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) ++ help ++ Say Y here if you have a Moxa SmartIO multiport serial card and/or ++ want to help develop a new version of this driver. ++ ++ This is upgraded (1.9.1) driver from original Moxa drivers with ++ changes finally resulting in PCI probing. ++ ++ This driver can also be built as a module. The module will be called ++ mxser_new. If you want to do that, say M here. ++ ++config ISI ++ tristate "Multi-Tech multiport card support (EXPERIMENTAL)" ++ depends on SERIAL_NONSTANDARD && PCI ++ select FW_LOADER ++ help ++ This is a driver for the Multi-Tech cards which provide several ++ serial ports. The driver is experimental and can currently only be ++ built as a module. The module will be called isicom. ++ If you want to do that, choose M here. ++ ++config SYNCLINK ++ tristate "Microgate SyncLink card support" ++ depends on SERIAL_NONSTANDARD && PCI && ISA_DMA_API ++ help ++ Provides support for the SyncLink ISA and PCI multiprotocol serial ++ adapters. These adapters support asynchronous and HDLC bit ++ synchronous communication up to 10Mbps (PCI adapter). ++ ++ This driver can only be built as a module ( = code which can be ++ inserted in and removed from the running kernel whenever you want). ++ The module will be called synclink. If you want to do that, say M ++ here. ++ ++config SYNCLINKMP ++ tristate "SyncLink Multiport support" ++ depends on SERIAL_NONSTANDARD && PCI ++ help ++ Enable support for the SyncLink Multiport (2 or 4 ports) ++ serial adapter, running asynchronous and HDLC communications up ++ to 2.048Mbps. Each ports is independently selectable for ++ RS-232, V.35, RS-449, RS-530, and X.21 ++ ++ This driver may be built as a module ( = code which can be ++ inserted in and removed from the running kernel whenever you want). ++ The module will be called synclinkmp. If you want to do that, say M ++ here. ++ ++config SYNCLINK_GT ++ tristate "SyncLink GT/AC support" ++ depends on SERIAL_NONSTANDARD && PCI ++ help ++ Support for SyncLink GT and SyncLink AC families of ++ synchronous and asynchronous serial adapters ++ manufactured by Microgate Systems, Ltd. (www.microgate.com) ++ ++config N_HDLC ++ tristate "HDLC line discipline support" ++ depends on SERIAL_NONSTANDARD ++ help ++ Allows synchronous HDLC communications with tty device drivers that ++ support synchronous HDLC such as the Microgate SyncLink adapter. ++ ++ This driver can only be built as a module ( = code which can be ++ inserted in and removed from the running kernel whenever you want). ++ The module will be called n_hdlc. If you want to do that, say M ++ here. ++ ++config RISCOM8 ++ tristate "SDL RISCom/8 card support" ++ depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP ++ help ++ This is a driver for the SDL Communications RISCom/8 multiport card, ++ which gives you many serial ports. You would need something like ++ this to connect more than two modems to your Linux box, for instance ++ in order to become a dial-in server. If you have a card like that, ++ say Y here and read the file . ++ ++ Also it's possible to say M here and compile this driver as kernel ++ loadable module; the module will be called riscom8. ++ ++config SPECIALIX ++ tristate "Specialix IO8+ card support" ++ depends on SERIAL_NONSTANDARD ++ help ++ This is a driver for the Specialix IO8+ multiport card (both the ++ ISA and the PCI version) which gives you many serial ports. You ++ would need something like this to connect more than two modems to ++ your Linux box, for instance in order to become a dial-in server. ++ ++ If you have a card like that, say Y here and read the file ++ . Also it's possible to say M here ++ and compile this driver as kernel loadable module which will be ++ called specialix. ++ ++config SPECIALIX_RTSCTS ++ bool "Specialix DTR/RTS pin is RTS" ++ depends on SPECIALIX ++ help ++ The Specialix IO8+ card can only support either RTS or DTR. If you ++ say N here, the driver will use the pin as "DTR" when the tty is in ++ software handshake mode. If you say Y here or hardware handshake is ++ on, it will always be RTS. Read the file ++ for more information. ++ ++config SX ++ tristate "Specialix SX (and SI) card support" ++ depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) ++ help ++ This is a driver for the SX and SI multiport serial cards. ++ Please read the file for details. ++ ++ This driver can only be built as a module ( = code which can be ++ inserted in and removed from the running kernel whenever you want). ++ The module will be called sx. If you want to do that, say M here. ++ ++config RIO ++ tristate "Specialix RIO system support" ++ depends on SERIAL_NONSTANDARD ++ help ++ This is a driver for the Specialix RIO, a smart serial card which ++ drives an outboard box that can support up to 128 ports. Product ++ information is at . ++ There are both ISA and PCI versions. ++ ++config RIO_OLDPCI ++ bool "Support really old RIO/PCI cards" ++ depends on RIO ++ help ++ Older RIO PCI cards need some initialization-time configuration to ++ determine the IRQ and some control addresses. If you have a RIO and ++ this doesn't seem to work, try setting this to Y. ++ ++config STALDRV ++ bool "Stallion multiport serial support" ++ depends on SERIAL_NONSTANDARD ++ help ++ Stallion cards give you many serial ports. You would need something ++ like this to connect more than two modems to your Linux box, for ++ instance in order to become a dial-in server. If you say Y here, ++ you will be asked for your specific card model in the next ++ questions. Make sure to read in ++ this case. If you have never heard about all this, it's safe to ++ say N. ++ ++config STALLION ++ tristate "Stallion EasyIO or EC8/32 support" ++ depends on STALDRV && BROKEN_ON_SMP && (ISA || EISA || PCI) ++ help ++ If you have an EasyIO or EasyConnection 8/32 multiport Stallion ++ card, then this is for you; say Y. Make sure to read ++ . ++ ++ To compile this driver as a module, choose M here: the ++ module will be called stallion. ++ ++config ISTALLION ++ tristate "Stallion EC8/64, ONboard, Brumby support" ++ depends on STALDRV && BROKEN_ON_SMP && (ISA || EISA || PCI) ++ help ++ If you have an EasyConnection 8/64, ONboard, Brumby or Stallion ++ serial multiport card, say Y here. Make sure to read ++ . ++ ++ To compile this driver as a module, choose M here: the ++ module will be called istallion. ++ ++config A2232 ++ tristate "Commodore A2232 serial support (EXPERIMENTAL)" ++ depends on EXPERIMENTAL && ZORRO && BROKEN_ON_SMP ++ ---help--- ++ This option supports the 2232 7-port serial card shipped with the ++ Amiga 2000 and other Zorro-bus machines, dating from 1989. At ++ a max of 19,200 bps, the ports are served by a 6551 ACIA UART chip ++ each, plus a 8520 CIA, and a master 6502 CPU and buffer as well. The ++ ports were connected with 8 pin DIN connectors on the card bracket, ++ for which 8 pin to DB25 adapters were supplied. The card also had ++ jumpers internally to toggle various pinning configurations. ++ ++ This driver can be built as a module; but then "generic_serial" ++ will also be built as a module. This has to be loaded before ++ "ser_a2232". If you want to do this, answer M here. ++ ++config SGI_SNSC ++ bool "SGI Altix system controller communication support" ++ depends on (IA64_SGI_SN2 || IA64_GENERIC) ++ help ++ If you have an SGI Altix and you want to enable system ++ controller communication from user space (you want this!), ++ say Y. Otherwise, say N. ++ ++config SGI_TIOCX ++ bool "SGI TIO CX driver support" ++ depends on (IA64_SGI_SN2 || IA64_GENERIC) ++ help ++ If you have an SGI Altix and you have fpga devices attached ++ to your TIO, say Y here, otherwise say N. ++ ++config SGI_MBCS ++ tristate "SGI FPGA Core Services driver support" ++ depends on SGI_TIOCX ++ help ++ If you have an SGI Altix with an attached SABrick ++ say Y or M here, otherwise say N. ++ ++source "drivers/serial/Kconfig" ++ ++config UNIX98_PTYS ++ bool "Unix98 PTY support" if EMBEDDED ++ default y ++ ---help--- ++ A pseudo terminal (PTY) is a software device consisting of two ++ halves: a master and a slave. The slave device behaves identical to ++ a physical terminal; the master device is used by a process to ++ read data from and write data to the slave, thereby emulating a ++ terminal. Typical programs for the master side are telnet servers ++ and xterms. ++ ++ Linux has traditionally used the BSD-like names /dev/ptyxx for ++ masters and /dev/ttyxx for slaves of pseudo terminals. This scheme ++ has a number of problems. The GNU C library glibc 2.1 and later, ++ however, supports the Unix98 naming standard: in order to acquire a ++ pseudo terminal, a process opens /dev/ptmx; the number of the pseudo ++ terminal is then made available to the process and the pseudo ++ terminal slave can be accessed as /dev/pts/. What was ++ traditionally /dev/ttyp2 will then be /dev/pts/2, for example. ++ ++ All modern Linux systems use the Unix98 ptys. Say Y unless ++ you're on an embedded system and want to conserve memory. ++ ++config LEGACY_PTYS ++ bool "Legacy (BSD) PTY support" ++ default y ++ ---help--- ++ A pseudo terminal (PTY) is a software device consisting of two ++ halves: a master and a slave. The slave device behaves identical to ++ a physical terminal; the master device is used by a process to ++ read data from and write data to the slave, thereby emulating a ++ terminal. Typical programs for the master side are telnet servers ++ and xterms. ++ ++ Linux has traditionally used the BSD-like names /dev/ptyxx ++ for masters and /dev/ttyxx for slaves of pseudo ++ terminals. This scheme has a number of problems, including ++ security. This option enables these legacy devices; on most ++ systems, it is safe to say N. ++ ++ ++config LEGACY_PTY_COUNT ++ int "Maximum number of legacy PTY in use" ++ depends on LEGACY_PTYS ++ range 0 256 ++ default "256" ++ ---help--- ++ The maximum number of legacy PTYs that can be used at any one time. ++ The default is 256, and should be more than enough. Embedded ++ systems may want to reduce this to save memory. ++ ++ When not in use, each legacy PTY occupies 12 bytes on 32-bit ++ architectures and 24 bytes on 64-bit architectures. ++ ++config BRIQ_PANEL ++ tristate 'Total Impact briQ front panel driver' ++ depends on PPC_CHRP ++ ---help--- ++ The briQ is a small footprint CHRP computer with a frontpanel VFD, a ++ tristate led and two switches. It is the size of a CDROM drive. ++ ++ If you have such one and want anything showing on the VFD then you ++ must answer Y here. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called briq_panel. ++ ++ It's safe to say N here. ++ ++config PRINTER ++ tristate "Parallel printer support" ++ depends on PARPORT ++ ---help--- ++ If you intend to attach a printer to the parallel port of your Linux ++ box (as opposed to using a serial printer; if the connector at the ++ printer has 9 or 25 holes ["female"], then it's serial), say Y. ++ Also read the Printing-HOWTO, available from ++ . ++ ++ It is possible to share one parallel port among several devices ++ (e.g. printer and ZIP drive) and it is safe to compile the ++ corresponding drivers into the kernel. ++ ++ To compile this driver as a module, choose M here and read ++ . The module will be called lp. ++ ++ If you have several parallel ports, you can specify which ports to ++ use with the "lp" kernel command line option. (Try "man bootparam" ++ or see the documentation of your boot loader (lilo or loadlin) about ++ how to pass options to the kernel at boot time.) The syntax of the ++ "lp" command line option can be found in . ++ ++ If you have more than 8 printers, you need to increase the LP_NO ++ macro in lp.c and the PARPORT_MAX macro in parport.h. ++ ++config LP_CONSOLE ++ bool "Support for console on line printer" ++ depends on PRINTER ++ ---help--- ++ If you want kernel messages to be printed out as they occur, you ++ can have a console on the printer. This option adds support for ++ doing that; to actually get it to happen you need to pass the ++ option "console=lp0" to the kernel at boot time. ++ ++ If the printer is out of paper (or off, or unplugged, or too ++ busy..) the kernel will stall until the printer is ready again. ++ By defining CONSOLE_LP_STRICT to 0 (at your own risk) you ++ can make the kernel continue when this happens, ++ but it'll lose the kernel messages. ++ ++ If unsure, say N. ++ ++config PPDEV ++ tristate "Support for user-space parallel port device drivers" ++ depends on PARPORT ++ ---help--- ++ Saying Y to this adds support for /dev/parport device nodes. This ++ is needed for programs that want portable access to the parallel ++ port, for instance deviceid (which displays Plug-and-Play device ++ IDs). ++ ++ This is the parallel port equivalent of SCSI generic support (sg). ++ It is safe to say N to this -- it is not needed for normal printing ++ or parallel port CD-ROM/disk support. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ppdev. ++ ++ If unsure, say N. ++ ++config HVC_DRIVER ++ bool ++ help ++ Generic "hypervisor virtual console" infrastructure for various ++ hypervisors (pSeries, iSeries, Xen, lguest). ++ It will automatically be selected if one of the back-end console drivers ++ is selected. ++ ++ ++config HVC_CONSOLE ++ bool "pSeries Hypervisor Virtual Console support" ++ depends on PPC_PSERIES ++ select HVC_DRIVER ++ help ++ pSeries machines when partitioned support a hypervisor virtual ++ console. This driver allows each pSeries partition to have a console ++ which is accessed via the HMC. ++ ++config HVC_ISERIES ++ bool "iSeries Hypervisor Virtual Console support" ++ depends on PPC_ISERIES ++ default y ++ select HVC_DRIVER ++ help ++ iSeries machines support a hypervisor virtual console. ++ ++config HVC_RTAS ++ bool "IBM RTAS Console support" ++ depends on PPC_RTAS ++ select HVC_DRIVER ++ help ++ IBM Console device driver which makes use of RTAS ++ ++config HVC_BEAT ++ bool "Toshiba's Beat Hypervisor Console support" ++ depends on PPC_CELLEB ++ select HVC_DRIVER ++ help ++ Toshiba's Cell Reference Set Beat Console device driver ++ ++config HVC_XEN ++ bool "Xen Hypervisor Console support" ++ depends on XEN ++ select HVC_DRIVER ++ default y ++ help ++ Xen virtual console device driver ++ ++config VIRTIO_CONSOLE ++ bool ++ select HVC_DRIVER ++ ++config HVCS ++ tristate "IBM Hypervisor Virtual Console Server support" ++ depends on PPC_PSERIES ++ help ++ Partitionable IBM Power5 ppc64 machines allow hosting of ++ firmware virtual consoles from one Linux partition by ++ another Linux partition. This driver allows console data ++ from Linux partitions to be accessed through TTY device ++ interfaces in the device tree of a Linux partition running ++ this driver. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called hvcs.ko. Additionally, this module ++ will depend on arch specific APIs exported from hvcserver.ko ++ which will also be compiled when this driver is built as a ++ module. ++ ++source "drivers/char/ipmi/Kconfig" ++ ++config DS1620 ++ tristate "NetWinder thermometer support" ++ depends on ARCH_NETWINDER ++ help ++ Say Y here to include support for the thermal management hardware ++ found in the NetWinder. This driver allows the user to control the ++ temperature set points and to read the current temperature. ++ ++ It is also possible to say M here to build it as a module (ds1620) ++ It is recommended to be used on a NetWinder, but it is not a ++ necessity. ++ ++config NWBUTTON ++ tristate "NetWinder Button" ++ depends on ARCH_NETWINDER ++ ---help--- ++ If you say Y here and create a character device node /dev/nwbutton ++ with major and minor numbers 10 and 158 ("man mknod"), then every ++ time the orange button is pressed a number of times, the number of ++ times the button was pressed will be written to that device. ++ ++ This is most useful for applications, as yet unwritten, which ++ perform actions based on how many times the button is pressed in a ++ row. ++ ++ Do not hold the button down for too long, as the driver does not ++ alter the behaviour of the hardware reset circuitry attached to the ++ button; it will still execute a hard reset if the button is held ++ down for longer than approximately five seconds. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called nwbutton. ++ ++ Most people will answer Y to this question and "Reboot Using Button" ++ below to be able to initiate a system shutdown from the button. ++ ++config NWBUTTON_REBOOT ++ bool "Reboot Using Button" ++ depends on NWBUTTON ++ help ++ If you say Y here, then you will be able to initiate a system ++ shutdown and reboot by pressing the orange button a number of times. ++ The number of presses to initiate the shutdown is two by default, ++ but this can be altered by modifying the value of NUM_PRESSES_REBOOT ++ in nwbutton.h and recompiling the driver or, if you compile the ++ driver as a module, you can specify the number of presses at load ++ time with "insmod button reboot_count=". ++ ++config NWFLASH ++ tristate "NetWinder flash support" ++ depends on ARCH_NETWINDER ++ ---help--- ++ If you say Y here and create a character device /dev/flash with ++ major 10 and minor 160 you can manipulate the flash ROM containing ++ the NetWinder firmware. Be careful as accidentally overwriting the ++ flash contents can render your computer unbootable. On no account ++ allow random users access to this device. :-) ++ ++ To compile this driver as a module, choose M here: the ++ module will be called nwflash. ++ ++ If you're not sure, say N. ++ ++source "drivers/char/hw_random/Kconfig" ++ ++config NVRAM ++ tristate "/dev/nvram support" ++ depends on ATARI || X86 || ARM || GENERIC_NVRAM ++ ---help--- ++ If you say Y here and create a character special file /dev/nvram ++ with major number 10 and minor number 144 using mknod ("man mknod"), ++ you get read and write access to the extra bytes of non-volatile ++ memory in the real time clock (RTC), which is contained in every PC ++ and most Ataris. The actual number of bytes varies, depending on the ++ nvram in the system, but is usually 114 (128-14 for the RTC). ++ ++ This memory is conventionally called "CMOS RAM" on PCs and "NVRAM" ++ on Ataris. /dev/nvram may be used to view settings there, or to ++ change them (with some utility). It could also be used to frequently ++ save a few bits of very important data that may not be lost over ++ power-off and for which writing to disk is too insecure. Note ++ however that most NVRAM space in a PC belongs to the BIOS and you ++ should NEVER idly tamper with it. See Ralf Brown's interrupt list ++ for a guide to the use of CMOS bytes by your BIOS. ++ ++ On Atari machines, /dev/nvram is always configured and does not need ++ to be selected. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called nvram. ++ ++config RTC ++ tristate "Enhanced Real Time Clock Support" ++ depends on !PPC && !PARISC && !IA64 && !M68K && !SPARC && !FRV && !ARM && !SUPERH && !S390 ++ ---help--- ++ If you say Y here and create a character special file /dev/rtc with ++ major number 10 and minor number 135 using mknod ("man mknod"), you ++ will get access to the real time clock (or hardware clock) built ++ into your computer. ++ ++ Every PC has such a clock built in. It can be used to generate ++ signals from as low as 1Hz up to 8192Hz, and can also be used ++ as a 24 hour alarm. It reports status information via the file ++ /proc/driver/rtc and its behaviour is set by various ioctls on ++ /dev/rtc. ++ ++ If you run Linux on a multiprocessor machine and said Y to ++ "Symmetric Multi Processing" above, you should say Y here to read ++ and set the RTC in an SMP compatible fashion. ++ ++ If you think you have a use for such a device (such as periodic data ++ sampling), then say Y here, and read ++ for details. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called rtc. ++ ++config JS_RTC ++ tristate "Enhanced Real Time Clock Support" ++ depends on SPARC32 && PCI ++ ---help--- ++ If you say Y here and create a character special file /dev/rtc with ++ major number 10 and minor number 135 using mknod ("man mknod"), you ++ will get access to the real time clock (or hardware clock) built ++ into your computer. ++ ++ Every PC has such a clock built in. It can be used to generate ++ signals from as low as 1Hz up to 8192Hz, and can also be used ++ as a 24 hour alarm. It reports status information via the file ++ /proc/driver/rtc and its behaviour is set by various ioctls on ++ /dev/rtc. ++ ++ If you think you have a use for such a device (such as periodic data ++ sampling), then say Y here, and read ++ for details. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called js-rtc. ++ ++config SGI_DS1286 ++ tristate "SGI DS1286 RTC support" ++ depends on SGI_IP22 ++ help ++ If you say Y here and create a character special file /dev/rtc with ++ major number 10 and minor number 135 using mknod ("man mknod"), you ++ will get access to the real time clock built into your computer. ++ Every SGI has such a clock built in. It reports status information ++ via the file /proc/rtc and its behaviour is set by various ioctls on ++ /dev/rtc. ++ ++config SGI_IP27_RTC ++ bool "SGI M48T35 RTC support" ++ depends on SGI_IP27 ++ help ++ If you say Y here and create a character special file /dev/rtc with ++ major number 10 and minor number 135 using mknod ("man mknod"), you ++ will get access to the real time clock built into your computer. ++ Every SGI has such a clock built in. It reports status information ++ via the file /proc/rtc and its behaviour is set by various ioctls on ++ /dev/rtc. ++ ++config GEN_RTC ++ tristate "Generic /dev/rtc emulation" ++ depends on RTC!=y && !IA64 && !ARM && !M32R && !MIPS && !SPARC && !FRV && !S390 && !SUPERH ++ ---help--- ++ If you say Y here and create a character special file /dev/rtc with ++ major number 10 and minor number 135 using mknod ("man mknod"), you ++ will get access to the real time clock (or hardware clock) built ++ into your computer. ++ ++ It reports status information via the file /proc/driver/rtc and its ++ behaviour is set by various ioctls on /dev/rtc. If you enable the ++ "extended RTC operation" below it will also provide an emulation ++ for RTC_UIE which is required by some programs and may improve ++ precision in some cases. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called genrtc. ++ ++config GEN_RTC_X ++ bool "Extended RTC operation" ++ depends on GEN_RTC ++ help ++ Provides an emulation for RTC_UIE which is required by some programs ++ and may improve precision of the generic RTC support in some cases. ++ ++config EFI_RTC ++ bool "EFI Real Time Clock Services" ++ depends on IA64 ++ ++config DS1302 ++ tristate "DS1302 RTC support" ++ depends on M32R && (PLAT_M32700UT || PLAT_OPSPUT) ++ help ++ If you say Y here and create a character special file /dev/rtc with ++ major number 121 and minor number 0 using mknod ("man mknod"), you ++ will get access to the real time clock (or hardware clock) built ++ into your computer. ++ ++config COBALT_LCD ++ bool "Support for Cobalt LCD" ++ depends on MIPS_COBALT ++ help ++ This option enables support for the LCD display and buttons found ++ on Cobalt systems through a misc device. ++ ++config DTLK ++ tristate "Double Talk PC internal speech card support" ++ depends on ISA ++ help ++ This driver is for the DoubleTalk PC, a speech synthesizer ++ manufactured by RC Systems (). It is also ++ called the `internal DoubleTalk'. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called dtlk. ++ ++config R3964 ++ tristate "Siemens R3964 line discipline" ++ ---help--- ++ This driver allows synchronous communication with devices using the ++ Siemens R3964 packet protocol. Unless you are dealing with special ++ hardware like PLCs, you are unlikely to need this. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called n_r3964. ++ ++ If unsure, say N. ++ ++config APPLICOM ++ tristate "Applicom intelligent fieldbus card support" ++ depends on PCI ++ ---help--- ++ This driver provides the kernel-side support for the intelligent ++ fieldbus cards made by Applicom International. More information ++ about these cards can be found on the WWW at the address ++ , or by email from David Woodhouse ++ . ++ ++ To compile this driver as a module, choose M here: the ++ module will be called applicom. ++ ++ If unsure, say N. ++ ++config SONYPI ++ tristate "Sony Vaio Programmable I/O Control Device support (EXPERIMENTAL)" ++ depends on EXPERIMENTAL && X86 && PCI && INPUT && !64BIT ++ ---help--- ++ This driver enables access to the Sony Programmable I/O Control ++ Device which can be found in many (all ?) Sony Vaio laptops. ++ ++ If you have one of those laptops, read ++ , and say Y or M here. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called sonypi. ++ ++config GPIO_TB0219 ++ tristate "TANBAC TB0219 GPIO support" ++ depends on TANBAC_TB022X ++ select GPIO_VR41XX ++ ++source "drivers/char/pcmcia/Kconfig" ++ ++config MWAVE ++ tristate "ACP Modem (Mwave) support" ++ depends on X86 ++ select SERIAL_8250 ++ ---help--- ++ The ACP modem (Mwave) for Linux is a WinModem. It is composed of a ++ kernel driver and a user level application. Together these components ++ support direct attachment to public switched telephone networks (PSTNs) ++ and support selected world wide countries. ++ ++ This version of the ACP Modem driver supports the IBM Thinkpad 600E, ++ 600, and 770 that include on board ACP modem hardware. ++ ++ The modem also supports the standard communications port interface ++ (ttySx) and is compatible with the Hayes AT Command Set. ++ ++ The user level application needed to use this driver can be found at ++ the IBM Linux Technology Center (LTC) web site: ++ . ++ ++ If you own one of the above IBM Thinkpads which has the Mwave chipset ++ in it, say Y. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called mwave. ++ ++config SCx200_GPIO ++ tristate "NatSemi SCx200 GPIO Support" ++ depends on SCx200 ++ select NSC_GPIO ++ help ++ Give userspace access to the GPIO pins on the National ++ Semiconductor SCx200 processors. ++ ++ If compiled as a module, it will be called scx200_gpio. ++ ++config PC8736x_GPIO ++ tristate "NatSemi PC8736x GPIO Support" ++ depends on X86 ++ default SCx200_GPIO # mostly N ++ select NSC_GPIO # needed for support routines ++ help ++ Give userspace access to the GPIO pins on the National ++ Semiconductor PC-8736x (x=[03456]) SuperIO chip. The chip ++ has multiple functional units, inc several managed by ++ hwmon/pc87360 driver. Tested with PC-87366 ++ ++ If compiled as a module, it will be called pc8736x_gpio. ++ ++config NSC_GPIO ++ tristate "NatSemi Base GPIO Support" ++ depends on X86_32 ++ # selected by SCx200_GPIO and PC8736x_GPIO ++ # what about 2 selectors differing: m != y ++ help ++ Common support used (and needed) by scx200_gpio and ++ pc8736x_gpio drivers. If those drivers are built as ++ modules, this one will be too, named nsc_gpio ++ ++config CS5535_GPIO ++ tristate "AMD CS5535/CS5536 GPIO (Geode Companion Device)" ++ depends on X86_32 ++ help ++ Give userspace access to the GPIO pins on the AMD CS5535 and ++ CS5536 Geode companion devices. ++ ++ If compiled as a module, it will be called cs5535_gpio. ++ ++config GPIO_DEVICE ++ tristate "GPIO device support" ++ depends on GENERIC_GPIO ++ help ++ Say Y to enable Linux GPIO device support. This allows control of ++ GPIO pins using a character device ++ ++config GPIO_VR41XX ++ tristate "NEC VR4100 series General-purpose I/O Unit support" ++ depends on CPU_VR41XX ++ ++config RAW_DRIVER ++ tristate "RAW driver (/dev/raw/rawN)" ++ depends on BLOCK ++ help ++ The raw driver permits block devices to be bound to /dev/raw/rawN. ++ Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O. ++ See the raw(8) manpage for more details. ++ ++ Applications should preferably open the device (eg /dev/hda1) ++ with the O_DIRECT flag. ++ ++config MAX_RAW_DEVS ++ int "Maximum number of RAW devices to support (1-8192)" ++ depends on RAW_DRIVER ++ default "256" ++ help ++ The maximum number of RAW devices that are supported. ++ Default is 256. Increase this number in case you need lots of ++ raw devices. ++ ++config HPET ++ bool "HPET - High Precision Event Timer" if (X86 || IA64) ++ default n ++ depends on ACPI ++ help ++ If you say Y here, you will have a miscdevice named "/dev/hpet/". Each ++ open selects one of the timers supported by the HPET. The timers are ++ non-periodic and/or periodic. ++ ++config HPET_RTC_IRQ ++ bool "HPET Control RTC IRQ" if !HPET_EMULATE_RTC ++ default n ++ depends on HPET ++ help ++ If you say Y here, you will disable RTC_IRQ in drivers/char/rtc.c. It ++ is assumed the platform called hpet_alloc with the RTC IRQ values for ++ the HPET timers. ++ ++config HPET_MMAP ++ bool "Allow mmap of HPET" ++ default y ++ depends on HPET ++ help ++ If you say Y here, user applications will be able to mmap ++ the HPET registers. ++ ++ In some hardware implementations, the page containing HPET ++ registers may also contain other things that shouldn't be ++ exposed to the user. If this applies to your hardware, ++ say N here. ++ ++config HANGCHECK_TIMER ++ tristate "Hangcheck timer" ++ depends on X86 || IA64 || PPC64 || S390 ++ help ++ The hangcheck-timer module detects when the system has gone ++ out to lunch past a certain margin. It can reboot the system ++ or merely print a warning. ++ ++config MMTIMER ++ tristate "MMTIMER Memory mapped RTC for SGI Altix" ++ depends on IA64_GENERIC || IA64_SGI_SN2 ++ default y ++ help ++ The mmtimer device allows direct userspace access to the ++ Altix system timer. ++ ++source "drivers/char/tpm/Kconfig" ++ ++config TELCLOCK ++ tristate "Telecom clock driver for ATCA SBC" ++ depends on EXPERIMENTAL && X86 ++ default n ++ help ++ The telecom clock device is specific to the MPCBL0010 and MPCBL0050 ++ ATCA computers and allows direct userspace access to the ++ configuration of the telecom clock configuration settings. This ++ device is used for hardware synchronization across the ATCA backplane ++ fabric. Upon loading, the driver exports a sysfs directory, ++ /sys/devices/platform/telco_clock, with a number of files for ++ controlling the behavior of this hardware. ++ ++config DEVPORT ++ bool ++ depends on !M68K ++ depends on ISA || PCI ++ default y ++ ++source "drivers/s390/char/Kconfig" ++ ++endmenu ++ +diff -urN linux-2.6.24.7.old/drivers/char/Makefile linux-2.6.24.7/drivers/char/Makefile +--- linux-2.6.24.7.old/drivers/char/Makefile 2009-04-12 18:05:07.000000000 +0200 ++++ linux-2.6.24.7/drivers/char/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -98,6 +98,10 @@ + obj-$(CONFIG_GPIO_TB0219) += tb0219.o + obj-$(CONFIG_TELCLOCK) += tlclk.o + ++obj-$(CONFIG_RTC_PCF8563) += rtc_pcf8563.o ++obj-$(CONFIG_RTC_JZ) += rtc_jz.o ++obj-$(CONFIG_JZCHAR) += jzchar/ ++ + obj-$(CONFIG_MWAVE) += mwave/ + obj-$(CONFIG_AGP) += agp/ + obj-$(CONFIG_DRM) += drm/ +diff -urN linux-2.6.24.7.old/drivers/char/Makefile.orig linux-2.6.24.7/drivers/char/Makefile.orig +--- linux-2.6.24.7.old/drivers/char/Makefile.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/Makefile.orig 2009-04-12 18:01:55.000000000 +0200 +@@ -0,0 +1,138 @@ ++# ++# Makefile for the kernel character device drivers. ++# ++ ++# ++# This file contains the font map for the default (hardware) font ++# ++FONTMAPFILE = cp437.uni ++ ++obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o ++ ++obj-$(CONFIG_LEGACY_PTYS) += pty.o ++obj-$(CONFIG_UNIX98_PTYS) += pty.o ++obj-y += misc.o ++obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o consolemap.o \ ++ consolemap_deftbl.o selection.o keyboard.o ++obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o ++obj-$(CONFIG_AUDIT) += tty_audit.o ++obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o ++obj-$(CONFIG_ESPSERIAL) += esp.o ++obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o ++obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o ++obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o ++obj-$(CONFIG_ROCKETPORT) += rocket.o ++obj-$(CONFIG_SERIAL167) += serial167.o ++obj-$(CONFIG_CYCLADES) += cyclades.o ++obj-$(CONFIG_STALLION) += stallion.o ++obj-$(CONFIG_ISTALLION) += istallion.o ++obj-$(CONFIG_DIGIEPCA) += epca.o ++obj-$(CONFIG_SPECIALIX) += specialix.o ++obj-$(CONFIG_MOXA_INTELLIO) += moxa.o ++obj-$(CONFIG_A2232) += ser_a2232.o generic_serial.o ++obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o ++obj-$(CONFIG_MOXA_SMARTIO) += mxser.o ++obj-$(CONFIG_MOXA_SMARTIO_NEW) += mxser_new.o ++obj-$(CONFIG_COMPUTONE) += ip2/ ++obj-$(CONFIG_RISCOM8) += riscom8.o ++obj-$(CONFIG_ISI) += isicom.o ++obj-$(CONFIG_SYNCLINK) += synclink.o ++obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o ++obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o ++obj-$(CONFIG_N_HDLC) += n_hdlc.o ++obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o ++obj-$(CONFIG_SX) += sx.o generic_serial.o ++obj-$(CONFIG_RIO) += rio/ generic_serial.o ++obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi.o ++obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o ++obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o ++obj-$(CONFIG_HVC_BEAT) += hvc_beat.o ++obj-$(CONFIG_HVC_DRIVER) += hvc_console.o ++obj-$(CONFIG_HVC_XEN) += hvc_xen.o ++obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o ++obj-$(CONFIG_RAW_DRIVER) += raw.o ++obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o ++obj-$(CONFIG_MSPEC) += mspec.o ++obj-$(CONFIG_MMTIMER) += mmtimer.o ++obj-$(CONFIG_VIOCONS) += viocons.o ++obj-$(CONFIG_VIOTAPE) += viotape.o ++obj-$(CONFIG_HVCS) += hvcs.o ++obj-$(CONFIG_SGI_MBCS) += mbcs.o ++obj-$(CONFIG_BRIQ_PANEL) += briq_panel.o ++ ++obj-$(CONFIG_PRINTER) += lp.o ++obj-$(CONFIG_TIPAR) += tipar.o ++ ++obj-$(CONFIG_APM_EMULATION) += apm-emulation.o ++ ++obj-$(CONFIG_DTLK) += dtlk.o ++obj-$(CONFIG_R3964) += n_r3964.o ++obj-$(CONFIG_APPLICOM) += applicom.o ++obj-$(CONFIG_SONYPI) += sonypi.o ++obj-$(CONFIG_RTC) += rtc.o ++obj-$(CONFIG_HPET) += hpet.o ++obj-$(CONFIG_GEN_RTC) += genrtc.o ++obj-$(CONFIG_EFI_RTC) += efirtc.o ++obj-$(CONFIG_SGI_DS1286) += ds1286.o ++obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o ++obj-$(CONFIG_DS1302) += ds1302.o ++ifeq ($(CONFIG_GENERIC_NVRAM),y) ++ obj-$(CONFIG_NVRAM) += generic_nvram.o ++else ++ obj-$(CONFIG_NVRAM) += nvram.o ++endif ++obj-$(CONFIG_TOSHIBA) += toshiba.o ++obj-$(CONFIG_I8K) += i8k.o ++obj-$(CONFIG_DS1620) += ds1620.o ++obj-$(CONFIG_HW_RANDOM) += hw_random/ ++obj-$(CONFIG_COBALT_LCD) += lcd.o ++obj-$(CONFIG_PPDEV) += ppdev.o ++obj-$(CONFIG_NWBUTTON) += nwbutton.o ++obj-$(CONFIG_NWFLASH) += nwflash.o ++obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o ++obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o ++obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o ++obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio.o ++obj-$(CONFIG_GPIO_DEVICE) += gpio_dev.o ++obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o ++obj-$(CONFIG_GPIO_TB0219) += tb0219.o ++obj-$(CONFIG_TELCLOCK) += tlclk.o ++ ++obj-$(CONFIG_MWAVE) += mwave/ ++obj-$(CONFIG_AGP) += agp/ ++obj-$(CONFIG_DRM) += drm/ ++obj-$(CONFIG_PCMCIA) += pcmcia/ ++obj-$(CONFIG_IPMI_HANDLER) += ipmi/ ++ ++obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o ++obj-$(CONFIG_TCG_TPM) += tpm/ ++ ++obj-$(CONFIG_PS3_FLASH) += ps3flash.o ++ ++obj-$(CONFIG_JS_RTC) += js-rtc.o ++js-rtc-y = rtc.o ++ ++# Files generated that shall be removed upon make clean ++clean-files := consolemap_deftbl.c defkeymap.c ++ ++quiet_cmd_conmk = CONMK $@ ++ cmd_conmk = scripts/conmakehash $< > $@ ++ ++$(obj)/consolemap_deftbl.c: $(src)/$(FONTMAPFILE) ++ $(call cmd,conmk) ++ ++$(obj)/defkeymap.o: $(obj)/defkeymap.c ++ ++# Uncomment if you're changing the keymap and have an appropriate ++# loadkeys version for the map. By default, we'll use the shipped ++# versions. ++# GENERATE_KEYMAP := 1 ++ ++ifdef GENERATE_KEYMAP ++ ++$(obj)/defkeymap.c: $(obj)/%.c: $(src)/%.map ++ loadkeys --mktable $< > $@.tmp ++ sed -e 's/^static *//' $@.tmp > $@ ++ rm $@.tmp ++ ++endif +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/Kconfig linux-2.6.24.7/drivers/char/jzchar/Kconfig +--- linux-2.6.24.7.old/drivers/char/jzchar/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,70 @@ ++# ++# JzSOC char devices configuration ++# ++ ++menu "JZSOC char device support" ++ depends on SOC_JZ4740 || SOC_JZ4730 || SOC_JZ4750 || SOC_JZ4750D ++ ++config JZCHAR ++ tristate 'JzSOC char device support' ++ ++config JZ_CAMERA_SENSOR ++ bool ++ ++config JZ_CIM ++ tristate 'JzSOC Camera Interface Module (CIM) support' ++ depends on JZCHAR ++ select JZ_CAMERA_SENSOR ++ ++config JZ_TPANEL_ATA2508 ++ tristate 'JzSOC MPEG4 TOUCH PANEL ATA2508 support' ++ depends on JZCHAR ++ ++config JZ_TPANEL ++ tristate 'JzSOC touchpanel driver support' ++ depends on JZCHAR ++# select JZ_SADC if SOC_JZ4740 ++# select JZ_TPANEL_AK4182 if SOC_JZ4730 ++ ++choice ++ prompt "Touch Panel ADC type" ++ depends on JZ_TPANEL ++ default JZ_SADC if SOC_JZ4740 || SOC_JZ4750 || SOC_JZ4750D ++ default JZ_TPANEL_AK4182 if SOC_JZ4730 ++ ++config JZ_SADC ++ bool 'Select the JZ47XX internal SADC' ++ ++config JZ_TPANEL_AK4182 ++ bool 'Select the AK4182 codec' ++ ++config JZ_TPANEL_UCB1400 ++ bool 'Select the UCB1400 codec' ++ ++config JZ_TPANEL_WM9712 ++ bool 'Select the WM9712 codec' ++ ++endchoice ++ ++config JZ_UDC_HOTPLUG ++ tristate 'JZ UDC hotplug driver support' ++ depends on JZCHAR ++ ++config JZ_POWEROFF ++ tristate 'JZ board poweroff support' ++ depends on JZCHAR ++ ++config JZ_OW ++ tristate 'JZ One-wire bus support' ++ depends on JZCHAR ++ ++config JZ_TCSM ++ tristate 'JZ TCSM support' ++ depends on JZCHAR ++ ++config JZ_TSSI ++ tristate 'JZ MPEG2-TS interface support' ++ depends on JZCHAR && (SOC_JZ4750 || SOC_JZ4750D) ++ ++endmenu ++ +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/Makefile linux-2.6.24.7/drivers/char/jzchar/Makefile +--- linux-2.6.24.7.old/drivers/char/jzchar/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,24 @@ ++# ++# Makefile for jzchar ++# ++obj-$(CONFIG_JZCHAR) += jzchars.o ++ ++obj-$(CONFIG_JZ_SCC) += scc.o ++obj-$(CONFIG_JZ_CIM) += cim.o ++obj-$(CONFIG_JZ_TPANEL_ATA2508) += ata2508.o ++obj-$(CONFIG_JZ_CAMERA_SENSOR) += sensor.o ++obj-$(CONFIG_JZ_I2C_EEPROM) += eeprom.o ++obj-$(CONFIG_JZ_EJTAG) += ejtag.o ++obj-$(CONFIG_JZ_POWEROFF) += poweroff.o ++ ++obj-$(CONFIG_JZ_TPANEL) += jz_ts.o ++obj-$(CONFIG_JZ_TPANEL_UCB1400) += ucb1400.o ++obj-$(CONFIG_JZ_TPANEL_WM9712) += wm9712.o ++obj-$(CONFIG_JZ_TPANEL_AK4182) += ak4182.o ++obj-$(CONFIG_JZ_SADC) += sadc.o ++ ++obj-$(CONFIG_JZ_SMART_LCD) += slcd.o ++obj-$(CONFIG_JZ_UDC_HOTPLUG) += udc_hotplug.o ++obj-$(CONFIG_JZ_OW) += jz_ow.o ++obj-$(CONFIG_JZ_TCSM) += tcsm.o ++obj-$(CONFIG_JZ_TSSI) += jz_tssi.o +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/ak4182.c linux-2.6.24.7/drivers/char/jzchar/ak4182.c +--- linux-2.6.24.7.old/drivers/char/jzchar/ak4182.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/ak4182.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,657 @@ ++/* ++ * ak4182.c using national microwire protocol ++ * ++ * Touch screen driver interface to the AK4182A . ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "jz_ts.h" ++#include "ak4182.h" ++ ++#define TS_PIN GPIO_TS_PENIRQ ++#define TS_IRQ (IRQ_GPIO_0 + TS_PIN) ++ ++static int samples = 5; ++static int first_time = 0; ++static unsigned long last_x, last_y, last_p; ++ ++static int adcsync = 0; ++ ++static struct ak4182 *ak; ++ ++extern unsigned int (*codec_read_battery)(void); ++ ++/*------------------JzSoc SSI configure----------------*/ ++static void ak4182_ssi_reset(void) ++{ ++ REG_SSI_CR0 = 0x0000; ++ REG_SSI_CR1 = 0x00007960; ++ REG_SSI_SR = 0x00000098; ++ REG_SSI_ITR = 0x0000; ++ REG_SSI_ICR = 0x00; ++ REG_SSI_GR = 0x0000; ++ ++ __ssi_disable(); ++ __ssi_flush_fifo(); ++ __ssi_clear_errors(); ++ __ssi_select_ce(); ++} ++ ++static void ak4182_ssi_enable(void) ++{ ++ __ssi_enable(); ++} ++ ++#ifdef CONFIG_PM ++static void ak4182_ssi_disable(void) ++{ ++ __ssi_disable(); ++} ++#endif ++ ++static void ak4182_ssi_set_trans_mode_format(void) ++{ ++ __ssi_microwire_format(); ++ __ssi_set_msb(); ++ __ssi_set_microwire_command_length(8); ++ __ssi_set_frame_length(12); ++} ++ ++static void ak4182_ssi_set_clk_div_ratio(int dev_clk, int ssi_clk) ++{ ++ __ssi_set_clk(dev_clk, ssi_clk); ++} ++ ++static void ak4182_ssi_set_normal_mode(void) ++{ ++ __ssi_normal_mode(); ++} ++ ++static void ak4182_ssi_set_IRQ(void) ++{ ++ __ssi_disable_tx_intr(); ++ __ssi_disable_rx_intr(); ++} ++ ++/*------------------ AK4182 routines ------------------*/ ++static inline void ak4182_reg_write(unsigned short val) ++{ ++ __ssi_transmit_data(val); ++} ++ ++static inline unsigned int ak4182_reg_read(void) ++{ ++ unsigned int val; ++ val = __ssi_receive_data(); ++ return val; ++} ++ ++static unsigned int ak4182_adc_read(int cmd_code, int sync) ++{ ++ unsigned int val, timeout = 10000; ++ unsigned int status,valid1,valid2,dataentry; ++ ++ ak4182_reg_write(cmd_code); ++ udelay(2);//wait 2 D_CLK ++ for (;;) { ++ status =0; ++ status = REG_SSI_SR; ++ valid1 = (status>>7) & 1; ++ valid2 = (status>>6) & 1; ++ if( valid1==1 && valid2==0 )//SSI transfer is finished ++ { ++ //Receive FIFO data entry number ++ dataentry = val = 0; ++ dataentry = (status>>8) & 0x1F; ++ if( dataentry > 5 ) ++ { ++ printk("R-FIFO entry=%d,SSI transfer is wrong!\n",dataentry); ++ while(dataentry > 0) ++ { ++ ak4182_reg_read(); ++ dataentry--; ++ } ++ return 0; ++ } ++ while(dataentry > 0) ++ { ++ val = ak4182_reg_read(); ++ dataentry--; ++ } ++ return val; ++ } ++ ++ if (--timeout == 0) ++ break; ++ udelay(1); ++ } ++ return 0; ++} ++ ++ ++//enable pen down IRQ ++static void ak4182_enable_irq(void) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ak->lock, flags); ++ __gpio_unmask_irq(TS_PIN); ++ spin_unlock_irqrestore(&ak->lock, flags); ++} ++ ++//disable pen down IRQ ++static void ak4182_disable_irq(void) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ak->lock, flags); ++ __gpio_mask_irq(TS_PIN); ++// spin_unlock_irqrestore(&ucb->lock, flags); ++ spin_unlock_irqrestore(&ak->lock, flags); ++} ++/* ++ * Switch to X position mode and measure Y plate. We switch the plate ++ * configuration in pressure mode, then switch to position mode. This ++ * gives a faster response time. Even so, we need to wait about 55us ++ * for things to stabilise. ++ */ ++static inline unsigned int ak4182_ts_read_xpos(void) ++{ ++ return ak4182_adc_read(0xD0, adcsync);//X-axis,0xD0 for 12bit,0xD8 for 8bit ++} ++ ++ ++/* ++ * Switch to pressure mode, and read pressure. We don't need to wait ++ * here, since both plates are being driven. ++ */ ++static inline unsigned int ak4182_ts_read_pressure(void) ++{ ++ unsigned int z1,z2,xpos,pressureval=0;//300 Om ++ //Z1 pressure ++ z1 = ak4182_adc_read(0xB0, adcsync);//0xB0 for 12bit,0xB8 for 8bit ++ if(z1>0) ++ { ++ //Z2 pressure ++ z2 = ak4182_adc_read(0xC0, adcsync);//0xC0 for 12bit,0xC8 for 8bit ++ if(z2>z1) ++ { ++ xpos = ak4182_ts_read_xpos(); ++ pressureval = (300*xpos*(z2-z1))/(4096*z1); ++ } ++ } ++ ++ return pressureval; ++} ++ ++ ++/* ++ * Switch to Y position mode and measure X plate. We switch the plate ++ * configuration in pressure mode, then switch to position mode. This ++ * gives a faster response time. Even so, we need to wait about 55us ++ * for things to stabilise. ++ */ ++static inline unsigned int ak4182_ts_read_ypos(void) ++{ ++ return ak4182_adc_read(0x90, adcsync);//Y-axis,0x90 for 12bit,0x98 for 8bit ++} ++ ++/*------------------------------------------------------------ ++ * Read the battery voltage ++ */ ++ ++unsigned int ak4182_read_battery(void) ++{ ++ unsigned int v; ++ int bat_val[5]; ++ int total = 0, max_bat, min_bat; ++ ++ v = ak4182_adc_read(0xA7, adcsync); ++ v = ak4182_adc_read(0xA7, adcsync); ++ for(v = 0;v <= 4;v++) ++ bat_val[v] = ak4182_adc_read(0xA7, adcsync); ++ ++ ak4182_adc_read(0xA4, adcsync); ++ max_bat = min_bat = bat_val[0]; ++ for(v = 0;v <= 4;v++) { ++ total += bat_val[v]; ++ if(bat_val[v] > max_bat) ++ max_bat = bat_val[v]; ++ if(bat_val[v] < min_bat) ++ min_bat = bat_val[v]; ++ } ++ total = total - max_bat - min_bat; ++ v = total / 3; ++ return v; ++} ++ ++/*------------------ Calibrate samples -------------------*/ ++ ++#define DIFF(a,b) ((a>b)?(a-b):(b-a)) ++ ++static int calibrate_samples(void *xbuf, void *ybuf, void *pbuf, int count) ++{ ++ unsigned long *xp = (unsigned long *)xbuf; ++ unsigned long *yp = (unsigned long *)ybuf; ++ unsigned long *pp = (unsigned long *)pbuf; ++ unsigned long x_cal = 0, y_cal = 0, p_cal = 0, tmp; ++ int ignored, i, j; ++ int valid = 0; ++ ++ /* throw away the max cases */ ++ tmp = xp[0]; ++ ignored = 0; ++ for (i = 1; i < count; i++) { ++ if (xp[i] > tmp) { ++ tmp = xp[i]; ++ ignored = i; ++ } ++ }//find the max val ++ j = 0; ++ for (i = 0; i < count; i++) { ++ if (i == ignored) ++ continue; ++ xp[j++] = xp[i]; ++ }//shift val and delete the max val ++ ++ tmp = yp[0]; ++ ignored = 0; ++ for (i = 1; i < count; i++) { ++ if (yp[i] > tmp) { ++ tmp = yp[i]; ++ ignored = i; ++ } ++ } ++ j = 0; ++ for (i = 0; i < count; i++) { ++ if (i == ignored) ++ continue; ++ yp[j++] = yp[i]; ++ } ++ ++ tmp = pp[0]; ++ ignored = 0; ++ for (i = 1; i < count; i++) { ++ if (pp[i] > tmp) { ++ tmp = pp[i]; ++ ignored = i; ++ } ++ } ++ j = 0; ++ for (i = 0; i < count; i++) { ++ if (i == ignored) ++ continue; ++ pp[j++] = pp[i]; ++ } ++ ++ /* throw away the min cases */ ++ ++ count -= 1; // decrement by 1 ++ ++ tmp = xp[0]; ++ ignored = 0; ++ for (i = 1; i < count; i++) { ++ if (xp[i] < tmp) { ++ tmp = xp[i]; ++ ignored = i; ++ } ++ } ++ j = 0; ++ for (i = 0; i < count; i++) { ++ if (i == ignored) ++ continue; ++ xp[j++] = xp[i]; ++ } ++ ++ tmp = yp[0]; ++ ignored = 0; ++ for (i = 1; i < count; i++) { ++ if (yp[i] < tmp) { ++ tmp = yp[i]; ++ ignored = i; ++ } ++ } ++ j = 0; ++ for (i = 0; i < count; i++) { ++ if (i == ignored) ++ continue; ++ yp[j++] = yp[i]; ++ } ++ ++ tmp = pp[0]; ++ ignored = 0; ++ for (i = 1; i < count; i++) { ++ if (pp[i] < tmp) { ++ tmp = pp[i]; ++ ignored = i; ++ } ++ } ++ j = 0; ++ for (i = 0; i < count; i++) { ++ if (i == ignored) ++ continue; ++ pp[j++] = pp[i]; ++ } ++ ++ count -= 1; // decrement by 1 ++ ++ /* calculate the average of the rest */ ++ for (i = 0; i < count; i++) { ++ x_cal += xp[i]; ++ y_cal += yp[i]; ++ p_cal += pp[i]; ++ } ++ x_cal /= count; ++ y_cal /= count; ++ p_cal /= count; ++ ++ if (first_time) { ++ first_time = 0; ++ last_x = x_cal; ++ last_y = y_cal; ++ last_p = p_cal; ++ valid = 1; ++ } ++ else { ++ if ((DIFF(x_cal, last_x) > 100) || ++ (DIFF(y_cal, last_y) > 100)) ++ valid = 0; ++ else ++ valid = 1; ++ } ++ ++ //printk("x_cal=%d y_cal=%d p_cal=%d valid=%d\n", x_cal, y_cal, p_cal, valid); ++ ++ if (valid) { ++ *xp = last_x = x_cal; ++ *yp = last_y = y_cal; ++ *pp = last_p = p_cal; ++ } ++ ++ return valid; ++} ++ ++ ++#define TSMAXX 945 ++#define TSMAXY 830 ++#define TSMINX 90 ++#define TSMINY 105 ++ ++#define SCREEN_X 480 ++#define SCREEN_Y 272 ++ ++static unsigned long transform_to_screen_x(struct jz_ts_t *ts, unsigned long x ) ++{ ++ ++ if (ts->minx) ++ { ++ if (x < ts->minx) x = ts->minx; ++ if (x > ts->maxx) x = ts->maxx; ++ ++ return (x - ts->minx) * SCREEN_X / (ts->maxx - ts->minx); ++ } ++ else ++ { ++ if (x < TSMINX) x = TSMINX; ++ if (x > TSMAXX) x = TSMAXX; ++ ++ return (x - TSMINX) * SCREEN_X / (TSMAXX - TSMINX); ++ } ++} ++ ++static unsigned long transform_to_screen_y(struct jz_ts_t *ts, unsigned long y) ++{ ++ if (ts->miny) ++ { ++ if (y < ts->miny) y = ts->miny; ++ if (y > ts->maxy) y = ts->maxy; ++ ++ return (y - ts->miny) * SCREEN_Y / (ts->maxy - ts->miny); ++ } ++ else ++ { ++ if (y < TSMINY) y = TSMINY; ++ if (y > TSMAXY) y = TSMAXY; ++ ++ return (y - TSMINY) * SCREEN_Y / (TSMAXY - TSMINY); ++ } ++} ++ ++/*------------------ Common routines -------------------*/ ++ ++void ts_enable_irq(void) ++{ ++ /* interrupt mode */ ++ ak4182_enable_irq(); ++ enable_irq(TS_IRQ); ++} ++ ++void ts_disable_irq(void) ++{ ++ ak4182_disable_irq(); ++ disable_irq(TS_IRQ); ++} ++ ++int ts_request_irq(u32 *irq, ++ irqreturn_t (*handler)(int, void *), ++ const char *devname, ++ void *dev_id) ++{ ++ int retval; ++ ++ /* return the irq number */ ++ *irq = TS_IRQ; ++ /* initializate ssi for AK4182 */ ++ ak4182_ssi_reset(); ++ ak4182_ssi_set_trans_mode_format(); ++ ak4182_ssi_set_normal_mode(); ++ ak4182_ssi_set_clk_div_ratio(JZ_EXTAL, 200*1000);//DCLK is 1.5M Hz max ++ ak4182_ssi_set_IRQ(); ++ ++ ak4182_enable_irq(); ++ ++ /* enable gpio irq */ ++ __gpio_as_irq_fall_edge(TS_PIN); ++ ++ /* register irq handler */ ++ retval = request_irq(TS_IRQ, handler, IRQF_DISABLED, devname, dev_id); ++ ak4182_ssi_enable(); ++ udelay(10); ++ return retval; ++} ++ ++void ts_free_irq(struct jz_ts_t *ts) ++{ ++ free_irq(ts->pendown_irq, ts); ++ //Close SSI mode ++ ak4182_ssi_reset(); ++} ++ ++void ts_irq_callback(void) ++{ ++ /* clear interrupt status */ ++ __gpio_ack_irq(TS_PIN); ++ first_time = 1; // first time to acquire sample ++} ++ ++int PenIsDown(void) ++{ ++ unsigned int p; ++ p = ak4182_ts_read_pressure(); ++ return (p > 100) ? 1 : 0; ++} ++ ++/* ++ * Acquire Raw pen coodinate data and compute touch screen ++ * pressure resistance. Hold spinlock when calling. ++ */ ++int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event) ++{ ++ unsigned int x_raw[8], y_raw[8], p_raw[8]; ++ int valid, i; ++ ++ for (i = 0; i < samples; i++) { ++ x_raw[i] = ak4182_ts_read_xpos(); ++ } ++ for (i = 0; i < samples; i++) { ++ y_raw[i] = ak4182_ts_read_ypos(); ++ } ++ for (i = 0; i < samples; i++) { ++ p_raw[i] = ak4182_ts_read_pressure(); ++ } ++ ++ valid = calibrate_samples(x_raw, y_raw, p_raw, samples); ++ ++ if (valid) { ++ unsigned int x_scr, y_scr; ++ ++ if(ts->filter) { ++ x_scr = transform_to_screen_x(ts, x_raw[0]); ++ y_scr = transform_to_screen_y(ts, y_raw[0]); ++ ++ if (ts->prints) ++ printk("filter:x_raw:%d,y_raw:%d,x_tran:%d,y_tran:%d\n", x_raw[0], y_raw[0], x_scr, y_scr); ++ } ++ else { ++ x_scr = x_raw[0]; ++ y_scr = y_raw[0]; ++ ++ if (ts->prints) ++ printk("no filter:x_raw=%d y_raw=%d \n", x_raw[0], y_raw[0]); ++ } ++ ++ event->x = x_scr; ++ event->y = y_scr; ++ event->pressure = (u16)p_raw[0]; ++ event->status = PENDOWN; ++ return 1; ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++/* ++ * Suspend the Touch pad. ++ */ ++static int ak4182_suspend(struct ak4182 *ak , int state) ++{ ++ ak4182_ssi_disable(); ++ ++ return 0; ++} ++ ++/* ++ * Resume the Touch panel. ++ */ ++static int ak4182_resume(struct ak4182 *ak) ++{ ++ /* initializate ssi for AK4182 */ ++ ak4182_ssi_reset(); ++ ak4182_ssi_set_trans_mode_format(); ++ ak4182_ssi_set_normal_mode(); ++ ak4182_ssi_set_clk_div_ratio(JZ_EXTAL, 200*1000);//DCLK is 1.5M Hz max ++ ak4182_ssi_set_IRQ(); ++ ++ ak4182_enable_irq(); ++ ++ ak4182_ssi_enable(); ++ ++ return 0; ++} ++ ++static int ak4182_pm_callback(struct pm_dev *pm_dev, pm_request_t rqst, void *data) ++{ ++ int ret; ++ struct ak4182 *akinfo = pm_dev->data; ++ ++ if (!akinfo) ++ return -EINVAL; ++ ++ ++ switch (rqst) { ++ case PM_SUSPEND: ++ ret = ak4182_suspend(akinfo, (int)data); ++ break; ++ ++ case PM_RESUME: ++ ret = ak4182_resume(akinfo); ++ break; ++ ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++#endif /* CONFIG_PM */ ++ ++ ++/* ++ * Module init and exit ++ */ ++ ++int __init ak4182_init(void) ++{ ++ ak = kmalloc(sizeof(struct ak4182), GFP_KERNEL); ++ if (!ak) return -ENOMEM; ++ ++ memset(ak, 0, sizeof(struct ak4182)); ++ ++ codec_read_battery = ak4182_read_battery; ++ ++ spin_lock_init(&ak->lock); ++ sema_init(&ak->adc_sem, 1); ++ ++ //initialize AK4182 register ++ __gpio_clear_pin(73); ++ __gpio_as_output(73); ++ mdelay(2); ++ __gpio_set_pin(73); ++ __gpio_as_ssi(); ++ ++ ak4182_read_battery(); ++ ++#ifdef CONFIG_PM ++ ak->pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ak4182_pm_callback); ++ if (ak->pmdev) ++ { ++ ak->pmdev->data = ak; ++ } ++#endif ++ ++ printk("AK4182 touch screen driver initialized\n"); ++ ++ return 0; ++} ++ ++void ak4182_cleanup(void) ++{ ++} ++ ++module_init(ak4182_init); ++module_exit(ak4182_cleanup); ++ +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/ak4182.h linux-2.6.24.7/drivers/char/jzchar/ak4182.h +--- linux-2.6.24.7.old/drivers/char/jzchar/ak4182.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/ak4182.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,16 @@ ++#ifndef __AK4182_H__ ++#define __AK4182_H__ ++ ++/* Device data structure */ ++ ++struct ak4182 { ++ spinlock_t lock; ++ struct pm_dev *pmdev; ++ struct semaphore adc_sem; ++ u16 adc_cr; ++ u16 irq_fal_enbl; ++ u16 irq_ris_enbl; ++ int irq_enabled; ++}; ++ ++#endif /* __AK4182_H__ */ +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/ata2508.c linux-2.6.24.7/drivers/char/jzchar/ata2508.c +--- linux-2.6.24.7.old/drivers/char/jzchar/ata2508.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/ata2508.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,227 @@ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define MP4_KEY_RST (32*3+3) ++#define MP4_KEY_TINT (32*3+2) ++#define MP4_KEY_SCL (32*3+1) ++#define MP4_KEY_SDA (32*3+0) ++#define MP4_TINT_IRQ (IRQ_GPIO_0 + MP4_KEY_TINT) ++ ++#define ADDR_WARM_RESET 0xFF ++#define ATA2508_SENSOR_MASK 0x1F ++ ++const unsigned char init_data_burst[] = {//Address:0x0D-0x3E ++ 0x04, // BETA ++ 0x27, // AIC_WAIT ++ //0x32, // REF_DELAY ++ 0x16, // REF_DELAY ++ 0x02, // HYSTERESIS01 ++ 0x02, // HYSTERESIS1 ++ 0x02, // HYSTERESIS2 ++ 0x02, // HYSTERESIS3 ++ 0x02, // HYSTERESIS4 ++ 0x02, // HYSTERESIS51 ++ 0x02, // HYSTERESIS61 ++ 0x02, // HYSTERESIS7 ++ 0x02, // HYSTERESIS8 ++ 0x02, // HYSTERESIS9 ++ 0x02, // HYSTERESIS10 ++ 0x02, // HYSTERESIS11 ++ 0x64, // STRENGTH_THRESHOLD0 ++ 0x64, // STRENGTH_THRESHOLD1 ++ 0x64, // STRENGTH_THRESHOLD2 ++ 0x64, // STRENGTH_THRESHOLD3 ++ 0x64, // STRENGTH_THRESHOLD4 ++ 0x64, // STRENGTH_THRESHOLD5 ++ 0x64, // STRENGTH_THRESHOLD6 ++ 0x64, // STRENGTH_THRESHOLD7 ++ 0x64, // STRENGTH_THRESHOLD8 ++ 0x64, // STRENGTH_THRESHOLD9 ++ 0x64, // STRENGTH_THRESHOLD10 ++ 0x64, // STRENGTH_THRESHOLD11 ++ 0x0f, // Sampling Interval ++ 0xC8, // INTEGRATION TIME ++ 0x0f, // IDLE TIME ++ 0x00, // SIF_SETUP(RESERVED) ++ 0x01, // MODE ++ 0x00, // GPIO_REG_L ++ 0x00, // GPIO_REG_H ++ 0x00, // GPIO_CONFIGURATION_L ++ 0x00, // GPIO_CONFIGURATION_H ++ 0x00, // GPIO_DIR_L ++ 0x00, // GPIO_DIR_H ++ 0x0c, // CONTROL ++ 0x38, // INT_MASK ++ 0x00, // INT_CLEAR ++ 0xFF, // INT_edge ++ 0x02, // CONTROL_2 ++ 0xAF, // BEEP_TIME ++ 0x7F, // BEEP_FREQUENCY ++ 0x30, // CALIBRATION INTERVAL ++ 0x00, // EINT_ENABLE ++ 0x00, // EINT_POL ++ 0x00, // FILTER_PERIOD ++ 0x00, // FILTER_THRESHOLD ++}; ++const unsigned char init_data_alpha[] = {//Address:0x00-0x0C ++ 0x02, // APIS ++ 0x08, // ALPHA0 ++ 0x08, // ALPHA1 ++ 0x08, // ALPHA2 ++ 0x08, // ALPHA3 ++ 0x08, // ALPHA4 ++ 0x28, // ALPHA5 ++ 0x28, // ALPHA6 ++ 0x28, // ALPHA7 ++ 0x28, // ALPHA8 ++ 0x28, // ALPHA9 ++ 0x28, // ALPHA10 ++ 0x28, // ALPHA11 ++}; ++static unsigned int i2c_addr = 0x58; ++static unsigned int i2c_clk = 100000; ++ ++static void write_reg(u8 reg, u8 val) ++{ ++ int ret; ++ i2c_open(); ++ i2c_setclk(i2c_clk); ++ ret = i2c_write(i2c_addr, &val, reg, 1); ++ i2c_close(); ++} ++ ++static u8 read_reg(u8 reg) ++{ ++ u8 val; ++ ++ i2c_open(); ++ i2c_setclk(i2c_clk); ++ i2c_read(i2c_addr, &val, reg, 1); ++ i2c_close(); ++ return val; ++} ++ ++/* ++ * Interrupt handler ++ */ ++static irqreturn_t mp4_tint_irq(int irq, void *dev_id) ++{ ++ int key_num = 0; ++ u8 value0, value1; ++ ++ __gpio_ack_irq(MP4_KEY_TINT); ++ value0 = read_reg(0x75); ++ value1 = read_reg(0x76); ++ value0 &= ATA2508_SENSOR_MASK; ++ if (value0 == 0) { ++ printk("\nRelease key!\n"); ++ return IRQ_HANDLED; ++ } ++ while(value0 >> 1){ ++ value0 >>= 1; ++ key_num++; ++ } ++ ++ printk("\nPress key %d!\n", key_num); ++ return IRQ_HANDLED; ++} ++ ++static int __init init_ata2508(void) ++{ ++ int i; ++ unsigned char data1; ++ int retval; ++ ++ __gpio_as_output(MP4_KEY_RST); ++ __gpio_set_pin(MP4_KEY_RST); ++ mdelay(100); ++ __gpio_clear_pin(MP4_KEY_RST); ++ mdelay(800); ++ __gpio_set_pin(MP4_KEY_RST); ++ __gpio_mask_irq(MP4_KEY_TINT); ++ ++ /*write registers*/ ++ for(i=0; i<13; i++) ++ { ++ data1 = init_data_alpha[i]; ++ write_reg(i, data1); ++ } ++ ++ for(i=13; i<63; i++) ++ { ++ data1 = init_data_burst[i-13]; ++ write_reg(i, data1); ++ } ++#if 0 ++ for (i = 0; i < 63; i++) ++ { ++ data1 = read_reg(i); ++ printk("REG0x%02x = 0x%02x\n", i, data1); ++ } ++#endif ++ ++ /* wait for 1 ms*/ ++ mdelay(1); ++#if 0 ++ while(1) ++ { ++ data1 = read_reg(0x68); ++ printk("REG0x68 = %d\n", data1); ++ data1 = read_reg(0x75); ++ printk("REG0x75 = 0x%02x\n", data1); ++ data1 = read_reg(0x76); ++ printk("REG0x76 = 0x%02x\n", data1); ++ mdelay(2000); ++ } ++#endif ++ data1 = read_reg(0x68); ++ printk("REG0x68 = %d\n", data1); ++ ++ /* to activate all the new settings, give a WARM RESET.*/ ++ write_reg(ADDR_WARM_RESET, 0x00); //ADDR_WARM_RESET=0xFF ++ ++ //printk("REG0x68 = %d\n", data1); ++ ++ /* wait for 1 ~ 10 ms.*/ ++ mdelay(10); ++ data1 = read_reg(0x68); ++ ++ /* Enable INT that connected to ATA2508's TINT.*/ ++ __gpio_as_irq_rise_edge(MP4_KEY_TINT); ++ ++ retval = request_irq(MP4_TINT_IRQ, mp4_tint_irq, ++ IRQF_DISABLED, "mp4_key_tint", NULL); ++ if (retval) { ++ printk("Could not get mp4 key irq %d\n", MP4_TINT_IRQ); ++ return retval; ++ } ++ ++ printk("MP4 touch panel register!\n"); ++ ++ return 0; ++} ++ ++static void __exit exit_ata2508(void) ++{ ++ free_irq(MP4_TINT_IRQ, NULL); ++} ++ ++module_init(init_ata2508); ++module_exit(exit_ata2508); +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/cim.c linux-2.6.24.7/drivers/char/jzchar/cim.c +--- linux-2.6.24.7.old/drivers/char/jzchar/cim.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/cim.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,366 @@ ++/* ++ * linux/drivers/char/jzchar/cim.c ++ * ++ * Camera Interface Module (CIM) driver for JzSOC ++ * This driver is independent of the camera sensor ++ * ++ * Copyright (C) 2005 JunZheng semiconductor ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "jzchars.h" ++ ++#define CIM_NAME "cim" ++ ++MODULE_AUTHOR("Jianli Wei"); ++MODULE_DESCRIPTION("JzSOC Camera Interface Module driver"); ++MODULE_LICENSE("GPL"); ++ ++/* ++ * Define the Max Image Size ++ */ ++#define MAX_IMAGE_WIDTH 640 ++#define MAX_IMAGE_HEIGHT 480 ++#define MAX_IMAGE_BPP 16 ++#define MAX_FRAME_SIZE (MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT * MAX_IMAGE_BPP / 8) ++ ++typedef struct ++{ ++ u32 width; ++ u32 height; ++ u32 bpp; ++} img_param_t; ++ ++typedef struct ++{ ++ u32 cfg; ++ u32 ctrl; ++ u32 mclk; ++} cim_config_t; ++ ++/* ++ * IOCTL_XXX commands ++ */ ++#define IOCTL_SET_IMG_PARAM 0 // arg type: img_param_t * ++#define IOCTL_CIM_CONFIG 1 // arg type: cim_config_t * ++ ++/* Actual image size, must less than max values */ ++static int img_width = MAX_IMAGE_WIDTH, img_height = MAX_IMAGE_HEIGHT, img_bpp = MAX_IMAGE_BPP; ++ ++/* ++ * CIM DMA descriptor ++ */ ++struct cim_desc { ++ u32 nextdesc; /* Physical address of next desc */ ++ u32 framebuf; /* Physical address of frame buffer */ ++ u32 frameid; /* Frame ID */ ++ u32 dmacmd; /* DMA command */ ++}; ++ ++/* ++ * CIM device structure ++ */ ++struct cim_device { ++ unsigned char *framebuf; ++ unsigned int frame_size; ++ unsigned int page_order; ++ wait_queue_head_t wait_queue; ++ struct cim_desc frame_desc __attribute__ ((aligned (16))); ++}; ++ ++// global ++static struct cim_device *cim_dev; ++ ++/*========================================================================== ++ * CIM init routines ++ *========================================================================*/ ++ ++static void cim_config(cim_config_t *c) ++{ ++ REG_CIM_CFG = c->cfg; ++ REG_CIM_CTRL = c->ctrl; ++ // Set the master clock output ++#if defined(CONFIG_SOC_JZ4730) ++ __cim_set_master_clk(__cpm_get_sclk(), c->mclk); ++#elif defined(CONFIG_SOC_JZ4740) || defined(CONFIG_SOC_JZ4750) ++ __cim_set_master_clk(__cpm_get_hclk(), c->mclk); ++#else ++ __cim_set_master_clk(__cpm_get_sclk(), c->mclk); ++#endif ++ // Enable sof, eof and stop interrupts ++ __cim_enable_sof_intr(); ++ __cim_enable_eof_intr(); ++ __cim_enable_stop_intr(); ++} ++ ++/*========================================================================== ++ * CIM start/stop operations ++ *========================================================================*/ ++ ++static int cim_start_dma(char *ubuf) ++{ ++ __cim_disable(); ++ ++ dma_cache_wback((unsigned long)cim_dev->framebuf, (2 ^ (cim_dev->page_order)) * 4096); ++ ++ // set the desc addr ++ __cim_set_da(virt_to_phys(&(cim_dev->frame_desc))); ++ ++ __cim_clear_state(); // clear state register ++ __cim_reset_rxfifo(); // resetting rxfifo ++ __cim_unreset_rxfifo(); ++ __cim_enable_dma(); // enable dma ++ ++ // start ++ __cim_enable(); ++ ++ // wait for interrupts ++ interruptible_sleep_on(&cim_dev->wait_queue); ++ ++ // copy frame data to user buffer ++ memcpy(ubuf, cim_dev->framebuf, cim_dev->frame_size); ++ ++ return cim_dev->frame_size; ++} ++ ++static void cim_stop(void) ++{ ++ __cim_disable(); ++ __cim_clear_state(); ++} ++ ++/*========================================================================== ++ * Framebuffer allocation and destroy ++ *========================================================================*/ ++ ++static void cim_fb_destroy(void) ++{ ++ if (cim_dev->framebuf) { ++ free_pages((unsigned long)(cim_dev->framebuf), cim_dev->page_order); ++ cim_dev->framebuf = NULL; ++ } ++} ++ ++static int cim_fb_alloc(void) ++{ ++ cim_dev->frame_size = img_width * img_height * (img_bpp/8); ++ cim_dev->page_order = get_order(cim_dev->frame_size); ++ ++ /* frame buffer */ ++ cim_dev->framebuf = (unsigned char *)__get_free_pages(GFP_KERNEL, cim_dev->page_order); ++ if ( !(cim_dev->framebuf) ) { ++ return -ENOMEM; ++ } ++ ++ cim_dev->frame_desc.nextdesc = virt_to_phys(&(cim_dev->frame_desc)); ++ cim_dev->frame_desc.framebuf = virt_to_phys(cim_dev->framebuf); ++ cim_dev->frame_desc.frameid = 0x52052018; ++ cim_dev->frame_desc.dmacmd = CIM_CMD_EOFINT | CIM_CMD_STOP | (cim_dev->frame_size >> 2); // stop after capturing a frame ++ ++ dma_cache_wback((unsigned long)(&(cim_dev->frame_desc)), 16); ++ ++ return 0; ++} ++ ++/*========================================================================== ++ * File operations ++ *========================================================================*/ ++ ++static int cim_open(struct inode *inode, struct file *filp); ++static int cim_release(struct inode *inode, struct file *filp); ++static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l); ++static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l); ++static int cim_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); ++ ++static struct file_operations cim_fops = ++{ ++ open: cim_open, ++ release: cim_release, ++ read: cim_read, ++ write: cim_write, ++ ioctl: cim_ioctl ++}; ++ ++static int cim_open(struct inode *inode, struct file *filp) ++{ ++ try_module_get(THIS_MODULE); ++ return 0; ++} ++ ++static int cim_release(struct inode *inode, struct file *filp) ++{ ++ cim_stop(); ++ ++ module_put(THIS_MODULE); ++ return 0; ++} ++ ++static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l) ++{ ++ if (size < cim_dev->frame_size) ++ return -EINVAL; ++ ++ return cim_start_dma(buf); ++} ++ ++static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l) ++{ ++ printk("cim error: write is not implemented\n"); ++ return -1; ++} ++ ++static int cim_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case IOCTL_SET_IMG_PARAM: ++ { ++ img_param_t i; ++ ++ if (copy_from_user((void *)&i, (void *)arg, sizeof(img_param_t))) ++ return -EFAULT; ++ ++ img_width = i.width; ++ img_height = i.height; ++ img_bpp = i.bpp; ++ ++ if ((img_width * img_height * img_bpp/8) > MAX_FRAME_SIZE) { ++ /* realloc the buffer */ ++ cim_fb_destroy(); ++ if (cim_fb_alloc() < 0) ++ return -ENOMEM; ++ } ++ ++ cim_dev->frame_size = img_width * img_height * (img_bpp/8); ++ ++ cim_dev->frame_desc.dmacmd = CIM_CMD_EOFINT | CIM_CMD_STOP | (cim_dev->frame_size >> 2); // stop after capturing a frame ++ ++ dma_cache_wback((unsigned long)(&(cim_dev->frame_desc)), 16); ++ ++ break; ++ } ++ case IOCTL_CIM_CONFIG: ++ { ++ cim_config_t c; ++ ++ if (copy_from_user((void *)&c, (void *)arg, sizeof(cim_config_t))) ++ return -EFAULT; ++ ++ cim_config(&c); ++ ++ break; ++ } ++ default: ++ printk("Not supported command: 0x%x\n", cmd); ++ return -EINVAL; ++ break; ++ } ++ return 0; ++} ++ ++/*========================================================================== ++ * Interrupt handler ++ *========================================================================*/ ++ ++static irqreturn_t cim_irq_handler(int irq, void *dev_id) ++{ ++ u32 state = REG_CIM_STATE; ++#if 0 ++ if (state & CIM_STATE_DMA_EOF) { ++ wake_up_interruptible(&cim_dev->wait_queue); ++ } ++#endif ++ if (state & CIM_STATE_DMA_STOP) { ++ // Got a frame, wake up wait routine ++ wake_up_interruptible(&cim_dev->wait_queue); ++ } ++ ++ // clear status flags ++ REG_CIM_STATE = 0; ++ return IRQ_HANDLED; ++} ++ ++/*========================================================================== ++ * Module init and exit ++ *========================================================================*/ ++ ++static int __init cim_init(void) ++{ ++ struct cim_device *dev; ++ int ret; ++ ++ /* allocate device */ ++ dev = kmalloc(sizeof(struct cim_device), GFP_KERNEL); ++ if (!dev) return -ENOMEM; ++ ++ /* record device */ ++ cim_dev = dev; ++ ++ /* allocate a frame buffer */ ++ if (cim_fb_alloc() < 0) { ++ kfree(dev); ++ return -ENOMEM; ++ } ++ ++ init_waitqueue_head(&dev->wait_queue); ++ ++ ret = jz_register_chrdev(CIM_MINOR, CIM_NAME, &cim_fops, dev); ++ if (ret < 0) { ++ cim_fb_destroy(); ++ kfree(dev); ++ return ret; ++ } ++ ++ if ((ret = request_irq(IRQ_CIM, cim_irq_handler, IRQF_DISABLED, ++ CIM_NAME, dev))) { ++ cim_fb_destroy(); ++ kfree(dev); ++ printk(KERN_ERR "CIM could not get IRQ"); ++ return ret; ++ } ++ ++ printk("JzSOC Camera Interface Module (CIM) driver registered\n"); ++ ++ return 0; ++} ++ ++static void __exit cim_exit(void) ++{ ++ free_irq(IRQ_CIM, cim_dev); ++ jz_unregister_chrdev(CIM_MINOR, CIM_NAME); ++ cim_fb_destroy(); ++ kfree(cim_dev); ++} ++ ++module_init(cim_init); ++module_exit(cim_exit); +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/cim.h linux-2.6.24.7/drivers/char/jzchar/cim.h +--- linux-2.6.24.7.old/drivers/char/jzchar/cim.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/cim.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,36 @@ ++/* ++ * JzSOC CIM driver ++ * ++ * Copyright (C) 2005 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __CIM_H__ ++#define __CIM_H__ ++ ++typedef struct ++{ ++ u32 width; ++ u32 height; ++ u32 bpp; ++} IMG_PARAM; ++ ++/* ++ * IOCTL_XXX commands ++ */ ++#define IOCTL_SET_IMG_PARAM 0 // arg type: IMG_PARAM * ++ ++#endif /* __CIM_H__ */ +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/jz_ow.c linux-2.6.24.7/drivers/char/jzchar/jz_ow.c +--- linux-2.6.24.7.old/drivers/char/jzchar/jz_ow.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/jz_ow.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,497 @@ ++/* ++ * linux/drivers/char/jzchar/jz_ow.c ++ * ++ * One Wire Bus test driver ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "jzchars.h" ++ ++#define OW_CPU_READ_ROM 1 ++#define OW_INTC_READ_ROM 1 ++#define OW_CPU_SEARCH_ROM 0 ++#define OW_INTC_SEARCH_ROM 0 ++ ++#define OW_DEBUG 0 ++#if OW_DEBUG ++#define OWI_MAX 10 ++static char CFG[OWI_MAX]; ++static char CTL[OWI_MAX]; ++static char STS[OWI_MAX]; ++static char DAT[OWI_MAX]; ++static char DIV[OWI_MAX]; ++static void owi_register_dump(int i) ++{ ++ CFG[i]= REG_OWI_CFG; ++ CTL[i]= REG_OWI_CTL; ++ STS[i]= REG_OWI_STS; ++ DAT[i]= REG_OWI_DAT; ++ DIV[i]= REG_OWI_DIV; ++} ++static void owi_register_print(int i) ++{ ++ printk(" REG_OWI_CFG: 0x%08x\n", CFG[i]); ++ printk(" REG_OWI_CTL: 0x%08x\n", CTL[i]); ++ printk(" REG_OWI_STS: 0x%08x\n", STS[i]); ++ printk(" REG_OWI_DAT: 0x%08x\n", DAT[i]); ++ printk(" REG_OWI_DIV: 0x%08x\n", DIV[i]); ++} ++#endif ++ ++static DECLARE_WAIT_QUEUE_HEAD (ow_wait_queue); ++ ++/* ++ * fops routines ++ */ ++static int ow_open(struct inode *inode, struct file *filp); ++static int ow_release(struct inode *inode, struct file *filp); ++static ssize_t ow_read(struct file *filp, char *buf, size_t size, loff_t *l); ++static ssize_t ow_write(struct file *filp, const char *buf, size_t size, loff_t *l); ++static int ow_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); ++ ++static void do_ow_rddata(void); ++static void do_ow_wrdata(void); ++static void do_ow_wr1rd(void); ++static void do_ow_wr0(void); ++static void do_ow_rst(void); ++ ++static void do_interrupt_mode_test(void); ++static void do_cpu_mode_test(void); ++ ++static struct file_operations ow_fops = ++{ ++ open: ow_open, ++ release: ow_release, ++ read: ow_read, ++ write: ow_write, ++ ioctl: ow_ioctl, ++}; ++ ++static int ow_open(struct inode *inode, struct file *filp) ++{ ++ try_module_get(THIS_MODULE); ++ return 0; ++} ++ ++static int ow_release(struct inode *inode, struct file *filp) ++{ ++ module_put(THIS_MODULE); ++ return 0; ++} ++ ++static ssize_t ow_read(struct file *filp, char *buf, size_t size, loff_t *l) ++{ ++ printk("OW: read is not implemented\n"); ++ return -1; ++} ++ ++static ssize_t ow_write(struct file *filp, const char *buf, size_t size, loff_t *l) ++{ ++ printk("ow: write is not implemented\n"); ++ return -1; ++} ++ ++static int ow_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ int ret = 0; ++ switch (cmd) { ++ ++ default: ++ printk("Not supported command: 0x%x\n", cmd); ++ return -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static void do_ow_rddata(void) ++{ ++ __owi_clr_sts(); ++ __owi_set_rddata(); ++ __owi_enable_ow_ops(); ++} ++ ++static void do_ow_wrdata(void) ++{ ++ __owi_clr_sts(); ++ __owi_set_wrdata(); ++ __owi_enable_ow_ops(); ++} ++ ++static void do_ow_wr1rd(void) ++{ ++ __owi_clr_sts(); ++ __owi_set_wr1rd(); ++ __owi_enable_ow_ops(); ++} ++ ++static void do_ow_wr0(void) ++{ ++ __owi_clr_sts(); ++ __owi_set_wr0(); ++ __owi_enable_ow_ops(); ++} ++ ++static void do_ow_rst(void) ++{ ++ __owi_clr_sts(); ++ __owi_set_rst(); ++ __owi_enable_ow_ops(); ++} ++ ++static irqreturn_t ow_interrupt(int irq, void *dev_id) ++{ ++ __owi_clr_sts(); ++ wake_up(&ow_wait_queue); ++ ++ return IRQ_HANDLED; ++} ++ ++static void ow_intcm_read_rom(char *rom) ++{ ++ int i; ++ ++ __owi_select_regular_mode(); ++ REG_OWI_DIV = 23; ++ __owi_clr_sts(); ++ __intc_unmask_irq(IRQ_OWI); ++ __owi_enable_all_interrupts(); ++ ++ do_ow_rst(); ++ sleep_on(&ow_wait_queue); ++ ++ REG_OWI_DAT = 0x33; ++ do_ow_wrdata(); ++ sleep_on(&ow_wait_queue); ++ ++ for(i=0; i<8; i++){ ++ do_ow_rddata(); ++ sleep_on(&ow_wait_queue); ++ rom[i] = REG_OWI_DAT; ++ } ++ __intc_mask_irq(IRQ_OWI); ++} ++ ++static void ow_intcm_search_rom(void) ++{ ++ int i, j; ++ int normal, reverse; ++#if 1 ++ unsigned char rom[8]={0x01, 0xf9, 0x35, 0x53, 0x11, 0x00, 0x00, 0x3e}; ++#else ++ unsigned char rom[8]={0x01, 0xd8, 0x10, 0x02, 0x10, 0x00, 0x00, 0x22}; ++#endif ++ __owi_select_regular_mode(); ++ REG_OWI_DIV = __cpm_get_extalclk()/1000000 - 1; ++ __owi_clr_sts(); ++ __intc_unmask_irq(IRQ_OWI); ++ __owi_enable_all_interrupts(); ++ ++ /* reset */ ++ do_ow_rst(); ++ sleep_on(&ow_wait_queue); ++ ++ /* send search ROM command */ ++ REG_OWI_DAT = 0xf0; ++ do_ow_wrdata(); ++ sleep_on(&ow_wait_queue); ++ ++ for( i=0; i<8; i++){ ++ for (j=0; j<8; j++){ ++ do_ow_wr1rd(); ++ sleep_on(&ow_wait_queue); ++ normal = ( __owi_get_rdst() !=0); ++ printk("normal: %d\n",normal); ++ ++ do_ow_wr1rd(); ++ sleep_on(&ow_wait_queue); ++ reverse = ( __owi_get_rdst() !=0); ++ printk("reverse: %d\n",reverse); ++ ++ if(normal ==1 && reverse ==1){ ++ printk("Search rom INTC mode: 11 NO device found\n"); ++ __intc_mask_irq(IRQ_OWI); ++ return; ++ } ++#if 1 ++ if ( (rom[i]>>j) & 1 ){ ++ printk("write 1\n"); ++ do_ow_wr1rd(); ++ sleep_on(&ow_wait_queue); ++ } ++ else{ ++ printk("write 0\n"); ++ do_ow_wr0(); ++ sleep_on(&ow_wait_queue); ++ } ++ ++#else ++ if(normal ==0 && reverse ==0){ ++ if (!((rom[i]>>j) & 1) ){ ++ printk("write 1\n"); ++ do_ow_wr1rd(); ++ sleep_on(&ow_wait_queue); ++ } ++ else{ ++ printk("write 0\n"); ++ do_ow_wr0(); ++ sleep_on(&ow_wait_queue); ++ } ++ }else{ ++ ++ if(normal ==0){ ++ printk("write 0\n"); ++ do_ow_wr0(); ++ sleep_on(&ow_wait_queue); ++ } ++ if(normal ==1){ ++ printk("write 1\n"); ++ do_ow_wr1rd(); ++ sleep_on(&ow_wait_queue); ++ } ++ } ++#endif ++ ++ } ++ printk("\n\n"); ++ } ++ ++ printk("\nSearch rom INTC mode: device found SUCCESSFULLY\n"); ++ __intc_mask_irq(IRQ_OWI); ++ ++} ++ ++static void ow_cpum_read_rom(char *rom) ++{ ++ int i; ++ ++ __owi_select_regular_mode(); ++ REG_OWI_DIV = __cpm_get_extalclk()/1000000 - 1; ++ __owi_clr_sts(); ++ __owi_disable_all_interrupts(); ++ ++ do_ow_rst(); ++ __owi_wait_ops_rdy(); ++ ++ if(!__owi_get_sts_pst()) ++ printk("read rom no device found\n"); ++ ++ REG_OWI_DAT = 0x33; ++ do_ow_wrdata(); ++ __owi_wait_ops_rdy(); ++ ++ for(i=0; i<8; i++){ ++ do_ow_rddata(); ++ __owi_wait_ops_rdy(); ++ rom[i] = REG_OWI_DAT; ++ } ++} ++ ++ ++static void ow_comm_bit(unsigned comm) ++{ ++ int i; ++ for(i=0; i<8; i++){ ++ if ( comm & (1<>j) & 1 ){ ++ printk("write 1\n"); ++ do_ow_wr1rd(); ++ while(!__owi_get_sts_bit_rdy()) ; ++ } ++ else{ ++ printk("write 0\n"); ++ do_ow_wr0(); ++ while(!__owi_get_sts_bit_rdy()) ; ++ } ++ ++#else ++ if(normal ==0 && reverse ==0){ ++ if (!((rom[i]>>j) & 1) ){ ++ printk("write 1\n"); ++ do_ow_wr1rd(); ++ while(!__owi_get_sts_bit_rdy()) ; ++ } ++ else{ ++ printk("write 0\n"); ++ do_ow_wr0(); ++ while(!__owi_get_sts_bit_rdy()) ; ++ } ++ }else{ ++ ++ if(normal ==0){ ++ printk("write 0\n"); ++ do_ow_wr0(); ++ while(!__owi_get_sts_bit_rdy()) ; ++ } ++ if(normal ==1){ ++ printk("write 1\n"); ++ do_ow_wr1rd(); ++ while(!__owi_get_sts_bit_rdy()) ; ++ } ++ } ++#endif ++ ++ } ++ printk("\n\n"); ++ } ++ printk("\nSearch rom CPU mode: device found SUCCESSFULLY\n"); ++} ++ ++static void do_interrupt_mode_test(void) ++{ ++ int ret, i; ++ unsigned char rom[8]; ++ ++ /* interrupt mode */ ++ ret = request_irq(IRQ_OWI, ow_interrupt, IRQF_DISABLED, ++ "JZ_OWI", NULL); ++ if(ret) ++ printk("failed irq \n"); ++ ++#if OW_INTC_READ_ROM ++ ow_intcm_read_rom(rom); ++ printk("\n\nAfter intc mode read ROM ops: \n"); ++ printk("ROM: "); ++ for(i=0; i<8; i++) ++ printk("0x%02x,",rom[i]); ++#endif ++ ++#if OW_INTC_SEARCH_ROM ++ ow_intcm_search_rom(); ++#endif ++ ++} ++ ++static void do_cpu_mode_test(void) ++{ ++ ++#if OW_CPU_READ_ROM ++ int i; ++ unsigned char rom[8]; ++ ++ ow_cpum_read_rom(rom); ++ printk("\n\nAfter CPU mode read ROM ops: \n"); ++ printk("ROM: "); ++ for(i=0; i<8; i++) ++ printk("0x%02x,",rom[i]); ++#endif ++ ++#if OW_CPU_SEARCH_ROM ++ ow_cpum_search_rom(); ++#endif ++} ++ ++/* ++ * Module init and exit ++ */ ++static int __init ow_init(void) ++{ ++ int ret; ++ ++ ret = jz_register_chrdev(OW_MINOR, "ow", &ow_fops, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ __gpio_as_func1(153); ++ ++ REG_OWI_CFG=0; ++ REG_OWI_CTL=0; ++ REG_OWI_STS=0; ++ REG_OWI_DAT=0; ++ REG_OWI_DIV=0; ++ ++ do_interrupt_mode_test(); ++ do_cpu_mode_test(); ++ ++ printk("Ingenic OW driver registered\n"); ++ ++ return 0; ++} ++ ++static void __exit ow_exit(void) ++{ ++ free_irq(IRQ_OWI, NULL); ++ jz_unregister_chrdev(OW_MINOR, "ow"); ++} ++ ++module_init(ow_init); ++module_exit(ow_exit); ++ ++MODULE_AUTHOR("Yurong Tan"); ++MODULE_DESCRIPTION("One Wire Bus test Driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/jz_ts.c linux-2.6.24.7/drivers/char/jzchar/jz_ts.c +--- linux-2.6.24.7.old/drivers/char/jzchar/jz_ts.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/jz_ts.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,443 @@ ++/* ++ * jz_ts.c ++ * ++ * Touch screen driver for the Ingenic JZ47XX. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "jz_ts.h" ++ ++MODULE_AUTHOR("Peter Wei "); ++MODULE_DESCRIPTION("Ingenic Touch Screen Driver"); ++MODULE_LICENSE("GPL"); ++ ++#define TS_NAME "jz-ts" ++#define TS_MINOR 16 /* MAJOR: 10, MINOR: 16 */ ++#define PFX TS_NAME ++ ++//#define JZ_TS_DEBUG ++ ++#ifdef JZ_TS_DEBUG ++#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) do {} while (0) ++#endif ++#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg) ++ ++static struct jz_ts_t jz_ts; ++ ++unsigned int (*codec_read_battery)(void) = NULL; ++ ++// hold the spinlock before calling. ++static void event_add(struct jz_ts_t *ts, struct ts_event *event) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ts->lock, flags); ++ ++ // add this event to the event queue ++ ts->event_buf[ts->nextIn] = *event; ++ ts->nextIn = (ts->nextIn + 1) & (EVENT_BUFSIZE - 1); ++ if (ts->event_count < EVENT_BUFSIZE) { ++ ts->event_count++; ++ } else { ++ // throw out the oldest event ++ ts->nextOut = (ts->nextOut + 1) & (EVENT_BUFSIZE - 1); ++ } ++ ++ spin_unlock_irqrestore(&ts->lock, flags); ++ ++ // async notify ++ if (ts->fasync) ++ kill_fasync(&ts->fasync, SIGIO, POLL_IN); ++ // wake up any read call ++ if (waitqueue_active(&ts->wait)) ++ wake_up_interruptible(&ts->wait); ++} ++ ++static int event_pull(struct jz_ts_t *ts, struct ts_event *event) ++{ ++ unsigned long flags; ++ int ret; ++ ++ spin_lock_irqsave(&ts->lock, flags); ++ ret = ts->event_count; ++ if (ts->event_count) { ++ *event = ts->event_buf[ts->nextOut]; ++ ts->nextOut = (ts->nextOut + 1) & (EVENT_BUFSIZE - 1); ++ ts->event_count--; ++ } ++ spin_unlock_irqrestore(&ts->lock, flags); ++ ++ return ret; ++} ++ ++static int pen_is_down = 0; ++ ++static irqreturn_t pendown_interrupt(int irq, void * dev_id) ++{ ++ struct jz_ts_t* ts = &jz_ts; ++ struct ts_event event; ++ ++ dbg("pen down"); ++#if defined(CONFIG_SOC_JZ4740) ++ if (ts->sleeping) { ++ ts->sleeping = 0; ++ ts_data_ready(); ++ return IRQ_HANDLED; ++ } ++#endif ++ spin_lock(&ts->lock); ++ ++ if (ts->irq_enabled) { ++ ts->irq_enabled = 0; ++ } ++ else ++ ts->irq_enabled = 1; ++ ++ ++ if (pen_is_down) ++ pen_is_down = 0; ++ else ++ pen_is_down = 1; ++ ++ // callback routine to clear irq status ++ ts_irq_callback(); ++ ++ if ( (pen_is_down == 0)){ ++ del_timer(&ts->acq_timer); ++ spin_unlock(&ts->lock); ++ event.x = event.y = event.pressure = 0; ++ event.status = PENUP; ++ ts->first_read = 0; ++ event_add(ts, &event); ++ return IRQ_HANDLED; ++ } ++ ++ if ( (pen_is_down == 1)) ++ { ++ ts->acq_timer.expires = jiffies + HZ / 100; ++ del_timer(&ts->acq_timer); ++ ts->first_read = 1; ++ add_timer(&ts->acq_timer); ++ spin_unlock(&ts->lock); ++ } ++ return IRQ_HANDLED; ++} ++ ++ ++/* ++ * Raw X,Y,pressure acquisition timer function. It gets scheduled ++ * only while pen is down. Its duration between calls is the polling ++ * rate. ++ */ ++static void ++jz_acq_timer(unsigned long data) ++{ ++ struct jz_ts_t *ts = (struct jz_ts_t *)data; ++ struct ts_event event; ++ int pen_was_down = ts->pen_is_down; ++ ++ spin_lock(&ts->lock); ++ ++ if (PenIsDown()) { ++ ++ ts->pen_is_down = 1; ++ ++ if (AcquireEvent(ts, &event)) // check event is valid or not? ++ event_add(ts, &event); ++ ++ // schedule next acquire ++ ts->acq_timer.expires = jiffies + HZ / 100; ++ del_timer(&ts->acq_timer); ++ add_timer(&ts->acq_timer); ++ } else { ++ ++ if (!ts->irq_enabled) { ++ ts->irq_enabled = 1; ++ } ++ ts->pen_is_down = 0; ++ if (pen_was_down) { ++ event.x = event.y = event.pressure = 0; ++ event.status = PENUP; ++ event_add(ts, &event); ++ } ++ } ++ ++ spin_unlock(&ts->lock); ++} ++ ++/* +++++++++++++ Read battery voltage routine ++++++++++++++*/ ++ ++unsigned int jz_read_battery(void) ++{ ++ unsigned int v = 0; ++ struct jz_ts_t *ts = &jz_ts; ++ ++ spin_lock(&ts->lock); ++ ++ if (codec_read_battery) ++ v = codec_read_battery(); ++ ++ spin_unlock(&ts->lock); ++ ++ return v; ++} ++ ++/* +++++++++++++ File operations ++++++++++++++*/ ++ ++static int ++jz_fasync(int fd, struct file *filp, int mode) ++{ ++ struct jz_ts_t *ts = (struct jz_ts_t *)filp->private_data; ++ return fasync_helper(fd, filp, mode, &ts->fasync); ++} ++ ++ ++static unsigned int ++jz_poll(struct file * filp, poll_table * wait) ++{ ++ struct jz_ts_t* ts = (struct jz_ts_t*)filp->private_data; ++ poll_wait(filp, &ts->wait, wait); ++ if (ts->event_count) ++ return POLLIN | POLLRDNORM; ++ return 0; ++} ++ ++static ssize_t ++jz_read(struct file * filp, char * buffer, size_t count, loff_t * ppos) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ struct jz_ts_t* ts = (struct jz_ts_t*)filp->private_data; ++ char *ptr = buffer; ++ struct ts_event event; ++ int err = 0; ++ ++ dbg("jz_read"); ++ ++ add_wait_queue(&ts->wait, &wait); ++ while (count >= sizeof(struct ts_event)) { ++ err = -ERESTARTSYS; ++ if (signal_pending(current)) ++ break; ++ ++ ++ if (event_pull(ts, &event)) { ++ err = copy_to_user(ptr, &event, ++ sizeof(struct ts_event)); ++ if (err) ++ break; ++ ptr += sizeof(struct ts_event); ++ count -= sizeof(struct ts_event); ++ } else { ++ set_current_state(TASK_INTERRUPTIBLE); ++ err = -EAGAIN; ++ if (filp->f_flags & O_NONBLOCK) ++ break; ++ schedule(); ++ } ++ } ++ ++ current->state = TASK_RUNNING; ++ remove_wait_queue(&ts->wait, &wait); ++ ++ return ptr == buffer ? err : ptr - buffer; ++} ++ ++ ++static int ++jz_open(struct inode * inode, struct file * filp) ++{ ++ struct jz_ts_t *ts; ++ int retval; ++ ++ dbg("open ts device"); ++ filp->private_data = ts = &jz_ts; ++ ++ spin_lock(&ts->lock); ++ ++ ts->pen_is_down = 0; // start with pen up ++ ts->sleeping = 0; ++ // flush event queue ++ ts->nextIn = ts->nextOut = ts->event_count = 0; ++ ++ // Init acquisition timer function ++ init_timer(&ts->acq_timer); ++ ts->acq_timer.function = jz_acq_timer; ++ ts->acq_timer.data = (unsigned long)ts; ++ ++ ts->irq_enabled = 1; ++ ++ spin_unlock(&ts->lock); ++ ++ /* Since ts interrupt can happen immediately after request_irq, ++ * we wait until we've completed init of all relevent driver ++ * state variables. Now we grab the PenDown IRQ ++ */ ++ retval = ts_request_irq(&ts->pendown_irq, pendown_interrupt, TS_NAME, ts); ++ if (retval) { ++ err("unable to get PenDown IRQ %d", ts->pendown_irq); ++ return retval; ++ } ++ ++ try_module_get(THIS_MODULE); ++ return 0; ++} ++ ++static int ++jz_release(struct inode * inode, struct file * filp) ++{ ++ struct jz_ts_t* ts = (struct jz_ts_t*)filp->private_data; ++ ++ ts_free_irq(ts); ++ jz_fasync(-1, filp, 0); ++ del_timer_sync(&ts->acq_timer); ++ ++ module_put(THIS_MODULE); ++ return 0; ++} ++ ++static int jz_ioctl(struct inode *inode, struct file *file, unsigned int ioctl_num, unsigned long ioctl_param) ++{ ++ struct txy { ++ int minx; ++ int miny; ++ int maxx; ++ int maxy; ++ }; ++ ++ struct txy ch; ++ ++ /* ++ * Switch according to the ioctl called ++ */ ++ switch (ioctl_num) ++ { ++ case IOCTL_SET_MSG: ++ jz_ts.filter=1; ++ break; ++ case IOCTL_SET_NUM: ++ if (copy_from_user((void *)&ch, (void *)ioctl_param, sizeof(ch))) ++ return -EFAULT; ++ jz_ts.minx = ch.minx; ++ jz_ts.miny = ch.miny; ++ jz_ts.maxx = ch.maxx; ++ jz_ts.maxy = ch.maxy; ++ break; ++ } ++ ++ return 0; ++} ++ ++static struct file_operations ts_fops = { ++ owner: THIS_MODULE, ++ read: jz_read, ++ poll: jz_poll, ++ fasync: jz_fasync, ++ ioctl: jz_ioctl, ++ open: jz_open, ++ release: jz_release, ++}; ++ ++/* +++++++++++++ End File operations ++++++++++++++*/ ++ ++static int __init minx_setup(char *str) ++{ ++ int i; ++ ++ if (get_option(&str,&i)) jz_ts.minx = i; ++ jz_ts.filter=i; ++ return 1; ++} ++ ++__setup("ts_minx=", minx_setup); ++ ++static int __init miny_setup(char *str) ++{ ++ int i; ++ if (get_option(&str,&i)) jz_ts.miny = i; ++ return 1; ++} ++ ++__setup("ts_miny=", miny_setup); ++ ++static int __init maxx_setup(char *str) ++{ ++ int i; ++ if (get_option(&str,&i)) jz_ts.maxx = i; ++ return 1; ++} ++ ++__setup("ts_maxx=", maxx_setup); ++ ++static int __init maxy_setup(char *str) ++{ ++ int i; ++ if (get_option(&str,&i)) jz_ts.maxy = i; ++ return 1; ++} ++ ++__setup("ts_maxy=", maxy_setup); ++ ++static int __init printraw_setup(char *str) ++{ ++ if (str) ++ jz_ts.prints = 1; ++ ++ return 0; ++} ++ ++__setup("ts_debug", printraw_setup); ++ ++ ++static struct miscdevice jz_ts_dev = { ++ minor: TS_MINOR, ++ name: TS_NAME, ++ fops: &ts_fops, ++}; ++ ++static int __init jzts_init_module(void) ++{ ++ struct jz_ts_t *ts = &jz_ts; ++ int ret; ++ ++ if ((ret = misc_register(&jz_ts_dev)) < 0) { ++ err("can't register misc device"); ++ return ret; ++ } ++ ++// memset(ts, 0, sizeof(struct jz_ts_t)); ++ init_waitqueue_head(&ts->wait); ++ spin_lock_init(&ts->lock); ++ ++ printk("Jz generic touch screen driver registered\n"); ++ ++ return 0; ++} ++ ++static void jzts_cleanup_module(void) ++{ ++ misc_deregister(&jz_ts_dev); ++} ++ ++module_init(jzts_init_module); ++module_exit(jzts_cleanup_module); +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/jz_ts.h linux-2.6.24.7/drivers/char/jzchar/jz_ts.h +--- linux-2.6.24.7.old/drivers/char/jzchar/jz_ts.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/jz_ts.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,54 @@ ++#ifndef __JZ_TS_H__ ++#define __JZ_TS_H__ ++ ++/* ++ * IOCTL commands ++ */ ++#define IOCTL_SET_MSG 0 ++#define IOCTL_SET_NUM 1 ++ ++ ++/* ++ * TS Event type ++ */ ++struct ts_event { ++ u16 status; ++ u16 x; ++ u16 y; ++ u16 pressure; ++ u16 pad; ++}; ++ ++/* TS event status */ ++#define PENUP 0x00 ++#define PENDOWN 0x01 ++ ++#define EVENT_BUFSIZE 64 // must be power of two ++ ++struct jz_ts_t { ++ int pendown_irq; // IRQ of pendown interrupt ++ int pen_is_down; // 1 = pen is down, 0 = pen is up ++ int irq_enabled; ++ struct ts_event event_buf[EVENT_BUFSIZE];// The event queue ++ int nextIn, nextOut; ++ int event_count; ++ struct fasync_struct *fasync; // asynch notification ++ struct timer_list acq_timer; // Timer for triggering acquisitions ++ wait_queue_head_t wait; // read wait queue ++ spinlock_t lock; ++ int minx, miny, maxx, maxy; ++ int filter, prints; ++ int sleeping; ++ int first_read; ++}; ++ ++extern void ts_enable_irq(void); ++extern void ts_disable_irq(void); ++extern int ts_request_irq(u32 *irq,irqreturn_t (*handler)(int, void *), const char *devname, void *dev_id); ++extern void ts_free_irq(struct jz_ts_t *ts); ++extern int PenIsDown(void); ++extern int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event); ++extern void ts_irq_callback(void); ++extern void ts_data_ready(void); ++ ++#endif /* __JZ_TS_H__ */ +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/jz_tssi.c linux-2.6.24.7/drivers/char/jzchar/jz_tssi.c +--- linux-2.6.24.7.old/drivers/char/jzchar/jz_tssi.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/jz_tssi.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,457 @@ ++/* ++ * jz_tssi.c ++ * ++ * MPEG2-TS interface driver for the Ingenic JZ47XX. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "jzchars.h" ++ ++#include "jz_tssi.h" ++ ++ ++MODULE_AUTHOR("Lucifer Liu "); ++MODULE_DESCRIPTION("Ingenic MPEG2-TS interface Driver"); ++MODULE_LICENSE("GPL"); ++ ++#define TSSI_NAME "JZ MPEG2-TS SI" ++#define TSSI_MINOR 204 /* MAJOR: 10, MINOR: 16 */ ++#define TSSI_IRQ IRQ_TSSI ++#define PFX TSSI_NAME ++#define RING_BUF_NUM 100 ++ ++#define USE_DMA ++#define TRIG_PIN ( 32 * 2 + 15 ) ++#define DMA_ID_TSSI 5 ++//#define JZ_TSSI_DEBUG ++ ++#ifdef JZ_TSSISI_DEBUG ++#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) do {} while (0) ++#endif ++#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg) ++ ++static struct jz_tssi_t jz_tssi_g; ++static struct jz_tssi_buf_ring_t jz_tssi_ring_g; ++static int tssi_dma_reinit(int dma_chan, unsigned char *dma_buf, int size); ++ ++static void print_reg( void ) ++{ ++ printk("REG_TSSI_ENA %8x \n ", REG8( TSSI_ENA )); ++ printk("REG_TSSI_CFG %8x \n ", REG16( TSSI_CFG )); ++ printk("REG_TSSI_CTRL %8x \n ", REG8( TSSI_CTRL )); ++ printk("REG_TSSI_STAT %8x \n ", REG8( TSSI_STAT )); ++ printk("REG_TSSI_FIFO %8x \n ", REG32( TSSI_FIFO )); ++ printk("REG_TSSI_PEN %8x \n ", REG32( TSSI_PEN )); ++ printk("REG_TSSI_PID0 %8x \n ", REG32( TSSI_PID0 )); ++ printk("REG_TSSI_PID1 %8x \n ", REG32( TSSI_PID1 )); ++ printk("REG_TSSI_PID2 %8x \n ", REG32( TSSI_PID2 )); ++ printk("REG_TSSI_PID3 %8x \n ", REG32( TSSI_PID3 )); ++ printk("REG_TSSI_PID4 %8x \n ", REG32( TSSI_PID4 )); ++ printk("REG_TSSI_PID5 %8x \n ", REG32( TSSI_PID5 )); ++ printk("REG_TSSI_PID6 %8x \n ", REG32( TSSI_PID6 )); ++ printk("REG_TSSI_PID7 %8x \n ", REG32( TSSI_PID7 )); ++} ++ ++void dump_dma_channel(unsigned int dmanr) ++{ ++ printk("DMA%d Registers:\n", dmanr); ++ printk(" DMACR = 0x%8x\n", REG_DMAC_DMACR(0)); ++ printk(" DSAR = 0x%8x\n", REG_DMAC_DSAR(dmanr)); ++ printk(" DTAR = 0x%8x\n", REG_DMAC_DTAR(dmanr)); ++ printk(" DTCR = 0x%8x\n", REG_DMAC_DTCR(dmanr)); ++ printk(" DRSR = 0x%8x\n", REG_DMAC_DRSR(dmanr)); ++ printk(" DCCSR = 0x%8x\n", REG_DMAC_DCCSR(dmanr)); ++ printk(" DCMD = 0x%8x\n", REG_DMAC_DCMD(dmanr)); ++ printk(" DDA = 0x%8x\n", REG_DMAC_DDA(dmanr)); ++ printk(" DMADBR = 0x%8x\n", REG_DMAC_DMADBR(1)); ++} ++ ++static int tssi_buf_init( struct jz_tssi_buf_ring_t * ring ) ++{ ++ int i; ++ struct jz_tssi_buf * bp,* ap, *cp; ++ ++ ap = cp = bp = (struct jz_tssi_buf *)kmalloc( sizeof( struct jz_tssi_buf ) ,GFP_KERNEL ); //the first ++ if ( !bp ) { ++ printk("Can not malloc buffer! \n"); ++ return -1; ++ } ++ ++ for ( i = 0; i < RING_BUF_NUM; i ++ ) { ++ bp = ap; ++ bp->buf = (unsigned int *) kmalloc(MPEG2_TS_PACHAGE_SIZE / 4 * sizeof(unsigned int) ,GFP_KERNEL); ++ if ( !bp->buf ) { ++ printk("Can not malloc buffer! \n"); ++ return -1; ++ } ++ bp->index = i; ++ bp->pos = 0; ++ ap = (struct jz_tssi_buf *)kmalloc( sizeof( struct jz_tssi_buf ) ,GFP_KERNEL ); ++ if ( !ap ) { ++ printk("Can not malloc buffer! \n"); ++ return -1; ++ } ++ ++ bp->next = ap; //point to next ! ++ } ++ ++ bp->next = cp; //point loop to first! ++ ring->front = cp; ++ ring->rear = cp; ++ ring->fu_num = 0; ++ kfree(ap); ++ return 0; ++} ++ ++static void tssi_free_buf( struct jz_tssi_buf_ring_t * ring ) ++{ ++ int i; ++ struct jz_tssi_buf * ap; ++ for ( i = 0; i < RING_BUF_NUM; i ++ ) ++ { ++ ap = ring->front; ++ ring->front = ring->front->next; ++ kfree( ap ); ++ } ++} ++ ++#if 0 ++static void tssi_read_fifo(void *dev_id) ++{ ++ struct jz_tssi_t* tssi = ( struct jz_tssi_t* )dev_id; ++ struct jz_tssi_buf_ring_t * ring = tssi->cur_buf; ++ struct jz_tssi_buf *buf = ring->rear; ++ int i; ++#if 0 ++ if ( ring->fu_num > RING_BUF_NUM ) ++ { ++ printk("Ring buffer full ! %d \n",ring->fu_num); ++ return; ++ } ++#endif ++ ++ for ( i = 0; i < 8 ; i ++ ) ++ { ++ ring->front->buf[ring->front->pos++] = REG_TSSI_FIFO; ++ } ++ ++ if ( ring->front->pos >= MPEG2_TS_PACHAGE_SIZE ) ++ { ++ ring->fu_num ++; ++ ring->front = ring->front->next; ++ ring->front->pos = 0; ++ } ++} ++#endif ++ ++static void tssi_config_filting( void ) ++{ ++ __tssi_soft_reset(); ++ __gpio_as_tssi(); ++ __tssi_disable_ovrn_irq(); //use dma ,no need irq ++ __tssi_disable_trig_irq(); ++ __tssi_set_tigger_num( 8 ); //trig is 4 word! ++// __tssi_filter_enable(); ++ __tssi_clear_state(); ++ __tssi_filter_disable(); ++ __tssi_state_clear_overrun(); ++// __tssi_clear_trig_irq_flag(); ++#ifdef USE_DMA ++ __tssi_dma_enable(); ++#else ++ __tssi_dma_disable(); ++#endif ++ ++ __tssi_enable_ovrn_irq(); ++// __tssi_enable_trig_irq(); ++ ++ //set config ++// __tssi_set_bt_1(); ++ __tssi_set_wd_1(); ++ __tssi_set_data_use_data7(); ++ __tssi_set_data_pola_high(); ++// __tssi_select_serail_mode(); ++ __tssi_select_paral_mode(); ++ __tssi_select_clk_fast(); ++ __tssi_select_clk_posi_edge(); ++ __tssi_select_frm_act_high(); ++ __tssi_select_str_act_high(); ++ __tssi_select_fail_act_high(); ++// __tssi_select_fail_act_low(); ++ __tssi_disable_filte_pid0(); //we disable pid0 filter for ever! ++} ++ ++static void tssi_add_pid(int pid_num, int pid) ++{ ++ unsigned int addr ; ++ int n = pid_num / 2, hl = pid_num % 2; ++ if ( hl ) //use high pid, pid1 ++ { ++ addr = TSSI_PID0 + ( n * 4 ); ++ REG32( addr ) |= ( (pid & 0x1fff) << 16 ); //13bit ++ REG_TSSI_PEN |= ( 1 << (16 + n) ); ++ } ++ else //use low pid, pid0 ++ { ++ addr = TSSI_PID0 + ( n * 4 ); ++ REG32( addr ) |= pid & 0x1fff; //13bit ++ REG_TSSI_PEN |= ( 1 << n ); ++ } ++} ++ ++static irqreturn_t tssi_dma_irq(int irq, void * dev_id) ++{ ++ struct jz_tssi_t *tssi = (struct jz_tssi_t *)dev_id; ++ struct jz_tssi_buf_ring_t *buf = tssi->cur_buf; ++ ++ REG_DMAC_DCCSR(tssi->dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ ++ if (__dmac_channel_transmit_end_detected(tssi->dma_chan)) { ++ __dmac_channel_clear_transmit_end(tssi->dma_chan); ++ if ( buf->fu_num < RING_BUF_NUM ) ++ { ++ buf->front = buf->front->next; ++ REG_DMAC_DSAR(tssi->dma_chan) = CPHYSADDR(TSSI_FIFO); ++ REG_DMAC_DTAR(tssi->dma_chan) = CPHYSADDR((unsigned int)buf->front->buf); ++ REG_DMAC_DTCR(tssi->dma_chan) = MPEG2_TS_PACHAGE_SIZE / 32; ++ REG_DMAC_DCCSR(tssi->dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; ++ buf->fu_num ++; ++ } ++ __tssi_clear_state(); ++ } ++ ++ if (__dmac_channel_transmit_halt_detected(tssi->dma_chan)) { ++ printk("DMA HALT\n"); ++ __dmac_channel_clear_transmit_halt(tssi->dma_chan); ++ } ++ ++ if (__dmac_channel_address_error_detected(tssi->dma_chan)) { ++ printk("DMA ADDR ERROR\n"); ++ __dmac_channel_clear_address_error(tssi->dma_chan); ++ } ++ ++ if (__dmac_channel_descriptor_invalid_detected(tssi->dma_chan)) { ++ printk("DMA DESC INVALID\n"); ++ __dmac_channel_clear_descriptor_invalid(tssi->dma_chan); ++ } ++ ++ if (__dmac_channel_count_terminated_detected(tssi->dma_chan)) { ++ printk("DMA CT\n"); ++ __dmac_channel_clear_count_terminated(tssi->dma_chan); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t tssi_interrupt(int irq, void * dev_id) ++{ ++ __intc_mask_irq(TSSI_IRQ); ++#if 1 ++ if ( REG_TSSI_STAT & TSSI_STAT_OVRN ) ++ { ++ printk("tssi over run occur! %x\n",REG8( TSSI_STAT )); ++ __tssi_clear_state(); ++ printk("clear ! %x\n",REG8( TSSI_STAT )); ++ } ++#endif ++ if ( REG_TSSI_STAT & TSSI_STAT_TRIG ) ++ { ++ printk("tssi trig irq occur! \n"); ++ tssi_read_fifo( dev_id ); ++ } ++ ++ __intc_ack_irq(TSSI_IRQ); ++ __intc_unmask_irq(TSSI_IRQ); ++ return IRQ_HANDLED; ++} ++ ++static ssize_t jz_read(struct file * filp, char * buffer, size_t count, loff_t * ppos) ++{ ++ jz_char_dev_t *adev = (jz_char_dev_t *)filp->private_data; ++ struct jz_tssi_t* tssi = (struct jz_tssi_t*)adev->private; ++ struct jz_tssi_buf_ring_t* ring = tssi->cur_buf; ++ ++ int i; ++ ++ count /= MPEG2_TS_PACHAGE_SIZE; ++ ++ if ( count > ring->fu_num ) ++ count = ring->fu_num; ++ ++ for ( i = 0; i < count; i ++ ) ++ { ++ memcpy( buffer + ( i * MPEG2_TS_PACHAGE_SIZE), ++ ring->rear->buf, MPEG2_TS_PACHAGE_SIZE ); ++ ring->rear->pos = 0; ++ ring->rear = ring->rear->next; ++ } ++ ring->fu_num -= count; ++ return count * MPEG2_TS_PACHAGE_SIZE; ++} ++ ++static int tssi_dma_reinit(int dma_chan, unsigned char *dma_buf, int size) ++{ ++ static unsigned int dma_src_phys_addr, dma_dst_phys_addr; ++ REG_DMAC_DMACKE(0) = 0xff; ++ dma_src_phys_addr = CPHYSADDR(TSSI_FIFO); ++ dma_dst_phys_addr = CPHYSADDR((unsigned int)dma_buf); ++ REG_DMAC_DMACR(dma_chan/HALF_DMA_NUM) = 0; ++ REG_DMAC_DCCSR(dma_chan) = 0; ++ REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_TSSIIN; ++ REG_DMAC_DSAR(dma_chan) = dma_src_phys_addr; ++ REG_DMAC_DTAR(dma_chan) = dma_dst_phys_addr; ++ REG_DMAC_DTCR(dma_chan) = size / 32; ++ REG_DMAC_DCMD(dma_chan) = DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_TIE; ++ REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; ++ REG_DMAC_DMACR(dma_chan/HALF_DMA_NUM) = DMAC_DMACR_DMAE; /* global DMA enable bit */ ++ return 0; ++} ++ ++static int jz_open(struct inode * inode, struct file * filp) ++{ ++ try_module_get(THIS_MODULE); ++ ++ __tssi_soft_reset(); ++ __intc_mask_irq(TSSI_IRQ); ++ tssi_config_filting(); ++ ++ return 0; ++} ++ ++static int jz_release(struct inode * inode, struct file * filp) ++{ ++ __intc_mask_irq(TSSI_IRQ); ++ module_put(THIS_MODULE); ++ return 0; ++} ++ ++static int jz_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ jz_char_dev_t *adev = (jz_char_dev_t *)file->private_data; ++ struct jz_tssi_t* tssi = (struct jz_tssi_t*)adev->private; ++ ++ switch (cmd) ++ { ++ case IOCTL_TSSI_ENABLE : ++ __intc_ack_irq(TSSI_IRQ); ++ __intc_unmask_irq(TSSI_IRQ); ++ __tssi_enable(); ++ print_reg(); ++ ++ break; ++ case IOCTL_TSSI_DISABLE : ++ __tssi_disable(); ++ ++ break; ++ case IOCTL_TSSI_SOFTRESET : ++ __tssi_soft_reset(); ++ ++ break; ++ case IOCTL_TSSI_ENFILTER : ++ __tssi_filter_enable(); ++ break; ++ case IOCTL_TSSI_DEFILTER : ++ __tssi_filter_disable(); ++ break; ++ case IOCTL_TSSI_ADDPID : //add one pid to filter ++ if ( tssi->pid_num < 15 ) ++ { ++ tssi_add_pid(tssi->pid_num, arg); ++ tssi->pid_num ++ ; ++ } ++ break; ++ ++ case IOCTL_TSSI_FLUSHPID : //set all filting pid to false ++ REG_TSSI_PEN = 0x0; ++ REG_TSSI_PID0 = 0x0; ++ REG_TSSI_PID1 = 0x0; ++ REG_TSSI_PID2 = 0x0; ++ REG_TSSI_PID3 = 0x0; ++ REG_TSSI_PID4 = 0x0; ++ REG_TSSI_PID5 = 0x0; ++ REG_TSSI_PID6 = 0x0; ++ REG_TSSI_PID7 = 0x0; ++ break; ++ ++ case IOCTL_TSSI_INIT_DMA: ++ tssi_dma_reinit(tssi->dma_chan, tssi->cur_buf->front->buf, MPEG2_TS_PACHAGE_SIZE); ++ break; ++ case IOCTL_TSSI_DISABLE_DMA: ++ REG_DMAC_DCCSR(tssi->dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ break; ++ } ++ ++ return 0; ++} ++ ++static struct file_operations tssi_fops = { ++ owner: THIS_MODULE, ++ read: jz_read, ++ poll: NULL, ++ fasync: NULL, ++ ioctl: jz_ioctl, ++ open: jz_open, ++ release: jz_release, ++}; ++ ++static int __init jztssi_init_module(void) ++{ ++ int retval; ++ struct jz_tssi_t *tssi = &jz_tssi_g; ++ ++ __cpm_start_tssi(); ++ __cpm_start_dmac(); ++ tssi_buf_init( &jz_tssi_ring_g ); ++ tssi->cur_buf = &jz_tssi_ring_g; ++ tssi->pid_num = 0; ++ retval = request_irq(TSSI_IRQ, tssi_interrupt, IRQF_DISABLED, TSSI_NAME, &jz_tssi_g); ++ ++ if (retval) { ++ printk("unable to get IRQ %d",TSSI_IRQ); ++ return retval; ++ } ++ ++ tssi->dma_chan = jz_request_dma(DMA_ID_TSSI, "tssi", tssi_dma_irq, ++ IRQF_DISABLED, &jz_tssi_g); ++ if ( tssi->dma_chan < 0 ) ++ { ++ printk("MPEG2-TS request irq fail! \n"); ++ return -1; ++ } ++ ++ jz_register_chrdev(TSSI_MINOR, TSSI_NAME, &tssi_fops, &jz_tssi_g); ++ ++ printk("Jz MPEG2-TS interface driver registered %x %d\n",&jz_tssi_g,tssi->dma_chan); ++ return 0; ++} ++ ++static void jztssi_cleanup_module(void) ++{ ++ free_irq(TSSI_IRQ,0); ++ jz_free_dma(jz_tssi_g.dma_chan); ++ tssi_free_buf( &jz_tssi_ring_g ); ++ jz_unregister_chrdev(TSSI_MINOR, TSSI_NAME); ++} ++ ++module_init(jztssi_init_module); ++module_exit(jztssi_cleanup_module); +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/jz_tssi.h linux-2.6.24.7/drivers/char/jzchar/jz_tssi.h +--- linux-2.6.24.7.old/drivers/char/jzchar/jz_tssi.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/jz_tssi.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,76 @@ ++#ifndef __JZ_TSSI_H__ ++#define __JZ_TSSI_H__ ++ ++/* ++ * IOCTL commands ++ */ ++#define IOCTL_TSSI_ENABLE 0x01 ++#define IOCTL_TSSI_DISABLE 0x02 ++#define IOCTL_TSSI_SOFTRESET 0x03 ++#define IOCTL_TSSI_ENFILTER 0x04 ++#define IOCTL_TSSI_DEFILTER 0x05 ++#define IOCTL_TSSI_ADDPID 0x06 ++#define IOCTL_TSSI_FLUSHPID 0x07 ++#define IOCTL_TSSI_INIT_DMA 0x08 ++#define IOCTL_TSSI_DISABLE_DMA 0x09 ++ ++#if 0 ++#define IOCTL_TSSI_SET_CFG 0x06 ++#define IOCTL_TSSI_GET_CFG 0x07 ++#define IOCTL_TSSI_ENIRQ_TRIG 0x08 ++#define IOCTL_TSSI_DEIRQ_TRIG 0x09 ++#define IOCTL_TSSI_ENIRQ_OVRN 0x0a ++#define IOCTL_TSSI_DEIRQ_OVRN 0x0b ++#define IOCTL_TSSI_ENPID0 0x0c ++#define IOCTL_TSSI_DEPID0 0x0d ++#define IOCTL_TSSI_ENPIDN 0x0e ++#define IOCTL_TSSI_DEPIDN 0x0f ++#define IOCTL_TSSI_SETPIDN 0x10 ++#define IOCTL_TSSI_SET_TRIG 0x11 ++#endif ++ ++#define MAX_PID_NUM 15 ++#define MPEG2_TS_PACHAGE_SIZE 19200 ++ ++struct jz_tssi_cfg_t ++{ ++ unsigned char wordorder; ++ unsigned char byteorder; ++ unsigned char dataploa; ++ unsigned char use0; ++ unsigned char clkch; ++ unsigned char mode; ++ unsigned char clkpola; ++ unsigned char frmpola; ++ unsigned char strpola; ++ unsigned char failpola; ++ unsigned char trignum; ++ ++ unsigned short pid; ++ unsigned char pid_index; //0 to 15 ++}; ++ ++struct jz_tssi_buf ++{ ++ unsigned int *buf; ++ unsigned int pos; ++ unsigned int index; ++ struct jz_tssi_buf *next; ++}; ++ ++struct jz_tssi_buf_ring_t ++{ ++ struct jz_tssi_buf *front; ++ struct jz_tssi_buf *rear; ++ unsigned int fu_num; ++}; ++ ++struct jz_tssi_t ++{ ++ struct jz_tssi_cfg_t cur_config; ++ struct jz_tssi_buf_ring_t *cur_buf; ++ struct semaphore tssi_sem; ++ int dma_chan, pid_num; ++}; ++ ++#endif /* __JZ_TSSI_H__ */ +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/jzchars.c linux-2.6.24.7/drivers/char/jzchar/jzchars.c +--- linux-2.6.24.7.old/drivers/char/jzchar/jzchars.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/jzchars.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,158 @@ ++/* ++ * linux/drivers/char/jzchar/jzchars.c ++ * ++ * JzSOC char device family common layer. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "jzchars.h" ++ ++LIST_HEAD(jz_char_devs); ++ ++int jz_register_chrdev(unsigned char minor, const char *name, ++ struct file_operations *fops, void *private) ++{ ++ struct list_head *p; ++ jz_char_dev_t *new; ++ list_for_each(p, &jz_char_devs) { ++ jz_char_dev_t *dev = (jz_char_dev_t *)p; ++ if (minor == dev->dev_minor) ++ return -EBUSY; ++ } ++ new = (jz_char_dev_t *)kmalloc(sizeof(jz_char_dev_t), GFP_KERNEL); ++ new->dev_minor = minor; ++ new->name = (char *)name; ++ new->fops = fops; ++ new->private = private; ++ list_add_tail((struct list_head *)new, &jz_char_devs); ++ return 0; ++} ++ ++int jz_unregister_chrdev(unsigned char minor, const char *name) ++{ ++ struct list_head *p; ++ jz_char_dev_t *dev = NULL; ++ list_for_each(p, &jz_char_devs) { ++ jz_char_dev_t *one = (jz_char_dev_t *)p; ++ if (minor == one->dev_minor) { ++ dev = one; ++ break; ++ } ++ } ++ if (dev == NULL) ++ return -EINVAL; ++ list_del((struct list_head *)dev); ++ kfree(dev); ++ return 0; ++} ++ ++static ssize_t jz_char_read(struct file *, char *, size_t, loff_t *); ++static ssize_t jz_char_write(struct file *, const char *, size_t, loff_t *); ++static int jz_char_open(struct inode *, struct file *); ++static int jz_char_release(struct inode *, struct file *); ++static int jz_char_ioctl(struct inode *, struct file *, ++ unsigned int, unsigned long); ++ ++static struct file_operations jz_char_fops = ++{ ++ read: jz_char_read, ++ write: jz_char_write, ++ ioctl: jz_char_ioctl, ++ open: jz_char_open, ++ release: jz_char_release ++}; ++ ++static int __init jz_char_family_init(void) ++{ ++ printk("JzSOC: char device family.\n"); ++ return register_chrdev(JZ_CHAR_MAJOR, "JzChar", &jz_char_fops); ++} ++ ++static void __exit jz_char_family_exit(void) ++{ ++ printk("JzSOC: exit char device family.\n"); ++ unregister_chrdev(JZ_CHAR_MAJOR, "JzChar"); ++} ++ ++module_init(jz_char_family_init); ++module_exit(jz_char_family_exit); ++ ++static int jz_char_open(struct inode *inode, struct file *filp) ++{ ++ jz_char_dev_t *dev = NULL; ++ unsigned int minor = iminor(inode); //minor extend to 20bit! ++ struct list_head *p; ++ list_for_each(p, &jz_char_devs) { ++ jz_char_dev_t *one = (jz_char_dev_t *)p; ++ if (one->dev_minor == minor) { ++ dev = one; ++ filp->private_data = dev; ++ return dev->fops->open(inode, filp); ++ } ++ } ++ printk("JzChar: No such device\n"); ++ return -EINVAL; ++} ++ ++static int jz_char_release(struct inode *inode, struct file *filp) ++{ ++ jz_char_dev_t *dev = (jz_char_dev_t *)filp->private_data; ++ if (dev->fops->release) ++ return dev->fops->release(inode, filp); ++ return 0; ++} ++ ++static int jz_char_ioctl(struct inode *inode, struct file *filp, ++ unsigned int cmd, unsigned long arg) ++{ ++ jz_char_dev_t *dev = (jz_char_dev_t *)filp->private_data; ++ if (dev->fops->ioctl) ++ return dev->fops->ioctl(inode, filp, cmd, arg); ++ return 0; ++} ++ ++static ssize_t jz_char_read(struct file *filp, char *buf, ++ size_t count, loff_t *ppos) ++{ ++ jz_char_dev_t *dev = (jz_char_dev_t *)filp->private_data; ++ if (dev->fops->read) ++ return dev->fops->read(filp, buf, count, ppos); ++ return 0; ++} ++ ++static ssize_t jz_char_write(struct file *filp, const char *buf, ++ size_t count, loff_t *ppos) ++{ ++ jz_char_dev_t *dev = (jz_char_dev_t *)filp->private_data; ++ if (dev->fops->write) ++ return dev->fops->write(filp, buf, count, ppos); ++ return 0; ++} ++ ++EXPORT_SYMBOL(jz_register_chrdev); ++EXPORT_SYMBOL(jz_unregister_chrdev); +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/jzchars.h linux-2.6.24.7/drivers/char/jzchar/jzchars.h +--- linux-2.6.24.7.old/drivers/char/jzchar/jzchars.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/jzchars.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,47 @@ ++#ifndef __JZ_CHARS_H__ ++#define __JZ_CHARS_H__ ++ ++#include ++#include ++ ++#define JZ_CHAR_MAJOR 238 ++ ++#define UPRT_MINOR 0 // Micro printer ++#define CIM_MINOR 1 // Camera interface module ++#define TPANEL_MINOR 2 // Touchpanel ++#define KEYPAD_MINOR 3 // Keypad ++#define MEMCARD_MINOR 4 // Memory card ++#define MAGCARD_MINOR 5 // Magcard ++#define VFD_MINOR 6 // VFD ++#define POWERFAIL_MINOR 7 // Powerfail ++#define EJTAG_MINOR 8 // EJTAG emulation ++#define REMR0_MINOR 9 // Remote output receive 0 ++#define REMR1_MINOR 10 // Remote output receive 1 ++#define USPI_MINOR 11 // Ultra-speed SPI device ++#define SADC_MINOR 12 // SAR-ADC ++#define SLCD_MINOR 13 // Smart LCD ++ ++// 32 to 47 are reserved for SCC ++#define SCC_MINOR 32 ++// 48 to 63 are reserved for Camera sensor ++#define SENSOR_MINOR 48 ++// 64 to 71 are for EEPROM ++#define EEPROM_MINOR_BASE 64 ++// 72 for OWI ++#define OW_MINOR 72 ++// 73 for TCSM_MINOR ++#define TCSM_MINOR 73 ++ ++typedef struct { ++ struct list_head list; ++ char *name; ++ struct file_operations *fops; ++ void *private; ++ unsigned short dev_minor; ++} jz_char_dev_t; ++ ++extern int jz_register_chrdev(unsigned char minor, const char *name, ++ struct file_operations *fops, void * private); ++extern int jz_unregister_chrdev(unsigned char minor, const char *name); ++ ++#endif /* __JZ_CHARS_H__ */ +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/poweroff.c linux-2.6.24.7/drivers/char/jzchar/poweroff.c +--- linux-2.6.24.7.old/drivers/char/jzchar/poweroff.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/poweroff.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,383 @@ ++/* ++ * linux/drivers/char/jzchar/poweroff.c ++ * ++ * Power off handling. ++ * ++ * Copyright (C) 2005-2007 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "jzchars.h" ++ ++MODULE_AUTHOR("Jianli Wei "); ++MODULE_DESCRIPTION("Poweroff handling"); ++MODULE_LICENSE("GPL"); ++ ++#undef DEBUG ++//#define DEBUG ++#ifdef DEBUG ++#define dprintk(x...) printk(x) ++#else ++#define dprintk(x...) ++#endif ++ ++//#define USE_SUSPEND_HOTPLUG ++ ++#ifdef CONFIG_SOC_JZ4730 ++#define GPIO_PW_I 97 ++#define GPIO_PW_O 66 ++#define POWEROFF_PIN_DOWN 1 ++#define SET_POWEROFF_PIN_AS_IRQ __gpio_as_irq_rise_edge(POWEROFF_PIN) ++#define DO_SHUTDOWN_SYSTEM __gpio_clear_pin(GPIO_PW_O) ++#define DO_SUSPEND jz_pm_suspend() ++ ++#define GPIO_DISP_OFF_N 93 ++#define __lcd_set_backlight_level(n) \ ++do { \ ++ REG_PWM_DUT(0) = n; \ ++ REG_PWM_PER(0) = 7; \ ++ REG_PWM_CTR(0) = 0x81; \ ++} while (0) ++#define __lcd_close_backlight() \ ++do { \ ++ __lcd_set_backlight_level(0); \ ++} while (0) ++#endif ++ ++#ifdef CONFIG_SOC_JZ4740 ++#define GPIO_PW_I 125 ++#define POWEROFF_PIN_DOWN 0 ++#define SET_POWEROFF_PIN_AS_IRQ __gpio_as_irq_fall_edge(POWEROFF_PIN) ++#define DO_SHUTDOWN_SYSTEM jz_pm_hibernate() ++#define DO_SUSPEND { \ ++ jz_pm_sleep();\ ++ suspend_flag = 0;\ ++ SET_POWEROFF_PIN_AS_IRQ;\ ++ } ++ ++#define GPIO_DISP_OFF_N 118 ++#define GPIO_PWM 123 ++#define __lcd_close_backlight() \ ++do { \ ++__gpio_as_output(GPIO_PWM); \ ++__gpio_clear_pin(GPIO_PWM); \ ++} while (0) ++#endif ++ ++#ifdef CONFIG_SOC_JZ4750 ++#define GPIO_PW_I GPIO_WAKEUP ++#define POWEROFF_PIN_DOWN 0 ++#define SET_POWEROFF_PIN_AS_IRQ __gpio_as_irq_fall_edge(POWEROFF_PIN) ++#define DO_SHUTDOWN_SYSTEM jz_pm_hibernate() ++#define DO_SUSPEND { \ ++ jz_pm_sleep();\ ++ suspend_flag = 0;\ ++ SET_POWEROFF_PIN_AS_IRQ;\ ++ } ++#endif ++ ++#define POWEROFF_PIN GPIO_PW_I ++#define POWEROFF_IRQ (IRQ_GPIO_0 + POWEROFF_PIN) ++ ++#define POWEROFF_PERIOD 1000 /* unit: ms */ ++#define POWEROFF_DELAY 100 /* unit: ms */ ++ ++static struct timer_list poweroff_timer; ++static struct timer_list poweroff_delaytimer; ++static struct work_struct suspend_work; ++ ++static int poweroff_flag = 0; ++static int suspend_flag = 0; ++static int num_seconds = 0; ++ ++#ifdef CONFIG_JZ_UDC_HOTPLUG ++extern int jz_udc_active; ++#endif ++ ++extern void jz_pm_suspend(void); ++extern int jz_pm_hibernate(void); ++extern int jz_pm_sleep(void); ++ ++static void poweroff_timer_routine(unsigned long dummy) ++{ ++ if (__gpio_get_pin(POWEROFF_PIN) == POWEROFF_PIN_DOWN) { ++ if (++num_seconds > 3) ++ { ++ printk("\nShutdown system now ..\n"); ++ ++#ifndef USE_SUSPEND_HOTPLUG ++ /* Turn off LCD to inform user that the system is shutting down. ++ * But the information of shutting down system will be shown ++ * by userspace program if hotplug is used. ++ */ ++ __lcd_close_backlight(); ++#endif ++ ++ /* ++ * Wait until the power key is up, or the system will reset with ++ * power key down after entering hibernate. ++ */ ++ while(__gpio_get_pin(POWEROFF_PIN)==POWEROFF_PIN_DOWN); ++ ++ poweroff_flag = 1; ++ schedule_work(&suspend_work); /* inform user to poweroff */ ++ } ++ else { ++ del_timer(&poweroff_timer); ++ init_timer(&poweroff_timer); ++ poweroff_timer.expires = jiffies + POWEROFF_PERIOD/10; ++ poweroff_timer.data = 0; ++ poweroff_timer.function = poweroff_timer_routine; ++ add_timer(&poweroff_timer); ++ } ++ } ++ else ++ { ++ printk("\nSuspend system now ..\n"); ++ num_seconds = 0; ++ suspend_flag = 1; ++ poweroff_flag = 0; ++ schedule_work(&suspend_work); /* we are entering suspend */ ++ } ++} ++ ++static void poweroff_delaytimer_routine(unsigned long dummy) ++{ ++ __gpio_as_input(POWEROFF_PIN); ++ if (__gpio_get_pin(POWEROFF_PIN)==POWEROFF_PIN_DOWN) { ++ if (suspend_flag) { ++ suspend_flag = 0; ++ del_timer(&poweroff_delaytimer); ++ SET_POWEROFF_PIN_AS_IRQ; ++ __gpio_unmask_irq(POWEROFF_PIN); ++ return; ++ } ++ del_timer(&poweroff_delaytimer); ++ del_timer(&poweroff_timer); ++ init_timer(&poweroff_timer); ++ poweroff_timer.expires = jiffies + POWEROFF_PERIOD/100; ++ poweroff_timer.data = 0; ++ poweroff_timer.function = poweroff_timer_routine; ++ add_timer(&poweroff_timer); ++ } ++ else { ++ del_timer(&poweroff_delaytimer); ++ SET_POWEROFF_PIN_AS_IRQ; ++ __gpio_unmask_irq(POWEROFF_PIN); ++ ++ printk("This is a dummy key\n"); ++ } ++} ++ ++/* ++ * Poweroff pin interrupt handler ++ */ ++static irqreturn_t poweroff_irq(int irq, void *dev_id) ++{ ++ __gpio_ack_irq(POWEROFF_PIN); ++ __gpio_mask_irq(POWEROFF_PIN); ++ __gpio_as_input(POWEROFF_PIN); ++#ifdef CONFIG_JZ_UDC_HOTPLUG ++ if (__gpio_get_pin(POWEROFF_PIN)==POWEROFF_PIN_DOWN && jz_udc_active == 0){ ++#else ++ if (__gpio_get_pin(POWEROFF_PIN)==POWEROFF_PIN_DOWN){ ++#endif ++ del_timer(&poweroff_delaytimer); ++ init_timer(&poweroff_delaytimer); ++ poweroff_delaytimer.expires = jiffies + POWEROFF_DELAY/10; ++ poweroff_delaytimer.data = 0; ++ poweroff_delaytimer.function = poweroff_delaytimer_routine; ++ add_timer(&poweroff_delaytimer); ++ } ++ else { ++ ++/* ++ * If it reaches here without jz_udc_active == 0, then it indicates POWEROFF_PIN was ++ * changed to WAKEUP key in pm.c for hand is not able to rise up so quickly, so the ++ * irq handler entered because of WAKEUP key not POWEROFF_PIN. ++ */ ++ ++#ifdef CONFIG_JZ_UDC_HOTPLUG ++ if (jz_udc_active == 1) ++ printk("\nUSB is working; Operation is denied\n"); ++#endif ++ SET_POWEROFF_PIN_AS_IRQ; ++ __gpio_unmask_irq(POWEROFF_PIN); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++#ifdef CONFIG_PM ++ ++static struct pm_dev *poweroff_dev; ++ ++static int poweroff_suspend(int *poweroff , int state) ++{ ++ suspend_flag = 1; ++ poweroff_flag = 0; ++ ++ return 0; ++} ++ ++static int poweroff_resume(int *poweroff) ++{ ++ suspend_flag = 0; ++ SET_POWEROFF_PIN_AS_IRQ; ++ __gpio_unmask_irq(POWEROFF_PIN); ++ ++ return 0; ++} ++ ++static int poweroff_pm_callback(struct pm_dev *pm_dev, pm_request_t rqst, void *data) ++{ ++ int ret; ++ int *poweroff_info = pm_dev->data; ++ ++ if (!poweroff_info) ++ return -EINVAL; ++ ++ switch (rqst) { ++ case PM_SUSPEND: ++ ret = poweroff_suspend(poweroff_info, (int)data); ++ break; ++ case PM_RESUME: ++ ret = poweroff_resume(poweroff_info); ++ break; ++ ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++#endif /* CONFIG_PM */ ++ ++#ifdef USE_SUSPEND_HOTPLUG ++static void run_sbin_hotplug(int state) ++{ ++ int i; ++ char *argv[3], *envp[8]; ++ char media[64], slotnum[16]; ++ if (!uevent_helper[0]) ++ return; ++ ++ i = 0; ++ argv[i++] = uevent_helper; ++ //argv[i++] = "home/lhhuang/hotplug"; ++ ++ if ( poweroff_flag == 1 ) ++ argv[i++] = "poweroff"; ++ else ++ argv[i++] = "suspend"; ++ ++ argv[i] = 0; ++ ++ /* minimal command environment */ ++ i = 0; ++ envp[i++] = "HOME=/"; ++ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; ++ ++ /* other stuff we want to pass to /sbin/hotplug */ ++ sprintf(slotnum, "SLOT=0"); ++ ++ if ( poweroff_flag == 1 ) ++ sprintf(media, "MEDIA=poweroff"); ++ else ++ sprintf(media, "MEDIA=suspend"); ++ ++ envp[i++] = slotnum; ++ envp[i++] = media; ++ ++ if (state) ++ envp[i++] = "ACTION=enter"; ++ else ++ envp[i++] = "ACTION=exit"; ++ ++ envp[i] = 0; ++ ++ dprintk("SUSPEND: hotplug path=%s state=%d\n", argv[0], state); ++ ++ SET_POWEROFF_PIN_AS_IRQ; ++ __gpio_unmask_irq(POWEROFF_PIN); /* set it because call hotplug with call_usermodehelper() \ ++ might failed, especially when using nfsroot */ ++ ++ call_usermodehelper (argv [0], argv, envp, -1); ++} ++#endif ++ ++static void suspend_handler(struct work_struct *work) ++{ ++#ifdef USE_SUSPEND_HOTPLUG ++ int state = 1; ++ run_sbin_hotplug(state); ++#else ++ if (poweroff_flag) { ++ dprintk("DO_SHUTDOWN_SYSTEM\n"); ++ DO_SHUTDOWN_SYSTEM; ++ } else { ++ dprintk("DO_SUSPEND\n"); ++ DO_SUSPEND; ++ } ++#endif ++} ++ ++static int __init poweroff_init(void) ++{ ++ int retval; ++ ++ retval = request_irq(POWEROFF_IRQ, poweroff_irq, ++ IRQF_DISABLED, "poweroff", NULL); ++ ++ SET_POWEROFF_PIN_AS_IRQ; ++ ++ if (retval) { ++ printk("Could not get poweroff irq %d\n", POWEROFF_IRQ); ++ return retval; ++ } ++ ++#ifdef CONFIG_PM ++ poweroff_dev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, poweroff_pm_callback); ++ if (poweroff_dev) { ++ poweroff_dev->data = &poweroff_dev; ++ } ++#endif ++ ++ INIT_WORK(&suspend_work, suspend_handler); ++ ++ return 0; ++} ++ ++static void __exit poweroff_exit(void) ++{ ++ free_irq(POWEROFF_IRQ, NULL); ++} ++ ++module_init(poweroff_init); ++module_exit(poweroff_exit); ++ +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/sadc.c linux-2.6.24.7/drivers/char/jzchar/sadc.c +--- linux-2.6.24.7.old/drivers/char/jzchar/sadc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/sadc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,580 @@ ++/* ++ * linux/drivers/char/jzchar/sadc.c ++ * ++ * SAR-ADC driver for JZ4740. ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "jzchars.h" ++#include "jz_ts.h" ++ ++MODULE_AUTHOR("Jianli Wei"); ++MODULE_DESCRIPTION("JZ4740 SADC driver"); ++MODULE_LICENSE("GPL"); ++ ++#define SADC_NAME "sadc" ++static DECLARE_WAIT_QUEUE_HEAD (sadc_wait_queue); ++ ++struct sadc_device { ++ int mode; ++ int dma_chan; ++ char *ts_buf; ++ char *pbat_buf; ++}; ++ ++static struct sadc_device *sadc_dev; ++ ++static int samples = 3; /* we sample 3 every time */ ++static int first_time = 0; ++static unsigned long last_x, last_y, last_p; ++ ++typedef struct datasource { ++ u16 xbuf; ++ u16 ybuf; ++ u16 zbuf; ++}datasource_t; ++ ++static datasource_t data_s; ++static unsigned int p; ++static unsigned int old_x, old_y; ++extern unsigned int (*codec_read_battery)(void); ++ ++/* ++ * set adc clock to 12MHz/div. A/D works at freq between 500KHz to 6MHz. ++ */ ++static void sadc_init_clock(int div) ++{ ++ if (div < 2) div = 2; ++ if (div > 23) div = 23; ++#if defined(CONFIG_SOC_JZ4740) ++ REG_SADC_CFG &= ~SADC_CFG_CLKDIV_MASK; ++ REG_SADC_CFG |= (div - 1) << SADC_CFG_CLKDIV_BIT; ++#endif ++#if defined(CONFIG_SOC_JZ4750) ++ REG_SADC_ADCLK &= ~SADC_ADCLK_CLKDIV_MASK; ++ REG_SADC_ADCLK |= (div - 1) << SADC_ADCLK_CLKDIV_BIT; ++ REG_SADC_ADCLK &= ~SADC_ADCLK_CLKDIV_BIT; ++ REG_SADC_ADCLK |= 39 << SADC_ADCLK_CLKDIV_10_BIT; /* if div ==3,here is 39 */ ++#endif ++} ++ ++void start_sadcin(void) ++{ ++ REG_SADC_CTRL &= ~SADC_CTRL_SRDYM; /* enable interrupt */ ++ REG_SADC_ENA |= SADC_ENA_SADCINEN; ++} ++ ++void start_pbat_adc(void) ++{ ++ REG_SADC_CFG |= SADC_CFG_PBAT_HIGH ; /* full baterry voltage >= 2.5V */ ++// REG_SADC_CFG |= SADC_CFG_PBAT_LOW; /* full baterry voltage < 2.5V */ ++ ++ REG_SADC_ENA |= SADC_ENA_PBATEN; /* Enable pbat adc */ ++} ++ ++void start_ts_adc(void) ++{ ++ REG_SADC_SAMETIME = 10; /* about 0.1 ms,you can change it */ ++ REG_SADC_WAITTIME = 2; /* about 0.02 ms,you can change it */ ++ ++ REG_SADC_CFG &= ~(SADC_CFG_TS_DMA | SADC_CFG_XYZ_MASK | SADC_CFG_SNUM_MASK); ++ REG_SADC_CFG |= (SADC_CFG_EXIN | SADC_CFG_XYZ | SADC_CFG_SNUM_3); ++ REG_SADC_CTRL |= (SADC_CTRL_TSRDYM|SADC_CTRL_PBATRDYM|SADC_CTRL_PENUM |SADC_CTRL_SRDYM); ++ REG_SADC_CTRL &= ~SADC_CTRL_PENDM; ++ REG_SADC_ENA |= SADC_ENA_TSEN; ++} ++ ++static int jz4740_adc_read(struct jz_ts_t *ts) ++{ ++ struct datasource *ds = &data_s; ++ u32 xybuf,z; ++ ++ if (!(REG_SADC_STATE & SADC_STATE_TSRDY)) { ++ /* sleep */ ++ REG_SADC_CTRL &= ~SADC_CTRL_TSRDYM; ++ ts->sleeping = 1; ++ sleep_on(&sadc_wait_queue); ++ } ++ ts->sleeping = 0; ++ ++ xybuf = REG_SADC_TSDAT; ++ ds->xbuf = (xybuf>>16) & 0x0fff; ++ ds->ybuf = (xybuf)& 0x0fff; ++ z = REG_SADC_TSDAT; ++ ds->zbuf = z& 0x0fff; ++ REG_SADC_STATE &= ~SADC_STATE_TSRDY; ++ return 0; ++} ++ ++/*------------------------------------------------------------ ++ * Read the battery voltage ++ */ ++unsigned int jz4740_read_battery(void) ++{ ++ unsigned int v; ++ unsigned int timeout = 0x3ff; ++ u16 pbat; ++ ++ if(!(REG_SADC_STATE & SADC_STATE_PBATRDY) ==1) ++ start_pbat_adc(); ++ ++ while(!(REG_SADC_STATE & SADC_STATE_PBATRDY) && --timeout) ++ ; ++ ++ pbat = REG_SADC_BATDAT; ++ v = pbat & 0x0fff; ++ REG_SADC_STATE = SADC_STATE_PBATRDY; ++ return v; ++} ++ ++/*------------------ Calibrate samples -------------------*/ ++ ++#define DIFF(a,b) (((a)>(b))?((a)-(b)):((b)-(a))) ++#define MIN(a,b) (((a)<(b))?(a):(b)) ++ ++#if 0 ++#define XM 36 /* XM and YM may be changed for your screen */ ++#define YM 20 ++static int calibrate_samples(void *xbuf, void *ybuf, void *pbuf, int count) ++{ ++ unsigned long usd0,usd1,usd2; ++ int xMaxError = XM,yMaxError = YM; ++ int x_valid = 0,y_valid = 0,valid = 0; ++ unsigned long x_cal = 0, y_cal = 0, p_cal = 0; ++ unsigned long *xp = (unsigned long *)xbuf; ++ unsigned long *yp = (unsigned long *)ybuf; ++ unsigned long *pp = (unsigned long *)pbuf; ++ ++ usd0 = (xp[0] > xp[1]) ? (xp[0] - xp[1]) : (xp[1] - xp[0]); ++ usd1 = (xp[1] > xp[2]) ? (xp[1] - xp[2]) : (xp[2] - xp[1]); ++ usd2 = (xp[2] > xp[0]) ? (xp[2] - xp[0]) : (xp[0] - xp[2]); ++ ++ if ( usd0 < usd1) ++ x_cal = xp[0] + ((usd2 < usd0) ? xp[2] : xp[1]); ++ else ++ x_cal= xp[2] + ((usd2 < usd1) ? xp[0] : xp[1]); ++ x_cal >>= 1; ++ ++ if ( (usd0 < xMaxError) && (usd1 < xMaxError) && (usd2 < xMaxError) ) ++ x_valid = 1; ++ ++ usd0 = (yp[0] > yp[1]) ? (yp[0] - yp[1]) : (yp[1] - yp[0]); ++ usd1 = (yp[1] > yp[2]) ? (yp[1] - yp[2]) : (yp[2] - yp[1]); ++ usd2 = (yp[2] > yp[0]) ? (yp[2] - yp[0]) : (yp[0] - yp[2]); ++ ++ if ( usd0 < usd1) ++ y_cal = yp[0] + ((usd2 < usd0) ? yp[2] : yp[1]); ++ else ++ y_cal = yp[2] + ((usd2 < usd1) ? yp[0] : yp[1]); ++ ++ y_cal >>= 1; ++ ++ if ( (usd0 < yMaxError) && (usd1 < yMaxError) && (usd2 < yMaxError) ) ++ y_valid = 1; ++ ++ if( x_valid && y_valid) ++ valid = 1; ++ ++ usd0 = (pp[0] > pp[1]) ? (pp[0] - pp[1]) : (pp[1] - pp[0]); ++ usd1 = (pp[1] > pp[2]) ? (pp[1] - pp[2]) : (pp[2] - pp[1]); ++ usd2 = (pp[2] > pp[0]) ? (pp[2] - pp[0]) : (pp[0] - pp[2]); ++ ++ if ( usd0 < usd1) ++ p_cal = pp[0] + ((usd2 < usd0) ? pp[2] : pp[1]); ++ else ++ p_cal= pp[2] + ((usd2 < usd1) ? pp[0] : pp[1]); ++ ++ p_cal >>= 1; ++ ++ if (first_time) { ++ first_time = 0; ++ last_x = x_cal; ++ last_y = y_cal; ++ last_p = p_cal; ++ } ++ else{ ++ if ((DIFF(x_cal, last_x) > 50) || ++ (DIFF(y_cal, last_y) > 50)) ++ valid = 0; ++ else ++ valid = 1; ++ } ++ *xp = last_x = x_cal; ++ *yp = last_y = y_cal; ++ *pp = last_p = p_cal; ++ ++ return valid; ++} ++#endif ++ ++static int calibrate_samples(void *xbuf, void *ybuf, void *pbuf, int count) ++{ ++ unsigned long *xp = (unsigned long *)xbuf; ++ unsigned long *yp = (unsigned long *)ybuf; ++ unsigned long *pp = (unsigned long *)pbuf; ++ unsigned long x_cal = 0, y_cal = 0, p_cal = 0; ++ int i; ++ int valid = 1; ++ ++ /* calculate the average of the rest */ ++ for (i = 0; i < count; i++) { ++ x_cal += xp[i]; ++ y_cal += yp[i]; ++ p_cal += pp[i]; ++ } ++ x_cal /= count; ++ y_cal /= count; ++ p_cal /= count; ++ ++ if (first_time) { ++ first_time = 0; ++ last_x = x_cal; ++ last_y = y_cal; ++ last_p = p_cal; ++ } ++ else { ++ if ((DIFF(x_cal, last_x) > 50) || ++ (DIFF(y_cal, last_y) > 50)) ++ valid = 0; ++ else ++ valid = 1; ++ } ++ ++ *xp = last_x = x_cal; ++ *yp = last_y = y_cal; ++ *pp = last_p = p_cal; ++ ++ return valid; ++} ++ ++#define TSMAXX 945 ++#define TSMAXY 830 ++#define TSMINX 90 ++#define TSMINY 105 ++ ++#define SCREEN_X 480 ++#define SCREEN_Y 272 ++ ++static unsigned long transform_to_screen_x(struct jz_ts_t *ts, unsigned long x ) ++{ ++ ++ if (ts->minx) ++ { ++ if (x < ts->minx) x = ts->minx; ++ if (x > ts->maxx) x = ts->maxx; ++ ++ return (x - ts->minx) * SCREEN_X / (ts->maxx - ts->minx); ++ } ++ else ++ { ++ if (x < TSMINX) x = TSMINX; ++ if (x > TSMAXX) x = TSMAXX; ++ ++ return (x - TSMINX) * SCREEN_X / (TSMAXX - TSMINX); ++ } ++} ++ ++static unsigned long transform_to_screen_y(struct jz_ts_t *ts, unsigned long y) ++{ ++ if (ts->minx) ++ { ++ if (y < ts->minx) y = ts->miny; ++ if (y > ts->maxx) y = ts->maxy; ++ ++ return (y - ts->miny) * SCREEN_Y / (ts->maxy - ts->miny); ++ } ++ else ++ { ++ if (y < TSMINX) y = TSMINY; ++ if (y > TSMAXX) y = TSMAXY; ++ ++ return (y - TSMINY) * SCREEN_Y / (TSMAXY - TSMINY); ++ } ++} ++ ++/* ++ * File operations ++ */ ++static int sadc_open(struct inode *inode, struct file *filp); ++static int sadc_release(struct inode *inode, struct file *filp); ++static ssize_t sadc_read(struct file *filp, char *buf, size_t size, loff_t *l); ++static ssize_t sadc_write(struct file *filp, const char *buf, size_t size, loff_t *l); ++static int sadc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); ++ ++static struct file_operations sadc_fops = ++{ ++ open: sadc_open, ++ release: sadc_release, ++ read: sadc_read, ++ write: sadc_write, ++ ioctl: sadc_ioctl ++}; ++ ++static int sadc_open(struct inode *inode, struct file *filp) ++{ ++ try_module_get(THIS_MODULE); ++ return 0; ++} ++ ++static int sadc_release(struct inode *inode, struct file *filp) ++{ ++ module_put(THIS_MODULE); ++ return 0; ++} ++ ++static ssize_t sadc_read(struct file *filp, char *buf, size_t size, loff_t *l) ++{ ++ return size; ++} ++ ++static ssize_t sadc_write(struct file *filp, const char *buf, size_t size, loff_t *l) ++{ ++ return size; ++} ++ ++static int sadc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ default: ++ printk("Not supported command: 0x%x\n", cmd); ++ return -EINVAL; ++ break; ++ } ++ return 0; ++} ++ ++/*------------------ Common routines -------------------*/ ++ ++void ts_enable_irq(void) ++{ ++ REG_SADC_CTRL &= ~SADC_CTRL_PENDM; ++} ++ ++void ts_disable_irq(void) ++{ ++ REG_SADC_CTRL |= (SADC_CTRL_PENDM | SADC_CTRL_PENUM); ++} ++ ++void ts_free_irq(struct jz_ts_t *ts) ++{ ++ free_irq(ts->pendown_irq, ts); ++} ++ ++void ts_data_ready(void) ++{ ++ REG_SADC_CTRL |= SADC_CTRL_TSRDYM; ++ wake_up(&sadc_wait_queue); ++} ++ ++/* ++ * Interrupt handler ++ */ ++void ts_irq_callback(void) ++{ ++ u32 state; ++ ++ state = REG_SADC_STATE; ++ if (!(REG_SADC_CTRL&SADC_CTRL_PENDM)&&(REG_SADC_STATE & SADC_STATE_PEND)) { ++ REG_SADC_STATE = SADC_STATE_PEND; ++ REG_SADC_STATE = SADC_STATE_PENU; ++ REG_SADC_CTRL |= SADC_CTRL_PENDM; ++ REG_SADC_CTRL &= ~SADC_CTRL_PENUM; ++ p = 1; ++ } ++ ++ if (!(REG_SADC_CTRL&SADC_CTRL_PENUM)&&(REG_SADC_STATE & SADC_STATE_PENU)) { ++ REG_SADC_STATE = SADC_STATE_PENU; ++ REG_SADC_CTRL |= SADC_CTRL_PENUM; ++ REG_SADC_CTRL &= ~SADC_CTRL_PENDM; ++ p = 0; ++ } ++ ++ first_time = 1; // first time to acquire sample ++} ++ ++int PenIsDown(void) ++{ ++ return p; ++} ++ ++int ts_request_irq(u32 *irq, ++ irqreturn_t (*handler)(int, void *), ++ const char *devname, ++ void *dev_id) ++{ ++ int ret; ++ ++ /* return the irq number */ ++ *irq = IRQ_SADC; ++ ts_disable_irq(); ++ /* interrupt mode */ ++ ret = request_irq(IRQ_SADC, handler, IRQF_DISABLED, ++ devname, dev_id); ++ if(ret) ++ printk("failed irq \n"); ++ ++ start_ts_adc(); ++ return ret; ++} ++ ++/* ++ * Acquire Raw pen coodinate data and compute touch screen ++ * pressure resistance. Hold spinlock when calling. ++ */ ++int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event) ++{ ++ unsigned int x_raw[8], y_raw[8], p_raw[8]; ++ int valid, i; ++ unsigned int avl_x, avl_y, diff_x, diff_y; ++ struct datasource *ds = &data_s; ++ avl_x = avl_y = 0; ++ ++ for (i = 0; i < samples; i++) { ++ if (jz4740_adc_read(ts)) { ++ return 0; ++ } ++ ++ x_raw[i] = ds->ybuf; ++ y_raw[i] = ds->xbuf; ++ p_raw[i] = ds->zbuf; ++ avl_x += x_raw[i]; ++ avl_y += y_raw[i]; ++#if 0 ++ printk("x_raw=%x y_raw=%x z_raw=%x\n",x_raw[i],y_raw[i],p_raw[i]); ++#endif ++ } ++ ++ avl_x /= samples; ++ avl_y /= samples; ++#define MAX_DELTA 20 ++ valid = 1; ++ ++ for (i = 1; i < samples; i++) ++ { ++ if ((100 * DIFF(x_raw[i],x_raw[i-1])/MIN(x_raw[i],x_raw[i-1])) > MAX_DELTA) { ++ valid = 0; ++ break; ++ } ++ ++ if ((100 * DIFF(y_raw[i],y_raw[i-1])/MIN(y_raw[i],y_raw[i-1])) > MAX_DELTA) { ++ valid = 0; ++ break; ++ } ++ ++ if ((100 * DIFF(p_raw[i],p_raw[i-1])/MIN(p_raw[i],p_raw[i-1])) > MAX_DELTA) { ++ valid = 0; ++ break; ++ } ++ } ++ ++ if (valid) { ++ if (ts->first_read) { ++ ts->first_read = 0; ++ old_x = avl_x; ++ old_y = avl_y; ++ } ++ diff_x = DIFF(old_x, avl_x); ++ diff_y = DIFF(old_y, avl_y); ++ if (diff_x < 100 && diff_y < 100) { ++ old_x = avl_x; ++ old_y = avl_y; ++ } else ++ valid = 0; ++ } ++ if (valid) { ++ valid = calibrate_samples(x_raw, y_raw, p_raw, samples); ++ } ++ ++ if (valid) { ++ unsigned int x_scr, y_scr; ++ ++ if(ts->filter) { ++ x_scr = transform_to_screen_x(ts, x_raw[0]); ++ y_scr = transform_to_screen_y(ts, y_raw[0]); ++ ++ if (ts->prints) ++ printk("x_raw=%d y_raw=%d x_transform=%d y_transform=%d\n", x_raw[0], y_raw[0], x_scr, y_scr); ++ } ++ else { ++ x_scr = x_raw[0]; ++ y_scr = y_raw[0]; ++ ++ if (ts->prints) ++ printk("x_raw=%d y_raw=%d \n", x_raw[0], y_raw[0]); ++ } ++ ++ event->x = x_scr; ++ event->y = y_scr; ++ event->pressure = (u16)p_raw[0]; ++ event->status = PENDOWN; ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * Module init and exit ++ */ ++static int __init sadc_init(void) ++{ ++ struct sadc_device *dev; ++ int ret; ++ ++ /* allocate device */ ++ dev = kmalloc(sizeof(struct sadc_device), GFP_KERNEL); ++ if (!dev) return -ENOMEM; ++ ++ sadc_dev = dev; ++ ret = jz_register_chrdev(SADC_MINOR, SADC_NAME, &sadc_fops, dev); ++ if (ret < 0) { ++ kfree(dev); ++ return ret; ++ } ++ ++ codec_read_battery = jz4740_read_battery; ++ sadc_init_clock(3); ++ ++ printk("JZ4740 SAR-ADC driver registered\n"); ++ return 0; ++} ++ ++static void __exit sadc_exit(void) ++{ ++ struct sadc_device *dev = sadc_dev; ++ ++ free_irq(IRQ_SADC, dev); ++ jz_unregister_chrdev(SADC_MINOR, SADC_NAME); ++ kfree(dev); ++} ++ ++module_init(sadc_init); ++module_exit(sadc_exit); +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/sensor.c linux-2.6.24.7/drivers/char/jzchar/sensor.c +--- linux-2.6.24.7.old/drivers/char/jzchar/sensor.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/sensor.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,182 @@ ++/* ++ * linux/drivers/char/jzchar/sensor.c ++ * ++ * Common CMOS Camera Sensor Driver ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "jzchars.h" ++ ++MODULE_AUTHOR("Jianli Wei"); ++MODULE_DESCRIPTION("Common CMOS Camera Sensor Driver"); ++MODULE_LICENSE("GPL"); ++ ++/* ++ * ioctl commands ++ */ ++#define IOCTL_SET_ADDR 0 /* set i2c address */ ++#define IOCTL_SET_CLK 1 /* set i2c clock */ ++#define IOCTL_WRITE_REG 2 /* write sensor register */ ++#define IOCTL_READ_REG 3 /* read sensor register */ ++ ++/* ++ * i2c related ++ */ ++static unsigned int i2c_addr = 0x42; ++static unsigned int i2c_clk = 100000; ++ ++static void write_reg(u8 reg, u8 val) ++{ ++ i2c_open(); ++ i2c_setclk(i2c_clk); ++ i2c_write((i2c_addr >> 1), &val, reg, 1); ++ i2c_close(); ++} ++ ++static u8 read_reg(u8 reg) ++{ ++ u8 val; ++ ++ i2c_open(); ++ i2c_setclk(i2c_clk); ++ i2c_read((i2c_addr >> 1), &val, reg, 1); ++ i2c_close(); ++ return val; ++} ++ ++/* ++ * fops routines ++ */ ++ ++static int sensor_open(struct inode *inode, struct file *filp); ++static int sensor_release(struct inode *inode, struct file *filp); ++static ssize_t sensor_read(struct file *filp, char *buf, size_t size, loff_t *l); ++static ssize_t sensor_write(struct file *filp, const char *buf, size_t size, loff_t *l); ++static int sensor_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); ++ ++static struct file_operations sensor_fops = ++{ ++ open: sensor_open, ++ release: sensor_release, ++ read: sensor_read, ++ write: sensor_write, ++ ioctl: sensor_ioctl, ++}; ++ ++static int sensor_open(struct inode *inode, struct file *filp) ++{ ++ try_module_get(THIS_MODULE); ++ return 0; ++} ++ ++static int sensor_release(struct inode *inode, struct file *filp) ++{ ++ module_put(THIS_MODULE); ++ return 0; ++} ++ ++static ssize_t sensor_read(struct file *filp, char *buf, size_t size, loff_t *l) ++{ ++ printk("sensor: read is not implemented\n"); ++ return -1; ++} ++ ++static ssize_t sensor_write(struct file *filp, const char *buf, size_t size, loff_t *l) ++{ ++ printk("sensor: write is not implemented\n"); ++ return -1; ++} ++ ++static int sensor_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ int ret = 0; ++ ++ switch (cmd) { ++ case IOCTL_SET_ADDR: ++ if (copy_from_user(&i2c_addr, (void *)arg, 4)) ++ return -EFAULT; ++ break; ++ case IOCTL_SET_CLK: ++ if (copy_from_user(&i2c_clk, (void *)arg, 4)) ++ return -EFAULT; ++ break; ++ case IOCTL_WRITE_REG: ++ { ++ u8 regval[2]; ++ ++ if (copy_from_user(regval, (void *)arg, 2)) ++ return -EFAULT; ++ ++ write_reg(regval[0], regval[1]); ++ break; ++ } ++ case IOCTL_READ_REG: ++ { ++ u8 reg, val; ++ ++ if (copy_from_user(®, (void *)arg, 1)) ++ return -EFAULT; ++ ++ val = read_reg(reg); ++ ++ if (copy_to_user((void *)(arg + 1), &val, 1)) ++ return -EFAULT; ++ break; ++ } ++ default: ++ printk("Not supported command: 0x%x\n", cmd); ++ return -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++/* ++ * Module init and exit ++ */ ++ ++static int __init sensor_init(void) ++{ ++ int ret; ++ ++ ret = jz_register_chrdev(SENSOR_MINOR, "sensor", &sensor_fops, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ printk("Ingenic CMOS camera sensor driver registered\n"); ++ ++ return 0; ++} ++ ++static void __exit sensor_exit(void) ++{ ++ jz_unregister_chrdev(SENSOR_MINOR, "sensor"); ++} ++ ++module_init(sensor_init); ++module_exit(sensor_exit); +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/tcsm.c linux-2.6.24.7/drivers/char/jzchar/tcsm.c +--- linux-2.6.24.7.old/drivers/char/jzchar/tcsm.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/tcsm.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,123 @@ ++/* ++ * linux/drivers/char/jzchar/tcsm.c ++ * ++ * Virtual device driver with tricky appoach to manage TCSM ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "jzchars.h" ++ ++MODULE_AUTHOR("Jianli Wei"); ++MODULE_DESCRIPTION("Virtual Driver of TCSM"); ++MODULE_LICENSE("GPL"); ++ ++/* ++ * fops routines ++ */ ++ ++static int tcsm_open(struct inode *inode, struct file *filp); ++static int tcsm_release(struct inode *inode, struct file *filp); ++static ssize_t tcsm_read(struct file *filp, char *buf, size_t size, loff_t *l); ++static ssize_t tcsm_write(struct file *filp, const char *buf, size_t size, loff_t *l); ++static int tcsm_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); ++ ++static struct file_operations tcsm_fops = ++{ ++ open: tcsm_open, ++ release: tcsm_release, ++ read: tcsm_read, ++ write: tcsm_write, ++ ioctl: tcsm_ioctl, ++}; ++ ++static int tcsm_open(struct inode *inode, struct file *filp) ++{ ++ // printk("%d[%s]\n", __LINE__, __FILE__); ++ struct pt_regs *info = task_pt_regs(current); ++ printk("pt_regs =%p\n", info); ++ printk("cp0 status=0x%08x\n", info->cp0_status); ++ info->cp0_status &= ~0x10; ++ info->cp0_status |= 0x08000000; // a tricky ++ printk("cp0 status=0x%08x\n", info->cp0_status); ++ return 0; ++} ++ ++static int tcsm_release(struct inode *inode, struct file *filp) ++{ ++ struct pt_regs *info = task_pt_regs(current); ++ info->cp0_status |= 0x10; ++ info->cp0_status &= ~0x08000000; // a tricky ++ return 0; ++} ++ ++static ssize_t tcsm_read(struct file *filp, char *buf, size_t size, loff_t *l) ++{ ++ printk("tcsm: read is not implemented\n"); ++ return -1; ++} ++ ++static ssize_t tcsm_write(struct file *filp, const char *buf, size_t size, loff_t *l) ++{ ++ printk("tcsm: write is not implemented\n"); ++ return -1; ++} ++ ++static int tcsm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ int ret = 0; ++ printk("tcsm: ioctl is not implemented\n"); ++ return ret; ++} ++ ++/* ++ * Module init and exit ++ */ ++ ++static int __init tcsm_init(void) ++{ ++ int ret; ++ ++ ret = jz_register_chrdev(TCSM_MINOR, "tcsm", &tcsm_fops, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ printk("Virtual Driver of TCSM registered\n"); ++ return 0; ++} ++ ++static void __exit tcsm_exit(void) ++{ ++ jz_unregister_chrdev(TCSM_MINOR, "tcsm"); ++} ++ ++module_init(tcsm_init); ++module_exit(tcsm_exit); +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/ucb1400.c linux-2.6.24.7/drivers/char/jzchar/ucb1400.c +--- linux-2.6.24.7.old/drivers/char/jzchar/ucb1400.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/ucb1400.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,585 @@ ++/* ++ * ucb1400.c ++ * ++ * Touch screen driver interface to the UCB1400 codec. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "jz_ts.h" ++#include "ucb1400.h" ++ ++#ifndef GPIO_TS_PENIRQ ++#define GPIO_TS_PENIRQ 68 ++#endif ++ ++#define TS_PIN GPIO_TS_PENIRQ ++#define TS_IRQ (IRQ_GPIO_0 + TS_PIN) ++ ++static int samples = 5; /* we sample 5 every time, and throw away the max and min cases, then use the average of the other 3 samples */ ++static int first_time = 0; ++static unsigned long last_x, last_y, last_p; ++ ++static int adcsync = 0; ++ ++static unsigned int ucb_id = 0; ++static struct ucb1400 *ucb; ++ ++extern struct ac97_codec * find_ac97_codec(void); ++ ++extern unsigned int (*codec_read_battery)(void); ++ ++/*------------------ UCB1400 routines ------------------*/ ++ ++static inline void ucb1400_reg_write(unsigned char reg, unsigned short val) ++{ ++ struct ac97_codec *codec = find_ac97_codec(); ++ if (!codec) ++ return; ++ codec->codec_write(codec, reg, val); ++} ++ ++static inline unsigned int ucb1400_reg_read(unsigned char reg) ++{ ++ struct ac97_codec *codec = find_ac97_codec(); ++ if (!codec) ++ return 0; ++ return codec->codec_read(codec, reg); ++} ++ ++static void ucb1400_adc_enable(void) ++{ ++ down(&ucb->adc_sem); ++ ++ ucb->adc_cr |= UCB_ADC_ENA; ++ ucb1400_reg_write(UCB_ADC_CR, ucb->adc_cr); ++} ++ ++static unsigned int ucb1400_adc_read(int adc_channel, int sync) ++{ ++ unsigned int val, timeout = 10000; ++ ++ if (sync) ++ adc_channel |= UCB_ADC_SYNC_ENA; ++ ++ ucb1400_reg_write(UCB_ADC_CR, ucb->adc_cr | adc_channel); ++ ucb1400_reg_write(UCB_ADC_CR, ucb->adc_cr | adc_channel | UCB_ADC_START); ++ ++ for (;;) { ++ val = ucb1400_reg_read(UCB_ADC_DATA); ++ if (val & UCB_ADC_DAT_VAL) ++ break; ++ if (--timeout == 0) ++ break; ++ udelay(1); ++ } ++ ++ return UCB_ADC_DAT(val); ++} ++ ++static void ucb1400_adc_disable(void) ++{ ++ ucb->adc_cr &= ~UCB_ADC_ENA; ++ ucb1400_reg_write(UCB_ADC_CR, ucb->adc_cr); ++ ++ up(&ucb->adc_sem); ++} ++ ++static void ucb1400_enable_irq(unsigned int idx, int edges) ++{ ++ unsigned long flags; ++ ++ if (idx < 16) { ++ spin_lock_irqsave(&ucb->lock, flags); ++ ++ /* This prevents spurious interrupts on the UCB1400 */ ++ ucb1400_reg_write(UCB_IE_CLEAR, 1 << idx); ++ ucb1400_reg_write(UCB_IE_CLEAR, 0); ++ ++ if (edges & UCB_RISING) { ++ ucb->irq_ris_enbl |= 1 << idx; ++ ucb1400_reg_write(UCB_IE_RIS, ucb->irq_ris_enbl); ++ } ++ if (edges & UCB_FALLING) { ++ ucb->irq_fal_enbl |= 1 << idx; ++ ucb1400_reg_write(UCB_IE_FAL, ucb->irq_fal_enbl); ++ } ++ spin_unlock_irqrestore(&ucb->lock, flags); ++ } ++} ++ ++static void ucb1400_disable_irq(unsigned int idx, int edges) ++{ ++ unsigned long flags; ++ ++ if (idx < 16) { ++ spin_lock_irqsave(&ucb->lock, flags); ++ ++ if (edges & UCB_RISING) { ++ ucb->irq_ris_enbl &= ~(1 << idx); ++ ucb1400_reg_write(UCB_IE_RIS, ucb->irq_ris_enbl); ++ } ++ if (edges & UCB_FALLING) { ++ ucb->irq_fal_enbl &= ~(1 << idx); ++ ucb1400_reg_write(UCB_IE_FAL, ucb->irq_fal_enbl); ++ } ++ spin_unlock_irqrestore(&ucb->lock, flags); ++ } ++} ++ ++/* ++ * Switch to interrupt mode. ++ */ ++static inline void ucb1400_ts_mode_int(void) ++{ ++ if (!ucb_id) { ++ ucb_id = ucb1400_reg_read(UCB_ID); ++ } ++ ++ if (ucb_id == UCB_ID_1400_BUGGY) ++ ucb1400_reg_write(UCB_TS_CR, ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | ++ UCB_TS_CR_MODE_INT); ++ else ++ ucb1400_reg_write(UCB_TS_CR, ++ UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | ++ UCB_TS_CR_MODE_INT); ++} ++ ++/* ++ * Switch to pressure mode, and read pressure. We don't need to wait ++ * here, since both plates are being driven. ++ */ ++static inline unsigned int ucb1400_ts_read_pressure(void) ++{ ++ ucb1400_reg_write(UCB_TS_CR, ++ UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ ++ return ucb1400_adc_read(UCB_ADC_INP_TSPY, adcsync); ++} ++ ++/* ++ * Switch to X position mode and measure Y plate. We switch the plate ++ * configuration in pressure mode, then switch to position mode. This ++ * gives a faster response time. Even so, we need to wait about 55us ++ * for things to stabilise. ++ */ ++static inline unsigned int ucb1400_ts_read_xpos(void) ++{ ++ ucb1400_reg_write(UCB_TS_CR, ++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ ucb1400_reg_write(UCB_TS_CR, ++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ ucb1400_reg_write(UCB_TS_CR, ++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); ++ ++ udelay(55); ++ ++ return ucb1400_adc_read(UCB_ADC_INP_TSPY, adcsync); ++} ++ ++/* ++ * Switch to Y position mode and measure X plate. We switch the plate ++ * configuration in pressure mode, then switch to position mode. This ++ * gives a faster response time. Even so, we need to wait about 55us ++ * for things to stabilise. ++ */ ++static inline unsigned int ucb1400_ts_read_ypos(void) ++{ ++ ucb1400_reg_write(UCB_TS_CR, ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ ucb1400_reg_write(UCB_TS_CR, ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ ucb1400_reg_write(UCB_TS_CR, ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | ++ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); ++ ++ udelay(55); ++ ++ return ucb1400_adc_read(UCB_ADC_INP_TSPX, adcsync); ++} ++ ++/*------------------------------------------------------------ ++ * Read the battery voltage ++ */ ++ ++unsigned int ucb1400_read_battery(void) ++{ ++ unsigned int v; ++ ++ ucb1400_adc_enable(); ++ ++ // read twice to reduce fault value ++ v = ucb1400_adc_read(UCB_ADC_INP_AD0, adcsync); ++ v = ucb1400_adc_read(UCB_ADC_INP_AD0, adcsync); ++ ++ ucb1400_adc_disable(); ++ ++// printk("ucb1400_read_battery v=%d\n", v); ++ ++ return v; ++} ++ ++/*------------------ Calibrate samples -------------------*/ ++ ++#define DIFF(a,b) ((a>b)?(a-b):(b-a)) ++ ++static int calibrate_samples(void *xbuf, void *ybuf, void *pbuf, int count) ++{ ++ unsigned long *xp = (unsigned long *)xbuf; ++ unsigned long *yp = (unsigned long *)ybuf; ++ unsigned long *pp = (unsigned long *)pbuf; ++ unsigned long x_cal = 0, y_cal = 0, p_cal = 0, tmp; ++ int ignored, i, j; ++ int valid = 0; ++ ++ /* throw away the max cases */ ++ tmp = xp[0]; ++ ignored = 0; ++ for (i = 1; i < count; i++) { ++ if (xp[i] > tmp) { ++ tmp = xp[i]; ++ ignored = i; ++ } ++ } ++ j = 0; ++ for (i = 0; i < count; i++) { ++ if (i == ignored) ++ continue; ++ xp[j++] = xp[i]; ++ } ++ ++ tmp = yp[0]; ++ ignored = 0; ++ for (i = 1; i < count; i++) { ++ if (yp[i] > tmp) { ++ tmp = yp[i]; ++ ignored = i; ++ } ++ } ++ j = 0; ++ for (i = 0; i < count; i++) { ++ if (i == ignored) ++ continue; ++ yp[j++] = yp[i]; ++ } ++ ++ tmp = pp[0]; ++ ignored = 0; ++ for (i = 1; i < count; i++) { ++ if (pp[i] > tmp) { ++ tmp = pp[i]; ++ ignored = i; ++ } ++ } ++ j = 0; ++ for (i = 0; i < count; i++) { ++ if (i == ignored) ++ continue; ++ pp[j++] = pp[i]; ++ } ++ ++ /* throw away the min cases */ ++ ++ count -= 1; // decrement by 1 ++ ++ tmp = xp[0]; ++ ignored = 0; ++ for (i = 1; i < count; i++) { ++ if (xp[i] < tmp) { ++ tmp = xp[i]; ++ ignored = i; ++ } ++ } ++ j = 0; ++ for (i = 0; i < count; i++) { ++ if (i == ignored) ++ continue; ++ xp[j++] = xp[i]; ++ } ++ ++ tmp = yp[0]; ++ ignored = 0; ++ for (i = 1; i < count; i++) { ++ if (yp[i] < tmp) { ++ tmp = yp[i]; ++ ignored = i; ++ } ++ } ++ j = 0; ++ for (i = 0; i < count; i++) { ++ if (i == ignored) ++ continue; ++ yp[j++] = yp[i]; ++ } ++ ++ tmp = pp[0]; ++ ignored = 0; ++ for (i = 1; i < count; i++) { ++ if (pp[i] < tmp) { ++ tmp = pp[i]; ++ ignored = i; ++ } ++ } ++ j = 0; ++ for (i = 0; i < count; i++) { ++ if (i == ignored) ++ continue; ++ pp[j++] = pp[i]; ++ } ++ ++ count -= 1; // decrement by 1 ++ ++ /* calculate the average of the rest */ ++ for (i = 0; i < count; i++) { ++ x_cal += xp[i]; ++ y_cal += yp[i]; ++ p_cal += pp[i]; ++ } ++ x_cal /= count; ++ y_cal /= count; ++ p_cal /= count; ++ ++ if (first_time) { ++ first_time = 0; ++ last_x = x_cal; ++ last_y = y_cal; ++ last_p = p_cal; ++ valid = 1; ++ } ++ else { ++ if ((DIFF(x_cal, last_x) > 50) || ++ (DIFF(y_cal, last_y) > 50)) ++ valid = 0; ++ else ++ valid = 1; ++ } ++ ++// printk("x_cal=%d y_cal=%d p_cal=%d valid=%d\n", x_cal, y_cal, p_cal, valid); ++ ++ if (valid) { ++ *xp = last_x = x_cal; ++ *yp = last_y = y_cal; ++ *pp = last_p = p_cal; ++ } ++ ++ return valid; ++} ++ ++ ++#define TSMAXX 945 ++#define TSMAXY 830 ++#define TSMINX 90 ++#define TSMINY 105 ++ ++#define SCREEN_X 480 ++#define SCREEN_Y 272 ++ ++static unsigned long transform_to_screen_x(struct jz_ts_t *ts, unsigned long x ) ++{ ++ ++ if (ts->minx) ++ { ++ if (x < ts->minx) x = ts->minx; ++ if (x > ts->maxx) x = ts->maxx; ++ ++ return (x - ts->minx) * SCREEN_X / (ts->maxx - ts->minx); ++ } ++ else ++ { ++ if (x < TSMINX) x = TSMINX; ++ if (x > TSMAXX) x = TSMAXX; ++ ++ return (x - TSMINX) * SCREEN_X / (TSMAXX - TSMINX); ++ } ++} ++ ++static unsigned long transform_to_screen_y(struct jz_ts_t *ts, unsigned long y) ++{ ++ if (ts->minx) ++ { ++ if (y < ts->minx) y = ts->miny; ++ if (y > ts->maxx) y = ts->maxy; ++ ++ return (y - ts->miny) * SCREEN_Y / (ts->maxy - ts->miny); ++ } ++ else ++ { ++ if (y < TSMINX) y = TSMINY; ++ if (y > TSMAXX) y = TSMAXY; ++ ++ return (y - TSMINY) * SCREEN_Y / (TSMAXY - TSMINY); ++ } ++} ++ ++/*------------------ Common routines -------------------*/ ++ ++void ts_enable_irq(void) ++{ ++ /* interrupt mode */ ++ ucb1400_ts_mode_int(); ++ ucb1400_enable_irq(UCB_IRQ_TSPX, UCB_FALLING); ++ ++ enable_irq(TS_IRQ); ++} ++ ++void ts_disable_irq(void) ++{ ++ ucb1400_disable_irq(UCB_IRQ_TSPX, UCB_FALLING); ++ disable_irq(TS_IRQ); ++} ++ ++int ts_request_irq(u32 *irq, ++ void (*handler)(int, void *, struct pt_regs *), ++ const char *devname, ++ void *dev_id) ++{ ++ int retval; ++ ++ /* return the irq number */ ++ *irq = TS_IRQ; ++ ++ /* interrupt mode */ ++ ucb1400_ts_mode_int(); ++ ucb1400_enable_irq(UCB_IRQ_TSPX, UCB_FALLING); ++ ++ /* enable gpio irq */ ++ __gpio_as_irq_rise_edge(TS_PIN); ++ ++ /* register irq handler */ ++ retval = request_irq(TS_IRQ, handler, SA_INTERRUPT, devname, dev_id); ++ ++ return retval; ++} ++ ++void ts_free_irq(struct jz_ts_t *ts) ++{ ++ free_irq(ts->pendown_irq, ts); ++} ++ ++void ts_irq_callback(void) ++{ ++ /* clear interrupt status */ ++ ucb1400_reg_write(UCB_IE_CLEAR, ucb1400_reg_read(UCB_IE_STATUS)); ++ __gpio_ack_irq(TS_PIN); ++ ++ first_time = 1; // first time to acquire sample ++} ++ ++int PenIsDown(void) ++{ ++ unsigned int p; ++ ++ ucb1400_adc_enable(); ++ p = ucb1400_ts_read_pressure(); ++ ucb1400_adc_disable(); ++ ++ return (p > 100) ? 1 : 0; ++} ++ ++/* ++ * Acquire Raw pen coodinate data and compute touch screen ++ * pressure resistance. Hold spinlock when calling. ++ */ ++int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event) ++{ ++ unsigned int x_raw[8], y_raw[8], p_raw[8]; ++ int valid, i; ++ ++ /* Enable ADC */ ++ ucb1400_adc_enable(); ++ ++ for (i = 0; i < samples; i++) { ++ x_raw[i] = ucb1400_ts_read_xpos(); ++ } ++ for (i = 0; i < samples; i++) { ++ y_raw[i] = ucb1400_ts_read_ypos(); ++ } ++ for (i = 0; i < samples; i++) { ++ p_raw[i] = ucb1400_ts_read_pressure(); ++ } ++ ++ /* Disable ADC */ ++ ucb1400_adc_disable(); ++ ++ valid = calibrate_samples(x_raw, y_raw, p_raw, samples); ++ ++ if (valid) { ++ unsigned int x_scr, y_scr; ++ ++ if(ts->filter) { ++ x_scr = transform_to_screen_x(ts, x_raw[0]); ++ y_scr = transform_to_screen_y(ts, y_raw[0]); ++ ++ if (ts->prints) ++ printk("x_raw=%d y_raw=%d x_transform=%d y_transform=%d\n", x_raw[0], y_raw[0], x_scr, y_scr); ++ } ++ else { ++ x_scr = x_raw[0]; ++ y_scr = y_raw[0]; ++ ++ if (ts->prints) ++ printk("x_raw=%d y_raw=%d \n", x_raw[0], y_raw[0]); ++ } ++ ++ event->x = x_scr; ++ event->y = y_scr; ++ event->pressure = (u16)p_raw[0]; ++ event->status = PENDOWN; ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * Module init and exit ++ */ ++ ++int __init ucb1400_init(void) ++{ ++ ucb = kmalloc(sizeof(struct ucb1400), GFP_KERNEL); ++ if (!ucb) return -ENOMEM; ++ ++ memset(ucb, 0, sizeof(struct ucb1400)); ++ ++ codec_read_battery = ucb1400_read_battery; ++ ++ spin_lock_init(&ucb->lock); ++ sema_init(&ucb->adc_sem, 1); ++ ++ return 0; ++} ++ ++void ucb1400_cleanup(void) ++{ ++ kfree(ucb); ++} ++ ++module_init(ucb1400_init); ++module_exit(ucb1400_cleanup); +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/ucb1400.h linux-2.6.24.7/drivers/char/jzchar/ucb1400.h +--- linux-2.6.24.7.old/drivers/char/jzchar/ucb1400.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/ucb1400.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,113 @@ ++#ifndef __UCB1400_H__ ++#define __UCB1400_H__ ++ ++/* ucb1400 aclink register mappings */ ++ ++#define UCB_IO_DATA 0x5a ++#define UCB_IO_DIR 0x5c ++#define UCB_IE_RIS 0x5e ++#define UCB_IE_FAL 0x60 ++#define UCB_IE_STATUS 0x62 ++#define UCB_IE_CLEAR 0x62 ++#define UCB_TS_CR 0x64 ++#define UCB_ADC_CR 0x66 ++#define UCB_ADC_DATA 0x68 ++#define UCB_ID 0x7e /* 7c is mfr id, 7e part id (from aclink spec) */ ++ ++#define UCB_ADC_DAT(x) ((x) & 0x3ff) ++ ++/* register bits */ ++ ++#define UCB_IO_0 (1 << 0) ++#define UCB_IO_1 (1 << 1) ++#define UCB_IO_2 (1 << 2) ++#define UCB_IO_3 (1 << 3) ++#define UCB_IO_4 (1 << 4) ++#define UCB_IO_5 (1 << 5) ++#define UCB_IO_6 (1 << 6) ++#define UCB_IO_7 (1 << 7) ++#define UCB_IO_8 (1 << 8) ++#define UCB_IO_9 (1 << 9) ++ ++#define UCB_IE_ADC (1 << 11) ++#define UCB_IE_TSPX (1 << 12) ++#define UCB_IE_TSMX (1 << 13) ++#define UCB_IE_TCLIP (1 << 14) ++#define UCB_IE_ACLIP (1 << 15) ++ ++#define UCB_IRQ_TSPX 12 ++ ++#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */ ++#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */ ++ ++#define UCB_TC_B_VOICE_ENA (1 << 3) ++#define UCB_TC_B_CLIP (1 << 4) ++#define UCB_TC_B_ATT (1 << 6) ++#define UCB_TC_B_SIDE_ENA (1 << 11) ++#define UCB_TC_B_MUTE (1 << 13) ++#define UCB_TC_B_IN_ENA (1 << 14) ++#define UCB_TC_B_OUT_ENA (1 << 15) ++ ++#define UCB_AC_B_LOOP (1 << 8) ++#define UCB_AC_B_MUTE (1 << 13) ++#define UCB_AC_B_IN_ENA (1 << 14) ++#define UCB_AC_B_OUT_ENA (1 << 15) ++ ++#define UCB_TS_CR_TSMX_POW (1 << 0) ++#define UCB_TS_CR_TSPX_POW (1 << 1) ++#define UCB_TS_CR_TSMY_POW (1 << 2) ++#define UCB_TS_CR_TSPY_POW (1 << 3) ++#define UCB_TS_CR_TSMX_GND (1 << 4) ++#define UCB_TS_CR_TSPX_GND (1 << 5) ++#define UCB_TS_CR_TSMY_GND (1 << 6) ++#define UCB_TS_CR_TSPY_GND (1 << 7) ++#define UCB_TS_CR_MODE_INT (0 << 8) ++#define UCB_TS_CR_MODE_PRES (1 << 8) ++#define UCB_TS_CR_MODE_POS (2 << 8) ++#define UCB_TS_CR_BIAS_ENA (1 << 11) ++#define UCB_TS_CR_TSPX_LOW (1 << 12) ++#define UCB_TS_CR_TSMX_LOW (1 << 13) ++ ++#define UCB_ADC_SYNC_ENA (1 << 0) ++#define UCB_ADC_VREFBYP_CON (1 << 1) ++#define UCB_ADC_INP_TSPX (0 << 2) ++#define UCB_ADC_INP_TSMX (1 << 2) ++#define UCB_ADC_INP_TSPY (2 << 2) ++#define UCB_ADC_INP_TSMY (3 << 2) ++#define UCB_ADC_INP_AD0 (4 << 2) ++#define UCB_ADC_INP_AD1 (5 << 2) ++#define UCB_ADC_INP_AD2 (6 << 2) ++#define UCB_ADC_INP_AD3 (7 << 2) ++#define UCB_ADC_EXT_REF (1 << 5) ++#define UCB_ADC_START (1 << 7) ++#define UCB_ADC_ENA (1 << 15) ++ ++#define UCB_ADC_DAT_VAL (1 << 15) ++ ++#define UCB_ID_1200 0x1004 ++#define UCB_ID_1300 0x1005 ++#define UCB_ID_1400 0x4304 ++#define UCB_ID_1400_BUGGY 0x4303 /* fake ID */ ++ ++#define UCB_MODE_DYN_VFLAG_ENA (1 << 12) ++#define UCB_MODE_AUD_OFF_CAN (1 << 13) ++ ++/* ++ * Which edges of the IRQ do you want to control today? ++ */ ++#define UCB_RISING (1 << 0) ++#define UCB_FALLING (1 << 1) ++ ++/* Device data structure */ ++ ++struct ucb1400 { ++ spinlock_t lock; ++ struct pm_dev *pmdev; ++ struct semaphore adc_sem; ++ u16 adc_cr; ++ u16 irq_fal_enbl; ++ u16 irq_ris_enbl; ++ int irq_enabled; ++}; ++ ++#endif /* __UCB1400_H__ */ +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/udc_hotplug.c linux-2.6.24.7/drivers/char/jzchar/udc_hotplug.c +--- linux-2.6.24.7.old/drivers/char/jzchar/udc_hotplug.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/udc_hotplug.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,451 @@ ++/* ++ * linux/drivers/char/jzchar/udc_hotplug.c ++ * ++ * New UDC hotplug driver. ++ * ++ * Copyright (C) 2007 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "jzchars.h" ++ ++#ifndef GPIO_UDC_HOTPLUG ++#define GPIO_UDC_HOTPLUG 86 ++#endif ++ ++#define UDC_HOTPLUG_PIN GPIO_UDC_HOTPLUG ++#define UDC_HOTPLUG_IRQ (IRQ_GPIO_0 + UDC_HOTPLUG_PIN) ++ ++#define dprintk(x,...) ++ ++//simple meaning define ++#define NOT_CONNECT 0 ++#define YES_CONNECT 1 ++#define MAX_GPIO_TIME 50 ++ ++#define EVENT_USB_ADD 1 ++#define EVENT_USB_REMOVE 2 ++#define EVENT_POWER_ADD 3 ++#define EVENT_POWER_REMOVE 4 ++#define EVENT_POWER_TO_USB 5 ++#define EVENT_USB_SUSPEND_POWER 6 ++ ++struct udc_pnp_stat ++{ ++ char cable_stat, old_cable_stat; ++ char protl_stat, old_protl_stat; ++ char object_stat1; ++ char object_stat2; ++}; ++ ++static struct udc_pnp_stat cur_pnp_stat; ++ ++static struct file_operations cable_fops = { ++ owner: THIS_MODULE, ++}; ++ ++static struct miscdevice cable_dev= ++{ ++ 231, ++ "udc_cable", ++ &cable_fops ++}; ++ ++static struct file_operations power_fops = { ++ owner: THIS_MODULE, ++}; ++ ++static struct miscdevice power_dev= ++{ ++ 232, ++ "power_cable", ++ &power_fops ++}; ++ ++int jz_udc_active = 0; /* 0: Have no actions; 1: Have actions */ ++ ++static int udc_pin_level; ++static int udc_old_state; ++static int udc_pin_time; ++ ++static struct timer_list udc_long_timer, udc_gpio_timer; ++ ++/* Kernel thread to deliver event to user space */ ++static struct task_struct *kudcd_task; ++ ++static void udc_gpio_timer_routine(unsigned long data) ++{ ++ wake_up_process(kudcd_task); ++} ++ ++static void udc_long_timer_routine(unsigned long data) ++{ ++ dprintk("udc_timer\n"); ++ if (jz_udc_active) ++ udc_old_state = 1; ++ if (!jz_udc_active && udc_old_state) //udc irq timeout! do suspend ++ { ++ dprintk("udc suspend!\n"); ++ udc_old_state = 0; ++ cur_pnp_stat.protl_stat = NOT_CONNECT; ++ del_timer(&udc_long_timer); ++ wake_up_process(kudcd_task); ++ return; ++ } ++ jz_udc_active = 0; ++ udc_long_timer.expires = jiffies + 3 * HZ; /* about 3 s */ ++ add_timer(&udc_long_timer); ++} ++ ++static int udc_get_pnp_stat(void) ++{ ++ udc_pin_level = __gpio_get_pin(UDC_HOTPLUG_PIN); ++ udc_pin_time = 1; ++ ++ init_timer(&udc_gpio_timer); ++ del_timer(&udc_gpio_timer); ++ udc_gpio_timer.function = udc_gpio_timer_routine; ++ udc_gpio_timer.expires = jiffies + 1; /* about 10 ms */ ++ add_timer(&udc_gpio_timer); ++ ++ while(1) ++ { ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule(); ++ ++ if (__gpio_get_pin(UDC_HOTPLUG_PIN) != udc_pin_level) ++ { ++ udc_pin_level = __gpio_get_pin(UDC_HOTPLUG_PIN); ++ udc_pin_time = 1; ++ dprintk("udc gpio detect restart! \n"); ++ } ++ ++ udc_pin_time ++; ++ if (udc_pin_time > MAX_GPIO_TIME) ++ break; ++ ++ del_timer(&udc_gpio_timer); ++ udc_gpio_timer.function = udc_gpio_timer_routine; ++ udc_gpio_timer.expires = jiffies + 1; /* about 10 ms */ ++ add_timer(&udc_gpio_timer); ++ } ++ ++ del_timer(&udc_gpio_timer); ++ if (__gpio_get_pin(UDC_HOTPLUG_PIN)) ++ return YES_CONNECT; ++ else ++ return NOT_CONNECT; ++} ++ ++static void udc_get_cable(void) ++{ ++ u32 intr_usb; ++ ++ __intc_mask_irq(IRQ_UDC); ++ ++ /* Now enable PHY to start detect */ ++#ifdef CONFIG_SOC_JZ4740 ++ REG_CPM_SCR |= CPM_SCR_USBPHY_ENABLE; ++#elif CONFIG_SOC_JZ4750 ++ REG_CPM_OPCR |= CPM_OPCR_UDCPHY_ENABLE; ++#endif ++ /* Clear IRQs */ ++ REG16(USB_REG_INTRINE) = 0; ++ REG16(USB_REG_INTROUTE) = 0; ++ REG8(USB_REG_INTRUSBE) = 0; ++ ++ /* disable UDC IRQs first */ ++ REG16(USB_REG_INTRINE) = 0; ++ REG16(USB_REG_INTROUTE) = 0; ++ REG8(USB_REG_INTRUSBE) = 0; ++ ++ /* Disable DMA */ ++ REG32(USB_REG_CNTL1) = 0; ++ REG32(USB_REG_CNTL2) = 0; ++ ++ /* Enable HS Mode */ ++ REG8(USB_REG_POWER) |= USB_POWER_HSENAB; ++ /* Enable soft connect */ ++ REG8(USB_REG_POWER) |= USB_POWER_SOFTCONN; ++ ++ dprintk("enable phy! %x %x %x %x %x\n", ++ REG8(USB_REG_POWER), ++ REG_CPM_SCR, ++ REG16(USB_REG_INTRINE), ++ REG16(USB_REG_INTROUTE), ++ REG8(USB_REG_INTRUSBE)); ++ ++ init_timer(&udc_gpio_timer); ++ del_timer(&udc_gpio_timer); ++ udc_gpio_timer.function = udc_gpio_timer_routine; ++ udc_gpio_timer.expires = jiffies + 11; /* about 100 ms */ ++ add_timer(&udc_gpio_timer); ++ /* Sleep a short time to see result */ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule(); ++ ++ del_timer(&udc_gpio_timer); ++ intr_usb = REG8(USB_REG_INTRUSB); ++ if ((intr_usb & USB_INTR_RESET) || ++ (intr_usb & USB_INTR_RESUME) || ++ (intr_usb & USB_INTR_SUSPEND)) ++ { ++ cur_pnp_stat.protl_stat = YES_CONNECT; ++ dprintk("cable is usb! \n"); ++ } ++ else ++ { ++ cur_pnp_stat.protl_stat = NOT_CONNECT; ++ dprintk("cable is power! \n"); ++ } ++ ++ /* Detect finish ,clean every thing */ ++ /* Disconnect from usb */ ++ REG8(USB_REG_POWER) &= ~USB_POWER_SOFTCONN; ++ /* Disable the USB PHY */ ++#ifdef CONFIG_SOC_JZ4740 ++ REG_CPM_SCR &= ~CPM_SCR_USBPHY_ENABLE; ++#elif CONFIG_SOC_JZ4750 ++ REG_CPM_OPCR &= ~CPM_OPCR_UDCPHY_ENABLE; ++#endif ++ /* Clear IRQs */ ++ REG16(USB_REG_INTRINE) = 0; ++ REG16(USB_REG_INTROUTE) = 0; ++ REG8(USB_REG_INTRUSBE) = 0; ++ __intc_ack_irq(IRQ_UDC); ++ __intc_unmask_irq(IRQ_UDC); ++} ++ ++static void send_event_udev(int event) ++{ ++ dprintk("Send udev message: cable=%d old=%d protl=%d old=%d \n", ++ cur_pnp_stat.cable_stat, ++ cur_pnp_stat.old_cable_stat, ++ cur_pnp_stat.protl_stat, ++ cur_pnp_stat.old_protl_stat); ++ ++ switch (event) ++ { ++ case EVENT_USB_ADD: ++ printk("usb cable insert! \n"); ++ misc_register(&cable_dev); ++ kobject_uevent(&cable_dev.this_device->kobj, KOBJ_ADD); ++ init_timer(&udc_long_timer); ++ del_timer(&udc_long_timer); ++ udc_long_timer.function = udc_long_timer_routine; ++ udc_long_timer.expires = jiffies + 3 * HZ; /* about 3 s */ ++ add_timer(&udc_long_timer); ++ break; ++ case EVENT_USB_REMOVE: ++ printk("usb cable remove! \n"); ++ kobject_uevent(&cable_dev.this_device->kobj, KOBJ_REMOVE); ++ misc_deregister(&cable_dev); ++ del_timer(&udc_long_timer); ++ break; ++ case EVENT_POWER_ADD: ++ printk("power cable insert! \n"); ++ misc_register(&power_dev); ++ kobject_uevent(&power_dev.this_device->kobj, KOBJ_ADD); ++ break; ++ case EVENT_POWER_REMOVE: ++ printk("power cable remove! \n"); ++ kobject_uevent(&power_dev.this_device->kobj, KOBJ_REMOVE); ++ misc_deregister(&power_dev); ++ break; ++ case EVENT_POWER_TO_USB: ++ printk("change power cable to usb! \n"); ++ kobject_uevent(&power_dev.this_device->kobj, KOBJ_REMOVE); ++ misc_deregister(&power_dev); ++ misc_register(&cable_dev); ++ kobject_uevent(&cable_dev.this_device->kobj, KOBJ_ADD); ++ break; ++ case EVENT_USB_SUSPEND_POWER: ++ printk("usb cable suspend! \n"); ++ printk("as power cable insert! \n"); ++ kobject_uevent(&cable_dev.this_device->kobj, KOBJ_REMOVE); ++ misc_deregister(&cable_dev); ++ misc_register(&power_dev); ++ kobject_uevent(&power_dev.this_device->kobj, KOBJ_ADD); ++ break; ++ }; ++} ++ ++static void udc_pnp_detect(void) ++{ ++ if (cur_pnp_stat.cable_stat == YES_CONNECT) /* already connected! */ ++ { ++ if (udc_get_pnp_stat() == NOT_CONNECT) ++ { ++ dprintk("cable real out! \n"); ++ cur_pnp_stat.cable_stat = NOT_CONNECT; ++ cur_pnp_stat.protl_stat = NOT_CONNECT; ++ /* Deliver this event to user space in udev model */ ++ if (cur_pnp_stat.old_protl_stat) ++ send_event_udev(EVENT_USB_REMOVE); ++ else ++ send_event_udev(EVENT_POWER_REMOVE); ++ cur_pnp_stat.old_cable_stat = cur_pnp_stat.cable_stat; ++ cur_pnp_stat.old_protl_stat = cur_pnp_stat.protl_stat; ++ } ++ else ++ { ++ if (cur_pnp_stat.old_protl_stat != cur_pnp_stat.protl_stat) ++ { ++ send_event_udev(EVENT_USB_SUSPEND_POWER); ++ cur_pnp_stat.old_cable_stat = cur_pnp_stat.cable_stat; ++ cur_pnp_stat.old_protl_stat = cur_pnp_stat.protl_stat; ++ } ++ else //change power to cable ++ { ++#if 0 //not support yet! ++ udc_get_cable(); ++ if (cur_pnp_stat.old_protl_stat != cur_pnp_stat.protl_stat) ++ send_event_udev(EVENT_POWER_TO_USB); ++ cur_pnp_stat.old_cable_stat = cur_pnp_stat.cable_stat; ++ cur_pnp_stat.old_protl_stat = cur_pnp_stat.protl_stat; ++#endif ++ } ++ } ++ } ++ else ++ { ++ if (udc_get_pnp_stat() == YES_CONNECT) ++ { ++ dprintk("cable real in! \n"); ++ cur_pnp_stat.cable_stat = YES_CONNECT; ++ udc_get_cable(); ++ /* Deliver this event to user space in udev model */ ++ if (cur_pnp_stat.protl_stat) ++ send_event_udev(EVENT_USB_ADD); ++ else ++ send_event_udev(EVENT_POWER_ADD); ++ cur_pnp_stat.old_cable_stat = cur_pnp_stat.cable_stat; ++ cur_pnp_stat.old_protl_stat = cur_pnp_stat.protl_stat; ++ } ++ else ++ dprintk("cable false in! \n"); ++ ++ } ++} ++ ++static void udc_pnp_set_gpio(void) ++{ ++ if (cur_pnp_stat.cable_stat == YES_CONNECT) ++ __gpio_as_irq_fall_edge(UDC_HOTPLUG_PIN); ++ else ++ __gpio_as_irq_rise_edge(UDC_HOTPLUG_PIN); ++ ++ /* clear interrupt pending status */ ++ __gpio_ack_irq(UDC_HOTPLUG_PIN); ++ /* unmask interrupt */ ++ __gpio_unmask_irq(UDC_HOTPLUG_PIN); ++} ++ ++static int udc_pnp_thread(void *unused) ++{ ++ printk(KERN_NOTICE "UDC starting pnp monitor thread\n"); ++ ++ while(1) ++ { ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule(); ++ ++ dprintk("pnp thread wake up! \n"); ++ /* wake up here */ ++ udc_pnp_detect(); ++ /* Reset gpio state last */ ++ udc_pnp_set_gpio(); ++ } ++} ++ ++static irqreturn_t udc_pnp_irq(int irq, void *dev_id) ++{ ++ ++ /* clear interrupt pending status */ ++ __gpio_ack_irq(UDC_HOTPLUG_PIN); ++ /* mask interrupt */ ++ __gpio_mask_irq(UDC_HOTPLUG_PIN); ++ /* wake up pnp detect thread */ ++ wake_up_process(kudcd_task); ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * Module init and exit ++ */ ++static int __init udc_hotplug_init(void) ++{ ++ int retval; ++ /* Init pnp stat first */ ++ cur_pnp_stat.cable_stat = NOT_CONNECT; ++ cur_pnp_stat.protl_stat = NOT_CONNECT; ++ cur_pnp_stat.old_cable_stat = NOT_CONNECT; ++ cur_pnp_stat.old_protl_stat = NOT_CONNECT; ++ cur_pnp_stat.object_stat1 = NOT_CONNECT; ++ cur_pnp_stat.object_stat2 = NOT_CONNECT; ++ udc_old_state = 0; ++ ++ /* create pnp thread and register IRQ */ ++ kudcd_task = kthread_run(udc_pnp_thread, NULL, "kudcd"); ++ if (IS_ERR(kudcd_task)) { ++ printk(KERN_ERR "jz_udc_hotplug: Failed to create system monitor thread.\n"); ++ return PTR_ERR(kudcd_task); ++ } ++ ++ retval = request_irq(UDC_HOTPLUG_IRQ, udc_pnp_irq, ++ IRQF_DISABLED, "udc_pnp", NULL); ++ if (retval) { ++ printk("Could not get udc hotplug irq %d\n", UDC_HOTPLUG_IRQ); ++ return retval; ++ } ++ ++ /* get current pin level */ ++ __gpio_disable_pull(UDC_HOTPLUG_PIN); ++ __gpio_as_input(UDC_HOTPLUG_PIN); ++ udelay(1); ++ udc_pin_level = __gpio_get_pin(UDC_HOTPLUG_PIN); ++ ++ if (udc_pin_level) { ++ dprintk("Cable already in! \n"); ++ /* Post a event */ ++ wake_up_process(kudcd_task); ++ } ++ else { ++ __gpio_as_irq_rise_edge(UDC_HOTPLUG_PIN); ++ dprintk("Cable not in! \n"); ++ } ++ ++ printk("JZ UDC hotplug driver registered\n"); ++ ++ return 0; ++} ++ ++static void __exit udc_hotplug_exit(void) ++{ ++ free_irq(UDC_HOTPLUG_IRQ, NULL); ++} ++ ++module_init(udc_hotplug_init); ++module_exit(udc_hotplug_exit); ++ ++EXPORT_SYMBOL(jz_udc_active); ++ ++MODULE_AUTHOR("Lucifer "); ++MODULE_DESCRIPTION("JzSOC OnChip udc hotplug driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/wm9712.c linux-2.6.24.7/drivers/char/jzchar/wm9712.c +--- linux-2.6.24.7.old/drivers/char/jzchar/wm9712.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/wm9712.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,334 @@ ++/* ++ * wm9712.c ++ * ++ * Touch screen driver interface to the Wolfson WM9712 codec. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "jz_ts.h" ++#include "wm9712.h" ++ ++#define POLL_TIMES 10 ++ ++static int samples = 1; ++static int inited = 0, started = 0; ++ ++extern struct ac97_codec * find_ac97_codec(void); ++extern int PenIsDown(void); ++ ++ ++static inline void wm9712_reg_write(unsigned int reg, unsigned int val) ++{ ++ struct ac97_codec *codec = find_ac97_codec(); ++ if (!codec) ++ return; ++ codec->codec_write(codec, reg, val); ++} ++ ++static inline unsigned int wm9712_reg_read(unsigned int reg) ++{ ++ struct ac97_codec *codec = find_ac97_codec(); ++ if (!codec) ++ return 0; ++ return codec->codec_read(codec, reg); ++} ++ ++static unsigned int wm9712_adc_read(int adc_channel) ++{ ++ unsigned int val; ++ ++ if (!PenIsDown()) ++ return 0; ++ ++ val = wm9712_reg_read(DIGI_REG1); ++ wm9712_reg_write(DIGI_REG1, val|adc_channel|DIGI_REG1_POLL); ++ ++ for (;;) { ++ if (wm9712_reg_read(0x54) & (1 << 12)) { ++ val = wm9712_reg_read(DIGI_READBACK); ++ break; ++ } ++ } ++ ++ /* stop the measure */ ++ wm9712_reg_write(DIGI_REG1, 0); ++ ++ return (val & 0x0fff); ++} ++ ++static struct timer_list pndn_timer; ++static void (*irq_handler)(int, void *, struct pt_regs *) = NULL; ++ ++void ts_irq_callback(void) ++{ ++#ifdef TS_IRQ ++ __gpio_ack_irq(TS_IRQ); ++#else ++#endif ++} ++ ++void ts_enable_irq(void) ++{ ++ if (!inited) ++ return; ++#ifdef TS_IRQ ++ enable_irq(TS_IRQ); ++#else ++ pndn_timer.expires = jiffies + HZ/POLL_TIMES; ++ add_timer(&pndn_timer); ++#endif ++} ++ ++void ts_disable_irq(void) ++{ ++ if (!inited) ++ return; ++#ifdef TS_IRQ ++ disable_irq(TS_IRQ); ++#endif ++} ++ ++#ifndef TS_IRQ ++static void pndn_detect(unsigned long data) ++{ ++ if (PenIsDown()) { ++ if (!started) ++ return; ++ if (irq_handler) ++ irq_handler(NULL, data, NULL); ++ } else { ++ pndn_timer.expires = jiffies + HZ/POLL_TIMES; ++ add_timer(&pndn_timer); ++ } ++} ++#endif ++ ++void ts_free_irq(struct jz_ts_t *ts) ++{ ++#ifdef TS_IRQ ++ free_irq(ts->pendown_irq, ts); ++#else ++ started = 0; ++ del_timer_sync(&pndn_timer); ++#endif ++} ++ ++int ts_request_irq(u32 *irq, ++ void (*handler)(int, void *, struct pt_regs *), ++ const char *devname, ++ void *dev_id) ++{ ++ /* 4wire, Ip=400uA, Rpu=64Kohm/64, wake-up on pendown without ++ * reset, meassure on pen down. Do not use wait mode. ++ */ ++ started = 1; ++ if (!inited) { ++ wm9712_reg_write(DIGI_REG2, ++ DIGI_REG2_WIRE_4 | ++ DIGI_REG2_PIL_200uA | ++ (31 << DIGI_REG2_RPU_BIT) | ++ DIGI_REG2_PRP_ALLON | ++ DIGI_REG2_RPR_NWOR); ++ /* Polling mode and no measurement */ ++ wm9712_reg_write(DIGI_REG1, 0); ++ } ++ ++#ifdef TS_IRQ ++ /* Generate irq request on PENDOWN pin, pendown cause the level high */ ++ wm9712_reg_write(0x56, wm9712_reg_read(0x56) & ~(1 << 3)); ++ wm9712_reg_write(0x4c, wm9712_reg_read(0x4c) & ~(1 << 3)); ++ ++ *irq = TS_IRQ; ++ return request_irq(TS_IRQ, handler, SA_INTERRUPT, devname, dev_id); ++#else ++ if (!inited) { ++ irq_handler = handler; ++ init_timer(&pndn_timer); ++ pndn_timer.function = pndn_detect; ++ pndn_timer.data = (unsigned long)dev_id; ++ pndn_timer.expires = jiffies + HZ/POLL_TIMES; ++ add_timer(&pndn_timer); ++ inited = 1; ++ } else { ++ pndn_timer.expires = jiffies + HZ/POLL_TIMES; ++ add_timer(&pndn_timer); ++ } ++ return 0; ++#endif ++} ++ ++int PenIsDown(void) ++{ ++ if (wm9712_reg_read(DIGI_READBACK) & DIGI_READBACK_PNDN) ++ return 1; ++ return 0; ++} ++ ++#if defined(CONFIG_MIPS_JZ4730_GPS) ++#define adj_data(r1, r2, r3, s) \ ++do { \ ++ if (r1 < 0x90) \ ++ r1 = 0x90; \ ++ if (r2 < 0xed) \ ++ r2 = 0xed; \ ++ r1 = ((r1 - 0x90) * 240) / 3354; \ ++ r2 = ((r2 - 0xed) * 320) / 3671; \ ++ if (r1 > 239) \ ++ r1 = 239; \ ++ if (r2 > 319) \ ++ r2 = 319; \ ++ \ ++ *s = r2; \ ++ *(s+1) = 239 - r1; \ ++ *(s+2) = z_raw; \ ++} while (0) ++#endif ++ ++#ifndef adj_data ++#define adj_data(r1, r2, r3, s) ++#endif ++ ++static int read_adc(unsigned int *sdata) ++{ ++ unsigned long x_raw=0, y_raw=0, z_raw=0, t, fail = 0; ++ int i; ++ ++ for (i=0; i 1) { ++ x_raw = (x_raw + (samples>>1)) / samples; ++ y_raw = (y_raw + (samples>>1)) / samples; ++ z_raw = (z_raw + (samples>>1)) / samples; ++ } ++ ++ adj_data (x_raw, y_raw, z_raw, sdata); ++ ++ return 1; ++} ++ ++ ++#define TSMAXX 945 ++#define TSMAXY 830 ++#define TSMINX 90 ++#define TSMINY 105 ++ ++#define SCREEN_X 480 ++#define SCREEN_Y 272 ++ ++static unsigned long transform_to_screen_x(struct jz_ts_t *ts, unsigned long x ) ++{ ++ ++ if (ts->minx) ++ { ++ if (x < ts->minx) x = ts->minx; ++ if (x > ts->maxx) x = ts->maxx; ++ ++ return (x - ts->minx) * SCREEN_X / (ts->maxx - ts->minx); ++ } ++ else ++ { ++ if (x < TSMINX) x = TSMINX; ++ if (x > TSMAXX) x = TSMAXX; ++ ++ return (x - TSMINX) * SCREEN_X / (TSMAXX - TSMINX); ++ } ++} ++ ++static unsigned long transform_to_screen_y(struct jz_ts_t *ts, unsigned long y) ++{ ++ if (ts->minx) ++ { ++ if (y < ts->minx) y = ts->miny; ++ if (y > ts->maxx) y = ts->maxy; ++ ++ return (y - ts->miny) * SCREEN_Y / (ts->maxy - ts->miny); ++ } ++ else ++ { ++ if (y < TSMINX) y = TSMINY; ++ if (y > TSMAXX) y = TSMAXY; ++ ++ return (y - TSMINY) * SCREEN_Y / (TSMAXY - TSMINY); ++ } ++} ++ ++ ++/* ++ * Acquire Raw pen coodinate data and compute touch screen ++ * pressure resistance. Hold spinlock when calling. ++ */ ++int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event) ++{ ++ unsigned int s[3]; ++ unsigned int x_scr, y_scr; ++ if (!read_adc(s)) ++ return 0; ++ if(ts->filter) { ++ x_scr = transform_to_screen_x(ts, s[0]); ++ y_scr = transform_to_screen_y(ts, s[1]); ++ ++ if (ts->prints) ++ printk("x_raw=%d y_raw=%d x_transform=%d y_transform=%d\n", s[0], s[1], x_scr, y_scr); } ++ else { ++ x_scr = s[0]; ++ y_scr = s[1]; ++ ++ if (ts->prints) ++ printk("x_raw=%d y_raw=%d \n", s[0], s[1]); ++ } ++ event->x = x_scr; ++ event->y = y_scr; ++ event->pressure = (u16)s[2]; ++ event->status = PENDOWN; ++ return 1; ++#if 0 ++ do_gettimeofday(&event->stamp); ++#endif ++} ++ ++int __init wm9712_init(void) ++{ ++ return 0; ++} ++ ++void wm9712_cleanup(void) ++{ ++} ++ ++module_init(wm9712_init); ++module_exit(wm9712_cleanup); ++ +diff -urN linux-2.6.24.7.old/drivers/char/jzchar/wm9712.h linux-2.6.24.7/drivers/char/jzchar/wm9712.h +--- linux-2.6.24.7.old/drivers/char/jzchar/wm9712.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/jzchar/wm9712.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,58 @@ ++#ifndef __WM9712_H__ ++#define __WM9712_H__ ++ ++#define DIGI_REG1 0x76 ++#define DIGI_REG2 0x78 ++#define DIGI_READBACK 0x7A ++ ++#define ADCSEL_BIT 12 ++#define ADCSEL_MASK (7 << ADCSEL_BIT) ++#define ADCSEL_NONE (0 << ADCSEL_BIT) ++#define ADCSEL_XPOS (1 << ADCSEL_BIT) ++#define ADCSEL_YPOS (2 << ADCSEL_BIT) ++#define ADCSEL_PRESSURE (3 << ADCSEL_BIT) ++#define ADCSEL_COMP1 (4 << ADCSEL_BIT) ++#define ADCSEL_COMP2 (5 << ADCSEL_BIT) ++#define ADCSEL_BMON (6 << ADCSEL_BIT) ++#define ADCSEL_WIPER (7 << ADCSEL_BIT) ++ ++#define DIGI_REG1_CTC (1 << 10) ++#define DIGI_REG1_POLL (1 << 15) ++#define DIGI_REG1_CR_BIT 8 ++#define DIGI_REG1_CR_MASK (3 << DIGI_REG1_CR_BIT) ++#define DIGI_REG1_COO (1 << 11) ++#define DIGI_REG1_SLEN (1 << 3) ++#define DIGI_REG1_SLT_BIT 0 ++#define DIGI_REG1_SLT_MASK (7 << DIGI_REG1_SLT_BIT) ++#define DIGI_REG1_SLT_5 (0 << DIGI_REG1_SLT_BIT) ++#define DIGI_REG1_SLT_6 (1 << DIGI_REG1_SLT_BIT) ++#define DIGI_REG1_SLT_7 (2 << DIGI_REG1_SLT_BIT) ++#define DIGI_REG1_SLT_8 (3 << DIGI_REG1_SLT_BIT) ++#define DIGI_REG1_SLT_9 (4 << DIGI_REG1_SLT_BIT) ++#define DIGI_REG1_SLT_10 (5 << DIGI_REG1_SLT_BIT) ++#define DIGI_REG1_SLT_11 (6 << DIGI_REG1_SLT_BIT) ++#define DIGI_REG1_SLT_RES (7 << DIGI_REG1_SLT_BIT) ++#define DIGI_REG1_DEL_BIT 4 ++#define DIGI_REG1_DEL_MASK (0x0f << DIGI_REG1_DEL_BIT) ++ ++#define DIGI_REG2_WIRE_5 (1 << 12) ++#define DIGI_REG2_WIRE_4 (0 << 12) ++#define DIGI_REG2_RPU_BIT 0 ++#define DIGI_REG2_RPU_MASK (0x3f << DIGI_REG2_RPU_BIT) ++#define DIGI_REG2_PIL_400uA (1 << 8) ++#define DIGI_REG2_PIL_200uA (0 << 8) ++#define DIGI_REG2_PRP_BIT 14 ++#define DIGI_REG2_PRP_MASK (3 << DIGI_REG2_PRP_BIT) ++#define DIGI_REG2_PRP_ALLOFF (0 << DIGI_REG2_PRP_BIT) ++#define DIGI_REG2_PRP_WOP (1 << DIGI_REG2_PRP_BIT) ++#define DIGI_REG2_PRP_NWOP (2 << DIGI_REG2_PRP_BIT) ++#define DIGI_REG2_PRP_ALLON (3 << DIGI_REG2_PRP_BIT) ++#define DIGI_REG2_RPR_WOR (0 << 13) ++#define DIGI_REG2_RPR_NWOR (1 << 13) ++#define DIGI_REG2_PDEN (1 << 11) ++#define DIGI_REG2_WAIT (1 << 9) ++ ++#define DIGI_READBACK_PNDN (1 << 15) ++ ++#endif /* __WM9712_H__ */ ++ +diff -urN linux-2.6.24.7.old/drivers/char/rtc_jz.c linux-2.6.24.7/drivers/char/rtc_jz.c +--- linux-2.6.24.7.old/drivers/char/rtc_jz.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/rtc_jz.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,503 @@ ++/* ++ * Jz OnChip Real Time Clock interface for Linux ++ * ++ * NOTE: we need to wait rtc write ready before read or write RTC registers. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include /* get the user-level API */ ++#include ++#include ++ ++#include "rtc_jz.h" ++ ++ ++char sbin_rtc_alarm_handler_path[] = "/sbin/rtcalarm"; ++//call_usermodehelper(char *path, char **argv, char **envp, int wait) ++//extern int call_usermodehelper(char *path, char **argv, char **envp); ++ ++extern spinlock_t rtc_lock; ++ ++static int rtc_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg); ++ ++ ++static void get_rtc_time (struct rtc_time *rtc_tm); ++static int set_rtc_time (struct rtc_time *rtc_tm); ++static void get_rtc_alm_time (struct rtc_time *alm_tm); ++static int set_rtc_alm_time (struct rtc_time *alm_tm); ++ ++static void set_rtc_irq_bit(int bit); ++static void mask_rtc_irq_bit(int bit); ++ ++static unsigned int rtc_status = 0; ++static unsigned int epoch = 1900; ++ ++static void get_rtc_time(struct rtc_time *rtc_tm) ++{ ++ unsigned long lval; ++ struct rtc_time ltm; ++ ++ spin_lock_irq(&rtc_lock); ++ while ( !__rtc_write_ready() ) ; ++ lval = REG_RTC_RSR; ++ rtc_time_to_tm(lval, <m); ++ if(rtc_valid_tm(<m) == 0) { ++ /* is valid */ ++ rtc_tm->tm_sec = ltm.tm_sec; ++ rtc_tm->tm_min = ltm.tm_min; ++ rtc_tm->tm_hour = ltm.tm_hour; ++ rtc_tm->tm_mday = ltm.tm_mday; ++ rtc_tm->tm_wday = ltm.tm_wday; ++ rtc_tm->tm_mon = ltm.tm_mon; ++ rtc_tm->tm_year = ltm.tm_year; ++ } else { ++ printk("invlaid data / time!\n"); ++ } ++ spin_unlock_irq(&rtc_lock); ++} ++ ++static int set_rtc_time(struct rtc_time *rtc_tm) ++{ ++ unsigned long lval; ++ ++ rtc_tm_to_time(rtc_tm, &lval); ++ ++ spin_lock_irq(&rtc_lock); ++ while ( !__rtc_write_ready() ) ; ++ REG_RTC_RSR = lval; ++ ++ spin_unlock_irq(&rtc_lock); ++ ++ return 0; ++ ++} ++ ++static void get_rtc_alm_time(struct rtc_time *alm_tm) ++{ ++ unsigned long lval; ++ struct rtc_time altm; ++ ++ spin_lock_irq(&rtc_lock); ++ while ( !__rtc_write_ready() ) ; ++ lval = REG_RTC_RSAR; ++ rtc_time_to_tm(lval, &altm); ++ if(rtc_valid_tm(&altm) == 0) { ++ /* is valid */ ++ alm_tm->tm_sec = altm.tm_sec; ++ alm_tm->tm_min = altm.tm_min; ++ alm_tm->tm_hour = altm.tm_hour; ++ alm_tm->tm_mday = altm.tm_mday; ++ alm_tm->tm_wday = altm.tm_wday; ++ alm_tm->tm_mon = altm.tm_mon; ++ alm_tm->tm_year = altm.tm_year; ++ } else { ++ printk("invlaid data / time in Line:%d!\n",__LINE__); ++ } ++ spin_unlock_irq(&rtc_lock); ++} ++ ++static int set_rtc_alm_time(struct rtc_time *alm_tm) ++{ ++ unsigned long lval; ++ ++ rtc_tm_to_time(alm_tm, &lval); ++ ++ spin_lock_irq(&rtc_lock); ++ while ( !__rtc_write_ready() ) ; ++ REG_RTC_RSAR = lval; ++ ++ while ( !__rtc_write_ready() ) ; /* set alarm function */ ++ if ( !((REG_RTC_RCR>>2) & 0x1) ) { ++ while ( !__rtc_write_ready() ) ; ++ __rtc_enable_alarm(); ++ } ++ ++ while ( !__rtc_write_ready() ) ; ++ if ( !(REG_RTC_RCR & RTC_RCR_AIE) ) { /* Enable alarm irq */ ++ __rtc_enable_alarm_irq(); ++ } ++ ++ spin_unlock_irq(&rtc_lock); ++ ++ return 0; ++} ++ ++static void get_rtc_wakeup_alarm(struct rtc_wkalrm *wkalm) ++{ ++ int enabled, pending; ++ ++ get_rtc_alm_time(&wkalm->time); ++ ++ spin_lock_irq(&rtc_lock); ++ while ( !__rtc_write_ready() ) ; ++ enabled = (REG_RTC_HWCR & 0x1); ++ pending = 0; ++ if ( enabled ) { ++ if ( (u32)REG_RTC_RSAR > (u32)REG_RTC_RSR ) /* 32bit val */ ++ pending = 1; ++ } ++ ++ wkalm->enabled = enabled; ++ wkalm->pending = pending; ++ spin_unlock_irq(&rtc_lock); ++} ++ ++static int set_rtc_wakeup_alarm(struct rtc_wkalrm *wkalm) ++{ ++ int enabled; ++ //int pending; ++ ++ enabled = wkalm->enabled; ++ //pending = wkalm->pending; /* Fix me, what's pending mean??? */ ++ ++ while ( !__rtc_write_ready() ) ; /* set wakeup alarm enable */ ++ if ( enabled != (REG_RTC_HWCR & 0x1) ) { ++ while ( !__rtc_write_ready() ) ; ++ REG_RTC_HWCR = (REG_RTC_HWCR & ~0x1) | enabled; ++ } ++ while ( !__rtc_write_ready() ) ; /* set alarm function */ ++ if ( enabled != ((REG_RTC_RCR>>2) & 0x1) ) { ++ while ( !__rtc_write_ready() ) ; ++ REG_RTC_RCR = (REG_RTC_RCR & ~(1<<2)) | (enabled<<2); ++ } ++ ++ if ( !enabled ) /* if disabled wkalrm, rturn. */ ++ { ++ return 0; ++ } ++ ++ while ( !__rtc_write_ready() ) ; ++ if ( !(REG_RTC_RCR & RTC_RCR_AIE) ) { /* Enable alarm irq */ ++ __rtc_enable_alarm_irq(); ++ } ++ ++ set_rtc_alm_time(&wkalm->time); ++ ++ return 0; ++} ++ ++ ++static void set_rtc_irq_bit( int bit ) ++{ ++ spin_lock_irq(&rtc_lock); ++ ++ while ( !__rtc_write_ready() ) ; ++ REG_RTC_RCR |= (1<= 0xc0 ++ * means "don't care" or "match all". Only the tm_hour, ++ * tm_min, and tm_sec values are filled in. ++ */ ++ ++ get_rtc_alm_time(&wtime); ++ return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; ++ ++ case RTC_ALM_SET: /* Store a time into the alarm */ ++ { ++ struct rtc_time alm_tm; ++ ++ if (copy_from_user(&alm_tm, (struct rtc_time*)arg, ++ sizeof(struct rtc_time))) ++ return -EFAULT; ++ if(rtc_valid_tm(&alm_tm) != 0) { ++ printk("invalid time set in Line:%d! \n",__LINE__); ++ return -EFAULT; ++ } ++ ++ return set_rtc_alm_time(&alm_tm); ++ } ++ case RTC_RD_TIME: /* Read the time/date from RTC */ ++ get_rtc_time(&wtime); ++ return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; ++ case RTC_SET_TIME: /* Set the RTC */ ++ { ++ struct rtc_time rtc_tm; ++ ++ if (!capable(CAP_SYS_TIME)) ++ return -EACCES; ++ ++ if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, ++ sizeof(struct rtc_time))) ++ return -EFAULT; ++ if(rtc_valid_tm(&rtc_tm) != 0) { ++ printk("invalid time set in Line:%d! \n",__LINE__); ++ return -EFAULT; ++ } ++ ++ return set_rtc_time(&rtc_tm); ++ } ++ case RTC_EPOCH_READ: /* Read the epoch. */ ++ return put_user (epoch, (unsigned long *)arg); ++ case RTC_EPOCH_SET: /* Set the epoch. */ ++ /* ++ * There were no RTC clocks before 1900. ++ */ ++ if (arg < 1900) ++ return -EINVAL; ++ ++ if (!capable(CAP_SYS_TIME)) ++ return -EACCES; ++ ++ epoch = arg; ++ return 0; ++ case RTC_WKALM_SET: /* Wake alarm set. */ ++ { ++ struct rtc_wkalrm wkalrm; ++ ++ if (copy_from_user(&wkalrm, (struct rtc_wkalrm*)arg, ++ sizeof(struct rtc_wkalrm))) ++ return -EFAULT; ++ return set_rtc_wakeup_alarm(&wkalrm); ++ } ++ case RTC_WKALM_RD: /* Wake alarm read. */ ++ { ++ struct rtc_wkalrm wkalrm; ++ get_rtc_wakeup_alarm(&wkalrm); ++ return copy_to_user((void *)arg, &wkalrm, sizeof(struct rtc_wkalrm)) ? -EFAULT : 0; ++ } ++ /* set power down: shut down the machine. */ ++ case RTC_POWER_DOWN: /* enter HIBERNATE mode */ ++ dprintk("Power down. Bye....\n"); ++ while ( !__rtc_write_ready() ) ; ++ REG_RTC_HCR = 0x1; ++ return 0; ++#ifdef DEBUG ++ case RTC_PRINT_REG: /* Print RTC registers */ ++ print_rtc_registers(); ++ return 0; ++#endif ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* ++ * We enforce only one user at a time here with the open/close. ++ * Also clear the previous interrupt data on an open, and clean ++ * up things on a close. ++ */ ++ ++/* We use rtc_lock to protect against concurrent opens. So the BKL is not ++ * needed here. Or anywhere else in this driver. */ ++static int rtc_open(struct inode *inode, struct file *file) ++{ ++ spin_lock_irq (&rtc_lock); ++ ++ if(rtc_status) ++ goto out_busy; ++ ++ rtc_status = 1; ++ ++ spin_unlock_irq (&rtc_lock); ++ return 0; ++ ++out_busy: ++ return -EBUSY; ++} ++ ++static int rtc_release(struct inode *inode, struct file *file) ++{ ++ ++ rtc_status = 0; ++ /* No need for locking -- nobody else can do anything until this rmw is ++ * committed, and no timer is running. */ ++ return 0; ++} ++ ++/* ++ * The various file operations we support. ++ */ ++ ++static struct file_operations rtc_fops = { ++ owner: THIS_MODULE, ++ llseek: no_llseek, ++ ioctl: rtc_ioctl, ++ open: rtc_open, ++ release: rtc_release, ++}; ++ ++ ++static void run_sbin_rtc_alarm( void ) ++{ ++ int i; ++ char *argv[2], *envp[3]; ++ ++ if (!sbin_rtc_alarm_handler_path[0]) ++ return; ++ ++ print_dbg(": sbin_rtc_alarm_handler_path=%s\n", sbin_rtc_alarm_handler_path); ++ ++ i = 0; ++ argv[i++] = sbin_rtc_alarm_handler_path; ++ argv[i] = 0; ++ ++ /* minimal command environment */ ++ i = 0; ++ envp[i++] = "HOME=/"; ++ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; ++ ++ /* other stuff we want to pass to /sbin/hotplug */ ++ ++ envp[i] = 0; ++ ++ call_usermodehelper (argv [0], argv, envp, 0); ++} ++ ++static void rtc_alarm_task_handler(struct work_struct *work) ++{ ++ run_sbin_rtc_alarm(); ++} ++ ++static struct work_struct rtc_alarm_task; ++ ++static irqreturn_t jz_rtc_interrupt(int irq, void *dev_id) ++{ ++ REG_RTC_HCR = 0x0; ++ printk("%s:%s:%d\n",__FILE__,__FUNCTION__,__LINE__); ++ spin_lock_irq(&rtc_lock); ++ ++ if ( __rtc_get_1Hz_flag() ) { ++ while ( !__rtc_write_ready() ) ; ++ __rtc_clear_1Hz_flag(); ++ dprintk("RTC 1Hz interrupt occur.\n"); ++ } ++ ++ if ( __rtc_get_alarm_flag() ) { /* rtc alarm interrupt */ ++ while ( !__rtc_write_ready() ) ; ++ __rtc_clear_alarm_flag(); ++ dprintk("RTC alarm interrupt occur.\n"); ++ //schedule_task( &rtc_alarm_task ); ++ schedule_work( &rtc_alarm_task ); ++ } ++ spin_unlock_irq(&rtc_lock); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++#define RTC_MINOR 135 ++ ++static struct miscdevice rtc_dev= ++{ ++ RTC_MINOR, ++ "rtc", ++ &rtc_fops ++}; ++ ++int __init Jz_rtc_init(void) ++{ ++ ++ INIT_WORK(&rtc_alarm_task, rtc_alarm_task_handler); ++ ++ /* Enabled rtc function, enable rtc alarm function */ ++ while ( !__rtc_write_ready() ) ; /* need we wait for WRDY??? */ ++ if ( !(REG_RTC_RCR & RTC_RCR_RTCE) || !(REG_RTC_RCR &RTC_RCR_AE) ) { ++ REG_RTC_RCR |= RTC_RCR_AE | RTC_RCR_RTCE; ++ } ++ /* clear irq flags */ ++ __rtc_clear_1Hz_flag(); ++ /* In a alarm reset, we expect a alarm interrupt. ++ * We can do something in the interrupt handler. ++ * So, do not clear alarm flag. ++ */ ++/* __rtc_clear_alarm_flag(); */ ++ ++ if (request_irq(IRQ_RTC, jz_rtc_interrupt, 0, "rtc", NULL) < 0) ++ return -EBUSY; ++ ++ misc_register(&rtc_dev); ++ ++ printk("JzSOC onchip RTC installed !!!\n"); ++ return 0; ++ ++} ++ ++void __exit Jz_rtc_exit (void) ++{ ++ misc_deregister(&rtc_dev); ++ free_irq (IRQ_RTC, NULL); ++} ++ ++module_init(Jz_rtc_init); ++module_exit(Jz_rtc_exit); ++ +diff -urN linux-2.6.24.7.old/drivers/char/rtc_jz.h linux-2.6.24.7/drivers/char/rtc_jz.h +--- linux-2.6.24.7.old/drivers/char/rtc_jz.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/rtc_jz.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,74 @@ ++#ifndef __RTC_JZ_H__ ++#define __RTC_JZ_H__ ++ ++//#define DEBUG 1 ++#undef DEBUG ++ ++#ifdef DEBUG ++#define dprintk(x...) printk(x) ++#define print_dbg(f, arg...) \ ++ printk("%s, %s[%d]:" f , __FUNCTION__, __FILE__, __LINE__ , ##arg ) ++#else ++#define dprintk(x...) ++#define print_dbg(n, arg...) ++#endif ++ ++ ++#ifdef DEBUG ++ ++static void print_rtc_time( struct rtc_time * tm ) ++{ ++ printk("%02d%02d-%02d:%02d:%02d-%d\n", tm->tm_mon, tm->tm_mday, ++ tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_year); ++ printk("sec:\t%d\n", tm->tm_sec); ++ printk("min:\t%d\n", tm->tm_min); ++ printk("hour:\t%d\n", tm->tm_hour); ++ printk("mday:\t%d\n", tm->tm_mday); ++ printk("mon:\t%d\n", tm->tm_mon); ++ printk("year:\t%d\n", tm->tm_year); ++ printk("wday:\t%d\n", tm->tm_wday); ++ printk("yday:\t%d\n", tm->tm_yday); ++ printk("isdst:\t%d\n", tm->tm_isdst); ++ ++} ++ ++static void print_rtc_registers( void ) ++{ ++ while ( !__rtc_write_ready() ) ; ++ printk("REG_RTC_RCR:\t 0x%8.8x\n", REG_RTC_RCR ); ++ printk("REG_RTC_RSR:\t 0x%8.8x\n", REG_RTC_RSR ); ++ printk("REG_RTC_RSAR:\t 0x%8.8x\n", REG_RTC_RSAR ); ++ printk("REG_RTC_RGR:\t 0x%8.8x\n", REG_RTC_RGR ); ++ printk("REG_RTC_HCR:\t 0x%8.8x\n", REG_RTC_HCR ); ++ printk("REG_RTC_HWFCR:\t 0x%8.8x\n", REG_RTC_HWFCR ); ++ printk("REG_RTC_HRCR:\t 0x%8.8x\n", REG_RTC_HRCR ); ++ printk("REG_RTC_HWCR:\t 0x%8.8x\n", REG_RTC_HWCR ); ++ printk("REG_RTC_HWRSR:\t 0x%8.8x\n", REG_RTC_HWRSR ); ++ printk("REG_RTC_HSPR:\t 0x%8.8x\n", REG_RTC_HSPR ); ++} ++ ++#define RTC_PRINT_REG _IOR('p', 0x12, unsigned long)/* Set power down */ ++#endif /* #ifdef DEBUG */ ++ ++ ++/* ++ * JZSOC ioctl calls that are permitted to the /dev/rtc interface ++ */ ++ ++#define RTC_ENABLED _IO('p', 0x11) /* enable rtc */ ++#define RTC_DISABLED _IO('p', 0x12) /* disable rtc */ ++#define RTC_ALM_ON _IO('p', 0x13) /* enable rtc */ ++#define RTC_ALM_OFF _IO('p', 0x14) /* disable rtc */ ++#define RTC_1HZIE_ON _IO('p', 0x15) /* 1Hz int. enable on */ ++#define RTC_1HZIE_OFF _IO('p', 0x16) /* ... off */ ++ ++#define RTC_POWER_DOWN _IOR('p', 0x11, unsigned long)/* Set power down */ ++ ++/* Registers define */ ++/* RTC Control register */ ++#define RTC_AIE 3 /* jz4740_06_rtc_spec.pdf, RTC Control Register */ ++#define RTC_1HZIE 5 /* ... */ ++#define RTC_ALM_EN 2 /* ... */ ++#define RTC_EN 0 /* ... */ ++ ++#endif /* #define __RTC_JZ_H__ */ +diff -urN linux-2.6.24.7.old/drivers/char/rtc_pcf8563.c linux-2.6.24.7/drivers/char/rtc_pcf8563.c +--- linux-2.6.24.7.old/drivers/char/rtc_pcf8563.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/char/rtc_pcf8563.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,448 @@ ++/* ++ * PCF8563 Real Time Clock interface for Linux ++ * ++ * It only support 24Hour Mode, And the stored values are in BCD format. ++ * The alarm register is start at minute reg, no second alarm register. ++ */ ++ ++//#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include /* get the user-level API */ ++#include ++#include ++ ++/********************************************************************** ++ * register summary ++ **********************************************************************/ ++#define RTC_SECONDS 2 ++#define RTC_MINUTES 3 ++#define RTC_HOURS 4 ++#define RTC_DAY_OF_MONTH 5 ++#define RTC_DAY_OF_WEEK 6 ++#define RTC_MONTH 7 ++#define RTC_YEAR 8 ++ ++#define RTC_MINUTES_ALARM 9 ++#define RTC_HOURS_ALARM 0x0a ++#define RTC_DAY_ALARM 0x0b ++#define RTC_WEEKDAY_ALARM 0x0c ++ ++/* control registers - Moto names ++ */ ++#define RTC_CONTROL 0x00 ++#define RTC_STATUS 0x01 ++#define RTC_CLKOUT 0x0d ++#define RTC_TIMERCTL 0x0e ++#define RTC_TIMERCOUNTDOWN 0x0f ++ ++ ++/* example: !(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) ++ * determines if the following two #defines are needed ++ */ ++#ifndef BCD2BIN ++#define BCD2BIN(val) (((val) & 0x0f) + ((val) >> 4) * 10) ++#endif ++ ++#ifndef BIN2BCD ++#define BIN2BCD(val) ((((val) / 10) << 4) + (val) % 10) ++#endif ++ ++extern spinlock_t rtc_lock; ++extern void i2c_open(void); ++extern void i2c_close(void); ++extern int i2c_read(unsigned char device, unsigned char *buf, ++ unsigned char address, int count); ++extern int i2c_write(unsigned char device, unsigned char *buf, ++ unsigned char address, int count); ++/* ++ * We sponge a minor off of the misc major. No need slurping ++ * up another valuable major dev number for this. If you add ++ * an ioctl, make sure you don't conflict with SPARC's RTC ++ * ioctls. ++ */ ++ ++static int rtc_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg); ++ ++ ++static void get_rtc_time (struct rtc_time *rtc_tm); ++static void get_rtc_alm_time (struct rtc_time *alm_tm); ++ ++/* ++ * rtc_status is never changed by rtc_interrupt, and ioctl/open/close is ++ * protected by the big kernel lock. However, ioctl can still disable the timer ++ * in rtc_status and then with del_timer after the interrupt has read ++ * rtc_status but before mod_timer is called, which would then reenable the ++ * timer (but you would need to have an awful timing before you'd trip on it) ++ */ ++static unsigned long rtc_status = 0; /* bitmapped status byte. */ ++ ++/* ++ * If this driver ever becomes modularised, it will be really nice ++ * to make the epoch retain its value across module reload... ++ */ ++static unsigned int epoch = 1900; ++static const unsigned char days_in_mo[] = ++{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; ++ ++static unsigned char rtcframe[16]; ++ ++static void read_rtcframe(void) ++{ ++ i2c_open(); ++ i2c_read(0x51, rtcframe, 0, 16); ++ i2c_close(); ++} ++ ++static void write_rtcframe(void) ++{ ++ i2c_open(); ++ i2c_write(0x51, rtcframe, 0, 16); ++ i2c_close(); ++} ++ ++static void write_rtc(unsigned char addr, unsigned char val) ++{ ++ volatile unsigned char v = val; ++ i2c_open(); ++ i2c_write(0x51, (unsigned char *)&v, addr, 1); ++ i2c_close(); ++} ++ ++static unsigned char read_rtc(unsigned char addr) ++{ ++ volatile unsigned char v; ++ i2c_open(); ++ i2c_read(0x51, (unsigned char *)&v, addr, 1); ++ i2c_close(); ++ return v; ++} ++ ++static void CMOS_WRITE(unsigned char addr, unsigned char val) ++{ ++ rtcframe[addr] = val; ++} ++ ++static unsigned char CMOS_READ(unsigned char addr) ++{ ++ return rtcframe[addr]; ++} ++ ++static void get_rtc_time(struct rtc_time *rtc_tm) ++{ ++ unsigned char sec,mon,mday,wday,year,hour,min; ++ ++ /* ++ * Only the values that we read from the RTC are set. We leave ++ * tm_wday, tm_yday and tm_isdst untouched. Even though the ++ * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated ++ * by the RTC when initially set to a non-zero value. ++ */ ++ ++ spin_lock_irq(&rtc_lock); ++ read_rtcframe(); ++ sec = CMOS_READ(RTC_SECONDS) & ~0x80; ++ min = CMOS_READ(RTC_MINUTES) & ~0x80; ++ hour = CMOS_READ(RTC_HOURS) & ~0xc0; ++ mday = CMOS_READ(RTC_DAY_OF_MONTH) & ~0xc0; ++ wday = CMOS_READ(RTC_DAY_OF_WEEK) & ~0xf8; ++ mon = CMOS_READ(RTC_MONTH) & ~0xe0; ++ year = CMOS_READ(RTC_YEAR) ; ++ ++ rtc_tm->tm_sec = BCD2BIN(sec); ++ rtc_tm->tm_min = BCD2BIN(min); ++ rtc_tm->tm_hour = BCD2BIN(hour); ++ rtc_tm->tm_mday = BCD2BIN(mday); ++ rtc_tm->tm_wday = wday; ++ /* Don't use centry, but start from year 1970 */ ++ rtc_tm->tm_mon = BCD2BIN(mon); ++ year = BCD2BIN(year); ++ if ((year += (epoch - 1900)) <= 69) ++ year += 100; ++ rtc_tm->tm_year = year; ++ ++ spin_unlock_irq(&rtc_lock); ++ ++ ++ /* ++ * Account for differences between how the RTC uses the values ++ * and how they are defined in a struct rtc_time; ++ */ ++ rtc_tm->tm_mon--; ++} ++ ++static void get_rtc_alm_time(struct rtc_time *alm_tm) ++{ ++ unsigned char sec, min, hour; ++ ++ /* ++ * Only the values that we read from the RTC are set. That ++ * means only tm_hour, tm_min, and tm_sec. ++ */ ++ spin_lock_irq(&rtc_lock); ++ read_rtcframe(); ++ sec = 0; ++ min = CMOS_READ(RTC_MINUTES_ALARM); ++ hour = CMOS_READ(RTC_HOURS_ALARM); ++ ++ alm_tm->tm_sec = sec;//not set sec ++ alm_tm->tm_min = BCD2BIN(min); ++ alm_tm->tm_hour = BCD2BIN(hour); ++ ++ spin_unlock_irq(&rtc_lock); ++} ++ ++static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct rtc_time wtime; ++ switch (cmd) { ++ case RTC_ALM_READ: /* Read the present alarm time */ ++ { ++ /* ++ * This returns a struct rtc_time. Reading >= 0xc0 ++ * means "don't care" or "match all". Only the tm_hour, ++ * tm_min, and tm_sec values are filled in. ++ */ ++ ++ get_rtc_alm_time(&wtime); ++ break; ++ } ++ case RTC_ALM_SET: /* Store a time into the alarm */ ++ { ++ unsigned char hrs, min, sec; ++ struct rtc_time alm_tm; ++ ++ if (copy_from_user(&alm_tm, (struct rtc_time*)arg, ++ sizeof(struct rtc_time))) ++ return -EFAULT; ++ ++ hrs = alm_tm.tm_hour; ++ min = alm_tm.tm_min; ++ sec = alm_tm.tm_sec; ++ ++ ++ ++ if (hrs >= 24) ++ return -EINVAL; ++ ++ hrs = BIN2BCD(hrs); ++ ++ if (min >= 60) ++ return -EINVAL; ++ ++ min = BIN2BCD(min); ++ ++ if (sec >= 60) ++ return -EINVAL; ++ ++ spin_lock_irq(&rtc_lock); ++ read_rtcframe(); ++ CMOS_WRITE(RTC_HOURS_ALARM, hrs | 0x80); ++ CMOS_WRITE(RTC_MINUTES_ALARM, min | 0x80); ++ ++ CMOS_WRITE(RTC_DAY_ALARM, CMOS_READ(RTC_DAY_ALARM) | 0x80); ++ CMOS_WRITE(RTC_WEEKDAY_ALARM, CMOS_READ(RTC_WEEKDAY_ALARM) | 0x80); ++ CMOS_WRITE(RTC_STATUS, CMOS_READ(RTC_STATUS) | 0x02);/*open alarm int*/ ++ write_rtcframe(); ++ spin_unlock_irq(&rtc_lock); ++ break; ++ } ++ case RTC_RD_TIME: /* Read the time/date from RTC */ ++ { ++ get_rtc_time(&wtime); ++ break; ++ } ++ case RTC_SET_TIME: /* Set the RTC */ ++ { ++ struct rtc_time rtc_tm; ++ unsigned char mon, day, hrs, min, sec, leap_yr, date; ++ unsigned int yrs; ++// unsigned char ctr; ++ ++ if (!capable(CAP_SYS_TIME)) ++ return -EACCES; ++ ++ if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, ++ sizeof(struct rtc_time))) ++ return -EFAULT; ++ ++ ++ yrs = rtc_tm.tm_year + 1900; ++ mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ ++ day = rtc_tm.tm_wday; ++ date = rtc_tm.tm_mday; ++ hrs = rtc_tm.tm_hour; ++ min = rtc_tm.tm_min; ++ sec = rtc_tm.tm_sec; ++ ++ ++ if (yrs < 1970) ++ return -EINVAL; ++ ++ leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); ++ ++ if ((mon > 12) || (date == 0)) ++ return -EINVAL; ++ ++ if (date > (days_in_mo[mon] + ((mon == 2) && leap_yr))) ++ return -EINVAL; ++ ++ if ((hrs >= 24) || (min >= 60) || (sec >= 60)) ++ return -EINVAL; ++ ++ if ((yrs -= epoch) > 255) /* They are unsigned */ ++ return -EINVAL; ++ ++ spin_lock_irq(&rtc_lock); ++ /* These limits and adjustments are independant of ++ * whether the chip is in binary mode or not. ++ */ ++ if (yrs > 169) { ++ spin_unlock_irq(&rtc_lock); ++ return -EINVAL; ++ } ++ ++ if (yrs >= 100) ++ yrs -= 100; ++ ++ min = BIN2BCD(min); ++ sec = BIN2BCD(sec); ++ hrs = BIN2BCD(hrs); ++ mon = BIN2BCD(mon); ++ yrs = BIN2BCD(yrs); ++ date = BIN2BCD(date); ++ ++ read_rtcframe(); ++ CMOS_WRITE(RTC_SECONDS, sec ); ++ CMOS_WRITE(RTC_MINUTES, min); ++ CMOS_WRITE(RTC_HOURS, hrs); ++ CMOS_WRITE(RTC_DAY_OF_MONTH, date); ++ CMOS_WRITE(RTC_DAY_OF_WEEK, day); ++ CMOS_WRITE(RTC_MONTH, mon); ++ CMOS_WRITE(RTC_YEAR, yrs); ++ write_rtcframe(); ++ ++ spin_unlock_irq(&rtc_lock); ++ return 0; ++ } ++ case RTC_EPOCH_READ: /* Read the epoch. */ ++ { ++ return put_user (epoch, (unsigned long *)arg); ++ } ++ case RTC_EPOCH_SET: /* Set the epoch. */ ++ { ++ /* ++ * There were no RTC clocks before 1900. ++ */ ++ if (arg < 1900) ++ return -EINVAL; ++ ++ if (!capable(CAP_SYS_TIME)) ++ return -EACCES; ++ ++ epoch = arg; ++ return 0; ++ } ++ default: ++ return -EINVAL; ++ } ++ return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; ++} ++ ++/* ++ * We enforce only one user at a time here with the open/close. ++ * Also clear the previous interrupt data on an open, and clean ++ * up things on a close. ++ */ ++ ++/* We use rtc_lock to protect against concurrent opens. So the BKL is not ++ * needed here. Or anywhere else in this driver. */ ++static int rtc_open(struct inode *inode, struct file *file) ++{ ++ spin_lock_irq (&rtc_lock); ++ ++ if(rtc_status) ++ goto out_busy; ++ ++ rtc_status = 1; ++ ++ spin_unlock_irq (&rtc_lock); ++ return 0; ++ ++out_busy: ++ spin_unlock_irq (&rtc_lock); ++ return -EBUSY; ++} ++ ++static int rtc_release(struct inode *inode, struct file *file) ++{ ++ ++ ++ /* No need for locking -- nobody else can do anything until this rmw is ++ * committed, and no timer is running. */ ++ rtc_status = 0; ++ return 0; ++} ++ ++/* ++ * The various file operations we support. ++ */ ++ ++static struct file_operations rtc_fops = { ++ owner: THIS_MODULE, ++ llseek: no_llseek, ++ ioctl: rtc_ioctl, ++ open: rtc_open, ++ release: rtc_release, ++}; ++ ++#define RTC_MINOR 135 ++ ++static struct miscdevice rtc_dev= ++{ ++ RTC_MINOR, ++ "rtc", ++ &rtc_fops ++}; ++ ++int __init pcf_rtc_init(void) ++{ ++ int r; ++ unsigned char ctr; ++ r = misc_register(&rtc_dev); ++ ++ ctr = read_rtc(RTC_CONTROL); ++ write_rtc(RTC_CONTROL,0x00 ); ++ ++ read_rtcframe(); ++ CMOS_WRITE(RTC_STATUS, 0x00); ++ CMOS_WRITE(RTC_CLKOUT, 0x80); ++ /* RTC clock out, 32.768k */ ++ ++ CMOS_WRITE(RTC_TIMERCTL, 0x00); ++ CMOS_WRITE(RTC_TIMERCOUNTDOWN, 0x00); ++ write_rtcframe(); ++ ++ printk("PCF8563 RTC installed !!!\n"); ++ return 0; ++ ++} ++ ++void __exit pcf_rtc_exit (void) ++{ ++ misc_deregister(&rtc_dev); ++} ++ ++module_init(pcf_rtc_init); ++module_exit(pcf_rtc_exit); +diff -urN linux-2.6.24.7.old/drivers/i2c/busses/Kconfig linux-2.6.24.7/drivers/i2c/busses/Kconfig +--- linux-2.6.24.7.old/drivers/i2c/busses/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/i2c/busses/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -4,6 +4,14 @@ + + menu "I2C Hardware Bus support" + ++config I2C_JZ47XX ++ tristate "JZ47XX I2C Interface support" ++ depends on SOC_JZ4730 || SOC_JZ4740 ++ help ++ If you have devices in the Ingenic JZ4730/JZ4740 I2C bus, say yes to ++ this option. This driver can also be built as a module. If so, the ++ module will be called i2c-jz47xx. ++ + config I2C_ALI1535 + tristate "ALI 1535" + depends on PCI +diff -urN linux-2.6.24.7.old/drivers/i2c/busses/Makefile linux-2.6.24.7/drivers/i2c/busses/Makefile +--- linux-2.6.24.7.old/drivers/i2c/busses/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/i2c/busses/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -2,6 +2,7 @@ + # Makefile for the i2c bus drivers. + # + ++obj-$(CONFIG_I2C_JZ47XX) += i2c-jz47xx.o + obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o + obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o + obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o +diff -urN linux-2.6.24.7.old/drivers/i2c/busses/i2c-jz47xx.c linux-2.6.24.7/drivers/i2c/busses/i2c-jz47xx.c +--- linux-2.6.24.7.old/drivers/i2c/busses/i2c-jz47xx.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/i2c/busses/i2c-jz47xx.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,330 @@ ++/* ++ * i2c_jz47xx.c ++ * I2C adapter for the INGENIC I2C bus access. ++ * ++ * Copyright (C) 2006 - 2008 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include "i2c-jz47xx.h" ++ ++/* I2C protocol */ ++#define I2C_READ 1 ++#define I2C_WRITE 0 ++ ++#define TIMEOUT 1000 ++unsigned short sub_addr = 0; ++int addr_val = 0; ++struct jz_i2c { ++ spinlock_t lock; ++ wait_queue_head_t wait; ++ struct i2c_msg *msg; ++ unsigned int msg_num; ++ unsigned int slave_addr; ++ struct i2c_adapter adap; ++ struct clk *clk; ++}; ++ ++/* ++ * I2C bus protocol basic routines ++ */ ++static int i2c_put_data(unsigned char data) ++{ ++ unsigned int timeout = TIMEOUT*10; ++ ++ __i2c_write(data); ++ __i2c_set_drf(); ++ while (__i2c_check_drf() != 0); ++ while (!__i2c_transmit_ended()); ++ while (!__i2c_received_ack() && timeout) ++ timeout--; ++ ++ if (timeout) ++ return 0; ++ else ++ return -ETIMEDOUT; ++} ++ ++static int i2c_get_data(unsigned char *data, int ack) ++{ ++ int timeout = TIMEOUT*10; ++ ++ if (!ack) ++ __i2c_send_nack(); ++ else ++ __i2c_send_ack(); ++ ++ while (__i2c_check_drf() == 0 && timeout) ++ timeout--; ++ ++ if (timeout) { ++ if (!ack) ++ __i2c_send_stop(); ++ *data = __i2c_read(); ++ __i2c_clear_drf(); ++ return 0; ++ } else ++ return -ETIMEDOUT; ++} ++ ++/* ++ * I2C interface ++ */ ++void i2c_jz_setclk(unsigned int i2cclk) ++{ ++ __i2c_set_clk(jz_clocks.extalclk, i2cclk); ++} ++ ++static int xfer_read(unsigned char device, struct i2c_adapter *adap, unsigned char *buf, int length) ++{ ++ int cnt = length; ++ int timeout = 5; ++ ++ device = (0xa << 3) | ((sub_addr & 0x0700) >> 8); ++ sub_addr = sub_addr & 0xff; ++ ++L_try_again: ++ ++ if (timeout < 0) ++ goto L_timeout; ++ ++ __i2c_send_nack(); /* Master does not send ACK, slave sends it */ ++ ++ if (addr_val) { ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) ++ goto device_werr; ++ if (i2c_put_data(sub_addr) < 0) ++ goto address_err; ++ } ++ __i2c_send_start(); ++ ++ if (i2c_put_data((device << 1) | I2C_READ ) < 0) ++ goto device_rerr; ++ ++ __i2c_send_ack(); /* Master sends ACK for continue reading */ ++ __i2c_send_start(); ++ ++ while (cnt) { ++ if (cnt == 1) { ++ if (i2c_get_data(buf, 0) < 0) ++ break; ++ } else { ++ if (i2c_get_data(buf, 1) < 0) ++ break; ++ } ++ cnt--; ++ buf++; ++ } ++ addr_val = 0; ++ return length - cnt; ++ device_rerr: ++ device_werr: ++ address_err: ++ timeout --; ++ __i2c_send_stop(); ++ goto L_try_again; ++ ++L_timeout: ++ __i2c_send_stop(); ++ printk("Read I2C device 0x%2x failed.\n", device); ++ return -ENODEV; ++} ++ ++static int xfer_write(unsigned char device, struct i2c_adapter *adap, unsigned char *buf, int length) ++{ ++ int cnt = length; ++ int cnt_in_pg; ++ int timeout = 5; ++ unsigned char *tmpbuf; ++ unsigned char tmpaddr; ++ ++ device = (0xa << 3) | ((sub_addr & 0x0700) >> 8); ++ sub_addr = sub_addr & 0xff; ++ ++ __i2c_send_nack(); /* Master does not send ACK, slave sends it */ ++ ++ W_try_again: ++ if (timeout < 0) ++ goto W_timeout; ++ ++ cnt = length; ++ tmpbuf = (unsigned char *)buf; ++ tmpaddr = device; ++ start_write_page: ++ cnt_in_pg = 0; ++ __i2c_send_start(); ++ if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) ++ goto device_err; ++ if (addr_val) { ++ if (i2c_put_data(sub_addr) < 0) ++ goto address_err; ++ } ++ while (cnt) { ++ if (++cnt_in_pg > 8) { ++ __i2c_send_stop(); ++ mdelay(1); ++ sub_addr += 8; ++ goto start_write_page; ++ } ++ if (i2c_put_data(*tmpbuf) < 0) ++ break; ++ cnt--; ++ tmpbuf++; ++ } ++ __i2c_send_stop(); ++ addr_val = 0; ++ return length - cnt; ++ device_err: ++ address_err: ++ timeout--; ++ __i2c_send_stop(); ++ goto W_try_again; ++ ++W_timeout: ++ printk(KERN_DEBUG "Write I2C device 0x%2x failed.\n", device); ++ __i2c_send_stop(); ++ return -ENODEV; ++} ++ ++static int i2c_jz_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg, int num) ++{ ++ int ret, i; ++ ++ dev_dbg(&adap->dev, "jz47xx_xfer: processing %d messages:\n", num); ++ for (i = 0; i < num; i++) { ++ dev_dbg(&adap->dev, " #%d: %sing %d byte%s %s 0x%02x\n", i, ++ pmsg->flags & I2C_M_RD ? "read" : "writ", ++ pmsg->len, pmsg->len > 1 ? "s" : "", ++ pmsg->flags & I2C_M_RD ? "from" : "to", pmsg->addr); ++ if (pmsg->len && pmsg->buf) { /* sanity check */ ++ if (pmsg->flags & I2C_M_RD) ++ ret = xfer_read(pmsg->addr, adap, pmsg->buf, pmsg->len); ++ else ++ ret = xfer_write(pmsg->addr, adap, pmsg->buf, pmsg->len); ++ ++ if (ret) ++ return ret; ++ /* Wait until transfer is finished */ ++ } ++ dev_dbg(&adap->dev, "transfer complete\n"); ++ pmsg++; /* next message */ ++ } ++ return i; ++} ++ ++static u32 i2c_jz_functionality(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; ++} ++ ++static const struct i2c_algorithm i2c_jz_algorithm = { ++ .master_xfer = i2c_jz_xfer, ++ .functionality = i2c_jz_functionality, ++}; ++ ++static int i2c_jz_probe(struct platform_device *dev) ++{ ++ struct jz_i2c *i2c; ++ struct i2c_jz_platform_data *plat = dev->dev.platform_data; ++ int ret; ++ ++ __i2c_set_clk(jz_clocks.extalclk, 10000); /* default 10 KHz */ ++ __i2c_enable(); ++ ++ i2c = kzalloc(sizeof(struct jz_i2c), GFP_KERNEL); ++ if (!i2c) { ++ printk("There is no enough memory\n"); ++ ret = -ENOMEM; ++ goto emalloc; ++ } ++ ++ i2c->adap.owner = THIS_MODULE; ++ i2c->adap.algo = &i2c_jz_algorithm; ++ i2c->adap.retries = 5; ++ spin_lock_init(&i2c->lock); ++ init_waitqueue_head(&i2c->wait); ++ sprintf(i2c->adap.name, "jz_i2c-i2c.%u", dev->id); ++ i2c->adap.algo_data = i2c; ++ i2c->adap.dev.parent = &dev->dev; ++ ++ if (plat) { ++ i2c->adap.class = plat->class; ++ } ++ ++ /* ++ * If "dev->id" is negative we consider it as zero. ++ * The reason to do so is to avoid sysfs names that only make ++ * sense when there are multiple adapters. ++ */ ++ i2c->adap.nr = dev->id != -1 ? dev->id : 0; ++ /* ret = i2c_add_adapter(&i2c->adap); */ ++ ret = i2c_add_numbered_adapter(&i2c->adap); ++ if (ret < 0) { ++ printk(KERN_INFO "I2C: Failed to add bus\n"); ++ goto eadapt; ++ } ++ ++ platform_set_drvdata(dev, i2c); ++ dev_info(&dev->dev, "JZ47xx i2c bus driver.\n"); ++ return 0; ++eadapt: ++ __i2c_disable(); ++emalloc: ++ return ret; ++} ++ ++static int i2c_jz_remove(struct platform_device *dev) ++{ ++ struct i2c_adapter *adapter = platform_get_drvdata(dev); ++ int rc; ++ ++ rc = i2c_del_adapter(adapter); ++ platform_set_drvdata(dev, NULL); ++ return rc; ++} ++ ++static struct platform_driver i2c_jz_driver = { ++ .probe = i2c_jz_probe, ++ .remove = i2c_jz_remove, ++ .driver = { ++ .name = "jz_i2c", ++ }, ++}; ++ ++static int __init i2c_adap_jz_init(void) ++{ ++ return platform_driver_register(&i2c_jz_driver); ++} ++ ++static void __exit i2c_adap_jz_exit(void) ++{ ++ return platform_driver_unregister(&i2c_jz_driver); ++} ++ ++MODULE_LICENSE("GPL"); ++ ++module_init(i2c_adap_jz_init); ++module_exit(i2c_adap_jz_exit); +diff -urN linux-2.6.24.7.old/drivers/i2c/busses/i2c-jz47xx.h linux-2.6.24.7/drivers/i2c/busses/i2c-jz47xx.h +--- linux-2.6.24.7.old/drivers/i2c/busses/i2c-jz47xx.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/i2c/busses/i2c-jz47xx.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,20 @@ ++/* ++ * i2c_jz47xx.h ++ * ++ * 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 _I2C_JZ_H_ ++#define _I2C_JZ_H_ ++ ++struct i2c_slave_client; ++ ++struct i2c_jz_platform_data { ++ unsigned int slave_addr; ++ struct i2c_slave_client *slave; ++ unsigned int class; ++}; ++ ++extern void jz_set_i2c_info(struct i2c_jz_platform_data *info); ++#endif +diff -urN linux-2.6.24.7.old/drivers/i2c/i2c-dev.c linux-2.6.24.7/drivers/i2c/i2c-dev.c +--- linux-2.6.24.7.old/drivers/i2c/i2c-dev.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/i2c/i2c-dev.c 2009-04-12 18:13:57.000000000 +0200 +@@ -36,8 +36,9 @@ + #include + #include + ++extern unsigned short sub_addr; ++extern int addr_val; + static struct i2c_driver i2cdev_driver; +- + /* + * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a + * slave (i2c_client) with which messages will be exchanged. It's coupled +@@ -422,6 +423,14 @@ + case I2C_TIMEOUT: + client->adapter->timeout = arg; + break; ++ case I2C_SET_SUB_ADDRESS: ++ addr_val = 1; ++ sub_addr = arg; ++ break; ++ case I2C_SET_CLOCK: ++ i2c_jz_setclk(arg); ++ break; ++ + default: + /* NOTE: returning a fault code here could cause trouble + * in buggy userspace code. Some old kernel bugs returned +diff -urN linux-2.6.24.7.old/drivers/input/keyboard/Kconfig linux-2.6.24.7/drivers/input/keyboard/Kconfig +--- linux-2.6.24.7.old/drivers/input/keyboard/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/input/keyboard/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -259,9 +259,27 @@ + To compile this driver as a module, choose M here: the + module will be called aaed2000_kbd. + ++config KEYBOARD_JZ ++ tristate "JZ keypad support" ++ depends on JZSOC ++ help ++ Enable Y here to support JZ keypad. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called jz-keypad. ++ ++config 5x5_KEYBOARD_JZ ++ tristate "JZ 5x5 keypad support" ++ depends on JZSOC ++ help ++ Enable Y here to support JZ keypad. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called jz-keypad. ++ + config KEYBOARD_GPIO +- tristate "GPIO Buttons" +- depends on GENERIC_GPIO ++ tristate "JZ GPIO Buttons support" ++# depends on GENERIC_GPIO + help + This driver implements support for buttons connected + to GPIO pins of various CPUs (and some other chips). +@@ -272,7 +290,7 @@ + with configuration data saying which GPIOs are used. + + To compile this driver as a module, choose M here: the +- module will be called gpio-keys. ++ module will be called gpio_keys. + + config KEYBOARD_MAPLE + tristate "Maple bus keyboard" +diff -urN linux-2.6.24.7.old/drivers/input/keyboard/Makefile linux-2.6.24.7/drivers/input/keyboard/Makefile +--- linux-2.6.24.7.old/drivers/input/keyboard/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/input/keyboard/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -19,6 +19,8 @@ + obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o + obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o + obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keyboard.o ++obj-$(CONFIG_KEYBOARD_JZ) += jz_keypad.o ++obj-$(CONFIG_5x5_KEYBOARD_JZ) += jz_keypad_5x5.o + obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o + obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o + obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o +diff -urN linux-2.6.24.7.old/drivers/input/keyboard/gpio_keys.c linux-2.6.24.7/drivers/input/keyboard/gpio_keys.c +--- linux-2.6.24.7.old/drivers/input/keyboard/gpio_keys.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/input/keyboard/gpio_keys.c 2009-04-12 18:13:57.000000000 +0200 +@@ -1,7 +1,13 @@ + /* +- * Driver for keys on GPIO lines capable of generating interrupts. ++ * linux/drivers/input/keyboard/gpio_keys.c + * +- * Copyright 2005 Phil Blundell ++ * JZ GPIO Buttons driver for JZ4740 PAVO ++ * ++ * User applications can access to this device via /dev/input/eventX. ++ * ++ * Copyright (c) 2005 - 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: Richard + * + * 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 +@@ -23,26 +29,131 @@ + #include + #include + #include +- + #include ++#include ++ ++ ++#define SCAN_INTERVAL (10) ++ ++/* ++ * GPIO Buttons ++ */ ++#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE) ++static struct gpio_keys_button pavo_buttons[] = { ++ { ++ .gpio = 96, ++ .code = KEY_1, ++ .desc = "Button 0", ++ .active_low = 1, ++ }, ++ { ++ .gpio = 97, ++ .code = KEY_2, ++ .desc = "Button 1", ++ .active_low = 1, ++ }, ++ { ++ .gpio = 98, ++ .code = KEY_3, ++ .desc = "Button 2", ++ .active_low = 1, ++ }, ++ { ++ .gpio = 99, ++ .code = KEY_4, ++ .desc = "Button 3", ++ .active_low = 1, ++ } ++}; ++ ++static struct timer_list button_timer; ++static spinlock_t gpio_lock; ++static int button_no; ++ ++static struct gpio_keys_platform_data pavo_button_data = { ++ .buttons = pavo_buttons, ++ .nbuttons = ARRAY_SIZE(pavo_buttons), ++}; ++ ++static struct platform_device pavo_button_device = { ++ .name = "gpio-keys", ++ .id = -1, ++ .num_resources = 0, ++ .dev = { ++ .platform_data = &pavo_button_data, ++ } ++}; ++ ++static void __init pavo_add_device_buttons(void) ++{ ++ __gpio_as_input(96); ++ __gpio_as_irq_fall_edge(96); ++ ++ __gpio_as_input(97); ++ __gpio_as_irq_fall_edge(97); ++ ++ __gpio_as_input(98); ++ __gpio_as_irq_fall_edge(98); ++ ++ __gpio_as_input(99); ++ __gpio_as_irq_fall_edge(99); ++ ++ platform_device_register(&pavo_button_device); ++} ++#else ++static void __init pavo_add_device_buttons(void) {} ++#endif ++ ++static void __init pavo_board_init(void) ++{ ++ /* Push Buttons */ ++ pavo_add_device_buttons(); ++} ++ ++static void button_timer_callback(unsigned long data) ++{ ++ unsigned long flags; ++ int gpio = pavo_buttons[button_no].gpio; ++ int code = pavo_buttons[button_no].code; ++ struct platform_device *pdev = (struct platform_device *)data; ++ struct input_dev *input = platform_get_drvdata(pdev); ++ int state; ++ ++ spin_lock_irqsave(&gpio_lock, flags); ++ state = __gpio_get_pin(gpio); ++ ++ if (state == 0) { ++ /* press down */ ++ input_report_key(input, code, 1); ++ input_sync(input); ++ mod_timer(&button_timer, jiffies + SCAN_INTERVAL); ++ } else { ++ /* up */ ++ input_report_key(input, code, 0); ++ input_sync(input); ++ udelay(1000); ++ __gpio_as_irq_fall_edge(gpio); ++ } ++ spin_unlock_irqrestore(&gpio_lock, flags); ++} + + static irqreturn_t gpio_keys_isr(int irq, void *dev_id) + { + int i; + struct platform_device *pdev = dev_id; + struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; +- struct input_dev *input = platform_get_drvdata(pdev); + ++ __gpio_ack_irq(irq - IRQ_GPIO_0); + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_keys_button *button = &pdata->buttons[i]; + int gpio = button->gpio; + +- if (irq == gpio_to_irq(gpio)) { +- unsigned int type = button->type ?: EV_KEY; +- int state = (gpio_get_value(gpio) ? 1 : 0) ^ button->active_low; +- +- input_event(input, type, button->code, !!state); +- input_sync(input); ++ if (irq == (gpio + IRQ_GPIO_0) ) { ++ /* start timer */ ++ __gpio_as_input(gpio); ++ button_no = i; ++ mod_timer(&button_timer, jiffies + 2 * SCAN_INTERVAL); ++ break; + } + } + +@@ -62,7 +173,7 @@ + + platform_set_drvdata(pdev, input); + +- input->evbit[0] = BIT_MASK(EV_KEY); ++ spin_lock_init(&gpio_lock); + + input->name = pdev->name; + input->phys = "gpio-keys/input0"; +@@ -72,56 +183,42 @@ + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; ++ input->evbit[0] = BIT(EV_KEY) | BIT(EV_SYN) | BIT(EV_REP); + + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_keys_button *button = &pdata->buttons[i]; + int irq; + unsigned int type = button->type ?: EV_KEY; + +- error = gpio_request(button->gpio, button->desc ?: "gpio_keys"); +- if (error < 0) { +- pr_err("gpio-keys: failed to request GPIO %d," +- " error %d\n", button->gpio, error); +- goto fail; +- } +- +- error = gpio_direction_input(button->gpio); +- if (error < 0) { +- pr_err("gpio-keys: failed to configure input" +- " direction for GPIO %d, error %d\n", +- button->gpio, error); +- gpio_free(button->gpio); +- goto fail; +- } +- +- irq = gpio_to_irq(button->gpio); ++ irq = IRQ_GPIO_0 + button->gpio; + if (irq < 0) { + error = irq; + pr_err("gpio-keys: Unable to get irq number" + " for GPIO %d, error %d\n", + button->gpio, error); +- gpio_free(button->gpio); + goto fail; + } + + error = request_irq(irq, gpio_keys_isr, +- IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING | +- IRQF_TRIGGER_FALLING, ++ IRQF_SAMPLE_RANDOM | IRQF_DISABLED, + button->desc ? button->desc : "gpio_keys", + pdev); + if (error) { + pr_err("gpio-keys: Unable to claim irq %d; error %d\n", + irq, error); +- gpio_free(button->gpio); + goto fail; + } + + if (button->wakeup) + wakeup = 1; +- + input_set_capability(input, type, button->code); + } + ++ /* Init timer */ ++ init_timer(&button_timer); ++ button_timer.data = (unsigned long)&pavo_button_device; ++ button_timer.function = button_timer_callback; ++ + error = input_register_device(input); + if (error) { + pr_err("gpio-keys: Unable to register input device, " +@@ -135,8 +232,7 @@ + + fail: + while (--i >= 0) { +- free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev); +- gpio_free(pdata->buttons[i].gpio); ++ free_irq(pdata->buttons[i].gpio + IRQ_GPIO_0 , pdev); + } + + platform_set_drvdata(pdev, NULL); +@@ -154,9 +250,8 @@ + device_init_wakeup(&pdev->dev, 0); + + for (i = 0; i < pdata->nbuttons; i++) { +- int irq = gpio_to_irq(pdata->buttons[i].gpio); ++ int irq = pdata->buttons[i].gpio + IRQ_GPIO_0; + free_irq(irq, pdev); +- gpio_free(pdata->buttons[i].gpio); + } + + input_unregister_device(input); +@@ -175,7 +270,7 @@ + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_keys_button *button = &pdata->buttons[i]; + if (button->wakeup) { +- int irq = gpio_to_irq(button->gpio); ++ int irq = button->gpio + IRQ_GPIO_0; + enable_irq_wake(irq); + } + } +@@ -193,7 +288,7 @@ + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_keys_button *button = &pdata->buttons[i]; + if (button->wakeup) { +- int irq = gpio_to_irq(button->gpio); ++ int irq = button->gpio + IRQ_GPIO_0; + disable_irq_wake(irq); + } + } +@@ -218,11 +313,13 @@ + + static int __init gpio_keys_init(void) + { ++ pavo_board_init(); + return platform_driver_register(&gpio_keys_device_driver); + } + + static void __exit gpio_keys_exit(void) + { ++ platform_device_unregister(&pavo_button_device); + platform_driver_unregister(&gpio_keys_device_driver); + } + +diff -urN linux-2.6.24.7.old/drivers/input/keyboard/jz_keypad.c linux-2.6.24.7/drivers/input/keyboard/jz_keypad.c +--- linux-2.6.24.7.old/drivers/input/keyboard/jz_keypad.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/input/keyboard/jz_keypad.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,357 @@ ++/* ++ * linux/drivers/input/keyboard/jz_keypad.c ++ * ++ * JZ Keypad Driver ++ * ++ * Copyright (c) 2005 - 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: Richard ++ * ++ * 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#define KB_ROWS 3 ++#define KB_COLS 3 ++ ++#define SCAN_INTERVAL (10) ++ ++static unsigned short col[KB_COLS] = {85,87,91}; ++static unsigned short row[KB_ROWS] = {60,61,62}; ++static unsigned short s0[KB_COLS]; ++static unsigned short s1[KB_COLS]={7,7,7}; ++static unsigned short precol,prerow; ++ ++static const unsigned int jz_kbd_keycode[KB_COLS * KB_ROWS] = { ++ KEY_1, KEY_4, KEY_7, ++ KEY_2, KEY_5, 0, ++ KEY_3, KEY_6, 0, ++}; ++ ++struct jz_kbd { ++ unsigned int keycode[ARRAY_SIZE(jz_kbd_keycode)]; ++ struct input_dev *input; ++ char phys[32]; ++ ++ spinlock_t lock; ++ struct timer_list timer; ++ ++ unsigned int suspended; ++ unsigned long suspend_jiffies; ++}; ++ ++static struct jz_kbd g_jz_kbd; ++ ++static inline void jz_scan_kbd(unsigned short *s) ++{ ++ int i; ++ ++ if (!s) ++ return; ++ ++ for (i = 0; i < KB_COLS; i++) { ++ ++ __gpio_as_input(85); /* row */ ++ __gpio_as_input(87); /* row */ ++ __gpio_as_input(91); /* row */ ++ ++ __gpio_as_input(60); /* col */ ++ __gpio_as_input(61); /* col */ ++ __gpio_as_input(62); /* col */ ++ ++ __gpio_clear_pin(col[i]); ++ __gpio_as_output(col[i]); ++ ++ udelay(1000); ++ s[i]=(__gpio_get_pin(60) << 0) | (__gpio_get_pin(61) << 1) | ++ (__gpio_get_pin(62) << 2); ++ } ++} ++ ++static void jz_kbd_scankeyboard(struct jz_kbd *kbd_data) ++{ ++ unsigned int row,col; ++ unsigned long flags; ++ unsigned int num_pressed; ++ ++ if (kbd_data->suspended) ++ return; ++ ++ spin_lock_irqsave(&kbd_data->lock, flags); ++ ++ num_pressed = 0; ++ jz_scan_kbd(s0); ++ ++ /* look for key if pressed down on not, col & row */ ++ if (s0[0] == 7 && s0[1] == 7 && s0[2] == 7) { ++ if (s1[0] != 7 || s1[1] != 7 || s1[2] != 7) { ++ /* up */ ++ input_report_key(kbd_data->input, kbd_data->keycode[prerow * KB_COLS + precol], 0); ++ input_sync(kbd_data->input); ++ } ++ precol = prerow = -1; ++ s1[0] = s1[1] = s1[2] = 7; ++ spin_unlock_irqrestore(&kbd_data->lock, flags); ++ return; ++ } ++ ++ if (s0[0] == 6 && s0[1] == 7 && s0[2] == 7) { ++ row = 0;//K7 ++ col = 2; ++ goto find_row_col; ++ } ++ if (s0[0] == 7 && s0[1] == 3 && s0[2] == 7) { ++ row = 2;//k6 ++ col = 1; ++ goto find_row_col; ++ } ++ if (s0[0] == 7 && s0[1] == 5 && s0[2] == 7) { ++ row = 1;//k5 ++ col = 1; ++ goto find_row_col; ++ } ++ if (s0[0] == 7 && s0[1] == 6 && s0[2] == 7) { ++ row = 0;//k4 ++ col = 1; ++ goto find_row_col; ++ } ++ if (s0[0] == 7 && s0[1] == 7 && s0[2] == 3) { ++ row = 2;//k3 ++ col = 0; ++ goto find_row_col; ++ } ++ if (s0[0] == 7 && s0[1] == 7 && s0[2] == 5) { ++ row = 1;//k2 ++ col = 0; ++ goto find_row_col; ++ } ++ if (s0[0] == 7 && s0[1] == 7 && s0[2] == 6) { ++ row = 0;//k1 ++ col = 0; ++ goto find_row_col; ++ } ++ /* 2 or 3 buttons are pressed */ ++ s0[0] = s0[1] = s0[2] = 7; ++ s1[0] = s1[1] = s1[2] = 7; ++ prerow = precol = -1; ++ spin_unlock_irqrestore(&kbd_data->lock, flags); ++ return; ++find_row_col: ++ if (s1[0] == 7 && s1[1] == 7 && s1[2] == 7) { ++ /* down */ ++ input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); ++ input_sync(kbd_data->input); ++ s1[0] = s0[0]; ++ s1[1] = s0[1]; ++ s1[2] = s0[2]; ++ ++ precol = col; ++ prerow = row; ++ spin_unlock_irqrestore(&kbd_data->lock, flags); ++ return; ++ } ++ if (s1[0] != 7 || s1[1] != 7 || s1[2] != 7) { ++ /* is the same as the preview key */ ++ if (s0[0] == s1[0] && s0[1] == s1[1] && s0[2] == s1[2]) { ++ input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); ++ input_sync(kbd_data->input); ++ s1[0] = s0[0]; ++ s1[1] = s0[1]; ++ s1[2] = s0[2]; ++ ++ precol = col; ++ prerow = row; ++ spin_unlock_irqrestore(&kbd_data->lock, flags); ++ return; ++ } else { ++ /* the preview key is up and other key is down */ ++ if (s0[0] != s1[0] || s0[1] != s1[1] || s0[2] != s1[2]) { ++ input_report_key(kbd_data->input, kbd_data->keycode[prerow * KB_COLS + precol], 0); ++ input_sync(kbd_data->input); ++ input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); ++ input_sync(kbd_data->input); ++ s1[0] = s0[0]; ++ s1[1] = s0[1]; ++ s1[2] = s0[2]; ++ precol = col; ++ prerow = row; ++ spin_unlock_irqrestore(&kbd_data->lock, flags); ++ return; ++ } ++ } ++ } ++} ++ ++static void jz_kbd_timer_callback(unsigned long data) ++{ ++ jz_kbd_scankeyboard(&g_jz_kbd); ++ mod_timer(&g_jz_kbd.timer, jiffies + SCAN_INTERVAL); ++} ++ ++#ifdef CONFIG_PM ++static int jz_kbd_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct jz_kbd *jz_kbd = platform_get_drvdata(dev); ++ jz_kbd->suspended = 1; ++ ++ return 0; ++} ++ ++static int jz_kbd_resume(struct platform_device *dev) ++{ ++ struct jz_kbd *jz_kbd = platform_get_drvdata(dev); ++ ++ jz_kbd->suspend_jiffies = jiffies; ++ jz_kbd->suspended = 0; ++ ++ return 0; ++} ++#else ++#define jz_kbd_suspend NULL ++#define jz_kbd_resume NULL ++#endif ++ ++static int __init jz_kbd_probe(struct platform_device *dev) ++{ ++ struct input_dev *input_dev; ++ int i, error; ++ ++ input_dev = input_allocate_device(); ++ if (!input_dev) ++ return -ENOMEM; ++ ++ platform_set_drvdata(dev, &g_jz_kbd); ++ ++ strcpy(g_jz_kbd.phys, "input/kbd0"); ++ ++ spin_lock_init(&g_jz_kbd.lock); ++ ++ g_jz_kbd.suspend_jiffies = jiffies; ++ g_jz_kbd.input = input_dev; ++ ++ input_dev->private = &g_jz_kbd; ++ input_dev->name = "JZ Keypad"; ++ input_dev->phys = g_jz_kbd.phys; ++ input_dev->cdev.dev = &dev->dev; ++ ++ input_dev->id.bustype = BUS_PARPORT; ++ input_dev->id.vendor = 0x0001; ++ input_dev->id.product = 0x0001; ++ input_dev->id.version = 0x0100; ++ ++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_SYN); ++ input_dev->keycode = g_jz_kbd.keycode; /* keycode array address */ ++ input_dev->keycodesize = sizeof(unsigned int); ++ input_dev->keycodemax = ARRAY_SIZE(jz_kbd_keycode); ++ ++ memcpy(g_jz_kbd.keycode, jz_kbd_keycode, sizeof(g_jz_kbd.keycode)); ++ ++ for (i = 0; i < ARRAY_SIZE(jz_kbd_keycode); i++) ++ set_bit(g_jz_kbd.keycode[i], input_dev->keybit); ++ ++ //clear_bit(0, input_dev->keybit); ++ ++ __gpio_as_input(85); ++ __gpio_as_input(87); ++ __gpio_as_input(91); ++ ++#if 0 ++ __gpio_as_input(60); ++ __gpio_as_input(61); ++ __gpio_as_input(62); ++#endif ++ ++ /* Init Keyboard rescan timer */ ++ init_timer(&g_jz_kbd.timer); ++ g_jz_kbd.timer.function = jz_kbd_timer_callback; ++ g_jz_kbd.timer.data = (unsigned long)&g_jz_kbd; ++ mod_timer(&g_jz_kbd.timer, jiffies + SCAN_INTERVAL); ++ ++ error = input_register_device(input_dev); ++ if (error) { ++ pr_err("gpio-keys: Unable to register input device, " ++ "error: %d\n", error); ++ } ++ printk("input: JZ Keypad Registered\n"); ++ ++ return 0; ++} ++ ++static int jz_kbd_remove(struct platform_device *dev) ++{ ++ struct jz_kbd *jz_kbd = platform_get_drvdata(dev); ++ ++ del_timer_sync(&jz_kbd->timer); ++ ++ __gpio_as_input(85); ++ __gpio_as_input(87); ++ __gpio_as_input(91); ++ ++ /* These pins is conficting with cs8900a's CS RD WE pins on JZ4740-PAVO board */ ++ __gpio_as_input(60); ++ __gpio_as_input(61); ++ __gpio_as_input(62); ++ ++ input_unregister_device(jz_kbd->input); ++ ++ return 0; ++} ++ ++static struct platform_driver jz_kbd_driver = { ++ .probe = jz_kbd_probe, ++ .remove = jz_kbd_remove, ++ .suspend = jz_kbd_suspend, ++ .resume = jz_kbd_resume, ++ .driver = { ++ .name = "jz-keypad", ++ }, ++}; ++ ++/* ++ * Jz Keyboard Device ++ */ ++static struct platform_device jzkbd_device = { ++ .name = "jz-keypad", ++ .id = -1, ++}; ++ ++static int __init jz_kbd_init(void) ++{ ++ platform_device_register(&jzkbd_device); ++ return platform_driver_register(&jz_kbd_driver); ++} ++ ++static void __exit jz_kbd_exit(void) ++{ ++ platform_device_unregister(&jzkbd_device); ++ platform_driver_unregister(&jz_kbd_driver); ++} ++ ++module_init(jz_kbd_init); ++module_exit(jz_kbd_exit); ++ ++MODULE_AUTHOR("Richard"); ++MODULE_DESCRIPTION("JZ keypad driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/drivers/input/keyboard/jz_keypad_5x5.c linux-2.6.24.7/drivers/input/keyboard/jz_keypad_5x5.c +--- linux-2.6.24.7.old/drivers/input/keyboard/jz_keypad_5x5.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/input/keyboard/jz_keypad_5x5.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,329 @@ ++/* ++ * JZ Keypad ( 5 x 5 ) Driver ++ * ++ * Copyright (c) 2005 - 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: Jason 20090210 ++ * ++ * 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define KB_ROWS 5 ++#define KB_COLS 5 ++ ++#define SCAN_INTERVAL (10) ++ ++#define ROW_KEYBIT_MASK 0xFFE0 ++ ++#define SET_GPIOS_AS_INPUT() \ ++do { \ ++ unsigned short i; \ ++ \ ++ for (i = 0; i < KB_ROWS; i++) { \ ++ __gpio_as_input(jz_row_gpios[i]); \ ++ __gpio_as_input(jz_col_gpios[i]); \ ++ } \ ++} while (0) ++ ++ ++#define GET_ROW_GPIO_PINS() \ ++({ \ ++ unsigned short _pins = 0, i; \ ++ for (i = 0; \ ++ i < KB_ROWS; \ ++ _pins |= __gpio_get_pin(jz_row_gpios[i]) << i, i++) \ ++ ; \ ++ _pins; \ ++}) ++ ++#define CHECK_IF_KEY_PRESSED(s) \ ++({ \ ++ unsigned short i; \ ++ for (i = 0; i < KB_COLS && s[i] == 0x1F ; i++) \ ++ ; \ ++ i != KB_ROWS; \ ++}) ++ ++#define CLEAN_SCAN_RESULT(s) \ ++do { \ ++ unsigned short i; \ ++ for (i = 0; i < KB_COLS; s[i++] = 0x1F) \ ++ ; \ ++} while (0) ++ ++ ++static const unsigned short jz_col_gpios[KB_ROWS] = {76, 75, 74, 73, 72}; ++static const unsigned short jz_row_gpios[KB_COLS] = {181, 182, 79, 78, 77}; ++ ++static const unsigned int jz_kbd_keycode[KB_ROWS * KB_COLS] = { ++ KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, ++ KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, ++ KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, ++ KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, ++ KEY_LEFTSHIFT, KEY_CAPSLOCK, KEY_SPACE, KEY_BACKSPACE, KEY_Y ++}; ++ ++struct jz_kbd { ++ unsigned int keycode[ARRAY_SIZE(jz_kbd_keycode)]; ++ struct input_dev *input; ++ char phys[32]; ++ ++ spinlock_t lock; ++ struct timer_list timer; ++ ++ unsigned int suspended; ++ unsigned long suspend_jiffies; ++}; ++static struct jz_kbd g_jz_kbd; ++ ++static unsigned short scan_result[KB_COLS]; ++static unsigned short pre_scan_result[KB_COLS] = {0x1F, 0x1F, 0x1F, 0x1F, 0x1F}; ++static unsigned short pre_col, pre_row; ++ ++/** ++ * Scan keypad by reading GPIO pins. ++ */ ++static inline void jz_do_scan(unsigned short *s) ++{ ++ unsigned short i; ++ ++ if (!s) ++ return ; ++ ++ for (i = 0; i < KB_COLS; i++) { ++ ++ SET_GPIOS_AS_INPUT(); ++ __gpio_clear_pin(jz_col_gpios[i]); ++ __gpio_as_output(jz_col_gpios[i]); ++ ++ udelay(1000); ++ ++ s[i] = GET_ROW_GPIO_PINS(); ++ } ++} ++ ++/** ++ * Call scan function and handle 'GPIO event'(like key down, key up), ++ * and report it to upper layer of input subsystem ... if necessary ++ */ ++static void jz_kbd_scan(struct jz_kbd *kbd_data) ++{ ++ unsigned short row, col, i; ++ unsigned long flags; ++ ++ if (kbd_data->suspended) ++ return; ++ ++ spin_lock_irqsave(&kbd_data->lock, flags); ++ ++ jz_do_scan(scan_result); ++ ++ /* check if any key was pressed or not */ ++ if (!CHECK_IF_KEY_PRESSED(scan_result)) { ++ ++ /* key up */ ++ if (CHECK_IF_KEY_PRESSED(pre_scan_result)) { ++ input_report_key(kbd_data->input, kbd_data->keycode[pre_row * KB_COLS + pre_col], 0); ++ input_sync(kbd_data->input); ++ } ++ pre_col = pre_row = 0xFFFF; ++ CLEAN_SCAN_RESULT(pre_scan_result); ++ ++ spin_unlock_irqrestore(&kbd_data->lock, flags); ++ return; ++ } ++ ++ /* find the key */ ++ for (row = 0; row < KB_ROWS; row++) { ++ for (i = scan_result[row], col = 0; col < KB_COLS; col++) { ++ if ( !(i & 0x01) ) ++ break; ++ i >>= 1; ++ } ++ if (col != KB_COLS) ++ break; ++ } ++ ++ //printk("[DRIVER] row = %d, col = %d, key code: 0x%02X\n", row, col, kbd_data->keycode[row * KB_COLS + col]); ++ ++ /* the same as the preview one || new key */ ++ if ( (col == pre_col && row == pre_row) ++ || (pre_col == 0xFFFF && pre_row == 0xFFFF) ) { ++ ++ input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); ++ input_sync(kbd_data->input); ++ ++ } else { ++ /* the preview key is up and other key is down */ ++ input_report_key(kbd_data->input, kbd_data->keycode[pre_row * KB_COLS + col], 0); ++ input_sync(kbd_data->input); ++ input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); ++ input_sync(kbd_data->input); ++ } ++ ++ for (i = 0; i < KB_ROWS; i++) { ++ pre_scan_result[i] = scan_result[i]; ++ } ++ ++ pre_col = col; ++ pre_row = row; ++ ++ spin_unlock_irqrestore(&kbd_data->lock, flags); ++ ++ return; ++} ++ ++static void jz_kbd_timer_callback(unsigned long data) ++{ ++ jz_kbd_scan(&g_jz_kbd); ++ mod_timer(&g_jz_kbd.timer, jiffies + SCAN_INTERVAL); ++} ++ ++#ifdef CONFIG_PM ++static int jz_kbd_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct jz_kbd *jz_kbd = platform_get_drvdata(dev); ++ jz_kbd->suspended = 1; ++ ++ return 0; ++} ++ ++static int jz_kbd_resume(struct platform_device *dev) ++{ ++ struct jz_kbd *jz_kbd = platform_get_drvdata(dev); ++ ++ jz_kbd->suspend_jiffies = jiffies; ++ jz_kbd->suspended = 0; ++ ++ return 0; ++} ++ ++#else ++#define jz_kbd_suspend NULL ++#define jz_kbd_resume NULL ++#endif ++ ++/** ++ * Driver init ++ */ ++static int __init jz_kbd_probe(struct platform_device *dev) ++{ ++ struct input_dev *input_dev; ++ int i, error; ++ ++ input_dev = input_allocate_device(); ++ if (!input_dev) ++ return -ENOMEM; ++ ++ platform_set_drvdata(dev, &g_jz_kbd); ++ ++ strcpy(g_jz_kbd.phys, "input/kbd0"); ++ ++ spin_lock_init(&g_jz_kbd.lock); ++ ++ g_jz_kbd.suspend_jiffies = jiffies; ++ g_jz_kbd.input = input_dev; ++ ++ input_dev->private = &g_jz_kbd; ++ input_dev->name = "JZ 5x5 Keypad"; ++ input_dev->phys = g_jz_kbd.phys; ++ input_dev->cdev.dev = &dev->dev; ++ ++ input_dev->id.bustype = BUS_PARPORT; ++ input_dev->id.vendor = 0x0001; ++ input_dev->id.product = 0x0001; ++ input_dev->id.version = 0x0100; ++ ++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_SYN); ++ input_dev->keycode = g_jz_kbd.keycode; ++ input_dev->keycodesize = sizeof(unsigned int); ++ input_dev->keycodemax = ARRAY_SIZE(jz_kbd_keycode); ++ ++ memcpy(g_jz_kbd.keycode, jz_kbd_keycode, sizeof(g_jz_kbd.keycode)); ++ ++ for ( i = 0; i < ARRAY_SIZE(jz_kbd_keycode); i++) ++ set_bit(g_jz_kbd.keycode[i], input_dev->keybit); ++ ++ init_timer(&g_jz_kbd.timer); ++ g_jz_kbd.timer.function = jz_kbd_timer_callback; ++ g_jz_kbd.timer.data = (unsigned long)&g_jz_kbd; ++ mod_timer(&g_jz_kbd.timer, jiffies + SCAN_INTERVAL); ++ ++ error = input_register_device(input_dev); ++ if (error) { ++ pr_err("gpio-keys: Unable to register input device, " ++ "error: %d\n", error); ++ } ++ printk("input: JZ 5x5 Keypad Registered.\n"); ++ ++ return 0; ++} ++ ++static int jz_kbd_remove(struct platform_device *dev) ++{ ++ struct jz_kbd *kbd = platform_get_drvdata(dev); ++ ++ del_timer_sync(&kbd->timer); ++ ++ SET_GPIOS_AS_INPUT(); ++ ++ input_unregister_device(kbd->input); ++ ++ return 0; ++} ++ ++static struct platform_driver jz_kbd_driver = { ++ .probe = jz_kbd_probe, ++ .remove = jz_kbd_remove, ++ .suspend= jz_kbd_suspend, ++ .resume = jz_kbd_resume, ++ .driver = { ++ .name = "jz-5x5-keypad", ++ }, ++}; ++ ++static struct platform_device jzkbd_device = { ++ .name = "jz-5x5-keypad", ++ .id = -1, ++}; ++ ++static int __init jz_kbd_init(void) ++{ ++ platform_device_register(&jzkbd_device); ++ return platform_driver_register(&jz_kbd_driver); ++} ++ ++static void __exit jz_kbd_exit(void) ++{ ++ platform_device_unregister(&jzkbd_device); ++ platform_driver_unregister(&jz_kbd_driver); ++} ++ ++module_init(jz_kbd_init); ++module_exit(jz_kbd_exit); ++ ++MODULE_AUTHOR("Jason "); ++MODULE_DESCRIPTION("JZ 5x5 keypad driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/drivers/media/video/Kconfig linux-2.6.24.7/drivers/media/video/Kconfig +--- linux-2.6.24.7.old/drivers/media/video/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/media/video/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -514,6 +514,15 @@ + Say Y here to build in support for the Vino video input system found + on SGI Indy machines. + ++config VIDEO_JZ_CIM ++ tristate 'JzSOC Camera Interface Module (CIM) support' ++ depends on VIDEO_V4L2 ++ select VIDEO_JZ_SENSOR ++ ++config VIDEO_JZ_SENSOR ++ tristate "Jz generic camera sensor driver" ++ depends on VIDEO_JZ_CIM ++ + config VIDEO_STRADIS + tristate "Stradis 4:2:2 MPEG-2 video driver (EXPERIMENTAL)" + depends on EXPERIMENTAL && PCI && VIDEO_V4L1 && VIRT_TO_BUS +diff -urN linux-2.6.24.7.old/drivers/media/video/Makefile linux-2.6.24.7/drivers/media/video/Makefile +--- linux-2.6.24.7.old/drivers/media/video/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/media/video/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -15,6 +15,9 @@ + obj-$(CONFIG_VIDEO_DEV) += v4l1-compat.o + endif + ++obj-$(CONFIG_VIDEO_JZ_CIM) += jz_cim.o ++obj-$(CONFIG_VIDEO_JZ_SENSOR) += jz_sensor.o ++ + obj-$(CONFIG_VIDEO_BT848) += bt8xx/ + obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o + obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o +diff -urN linux-2.6.24.7.old/drivers/media/video/jz_cim.c linux-2.6.24.7/drivers/media/video/jz_cim.c +--- linux-2.6.24.7.old/drivers/media/video/jz_cim.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/media/video/jz_cim.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,622 @@ ++/* ++ * linux/drivers/char/jzchar/cim.c ++ * ++ * Camera Interface Module (CIM) driver for JzSOC ++ * This driver is independent of the camera sensor ++ * ++ * Copyright (C) 2005 JunZheng semiconductor ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++//#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define CIM_NAME "cim" ++ ++MODULE_AUTHOR("Jianli Wei"); ++MODULE_DESCRIPTION("JzSOC Camera Interface Module driver"); ++MODULE_LICENSE("GPL"); ++ ++#undef DEBUG ++//#define DEBUG ++#ifdef DEBUG ++#define dprintk(x...) printk(x) ++#else ++#define dprintk(x...) ++#endif ++/* ++ * Define the Max Image Size ++ */ ++#define MAX_IMAGE_WIDTH 2048 ++#define MAX_IMAGE_HEIGHT 2048 ++#define MAX_IMAGE_BPP 16 ++#define MAX_FRAME_SIZE (MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT * MAX_IMAGE_BPP / 8) ++#define CIM_RAM_ADDR (CIM_BASE + 0x1000) ++ ++typedef struct ++{ ++ u32 width; ++ u32 height; ++ u32 bpp; ++} img_param_t; ++ ++typedef struct ++{ ++ u32 cfg; ++ u32 ctrl; ++ u32 mclk; ++} cim_config_t; ++ ++/* ++ * IOCTL_XXX commands ++ */ ++#define IOCTL_SET_IMG_PARAM 0 // arg type: img_param_t * ++#define IOCTL_CIM_CONFIG 1 // arg type: cim_config_t * ++#define IOCTL_STOP_CIM 2 // arg type: void ++#define IOCTL_GET_IMG_PARAM 3 // arg type: img_param_t * ++#define IOCTL_GET_CIM_CONFIG 4 // arg type: cim_config_t * ++#define IOCTL_TEST_CIM_RAM 5 // no arg type * ++ ++/* ++ * CIM DMA descriptor ++ */ ++struct cim_desc { ++ u32 nextdesc; /* Physical address of next desc */ ++ u32 framebuf; /* Physical address of frame buffer */ ++ u32 frameid; /* Frame ID */ ++ u32 dmacmd; /* DMA command */ ++ u32 pagenum; ++}; ++ ++/* ++ * CIM device structure ++ */ ++struct cim_device { ++ struct video_device *jz_cim; ++ unsigned char *framebuf; ++ unsigned int frame_size; ++ unsigned int page_order; ++ wait_queue_head_t wait_queue; ++ struct cim_desc *frame_desc __attribute__ ((aligned (16))); ++}; ++ ++/* global*/ ++static struct cim_device *cim_dev; ++ ++/*========================================================================== ++ * CIM init routines ++ *========================================================================*/ ++#if defined(CONFIG_SOC_JZ4750) ++static void cim_image_area(img_param_t *c) { ++ /*set the image data area start 0, 0, lines_per_frame and pixels_per_line*/ ++ REG_CIM_SIZE = 0; ++ REG_CIM_OFFSET = 0; ++ if (REG_CIM_CTRL & CIM_CTRL_SIZEEN_MASK) { ++ REG_CIM_SIZE = (c->height << CIM_SIZE_LPF_BIT) | (c->width << CIM_SIZE_PPL_BIT); ++ REG_CIM_OFFSET = (0 << CIM_OFFSET_V_BIT) | (0 << CIM_OFFSET_H_BIT); ++// REG_CIM_OFFSET = (100 << CIM_OFFSET_V_BIT) | (50 << CIM_OFFSET_H_BIT); ++ } ++} ++#endif ++ ++static void cim_config(cim_config_t *c) ++{ ++ REG_CIM_CFG = c->cfg; ++ REG_CIM_CTRL = c->ctrl; ++ ++ /*Set the master clock output*/ ++#if defined(CONFIG_SOC_JZ4730) ++ __cim_set_master_clk(__cpm_get_sclk(), c->mclk); ++#elif defined(CONFIG_SOC_JZ4740) ++ __cim_set_master_clk(__cpm_get_hclk(), c->mclk); ++#elif defined(CONFIG_SOC_JZ4750) ++ __cim_set_master_clk(__cpm_get_hclk(), c->mclk); ++#else ++ __cim_set_master_clk(__cpm_get_hclk(), c->mclk); ++#endif ++ /* Enable sof, eof and stop interrupts*/ ++ __cim_enable_sof_intr(); ++ __cim_enable_eof_intr(); ++ __cim_enable_stop_intr(); ++} ++ ++/*========================================================================== ++ * CIM start/stop operations ++ *========================================================================*/ ++static int cim_start_dma(char *ubuf) ++{ ++ struct cim_desc *jz_frame_desc; ++ int cim_frame_size = 0; ++ jz_frame_desc = cim_dev->frame_desc; ++ dprintk("framedesc = %x\n", (u32) jz_frame_desc); ++ __cim_disable(); ++ dprintk("__cim_disable\n"); ++ __cim_set_da(virt_to_phys(cim_dev->frame_desc)); ++ __cim_clear_state(); // clear state register ++ __cim_reset_rxfifo(); // resetting rxfifo ++ __cim_unreset_rxfifo(); ++ __cim_enable_dma(); // enable dma ++ __cim_enable(); ++ ++ dprintk("__cim_enable\n"); ++// while(1) { ++// mdelay(10); ++// dprintk("REG_CIM_DA = 0x%08x\n", REG_CIM_DA); ++// dprintk("REG_CIM_FA = 0x%08x\n", REG_CIM_FA); ++// dprintk("REG_CIM_FID = 0x%08x\n", REG_CIM_FID); ++// dprintk("REG_CIM_CMD = 0x%08x\n", REG_CIM_CMD); ++// dprintk("REG_CIM_CFG = 0x%08x\n", REG_CIM_CFG); ++// dprintk("REG_CIM_STATE = 0x%08x\n", REG_CIM_STATE); ++// dprintk("REG_CIM_CTRL = 0x%08x\n", REG_CIM_CTRL); ++// dprintk("REG_CIM_SIZE = 0x%08x\n", REG_CIM_SIZE); ++// dprintk("REG_CIM_OFFSET = 0x%08x\n", REG_CIM_OFFSET); ++// mdelay(100); ++// } ++ // wait for interrupts ++ interruptible_sleep_on(&cim_dev->wait_queue); ++ dprintk("interruptible_sleep_on\n"); ++ dprintk("REG_CIM_DA = 0x%08x\n", REG_CIM_DA); ++ dprintk("REG_CIM_FA = 0x%08x\n", REG_CIM_FA); ++ dprintk("REG_CIM_FID = 0x%08x\n", REG_CIM_FID); ++ dprintk("REG_CIM_CMD = 0x%08x\n", REG_CIM_CMD); ++ dprintk("REG_CIM_CFG = 0x%08x\n", REG_CIM_CFG); ++ dprintk("REG_CIM_STATE = 0x%08x\n", REG_CIM_STATE); ++ dprintk("REG_CIM_CTRL = 0x%08x\n", REG_CIM_CTRL); ++ dprintk("REG_CIM_SIZE = 0x%08x\n", REG_CIM_SIZE); ++ dprintk("REG_CIM_OFFSET = 0x%08x\n", REG_CIM_OFFSET); ++ dprintk("REG_CIM_CMD_3 = %x\n", REG_CIM_CMD); ++ dprintk("REG_CIM_FA = %x\n", REG_CIM_FA); ++ /* copy frame data to user buffer */ ++ jz_frame_desc = cim_dev->frame_desc; ++ ++ while(jz_frame_desc != NULL) ++ { ++ dprintk("ubuf = %x, framebuf = %x,frame_size= %d\n", (u32)ubuf,(u32) jz_frame_desc->framebuf, jz_frame_desc->dmacmd & 0xffffff); ++ memcpy(ubuf, phys_to_virt(jz_frame_desc->framebuf), ((jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4)); ++ ubuf += (jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4; ++ cim_frame_size += (jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4; ++ jz_frame_desc = (struct cim_desc *)phys_to_virt(jz_frame_desc->nextdesc); ++ } ++ return cim_dev->frame_size; ++} ++static void cim_stop(void) ++{ ++ __cim_disable(); ++ __cim_clear_state(); ++} ++ ++/*========================================================================== ++ * Framebuffer allocation and destroy ++ *========================================================================*/ ++static void cim_fb_destroy(void) ++{ ++ int pages; ++ struct cim_desc *jz_frame_desc, *p_desc; ++ if (cim_dev->frame_desc == NULL) { ++ printk("Original memory is NULL\n"); ++ return; ++ } ++ jz_frame_desc = cim_dev->frame_desc; ++ while (jz_frame_desc != NULL) { ++ dprintk("framebuf = %x,thisdesc = %x,frame_size= %d\n", (u32) jz_frame_desc->framebuf, (unsigned int)jz_frame_desc, (jz_frame_desc->dmacmd & 0xffffff) * 4); ++ p_desc = (struct cim_desc *)phys_to_virt(jz_frame_desc->nextdesc); ++ pages = jz_frame_desc->pagenum; ++ dprintk("page_order = %d\n", pages); ++ free_pages((unsigned long)phys_to_virt(jz_frame_desc->framebuf), pages); ++ kfree(jz_frame_desc); ++ jz_frame_desc = p_desc; ++ } ++ cim_dev->frame_desc = NULL; ++} ++ ++static struct cim_desc *get_desc_list(int page_order) ++{ ++ int num, page_nums = 0; ++ unsigned char *p_buf; ++ struct cim_desc *desc_list_head __attribute__ ((aligned (16))); ++ struct cim_desc *desc_list_tail __attribute__ ((aligned (16))); ++ struct cim_desc *p_desc; ++// num = page_order - 1; ++ num = page_order; ++ desc_list_head = desc_list_tail = NULL; ++ ++ while(page_nums < (1 << page_order)) { ++ p_desc = (struct cim_desc *)kmalloc(sizeof(struct cim_desc), GFP_KERNEL); ++ if (NULL == p_desc) ++ return NULL; ++ //return -ENOMEM; ++ cim_realloc_pages: ++ p_buf = (unsigned char *)__get_free_pages(GFP_KERNEL, num); ++ if ( !(p_buf) && num != 0) { ++ num --; ++ goto cim_realloc_pages; ++ } ++ else if ( !(p_buf) && num == 0) { ++ printk("No memory can be alloc!\n"); ++ //return -ENOMEM; ++ return NULL; ++ } ++ else { ++ if (desc_list_head == NULL) { ++ dprintk("Page_list_head\n"); ++ desc_list_head = p_desc; ++ } ++ ++ else ++ desc_list_tail->nextdesc = virt_to_phys(p_desc); ++ ++ desc_list_tail = p_desc; ++ desc_list_tail->framebuf = virt_to_phys(p_buf); ++ dprintk("framebuf addr is 0x%08x\n", (u32)desc_list_tail->framebuf); ++ dprintk("frame_desc addr is 0x%08x\n",(u32)virt_to_phys(desc_list_tail)); ++ ++ desc_list_tail->frameid = 0x52052018; ++ desc_list_tail->pagenum = num; ++ if ((page_nums + (1<< num)) < (1 << page_order)) { ++ desc_list_tail->dmacmd = ((1 << num) * 4096) >> 2 ; ++ } ++ else ++ desc_list_tail->dmacmd = ++ (cim_dev->frame_size - page_nums * 4096) >> 2 ; ++ dprintk("the desc_list_tail->dmacmd is 0x%08x\n", desc_list_tail->dmacmd); ++ page_nums += (1 << num); ++ dprintk("the pages_num is %d\n", page_nums); ++ } ++ } ++ ++ desc_list_tail->nextdesc = virt_to_phys(NULL); ++ /* stop after capturing a frame */ ++ desc_list_tail->dmacmd |= (CIM_CMD_STOP | CIM_CMD_EOFINT); ++ dprintk("the desc_list_tail->dmacmd is 0x%08x\n", desc_list_tail->dmacmd); ++ ++ return desc_list_head; ++} ++ ++static int cim_fb_alloc(int img_width, int img_height, int img_bpp) ++{ ++#if defined(CONFIG_SOC_JZ4750) ++ if ((REG_CIM_CFG & (CIM_CFG_DF_MASK | CIM_CFG_BYPASS_MASK)) == 0) ++ cim_dev->frame_size = img_width * (img_height-1) * (img_bpp/8); ++ else ++ cim_dev->frame_size = img_width * img_height * (img_bpp/8); ++#else ++ cim_dev->frame_size = img_width * img_height * (img_bpp/8); ++#endif ++ cim_dev->page_order = get_order(cim_dev->frame_size); ++ dprintk("cim_dev->page_order=%d\n", cim_dev->page_order); ++ /* frame buffer ?? need large mem ??*/ ++ cim_dev->frame_desc = get_desc_list(cim_dev->page_order); ++ if (cim_dev->frame_desc == NULL) ++ return -ENOMEM; ++ dma_cache_wback((unsigned long)(cim_dev->frame_desc), 16); ++ return 0; ++} ++ ++/*========================================================================== ++ * File operations ++ *========================================================================*/ ++ ++static int cim_open(struct inode *inode, struct file *filp); ++static int cim_release(struct inode *inode, struct file *filp); ++static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l); ++static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l); ++static int cim_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); ++static int cim_mmap(struct file *file, struct vm_area_struct *vma); ++ ++static struct file_operations cim_fops = ++{ ++ open: cim_open, ++ release: cim_release, ++ read: cim_read, ++ write: cim_write, ++ ioctl: cim_ioctl, ++ compat_ioctl: v4l_compat_ioctl32, ++ mmap: cim_mmap ++}; ++ ++static struct video_device jz_v4l_device = { ++ .name = "jz cim", ++ //.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE | ++ // VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY ++ .fops = &cim_fops, ++ .minor = -1, ++ .owner = THIS_MODULE, ++ .release = video_device_release, ++}; ++ ++static int cim_open(struct inode *inode, struct file *filp) ++{ ++ ++ try_module_get(THIS_MODULE); ++ return 0; ++} ++ ++static int cim_release(struct inode *inode, struct file *filp) ++{ ++ cim_fb_destroy(); ++ cim_stop(); ++ ++ module_put(THIS_MODULE); ++ return 0; ++} ++ ++static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l) ++{ ++ if (size < cim_dev->frame_size) ++ return -EINVAL; ++ dprintk("read cim\n"); ++ return cim_start_dma(buf); ++} ++ ++static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l) ++{ ++ printk("cim error: write is not implemented\n"); ++ return -1; ++} ++ ++static int cim_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ void __user *argp = (void __user *)arg; ++ switch (cmd) { ++ case IOCTL_GET_IMG_PARAM: ++ { ++ img_param_t i; ++ return copy_to_user(argp, &i, sizeof(img_param_t)) ? -EFAULT : 0; ++ } ++ case IOCTL_SET_IMG_PARAM: ++ { ++ img_param_t i; ++ int img_width, img_height, img_bpp; ++ if (copy_from_user((void *)&i, (void *)arg, sizeof(img_param_t))) ++ return -EFAULT; ++#if defined(CONFIG_SOC_JZ4750) ++ cim_image_area(&i); ++#endif ++ img_width = i.width; ++ img_height = i.height; ++ img_bpp = i.bpp; ++ dprintk("ioctl_set_cim_param\n"); ++ if ((img_width * img_height * img_bpp/8) > MAX_FRAME_SIZE){ ++ printk("ERROR! Image is too large!\n"); ++ return -EINVAL; ++ } ++ /* allocate frame buffers */ ++ if (cim_dev->frame_desc == NULL){ ++ if (cim_fb_alloc(img_width, img_height, img_bpp) < 0){ ++ printk("ERROR! Init & alloc cim fail!\n"); ++ return -ENOMEM; ++ } ++ } ++ else ++ if ((img_width * img_height * img_bpp/8) > cim_dev->frame_size){ ++ /* realloc the buffer */ ++ cim_fb_destroy(); ++ if (cim_fb_alloc(img_width, img_height, img_bpp) < 0){ ++ printk("ERRROR! Init & alloc cim fail!\n"); ++ return -ENOMEM; ++ } ++ } ++ break; ++ } ++ case IOCTL_CIM_CONFIG: ++ { ++ cim_config_t c; ++ ++ if (copy_from_user((void *)&c, (void *)arg, sizeof(cim_config_t))) ++ return -EFAULT; ++ ++ cim_config(&c); ++ ++ break; ++ } ++ case IOCTL_TEST_CIM_RAM: ++ { ++ ++ int i; ++ volatile unsigned int *ptr; ++ ptr = (volatile unsigned int *)(CIM_RAM_ADDR); ++ printk("RAM test!\n"); ++ printk("CIM_RAM_ADDR = 0x%08x\n", CIM_RAM_ADDR); ++ for (i = 0; i < 1024; ptr++, i++) ++ *ptr = i; ++ ptr = (volatile unsigned int *)(CIM_RAM_ADDR); ++ dma_cache_wback((unsigned long)CIM_RAM_ADDR,0xffc); ++ ++ for (i = 0; i < 1024; i++) { ++ if (i != *ptr) ++ printk("*ptr!=i, *ptr=%d, i=%d\n", *ptr, i); ++ if (i%32 == 0) { ++ if (i%128 == 0) ++ printk("\n"); ++ printk("*ptr=%04d, i=%04d | ", *ptr, i); ++ } ++ ptr++; ++ } ++ printk("\n"); ++ break; ++ } ++ default: ++ printk("Not supported command: 0x%x\n", cmd); ++ return -EINVAL; ++ break; ++ } ++ return 0; ++} ++ ++/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */ ++static int cim_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ unsigned long start; ++ unsigned long off; ++ u32 len; ++ ++ off = vma->vm_pgoff << PAGE_SHIFT; ++ //fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); ++ ++ /* frame buffer memory */ ++ start = cim_dev->frame_desc->framebuf; ++ len = PAGE_ALIGN((start & ~PAGE_MASK) + (cim_dev->frame_desc->dmacmd & CIM_CMD_LEN_MASK)); ++ start &= PAGE_MASK; ++ ++ if ((vma->vm_end - vma->vm_start + off) > len) ++ return -EINVAL; ++ off += start; ++ ++ vma->vm_pgoff = off >> PAGE_SHIFT; ++ vma->vm_flags |= VM_IO; ++ ++#if defined(CONFIG_MIPS32) ++ pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; ++// pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NO_WA; /* WT cachable */ ++ pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; ++#endif ++ ++ if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, ++ vma->vm_end - vma->vm_start, ++ vma->vm_page_prot)) ++ vma->vm_flags |= VM_IO; ++ return -EAGAIN; ++ ++ return 0; ++} ++/*========================================================================== ++ * Interrupt handler ++ *========================================================================*/ ++ ++static irqreturn_t cim_irq_handler(int irq, void *dev_id) ++{ ++ u32 state = REG_CIM_STATE; ++ dprintk("REG_CIM_STATE = %x\n", REG_CIM_STATE); ++ dprintk("REG_CIM_CTRL = %x\n", REG_CIM_CTRL); ++#if 1 ++ if (state & CIM_STATE_RXF_OF) { ++ dprintk("OverFlow interrupt!\n"); ++ } ++#endif ++ if (state & CIM_STATE_DMA_EOF) { ++ dprintk("EOF interrupt!\n"); ++ __cim_disable_dma(); ++ __cim_disable(); ++ wake_up_interruptible(&cim_dev->wait_queue); ++ dprintk("EOF interrupt wake up!\n"); ++ } ++ ++ if (state & CIM_STATE_DMA_STOP) { ++ // Got a frame, wake up wait routine ++ __cim_disable_dma(); ++ __cim_disable(); ++ dprintk("Stop interrupt!\n"); ++ wake_up_interruptible(&cim_dev->wait_queue); ++ } ++#if 1 ++ if (state & CIM_STATE_RXF_TRIG) { ++ dprintk("Trig!\n"); ++ } ++#endif ++ ++ /* clear status flags*/ ++ REG_CIM_STATE = 0; ++ return IRQ_HANDLED; ++} ++ ++static int v4l_device_init(void) ++{ ++ cim_dev = kzalloc(sizeof(struct cim_device), GFP_KERNEL); ++ if (!cim_dev) return -ENOMEM; ++ cim_dev->jz_cim = video_device_alloc(); ++ if (!cim_dev->jz_cim) { ++ return -ENOMEM; ++ } ++ memcpy(cim_dev->jz_cim, &jz_v4l_device, sizeof(struct video_device)); ++ cim_dev->frame_desc = NULL; ++ cim_dev->frame_size = 0; ++ cim_dev->page_order = 0; ++ return 0; ++} ++/*========================================================================== ++ * Module init and exit ++ *========================================================================*/ ++ ++static int __init jz_cim_init(void) ++{ ++ struct cim_device *dev; ++ int ret; ++ /* allocate device */ ++ ret = v4l_device_init(); ++ if (ret) ++ return ret; ++ /* record device */ ++ dev = cim_dev; ++ init_waitqueue_head(&dev->wait_queue); ++ ++ ret = video_register_device(dev->jz_cim, VFL_TYPE_GRABBER, -1); ++ if (ret < 0) { ++ printk(KERN_ERR "CIM Video4Linux-device " ++ "registration failed\n"); ++ return -EINVAL; ++ } ++ ++ if (ret < 0) { ++ cim_fb_destroy(); ++ kfree(dev); ++ return ret; ++ } ++ ++ if ((ret = request_irq(IRQ_CIM, cim_irq_handler, IRQF_DISABLED, ++ CIM_NAME, dev))) { ++ printk(KERN_ERR "request_irq return error, ret=%d\n", ret); ++ cim_fb_destroy(); ++ kfree(dev); ++ printk(KERN_ERR "CIM could not get IRQ\n"); ++ return ret; ++ } ++ ++ printk("JzSOC Camera Interface Module (CIM) driver registered\n"); ++ ++ return 0; ++} ++ ++static void __exit jz_cim_exit(void) ++{ ++ free_irq(IRQ_CIM, cim_dev); ++ kfree(cim_dev); ++ video_unregister_device(cim_dev->jz_cim); ++} ++ ++module_init(jz_cim_init); ++module_exit(jz_cim_exit); +diff -urN linux-2.6.24.7.old/drivers/media/video/jz_cim.h linux-2.6.24.7/drivers/media/video/jz_cim.h +--- linux-2.6.24.7.old/drivers/media/video/jz_cim.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/media/video/jz_cim.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,36 @@ ++/* ++ * JzSOC CIM driver ++ * ++ * Copyright (C) 2005 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __JZ_CIM_H__ ++#define __JZ_CIM_H__ ++ ++typedef struct ++{ ++ u32 width; ++ u32 height; ++ u32 bpp; ++} IMG_PARAM; ++ ++/* ++ * IOCTL_XXX commands ++ */ ++#define IOCTL_SET_IMG_PARAM 0 // arg type: IMG_PARAM * ++ ++#endif /* __JZ__CIM_H__ */ +diff -urN linux-2.6.24.7.old/drivers/media/video/jz_sensor.c linux-2.6.24.7/drivers/media/video/jz_sensor.c +--- linux-2.6.24.7.old/drivers/media/video/jz_sensor.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/media/video/jz_sensor.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,202 @@ ++/* ++ * linux/drivers/char/jzchar/sensor.c ++ * ++ * Common CMOS Camera Sensor Driver ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ */ ++ ++//#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++//#include ++//#include "jz-chars.h" ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Jianli Wei"); ++MODULE_DESCRIPTION("Common CMOS Camera Sensor Driver"); ++MODULE_LICENSE("GPL"); ++ ++/* ++ * ioctl commands ++ */ ++#define IOCTL_SET_ADDR 0 /* set i2c address */ ++#define IOCTL_SET_CLK 1 /* set i2c clock */ ++#define IOCTL_WRITE_REG 2 /* write sensor register */ ++#define IOCTL_READ_REG 3 /* read sensor register */ ++ ++/* ++ * i2c related ++ */ ++static unsigned int i2c_addr = 0x42; ++static unsigned int i2c_clk = 100000; ++ ++struct video_device *jz_sensor; ++ ++static void write_reg(u8 reg, u8 val) ++{ ++ i2c_open(); ++ i2c_setclk(i2c_clk); ++ i2c_write((i2c_addr >> 1), &val, reg, 1); ++ i2c_close(); ++} ++ ++static u8 read_reg(u8 reg) ++{ ++ u8 val; ++ ++ i2c_open(); ++ i2c_setclk(i2c_clk); ++ i2c_read((i2c_addr >> 1), &val, reg, 1); ++ i2c_close(); ++ return val; ++} ++ ++/* ++ * fops routines ++ */ ++ ++static int sensor_open(struct inode *inode, struct file *filp); ++static int sensor_release(struct inode *inode, struct file *filp); ++static ssize_t sensor_read(struct file *filp, char *buf, size_t size, loff_t *l); ++static ssize_t sensor_write(struct file *filp, const char *buf, size_t size, loff_t *l); ++static int sensor_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); ++ ++static struct file_operations sensor_fops = ++{ ++ open: sensor_open, ++ release: sensor_release, ++ read: sensor_read, ++ write: sensor_write, ++ ioctl: sensor_ioctl, ++}; ++ ++static int sensor_open(struct inode *inode, struct file *filp) ++{ ++ try_module_get(THIS_MODULE); ++ return 0; ++} ++ ++static int sensor_release(struct inode *inode, struct file *filp) ++{ ++ module_put(THIS_MODULE); ++ return 0; ++} ++ ++static ssize_t sensor_read(struct file *filp, char *buf, size_t size, loff_t *l) ++{ ++ printk("sensor: read is not implemented\n"); ++ return -1; ++} ++ ++static ssize_t sensor_write(struct file *filp, const char *buf, size_t size, loff_t *l) ++{ ++ printk("sensor: write is not implemented\n"); ++ return -1; ++} ++ ++static int sensor_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ int ret = 0; ++ ++ switch (cmd) { ++ case IOCTL_SET_ADDR: ++ if (copy_from_user(&i2c_addr, (void *)arg, 4)) ++ return -EFAULT; ++ break; ++ case IOCTL_SET_CLK: ++ if (copy_from_user(&i2c_clk, (void *)arg, 4)) ++ return -EFAULT; ++ break; ++ case IOCTL_WRITE_REG: ++ { ++ u8 regval[2]; ++ ++ if (copy_from_user(regval, (void *)arg, 2)) ++ return -EFAULT; ++ ++ write_reg(regval[0], regval[1]); ++ break; ++ } ++ case IOCTL_READ_REG: ++ { ++ u8 reg, val; ++ ++ if (copy_from_user(®, (void *)arg, 1)) ++ return -EFAULT; ++ ++ val = read_reg(reg); ++ ++ if (copy_to_user((void *)(arg + 1), &val, 1)) ++ return -EFAULT; ++ break; ++ } ++ default: ++ printk("Not supported command: 0x%x\n", cmd); ++ return -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static struct video_device jz_v4l_device = { ++ .name = "jz sensor", ++ //.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE | ++ // VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY ++ .fops = &sensor_fops, ++ .minor = -1, ++}; ++ ++/* ++ * Module init and exit ++ */ ++ ++static int __init jz_sensor_init(void) ++{ ++ int ret; ++// cim_dev = kzalloc(sizeof(struct cim_device), GFP_KERNEL); ++ jz_sensor = video_device_alloc(); ++ memcpy(jz_sensor, &jz_v4l_device, sizeof(struct video_device)); ++ jz_sensor->release = video_device_release; ++// ret = jz_register_chrdev(SENSOR_MINOR, "sensor", &sensor_fops, NULL); ++ ret = video_register_device(jz_sensor, VFL_TYPE_GRABBER, -1); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ printk("Ingenic CMOS camera sensor driver registered\n"); ++ ++ return 0; ++} ++ ++static void __exit jz_sensor_exit(void) ++{ ++// jz_unregister_chrdev(SENSOR_MINOR, "sensor"); ++ video_unregister_device(jz_sensor); ++} ++ ++module_init(jz_sensor_init); ++module_exit(jz_sensor_exit); +diff -urN linux-2.6.24.7.old/drivers/mmc/card/block.c linux-2.6.24.7/drivers/mmc/card/block.c +--- linux-2.6.24.7.old/drivers/mmc/card/block.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mmc/card/block.c 2009-04-12 18:13:57.000000000 +0200 +@@ -225,7 +225,11 @@ + brq.mrq.cmd = &brq.cmd; + brq.mrq.data = &brq.data; + ++#ifdef CONFIG_JZ4750_BOOT_FROM_MSC0 ++ brq.cmd.arg = req->sector + 8192; ++#else + brq.cmd.arg = req->sector; ++#endif + if (!mmc_card_blockaddr(card)) + brq.cmd.arg <<= 9; + brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; +diff -urN linux-2.6.24.7.old/drivers/mmc/core/mmc.c linux-2.6.24.7/drivers/mmc/core/mmc.c +--- linux-2.6.24.7.old/drivers/mmc/core/mmc.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mmc/core/mmc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -141,8 +141,13 @@ + + e = UNSTUFF_BITS(resp, 47, 3); + m = UNSTUFF_BITS(resp, 62, 12); +- csd->capacity = (1 + m) << (e + 2); + ++#ifdef CONFIG_JZ4750_BOOT_FROM_MSC0 ++ csd->capacity = (1 + m) << (e + 2); ++ csd->capacity -= 8192; ++#else ++ csd->capacity = (1 + m) << (e + 2); ++#endif + csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); + csd->read_partial = UNSTUFF_BITS(resp, 79, 1); + csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); +@@ -402,8 +407,9 @@ + EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4); + if (err) + goto free_card; +- +- mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); ++ ++ /* all mmc v4 support 8 bit mmc card */ ++ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_8); + } + + if (!oldcard) +diff -urN linux-2.6.24.7.old/drivers/mmc/core/sd.c linux-2.6.24.7/drivers/mmc/core/sd.c +--- linux-2.6.24.7.old/drivers/mmc/core/sd.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mmc/core/sd.c 2009-04-12 18:13:57.000000000 +0200 +@@ -110,8 +110,13 @@ + + e = UNSTUFF_BITS(resp, 47, 3); + m = UNSTUFF_BITS(resp, 62, 12); +- csd->capacity = (1 + m) << (e + 2); + ++#ifdef CONFIG_JZ4750_BOOT_FROM_MSC0 ++ csd->capacity = (1 + m) << (e + 2); ++ csd->capacity -= 8192; ++#else ++ csd->capacity = (1 + m) << (e + 2); ++#endif + csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); + csd->read_partial = UNSTUFF_BITS(resp, 79, 1); + csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); +@@ -138,8 +143,13 @@ + csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + + m = UNSTUFF_BITS(resp, 48, 22); +- csd->capacity = (1 + m) << 10; + ++#ifdef CONFIG_JZ4750_BOOT_FROM_MSC0 ++ csd->capacity = (1 + m) << 10; ++ csd->capacity -= 8192; ++#else ++ csd->capacity = (1 + m) << 10; ++#endif + csd->read_blkbits = 9; + csd->read_partial = 0; + csd->write_misalign = 0; +@@ -269,9 +279,11 @@ + goto out; + + if ((status[16] & 0xF) != 1) { ++#if 0 + printk(KERN_WARNING "%s: Problem switching card " + "into high-speed mode!\n", + mmc_hostname(card->host)); ++#endif + } else { + mmc_card_set_highspeed(card); + mmc_set_timing(card->host, MMC_TIMING_SD_HS); +@@ -386,6 +398,9 @@ + goto free_card; + + mmc_decode_cid(card); ++ ++ /* set 24MHz clock again, why?? */ ++ mmc_set_clock(host, 24000000); + } + + /* +diff -urN linux-2.6.24.7.old/drivers/mmc/host/Kconfig linux-2.6.24.7/drivers/mmc/host/Kconfig +--- linux-2.6.24.7.old/drivers/mmc/host/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mmc/host/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -4,6 +4,104 @@ + + comment "MMC/SD Host Controller Drivers" + ++config MMC_JZ ++ tristate "JZ SD/Multimedia Card Interface support" ++ depends on SOC_JZ4730 || SOC_JZ4740 ++ help ++ This selects the Ingenic JZ4730/JZ4740 SD/Multimedia card Interface. ++ If you have abIngenic platform with a Multimedia Card slot, ++ say Y or M here. ++ ++ If unsure, say N. ++choice ++ depends on MMC_JZ ++ prompt "MMC BUS Width" ++ default JZ_MMC_BUS_4 ++ help ++ This defines the BUS Width of the Ingenic JZ4730/JZ4740 SD/Multimedia card Interface. ++ ++config JZ_MMC_BUS_1 ++ bool "1 Bit Bus" ++ help ++ 1 Bit SD/Multimedia Card Bus ++ ++config JZ_MMC_BUS_4 ++ bool "4 Bit Bus" ++ help ++ 4 Bit SD/Multimedia Card Bus ++ ++endchoice ++ ++config MSC0_JZ4750 ++ tristate "JZ4750 SD/Multimedia Card 0 Interface support" ++ depends on SOC_JZ4750 || SOC_JZ4750D ++ help ++ This selects the Ingenic JZ4750 SD/Multimedia card 0 Interface. ++ If you have a Ingenic platform with a Multimedia Card slot, ++ say Y or M here. ++ ++ If unsure, say N. ++ ++choice ++ depends on MSC0_JZ4750 ++ prompt "MSC0 BUS Width" ++ default JZ4750_MSC0_BUS_4 ++ help ++ This defines the BUS Width of the Ingenic JZ4750 SD/Multimedia card Interface. ++ ++config JZ4750_MSC0_BUS_1 ++ bool "1 Bit Bus" ++ help ++ 1 Bit SD/Multimedia Card Bus ++ ++config JZ4750_MSC0_BUS_4 ++ bool "4 Bit Bus" ++ help ++ 4 Bit SD/Multimedia Card Bus ++ ++config JZ4750_MSC0_BUS_8 ++ bool "8 Bit Bus" ++ help ++ 8 Bit Multimedia Card Bus ++ ++endchoice ++ ++config MSC1_JZ4750 ++ tristate "JZ4750 SD/Multimedia Card 1 Interface support" ++ depends on SOC_JZ4750 || SOC_JZ4750D ++ help ++ This selects the Ingenic JZ4750 SD/Multimedia card 1 Interface. ++ If you have a Ingenic platform with a Multimedia Card slot, ++ say Y or M here. ++ ++ If unsure, say N. ++ ++choice ++ depends on MSC1_JZ4750 ++ prompt "MSC1 BUS Width" ++ default JZ4750_MSC1_BUS_4 ++ help ++ This defines the BUS Width of the Ingenic JZ4750 SD/Multimedia card Interface. ++ ++config JZ4750_MSC1_BUS_1 ++ bool "1 Bit Bus" ++ help ++ 1 Bit SD/Multimedia Card Bus ++ ++config JZ4750_MSC1_BUS_4 ++ bool "4 Bit Bus" ++ help ++ 4 Bit SD/Multimedia Card Bus ++endchoice ++ ++config JZ4750_BOOT_FROM_MSC0 ++ tristate "JZ4750 Boot from SD/Multimedia Card Interface support" ++ depends on SOC_JZ4750 || SOC_JZ4750D ++ help ++ This selects boot from the Sd/Multimedia Card. ++ ++ If unsure,say N. ++ + config MMC_ARMMMCI + tristate "ARM AMBA Multimedia Card Interface support" + depends on ARM_AMBA +diff -urN linux-2.6.24.7.old/drivers/mmc/host/Makefile linux-2.6.24.7/drivers/mmc/host/Makefile +--- linux-2.6.24.7.old/drivers/mmc/host/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mmc/host/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -6,6 +6,9 @@ + EXTRA_CFLAGS += -DDEBUG + endif + ++obj-$(CONFIG_MMC_JZ) += jz_mmc.o ++obj-$(CONFIG_MSC0_JZ4750) += jz4750_mmc.o ++obj-$(CONFIG_MSC1_JZ4750) += jz4750_mmc.o + obj-$(CONFIG_MMC_ARMMMCI) += mmci.o + obj-$(CONFIG_MMC_PXA) += pxamci.o + obj-$(CONFIG_MMC_IMX) += imxmmc.o +diff -urN linux-2.6.24.7.old/drivers/mmc/host/jz4750_mmc.c linux-2.6.24.7/drivers/mmc/host/jz4750_mmc.c +--- linux-2.6.24.7.old/drivers/mmc/host/jz4750_mmc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mmc/host/jz4750_mmc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1051 @@ ++/* ++ * linux/drivers/mmc/jz_mmc.c - JZ SD/MMC driver ++ * ++ * Copyright (C) 2005 - 2008 Ingenic Semiconductor Inc. ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "jz4750_mmc.h" ++ ++#define DRIVER_NAME "jz-mmc" ++ ++#define USE_DMA ++ ++static int r_type = 0; ++static int rxdmachan = 0; ++static int txdmachan = 0; ++static int mmc_slot_enable = 0; ++static int auto_select_bus = MSC_4BIT_BUS; /* default 4 bit bus*/ ++ ++/* Start the MMC clock and operation */ ++static inline int jz_mmc_start_op(void) ++{ ++ REG_MSC_STRPCL(MSC_ID) = MSC_STRPCL_START_OP; ++ ++ return MMC_NO_ERROR; ++} ++ ++static inline u32 jz_mmc_calc_clkrt(int is_low, u32 rate) ++{ ++ u32 clkrt; ++ u32 clk_src = is_low ? 24000000 : 48000000; ++ ++ clkrt = 0; ++ while (rate < clk_src) { ++ clkrt++; ++ clk_src >>= 1; ++ } ++ return clkrt; ++} ++ ++/* Select the MMC clock frequency */ ++static int jz_mmc_set_clock(u32 rate) ++{ ++ int clkrt; ++ ++ /* __cpm_select_msc_clk_high will select 48M clock for MMC/SD card ++ * perhaps this will made some card with bad quality init fail,or ++ * bad stabilization. ++ */ ++ if (rate > SD_CLOCK_FAST) { ++ __cpm_select_msc_clk_high(MSC_ID,1); /* select clock source from CPM */ ++ clkrt = jz_mmc_calc_clkrt(0, rate); ++ } else { ++ __cpm_select_msc_clk(MSC_ID,1); /* select clock source from CPM */ ++ clkrt = jz_mmc_calc_clkrt(1, rate); ++ } ++ ++#ifndef CONFIG_FPGA ++ REG_MSC_CLKRT(MSC_ID) = clkrt; ++#else ++ REG_MSC_CLKRT(MSC_ID) = 7; ++#endif ++ return MMC_NO_ERROR; ++} ++ ++static void jz_mmc_enable_irq(struct jz_mmc_host *host, unsigned int mask) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ host->imask &= ~mask; ++ REG_MSC_IMASK(MSC_ID) = host->imask; ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static void jz_mmc_disable_irq(struct jz_mmc_host *host, unsigned int mask) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ host->imask |= mask; ++ REG_MSC_IMASK(MSC_ID) = host->imask; ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++void jz_set_dma_block_size(int dmanr, int nbyte); ++ ++#ifdef USE_DMA ++static inline void ++jz_mmc_start_dma(int chan, unsigned long phyaddr, int count, int mode) ++{ ++ unsigned long flags; ++ ++ flags = claim_dma_lock(); ++ disable_dma(chan); ++ clear_dma_ff(chan); ++ jz_set_dma_block_size(chan, 32); ++ set_dma_mode(chan, mode); ++ set_dma_addr(chan, phyaddr); ++ set_dma_count(chan, count + 31); ++ enable_dma(chan); ++ release_dma_lock(flags); ++} ++ ++static irqreturn_t jz_mmc_dma_rx_callback(int irq, void *devid) ++{ ++ int chan = rxdmachan; ++ ++ disable_dma(chan); ++ if (__dmac_channel_address_error_detected(chan)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", ++ __FUNCTION__); ++ __dmac_channel_clear_address_error(chan); ++ } ++ if (__dmac_channel_transmit_end_detected(chan)) { ++ __dmac_channel_clear_transmit_end(chan); ++ } ++ return IRQ_HANDLED; ++} ++static irqreturn_t jz_mmc_dma_tx_callback(int irq, void *devid) ++{ ++ int chan = txdmachan; ++ ++ disable_dma(chan); ++ if (__dmac_channel_address_error_detected(chan)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", ++ __FUNCTION__); ++ __dmac_channel_clear_address_error(chan); ++ } ++ if (__dmac_channel_transmit_end_detected(chan)) { ++ __dmac_channel_clear_transmit_end(chan); ++ } ++ return IRQ_HANDLED; ++} ++ ++/* Prepare DMA to start data transfer from the MMC card */ ++static void jz_mmc_rx_setup_data(struct jz_mmc_host *host, ++ struct mmc_data *data) ++{ ++ unsigned int nob = data->blocks; ++ int channelrx = rxdmachan; ++ int i; ++ u32 size; ++ ++ if (data->flags & MMC_DATA_STREAM) ++ nob = 0xffff; ++ ++ REG_MSC_NOB(MSC_ID) = nob; ++ REG_MSC_BLKLEN(MSC_ID) = data->blksz; ++ size = nob * data->blksz; ++ ++ if (data->flags & MMC_DATA_READ) { ++ host->dma.dir = DMA_FROM_DEVICE; ++ } else { ++ host->dma.dir = DMA_TO_DEVICE; ++ } ++ ++ host->dma.len = ++ dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, ++ host->dma.dir); ++ ++ for (i = 0; i < host->dma.len; i++) { ++ host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); ++ host->sg_cpu[i].dcmd = sg_dma_len(&data->sg[i]); ++ dma_cache_wback_inv((unsigned long) ++ CKSEG0ADDR(sg_dma_address(data->sg)) + ++ data->sg->offset, ++ host->sg_cpu[i].dcmd); ++ jz_mmc_start_dma(channelrx, host->sg_cpu[i].dtadr, ++ host->sg_cpu[i].dcmd, DMA_MODE_READ); ++ } ++} ++ ++/* Prepare DMA to start data transfer from the MMC card */ ++static void jz_mmc_tx_setup_data(struct jz_mmc_host *host, ++ struct mmc_data *data) ++{ ++ unsigned int nob = data->blocks; ++ int channeltx = txdmachan; ++ int i; ++ u32 size; ++ ++ if (data->flags & MMC_DATA_STREAM) ++ nob = 0xffff; ++ ++ REG_MSC_NOB(MSC_ID) = nob; ++ REG_MSC_BLKLEN(MSC_ID) = data->blksz; ++ size = nob * data->blksz; ++ ++ if (data->flags & MMC_DATA_READ) { ++ host->dma.dir = DMA_FROM_DEVICE; ++ } else { ++ host->dma.dir = DMA_TO_DEVICE; ++ } ++ ++ host->dma.len = ++ dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, ++ host->dma.dir); ++ ++ for (i = 0; i < host->dma.len; i++) { ++ host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); ++ host->sg_cpu[i].dcmd = sg_dma_len(&data->sg[i]); ++ dma_cache_wback_inv((unsigned long) ++ CKSEG0ADDR(sg_dma_address(data->sg)) + ++ data->sg->offset, ++ host->sg_cpu[i].dcmd); ++ jz_mmc_start_dma(channeltx, host->sg_cpu[i].dtadr, ++ host->sg_cpu[i].dcmd, DMA_MODE_WRITE); ++ } ++} ++#else ++static void jz_mmc_receive_pio(struct jz_mmc_host *host) ++{ ++ ++ struct mmc_data *data = 0; ++ int sg_len = 0, max = 0, count = 0; ++ u32 *buf = 0; ++ struct scatterlist *sg; ++ unsigned int nob; ++ ++ data = host->mrq->data; ++ nob = data->blocks; ++ REG_MSC_NOB(MSC_ID) = nob; ++ REG_MSC_BLKLEN(MSC_ID) = data->blksz; ++ ++ max = host->pio.len; ++ if (host->pio.index < host->dma.len) { ++ sg = &data->sg[host->pio.index]; ++ buf = sg_virt(sg) + host->pio.offset; ++ ++ /* This is the space left inside the buffer */ ++ sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset; ++ /* Check to if we need less then the size of the sg_buffer */ ++ if (sg_len < max) max = sg_len; ++ } ++ max = max / 4; ++ for(count = 0; count < max; count++) { ++ while (REG_MSC_STAT(MSC_ID) & MSC_STAT_DATA_FIFO_EMPTY) ++ ; ++ *buf++ = REG_MSC_RXFIFO(MSC_ID); ++ } ++ host->pio.len -= count; ++ host->pio.offset += count; ++ ++ if (sg_len && count == sg_len) { ++ host->pio.index++; ++ host->pio.offset = 0; ++ } ++} ++ ++static void jz_mmc_send_pio(struct jz_mmc_host *host) ++{ ++ ++ struct mmc_data *data = 0; ++ int sg_len, max, count = 0; ++ u32 *wbuf = 0; ++ struct scatterlist *sg; ++ unsigned int nob; ++ ++ data = host->mrq->data; ++ nob = data->blocks; ++ ++ REG_MSC_NOB(MSC_ID) = nob; ++ REG_MSC_BLKLEN(MSC_ID) = data->blksz; ++ ++ /* This is the pointer to the data buffer */ ++ sg = &data->sg[host->pio.index]; ++ wbuf = sg_virt(sg) + host->pio.offset; ++ ++ /* This is the space left inside the buffer */ ++ sg_len = data->sg[host->pio.index].length - host->pio.offset; ++ ++ /* Check to if we need less then the size of the sg_buffer */ ++ max = (sg_len > host->pio.len) ? host->pio.len : sg_len; ++ max = max / 4; ++ for(count = 0; count < max; count++ ) { ++ while (REG_MSC_STAT(MSC_ID) & MSC_STAT_DATA_FIFO_FULL) ++ ; ++ REG_MSC_TXFIFO(MSC_ID) = *wbuf++; ++ } ++ ++ host->pio.len -= count; ++ host->pio.offset += count; ++ ++ if (count == sg_len) { ++ host->pio.index++; ++ host->pio.offset = 0; ++ } ++} ++ ++static int ++jz_mmc_prepare_data(struct jz_mmc_host *host, struct mmc_data *data) ++{ ++ int datalen = data->blocks * data->blksz; ++ ++ host->dma.dir = DMA_BIDIRECTIONAL; ++ host->dma.len = dma_map_sg(mmc_dev(host->mmc), data->sg, ++ data->sg_len, host->dma.dir); ++ if (host->dma.len == 0) ++ return -ETIMEDOUT; ++ ++ host->pio.index = 0; ++ host->pio.offset = 0; ++ host->pio.len = datalen; ++ return 0; ++} ++#endif ++ ++static int jz_mmc_cmd_done(struct jz_mmc_host *host, unsigned int stat); ++ ++static void jz_mmc_finish_request(struct jz_mmc_host *host, struct mmc_request *mrq) ++{ ++ host->mrq = NULL; ++ host->cmd = NULL; ++ host->data = NULL; ++ mmc_request_done(host->mmc, mrq); ++} ++ ++static void jz_mmc_start_cmd(struct jz_mmc_host *host, ++ struct mmc_command *cmd, unsigned int cmdat) ++{ ++ u32 timeout = 0x3fffff; ++ unsigned int stat; ++ struct jz_mmc_host *hst = host; ++ WARN_ON(host->cmd != NULL); ++ host->cmd = cmd; ++ ++ /* mask interrupts */ ++ REG_MSC_IMASK(MSC_ID) = 0xffff; ++ ++ /* clear status */ ++ REG_MSC_IREG(MSC_ID) = 0xffff; ++ ++ if (cmd->flags & MMC_RSP_BUSY) ++ cmdat |= MSC_CMDAT_BUSY; ++ ++#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE)) ++ switch (RSP_TYPE(mmc_resp_type(cmd))) { ++ case RSP_TYPE(MMC_RSP_R1): /* r1,r1b, r6, r7 */ ++ cmdat |= MSC_CMDAT_RESPONSE_R1; ++ r_type = 1; ++ break; ++ case RSP_TYPE(MMC_RSP_R3): ++ cmdat |= MSC_CMDAT_RESPONSE_R3; ++ r_type = 1; ++ break; ++ case RSP_TYPE(MMC_RSP_R2): ++ cmdat |= MSC_CMDAT_RESPONSE_R2; ++ r_type = 2; ++ break; ++ default: ++ break; ++ } ++ ++ REG_MSC_CMD(MSC_ID) = cmd->opcode; ++ ++ /* Set argument */ ++#ifdef CONFIG_MSC0_JZ4750 ++#ifdef CONFIG_JZ4750_MSC0_BUS_1 ++ if (cmd->opcode == 6) { ++ /* set 1 bit sd card bus*/ ++ if (cmd->arg ==2) ++ REG_MSC_ARG(MSC_ID) = 0; ++ ++ /* set 1 bit mmc card bus*/ ++ if (cmd->arg == 0x3b70101) { ++ REG_MSC_ARG(MSC_ID) = 0x3b70001; ++ } ++ } else ++ REG_MSC_ARG(MSC_ID) = cmd->arg; ++ ++#elif defined CONFIG_JZ4750_MSC0_BUS_8 ++ if (cmd->opcode == 6) { ++ /* set 8 bit mmc card bus*/ ++ if (cmd->arg == 0x3b70101) ++ REG_MSC_ARG(MSC_ID) = 0x3b70201; ++ else ++ REG_MSC_ARG(MSC_ID) = cmd->arg; ++ ++ } else ++ REG_MSC_ARG(MSC_ID) = cmd->arg; ++#else ++ REG_MSC_ARG(MSC_ID) = cmd->arg; ++#endif /* CONFIG_JZ4750_MSC0_BUS_1 */ ++#else ++#ifdef CONFIG_JZ4750_MSC1_BUS_1 ++ if (cmd->opcode == 6) { ++ /* set 1 bit sd card bus*/ ++ if (cmd->arg ==2) ++ REG_MSC_ARG(MSC_ID) = 0; ++ ++ /* set 1 bit mmc card bus*/ ++ if (cmd->arg == 0x3b70101) { ++ REG_MSC_ARG(MSC_ID) = 0x3b70001; ++ } ++ } else ++ REG_MSC_ARG(MSC_ID) = cmd->arg; ++ ++#else ++ REG_MSC_ARG(MSC_ID) = cmd->arg; ++#endif /* CONFIG_JZ4750_MSC1_BUS_1 */ ++#endif /* CONFIG_MSC0_JZ4750*/ ++ ++ /* Set command */ ++ REG_MSC_CMDAT(MSC_ID) = cmdat; ++ ++ /* Send command */ ++ jz_mmc_start_op(); ++ ++ while (timeout-- && !(REG_MSC_STAT(MSC_ID) & MSC_STAT_END_CMD_RES)) ++ ; ++ ++ REG_MSC_IREG(MSC_ID) = MSC_IREG_END_CMD_RES; /* clear irq flag */ ++ if (cmd->opcode == 12) { ++ while (timeout-- && !(REG_MSC_IREG(MSC_ID) & MSC_IREG_PRG_DONE)) ++ ; ++ REG_MSC_IREG(MSC_ID) = MSC_IREG_PRG_DONE; /* clear status */ ++ } ++ if (!mmc_slot_enable) { ++ /* It seems that MSC can't report the MSC_STAT_TIME_OUT_RES when ++ * card was removed. We force to return here. ++ */ ++ cmd->error = -ETIMEDOUT; ++ jz_mmc_finish_request(hst, hst->mrq); ++ return; ++ } ++ ++ if (SD_IO_SEND_OP_COND == cmd->opcode) { ++ /* ++ * Don't support SDIO card currently. ++ */ ++ cmd->error = -ETIMEDOUT; ++ jz_mmc_finish_request(hst, hst->mrq); ++ return; ++ } ++ ++ /* Check for status */ ++ stat = REG_MSC_STAT(MSC_ID); ++ jz_mmc_cmd_done(hst, stat); ++ if (host->data) { ++ if (cmd->opcode == MMC_WRITE_BLOCK || cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) ++#ifdef USE_DMA ++ jz_mmc_tx_setup_data(host, host->data); ++#else ++ jz_mmc_send_pio(host); ++ else ++ jz_mmc_receive_pio(host); ++#endif ++ } ++} ++ ++static int jz_mmc_cmd_done(struct jz_mmc_host *host, unsigned int stat) ++{ ++ struct mmc_command *cmd = host->cmd; ++ int i, temp[16]; ++ u8 *buf; ++ u32 data, v, w1, w2; ++ ++ if (!cmd) ++ return 0; ++ ++ host->cmd = NULL; ++ buf = (u8 *) temp; ++ switch (r_type) { ++ case 1: ++ { ++ data = REG_MSC_RES(MSC_ID); ++ buf[0] = (data >> 8) & 0xff; ++ buf[1] = data & 0xff; ++ data = REG_MSC_RES(MSC_ID); ++ buf[2] = (data >> 8) & 0xff; ++ buf[3] = data & 0xff; ++ data = REG_MSC_RES(MSC_ID); ++ buf[4] = data & 0xff; ++ cmd->resp[0] = ++ buf[1] << 24 | buf[2] << 16 | buf[3] << 8 | ++ buf[4]; ++ break; ++ } ++ case 2: ++ { ++ data = REG_MSC_RES(MSC_ID); ++ v = data & 0xffff; ++ for (i = 0; i < 4; i++) { ++ data = REG_MSC_RES(MSC_ID); ++ w1 = data & 0xffff; ++ data = REG_MSC_RES(MSC_ID); ++ w2 = data & 0xffff; ++ cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8; ++ v = w2; ++ } ++ break; ++ } ++ case 0: ++ break; ++ } ++ if (stat & MSC_STAT_TIME_OUT_RES) { ++ printk("MSC_STAT_TIME_OUT_RES\n"); ++ cmd->error = -ETIMEDOUT; ++ } else if (stat & MSC_STAT_CRC_RES_ERR && cmd->flags & MMC_RSP_CRC) { ++ printk("MSC_STAT_CRC\n"); ++ if (cmd->opcode == MMC_ALL_SEND_CID || ++ cmd->opcode == MMC_SEND_CSD || ++ cmd->opcode == MMC_SEND_CID) { ++ /* a bogus CRC error can appear if the msb of ++ the 15 byte response is a one */ ++ if ((cmd->resp[0] & 0x80000000) == 0) ++ cmd->error = -EILSEQ; ++ } ++ } ++ /* ++ * Did I mention this is Sick. We always need to ++ * discard the upper 8 bits of the first 16-bit word. ++ */ ++ if (host->data && cmd->error == 0) ++ jz_mmc_enable_irq(host, MSC_IMASK_DATA_TRAN_DONE); ++ else ++ jz_mmc_finish_request(host, host->mrq); ++ ++ return 1; ++} ++ ++static int jz_mmc_data_done(struct jz_mmc_host *host, unsigned int stat) ++{ ++ struct mmc_data *data = host->data; ++ ++ if (!data) ++ return 0; ++ REG_MSC_IREG(MSC_ID) = MSC_IREG_DATA_TRAN_DONE; /* clear status */ ++ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, ++ host->dma_dir); ++ if (stat & MSC_STAT_TIME_OUT_READ) { ++ printk("MMC/SD timeout, MMC_STAT 0x%x\n", stat); ++ data->error = -ETIMEDOUT; ++ } else if (REG_MSC_STAT(MSC_ID) & ++ (MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR)) { ++ printk("MMC/SD CRC error, MMC_STAT 0x%x\n", stat); ++ data->error = -EILSEQ; ++ } ++ /* ++ * There appears to be a hardware design bug here. There seems to ++ * be no way to find out how much data was transferred to the card. ++ * This means that if there was an error on any block, we mark all ++ * data blocks as being in error. ++ */ ++ if (data->error == 0) ++ data->bytes_xfered = data->blocks * data->blksz; ++ else ++ data->bytes_xfered = 0; ++ ++ jz_mmc_disable_irq(host, MSC_IMASK_DATA_TRAN_DONE); ++ host->data = NULL; ++ if (host->mrq->stop) { ++ jz_mmc_start_cmd(host, host->mrq->stop, 0); ++ } else { ++ jz_mmc_finish_request(host, host->mrq); ++ } ++ return 1; ++} ++ ++static void jz_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct jz_mmc_host *host = mmc_priv(mmc); ++ unsigned int cmdat; ++ ++ /* Save current request for the future processing */ ++ host->mrq = mrq; ++ host->data = mrq->data; ++ cmdat = host->cmdat; ++ host->cmdat &= ~MSC_CMDAT_INIT; ++ ++ if (mrq->data) { ++ cmdat &= ~MSC_CMDAT_BUSY; ++#ifdef USE_DMA ++ if ((mrq->cmd->opcode == 51) | (mrq->cmd->opcode == 8) | (mrq->cmd->opcode == 6)) ++ ++ cmdat |= ++ MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN | ++ MSC_CMDAT_DMA_EN; ++ else { ++#ifdef CONFIG_MSC0_JZ4750 ++#ifdef CONFIG_JZ4750_MSC0_BUS_1 ++ cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; ++ cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN | ++ MSC_CMDAT_DMA_EN; ++#elif defined CONFIG_JZ4750_MSC0_BUS_4 ++ if(auto_select_bus == MSC_1BIT_BUS) { ++ cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; ++ cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN | ++ MSC_CMDAT_DMA_EN; ++ } else { ++ cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; ++ cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT | MSC_CMDAT_DATA_EN | ++ MSC_CMDAT_DMA_EN; ++ } ++#else ++ cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_DMA_EN; ++#endif /* CONFIG_JZ4750_MSC0_BUS_1 */ ++#else ++#ifdef CONFIG_JZ4750_MSC1_BUS_1 ++ cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; ++ cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN | ++ MSC_CMDAT_DMA_EN; ++#else ++ cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_DMA_EN; ++#endif /* CONFIG_JZ4750_MSC1_BUS_1 */ ++#endif /* CONFIG_MSC0_JZ4750 */ ++ } ++ if (mrq->data->flags & MMC_DATA_WRITE) ++ cmdat |= MSC_CMDAT_WRITE; ++ ++ if (mrq->data->flags & MMC_DATA_STREAM) ++ cmdat |= MSC_CMDAT_STREAM_BLOCK; ++ if (mrq->cmd->opcode != MMC_WRITE_BLOCK ++ && mrq->cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK) ++ jz_mmc_rx_setup_data(host, mrq->data); ++#else /*USE_DMA*/ ++ ++ if ((mrq->cmd->opcode == 51) | (mrq->cmd->opcode == 8) | (mrq->cmd->opcode == 6)) ++ cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; ++ else { ++#ifdef CONFIG_MSC0_JZ4750 ++#ifdef CONFIG_JZ4750_MSC0_BUS_1 ++ cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; ++ cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; ++#elif defined CONFIG_JZ4750_MSC0_BUS_4 ++ if(auto_select_bus == MSC_1BIT_BUS) { ++ cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; ++ cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; ++ } else { ++ cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; ++ cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT | MSC_CMDAT_DATA_EN; ++ } ++#else ++ cmdat |= MSC_CMDAT_DATA_EN; ++#endif ++#else ++#ifdef CONFIG_JZ4750_MSC1_BUS_1 ++ cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; ++ cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; ++#else ++ cmdat |= MSC_CMDAT_DATA_EN; ++#endif /* CONFIG_JZ4750_MSC1_BUS_1 */ ++#endif /* CONFIG_MSC0_JZ4750 */ ++ } ++ if (mrq->data->flags & MMC_DATA_WRITE) ++ cmdat |= MSC_CMDAT_WRITE; ++ ++ if (mrq->data->flags & MMC_DATA_STREAM) ++ cmdat |= MSC_CMDAT_STREAM_BLOCK; ++ jz_mmc_prepare_data(host, host->data); ++#endif /*USE_DMA*/ ++ } ++ jz_mmc_start_cmd(host, mrq->cmd, cmdat); ++} ++ ++static irqreturn_t jz_mmc_irq(int irq, void *devid) ++{ ++ struct jz_mmc_host *host = devid; ++ unsigned int ireg; ++ int handled = 0; ++ ++ ireg = REG_MSC_IREG(MSC_ID); ++ ++ if (ireg) { ++ unsigned stat = REG_MSC_STAT(MSC_ID); ++ if (ireg & MSC_IREG_DATA_TRAN_DONE) ++ handled |= jz_mmc_data_done(host, stat); ++ } ++ return IRQ_RETVAL(handled); ++} ++ ++/* Returns true if MMC slot is empty */ ++static int jz_mmc_slot_is_empty(int slot) ++{ ++ int empty; ++ ++#ifdef CONFIG_FPGA ++ return 0; ++#endif ++ ++#ifdef CONFIG_MSC1_JZ4750 ++ empty = (__msc1_card_detected(slot) == 0) ? 1 : 0; ++#else ++ empty = (__msc0_card_detected(slot) == 0) ? 1 : 0; ++ ++#endif ++ if (empty) { ++ /* wait for card insertion */ ++#ifdef CONFIG_MSC1_JZ4750 ++ __gpio_as_irq_rise_edge(MSC_HOTPLUG_PIN); ++#else ++ __gpio_as_irq_fall_edge(MSC_HOTPLUG_PIN); ++#endif ++ } else { ++ /* wait for card removal */ ++#ifdef CONFIG_MSC1_JZ4750 ++ __gpio_as_irq_fall_edge(MSC_HOTPLUG_PIN); ++#else ++ __gpio_as_irq_rise_edge(MSC_HOTPLUG_PIN); ++#endif ++ } ++ ++ return empty; ++} ++ ++static irqreturn_t jz_mmc_detect_irq(int irq, void *devid) ++{ ++ struct jz_mmc_host *host = (struct jz_mmc_host *) devid; ++ ++ auto_select_bus = MSC_4BIT_BUS; ++ if (jz_mmc_slot_is_empty(0)) { ++ mmc_slot_enable = 0; ++ mmc_detect_change(host->mmc, 50); ++ } else { ++ mmc_slot_enable = 1; ++ mmc_detect_change(host->mmc, 50); ++ } ++ return IRQ_HANDLED; ++} ++ ++static int jz_mmc_get_ro(struct mmc_host *mmc) ++{ ++ struct jz_mmc_host *host = mmc_priv(mmc); ++ ++ if (host->pdata && host->pdata->get_ro) ++ return host->pdata->get_ro(mmc_dev(mmc)); ++ /* Host doesn't support read only detection so assume writeable */ ++ return 0; ++} ++ ++/* set clock and power */ ++static void jz_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct jz_mmc_host *host = mmc_priv(mmc); ++ ++ if (ios->clock) ++ jz_mmc_set_clock(ios->clock); ++ ++ if (host->power_mode != ios->power_mode) { ++ host->power_mode = ios->power_mode; ++ ++ if (ios->power_mode == MMC_POWER_ON) ++ host->cmdat |= CMDAT_INIT; ++ } ++ ++ if (ios->bus_width == MMC_BUS_WIDTH_4) { ++ auto_select_bus = MSC_4BIT_BUS; ++ host->cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT; ++ } ++ else if (ios->bus_width == MMC_BUS_WIDTH_8) { ++ host->cmdat |= MSC_CMDAT_BUS_WIDTH_8BIT; ++ auto_select_bus = MSC_8BIT_BUS; ++ } else { ++ /* 1 bit bus*/ ++ host->cmdat &= ~MSC_CMDAT_BUS_WIDTH_8BIT; ++ auto_select_bus = MSC_1BIT_BUS; ++ } ++} ++ ++static const struct mmc_host_ops jz_mmc_ops = { ++ .request = jz_mmc_request, ++ .get_ro = jz_mmc_get_ro, ++ .set_ios = jz_mmc_set_ios, ++}; ++static int jz_mmc_pm_callback(struct pm_dev *pm_dev, ++ pm_request_t req, void *data); ++ ++static int jz_mmc_probe(struct platform_device *pdev) ++{ ++ int retval; ++ struct mmc_host *mmc; ++ struct jz_mmc_host *host = NULL; ++ int irq; ++ struct resource *r; ++ ++#ifdef CONFIG_MSC0_JZ4750 ++#ifdef CONFIG_SOC_JZ4750 ++ __gpio_as_msc0_8bit(); // for jz4750 ++#else ++ __gpio_as_msc0_4bit(); // for jz4750d ++#endif ++ __msc0_init_io(); ++ __msc0_enable_power(); ++#else ++ __gpio_as_msc1_4bit(); ++ __msc1_init_io(); ++ __msc1_enable_power(); ++#endif ++ __msc_reset(MSC_ID); ++ REG_MSC_LPM(MSC_ID) = 0x1; ++ ++ MMC_IRQ_MASK(); ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ irq = platform_get_irq(pdev, 0); ++ if (!r || irq < 0) ++ return -ENXIO; ++ ++ r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); ++ if (!r) ++ return -EBUSY; ++ ++ mmc = mmc_alloc_host(sizeof(struct jz_mmc_host), &pdev->dev); ++ if (!mmc) { ++ retval = -ENOMEM; ++ goto out; ++ } ++ mmc->ops = &jz_mmc_ops; ++ mmc->f_min = MMC_CLOCK_SLOW; ++ mmc->f_max = SD_CLOCK_HIGH; ++ /* ++ * We can do SG-DMA, but we don't because we never know how much ++ * data we successfully wrote to the card. ++ */ ++ mmc->max_phys_segs = NR_SG; ++ ++ mmc->max_seg_size = PAGE_SIZE * 16; ++ mmc->max_req_size = mmc->max_seg_size; ++ mmc->max_blk_size = 4095; ++ /* ++ * Block count register is 16 bits. ++ */ ++ mmc->max_blk_count = 65535; ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ host->pdata = pdev->dev.platform_data; ++ mmc->ocr_avail = host->pdata ? ++ host->pdata->ocr_mask : MMC_VDD_32_33 | MMC_VDD_33_34; ++ host->mmc->caps = ++ MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_SD_HIGHSPEED ++ | MMC_CAP_MMC_HIGHSPEED; ++ /* ++ *MMC_CAP_4_BIT_DATA (1 << 0) The host can do 4 bit transfers ++ * ++ */ ++ host->sg_cpu = ++ dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, ++ GFP_KERNEL); ++ if (!host->sg_cpu) { ++ retval = -ENOMEM; ++ goto out; ++ } ++ spin_lock_init(&host->lock); ++ host->irq = IRQ_MSC; /* is it useful ?*/ ++ host->imask = 0xffff; ++ /* ++ * Ensure that the host controller is shut down, and setup ++ * with our defaults. ++ */ ++ retval = request_irq(IRQ_MSC, jz_mmc_irq, 0, "MMC/SD", host); ++ if (retval) { ++ printk(KERN_ERR "MMC/SD: can't request MMC/SD IRQ\n"); ++ return retval; ++ } ++ ++ jz_mmc_slot_is_empty(0); ++ /* Request card detect interrupt */ ++ ++ retval = request_irq(MSC_HOTPLUG_IRQ, jz_mmc_detect_irq, 0, //SA_INTERRUPT, ++ "MMC card detect", host); ++ if (retval) { ++ printk(KERN_ERR "MMC/SD: can't request card detect IRQ\n"); ++ goto err1; ++ } ++#ifdef USE_DMA ++ /* Request MMC Rx DMA channel */ ++ rxdmachan = ++ jz_request_dma(DMA_ID_MSC_RX, "MMC Rx", jz_mmc_dma_rx_callback, ++ 0, host); ++ if (rxdmachan < 0) { ++ printk(KERN_ERR "jz_request_dma failed for MMC Rx\n"); ++ goto err2; ++ } ++ ++ if (rxdmachan < HALF_DMA_NUM) ++ REG_DMAC_DMACR(0) |= DMAC_DMACR_FMSC; ++ else ++ REG_DMAC_DMACR(1) |= DMAC_DMACR_FMSC; ++ ++ /* Request MMC Tx DMA channel */ ++ txdmachan = ++ jz_request_dma(DMA_ID_MSC_TX, "MMC Tx", jz_mmc_dma_tx_callback, ++ 0, host); ++ if (txdmachan < 0) { ++ printk(KERN_ERR "jz_request_dma failed for MMC Tx\n"); ++ goto err3; ++ } ++ ++ if (txdmachan < HALF_DMA_NUM) ++ REG_DMAC_DMACR(0) |= DMAC_DMACR_FMSC; ++ else ++ REG_DMAC_DMACR(1) |= DMAC_DMACR_FMSC; ++ ++#endif ++ platform_set_drvdata(pdev, mmc); ++ mmc_add_host(mmc); ++#ifdef CONFIG_PM ++ /* Register MMC slot as as power-managed device */ ++ pm_register(PM_UNKNOWN_DEV, PM_SYS_UNKNOWN, jz_mmc_pm_callback); ++#endif ++ printk("JZ SD/MMC card driver registered\n"); ++ ++ /* Detect card during initialization */ ++#if defined(CONFIG_SOC_JZ4750) || defined(CONFIG_SOC_JZ4750D) ++ if (!jz_mmc_slot_is_empty(0)) { ++ mmc_slot_enable = 1; ++ mmc_detect_change(host->mmc, 0); ++ } ++#endif ++ return 0; ++ ++err1:free_irq(IRQ_MSC, &host); ++#ifdef USE_DMA ++ err2:jz_free_dma(rxdmachan); ++ err3:jz_free_dma(txdmachan); ++#endif ++out: ++ if (host) { ++ if (host->sg_cpu) ++ dma_free_coherent(&pdev->dev, PAGE_SIZE, ++ host->sg_cpu, host->sg_dma); ++ } ++ if (mmc) ++ mmc_free_host(mmc); ++ return -1; ++} ++ ++static int jz_mmc_remove(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(pdev); ++ long flags; ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ if (mmc) { ++ struct jz_mmc_host *host = mmc_priv(mmc); ++ ++ if (host->pdata && host->pdata->exit) ++ host->pdata->exit(&pdev->dev, mmc); ++ ++ mmc_remove_host(mmc); ++ ++ local_irq_save(flags); ++ __msc0_disable_power(); ++ jz_free_dma(rxdmachan); ++ jz_free_dma(txdmachan); ++ free_irq(IRQ_MSC, host); ++ local_irq_restore(flags); ++ mmc_free_host(mmc); ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++pm_message_t state; ++static int jz_mmc_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(dev); ++ int ret = 0; ++ ++ if (mmc) ++ ret = mmc_suspend_host(mmc, state); ++ ++ return ret; ++} ++ ++static int jz_mmc_resume(struct platform_device *dev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(dev); ++ int ret = 0; ++ ++ if (mmc) ++ ret = mmc_resume_host(mmc); ++ ++ return ret; ++} ++static int jz_mmc_pm_callback(struct pm_dev *pm_dev, ++ pm_request_t req, void *data) ++{ ++ struct platform_device *pdev = (struct platform_device *)pm_dev; ++ ++ switch(req) { ++ case PM_RESUME: ++ jz_mmc_resume(pdev); ++ break; ++ case PM_SUSPEND: ++ /* state has no use */ ++ jz_mmc_suspend(pdev,state); ++ break; ++ default: ++ printk("MMC/SD: invalid PM request %d\n", req); ++ break; ++ } ++ return 0; ++} ++#else ++#define jz_mmc_suspend NULL ++#define jz_mmc_resume NULL ++#endif ++ ++static struct platform_driver jz_mmc_driver = { ++ .probe = jz_mmc_probe, ++ .remove = jz_mmc_remove, ++ .suspend = jz_mmc_suspend, ++ .resume = jz_mmc_resume, ++ .driver = { ++ .name = DRIVER_NAME, ++ }, ++}; ++ ++static int __init jz_mmc_init(void) ++{ ++ return platform_driver_register(&jz_mmc_driver); ++} ++ ++static void __exit jz_mmc_exit(void) ++{ ++ platform_driver_unregister(&jz_mmc_driver); ++} ++ ++module_init(jz_mmc_init); ++module_exit(jz_mmc_exit); ++ ++MODULE_DESCRIPTION("JZ47XX SD/Multimedia Card Interface Driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/drivers/mmc/host/jz4750_mmc.h linux-2.6.24.7/drivers/mmc/host/jz4750_mmc.h +--- linux-2.6.24.7.old/drivers/mmc/host/jz4750_mmc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mmc/host/jz4750_mmc.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,88 @@ ++#ifndef __JZ4750_MMC_H__ ++#define __JZ4750_MMC_H__ ++ ++#define MMC_CLOCK_SLOW 400000 /* 400 kHz for initial setup */ ++#define MMC_CLOCK_FAST 20000000 /* 20 MHz for maximum for normal operation */ ++#define SD_CLOCK_FAST 24000000 /* 24 MHz for SD Cards */ ++#define SD_CLOCK_HIGH 24000000 /* 24 MHz for SD Cards */ ++#define MMC_NO_ERROR 0 ++ ++#define NR_SG 1 ++ ++#ifdef CONFIG_MSC0_JZ4750 ++#define MSC_ID 0 ++#define MSC_HOTPLUG_IRQ MSC0_HOTPLUG_IRQ ++#define IRQ_MSC IRQ_MSC0 ++#define DMA_ID_MSC_RX DMA_ID_MSC0_RX ++#define DMA_ID_MSC_TX DMA_ID_MSC0_TX ++#define MSC_HOTPLUG_PIN MSC0_HOTPLUG_PIN ++#else ++#define MSC_ID 1 ++#define MSC_HOTPLUG_IRQ MSC1_HOTPLUG_IRQ ++#define IRQ_MSC IRQ_MSC1 ++#define DMA_ID_MSC_RX DMA_ID_MSC1_RX ++#define DMA_ID_MSC_TX DMA_ID_MSC1_TX ++#define MSC_HOTPLUG_PIN MSC1_HOTPLUG_PIN ++#endif ++ ++#define MSC_1BIT_BUS 0 ++#define MSC_4BIT_BUS 1 ++#define MSC_8BIT_BUS 2 ++ ++#define SZ_4K 0x00001000 ++ ++struct jz_mmc_host { ++ struct mmc_host *mmc; ++ spinlock_t lock; ++ struct { ++ int len; ++ int dir; ++ } dma; ++ struct { ++ int index; ++ int offset; ++ int len; ++ } pio; ++ int irq; ++ unsigned int clkrt; ++ unsigned int cmdat; ++ unsigned int imask; ++ unsigned int power_mode; ++ struct jz_mmc_platform_data *pdata; ++ struct mmc_request *mrq; ++ struct mmc_command *cmd; ++ struct mmc_data *data; ++ dma_addr_t sg_dma; ++ struct jzsoc_dma_desc *sg_cpu; ++ unsigned int dma_len; ++ unsigned int dma_dir; ++}; ++ ++#define MMC_IRQ_MASK() \ ++do { \ ++ REG_MSC_IMASK(MSC_ID) = 0xffff; \ ++ REG_MSC_IREG(MSC_ID) = 0xffff; \ ++} while (0) ++ ++typedef struct jzsoc_dma_desc { ++ volatile u32 ddadr; /* Points to the next descriptor + flags */ ++ volatile u32 dsadr; /* DSADR value for the current transfer */ ++ volatile u32 dtadr; /* DTADR value for the current transfer */ ++ volatile u32 dcmd; /* DCMD value for the current transfer */ ++} jzsoc_dma_desc; ++ ++#include ++ ++struct device; ++struct mmc_host; ++ ++struct jz_mmc_platform_data { ++ unsigned int ocr_mask; /* available voltages */ ++ unsigned long detect_delay; /* delay in jiffies before detecting cards after interrupt */ ++ int (*init)(struct device *, irq_handler_t , void *); ++ int (*get_ro)(struct device *); ++ void (*setpower)(struct device *, unsigned int); ++ void (*exit)(struct device *, void *); ++}; ++ ++#endif /* __JZ4750_MMC_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mmc/host/jz_mmc.c linux-2.6.24.7/drivers/mmc/host/jz_mmc.c +--- linux-2.6.24.7.old/drivers/mmc/host/jz_mmc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mmc/host/jz_mmc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1026 @@ ++/* ++ * linux/drivers/mmc/jz_mmc.c - JZ SD/MMC driver ++ * ++ * Copyright (C) 2005 - 2008 Ingenic Semiconductor Inc. ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "jz_mmc.h" ++ ++#define DRIVER_NAME "jz-mmc" ++ ++#define NR_SG 1 ++ ++#if defined(CONFIG_SOC_JZ4725) || defined(CONFIG_SOC_JZ4720) ++#undef USE_DMA ++#else ++#define USE_DMA ++#endif ++ ++struct jz_mmc_host { ++ struct mmc_host *mmc; ++ spinlock_t lock; ++ struct { ++ int len; ++ int dir; ++ } dma; ++ struct { ++ int index; ++ int offset; ++ int len; ++ } pio; ++ int irq; ++ unsigned int clkrt; ++ unsigned int cmdat; ++ unsigned int imask; ++ unsigned int power_mode; ++ struct jz_mmc_platform_data *pdata; ++ struct mmc_request *mrq; ++ struct mmc_command *cmd; ++ struct mmc_data *data; ++ dma_addr_t sg_dma; ++ struct jzsoc_dma_desc *sg_cpu; ++ unsigned int dma_len; ++ unsigned int dma_dir; ++ struct pm_dev *pmdev; ++}; ++ ++static int r_type = 0; ++ ++#define MMC_IRQ_MASK() \ ++do { \ ++ REG_MSC_IMASK = 0xff; \ ++ REG_MSC_IREG = 0xff; \ ++} while (0) ++ ++static int rxdmachan = 0; ++static int txdmachan = 0; ++static int mmc_slot_enable = 0; ++ ++/* Stop the MMC clock and wait while it happens */ ++static inline int jz_mmc_stop_clock(void) ++{ ++ int timeout = 1000; ++ ++ REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_STOP; ++ while (timeout && (REG_MSC_STAT & MSC_STAT_CLK_EN)) { ++ timeout--; ++ if (timeout == 0) ++ return 0; ++ udelay(1); ++ } ++ return MMC_NO_ERROR; ++} ++ ++/* Start the MMC clock and operation */ ++static inline int jz_mmc_start_clock(void) ++{ ++ REG_MSC_STRPCL = ++ MSC_STRPCL_CLOCK_CONTROL_START | MSC_STRPCL_START_OP; ++ return MMC_NO_ERROR; ++} ++ ++static inline u32 jz_mmc_calc_clkrt(int is_sd, u32 rate) ++{ ++ u32 clkrt; ++ u32 clk_src = is_sd ? 24000000 : 20000000; ++ ++ clkrt = 0; ++ while (rate < clk_src) { ++ clkrt++; ++ clk_src >>= 1; ++ } ++ return clkrt; ++} ++ ++/* Select the MMC clock frequency */ ++static int jz_mmc_set_clock(u32 rate) ++{ ++ int clkrt; ++ ++ jz_mmc_stop_clock(); ++ __cpm_select_msc_clk(1); /* select clock source from CPM */ ++ clkrt = jz_mmc_calc_clkrt(1, rate); ++ REG_MSC_CLKRT = clkrt; ++ return MMC_NO_ERROR; ++} ++ ++static void jz_mmc_enable_irq(struct jz_mmc_host *host, unsigned int mask) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&host->lock, flags); ++ host->imask &= ~mask; ++ REG_MSC_IMASK = host->imask; ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static void jz_mmc_disable_irq(struct jz_mmc_host *host, unsigned int mask) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ host->imask |= mask; ++ REG_MSC_IMASK = host->imask; ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++void jz_set_dma_block_size(int dmanr, int nbyte); ++ ++#ifdef USE_DMA ++static inline void ++jz_mmc_start_dma(int chan, unsigned long phyaddr, int count, int mode) ++{ ++ unsigned long flags; ++ ++ flags = claim_dma_lock(); ++ disable_dma(chan); ++ clear_dma_ff(chan); ++ jz_set_dma_block_size(chan, 32); ++ set_dma_mode(chan, mode); ++ set_dma_addr(chan, phyaddr); ++ set_dma_count(chan, count + 31); ++ enable_dma(chan); ++ release_dma_lock(flags); ++} ++ ++static irqreturn_t jz_mmc_dma_rx_callback(int irq, void *devid) ++{ ++ int chan = rxdmachan; ++ ++ disable_dma(chan); ++ if (__dmac_channel_address_error_detected(chan)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", ++ __FUNCTION__); ++ __dmac_channel_clear_address_error(chan); ++ } ++ if (__dmac_channel_transmit_end_detected(chan)) { ++ __dmac_channel_clear_transmit_end(chan); ++ } ++ return IRQ_HANDLED; ++} ++static irqreturn_t jz_mmc_dma_tx_callback(int irq, void *devid) ++{ ++ int chan = txdmachan; ++ ++ disable_dma(chan); ++ if (__dmac_channel_address_error_detected(chan)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", ++ __FUNCTION__); ++ __dmac_channel_clear_address_error(chan); ++ } ++ if (__dmac_channel_transmit_end_detected(chan)) { ++ __dmac_channel_clear_transmit_end(chan); ++ } ++ return IRQ_HANDLED; ++} ++ ++/* Prepare DMA to start data transfer from the MMC card */ ++static void jz_mmc_rx_setup_data(struct jz_mmc_host *host, ++ struct mmc_data *data) ++{ ++ unsigned int nob = data->blocks; ++ int channelrx = rxdmachan; ++ int i; ++ u32 size; ++ ++ if (data->flags & MMC_DATA_STREAM) ++ nob = 0xffff; ++ ++ REG_MSC_NOB = nob; ++ REG_MSC_BLKLEN = data->blksz; ++ size = nob * data->blksz; ++ ++ if (data->flags & MMC_DATA_READ) { ++ host->dma.dir = DMA_FROM_DEVICE; ++ } else { ++ host->dma.dir = DMA_TO_DEVICE; ++ } ++ ++ host->dma.len = ++ dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, ++ host->dma.dir); ++ ++ for (i = 0; i < host->dma.len; i++) { ++ host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); ++ host->sg_cpu[i].dcmd = sg_dma_len(&data->sg[i]); ++ dma_cache_wback_inv((unsigned long) ++ CKSEG0ADDR(sg_dma_address(data->sg)) + ++ data->sg->offset, ++ host->sg_cpu[i].dcmd); ++ jz_mmc_start_dma(channelrx, host->sg_cpu[i].dtadr, ++ host->sg_cpu[i].dcmd, DMA_MODE_READ); ++ } ++} ++ ++/* Prepare DMA to start data transfer from the MMC card */ ++static void jz_mmc_tx_setup_data(struct jz_mmc_host *host, ++ struct mmc_data *data) ++{ ++ unsigned int nob = data->blocks; ++ int channeltx = txdmachan; ++ int i; ++ u32 size; ++ ++ if (data->flags & MMC_DATA_STREAM) ++ nob = 0xffff; ++ ++ REG_MSC_NOB = nob; ++ REG_MSC_BLKLEN = data->blksz; ++ size = nob * data->blksz; ++ ++ if (data->flags & MMC_DATA_READ) { ++ host->dma.dir = DMA_FROM_DEVICE; ++ } else { ++ host->dma.dir = DMA_TO_DEVICE; ++ } ++ ++ host->dma.len = ++ dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, ++ host->dma.dir); ++ ++ for (i = 0; i < host->dma.len; i++) { ++ host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); ++ host->sg_cpu[i].dcmd = sg_dma_len(&data->sg[i]); ++ dma_cache_wback_inv((unsigned long) ++ CKSEG0ADDR(sg_dma_address(data->sg)) + ++ data->sg->offset, ++ host->sg_cpu[i].dcmd); ++ jz_mmc_start_dma(channeltx, host->sg_cpu[i].dtadr, ++ host->sg_cpu[i].dcmd, DMA_MODE_WRITE); ++ } ++} ++#else ++static void jz_mmc_receive_pio(struct jz_mmc_host *host) ++{ ++ ++ struct mmc_data *data = 0; ++ int sg_len = 0, max = 0, count = 0; ++ u32 *buf = 0; ++ struct scatterlist *sg; ++ unsigned int nob; ++ ++ data = host->mrq->data; ++ nob = data->blocks; ++ REG_MSC_NOB = nob; ++ REG_MSC_BLKLEN = data->blksz; ++ ++ max = host->pio.len; ++ if (host->pio.index < host->dma.len) { ++ sg = &data->sg[host->pio.index]; ++ buf = sg_virt(sg) + host->pio.offset; ++ ++ /* This is the space left inside the buffer */ ++ sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset; ++ /* Check to if we need less then the size of the sg_buffer */ ++ if (sg_len < max) max = sg_len; ++ } ++ max = max / 4; ++ for(count = 0; count < max; count++) { ++ while (REG_MSC_STAT & MSC_STAT_DATA_FIFO_EMPTY) ++ ; ++ *buf++ = REG_MSC_RXFIFO; ++ } ++ host->pio.len -= count; ++ host->pio.offset += count; ++ ++ if (sg_len && count == sg_len) { ++ host->pio.index++; ++ host->pio.offset = 0; ++ } ++} ++ ++static void jz_mmc_send_pio(struct jz_mmc_host *host) ++{ ++ ++ struct mmc_data *data = 0; ++ int sg_len, max, count = 0; ++ u32 *wbuf = 0; ++ struct scatterlist *sg; ++ unsigned int nob; ++ ++ data = host->mrq->data; ++ nob = data->blocks; ++ ++ REG_MSC_NOB = nob; ++ REG_MSC_BLKLEN = data->blksz; ++ ++ /* This is the pointer to the data buffer */ ++ sg = &data->sg[host->pio.index]; ++ wbuf = sg_virt(sg) + host->pio.offset; ++ ++ /* This is the space left inside the buffer */ ++ sg_len = data->sg[host->pio.index].length - host->pio.offset; ++ ++ /* Check to if we need less then the size of the sg_buffer */ ++ max = (sg_len > host->pio.len) ? host->pio.len : sg_len; ++ max = max / 4; ++ for(count = 0; count < max; count++ ) { ++ while (REG_MSC_STAT & MSC_STAT_DATA_FIFO_FULL) ++ ; ++ REG_MSC_TXFIFO = *wbuf++; ++ } ++ ++ host->pio.len -= count; ++ host->pio.offset += count; ++ ++ if (count == sg_len) { ++ host->pio.index++; ++ host->pio.offset = 0; ++ } ++} ++ ++static int ++jz_mmc_prepare_data(struct jz_mmc_host *host, struct mmc_data *data) ++{ ++ int datalen = data->blocks * data->blksz; ++ ++ host->dma.dir = DMA_BIDIRECTIONAL; ++ host->dma.len = dma_map_sg(mmc_dev(host->mmc), data->sg, ++ data->sg_len, host->dma.dir); ++ if (host->dma.len == 0) ++ return -ETIMEDOUT; ++ ++ host->pio.index = 0; ++ host->pio.offset = 0; ++ host->pio.len = datalen; ++ return 0; ++} ++#endif ++ ++static int jz_mmc_cmd_done(struct jz_mmc_host *host, unsigned int stat); ++ ++static void jz_mmc_finish_request(struct jz_mmc_host *host, struct mmc_request *mrq) ++{ ++ jz_mmc_stop_clock(); ++ host->mrq = NULL; ++ host->cmd = NULL; ++ host->data = NULL; ++ mmc_request_done(host->mmc, mrq); ++} ++ ++static void jz_mmc_start_cmd(struct jz_mmc_host *host, ++ struct mmc_command *cmd, unsigned int cmdat) ++{ ++ u32 timeout = 0x3fffff; ++ unsigned int stat; ++ struct jz_mmc_host *hst = host; ++ WARN_ON(host->cmd != NULL); ++ host->cmd = cmd; ++ ++ /* stop MMC clock */ ++ jz_mmc_stop_clock(); ++ ++ /* mask interrupts */ ++ REG_MSC_IMASK = 0xff; ++ ++ /* clear status */ ++ REG_MSC_IREG = 0xff; ++ ++ if (cmd->flags & MMC_RSP_BUSY) ++ cmdat |= MSC_CMDAT_BUSY; ++ ++#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE)) ++ switch (RSP_TYPE(mmc_resp_type(cmd))) { ++ case RSP_TYPE(MMC_RSP_R1): /* r1,r1b, r6, r7 */ ++ cmdat |= MSC_CMDAT_RESPONSE_R1; ++ r_type = 1; ++ break; ++ case RSP_TYPE(MMC_RSP_R3): ++ cmdat |= MSC_CMDAT_RESPONSE_R3; ++ r_type = 1; ++ break; ++ case RSP_TYPE(MMC_RSP_R2): ++ cmdat |= MSC_CMDAT_RESPONSE_R2; ++ r_type = 2; ++ break; ++ default: ++ break; ++ } ++ REG_MSC_CMD = cmd->opcode; ++ ++ /* Set argument */ ++#ifdef CONFIG_JZ_MMC_BUS_1 ++ if (cmd->opcode == 6) { ++ /* set 1 bit sd card bus*/ ++ if (cmd->arg ==2) ++ REG_MSC_ARG = 0; ++ ++ /* set 1 bit mmc card bus*/ ++ if (cmd->arg == 0x3b70101) ++ REG_MSC_ARG = 0x3b70001; ++ } else ++ REG_MSC_ARG = cmd->arg; ++#else ++ REG_MSC_ARG = cmd->arg; ++#endif ++ ++ /* Set command */ ++ REG_MSC_CMDAT = cmdat; ++ ++ /* Send command */ ++ jz_mmc_start_clock(); ++ ++ while (timeout-- && !(REG_MSC_STAT & MSC_STAT_END_CMD_RES)) ++ ; ++ ++ REG_MSC_IREG = MSC_IREG_END_CMD_RES; /* clear irq flag */ ++ if (cmd->opcode == 12) { ++ while (timeout-- && !(REG_MSC_IREG & MSC_IREG_PRG_DONE)) ++ ; ++ REG_MSC_IREG = MSC_IREG_PRG_DONE; /* clear status */ ++ } ++ if (!mmc_slot_enable) { ++ /* It seems that MSC can't report the MSC_STAT_TIME_OUT_RES when ++ * card was removed. We force to return here. ++ */ ++ cmd->error = -ETIMEDOUT; ++ jz_mmc_finish_request(hst, hst->mrq); ++ return; ++ } ++ ++ if (SD_IO_SEND_OP_COND == cmd->opcode) { ++ /* ++ * Don't support SDIO card currently. ++ */ ++ cmd->error = -ETIMEDOUT; ++ jz_mmc_finish_request(hst, hst->mrq); ++ return; ++ } ++ ++ /* Check for status */ ++ stat = REG_MSC_STAT; ++ jz_mmc_cmd_done(hst, stat); ++ if (host->data) { ++ if (cmd->opcode == MMC_WRITE_BLOCK || cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) ++#ifdef USE_DMA ++ jz_mmc_tx_setup_data(host, host->data); ++#else ++ jz_mmc_send_pio(host); ++ else ++ jz_mmc_receive_pio(host); ++#endif ++ } ++} ++ ++static int jz_mmc_cmd_done(struct jz_mmc_host *host, unsigned int stat) ++{ ++ struct mmc_command *cmd = host->cmd; ++ int i, temp[16]; ++ u8 *buf; ++ u32 data, v, w1, w2; ++ ++ if (!cmd) ++ return 0; ++ ++ host->cmd = NULL; ++ buf = (u8 *) temp; ++ switch (r_type) { ++ case 1: ++ { ++ data = REG_MSC_RES; ++ buf[0] = (data >> 8) & 0xff; ++ buf[1] = data & 0xff; ++ data = REG_MSC_RES; ++ buf[2] = (data >> 8) & 0xff; ++ buf[3] = data & 0xff; ++ data = REG_MSC_RES; ++ buf[4] = data & 0xff; ++ cmd->resp[0] = ++ buf[1] << 24 | buf[2] << 16 | buf[3] << 8 | ++ buf[4]; ++ break; ++ } ++ case 2: ++ { ++ data = REG_MSC_RES; ++ v = data & 0xffff; ++ for (i = 0; i < 4; i++) { ++ data = REG_MSC_RES; ++ w1 = data & 0xffff; ++ data = REG_MSC_RES; ++ w2 = data & 0xffff; ++ cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8; ++ v = w2; ++ } ++ break; ++ } ++ case 0: ++ break; ++ } ++ if (stat & MSC_STAT_TIME_OUT_RES) { ++ printk("MSC_STAT_TIME_OUT_RES\n"); ++ cmd->error = -ETIMEDOUT; ++ } else if (stat & MSC_STAT_CRC_RES_ERR && cmd->flags & MMC_RSP_CRC) { ++ printk("MSC_STAT_CRC\n"); ++ if (cmd->opcode == MMC_ALL_SEND_CID || ++ cmd->opcode == MMC_SEND_CSD || ++ cmd->opcode == MMC_SEND_CID) { ++ /* a bogus CRC error can appear if the msb of ++ the 15 byte response is a one */ ++ if ((cmd->resp[0] & 0x80000000) == 0) ++ cmd->error = -EILSEQ; ++ } ++ } ++ /* ++ * Did I mention this is Sick. We always need to ++ * discard the upper 8 bits of the first 16-bit word. ++ */ ++ if (host->data && cmd->error == 0) ++ jz_mmc_enable_irq(host, MSC_IMASK_DATA_TRAN_DONE); ++ else ++ jz_mmc_finish_request(host, host->mrq); ++ ++ return 1; ++} ++ ++static int jz_mmc_data_done(struct jz_mmc_host *host, unsigned int stat) ++{ ++ struct mmc_data *data = host->data; ++ ++ if (!data) ++ return 0; ++ REG_MSC_IREG = MSC_IREG_DATA_TRAN_DONE; /* clear status */ ++ jz_mmc_stop_clock(); ++ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, ++ host->dma_dir); ++ if (stat & MSC_STAT_TIME_OUT_READ) { ++ printk("MMC/SD timeout, MMC_STAT 0x%x\n", stat); ++ data->error = -ETIMEDOUT; ++ } else if (REG_MSC_STAT & ++ (MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR)) { ++ printk("MMC/SD CRC error, MMC_STAT 0x%x\n", stat); ++ data->error = -EILSEQ; ++ } ++ /* ++ * There appears to be a hardware design bug here. There seems to ++ * be no way to find out how much data was transferred to the card. ++ * This means that if there was an error on any block, we mark all ++ * data blocks as being in error. ++ */ ++ if (data->error == 0) ++ data->bytes_xfered = data->blocks * data->blksz; ++ else ++ data->bytes_xfered = 0; ++ ++ jz_mmc_disable_irq(host, MSC_IMASK_DATA_TRAN_DONE); ++ host->data = NULL; ++ if (host->mrq->stop) { ++ jz_mmc_stop_clock(); ++ jz_mmc_start_cmd(host, host->mrq->stop, 0); ++ } else { ++ jz_mmc_finish_request(host, host->mrq); ++ } ++ return 1; ++} ++ ++static void jz_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct jz_mmc_host *host = mmc_priv(mmc); ++ unsigned int cmdat; ++ ++ /* stop MMC clock */ ++ jz_mmc_stop_clock(); ++ ++ /* Save current request for the future processing */ ++ host->mrq = mrq; ++ host->data = mrq->data; ++ cmdat = host->cmdat; ++ host->cmdat &= ~MSC_CMDAT_INIT; ++ ++ if (mrq->data) { ++ cmdat &= ~MSC_CMDAT_BUSY; ++#ifdef USE_DMA ++ if ((mrq->cmd->opcode == 51) | (mrq->cmd->opcode == 8) | (mrq->cmd->opcode == 6)) ++ ++ cmdat |= ++ MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN | ++ MSC_CMDAT_DMA_EN; ++ else { ++#ifdef CONFIG_JZ_MMC_BUS_1 ++ cmdat &= ~MSC_CMDAT_BUS_WIDTH_4BIT; ++ cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN | ++ MSC_CMDAT_DMA_EN; ++#else ++ cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_DMA_EN; ++#endif ++ } ++ if (mrq->data->flags & MMC_DATA_WRITE) ++ cmdat |= MSC_CMDAT_WRITE; ++ ++ if (mrq->data->flags & MMC_DATA_STREAM) ++ cmdat |= MSC_CMDAT_STREAM_BLOCK; ++ if (mrq->cmd->opcode != MMC_WRITE_BLOCK ++ && mrq->cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK) ++ jz_mmc_rx_setup_data(host, mrq->data); ++#else /*USE_DMA*/ ++ ++ if ((mrq->cmd->opcode == 51) | (mrq->cmd->opcode == 8) | (mrq->cmd->opcode == 6)) ++ cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; ++ else { ++#ifdef CONFIG_JZ_MMC_BUS_1 ++ cmdat &= ~MSC_CMDAT_BUS_WIDTH_4BIT; ++ cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; ++#else ++ cmdat |= MSC_CMDAT_DATA_EN; ++#endif ++ } ++ if (mrq->data->flags & MMC_DATA_WRITE) ++ cmdat |= MSC_CMDAT_WRITE; ++ ++ if (mrq->data->flags & MMC_DATA_STREAM) ++ cmdat |= MSC_CMDAT_STREAM_BLOCK; ++ jz_mmc_prepare_data(host, host->data); ++#endif /*USE_DMA*/ ++ } ++ jz_mmc_start_cmd(host, mrq->cmd, cmdat); ++} ++ ++static irqreturn_t jz_mmc_irq(int irq, void *devid) ++{ ++ struct jz_mmc_host *host = devid; ++ unsigned int ireg; ++ int handled = 0; ++ ++ ireg = REG_MSC_IREG; ++ ++ if (ireg) { ++ unsigned stat = REG_MSC_STAT; ++ if (ireg & MSC_IREG_DATA_TRAN_DONE) ++ handled |= jz_mmc_data_done(host, stat); ++ } ++ return IRQ_RETVAL(handled); ++} ++ ++/* Returns true if MMC slot is empty */ ++static int jz_mmc_slot_is_empty(int slot) ++{ ++ int empty; ++ ++ empty = (__msc_card_detected(slot) == 0) ? 1 : 0; ++ ++ if (empty) { ++ /* wait for card insertion */ ++#ifdef CONFIG_MIPS_JZ4740_LYRA ++ __gpio_as_irq_rise_edge(MSC_HOTPLUG_PIN); ++#else ++ __gpio_as_irq_fall_edge(MSC_HOTPLUG_PIN); ++#endif ++ } else { ++ /* wait for card removal */ ++#ifdef CONFIG_MIPS_JZ4740_LYRA ++ __gpio_as_irq_fall_edge(MSC_HOTPLUG_PIN); ++#else ++ __gpio_as_irq_rise_edge(MSC_HOTPLUG_PIN); ++#endif ++ } ++ ++ return empty; ++} ++ ++static irqreturn_t jz_mmc_detect_irq(int irq, void *devid) ++{ ++ struct jz_mmc_host *host = (struct jz_mmc_host *) devid; ++ ++ if (jz_mmc_slot_is_empty(0)) { ++ mmc_slot_enable = 0; ++ mmc_detect_change(host->mmc, 50); ++ } else { ++ mmc_slot_enable = 1; ++ mmc_detect_change(host->mmc, 50); ++ } ++ return IRQ_HANDLED; ++} ++ ++static int jz_mmc_get_ro(struct mmc_host *mmc) ++{ ++ struct jz_mmc_host *host = mmc_priv(mmc); ++ ++ if (host->pdata && host->pdata->get_ro) ++ return host->pdata->get_ro(mmc_dev(mmc)); ++ /* Host doesn't support read only detection so assume writeable */ ++ return 0; ++} ++ ++/* set clock and power */ ++static void jz_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct jz_mmc_host *host = mmc_priv(mmc); ++ ++ if (ios->clock) ++ jz_mmc_set_clock(ios->clock); ++ else ++ jz_mmc_stop_clock(); ++ ++ if (host->power_mode != ios->power_mode) { ++ host->power_mode = ios->power_mode; ++ ++ if (ios->power_mode == MMC_POWER_ON) ++ host->cmdat |= CMDAT_INIT; ++ } ++ ++ if ((ios->bus_width == MMC_BUS_WIDTH_4) || (ios->bus_width == MMC_BUS_WIDTH_8)) ++ host->cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT; ++ else ++ host->cmdat &= ~MSC_CMDAT_BUS_WIDTH_4BIT; ++} ++ ++static const struct mmc_host_ops jz_mmc_ops = { ++ .request = jz_mmc_request, ++ .get_ro = jz_mmc_get_ro, ++ .set_ios = jz_mmc_set_ios, ++}; ++static int jz_mmc_pm_callback(struct pm_dev *pm_dev, ++ pm_request_t req, void *data); ++ ++static int jz_mmc_probe(struct platform_device *pdev) ++{ ++ int retval; ++ struct mmc_host *mmc; ++ struct jz_mmc_host *host = NULL; ++ int irq; ++ struct resource *r; ++ ++ __gpio_as_msc(); ++ __msc_init_io(); ++ __msc_enable_power(); ++ ++ __msc_reset(); ++ ++ /* On reset, stop MMC clock */ ++ jz_mmc_stop_clock(); ++ ++ MMC_IRQ_MASK(); ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ irq = platform_get_irq(pdev, 0); ++ if (!r || irq < 0) ++ return -ENXIO; ++ ++ r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); ++ if (!r) ++ return -EBUSY; ++ ++ mmc = mmc_alloc_host(sizeof(struct jz_mmc_host), &pdev->dev); ++ if (!mmc) { ++ retval = -ENOMEM; ++ goto out; ++ } ++ mmc->ops = &jz_mmc_ops; ++ mmc->f_min = MMC_CLOCK_SLOW; ++ mmc->f_max = SD_CLOCK_FAST; ++ /* ++ * We can do SG-DMA, but we don't because we never know how much ++ * data we successfully wrote to the card. ++ */ ++ mmc->max_phys_segs = NR_SG; ++ /* ++ * Our hardware DMA can handle a maximum of one page per SG entry. ++ */ ++ mmc->max_seg_size = PAGE_SIZE; ++ /* ++ * Block length register is 10 bits. ++ */ ++ mmc->max_blk_size = 1023; ++ /* ++ * Block count register is 16 bits. ++ */ ++ mmc->max_blk_count = 65535; ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ host->pdata = pdev->dev.platform_data; ++ mmc->ocr_avail = host->pdata ? ++ host->pdata->ocr_mask : MMC_VDD_32_33 | MMC_VDD_33_34; ++ host->mmc->caps = ++ MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_SD_HIGHSPEED ++ | MMC_CAP_MMC_HIGHSPEED; ++ /* ++ *MMC_CAP_4_BIT_DATA (1 << 0) The host can do 4 bit transfers ++ * ++ */ ++ host->sg_cpu = ++ dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, ++ GFP_KERNEL); ++ if (!host->sg_cpu) { ++ retval = -ENOMEM; ++ goto out; ++ } ++ spin_lock_init(&host->lock); ++ host->irq = IRQ_MSC; ++ host->imask = 0xff; ++ /* ++ * Ensure that the host controller is shut down, and setup ++ * with our defaults. ++ */ ++ retval = request_irq(IRQ_MSC, jz_mmc_irq, 0, "MMC/SD", host); ++ if (retval) { ++ printk(KERN_ERR "MMC/SD: can't request MMC/SD IRQ\n"); ++ return retval; ++ } ++ jz_mmc_slot_is_empty(0); ++ /* Request card detect interrupt */ ++ ++ retval = request_irq(MSC_HOTPLUG_IRQ, jz_mmc_detect_irq, 0, //SA_INTERRUPT, ++ "MMC card detect", host); ++ if (retval) { ++ printk(KERN_ERR "MMC/SD: can't request card detect IRQ\n"); ++ goto err1; ++ } ++#ifdef USE_DMA ++ /* Request MMC Rx DMA channel */ ++ rxdmachan = ++ jz_request_dma(DMA_ID_MSC_RX, "MMC Rx", jz_mmc_dma_rx_callback, ++ 0, host); ++ if (rxdmachan < 0) { ++ printk(KERN_ERR "jz_request_dma failed for MMC Rx\n"); ++ goto err2; ++ } ++ ++ /* Request MMC Tx DMA channel */ ++ txdmachan = ++ jz_request_dma(DMA_ID_MSC_TX, "MMC Tx", jz_mmc_dma_tx_callback, ++ 0, host); ++ if (txdmachan < 0) { ++ printk(KERN_ERR "jz_request_dma failed for MMC Tx\n"); ++ goto err3; ++ } ++#endif ++ platform_set_drvdata(pdev, mmc); ++ mmc_add_host(mmc); ++#ifdef CONFIG_PM ++ /* Register MMC slot as as power-managed device */ ++ host->pmdev = pm_register(PM_UNKNOWN_DEV, PM_SYS_UNKNOWN, jz_mmc_pm_callback); ++ if (host->pmdev) ++ host->pmdev->data = pdev; ++#endif ++ printk("JZ SD/MMC card driver registered\n"); ++ ++ /* Detect card during initialization */ ++#ifdef CONFIG_SOC_JZ4740 ++ if (!jz_mmc_slot_is_empty(0)) { ++ mmc_slot_enable = 1; ++ mmc_detect_change(host->mmc, 0); ++ } ++#endif ++ return 0; ++ ++err1:free_irq(IRQ_MSC, &host); ++#ifdef USE_DMA ++ err2:jz_free_dma(rxdmachan); ++ err3:jz_free_dma(txdmachan); ++#endif ++out: ++ if (host) { ++ if (host->sg_cpu) ++ dma_free_coherent(&pdev->dev, PAGE_SIZE, ++ host->sg_cpu, host->sg_dma); ++ } ++ if (mmc) ++ mmc_free_host(mmc); ++ return -1; ++} ++ ++static int jz_mmc_remove(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(pdev); ++ long flags; ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ if (mmc) { ++ struct jz_mmc_host *host = mmc_priv(mmc); ++ ++ if (host->pdata && host->pdata->exit) ++ host->pdata->exit(&pdev->dev, mmc); ++ ++ mmc_remove_host(mmc); ++ ++ local_irq_save(flags); ++ jz_mmc_stop_clock(); ++ __msc_disable_power(); ++ jz_free_dma(rxdmachan); ++ jz_free_dma(txdmachan); ++ free_irq(IRQ_MSC, host); ++ local_irq_restore(flags); ++ mmc_free_host(mmc); ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++pm_message_t state; ++static int jz_mmc_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(dev); ++ int ret = 0; ++ ++ __msc_disable_power(); ++ if (mmc) ++ ret = mmc_suspend_host(mmc, state); ++ ++ return ret; ++} ++ ++static int jz_mmc_resume(struct platform_device *dev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(dev); ++ int ret = 0; ++#if 0 ++ /*for sandisk BB0807011816D and other strange cards*/ ++ int i; ++ ++ for(i = 104; i < 110; i++) ++ __gpio_as_input(i); ++ ++ /* perhaps you should mdelay more */ ++ mdelay(1000); ++ __gpio_as_msc(); ++#endif ++ __msc_init_io(); ++ __msc_enable_power(); ++ __msc_reset(); ++ ++ if (!jz_mmc_slot_is_empty(0)) { ++ mmc_slot_enable = 1; ++ mmc_detect_change(mmc, 10); ++ } ++ ++ if (mmc) ++ ret = mmc_resume_host(mmc); ++ ++ return ret; ++} ++static int jz_mmc_pm_callback(struct pm_dev *pm_dev, ++ pm_request_t req, void *data) ++{ ++ struct platform_device *pdev = (struct platform_device *)pm_dev->data; ++ ++ switch(req) { ++ case PM_RESUME: ++ jz_mmc_resume(pdev); ++ break; ++ case PM_SUSPEND: ++ /* state has no use */ ++ jz_mmc_suspend(pdev, state); ++ break; ++ default: ++ printk("MMC/SD: invalid PM request %d\n", req); ++ break; ++ } ++ return 0; ++} ++#else ++#define jz_mmc_suspend NULL ++#define jz_mmc_resume NULL ++#endif ++ ++static struct platform_driver jz_mmc_driver = { ++ .probe = jz_mmc_probe, ++ .remove = jz_mmc_remove, ++ .suspend = jz_mmc_suspend, ++ .resume = jz_mmc_resume, ++ .driver = { ++ .name = DRIVER_NAME, ++ }, ++}; ++ ++static int __init jz_mmc_init(void) ++{ ++ return platform_driver_register(&jz_mmc_driver); ++} ++ ++static void __exit jz_mmc_exit(void) ++{ ++ platform_driver_unregister(&jz_mmc_driver); ++} ++ ++module_init(jz_mmc_init); ++module_exit(jz_mmc_exit); ++ ++MODULE_DESCRIPTION("JZ47XX SD/Multimedia Card Interface Driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/drivers/mmc/host/jz_mmc.h linux-2.6.24.7/drivers/mmc/host/jz_mmc.h +--- linux-2.6.24.7.old/drivers/mmc/host/jz_mmc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mmc/host/jz_mmc.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,65 @@ ++#ifndef __JZ_MMC_H__ ++#define __JZ_MMC_H__ ++ ++#define MMC_CLOCK_SLOW 400000 /* 400 kHz for initial setup */ ++#define MMC_CLOCK_FAST 20000000 /* 20 MHz for maximum for normal operation */ ++#define SD_CLOCK_FAST 24000000 /* 24 MHz for SD Cards */ ++#define MMC_NO_ERROR 0 ++/* Extra MMC commands for state control */ ++/* Use negative numbers to disambiguate */ ++#define MMC_CIM_RESET -1 ++#define MMC_SET_CLOCK 100 ++ ++typedef struct jzsoc_dma_desc { ++ volatile u32 ddadr; /* Points to the next descriptor + flags */ ++ volatile u32 dsadr; /* DSADR value for the current transfer */ ++ volatile u32 dtadr; /* DTADR value for the current transfer */ ++ volatile u32 dcmd; /* DCMD value for the current transfer */ ++} jzsoc_dma_desc; ++ ++ ++ ++ ++#include ++ ++struct device; ++struct mmc_host; ++ ++struct jz_mmc_platform_data { ++ unsigned int ocr_mask; /* available voltages */ ++ unsigned long detect_delay; /* delay in jiffies before detecting cards after interrupt */ ++ int (*init)(struct device *, irq_handler_t , void *); ++ int (*get_ro)(struct device *); ++ void (*setpower)(struct device *, unsigned int); ++ void (*exit)(struct device *, void *); ++}; ++ ++//extern void pxa_set_mci_info(struct pxamci_platform_data *info); ++ ++ ++ ++#define SZ_1K 0x00000400 ++#define SZ_4K 0x00001000 ++#define SZ_8K 0x00002000 ++#define SZ_16K 0x00004000 ++#define SZ_64K 0x00010000 ++#define SZ_128K 0x00020000 ++#define SZ_256K 0x00040000 ++#define SZ_512K 0x00080000 ++ ++#define SZ_1M 0x00100000 ++#define SZ_2M 0x00200000 ++#define SZ_4M 0x00400000 ++#define SZ_8M 0x00800000 ++#define SZ_16M 0x01000000 ++#define SZ_32M 0x02000000 ++#define SZ_64M 0x04000000 ++#define SZ_128M 0x08000000 ++#define SZ_256M 0x10000000 ++#define SZ_512M 0x20000000 ++ ++#define SZ_1G 0x40000000 ++#define SZ_2G 0x80000000 ++ ++ ++#endif /* __JZ_MMC_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/Makefile linux-2.6.24.7/drivers/mtd/Makefile +--- linux-2.6.24.7.old/drivers/mtd/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -3,9 +3,9 @@ + # + + # Core functionality. +-obj-$(CONFIG_MTD) += mtd.o + mtd-y := mtdcore.o mtdsuper.o + mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o ++obj-$(CONFIG_MTD) += $(mtd-y) + + obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o + obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o +@@ -15,14 +15,15 @@ + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_CHAR) += mtdchar.o + obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o +-obj-$(CONFIG_MTD_BLOCK) += mtdblock.o ++#obj-$(CONFIG_MTD_BLOCK) += mtdblock.o ++obj-$(CONFIG_MTD_BLOCK) += mtdblock-jz.o ++obj-$(CONFIG_UDC_USE_LB_CACHE) += udc_cache.o + obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o + obj-$(CONFIG_FTL) += ftl.o + obj-$(CONFIG_NFTL) += nftl.o + obj-$(CONFIG_INFTL) += inftl.o + obj-$(CONFIG_RFD_FTL) += rfd_ftl.o + obj-$(CONFIG_SSFDC) += ssfdc.o +-obj-$(CONFIG_MTD_OOPS) += mtdoops.o + + nftl-objs := nftlcore.o nftlmount.o + inftl-objs := inftlcore.o inftlmount.o +@@ -30,3 +31,6 @@ + obj-y += chips/ maps/ devices/ nand/ onenand/ + + obj-$(CONFIG_MTD_UBI) += ubi/ ++ ++$(obj)/mtdblock-jz.o: $(obj)/mtdblock-jz.uu ++ uudecode $(obj)/mtdblock-jz.uu -o $(obj)/mtdblock-jz.o +\ No newline at end of file +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/HEAD linux-2.6.24.7/drivers/mtd/mtd-utils/.git/HEAD +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/HEAD 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/HEAD 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++ref: refs/heads/master +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/config linux-2.6.24.7/drivers/mtd/mtd-utils/.git/config +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/config 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/config 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,11 @@ ++[core] ++ repositoryformatversion = 0 ++ filemode = true ++ bare = false ++ logallrefupdates = true ++[remote "origin"] ++ url = git://git.infradead.org/mtd-utils.git ++ fetch = +refs/heads/*:refs/remotes/origin/* ++[branch "master"] ++ remote = origin ++ merge = refs/heads/master +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/description linux-2.6.24.7/drivers/mtd/mtd-utils/.git/description +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/description 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/description 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++Unnamed repository; edit this file to name it for gitweb. +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/applypatch-msg linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/applypatch-msg +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/applypatch-msg 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/applypatch-msg 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,15 @@ ++#!/bin/sh ++# ++# An example hook script to check the commit log message taken by ++# applypatch from an e-mail message. ++# ++# The hook should exit with non-zero status after issuing an ++# appropriate message if it wants to stop the commit. The hook is ++# allowed to edit the commit message file. ++# ++# To enable this hook, make this file executable. ++ ++. git-sh-setup ++test -x "$GIT_DIR/hooks/commit-msg" && ++ exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} ++: +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/commit-msg linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/commit-msg +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/commit-msg 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/commit-msg 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,24 @@ ++#!/bin/sh ++# ++# An example hook script to check the commit log message. ++# Called by git-commit with one argument, the name of the file ++# that has the commit message. The hook should exit with non-zero ++# status after issuing an appropriate message if it wants to stop the ++# commit. The hook is allowed to edit the commit message file. ++# ++# To enable this hook, make this file executable. ++ ++# Uncomment the below to add a Signed-off-by line to the message. ++# Doing this in a hook is a bad idea in general, but the prepare-commit-msg ++# hook is more suited to it. ++# ++# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') ++# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" ++ ++# This example catches duplicate Signed-off-by lines. ++ ++test "" = "$(grep '^Signed-off-by: ' "$1" | ++ sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { ++ echo >&2 Duplicate Signed-off-by lines. ++ exit 1 ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/post-commit linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/post-commit +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/post-commit 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/post-commit 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,8 @@ ++#!/bin/sh ++# ++# An example hook script that is called after a successful ++# commit is made. ++# ++# To enable this hook, make this file executable. ++ ++: Nothing +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/post-receive linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/post-receive +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/post-receive 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/post-receive 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,16 @@ ++#!/bin/sh ++# ++# An example hook script for the post-receive event ++# ++# This script is run after receive-pack has accepted a pack and the ++# repository has been updated. It is passed arguments in through stdin ++# in the form ++# ++# For example: ++# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master ++# ++# see contrib/hooks/ for an sample, or uncomment the next line (on debian) ++# ++ ++ ++#. /usr/share/doc/git-core/contrib/hooks/post-receive-email +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/post-update linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/post-update +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/post-update 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/post-update 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,8 @@ ++#!/bin/sh ++# ++# An example hook script to prepare a packed repository for use over ++# dumb transports. ++# ++# To enable this hook, make this file executable by "chmod +x post-update". ++ ++exec git-update-server-info +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/pre-applypatch linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/pre-applypatch +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/pre-applypatch 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/pre-applypatch 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,14 @@ ++#!/bin/sh ++# ++# An example hook script to verify what is about to be committed ++# by applypatch from an e-mail message. ++# ++# The hook should exit with non-zero status after issuing an ++# appropriate message if it wants to stop the commit. ++# ++# To enable this hook, make this file executable. ++ ++. git-sh-setup ++test -x "$GIT_DIR/hooks/pre-commit" && ++ exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} ++: +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/pre-commit linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/pre-commit +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/pre-commit 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/pre-commit 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,70 @@ ++#!/bin/sh ++# ++# An example hook script to verify what is about to be committed. ++# Called by git-commit with no arguments. The hook should ++# exit with non-zero status after issuing an appropriate message if ++# it wants to stop the commit. ++# ++# To enable this hook, make this file executable. ++ ++# This is slightly modified from Andrew Morton's Perfect Patch. ++# Lines you introduce should not have trailing whitespace. ++# Also check for an indentation that has SP before a TAB. ++ ++if git-rev-parse --verify HEAD 2>/dev/null ++then ++ git-diff-index -p -M --cached HEAD -- ++else ++ # NEEDSWORK: we should produce a diff with an empty tree here ++ # if we want to do the same verification for the initial import. ++ : ++fi | ++perl -e ' ++ my $found_bad = 0; ++ my $filename; ++ my $reported_filename = ""; ++ my $lineno; ++ sub bad_line { ++ my ($why, $line) = @_; ++ if (!$found_bad) { ++ print STDERR "*\n"; ++ print STDERR "* You have some suspicious patch lines:\n"; ++ print STDERR "*\n"; ++ $found_bad = 1; ++ } ++ if ($reported_filename ne $filename) { ++ print STDERR "* In $filename\n"; ++ $reported_filename = $filename; ++ } ++ print STDERR "* $why (line $lineno)\n"; ++ print STDERR "$filename:$lineno:$line\n"; ++ } ++ while (<>) { ++ if (m|^diff --git a/(.*) b/\1$|) { ++ $filename = $1; ++ next; ++ } ++ if (/^@@ -\S+ \+(\d+)/) { ++ $lineno = $1 - 1; ++ next; ++ } ++ if (/^ /) { ++ $lineno++; ++ next; ++ } ++ if (s/^\+//) { ++ $lineno++; ++ chomp; ++ if (/\s$/) { ++ bad_line("trailing whitespace", $_); ++ } ++ if (/^\s* \t/) { ++ bad_line("indent SP followed by a TAB", $_); ++ } ++ if (/^([<>])\1{6} |^={7}$/) { ++ bad_line("unresolved merge conflict", $_); ++ } ++ } ++ } ++ exit($found_bad); ++' +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/pre-rebase linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/pre-rebase +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/pre-rebase 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/pre-rebase 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,150 @@ ++#!/bin/sh ++# ++# Copyright (c) 2006 Junio C Hamano ++# ++ ++publish=next ++basebranch="$1" ++if test "$#" = 2 ++then ++ topic="refs/heads/$2" ++else ++ topic=`git symbolic-ref HEAD` ++fi ++ ++case "$basebranch,$topic" in ++master,refs/heads/??/*) ++ ;; ++*) ++ exit 0 ;# we do not interrupt others. ++ ;; ++esac ++ ++# Now we are dealing with a topic branch being rebased ++# on top of master. Is it OK to rebase it? ++ ++# Is topic fully merged to master? ++not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` ++if test -z "$not_in_master" ++then ++ echo >&2 "$topic is fully merged to master; better remove it." ++ exit 1 ;# we could allow it, but there is no point. ++fi ++ ++# Is topic ever merged to next? If so you should not be rebasing it. ++only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` ++only_next_2=`git-rev-list ^master ${publish} | sort` ++if test "$only_next_1" = "$only_next_2" ++then ++ not_in_topic=`git-rev-list "^$topic" master` ++ if test -z "$not_in_topic" ++ then ++ echo >&2 "$topic is already up-to-date with master" ++ exit 1 ;# we could allow it, but there is no point. ++ else ++ exit 0 ++ fi ++else ++ not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` ++ perl -e ' ++ my $topic = $ARGV[0]; ++ my $msg = "* $topic has commits already merged to public branch:\n"; ++ my (%not_in_next) = map { ++ /^([0-9a-f]+) /; ++ ($1 => 1); ++ } split(/\n/, $ARGV[1]); ++ for my $elem (map { ++ /^([0-9a-f]+) (.*)$/; ++ [$1 => $2]; ++ } split(/\n/, $ARGV[2])) { ++ if (!exists $not_in_next{$elem->[0]}) { ++ if ($msg) { ++ print STDERR $msg; ++ undef $msg; ++ } ++ print STDERR " $elem->[1]\n"; ++ } ++ } ++ ' "$topic" "$not_in_next" "$not_in_master" ++ exit 1 ++fi ++ ++exit 0 ++ ++################################################################ ++ ++This sample hook safeguards topic branches that have been ++published from being rewound. ++ ++The workflow assumed here is: ++ ++ * Once a topic branch forks from "master", "master" is never ++ merged into it again (either directly or indirectly). ++ ++ * Once a topic branch is fully cooked and merged into "master", ++ it is deleted. If you need to build on top of it to correct ++ earlier mistakes, a new topic branch is created by forking at ++ the tip of the "master". This is not strictly necessary, but ++ it makes it easier to keep your history simple. ++ ++ * Whenever you need to test or publish your changes to topic ++ branches, merge them into "next" branch. ++ ++The script, being an example, hardcodes the publish branch name ++to be "next", but it is trivial to make it configurable via ++$GIT_DIR/config mechanism. ++ ++With this workflow, you would want to know: ++ ++(1) ... if a topic branch has ever been merged to "next". Young ++ topic branches can have stupid mistakes you would rather ++ clean up before publishing, and things that have not been ++ merged into other branches can be easily rebased without ++ affecting other people. But once it is published, you would ++ not want to rewind it. ++ ++(2) ... if a topic branch has been fully merged to "master". ++ Then you can delete it. More importantly, you should not ++ build on top of it -- other people may already want to ++ change things related to the topic as patches against your ++ "master", so if you need further changes, it is better to ++ fork the topic (perhaps with the same name) afresh from the ++ tip of "master". ++ ++Let's look at this example: ++ ++ o---o---o---o---o---o---o---o---o---o "next" ++ / / / / ++ / a---a---b A / / ++ / / / / ++ / / c---c---c---c B / ++ / / / \ / ++ / / / b---b C \ / ++ / / / / \ / ++ ---o---o---o---o---o---o---o---o---o---o---o "master" ++ ++ ++A, B and C are topic branches. ++ ++ * A has one fix since it was merged up to "next". ++ ++ * B has finished. It has been fully merged up to "master" and "next", ++ and is ready to be deleted. ++ ++ * C has not merged to "next" at all. ++ ++We would want to allow C to be rebased, refuse A, and encourage ++B to be deleted. ++ ++To compute (1): ++ ++ git-rev-list ^master ^topic next ++ git-rev-list ^master next ++ ++ if these match, topic has not merged in next at all. ++ ++To compute (2): ++ ++ git-rev-list master..topic ++ ++ if this is empty, it is fully merged to "master". +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/prepare-commit-msg linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/prepare-commit-msg +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/prepare-commit-msg 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/prepare-commit-msg 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,36 @@ ++#!/bin/sh ++# ++# An example hook script to prepare the commit log message. ++# Called by git-commit with the name of the file that has the ++# commit message, followed by the description of the commit ++# message's source. The hook's purpose is to edit the commit ++# message file. If the hook fails with a non-zero status, ++# the commit is aborted. ++# ++# To enable this hook, make this file executable. ++ ++# This hook includes three examples. The first comments out the ++# "Conflicts:" part of a merge commit. ++# ++# The second includes the output of "git diff --name-status -r" ++# into the message, just before the "git status" output. It is ++# commented because it doesn't cope with --amend or with squashed ++# commits. ++# ++# The third example adds a Signed-off-by line to the message, that can ++# still be edited. This is rarely a good idea. ++ ++case "$2 $3" in ++ merge) ++ sed -i '/^Conflicts:/,/#/!b;s/^/# &/;s/^# #/#/' "$1" ;; ++ ++# ""|template) ++# perl -i -pe ' ++# print "\n" . `git diff --cached --name-status -r` ++# if /^#/ && $first++ == 0' "$1" ;; ++ ++ *) ;; ++esac ++ ++# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') ++# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/update linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/update +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/hooks/update 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/hooks/update 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,107 @@ ++#!/bin/sh ++# ++# An example hook script to blocks unannotated tags from entering. ++# Called by git-receive-pack with arguments: refname sha1-old sha1-new ++# ++# To enable this hook, make this file executable by "chmod +x update". ++# ++# Config ++# ------ ++# hooks.allowunannotated ++# This boolean sets whether unannotated tags will be allowed into the ++# repository. By default they won't be. ++# hooks.allowdeletetag ++# This boolean sets whether deleting tags will be allowed in the ++# repository. By default they won't be. ++# hooks.allowdeletebranch ++# This boolean sets whether deleting branches will be allowed in the ++# repository. By default they won't be. ++# ++ ++# --- Command line ++refname="$1" ++oldrev="$2" ++newrev="$3" ++ ++# --- Safety check ++if [ -z "$GIT_DIR" ]; then ++ echo "Don't run this script from the command line." >&2 ++ echo " (if you want, you could supply GIT_DIR then run" >&2 ++ echo " $0 )" >&2 ++ exit 1 ++fi ++ ++if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then ++ echo "Usage: $0 " >&2 ++ exit 1 ++fi ++ ++# --- Config ++allowunannotated=$(git config --bool hooks.allowunannotated) ++allowdeletebranch=$(git config --bool hooks.allowdeletebranch) ++allowdeletetag=$(git config --bool hooks.allowdeletetag) ++ ++# check for no description ++projectdesc=$(sed -e '1q' "$GIT_DIR/description") ++if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb." ]; then ++ echo "*** Project description file hasn't been set" >&2 ++ exit 1 ++fi ++ ++# --- Check types ++# if $newrev is 0000...0000, it's a commit to delete a ref. ++if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then ++ newrev_type=delete ++else ++ newrev_type=$(git-cat-file -t $newrev) ++fi ++ ++case "$refname","$newrev_type" in ++ refs/tags/*,commit) ++ # un-annotated tag ++ short_refname=${refname##refs/tags/} ++ if [ "$allowunannotated" != "true" ]; then ++ echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 ++ echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 ++ exit 1 ++ fi ++ ;; ++ refs/tags/*,delete) ++ # delete tag ++ if [ "$allowdeletetag" != "true" ]; then ++ echo "*** Deleting a tag is not allowed in this repository" >&2 ++ exit 1 ++ fi ++ ;; ++ refs/tags/*,tag) ++ # annotated tag ++ ;; ++ refs/heads/*,commit) ++ # branch ++ ;; ++ refs/heads/*,delete) ++ # delete branch ++ if [ "$allowdeletebranch" != "true" ]; then ++ echo "*** Deleting a branch is not allowed in this repository" >&2 ++ exit 1 ++ fi ++ ;; ++ refs/remotes/*,commit) ++ # tracking branch ++ ;; ++ refs/remotes/*,delete) ++ # delete tracking branch ++ if [ "$allowdeletebranch" != "true" ]; then ++ echo "*** Deleting a tracking branch is not allowed in this repository" >&2 ++ exit 1 ++ fi ++ ;; ++ *) ++ # Anything else (is there anything else?) ++ echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 ++ exit 1 ++ ;; ++esac ++ ++# --- Finished ++exit 0 +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/info/exclude linux-2.6.24.7/drivers/mtd/mtd-utils/.git/info/exclude +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/info/exclude 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/info/exclude 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,6 @@ ++# git-ls-files --others --exclude-from=.git/info/exclude ++# Lines that start with '#' are comments. ++# For a project mostly in C, the following would be a good set of ++# exclude patterns (uncomment them if you want to use them): ++# *.[oa] ++# *~ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/HEAD linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/HEAD +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/HEAD 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/HEAD 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++0000000000000000000000000000000000000000 d5f5e57b72289bb5f551a4e17be0132aa8dfb6c4 nancy 1210143725 +0800 +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/refs/heads/master linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/refs/heads/master +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/refs/heads/master 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/refs/heads/master 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++0000000000000000000000000000000000000000 d5f5e57b72289bb5f551a4e17be0132aa8dfb6c4 nancy 1210143725 +0800 +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/master linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/master +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/master 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/master 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++0000000000000000000000000000000000000000 d5f5e57b72289bb5f551a4e17be0132aa8dfb6c4 nancy 1210143724 +0800 clone: from git://git.infradead.org/mtd-utils.git +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/origin linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/origin +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/origin 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/origin 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++0000000000000000000000000000000000000000 7afca4241e082c23bb38a4da532cc222457ae719 nancy 1210143724 +0800 clone: from git://git.infradead.org/mtd-utils.git +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/ubi linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/ubi +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/ubi 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/logs/refs/remotes/origin/ubi 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++0000000000000000000000000000000000000000 22f90673165489fd50c893a91051a79c1b143d2a nancy 1210143724 +0800 clone: from git://git.infradead.org/mtd-utils.git +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/objects/pack/pack-3aac9dd611b9d9b1367a74ba6217f301d746b650.keep linux-2.6.24.7/drivers/mtd/mtd-utils/.git/objects/pack/pack-3aac9dd611b9d9b1367a74ba6217f301d746b650.keep +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/objects/pack/pack-3aac9dd611b9d9b1367a74ba6217f301d746b650.keep 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/objects/pack/pack-3aac9dd611b9d9b1367a74ba6217f301d746b650.keep 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++fetch-pack 10055 on bogon +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/heads/master linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/heads/master +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/heads/master 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/heads/master 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++d5f5e57b72289bb5f551a4e17be0132aa8dfb6c4 +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/remotes/origin/HEAD linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/remotes/origin/HEAD +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/remotes/origin/HEAD 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/remotes/origin/HEAD 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++ref: refs/remotes/origin/master +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/remotes/origin/master linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/remotes/origin/master +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/remotes/origin/master 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/remotes/origin/master 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++d5f5e57b72289bb5f551a4e17be0132aa8dfb6c4 +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/remotes/origin/origin linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/remotes/origin/origin +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/remotes/origin/origin 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/remotes/origin/origin 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++7afca4241e082c23bb38a4da532cc222457ae719 +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/remotes/origin/ubi linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/remotes/origin/ubi +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.git/refs/remotes/origin/ubi 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.git/refs/remotes/origin/ubi 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++22f90673165489fd50c893a91051a79c1b143d2a +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/.gitignore linux-2.6.24.7/drivers/mtd/mtd-utils/.gitignore +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/.gitignore 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/.gitignore 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,26 @@ ++# ++# NOTE! Don't add files that are generated in specific ++# subdirectories here. Add them in the ".gitignore" file ++# in that subdirectory instead. ++# ++# Normal rules ++# ++.* ++*.o ++*.a ++*.s ++*.ko ++*.so ++*.mod.c ++*~ ++ ++# ++# Top-level generic files ++# ++ ++# ++# Generated include files ++# ++ ++# stgit generated dirs ++patches-* +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/COPYING linux-2.6.24.7/drivers/mtd/mtd-utils/COPYING +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/COPYING 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/COPYING 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,340 @@ ++ GNU GENERAL PUBLIC LICENSE ++ Version 2, June 1991 ++ ++ Copyright (C) 1989, 1991 Free Software Foundation, Inc. ++ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ Everyone is permitted to copy and distribute verbatim copies ++ of this license document, but changing it is not allowed. ++ ++ Preamble ++ ++ The licenses for most software are designed to take away your ++freedom to share and change it. By contrast, the GNU General Public ++License is intended to guarantee your freedom to share and change free ++software--to make sure the software is free for all its users. This ++General Public License applies to most of the Free Software ++Foundation's software and to any other program whose authors commit to ++using it. (Some other Free Software Foundation software is covered by ++the GNU Library General Public License instead.) You can apply it to ++your programs, too. ++ ++ When we speak of free software, we are referring to freedom, not ++price. Our General Public Licenses are designed to make sure that you ++have the freedom to distribute copies of free software (and charge for ++this service if you wish), that you receive source code or can get it ++if you want it, that you can change the software or use pieces of it ++in new free programs; and that you know you can do these things. ++ ++ To protect your rights, we need to make restrictions that forbid ++anyone to deny you these rights or to ask you to surrender the rights. ++These restrictions translate to certain responsibilities for you if you ++distribute copies of the software, or if you modify it. ++ ++ For example, if you distribute copies of such a program, whether ++gratis or for a fee, you must give the recipients all the rights that ++you have. You must make sure that they, too, receive or can get the ++source code. And you must show them these terms so they know their ++rights. ++ ++ We protect your rights with two steps: (1) copyright the software, and ++(2) offer you this license which gives you legal permission to copy, ++distribute and/or modify the software. ++ ++ Also, for each author's protection and ours, we want to make certain ++that everyone understands that there is no warranty for this free ++software. If the software is modified by someone else and passed on, we ++want its recipients to know that what they have is not the original, so ++that any problems introduced by others will not reflect on the original ++authors' reputations. ++ ++ Finally, any free program is threatened constantly by software ++patents. We wish to avoid the danger that redistributors of a free ++program will individually obtain patent licenses, in effect making the ++program proprietary. To prevent this, we have made it clear that any ++patent must be licensed for everyone's free use or not licensed at all. ++ ++ The precise terms and conditions for copying, distribution and ++modification follow. ++ ++ GNU GENERAL PUBLIC LICENSE ++ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ++ ++ 0. This License applies to any program or other work which contains ++a notice placed by the copyright holder saying it may be distributed ++under the terms of this General Public License. The "Program", below, ++refers to any such program or work, and a "work based on the Program" ++means either the Program or any derivative work under copyright law: ++that is to say, a work containing the Program or a portion of it, ++either verbatim or with modifications and/or translated into another ++language. (Hereinafter, translation is included without limitation in ++the term "modification".) Each licensee is addressed as "you". ++ ++Activities other than copying, distribution and modification are not ++covered by this License; they are outside its scope. The act of ++running the Program is not restricted, and the output from the Program ++is covered only if its contents constitute a work based on the ++Program (independent of having been made by running the Program). ++Whether that is true depends on what the Program does. ++ ++ 1. You may copy and distribute verbatim copies of the Program's ++source code as you receive it, in any medium, provided that you ++conspicuously and appropriately publish on each copy an appropriate ++copyright notice and disclaimer of warranty; keep intact all the ++notices that refer to this License and to the absence of any warranty; ++and give any other recipients of the Program a copy of this License ++along with the Program. ++ ++You may charge a fee for the physical act of transferring a copy, and ++you may at your option offer warranty protection in exchange for a fee. ++ ++ 2. You may modify your copy or copies of the Program or any portion ++of it, thus forming a work based on the Program, and copy and ++distribute such modifications or work under the terms of Section 1 ++above, provided that you also meet all of these conditions: ++ ++ a) You must cause the modified files to carry prominent notices ++ stating that you changed the files and the date of any change. ++ ++ b) You must cause any work that you distribute or publish, that in ++ whole or in part contains or is derived from the Program or any ++ part thereof, to be licensed as a whole at no charge to all third ++ parties under the terms of this License. ++ ++ c) If the modified program normally reads commands interactively ++ when run, you must cause it, when started running for such ++ interactive use in the most ordinary way, to print or display an ++ announcement including an appropriate copyright notice and a ++ notice that there is no warranty (or else, saying that you provide ++ a warranty) and that users may redistribute the program under ++ these conditions, and telling the user how to view a copy of this ++ License. (Exception: if the Program itself is interactive but ++ does not normally print such an announcement, your work based on ++ the Program is not required to print an announcement.) ++ ++These requirements apply to the modified work as a whole. If ++identifiable sections of that work are not derived from the Program, ++and can be reasonably considered independent and separate works in ++themselves, then this License, and its terms, do not apply to those ++sections when you distribute them as separate works. But when you ++distribute the same sections as part of a whole which is a work based ++on the Program, the distribution of the whole must be on the terms of ++this License, whose permissions for other licensees extend to the ++entire whole, and thus to each and every part regardless of who wrote it. ++ ++Thus, it is not the intent of this section to claim rights or contest ++your rights to work written entirely by you; rather, the intent is to ++exercise the right to control the distribution of derivative or ++collective works based on the Program. ++ ++In addition, mere aggregation of another work not based on the Program ++with the Program (or with a work based on the Program) on a volume of ++a storage or distribution medium does not bring the other work under ++the scope of this License. ++ ++ 3. You may copy and distribute the Program (or a work based on it, ++under Section 2) in object code or executable form under the terms of ++Sections 1 and 2 above provided that you also do one of the following: ++ ++ a) Accompany it with the complete corresponding machine-readable ++ source code, which must be distributed under the terms of Sections ++ 1 and 2 above on a medium customarily used for software interchange; or, ++ ++ b) Accompany it with a written offer, valid for at least three ++ years, to give any third party, for a charge no more than your ++ cost of physically performing source distribution, a complete ++ machine-readable copy of the corresponding source code, to be ++ distributed under the terms of Sections 1 and 2 above on a medium ++ customarily used for software interchange; or, ++ ++ c) Accompany it with the information you received as to the offer ++ to distribute corresponding source code. (This alternative is ++ allowed only for noncommercial distribution and only if you ++ received the program in object code or executable form with such ++ an offer, in accord with Subsection b above.) ++ ++The source code for a work means the preferred form of the work for ++making modifications to it. For an executable work, complete source ++code means all the source code for all modules it contains, plus any ++associated interface definition files, plus the scripts used to ++control compilation and installation of the executable. However, as a ++special exception, the source code distributed need not include ++anything that is normally distributed (in either source or binary ++form) with the major components (compiler, kernel, and so on) of the ++operating system on which the executable runs, unless that component ++itself accompanies the executable. ++ ++If distribution of executable or object code is made by offering ++access to copy from a designated place, then offering equivalent ++access to copy the source code from the same place counts as ++distribution of the source code, even though third parties are not ++compelled to copy the source along with the object code. ++ ++ 4. You may not copy, modify, sublicense, or distribute the Program ++except as expressly provided under this License. Any attempt ++otherwise to copy, modify, sublicense or distribute the Program is ++void, and will automatically terminate your rights under this License. ++However, parties who have received copies, or rights, from you under ++this License will not have their licenses terminated so long as such ++parties remain in full compliance. ++ ++ 5. You are not required to accept this License, since you have not ++signed it. However, nothing else grants you permission to modify or ++distribute the Program or its derivative works. These actions are ++prohibited by law if you do not accept this License. Therefore, by ++modifying or distributing the Program (or any work based on the ++Program), you indicate your acceptance of this License to do so, and ++all its terms and conditions for copying, distributing or modifying ++the Program or works based on it. ++ ++ 6. Each time you redistribute the Program (or any work based on the ++Program), the recipient automatically receives a license from the ++original licensor to copy, distribute or modify the Program subject to ++these terms and conditions. You may not impose any further ++restrictions on the recipients' exercise of the rights granted herein. ++You are not responsible for enforcing compliance by third parties to ++this License. ++ ++ 7. If, as a consequence of a court judgment or allegation of patent ++infringement or for any other reason (not limited to patent issues), ++conditions are imposed on you (whether by court order, agreement or ++otherwise) that contradict the conditions of this License, they do not ++excuse you from the conditions of this License. If you cannot ++distribute so as to satisfy simultaneously your obligations under this ++License and any other pertinent obligations, then as a consequence you ++may not distribute the Program at all. For example, if a patent ++license would not permit royalty-free redistribution of the Program by ++all those who receive copies directly or indirectly through you, then ++the only way you could satisfy both it and this License would be to ++refrain entirely from distribution of the Program. ++ ++If any portion of this section is held invalid or unenforceable under ++any particular circumstance, the balance of the section is intended to ++apply and the section as a whole is intended to apply in other ++circumstances. ++ ++It is not the purpose of this section to induce you to infringe any ++patents or other property right claims or to contest validity of any ++such claims; this section has the sole purpose of protecting the ++integrity of the free software distribution system, which is ++implemented by public license practices. Many people have made ++generous contributions to the wide range of software distributed ++through that system in reliance on consistent application of that ++system; it is up to the author/donor to decide if he or she is willing ++to distribute software through any other system and a licensee cannot ++impose that choice. ++ ++This section is intended to make thoroughly clear what is believed to ++be a consequence of the rest of this License. ++ ++ 8. If the distribution and/or use of the Program is restricted in ++certain countries either by patents or by copyrighted interfaces, the ++original copyright holder who places the Program under this License ++may add an explicit geographical distribution limitation excluding ++those countries, so that distribution is permitted only in or among ++countries not thus excluded. In such case, this License incorporates ++the limitation as if written in the body of this License. ++ ++ 9. The Free Software Foundation may publish revised and/or new versions ++of the General Public License from time to time. Such new versions will ++be similar in spirit to the present version, but may differ in detail to ++address new problems or concerns. ++ ++Each version is given a distinguishing version number. If the Program ++specifies a version number of this License which applies to it and "any ++later version", you have the option of following the terms and conditions ++either of that version or of any later version published by the Free ++Software Foundation. If the Program does not specify a version number of ++this License, you may choose any version ever published by the Free Software ++Foundation. ++ ++ 10. If you wish to incorporate parts of the Program into other free ++programs whose distribution conditions are different, write to the author ++to ask for permission. For software which is copyrighted by the Free ++Software Foundation, write to the Free Software Foundation; we sometimes ++make exceptions for this. Our decision will be guided by the two goals ++of preserving the free status of all derivatives of our free software and ++of promoting the sharing and reuse of software generally. ++ ++ NO WARRANTY ++ ++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY ++FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN ++OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES ++PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED ++OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS ++TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE ++PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, ++REPAIR OR CORRECTION. ++ ++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING ++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR ++REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, ++INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING ++OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED ++TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY ++YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER ++PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGES. ++ ++ END OF TERMS AND CONDITIONS ++ ++ How to Apply These Terms to Your New Programs ++ ++ If you develop a new program, and you want it to be of the greatest ++possible use to the public, the best way to achieve this is to make it ++free software which everyone can redistribute and change under these terms. ++ ++ To do so, attach the following notices to the program. It is safest ++to attach them to the start of each source file to most effectively ++convey the exclusion of warranty; and each file should have at least ++the "copyright" line and a pointer to where the full notice is found. ++ ++ ++ Copyright (C) 19yy ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++ ++Also add information on how to contact you by electronic and paper mail. ++ ++If the program is interactive, make it output a short notice like this ++when it starts in an interactive mode: ++ ++ Gnomovision version 69, Copyright (C) 19yy name of author ++ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. ++ This is free software, and you are welcome to redistribute it ++ under certain conditions; type `show c' for details. ++ ++The hypothetical commands `show w' and `show c' should show the appropriate ++parts of the General Public License. Of course, the commands you use may ++be called something other than `show w' and `show c'; they could even be ++mouse-clicks or menu items--whatever suits your program. ++ ++You should also get your employer (if you work as a programmer) or your ++school, if any, to sign a "copyright disclaimer" for the program, if ++necessary. Here is a sample; alter the names: ++ ++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program ++ `Gnomovision' (which makes passes at compilers) written by James Hacker. ++ ++ , 1 April 1989 ++ Ty Coon, President of Vice ++ ++This General Public License does not permit incorporating your program into ++proprietary programs. If your program is a subroutine library, you may ++consider it more useful to permit linking proprietary applications with the ++library. If this is what you want to do, use the GNU Library General ++Public License instead of this License. +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/MAKEDEV linux-2.6.24.7/drivers/mtd/mtd-utils/MAKEDEV +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/MAKEDEV 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/MAKEDEV 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,42 @@ ++#!/bin/bash ++ ++function mkftl () { ++ mknod /dev/ftl$1 b 44 $2 ++ for a in `seq 1 15`; do ++ mknod /dev/ftl$1$a b 44 `expr $2 + $a` ++ done ++} ++function mknftl () { ++ mknod /dev/nftl$1 b 93 $2 ++ for a in `seq 1 15`; do ++ mknod /dev/nftl$1$a b 93 `expr $2 + $a` ++ done ++} ++function mkrfd () { ++ mknod /dev/rfd$1 b 256 $2 ++ for a in `seq 1 15`; do ++ mknod /dev/rfd$1$a b 256 `expr $2 + $a` ++ done ++} ++function mkinftl () { ++ mknod /dev/inftl$1 b 96 $2 ++ for a in `seq 1 15`; do ++ mknod /dev/inftl$1$a b 96 `expr $2 + $a` ++ done ++} ++ ++M=0 ++for C in a b c d e f g h i j k l m n o p; do ++ mkftl $C $M ++ mknftl $C $M ++ mkrfd $C $M ++ mkinftl $C $M ++ let M=M+16 ++done ++ ++for a in `seq 0 16` ; do ++ mknod /dev/mtd$a c 90 `expr $a + $a` ++ mknod /dev/mtdr$a c 90 `expr $a + $a + 1` ++ mknod /dev/mtdblock$a b 31 $a ++done ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/Makefile linux-2.6.24.7/drivers/mtd/mtd-utils/Makefile +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,102 @@ ++ ++# -*- sh -*- ++ ++OPTFLAGS := -O2 -Wall ++SBINDIR=/usr/sbin ++MANDIR=/usr/share/man ++INCLUDEDIR=/usr/include ++CROSS=mipsel-linux- ++CC := $(CROSS)gcc ++CFLAGS := -I./include $(OPTFLAGS) ++ ++ifeq ($(origin CROSS),undefined) ++ BUILDDIR := . ++else ++# Remove the trailing slash to make the directory name ++ BUILDDIR := .#$(CROSS:-=) ++endif ++ ++ifeq ($(WITHOUT_XATTR), 1) ++ CFLAGS += -DWITHOUT_XATTR ++endif ++ ++#RAWTARGETS = ftl_format flash_erase flash_eraseall nanddump doc_loadbios \ ++# ftl_check mkfs.jffs2 flash_lock flash_unlock flash_info \ ++# flash_otp_info flash_otp_dump mtd_debug flashcp nandwrite nandtest \ ++# jffs2dump \ ++# nftldump nftl_format docfdisk \ ++# rfddump rfdformat \ ++# serve_image recv_image \ ++# sumtool #jffs2reader ++ ++RAWTARGETS = flash_erase flash_eraseall nanddump nanddump_vfat \ ++ flash_info \ ++ flash_otp_info flash_otp_dump nandwrite nandwrite_mlc \ ++ nandtest \ ++ sumtool #jffs2reader ++ ++TARGETS = $(foreach target,$(RAWTARGETS),$(BUILDDIR)/$(target)) ++ ++SYMLINKS = ++ ++%: %.o ++ $(CC) $(CFLAGS) $(LDFLAGS) -g -o $@ $^ ++ ++$(BUILDDIR)/%.o: %.c ++ mkdir -p $(BUILDDIR) ++ $(CC) $(CFLAGS) -g -c -o $@ $< -g -Wp,-MD,$(BUILDDIR)/.$( ${DESTDIR}/${MANDIR}/man1/mkfs.jffs2.1.gz ++ make -C $(BUILDDIR)/ubi-utils install +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr.c linux-2.6.24.7/drivers/mtd/mtd-utils/compr.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/compr.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,538 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2004 Ferenc Havasi , ++ * University of Szeged, Hungary ++ * ++ * For licensing information, see the file 'LICENCE' in this directory ++ * in the jffs2 directory. ++ */ ++ ++#include "compr.h" ++#include ++#include ++#include ++ ++#define FAVOUR_LZO_PERCENT 80 ++ ++extern int page_size; ++ ++/* LIST IMPLEMENTATION (from linux/list.h) */ ++ ++#define LIST_HEAD_INIT(name) { &(name), &(name) } ++ ++#define LIST_HEAD(name) \ ++ struct list_head name = LIST_HEAD_INIT(name) ++ ++static inline void __list_add(struct list_head *new, ++ struct list_head *prev, ++ struct list_head *next) ++{ ++ next->prev = new; ++ new->next = next; ++ new->prev = prev; ++ prev->next = new; ++} ++ ++static inline void list_add(struct list_head *new, struct list_head *head) ++{ ++ __list_add(new, head, head->next); ++} ++ ++static inline void list_add_tail(struct list_head *new, struct list_head *head) ++{ ++ __list_add(new, head->prev, head); ++} ++ ++static inline void __list_del(struct list_head *prev, struct list_head *next) ++{ ++ next->prev = prev; ++ prev->next = next; ++} ++ ++static inline void list_del(struct list_head *entry) ++{ ++ __list_del(entry->prev, entry->next); ++ entry->next = (void *) 0; ++ entry->prev = (void *) 0; ++} ++ ++#define list_entry(ptr, type, member) \ ++ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) ++ ++#define list_for_each_entry(pos, head, member) \ ++ for (pos = list_entry((head)->next, typeof(*pos), member); \ ++ &pos->member != (head); \ ++ pos = list_entry(pos->member.next, typeof(*pos), member)) ++ ++ ++/* Available compressors are on this list */ ++static LIST_HEAD(jffs2_compressor_list); ++ ++/* Actual compression mode */ ++static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; ++ ++void jffs2_set_compression_mode(int mode) ++{ ++ jffs2_compression_mode = mode; ++} ++ ++int jffs2_get_compression_mode(void) ++{ ++ return jffs2_compression_mode; ++} ++ ++/* Statistics for blocks stored without compression */ ++static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; ++ ++/* Compression test stuffs */ ++ ++static int jffs2_compression_check = 0; ++ ++static unsigned char *jffs2_compression_check_buf = NULL; ++ ++void jffs2_compression_check_set(int yesno) ++{ ++ jffs2_compression_check = yesno; ++} ++ ++int jffs2_compression_check_get(void) ++{ ++ return jffs2_compression_check; ++} ++ ++static int jffs2_error_cnt = 0; ++ ++int jffs2_compression_check_errorcnt_get(void) ++{ ++ return jffs2_error_cnt; ++} ++ ++#define JFFS2_BUFFER_FILL 0x55 ++ ++/* Called before compression (if compression_check is setted) to prepare ++ the buffer for buffer overflow test */ ++static void jffs2_decompression_test_prepare(unsigned char *buf, int size) ++{ ++ memset(buf,JFFS2_BUFFER_FILL,size+1); ++} ++ ++/* Called after compression (if compression_check is setted) to test the result */ ++static void jffs2_decompression_test(struct jffs2_compressor *compr, ++ unsigned char *data_in, unsigned char *output_buf, ++ uint32_t cdatalen, uint32_t datalen, uint32_t buf_size) ++{ ++ uint32_t i; ++ ++ /* buffer overflow test */ ++ for (i=buf_size;i>cdatalen;i--) { ++ if (output_buf[i]!=JFFS2_BUFFER_FILL) { ++ fprintf(stderr,"COMPR_ERROR: buffer overflow at %s. " ++ "(bs=%d csize=%d b[%d]=%d)\n", compr->name, ++ buf_size, cdatalen, i, (int)(output_buf[i])); ++ jffs2_error_cnt++; ++ return; ++ } ++ } ++ /* allocing temporary buffer for decompression */ ++ if (!jffs2_compression_check_buf) { ++ jffs2_compression_check_buf = malloc(page_size); ++ if (!jffs2_compression_check_buf) { ++ fprintf(stderr,"No memory for buffer allocation. Compression check disabled.\n"); ++ jffs2_compression_check = 0; ++ return; ++ } ++ } ++ /* decompressing */ ++ if (!compr->decompress) { ++ fprintf(stderr,"JFFS2 compression check: there is no decompress function at %s.\n", compr->name); ++ jffs2_error_cnt++; ++ return; ++ } ++ if (compr->decompress(output_buf,jffs2_compression_check_buf,cdatalen,datalen,NULL)) { ++ fprintf(stderr,"JFFS2 compression check: decompression failed at %s.\n", compr->name); ++ jffs2_error_cnt++; ++ } ++ /* validate decompression */ ++ else { ++ for (i=0;iname, i); ++ jffs2_error_cnt++; ++ break; ++ } ++ } ++ } ++} ++ ++/* ++ * Return 1 to use this compression ++ */ ++static int jffs2_is_best_compression(struct jffs2_compressor *this, ++ struct jffs2_compressor *best, uint32_t size, uint32_t bestsize) ++{ ++ switch (jffs2_compression_mode) { ++ case JFFS2_COMPR_MODE_SIZE: ++ if (bestsize > size) ++ return 1; ++ return 0; ++ case JFFS2_COMPR_MODE_FAVOURLZO: ++ if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size)) ++ return 1; ++ if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size)) ++ return 1; ++ if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100))) ++ return 1; ++ if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size) ++ return 1; ++ ++ return 0; ++ } ++ /* Shouldn't happen */ ++ return 0; ++} ++ ++/* jffs2_compress: ++ * @data: Pointer to uncompressed data ++ * @cdata: Pointer to returned pointer to buffer for compressed data ++ * @datalen: On entry, holds the amount of data available for compression. ++ * On exit, expected to hold the amount of data actually compressed. ++ * @cdatalen: On entry, holds the amount of space available for compressed ++ * data. On exit, expected to hold the actual size of the compressed ++ * data. ++ * ++ * Returns: Lower byte to be stored with data indicating compression type used. ++ * Zero is used to show that the data could not be compressed - the ++ * compressed version was actually larger than the original. ++ * Upper byte will be used later. (soon) ++ * ++ * If the cdata buffer isn't large enough to hold all the uncompressed data, ++ * jffs2_compress should compress as much as will fit, and should set ++ * *datalen accordingly to show the amount of data which were compressed. ++ */ ++uint16_t jffs2_compress( unsigned char *data_in, unsigned char **cpage_out, ++ uint32_t *datalen, uint32_t *cdatalen) ++{ ++ int ret = JFFS2_COMPR_NONE; ++ int compr_ret; ++ struct jffs2_compressor *this, *best=NULL; ++ unsigned char *output_buf = NULL, *tmp_buf; ++ uint32_t orig_slen, orig_dlen; ++ uint32_t best_slen=0, best_dlen=0; ++ ++ switch (jffs2_compression_mode) { ++ case JFFS2_COMPR_MODE_NONE: ++ break; ++ case JFFS2_COMPR_MODE_PRIORITY: ++ orig_slen = *datalen; ++ orig_dlen = *cdatalen; ++ output_buf = malloc(orig_dlen+jffs2_compression_check); ++ if (!output_buf) { ++ fprintf(stderr,"mkfs.jffs2: No memory for compressor allocation. Compression failed.\n"); ++ goto out; ++ } ++ list_for_each_entry(this, &jffs2_compressor_list, list) { ++ /* Skip decompress-only backwards-compatibility and disabled modules */ ++ if ((!this->compress)||(this->disabled)) ++ continue; ++ ++ this->usecount++; ++ ++ if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */ ++ jffs2_decompression_test_prepare(output_buf, orig_dlen); ++ ++ *datalen = orig_slen; ++ *cdatalen = orig_dlen; ++ compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL); ++ this->usecount--; ++ if (!compr_ret) { ++ ret = this->compr; ++ this->stat_compr_blocks++; ++ this->stat_compr_orig_size += *datalen; ++ this->stat_compr_new_size += *cdatalen; ++ if (jffs2_compression_check) ++ jffs2_decompression_test(this, data_in, output_buf, *cdatalen, *datalen, orig_dlen); ++ break; ++ } ++ } ++ if (ret == JFFS2_COMPR_NONE) free(output_buf); ++ break; ++ case JFFS2_COMPR_MODE_FAVOURLZO: ++ case JFFS2_COMPR_MODE_SIZE: ++ orig_slen = *datalen; ++ orig_dlen = *cdatalen; ++ list_for_each_entry(this, &jffs2_compressor_list, list) { ++ uint32_t needed_buf_size; ++ ++ if (jffs2_compression_mode == JFFS2_COMPR_MODE_FAVOURLZO) ++ needed_buf_size = orig_slen + jffs2_compression_check; ++ else ++ needed_buf_size = orig_dlen + jffs2_compression_check; ++ ++ /* Skip decompress-only backwards-compatibility and disabled modules */ ++ if ((!this->compress)||(this->disabled)) ++ continue; ++ /* Allocating memory for output buffer if necessary */ ++ if ((this->compr_buf_size < needed_buf_size) && (this->compr_buf)) { ++ free(this->compr_buf); ++ this->compr_buf_size=0; ++ this->compr_buf=NULL; ++ } ++ if (!this->compr_buf) { ++ tmp_buf = malloc(needed_buf_size); ++ if (!tmp_buf) { ++ fprintf(stderr,"mkfs.jffs2: No memory for compressor allocation. (%d bytes)\n",orig_dlen); ++ continue; ++ } ++ else { ++ this->compr_buf = tmp_buf; ++ this->compr_buf_size = orig_dlen; ++ } ++ } ++ this->usecount++; ++ if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */ ++ jffs2_decompression_test_prepare(this->compr_buf,this->compr_buf_size); ++ *datalen = orig_slen; ++ *cdatalen = orig_dlen; ++ compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL); ++ this->usecount--; ++ if (!compr_ret) { ++ if (jffs2_compression_check) ++ jffs2_decompression_test(this, data_in, this->compr_buf, *cdatalen, *datalen, this->compr_buf_size); ++ if (((!best_dlen) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen)) ++ && (*cdatalen < *datalen)) { ++ best_dlen = *cdatalen; ++ best_slen = *datalen; ++ best = this; ++ } ++ } ++ } ++ if (best_dlen) { ++ *cdatalen = best_dlen; ++ *datalen = best_slen; ++ output_buf = best->compr_buf; ++ best->compr_buf = NULL; ++ best->compr_buf_size = 0; ++ best->stat_compr_blocks++; ++ best->stat_compr_orig_size += best_slen; ++ best->stat_compr_new_size += best_dlen; ++ ret = best->compr; ++ } ++ break; ++ default: ++ fprintf(stderr,"mkfs.jffs2: unknow compression mode.\n"); ++ } ++out: ++ if (ret == JFFS2_COMPR_NONE) { ++ *cpage_out = data_in; ++ *datalen = *cdatalen; ++ none_stat_compr_blocks++; ++ none_stat_compr_size += *datalen; ++ } ++ else { ++ *cpage_out = output_buf; ++ } ++ return ret; ++} ++ ++ ++int jffs2_register_compressor(struct jffs2_compressor *comp) ++{ ++ struct jffs2_compressor *this; ++ ++ if (!comp->name) { ++ fprintf(stderr,"NULL compressor name at registering JFFS2 compressor. Failed.\n"); ++ return -1; ++ } ++ comp->compr_buf_size=0; ++ comp->compr_buf=NULL; ++ comp->usecount=0; ++ comp->stat_compr_orig_size=0; ++ comp->stat_compr_new_size=0; ++ comp->stat_compr_blocks=0; ++ comp->stat_decompr_blocks=0; ++ ++ list_for_each_entry(this, &jffs2_compressor_list, list) { ++ if (this->priority < comp->priority) { ++ list_add(&comp->list, this->list.prev); ++ goto out; ++ } ++ } ++ list_add_tail(&comp->list, &jffs2_compressor_list); ++out: ++ return 0; ++} ++ ++int jffs2_unregister_compressor(struct jffs2_compressor *comp) ++{ ++ ++ if (comp->usecount) { ++ fprintf(stderr,"mkfs.jffs2: Compressor modul is in use. Unregister failed.\n"); ++ return -1; ++ } ++ list_del(&comp->list); ++ ++ return 0; ++} ++ ++#define JFFS2_STAT_BUF_SIZE 16000 ++ ++char *jffs2_list_compressors(void) ++{ ++ struct jffs2_compressor *this; ++ char *buf, *act_buf; ++ ++ act_buf = buf = malloc(JFFS2_STAT_BUF_SIZE); ++ list_for_each_entry(this, &jffs2_compressor_list, list) { ++ act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority); ++ if ((this->disabled)||(!this->compress)) ++ act_buf += sprintf(act_buf,"disabled"); ++ else ++ act_buf += sprintf(act_buf,"enabled"); ++ act_buf += sprintf(act_buf,"\n"); ++ } ++ return buf; ++} ++ ++char *jffs2_stats(void) ++{ ++ struct jffs2_compressor *this; ++ char *buf, *act_buf; ++ ++ act_buf = buf = malloc(JFFS2_STAT_BUF_SIZE); ++ ++ act_buf += sprintf(act_buf,"Compression mode: "); ++ switch (jffs2_compression_mode) { ++ case JFFS2_COMPR_MODE_NONE: ++ act_buf += sprintf(act_buf,"none"); ++ break; ++ case JFFS2_COMPR_MODE_PRIORITY: ++ act_buf += sprintf(act_buf,"priority"); ++ break; ++ case JFFS2_COMPR_MODE_SIZE: ++ act_buf += sprintf(act_buf,"size"); ++ break; ++ case JFFS2_COMPR_MODE_FAVOURLZO: ++ act_buf += sprintf(act_buf, "favourlzo"); ++ break; ++ default: ++ act_buf += sprintf(act_buf,"unkown"); ++ break; ++ } ++ act_buf += sprintf(act_buf,"\nCompressors:\n"); ++ act_buf += sprintf(act_buf,"%10s ","none"); ++ act_buf += sprintf(act_buf,"compr: %d blocks (%d) decompr: %d blocks\n", none_stat_compr_blocks, ++ none_stat_compr_size, none_stat_decompr_blocks); ++ list_for_each_entry(this, &jffs2_compressor_list, list) { ++ act_buf += sprintf(act_buf,"%10s (prio:%d) ",this->name,this->priority); ++ if ((this->disabled)||(!this->compress)) ++ act_buf += sprintf(act_buf,"- "); ++ else ++ act_buf += sprintf(act_buf,"+ "); ++ act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d) decompr: %d blocks ", this->stat_compr_blocks, ++ this->stat_compr_new_size, this->stat_compr_orig_size, ++ this->stat_decompr_blocks); ++ act_buf += sprintf(act_buf,"\n"); ++ } ++ return buf; ++} ++ ++int jffs2_set_compression_mode_name(const char *name) ++{ ++ if (!strcmp("none",name)) { ++ jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; ++ return 0; ++ } ++ if (!strcmp("priority",name)) { ++ jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; ++ return 0; ++ } ++ if (!strcmp("size",name)) { ++ jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; ++ return 0; ++ } ++ if (!strcmp("favourlzo", name)) { ++ jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO; ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static int jffs2_compressor_Xable(const char *name, int disabled) ++{ ++ struct jffs2_compressor *this; ++ list_for_each_entry(this, &jffs2_compressor_list, list) { ++ if (!strcmp(this->name, name)) { ++ this->disabled = disabled; ++ return 0; ++ } ++ } ++ return 1; ++} ++ ++int jffs2_enable_compressor_name(const char *name) ++{ ++ return jffs2_compressor_Xable(name, 0); ++} ++ ++int jffs2_disable_compressor_name(const char *name) ++{ ++ return jffs2_compressor_Xable(name, 1); ++} ++ ++int jffs2_set_compressor_priority(const char *name, int priority) ++{ ++ struct jffs2_compressor *this,*comp; ++ list_for_each_entry(this, &jffs2_compressor_list, list) { ++ if (!strcmp(this->name, name)) { ++ this->priority = priority; ++ comp = this; ++ goto reinsert; ++ } ++ } ++ fprintf(stderr,"mkfs.jffs2: compressor %s not found.\n",name); ++ return 1; ++reinsert: ++ /* list is sorted in the order of priority, so if ++ we change it we have to reinsert it into the ++ good place */ ++ list_del(&comp->list); ++ list_for_each_entry(this, &jffs2_compressor_list, list) { ++ if (this->priority < comp->priority) { ++ list_add(&comp->list, this->list.prev); ++ return 0; ++ } ++ } ++ list_add_tail(&comp->list, &jffs2_compressor_list); ++ return 0; ++} ++ ++ ++int jffs2_compressors_init(void) ++{ ++#ifdef CONFIG_JFFS2_ZLIB ++ jffs2_zlib_init(); ++#endif ++#ifdef CONFIG_JFFS2_RTIME ++ jffs2_rtime_init(); ++#endif ++#ifdef CONFIG_JFFS2_LZO ++ jffs2_lzo_init(); ++#endif ++ return 0; ++} ++ ++int jffs2_compressors_exit(void) ++{ ++#ifdef CONFIG_JFFS2_RTIME ++ jffs2_rtime_exit(); ++#endif ++#ifdef CONFIG_JFFS2_ZLIB ++ jffs2_zlib_exit(); ++#endif ++#ifdef CONFIG_JFFS2_LZO ++ jffs2_lzo_exit(); ++#endif ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr.h linux-2.6.24.7/drivers/mtd/mtd-utils/compr.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/compr.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,119 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2004 Ferenc Havasi , ++ * University of Szeged, Hungary ++ * ++ * For licensing information, see the file 'LICENCE' in the ++ * jffs2 directory. ++ */ ++ ++#ifndef __JFFS2_COMPR_H__ ++#define __JFFS2_COMPR_H__ ++ ++#include ++#include ++#include ++#include "linux/jffs2.h" ++ ++#define CONFIG_JFFS2_ZLIB ++#define CONFIG_JFFS2_RTIME ++#define CONFIG_JFFS2_LZO ++ ++#define JFFS2_RUBINMIPS_PRIORITY 10 ++#define JFFS2_DYNRUBIN_PRIORITY 20 ++#define JFFS2_RTIME_PRIORITY 50 ++#define JFFS2_ZLIB_PRIORITY 60 ++#define JFFS2_LZO_PRIORITY 80 ++ ++#define JFFS2_COMPR_MODE_NONE 0 ++#define JFFS2_COMPR_MODE_PRIORITY 1 ++#define JFFS2_COMPR_MODE_SIZE 2 ++#define JFFS2_COMPR_MODE_FAVOURLZO 3 ++ ++#define kmalloc(a,b) malloc(a) ++#define kfree(a) free(a) ++#ifndef GFP_KERNEL ++#define GFP_KERNEL 0 ++#endif ++ ++#define vmalloc(a) malloc(a) ++#define vfree(a) free(a) ++ ++#define printk(...) fprintf(stderr,__VA_ARGS__) ++ ++#define KERN_EMERG ++#define KERN_ALERT ++#define KERN_CRIT ++#define KERN_ERR ++#define KERN_WARNING ++#define KERN_NOTICE ++#define KERN_INFO ++#define KERN_DEBUG ++ ++struct list_head { ++ struct list_head *next, *prev; ++}; ++ ++void jffs2_set_compression_mode(int mode); ++int jffs2_get_compression_mode(void); ++int jffs2_set_compression_mode_name(const char *mode_name); ++ ++int jffs2_enable_compressor_name(const char *name); ++int jffs2_disable_compressor_name(const char *name); ++ ++int jffs2_set_compressor_priority(const char *name, int priority); ++ ++struct jffs2_compressor { ++ struct list_head list; ++ int priority; /* used by prirority comr. mode */ ++ char *name; ++ char compr; /* JFFS2_COMPR_XXX */ ++ int (*compress)(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *srclen, uint32_t *destlen, void *model); ++ int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, ++ uint32_t cdatalen, uint32_t datalen, void *model); ++ int usecount; ++ int disabled; /* if seted the compressor won't compress */ ++ unsigned char *compr_buf; /* used by size compr. mode */ ++ uint32_t compr_buf_size; /* used by size compr. mode */ ++ uint32_t stat_compr_orig_size; ++ uint32_t stat_compr_new_size; ++ uint32_t stat_compr_blocks; ++ uint32_t stat_decompr_blocks; ++}; ++ ++int jffs2_register_compressor(struct jffs2_compressor *comp); ++int jffs2_unregister_compressor(struct jffs2_compressor *comp); ++ ++int jffs2_compressors_init(void); ++int jffs2_compressors_exit(void); ++ ++uint16_t jffs2_compress(unsigned char *data_in, unsigned char **cpage_out, ++ uint32_t *datalen, uint32_t *cdatalen); ++ ++/* If it is setted, a decompress will be called after every compress */ ++void jffs2_compression_check_set(int yesno); ++int jffs2_compression_check_get(void); ++int jffs2_compression_check_errorcnt_get(void); ++ ++char *jffs2_list_compressors(void); ++char *jffs2_stats(void); ++ ++/* Compressor modules */ ++ ++/* These functions will be called by jffs2_compressors_init/exit */ ++#ifdef CONFIG_JFFS2_ZLIB ++int jffs2_zlib_init(void); ++void jffs2_zlib_exit(void); ++#endif ++#ifdef CONFIG_JFFS2_RTIME ++int jffs2_rtime_init(void); ++void jffs2_rtime_exit(void); ++#endif ++#ifdef CONFIG_JFFS2_LZO ++int jffs2_lzo_init(void); ++void jffs2_lzo_exit(void); ++#endif ++ ++#endif /* __JFFS2_COMPR_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr_lzo.c linux-2.6.24.7/drivers/mtd/mtd-utils/compr_lzo.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr_lzo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/compr_lzo.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,120 @@ ++/* ++ * JFFS2 LZO Compression Interface. ++ * ++ * Copyright (C) 2007 Nokia Corporation. All rights reserved. ++ * ++ * Author: Richard Purdie ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "compr.h" ++ ++extern int page_size; ++ ++static void *lzo_mem; ++static void *lzo_compress_buf; ++ ++/* ++ * Note about LZO compression. ++ * ++ * We want to use the _999_ compression routine which gives better compression ++ * rates at the expense of time. Decompression time is unaffected. We might as ++ * well use the standard lzo library routines for this but they will overflow ++ * the destination buffer since they don't check the destination size. ++ * ++ * We therefore compress to a temporary buffer and copy if it will fit. ++ * ++ */ ++static int jffs2_lzo_cmpr(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen, void *model) ++{ ++ uint32_t compress_size; ++ int ret; ++ ++ ret = lzo1x_999_compress(data_in, *sourcelen, lzo_compress_buf, &compress_size, lzo_mem); ++ ++ if (ret != LZO_E_OK) ++ return -1; ++ ++ if (compress_size > *dstlen) ++ return -1; ++ ++ memcpy(cpage_out, lzo_compress_buf, compress_size); ++ *dstlen = compress_size; ++ ++ return 0; ++} ++ ++static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t srclen, uint32_t destlen, void *model) ++{ ++ int ret; ++ uint32_t dl; ++ ++ ret = lzo1x_decompress_safe(data_in,srclen,cpage_out,&dl,NULL); ++ ++ if (ret != LZO_E_OK || dl != destlen) ++ return -1; ++ ++ return 0; ++} ++ ++static struct jffs2_compressor jffs2_lzo_comp = { ++ .priority = JFFS2_LZO_PRIORITY, ++ .name = "lzo", ++ .compr = JFFS2_COMPR_LZO, ++ .compress = &jffs2_lzo_cmpr, ++ .decompress = &jffs2_lzo_decompress, ++ .disabled = 1, ++}; ++ ++int jffs2_lzo_init(void) ++{ ++ int ret; ++ ++ lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); ++ if (!lzo_mem) ++ return -1; ++ ++ /* Worse case LZO compression size from their FAQ */ ++ lzo_compress_buf = malloc(page_size + (page_size / 16) + 64 + 3); ++ if (!lzo_compress_buf) { ++ free(lzo_mem); ++ return -1; ++ } ++ ++ ret = jffs2_register_compressor(&jffs2_lzo_comp); ++ if (ret < 0) { ++ free(lzo_compress_buf); ++ free(lzo_mem); ++ } ++ ++ return ret; ++} ++ ++void jffs2_lzo_exit(void) ++{ ++ jffs2_unregister_compressor(&jffs2_lzo_comp); ++ free(lzo_compress_buf); ++ free(lzo_mem); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr_rtime.c linux-2.6.24.7/drivers/mtd/mtd-utils/compr_rtime.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr_rtime.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/compr_rtime.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,119 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2001-2003 Red Hat, Inc. ++ * ++ * Created by Arjan van de Ven ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * Very simple lz77-ish encoder. ++ * ++ * Theory of operation: Both encoder and decoder have a list of "last ++ * occurrences" for every possible source-value; after sending the ++ * first source-byte, the second byte indicated the "run" length of ++ * matches ++ * ++ * The algorithm is intended to only send "whole bytes", no bit-messing. ++ * ++ */ ++ ++#include ++#include ++#include "compr.h" ++ ++/* _compress returns the compressed size, -1 if bigger */ ++static int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen, void *model) ++{ ++ short positions[256]; ++ int outpos = 0; ++ int pos=0; ++ ++ memset(positions,0,sizeof(positions)); ++ ++ while (pos < (*sourcelen) && outpos <= (*dstlen)-2) { ++ int backpos, runlen=0; ++ unsigned char value; ++ ++ value = data_in[pos]; ++ ++ cpage_out[outpos++] = data_in[pos++]; ++ ++ backpos = positions[value]; ++ positions[value]=pos; ++ ++ while ((backpos < pos) && (pos < (*sourcelen)) && ++ (data_in[pos]==data_in[backpos++]) && (runlen<255)) { ++ pos++; ++ runlen++; ++ } ++ cpage_out[outpos++] = runlen; ++ } ++ ++ if (outpos >= pos) { ++ /* We failed */ ++ return -1; ++ } ++ ++ /* Tell the caller how much we managed to compress, and how much space it took */ ++ *sourcelen = pos; ++ *dstlen = outpos; ++ return 0; ++} ++ ++ ++static int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t srclen, uint32_t destlen, void *model) ++{ ++ short positions[256]; ++ int outpos = 0; ++ int pos=0; ++ ++ memset(positions,0,sizeof(positions)); ++ ++ while (outpos= outpos) { ++ while(repeat) { ++ cpage_out[outpos++] = cpage_out[backoffs++]; ++ repeat--; ++ } ++ } else { ++ memcpy(&cpage_out[outpos],&cpage_out[backoffs],repeat); ++ outpos+=repeat; ++ } ++ } ++ } ++ return 0; ++} ++ ++ ++static struct jffs2_compressor jffs2_rtime_comp = { ++ .priority = JFFS2_RTIME_PRIORITY, ++ .name = "rtime", ++ .disabled = 0, ++ .compr = JFFS2_COMPR_RTIME, ++ .compress = &jffs2_rtime_compress, ++ .decompress = &jffs2_rtime_decompress, ++}; ++ ++int jffs2_rtime_init(void) ++{ ++ return jffs2_register_compressor(&jffs2_rtime_comp); ++} ++ ++void jffs2_rtime_exit(void) ++{ ++ jffs2_unregister_compressor(&jffs2_rtime_comp); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr_zlib.c linux-2.6.24.7/drivers/mtd/mtd-utils/compr_zlib.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/compr_zlib.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/compr_zlib.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,145 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2001 Red Hat, Inc. ++ * ++ * Created by David Woodhouse ++ * ++ * The original JFFS, from which the design for JFFS2 was derived, ++ * was designed and implemented by Axis Communications AB. ++ * ++ * The contents of this file are subject to the Red Hat eCos Public ++ * License Version 1.1 (the "Licence"); you may not use this file ++ * except in compliance with the Licence. You may obtain a copy of ++ * the Licence at http://www.redhat.com/ ++ * ++ * Software distributed under the Licence is distributed on an "AS IS" ++ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. ++ * See the Licence for the specific language governing rights and ++ * limitations under the Licence. ++ * ++ * The Original Code is JFFS2 - Journalling Flash File System, version 2 ++ * ++ * Alternatively, the contents of this file may be used under the ++ * terms of the GNU General Public License version 2 (the "GPL"), in ++ * which case the provisions of the GPL are applicable instead of the ++ * above. If you wish to allow the use of your version of this file ++ * only under the terms of the GPL and not to allow others to use your ++ * version of this file under the RHEPL, indicate your decision by ++ * deleting the provisions above and replace them with the notice and ++ * other provisions required by the GPL. If you do not delete the ++ * provisions above, a recipient may use your version of this file ++ * under either the RHEPL or the GPL. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "compr.h" ++ ++#define min(x,y) ((x)<(y)?(x):(y)) ++ ++/* Plan: call deflate() with avail_in == *sourcelen, ++ avail_out = *dstlen - 12 and flush == Z_FINISH. ++ If it doesn't manage to finish, call it again with ++ avail_in == 0 and avail_out set to the remaining 12 ++ bytes for it to clean up. ++Q: Is 12 bytes sufficient? ++ */ ++#define STREAM_END_SPACE 12 ++ ++int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen, void *model) ++{ ++ z_stream strm; ++ int ret; ++ ++ if (*dstlen <= STREAM_END_SPACE) ++ return -1; ++ ++ strm.zalloc = (void *)0; ++ strm.zfree = (void *)0; ++ ++ if (Z_OK != deflateInit(&strm, 3)) { ++ return -1; ++ } ++ strm.next_in = data_in; ++ strm.total_in = 0; ++ ++ strm.next_out = cpage_out; ++ strm.total_out = 0; ++ ++ while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) { ++ strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE); ++ strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out); ++ ret = deflate(&strm, Z_PARTIAL_FLUSH); ++ if (ret != Z_OK) { ++ deflateEnd(&strm); ++ return -1; ++ } ++ } ++ strm.avail_out += STREAM_END_SPACE; ++ strm.avail_in = 0; ++ ret = deflate(&strm, Z_FINISH); ++ if (ret != Z_STREAM_END) { ++ deflateEnd(&strm); ++ return -1; ++ } ++ deflateEnd(&strm); ++ ++ if (strm.total_out >= strm.total_in) ++ return -1; ++ ++ ++ *dstlen = strm.total_out; ++ *sourcelen = strm.total_in; ++ return 0; ++} ++ ++int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t srclen, uint32_t destlen, void *model) ++{ ++ z_stream strm; ++ int ret; ++ ++ strm.zalloc = (void *)0; ++ strm.zfree = (void *)0; ++ ++ if (Z_OK != inflateInit(&strm)) { ++ return 1; ++ } ++ strm.next_in = data_in; ++ strm.avail_in = srclen; ++ strm.total_in = 0; ++ ++ strm.next_out = cpage_out; ++ strm.avail_out = destlen; ++ strm.total_out = 0; ++ ++ while((ret = inflate(&strm, Z_FINISH)) == Z_OK) ++ ; ++ ++ inflateEnd(&strm); ++ return 0; ++} ++ ++static struct jffs2_compressor jffs2_zlib_comp = { ++ .priority = JFFS2_ZLIB_PRIORITY, ++ .name = "zlib", ++ .disabled = 0, ++ .compr = JFFS2_COMPR_ZLIB, ++ .compress = &jffs2_zlib_compress, ++ .decompress = &jffs2_zlib_decompress, ++}; ++ ++int jffs2_zlib_init(void) ++{ ++ return jffs2_register_compressor(&jffs2_zlib_comp); ++} ++ ++void jffs2_zlib_exit(void) ++{ ++ jffs2_unregister_compressor(&jffs2_zlib_comp); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/crc32.c linux-2.6.24.7/drivers/mtd/mtd-utils/crc32.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/crc32.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/crc32.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,95 @@ ++/* ++ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or ++ * code or tables extracted from it, as desired without restriction. ++ * ++ * First, the polynomial itself and its table of feedback terms. The ++ * polynomial is ++ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 ++ * ++ * Note that we take it "backwards" and put the highest-order term in ++ * the lowest-order bit. The X^32 term is "implied"; the LSB is the ++ * X^31 term, etc. The X^0 term (usually shown as "+1") results in ++ * the MSB being 1 ++ * ++ * Note that the usual hardware shift register implementation, which ++ * is what we're using (we're merely optimizing it by doing eight-bit ++ * chunks at a time) shifts bits into the lowest-order term. In our ++ * implementation, that means shifting towards the right. Why do we ++ * do it this way? Because the calculated CRC must be transmitted in ++ * order from highest-order term to lowest-order term. UARTs transmit ++ * characters in order from LSB to MSB. By storing the CRC this way ++ * we hand it to the UART in the order low-byte to high-byte; the UART ++ * sends each low-bit to hight-bit; and the result is transmission bit ++ * by bit from highest- to lowest-order term without requiring any bit ++ * shuffling on our part. Reception works similarly ++ * ++ * The feedback terms table consists of 256, 32-bit entries. Notes ++ * ++ * The table can be generated at runtime if desired; code to do so ++ * is shown later. It might not be obvious, but the feedback ++ * terms simply represent the results of eight shift/xor opera ++ * tions for all combinations of data and CRC register values ++ * ++ * The values must be right-shifted by eight bits by the "updcrc ++ * logic; the shift must be unsigned (bring in zeroes). On some ++ * hardware you could probably optimize the shift in assembler by ++ * using byte-swap instructions ++ * polynomial $edb88320 ++ */ ++ ++#include ++ ++const uint32_t crc32_table[256] = { ++ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, ++ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, ++ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, ++ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, ++ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, ++ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, ++ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, ++ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, ++ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, ++ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, ++ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, ++ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, ++ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, ++ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, ++ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, ++ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, ++ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, ++ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, ++ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, ++ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, ++ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, ++ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, ++ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, ++ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, ++ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, ++ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, ++ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, ++ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, ++ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, ++ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, ++ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, ++ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, ++ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, ++ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, ++ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, ++ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, ++ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, ++ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, ++ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, ++ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, ++ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, ++ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, ++ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, ++ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, ++ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, ++ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, ++ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, ++ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, ++ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, ++ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, ++ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, ++ 0x2d02ef8dL ++}; +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/crc32.h linux-2.6.24.7/drivers/mtd/mtd-utils/crc32.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/crc32.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/crc32.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,19 @@ ++#ifndef CRC32_H ++#define CRC32_H ++ ++#include ++ ++extern const uint32_t crc32_table[256]; ++ ++/* Return a 32-bit CRC of the contents of the buffer. */ ++ ++ static inline uint32_t ++crc32(uint32_t val, const void *ss, int len) ++{ ++ const unsigned char *s = ss; ++ while (--len >= 0) ++ val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8); ++ return val; ++} ++ ++#endif +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/device_table.txt linux-2.6.24.7/drivers/mtd/mtd-utils/device_table.txt +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/device_table.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/device_table.txt 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,129 @@ ++# This is a sample device table file for use with mkfs.jffs2. You can ++# do all sorts of interesting things with a device table file. For ++# example, if you want to adjust the permissions on a particular file ++# you can just add an entry like: ++# /sbin/foobar f 2755 0 0 - - - - - ++# and (assuming the file /sbin/foobar exists) it will be made setuid ++# root (regardless of what its permissions are on the host filesystem. ++# ++# Device table entries take the form of: ++# ++# where name is the file name, type can be one of: ++# f A regular file ++# d Directory ++# c Character special device file ++# b Block special device file ++# p Fifo (named pipe) ++# uid is the user id for the target file, gid is the group id for the ++# target file. The rest of the entried apply only to device special ++# file. ++ ++# When building a target filesystem, it is desirable to not have to ++# become root and then run 'mknod' a thousand times. Using a device ++# table you can create device nodes and directories "on the fly". ++# Furthermore, you can use a single table entry to create a many device ++# minors. For example, if I wanted to create /dev/hda and /dev/hda[0-15] ++# I could just use the following two table entries: ++# /dev/hda b 640 0 0 3 0 0 0 - ++# /dev/hda b 640 0 0 3 1 1 1 15 ++# ++# Have fun ++# -Erik Andersen ++# ++ ++# ++/dev d 755 0 0 - - - - - ++/dev/mem c 640 0 0 1 1 0 0 - ++/dev/kmem c 640 0 0 1 2 0 0 - ++/dev/null c 640 0 0 1 3 0 0 - ++/dev/zero c 640 0 0 1 5 0 0 - ++/dev/random c 640 0 0 1 8 0 0 - ++/dev/urandom c 640 0 0 1 9 0 0 - ++/dev/tty c 666 0 0 5 0 0 0 - ++/dev/tty c 666 0 0 4 0 0 1 6 ++/dev/console c 640 0 0 5 1 0 0 - ++/dev/ram b 640 0 0 1 1 0 0 - ++/dev/ram b 640 0 0 1 0 0 1 4 ++/dev/loop b 640 0 0 7 0 0 1 2 ++/dev/ptmx c 666 0 0 5 2 0 0 - ++#/dev/ttyS c 640 0 0 4 64 0 1 4 ++#/dev/psaux c 640 0 0 10 1 0 0 - ++#/dev/rtc c 640 0 0 10 135 0 0 - ++ ++# Adjust permissions on some normal files ++#/etc/shadow f 600 0 0 - - - - - ++#/bin/tinylogin f 4755 0 0 - - - - - ++ ++# User-mode Linux stuff ++/dev/ubda b 640 0 0 98 0 0 0 - ++/dev/ubda b 640 0 0 98 1 1 1 15 ++ ++# IDE Devices ++/dev/hda b 640 0 0 3 0 0 0 - ++/dev/hda b 640 0 0 3 1 1 1 15 ++/dev/hdb b 640 0 0 3 64 0 0 - ++/dev/hdb b 640 0 0 3 65 1 1 15 ++#/dev/hdc b 640 0 0 22 0 0 0 - ++#/dev/hdc b 640 0 0 22 1 1 1 15 ++#/dev/hdd b 640 0 0 22 64 0 0 - ++#/dev/hdd b 640 0 0 22 65 1 1 15 ++#/dev/hde b 640 0 0 33 0 0 0 - ++#/dev/hde b 640 0 0 33 1 1 1 15 ++#/dev/hdf b 640 0 0 33 64 0 0 - ++#/dev/hdf b 640 0 0 33 65 1 1 15 ++#/dev/hdg b 640 0 0 34 0 0 0 - ++#/dev/hdg b 640 0 0 34 1 1 1 15 ++#/dev/hdh b 640 0 0 34 64 0 0 - ++#/dev/hdh b 640 0 0 34 65 1 1 15 ++ ++# SCSI Devices ++#/dev/sda b 640 0 0 8 0 0 0 - ++#/dev/sda b 640 0 0 8 1 1 1 15 ++#/dev/sdb b 640 0 0 8 16 0 0 - ++#/dev/sdb b 640 0 0 8 17 1 1 15 ++#/dev/sdc b 640 0 0 8 32 0 0 - ++#/dev/sdc b 640 0 0 8 33 1 1 15 ++#/dev/sdd b 640 0 0 8 48 0 0 - ++#/dev/sdd b 640 0 0 8 49 1 1 15 ++#/dev/sde b 640 0 0 8 64 0 0 - ++#/dev/sde b 640 0 0 8 65 1 1 15 ++#/dev/sdf b 640 0 0 8 80 0 0 - ++#/dev/sdf b 640 0 0 8 81 1 1 15 ++#/dev/sdg b 640 0 0 8 96 0 0 - ++#/dev/sdg b 640 0 0 8 97 1 1 15 ++#/dev/sdh b 640 0 0 8 112 0 0 - ++#/dev/sdh b 640 0 0 8 113 1 1 15 ++#/dev/sg c 640 0 0 21 0 0 1 15 ++#/dev/scd b 640 0 0 11 0 0 1 15 ++#/dev/st c 640 0 0 9 0 0 1 8 ++#/dev/nst c 640 0 0 9 128 0 1 8 ++#/dev/st c 640 0 0 9 32 1 1 4 ++#/dev/st c 640 0 0 9 64 1 1 4 ++#/dev/st c 640 0 0 9 96 1 1 4 ++ ++# Floppy disk devices ++#/dev/fd b 640 0 0 2 0 0 1 2 ++#/dev/fd0d360 b 640 0 0 2 4 0 0 - ++#/dev/fd1d360 b 640 0 0 2 5 0 0 - ++#/dev/fd0h1200 b 640 0 0 2 8 0 0 - ++#/dev/fd1h1200 b 640 0 0 2 9 0 0 - ++#/dev/fd0u1440 b 640 0 0 2 28 0 0 - ++#/dev/fd1u1440 b 640 0 0 2 29 0 0 - ++#/dev/fd0u2880 b 640 0 0 2 32 0 0 - ++#/dev/fd1u2880 b 640 0 0 2 33 0 0 - ++ ++# All the proprietary cdrom devices in the world ++#/dev/aztcd b 640 0 0 29 0 0 0 - ++#/dev/bpcd b 640 0 0 41 0 0 0 - ++#/dev/capi20 c 640 0 0 68 0 0 1 2 ++#/dev/cdu31a b 640 0 0 15 0 0 0 - ++#/dev/cdu535 b 640 0 0 24 0 0 0 - ++#/dev/cm206cd b 640 0 0 32 0 0 0 - ++#/dev/sjcd b 640 0 0 18 0 0 0 - ++#/dev/sonycd b 640 0 0 15 0 0 0 - ++#/dev/gscd b 640 0 0 16 0 0 0 - ++#/dev/sbpcd b 640 0 0 25 0 0 0 - ++#/dev/sbpcd b 640 0 0 25 0 0 1 4 ++#/dev/mcd b 640 0 0 23 0 0 0 - ++#/dev/optcd b 640 0 0 17 0 0 0 - ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/doc_loadbios.c linux-2.6.24.7/drivers/mtd/mtd-utils/doc_loadbios.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/doc_loadbios.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/doc_loadbios.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,148 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++unsigned char databuf[512]; ++ ++int main(int argc,char **argv) ++{ ++ mtd_info_t meminfo; ++ int ifd,ofd; ++ struct stat statbuf; ++ erase_info_t erase; ++ unsigned long retlen, ofs, iplsize, ipltailsize; ++ unsigned char *iplbuf; ++ iplbuf = NULL; ++ ++ if (argc < 3) { ++ fprintf(stderr,"You must specify a device," ++ " the source firmware file and the offset\n"); ++ return 1; ++ } ++ ++ // Open and size the device ++ if ((ofd = open(argv[1],O_RDWR)) < 0) { ++ perror("Open flash device"); ++ return 1; ++ } ++ ++ if ((ifd = open(argv[2], O_RDONLY)) < 0) { ++ perror("Open firmware file\n"); ++ close(ofd); ++ return 1; ++ } ++ ++ if (fstat(ifd, &statbuf) != 0) { ++ perror("Stat firmware file"); ++ goto error; ++ } ++ ++#if 0 ++ if (statbuf.st_size > 65536) { ++ printf("Firmware too large (%ld bytes)\n",statbuf.st_size); ++ goto error; ++ } ++#endif ++ ++ if (ioctl(ofd,MEMGETINFO,&meminfo) != 0) { ++ perror("ioctl(MEMGETINFO)"); ++ goto error; ++ } ++ ++ iplsize = (ipltailsize = 0); ++ if (argc >= 4) { ++ /* DoC Millennium has IPL in the first 1K of flash memory */ ++ /* You may want to specify the offset 1024 to store ++ the firmware next to IPL. */ ++ iplsize = strtoul(argv[3], NULL, 0); ++ ipltailsize = iplsize % meminfo.erasesize; ++ } ++ ++ if (lseek(ofd, iplsize - ipltailsize, SEEK_SET) < 0) { ++ perror("lseek"); ++ goto error; ++ } ++ ++ if (ipltailsize) { ++ iplbuf = malloc(ipltailsize); ++ if (iplbuf == NULL) { ++ fprintf(stderr, "Not enough memory for IPL tail buffer of" ++ " %lu bytes\n", (unsigned long) ipltailsize); ++ goto error; ++ } ++ printf("Reading IPL%s area of length %lu at offset %lu\n", ++ (iplsize - ipltailsize) ? " tail" : "", ++ (long unsigned) ipltailsize, ++ (long unsigned) (iplsize - ipltailsize)); ++ if (read(ofd, iplbuf, ipltailsize) != ipltailsize) { ++ perror("read"); ++ goto error; ++ } ++ } ++ ++ erase.length = meminfo.erasesize; ++ ++ for (ofs = iplsize - ipltailsize ; ++ ofs < iplsize + statbuf.st_size ; ++ ofs += meminfo.erasesize) { ++ erase.start = ofs; ++ printf("Performing Flash Erase of length %lu at offset %lu\n", ++ (long unsigned) erase.length, (long unsigned) erase.start); ++ ++ if (ioctl(ofd,MEMERASE,&erase) != 0) { ++ perror("ioctl(MEMERASE)"); ++ goto error; ++ } ++ } ++ ++ if (lseek(ofd, iplsize - ipltailsize, SEEK_SET) < 0) { ++ perror("lseek"); ++ goto error; ++ } ++ ++ if (ipltailsize) { ++ printf("Writing IPL%s area of length %lu at offset %lu\n", ++ (iplsize - ipltailsize) ? " tail" : "", ++ (long unsigned) ipltailsize, ++ (long unsigned) (iplsize - ipltailsize)); ++ if (write(ofd, iplbuf, ipltailsize) != ipltailsize) { ++ perror("write"); ++ goto error; ++ } ++ } ++ ++ printf("Writing the firmware of length %lu at %lu... ", ++ (unsigned long) statbuf.st_size, ++ (unsigned long) iplsize); ++ do { ++ retlen = read(ifd, databuf, 512); ++ if (retlen < 512) ++ memset(databuf+retlen, 0xff, 512-retlen); ++ if (write(ofd, databuf, 512) != 512) { ++ perror("write"); ++ goto error; ++ } ++ } while (retlen == 512); ++ printf("Done.\n"); ++ ++ if (iplbuf != NULL) ++ free(iplbuf); ++ close(ifd); ++ close(ofd); ++ return 0; ++ ++error: ++ if (iplbuf != NULL) ++ free(iplbuf); ++ close(ifd); ++ close(ofd); ++ return 1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/docfdisk.c linux-2.6.24.7/drivers/mtd/mtd-utils/docfdisk.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/docfdisk.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/docfdisk.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,317 @@ ++/* ++ * docfdisk.c: Modify INFTL partition tables ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#define _XOPEN_SOURCE 500 /* for pread/pwrite */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++unsigned char *buf; ++ ++mtd_info_t meminfo; ++erase_info_t erase; ++int fd; ++struct INFTLMediaHeader *mh; ++ ++#define MAXSCAN 10 ++ ++void show_header(int mhoffs) { ++ int i, unitsize, numunits, bmbits, numpart; ++ int start, end, num, nextunit; ++ unsigned int flags; ++ struct INFTLPartition *ip; ++ ++ bmbits = le32_to_cpu(mh->BlockMultiplierBits); ++ printf(" bootRecordID = %s\n" ++ " NoOfBootImageBlocks = %d\n" ++ " NoOfBinaryPartitions = %d\n" ++ " NoOfBDTLPartitions = %d\n" ++ " BlockMultiplierBits = %d\n" ++ " FormatFlags = %d\n" ++ " OsakVersion = %d.%d.%d.%d\n" ++ " PercentUsed = %d\n", ++ mh->bootRecordID, le32_to_cpu(mh->NoOfBootImageBlocks), ++ le32_to_cpu(mh->NoOfBinaryPartitions), ++ le32_to_cpu(mh->NoOfBDTLPartitions), ++ bmbits, ++ le32_to_cpu(mh->FormatFlags), ++ ((unsigned char *) &mh->OsakVersion)[0] & 0xf, ++ ((unsigned char *) &mh->OsakVersion)[1] & 0xf, ++ ((unsigned char *) &mh->OsakVersion)[2] & 0xf, ++ ((unsigned char *) &mh->OsakVersion)[3] & 0xf, ++ le32_to_cpu(mh->PercentUsed)); ++ ++ numpart = le32_to_cpu(mh->NoOfBinaryPartitions) + ++ le32_to_cpu(mh->NoOfBDTLPartitions); ++ unitsize = meminfo.erasesize >> bmbits; ++ numunits = meminfo.size / unitsize; ++ nextunit = mhoffs / unitsize; ++ nextunit++; ++ printf("Unitsize is %d bytes. Device has %d units.\n", ++ unitsize, numunits); ++ if (numunits > 32768) { ++ printf("WARNING: More than 32768 units! Unexpectedly small BlockMultiplierBits.\n"); ++ } ++ if (bmbits && (numunits <= 16384)) { ++ printf("NOTICE: Unexpectedly large BlockMultiplierBits.\n"); ++ } ++ for (i = 0; i < 4; i++) { ++ ip = &(mh->Partitions[i]); ++ flags = le32_to_cpu(ip->flags); ++ start = le32_to_cpu(ip->firstUnit); ++ end = le32_to_cpu(ip->lastUnit); ++ num = le32_to_cpu(ip->virtualUnits); ++ if (start < nextunit) { ++ printf("ERROR: Overlapping or misordered partitions!\n"); ++ } ++ if (start > nextunit) { ++ printf(" Unpartitioned space: %d bytes\n" ++ " virtualUnits = %d\n" ++ " firstUnit = %d\n" ++ " lastUnit = %d\n", ++ (start - nextunit) * unitsize, start - nextunit, ++ nextunit, start - 1); ++ } ++ if (flags & INFTL_BINARY) ++ printf(" Partition %d (BDK):", i+1); ++ else ++ printf(" Partition %d (BDTL):", i+1); ++ printf(" %d bytes\n" ++ " virtualUnits = %d\n" ++ " firstUnit = %d\n" ++ " lastUnit = %d\n" ++ " flags = 0x%x\n" ++ " spareUnits = %d\n", ++ num * unitsize, num, start, end, ++ le32_to_cpu(ip->flags), le32_to_cpu(ip->spareUnits)); ++ if (num > (1 + end - start)) { ++ printf("ERROR: virtualUnits not consistent with first/lastUnit!\n"); ++ } ++ end++; ++ if (end > nextunit) ++ nextunit = end; ++ if (flags & INFTL_LAST) ++ break; ++ } ++ if (i >= 4) { ++ printf("Odd. Last partition was not marked with INFTL_LAST.\n"); ++ i--; ++ } ++ if ((i+1) != numpart) { ++ printf("ERROR: Number of partitions != (NoOfBinaryPartitions + NoOfBDTLPartitions)\n"); ++ } ++ if (nextunit > numunits) { ++ printf("ERROR: Partitions appear to extend beyond end of device!\n"); ++ } ++ if (nextunit < numunits) { ++ printf(" Unpartitioned space: %d bytes\n" ++ " virtualUnits = %d\n" ++ " firstUnit = %d\n" ++ " lastUnit = %d\n", ++ (numunits - nextunit) * unitsize, numunits - nextunit, ++ nextunit, numunits - 1); ++ } ++} ++ ++ ++int main(int argc, char **argv) ++{ ++ int ret, i, mhblock, unitsize, block; ++ unsigned int nblocks[4], npart; ++ unsigned int totblocks; ++ struct INFTLPartition *ip; ++ unsigned char *oobbuf; ++ struct mtd_oob_buf oob; ++ char line[20]; ++ int mhoffs; ++ struct INFTLMediaHeader *mh2; ++ ++ if (argc < 2) { ++ printf( ++ "Usage: %s [ [ [ [ 4) { ++ printf("Max 4 partitions allowed.\n"); ++ return 1; ++ } ++ ++ for (i = 0; i < npart; i++) { ++ nblocks[i] = strtoul(argv[2+i], NULL, 0); ++ if (i && !nblocks[i-1]) { ++ printf("No sizes allowed after 0\n"); ++ return 1; ++ } ++ } ++ ++ // Open and size the device ++ if ((fd = open(argv[1], O_RDWR)) < 0) { ++ perror("Open flash device"); ++ return 1; ++ } ++ ++ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { ++ perror("ioctl(MEMGETINFO)"); ++ return 1; ++ } ++ ++ printf("Device size is %d bytes. Erasesize is %d bytes.\n", ++ meminfo.size, meminfo.erasesize); ++ ++ buf = malloc(meminfo.erasesize); ++ oobbuf = malloc((meminfo.erasesize / meminfo.writesize) * meminfo.oobsize); ++ if (!buf || !oobbuf) { ++ printf("Can't malloc block buffer\n"); ++ return 1; ++ } ++ oob.length = meminfo.oobsize; ++ ++ mh = (struct INFTLMediaHeader *) buf; ++ ++ for (mhblock = 0; mhblock < MAXSCAN; mhblock++) { ++ if ((ret = pread(fd, buf, meminfo.erasesize, mhblock * meminfo.erasesize)) < 0) { ++ if (errno == EBADMSG) { ++ printf("ECC error at eraseblock %d\n", mhblock); ++ continue; ++ } ++ perror("Read eraseblock"); ++ return 1; ++ } ++ if (ret != meminfo.erasesize) { ++ printf("Short read!\n"); ++ return 1; ++ } ++ if (!strcmp("BNAND", mh->bootRecordID)) break; ++ } ++ if (mhblock >= MAXSCAN) { ++ printf("Unable to find INFTL Media Header\n"); ++ return 1; ++ } ++ printf("Found INFTL Media Header at block %d:\n", mhblock); ++ mhoffs = mhblock * meminfo.erasesize; ++ ++ oob.ptr = oobbuf; ++ oob.start = mhoffs; ++ for (i = 0; i < meminfo.erasesize; i += meminfo.writesize) { ++ if (ioctl(fd, MEMREADOOB, &oob)) { ++ perror("ioctl(MEMREADOOB)"); ++ return 1; ++ } ++ oob.start += meminfo.writesize; ++ oob.ptr += meminfo.oobsize; ++ } ++ ++ show_header(mhoffs); ++ ++ if (!npart) ++ return 0; ++ ++ printf("\n" ++ "-------------------------------------------------------------------------\n"); ++ ++ unitsize = meminfo.erasesize >> le32_to_cpu(mh->BlockMultiplierBits); ++ totblocks = meminfo.size / unitsize; ++ block = mhoffs / unitsize; ++ block++; ++ ++ mh->NoOfBDTLPartitions = 0; ++ mh->NoOfBinaryPartitions = npart; ++ ++ for (i = 0; i < npart; i++) { ++ ip = &(mh->Partitions[i]); ++ ip->firstUnit = cpu_to_le32(block); ++ if (!nblocks[i]) ++ nblocks[i] = totblocks - block; ++ ip->virtualUnits = cpu_to_le32(nblocks[i]); ++ block += nblocks[i]; ++ ip->lastUnit = cpu_to_le32(block-1); ++ ip->spareUnits = 0; ++ ip->flags = cpu_to_le32(INFTL_BINARY); ++ } ++ if (block > totblocks) { ++ printf("Requested partitions extend beyond end of device.\n"); ++ return 1; ++ } ++ ip->flags = cpu_to_le32(INFTL_BINARY | INFTL_LAST); ++ ++ /* update the spare as well */ ++ mh2 = (struct INFTLMediaHeader *) (buf + 4096); ++ memcpy((void *) mh2, (void *) mh, sizeof(struct INFTLMediaHeader)); ++ ++ printf("\nProposed new Media Header:\n"); ++ show_header(mhoffs); ++ ++ printf("\nReady to update device. Type 'yes' to proceed, anything else to abort: "); ++ fgets(line, sizeof(line), stdin); ++ if (strcmp("yes\n", line)) ++ return 0; ++ printf("Updating MediaHeader...\n"); ++ ++ erase.start = mhoffs; ++ erase.length = meminfo.erasesize; ++ if (ioctl(fd, MEMERASE, &erase)) { ++ perror("ioctl(MEMERASE)"); ++ printf("Your MediaHeader may be hosed. UHOH!\n"); ++ return 1; ++ } ++ ++ oob.ptr = oobbuf; ++ oob.start = mhoffs; ++ for (i = 0; i < meminfo.erasesize; i += meminfo.writesize) { ++ memset(oob.ptr, 0xff, 6); // clear ECC. ++ if (ioctl(fd, MEMWRITEOOB, &oob)) { ++ perror("ioctl(MEMWRITEOOB)"); ++ printf("Your MediaHeader may be hosed. UHOH!\n"); ++ return 1; ++ } ++ if ((ret = pwrite(fd, buf, meminfo.writesize, oob.start)) < 0) { ++ perror("Write page"); ++ printf("Your MediaHeader may be hosed. UHOH!\n"); ++ return 1; ++ } ++ if (ret != meminfo.writesize) { ++ printf("Short write!\n"); ++ printf("Your MediaHeader may be hosed. UHOH!\n"); ++ return 1; ++ } ++ ++ oob.start += meminfo.writesize; ++ oob.ptr += meminfo.oobsize; ++ buf += meminfo.writesize; ++ } ++ ++ printf("Success. REBOOT or unload the diskonchip module to update partitions!\n"); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/feature-removal-schedule.txt linux-2.6.24.7/drivers/mtd/mtd-utils/feature-removal-schedule.txt +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/feature-removal-schedule.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/feature-removal-schedule.txt 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,9 @@ ++The following is a list of files and features that are going to be ++removed in the mtd-utils source tree. Every entry should contain what ++exactly is going away, why it is happening, and who is going to be doing ++the work. When the feature is removed from the utils, it should also ++be removed from this file. ++ ++--------------------------- ++ ++--------------------------- +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/fec.c linux-2.6.24.7/drivers/mtd/mtd-utils/fec.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/fec.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/fec.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,917 @@ ++/* ++ * fec.c -- forward error correction based on Vandermonde matrices ++ * 980624 ++ * (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it) ++ * ++ * Portions derived from code by Phil Karn (karn@ka9q.ampr.org), ++ * Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari ++ * Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995 ++ * ++ * 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. ++ * 2. Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following ++ * disclaimer in the documentation and/or other materials ++ * provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A ++ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS ++ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++/* ++ * The following parameter defines how many bits are used for ++ * field elements. The code supports any value from 2 to 16 ++ * but fastest operation is achieved with 8 bit elements ++ * This is the only parameter you may want to change. ++ */ ++#ifndef GF_BITS ++#define GF_BITS 8 /* code over GF(2**GF_BITS) - change to suit */ ++#endif ++ ++#include ++#include ++#include ++ ++/* ++ * compatibility stuff ++ */ ++#ifdef MSDOS /* but also for others, e.g. sun... */ ++#define NEED_BCOPY ++#define bcmp(a,b,n) memcmp(a,b,n) ++#endif ++ ++#ifdef NEED_BCOPY ++#define bcopy(s, d, siz) memcpy((d), (s), (siz)) ++#define bzero(d, siz) memset((d), '\0', (siz)) ++#endif ++ ++/* ++ * stuff used for testing purposes only ++ */ ++ ++#ifdef TEST ++#define DEB(x) ++#define DDB(x) x ++#define DEBUG 0 /* minimal debugging */ ++#ifdef MSDOS ++#include ++struct timeval { ++ unsigned long ticks; ++}; ++#define gettimeofday(x, dummy) { (x)->ticks = clock() ; } ++#define DIFF_T(a,b) (1+ 1000000*(a.ticks - b.ticks) / CLOCKS_PER_SEC ) ++typedef unsigned long u_long ; ++typedef unsigned short u_short ; ++#else /* typically, unix systems */ ++#include ++#define DIFF_T(a,b) \ ++ (1+ 1000000*(a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec) ) ++#endif ++ ++#define TICK(t) \ ++ {struct timeval x ; \ ++ gettimeofday(&x, NULL) ; \ ++ t = x.tv_usec + 1000000* (x.tv_sec & 0xff ) ; \ ++ } ++#define TOCK(t) \ ++ { u_long t1 ; TICK(t1) ; \ ++ if (t1 < t) t = 256000000 + t1 - t ; \ ++ else t = t1 - t ; \ ++ if (t == 0) t = 1 ;} ++ ++u_long ticks[10]; /* vars for timekeeping */ ++#else ++#define DEB(x) ++#define DDB(x) ++#define TICK(x) ++#define TOCK(x) ++#endif /* TEST */ ++ ++/* ++ * You should not need to change anything beyond this point. ++ * The first part of the file implements linear algebra in GF. ++ * ++ * gf is the type used to store an element of the Galois Field. ++ * Must constain at least GF_BITS bits. ++ * ++ * Note: unsigned char will work up to GF(256) but int seems to run ++ * faster on the Pentium. We use int whenever have to deal with an ++ * index, since they are generally faster. ++ */ ++#if (GF_BITS < 2 && GF_BITS >16) ++#error "GF_BITS must be 2 .. 16" ++#endif ++#if (GF_BITS <= 8) ++typedef unsigned char gf; ++#else ++typedef unsigned short gf; ++#endif ++ ++#define GF_SIZE ((1 << GF_BITS) - 1) /* powers of \alpha */ ++ ++/* ++ * Primitive polynomials - see Lin & Costello, Appendix A, ++ * and Lee & Messerschmitt, p. 453. ++ */ ++static char *allPp[] = { /* GF_BITS polynomial */ ++ NULL, /* 0 no code */ ++ NULL, /* 1 no code */ ++ "111", /* 2 1+x+x^2 */ ++ "1101", /* 3 1+x+x^3 */ ++ "11001", /* 4 1+x+x^4 */ ++ "101001", /* 5 1+x^2+x^5 */ ++ "1100001", /* 6 1+x+x^6 */ ++ "10010001", /* 7 1 + x^3 + x^7 */ ++ "101110001", /* 8 1+x^2+x^3+x^4+x^8 */ ++ "1000100001", /* 9 1+x^4+x^9 */ ++ "10010000001", /* 10 1+x^3+x^10 */ ++ "101000000001", /* 11 1+x^2+x^11 */ ++ "1100101000001", /* 12 1+x+x^4+x^6+x^12 */ ++ "11011000000001", /* 13 1+x+x^3+x^4+x^13 */ ++ "110000100010001", /* 14 1+x+x^6+x^10+x^14 */ ++ "1100000000000001", /* 15 1+x+x^15 */ ++ "11010000000010001" /* 16 1+x+x^3+x^12+x^16 */ ++}; ++ ++ ++/* ++ * To speed up computations, we have tables for logarithm, exponent ++ * and inverse of a number. If GF_BITS <= 8, we use a table for ++ * multiplication as well (it takes 64K, no big deal even on a PDA, ++ * especially because it can be pre-initialized an put into a ROM!), ++ * otherwhise we use a table of logarithms. ++ * In any case the macro gf_mul(x,y) takes care of multiplications. ++ */ ++ ++static gf gf_exp[2*GF_SIZE]; /* index->poly form conversion table */ ++static int gf_log[GF_SIZE + 1]; /* Poly->index form conversion table */ ++static gf inverse[GF_SIZE+1]; /* inverse of field elem. */ ++ /* inv[\alpha**i]=\alpha**(GF_SIZE-i-1) */ ++ ++/* ++ * modnn(x) computes x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1, ++ * without a slow divide. ++ */ ++static inline gf ++modnn(int x) ++{ ++ while (x >= GF_SIZE) { ++ x -= GF_SIZE; ++ x = (x >> GF_BITS) + (x & GF_SIZE); ++ } ++ return x; ++} ++ ++#define SWAP(a,b,t) {t tmp; tmp=a; a=b; b=tmp;} ++ ++/* ++ * gf_mul(x,y) multiplies two numbers. If GF_BITS<=8, it is much ++ * faster to use a multiplication table. ++ * ++ * USE_GF_MULC, GF_MULC0(c) and GF_ADDMULC(x) can be used when multiplying ++ * many numbers by the same constant. In this case the first ++ * call sets the constant, and others perform the multiplications. ++ * A value related to the multiplication is held in a local variable ++ * declared with USE_GF_MULC . See usage in addmul1(). ++ */ ++#if (GF_BITS <= 8) ++static gf gf_mul_table[GF_SIZE + 1][GF_SIZE + 1]; ++ ++#define gf_mul(x,y) gf_mul_table[x][y] ++ ++#define USE_GF_MULC register gf * __gf_mulc_ ++#define GF_MULC0(c) __gf_mulc_ = gf_mul_table[c] ++#define GF_ADDMULC(dst, x) dst ^= __gf_mulc_[x] ++ ++static void ++init_mul_table() ++{ ++ int i, j; ++ for (i=0; i< GF_SIZE+1; i++) ++ for (j=0; j< GF_SIZE+1; j++) ++ gf_mul_table[i][j] = gf_exp[modnn(gf_log[i] + gf_log[j]) ] ; ++ ++ for (j=0; j< GF_SIZE+1; j++) ++ gf_mul_table[0][j] = gf_mul_table[j][0] = 0; ++} ++#else /* GF_BITS > 8 */ ++static inline gf ++gf_mul(x,y) ++{ ++ if ( (x) == 0 || (y)==0 ) return 0; ++ ++ return gf_exp[gf_log[x] + gf_log[y] ] ; ++} ++#define init_mul_table() ++ ++#define USE_GF_MULC register gf * __gf_mulc_ ++#define GF_MULC0(c) __gf_mulc_ = &gf_exp[ gf_log[c] ] ++#define GF_ADDMULC(dst, x) { if (x) dst ^= __gf_mulc_[ gf_log[x] ] ; } ++#endif ++ ++/* ++ * Generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m] ++ * Lookup tables: ++ * index->polynomial form gf_exp[] contains j= \alpha^i; ++ * polynomial form -> index form gf_log[ j = \alpha^i ] = i ++ * \alpha=x is the primitive element of GF(2^m) ++ * ++ * For efficiency, gf_exp[] has size 2*GF_SIZE, so that a simple ++ * multiplication of two numbers can be resolved without calling modnn ++ */ ++ ++/* ++ * i use malloc so many times, it is easier to put checks all in ++ * one place. ++ */ ++static void * ++my_malloc(int sz, char *err_string) ++{ ++ void *p = malloc( sz ); ++ if (p == NULL) { ++ fprintf(stderr, "-- malloc failure allocating %s\n", err_string); ++ exit(1) ; ++ } ++ return p ; ++} ++ ++#define NEW_GF_MATRIX(rows, cols) \ ++ (gf *)my_malloc(rows * cols * sizeof(gf), " ## __LINE__ ## " ) ++ ++/* ++ * initialize the data structures used for computations in GF. ++ */ ++static void ++generate_gf(void) ++{ ++ int i; ++ gf mask; ++ char *Pp = allPp[GF_BITS] ; ++ ++ mask = 1; /* x ** 0 = 1 */ ++ gf_exp[GF_BITS] = 0; /* will be updated at the end of the 1st loop */ ++ /* ++ * first, generate the (polynomial representation of) powers of \alpha, ++ * which are stored in gf_exp[i] = \alpha ** i . ++ * At the same time build gf_log[gf_exp[i]] = i . ++ * The first GF_BITS powers are simply bits shifted to the left. ++ */ ++ for (i = 0; i < GF_BITS; i++, mask <<= 1 ) { ++ gf_exp[i] = mask; ++ gf_log[gf_exp[i]] = i; ++ /* ++ * If Pp[i] == 1 then \alpha ** i occurs in poly-repr ++ * gf_exp[GF_BITS] = \alpha ** GF_BITS ++ */ ++ if ( Pp[i] == '1' ) ++ gf_exp[GF_BITS] ^= mask; ++ } ++ /* ++ * now gf_exp[GF_BITS] = \alpha ** GF_BITS is complete, so can als ++ * compute its inverse. ++ */ ++ gf_log[gf_exp[GF_BITS]] = GF_BITS; ++ /* ++ * Poly-repr of \alpha ** (i+1) is given by poly-repr of ++ * \alpha ** i shifted left one-bit and accounting for any ++ * \alpha ** GF_BITS term that may occur when poly-repr of ++ * \alpha ** i is shifted. ++ */ ++ mask = 1 << (GF_BITS - 1 ) ; ++ for (i = GF_BITS + 1; i < GF_SIZE; i++) { ++ if (gf_exp[i - 1] >= mask) ++ gf_exp[i] = gf_exp[GF_BITS] ^ ((gf_exp[i - 1] ^ mask) << 1); ++ else ++ gf_exp[i] = gf_exp[i - 1] << 1; ++ gf_log[gf_exp[i]] = i; ++ } ++ /* ++ * log(0) is not defined, so use a special value ++ */ ++ gf_log[0] = GF_SIZE ; ++ /* set the extended gf_exp values for fast multiply */ ++ for (i = 0 ; i < GF_SIZE ; i++) ++ gf_exp[i + GF_SIZE] = gf_exp[i] ; ++ ++ /* ++ * again special cases. 0 has no inverse. This used to ++ * be initialized to GF_SIZE, but it should make no difference ++ * since noone is supposed to read from here. ++ */ ++ inverse[0] = 0 ; ++ inverse[1] = 1; ++ for (i=2; i<=GF_SIZE; i++) ++ inverse[i] = gf_exp[GF_SIZE-gf_log[i]]; ++} ++ ++/* ++ * Various linear algebra operations that i use often. ++ */ ++ ++/* ++ * addmul() computes dst[] = dst[] + c * src[] ++ * This is used often, so better optimize it! Currently the loop is ++ * unrolled 16 times, a good value for 486 and pentium-class machines. ++ * The case c=0 is also optimized, whereas c=1 is not. These ++ * calls are unfrequent in my typical apps so I did not bother. ++ * ++ * Note that gcc on ++ */ ++#define addmul(dst, src, c, sz) \ ++ if (c != 0) addmul1(dst, src, c, sz) ++ ++#define UNROLL 16 /* 1, 4, 8, 16 */ ++static void ++addmul1(gf *dst1, gf *src1, gf c, int sz) ++{ ++ USE_GF_MULC ; ++ register gf *dst = dst1, *src = src1 ; ++ gf *lim = &dst[sz - UNROLL + 1] ; ++ ++ GF_MULC0(c) ; ++ ++#if (UNROLL > 1) /* unrolling by 8/16 is quite effective on the pentium */ ++ for (; dst < lim ; dst += UNROLL, src += UNROLL ) { ++ GF_ADDMULC( dst[0] , src[0] ); ++ GF_ADDMULC( dst[1] , src[1] ); ++ GF_ADDMULC( dst[2] , src[2] ); ++ GF_ADDMULC( dst[3] , src[3] ); ++#if (UNROLL > 4) ++ GF_ADDMULC( dst[4] , src[4] ); ++ GF_ADDMULC( dst[5] , src[5] ); ++ GF_ADDMULC( dst[6] , src[6] ); ++ GF_ADDMULC( dst[7] , src[7] ); ++#endif ++#if (UNROLL > 8) ++ GF_ADDMULC( dst[8] , src[8] ); ++ GF_ADDMULC( dst[9] , src[9] ); ++ GF_ADDMULC( dst[10] , src[10] ); ++ GF_ADDMULC( dst[11] , src[11] ); ++ GF_ADDMULC( dst[12] , src[12] ); ++ GF_ADDMULC( dst[13] , src[13] ); ++ GF_ADDMULC( dst[14] , src[14] ); ++ GF_ADDMULC( dst[15] , src[15] ); ++#endif ++ } ++#endif ++ lim += UNROLL - 1 ; ++ for (; dst < lim; dst++, src++ ) /* final components */ ++ GF_ADDMULC( *dst , *src ); ++} ++ ++/* ++ * computes C = AB where A is n*k, B is k*m, C is n*m ++ */ ++static void ++matmul(gf *a, gf *b, gf *c, int n, int k, int m) ++{ ++ int row, col, i ; ++ ++ for (row = 0; row < n ; row++) { ++ for (col = 0; col < m ; col++) { ++ gf *pa = &a[ row * k ]; ++ gf *pb = &b[ col ]; ++ gf acc = 0 ; ++ for (i = 0; i < k ; i++, pa++, pb += m ) ++ acc ^= gf_mul( *pa, *pb ) ; ++ c[ row * m + col ] = acc ; ++ } ++ } ++} ++ ++#ifdef DEBUG ++/* ++ * returns 1 if the square matrix is identiy ++ * (only for test) ++ */ ++static int ++is_identity(gf *m, int k) ++{ ++ int row, col ; ++ for (row=0; row 1) { ++ fprintf(stderr, "singular matrix\n"); ++ goto fail ; ++ } ++ } ++ } ++ } ++ if (icol == -1) { ++ fprintf(stderr, "XXX pivot not found!\n"); ++ goto fail ; ++ } ++found_piv: ++ ++(ipiv[icol]) ; ++ /* ++ * swap rows irow and icol, so afterwards the diagonal ++ * element will be correct. Rarely done, not worth ++ * optimizing. ++ */ ++ if (irow != icol) { ++ for (ix = 0 ; ix < k ; ix++ ) { ++ SWAP( src[irow*k + ix], src[icol*k + ix], gf) ; ++ } ++ } ++ indxr[col] = irow ; ++ indxc[col] = icol ; ++ pivot_row = &src[icol*k] ; ++ c = pivot_row[icol] ; ++ if (c == 0) { ++ fprintf(stderr, "singular matrix 2\n"); ++ goto fail ; ++ } ++ if (c != 1 ) { /* otherwhise this is a NOP */ ++ /* ++ * this is done often , but optimizing is not so ++ * fruitful, at least in the obvious ways (unrolling) ++ */ ++ DEB( pivswaps++ ; ) ++ c = inverse[ c ] ; ++ pivot_row[icol] = 1 ; ++ for (ix = 0 ; ix < k ; ix++ ) ++ pivot_row[ix] = gf_mul(c, pivot_row[ix] ); ++ } ++ /* ++ * from all rows, remove multiples of the selected row ++ * to zero the relevant entry (in fact, the entry is not zero ++ * because we know it must be zero). ++ * (Here, if we know that the pivot_row is the identity, ++ * we can optimize the addmul). ++ */ ++ id_row[icol] = 1; ++ if (bcmp(pivot_row, id_row, k*sizeof(gf)) != 0) { ++ for (p = src, ix = 0 ; ix < k ; ix++, p += k ) { ++ if (ix != icol) { ++ c = p[icol] ; ++ p[icol] = 0 ; ++ addmul(p, pivot_row, c, k ); ++ } ++ } ++ } ++ id_row[icol] = 0; ++ } /* done all columns */ ++ for (col = k-1 ; col >= 0 ; col-- ) { ++ if (indxr[col] <0 || indxr[col] >= k) ++ fprintf(stderr, "AARGH, indxr[col] %d\n", indxr[col]); ++ else if (indxc[col] <0 || indxc[col] >= k) ++ fprintf(stderr, "AARGH, indxc[col] %d\n", indxc[col]); ++ else ++ if (indxr[col] != indxc[col] ) { ++ for (row = 0 ; row < k ; row++ ) { ++ SWAP( src[row*k + indxr[col]], src[row*k + indxc[col]], gf) ; ++ } ++ } ++ } ++ error = 0 ; ++fail: ++ free(indxc); ++ free(indxr); ++ free(ipiv); ++ free(id_row); ++ free(temp_row); ++ return error ; ++} ++ ++/* ++ * fast code for inverting a vandermonde matrix. ++ * XXX NOTE: It assumes that the matrix ++ * is not singular and _IS_ a vandermonde matrix. Only uses ++ * the second column of the matrix, containing the p_i's. ++ * ++ * Algorithm borrowed from "Numerical recipes in C" -- sec.2.8, but ++ * largely revised for my purposes. ++ * p = coefficients of the matrix (p_i) ++ * q = values of the polynomial (known) ++ */ ++ ++int ++invert_vdm(gf *src, int k) ++{ ++ int i, j, row, col ; ++ gf *b, *c, *p; ++ gf t, xx ; ++ ++ if (k == 1) /* degenerate case, matrix must be p^0 = 1 */ ++ return 0 ; ++ /* ++ * c holds the coefficient of P(x) = Prod (x - p_i), i=0..k-1 ++ * b holds the coefficient for the matrix inversion ++ */ ++ c = NEW_GF_MATRIX(1, k); ++ b = NEW_GF_MATRIX(1, k); ++ ++ p = NEW_GF_MATRIX(1, k); ++ ++ for ( j=1, i = 0 ; i < k ; i++, j+=k ) { ++ c[i] = 0 ; ++ p[i] = src[j] ; /* p[i] */ ++ } ++ /* ++ * construct coeffs. recursively. We know c[k] = 1 (implicit) ++ * and start P_0 = x - p_0, then at each stage multiply by ++ * x - p_i generating P_i = x P_{i-1} - p_i P_{i-1} ++ * After k steps we are done. ++ */ ++ c[k-1] = p[0] ; /* really -p(0), but x = -x in GF(2^m) */ ++ for (i = 1 ; i < k ; i++ ) { ++ gf p_i = p[i] ; /* see above comment */ ++ for (j = k-1 - ( i - 1 ) ; j < k-1 ; j++ ) ++ c[j] ^= gf_mul( p_i, c[j+1] ) ; ++ c[k-1] ^= p_i ; ++ } ++ ++ for (row = 0 ; row < k ; row++ ) { ++ /* ++ * synthetic division etc. ++ */ ++ xx = p[row] ; ++ t = 1 ; ++ b[k-1] = 1 ; /* this is in fact c[k] */ ++ for (i = k-2 ; i >= 0 ; i-- ) { ++ b[i] = c[i+1] ^ gf_mul(xx, b[i+1]) ; ++ t = gf_mul(xx, t) ^ b[i] ; ++ } ++ for (col = 0 ; col < k ; col++ ) ++ src[col*k + row] = gf_mul(inverse[t], b[col] ); ++ } ++ free(c) ; ++ free(b) ; ++ free(p) ; ++ return 0 ; ++} ++ ++static int fec_initialized = 0 ; ++static void ++init_fec() ++{ ++ TICK(ticks[0]); ++ generate_gf(); ++ TOCK(ticks[0]); ++ DDB(fprintf(stderr, "generate_gf took %ldus\n", ticks[0]);) ++ TICK(ticks[0]); ++ init_mul_table(); ++ TOCK(ticks[0]); ++ DDB(fprintf(stderr, "init_mul_table took %ldus\n", ticks[0]);) ++ fec_initialized = 1 ; ++} ++ ++/* ++ * This section contains the proper FEC encoding/decoding routines. ++ * The encoding matrix is computed starting with a Vandermonde matrix, ++ * and then transforming it into a systematic matrix. ++ */ ++ ++#define FEC_MAGIC 0xFECC0DEC ++ ++struct fec_parms { ++ u_long magic ; ++ int k, n ; /* parameters of the code */ ++ gf *enc_matrix ; ++} ; ++ ++void ++fec_free(struct fec_parms *p) ++{ ++ if (p==NULL || ++ p->magic != ( ( (FEC_MAGIC ^ p->k) ^ p->n) ^ (int)(p->enc_matrix)) ) { ++ fprintf(stderr, "bad parameters to fec_free\n"); ++ return ; ++ } ++ free(p->enc_matrix); ++ free(p); ++} ++ ++/* ++ * create a new encoder, returning a descriptor. This contains k,n and ++ * the encoding matrix. ++ */ ++struct fec_parms * ++fec_new(int k, int n) ++{ ++ int row, col ; ++ gf *p, *tmp_m ; ++ ++ struct fec_parms *retval ; ++ ++ if (fec_initialized == 0) ++ init_fec(); ++ ++ if (k > GF_SIZE + 1 || n > GF_SIZE + 1 || k > n ) { ++ fprintf(stderr, "Invalid parameters k %d n %d GF_SIZE %d\n", ++ k, n, GF_SIZE ); ++ return NULL ; ++ } ++ retval = my_malloc(sizeof(struct fec_parms), "new_code"); ++ retval->k = k ; ++ retval->n = n ; ++ retval->enc_matrix = NEW_GF_MATRIX(n, k); ++ retval->magic = ( ( FEC_MAGIC ^ k) ^ n) ^ (int)(retval->enc_matrix) ; ++ tmp_m = NEW_GF_MATRIX(n, k); ++ /* ++ * fill the matrix with powers of field elements, starting from 0. ++ * The first row is special, cannot be computed with exp. table. ++ */ ++ tmp_m[0] = 1 ; ++ for (col = 1; col < k ; col++) ++ tmp_m[col] = 0 ; ++ for (p = tmp_m + k, row = 0; row < n-1 ; row++, p += k) { ++ for ( col = 0 ; col < k ; col ++ ) ++ p[col] = gf_exp[modnn(row*col)]; ++ } ++ ++ /* ++ * quick code to build systematic matrix: invert the top ++ * k*k vandermonde matrix, multiply right the bottom n-k rows ++ * by the inverse, and construct the identity matrix at the top. ++ */ ++ TICK(ticks[3]); ++ invert_vdm(tmp_m, k); /* much faster than invert_mat */ ++ matmul(tmp_m + k*k, tmp_m, retval->enc_matrix + k*k, n - k, k, k); ++ /* ++ * the upper matrix is I so do not bother with a slow multiply ++ */ ++ bzero(retval->enc_matrix, k*k*sizeof(gf) ); ++ for (p = retval->enc_matrix, col = 0 ; col < k ; col++, p += k+1 ) ++ *p = 1 ; ++ free(tmp_m); ++ TOCK(ticks[3]); ++ ++ DDB(fprintf(stderr, "--- %ld us to build encoding matrix\n", ++ ticks[3]);) ++ DEB(pr_matrix(retval->enc_matrix, n, k, "encoding_matrix");) ++ return retval ; ++} ++ ++/* ++ * fec_encode accepts as input pointers to n data packets of size sz, ++ * and produces as output a packet pointed to by fec, computed ++ * with index "index". ++ */ ++void ++fec_encode(struct fec_parms *code, gf *src[], gf *fec, int index, int sz) ++{ ++ int i, k = code->k ; ++ gf *p ; ++ ++ if (GF_BITS > 8) ++ sz /= 2 ; ++ ++ if (index < k) ++ bcopy(src[index], fec, sz*sizeof(gf) ) ; ++ else if (index < code->n) { ++ p = &(code->enc_matrix[index*k] ); ++ bzero(fec, sz*sizeof(gf)); ++ for (i = 0; i < k ; i++) ++ addmul(fec, src[i], p[i], sz ) ; ++ } else ++ fprintf(stderr, "Invalid index %d (max %d)\n", ++ index, code->n - 1 ); ++} ++ ++void fec_encode_linear(struct fec_parms *code, gf *src, gf *fec, int index, int sz) ++{ ++ int i, k = code->k ; ++ gf *p ; ++ ++ if (GF_BITS > 8) ++ sz /= 2 ; ++ ++ if (index < k) ++ bcopy(src + (index * sz), fec, sz*sizeof(gf) ) ; ++ else if (index < code->n) { ++ p = &(code->enc_matrix[index*k] ); ++ bzero(fec, sz*sizeof(gf)); ++ for (i = 0; i < k ; i++) ++ addmul(fec, src + (i * sz), p[i], sz ) ; ++ } else ++ fprintf(stderr, "Invalid index %d (max %d)\n", ++ index, code->n - 1 ); ++} ++/* ++ * shuffle move src packets in their position ++ */ ++static int ++shuffle(gf *pkt[], int index[], int k) ++{ ++ int i; ++ ++ for ( i = 0 ; i < k ; ) { ++ if (index[i] >= k || index[i] == i) ++ i++ ; ++ else { ++ /* ++ * put pkt in the right position (first check for conflicts). ++ */ ++ int c = index[i] ; ++ ++ if (index[c] == c) { ++ DEB(fprintf(stderr, "\nshuffle, error at %d\n", i);) ++ return 1 ; ++ } ++ SWAP(index[i], index[c], int) ; ++ SWAP(pkt[i], pkt[c], gf *) ; ++ } ++ } ++ DEB( /* just test that it works... */ ++ for ( i = 0 ; i < k ; i++ ) { ++ if (index[i] < k && index[i] != i) { ++ fprintf(stderr, "shuffle: after\n"); ++ for (i=0; ik ; ++ gf *p, *matrix = NEW_GF_MATRIX(k, k); ++ ++ TICK(ticks[9]); ++ for (i = 0, p = matrix ; i < k ; i++, p += k ) { ++#if 1 /* this is simply an optimization, not very useful indeed */ ++ if (index[i] < k) { ++ bzero(p, k*sizeof(gf) ); ++ p[i] = 1 ; ++ } else ++#endif ++ if (index[i] < code->n ) ++ bcopy( &(code->enc_matrix[index[i]*k]), p, k*sizeof(gf) ); ++ else { ++ fprintf(stderr, "decode: invalid index %d (max %d)\n", ++ index[i], code->n - 1 ); ++ free(matrix) ; ++ return NULL ; ++ } ++ } ++ TICK(ticks[9]); ++ if (invert_mat(matrix, k)) { ++ free(matrix); ++ matrix = NULL ; ++ } ++ TOCK(ticks[9]); ++ return matrix ; ++} ++ ++/* ++ * fec_decode receives as input a vector of packets, the indexes of ++ * packets, and produces the correct vector as output. ++ * ++ * Input: ++ * code: pointer to code descriptor ++ * pkt: pointers to received packets. They are modified ++ * to store the output packets (in place) ++ * index: pointer to packet indexes (modified) ++ * sz: size of each packet ++ */ ++int ++fec_decode(struct fec_parms *code, gf *pkt[], int index[], int sz) ++{ ++ gf *m_dec ; ++ gf **new_pkt ; ++ int row, col , k = code->k ; ++ ++ if (GF_BITS > 8) ++ sz /= 2 ; ++ ++ if (shuffle(pkt, index, k)) /* error if true */ ++ return 1 ; ++ m_dec = build_decode_matrix(code, pkt, index); ++ ++ if (m_dec == NULL) ++ return 1 ; /* error */ ++ /* ++ * do the actual decoding ++ */ ++ new_pkt = my_malloc (k * sizeof (gf * ), "new pkt pointers" ); ++ for (row = 0 ; row < k ; row++ ) { ++ if (index[row] >= k) { ++ new_pkt[row] = my_malloc (sz * sizeof (gf), "new pkt buffer" ); ++ bzero(new_pkt[row], sz * sizeof(gf) ) ; ++ for (col = 0 ; col < k ; col++ ) ++ addmul(new_pkt[row], pkt[col], m_dec[row*k + col], sz) ; ++ } ++ } ++ /* ++ * move pkts to their final destination ++ */ ++ for (row = 0 ; row < k ; row++ ) { ++ if (index[row] >= k) { ++ bcopy(new_pkt[row], pkt[row], sz*sizeof(gf)); ++ free(new_pkt[row]); ++ } ++ } ++ free(new_pkt); ++ free(m_dec); ++ ++ return 0; ++} ++ ++/*********** end of FEC code -- beginning of test code ************/ ++ ++#if (TEST || DEBUG) ++void ++test_gf() ++{ ++ int i ; ++ /* ++ * test gf tables. Sufficiently tested... ++ */ ++ for (i=0; i<= GF_SIZE; i++) { ++ if (gf_exp[gf_log[i]] != i) ++ fprintf(stderr, "bad exp/log i %d log %d exp(log) %d\n", ++ i, gf_log[i], gf_exp[gf_log[i]]); ++ ++ if (i != 0 && gf_mul(i, inverse[i]) != 1) ++ fprintf(stderr, "bad mul/inv i %d inv %d i*inv(i) %d\n", ++ i, inverse[i], gf_mul(i, inverse[i]) ); ++ if (gf_mul(0,i) != 0) ++ fprintf(stderr, "bad mul table 0,%d\n",i); ++ if (gf_mul(i,0) != 0) ++ fprintf(stderr, "bad mul table %d,0\n",i); ++ } ++} ++#endif /* TEST */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/fectest.c linux-2.6.24.7/drivers/mtd/mtd-utils/fectest.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/fectest.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/fectest.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,92 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mcast_image.h" ++#include "crc32.h" ++ ++#define ERASE_SIZE 131072 ++//#define PKT_SIZE 1400 ++#define NR_PKTS ((ERASE_SIZE + PKT_SIZE - 1) / PKT_SIZE) ++#define DROPS 8 ++ ++int main(void) ++{ ++ int i, j; ++ unsigned char buf[NR_PKTS * PKT_SIZE]; ++ unsigned char pktbuf[(NR_PKTS + DROPS) * PKT_SIZE]; ++ struct fec_parms *fec; ++ unsigned char *srcs[NR_PKTS]; ++ unsigned char *pkt[NR_PKTS + DROPS]; ++ int pktnr[NR_PKTS + DROPS]; ++ struct timeval then, now; ++ ++ srand(3453); ++ for (i=0; i < sizeof(buf); i++) ++ if (i < ERASE_SIZE) ++ buf[i] = rand(); ++ else ++ buf[i] = 0; ++ ++ for (i=0; i < NR_PKTS + DROPS; i++) ++ srcs[i] = buf + (i * PKT_SIZE); ++ ++ for (i=0; i < NR_PKTS + DROPS; i++) { ++ pkt[i] = malloc(PKT_SIZE); ++ pktnr[i] = -1; ++ } ++ fec = fec_new(NR_PKTS, NR_PKTS + DROPS); ++ if (!fec) { ++ printf("fec_init() failed\n"); ++ exit(1); ++ } ++ j = 0; ++ for (i=0; i < NR_PKTS + DROPS; i++) { ++#if 1 ++ if (i == 27 || i == 40 || i == 44 || i == 45 || i == 56 ) ++ continue; ++#endif ++ if (i == 69 || i == 93 || i == 103) ++ continue; ++ fec_encode(fec, srcs, pkt[j], i, PKT_SIZE); ++ pktnr[j] = i; ++ j++; ++ } ++ gettimeofday(&then, NULL); ++ if (fec_decode(fec, pkt, pktnr, PKT_SIZE)) { ++ printf("Decode failed\n"); ++ exit(1); ++ } ++ ++ for (i=0; i < NR_PKTS; i++) ++ memcpy(pktbuf + (i*PKT_SIZE), pkt[i], PKT_SIZE); ++ gettimeofday(&now, NULL); ++ now.tv_sec -= then.tv_sec; ++ now.tv_usec -= then.tv_usec; ++ if (now.tv_usec < 0) { ++ now.tv_usec += 1000000; ++ now.tv_sec--; ++ } ++ ++ if (memcmp(pktbuf, buf, ERASE_SIZE)) { ++ int fd; ++ printf("Compare failed\n"); ++ fd = open("before", O_WRONLY|O_TRUNC|O_CREAT, 0644); ++ if (fd >= 0) ++ write(fd, buf, ERASE_SIZE); ++ close(fd); ++ fd = open("after", O_WRONLY|O_TRUNC|O_CREAT, 0644); ++ if (fd >= 0) ++ write(fd, pktbuf, ERASE_SIZE); ++ ++ exit(1); ++ } ++ ++ printf("Decoded in %ld.%06lds\n", now.tv_sec, now.tv_usec); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_erase.c linux-2.6.24.7/drivers/mtd/mtd-utils/flash_erase.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_erase.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_erase.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,189 @@ ++/* ++ * flash_erase.c -- erase parts of a MTD device ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++int region_erase(int Fd, int start, int count, int unlock, int regcount) ++{ ++ int i, j; ++ region_info_t * reginfo; ++ ++ reginfo = calloc(regcount, sizeof(region_info_t)); ++ ++ for(i = 0; i < regcount; i++) ++ { ++ reginfo[i].regionindex = i; ++ if(ioctl(Fd,MEMGETREGIONINFO,&(reginfo[i])) != 0) ++ return 8; ++ else ++ printf("Region %d is at %d of %d sector and with sector " ++ "size %x\n", i, reginfo[i].offset, reginfo[i].numblocks, ++ reginfo[i].erasesize); ++ } ++ ++ // We have all the information about the chip we need. ++ ++ for(i = 0; i < regcount; i++) ++ { //Loop through the regions ++ region_info_t * r = &(reginfo[i]); ++ ++ if((start >= reginfo[i].offset) && ++ (start < (r->offset + r->numblocks*r->erasesize))) ++ break; ++ } ++ ++ if(i >= regcount) ++ { ++ printf("Starting offset %x not within chip.\n", start); ++ return 8; ++ } ++ ++ //We are now positioned within region i of the chip, so start erasing ++ //count sectors from there. ++ ++ for(j = 0; (j < count)&&(i < regcount); j++) ++ { ++ erase_info_t erase; ++ region_info_t * r = &(reginfo[i]); ++ ++ erase.start = start; ++ erase.length = r->erasesize; ++ ++ if(unlock != 0) ++ { //Unlock the sector first. ++ if(ioctl(Fd, MEMUNLOCK, &erase) != 0) ++ { ++ perror("\nMTD Unlock failure"); ++ close(Fd); ++ return 8; ++ } ++ } ++ printf("\rPerforming Flash Erase of length 0x%llx at offset 0x%llx", ++ erase.length, erase.start); ++ fflush(stdout); ++ if(ioctl(Fd, MEMERASE, &erase) != 0) ++ { ++ perror("\nMTD Erase failure"); ++ close(Fd); ++ return 8; ++ } ++ ++ ++ start += erase.length; ++ if(start >= (r->offset + r->numblocks*r->erasesize)) ++ { //We finished region i so move to region i+1 ++ printf("\nMoving to region %d\n", i+1); ++ i++; ++ } ++ } ++ ++ printf(" done\n"); ++ ++ return 0; ++} ++ ++int non_region_erase(int Fd, int start, int count, int unlock) ++{ ++ mtd_info_t meminfo; ++ ++ if (ioctl(Fd,MEMGETINFO,&meminfo) == 0) ++ { ++ erase_info_t erase; ++ ++ erase.start = start; ++ ++ erase.length = meminfo.erasesize; ++ ++ for (; count > 0; count--) { ++ printf("\rPerforming Flash Erase of length 0x%llx at offset 0x%llx", ++ erase.length, erase.start); ++ fflush(stdout); ++ ++ if(unlock != 0) ++ { ++ //Unlock the sector first. ++ printf("\rPerforming Flash unlock at offset 0x%llx",erase.start); ++ if(ioctl(Fd, MEMUNLOCK, &erase) != 0) ++ { ++ perror("\nMTD Unlock failure"); ++ close(Fd); ++ return 8; ++ } ++ } ++ ++ if (ioctl(Fd,MEMERASE,&erase) != 0) ++ { ++ perror("\nMTD Erase failure"); ++ close(Fd); ++ return 8; ++ } ++ erase.start += meminfo.erasesize; ++ } ++ printf(" done\n"); ++ } ++ return 0; ++} ++ ++int main(int argc,char *argv[]) ++{ ++ int regcount; ++ int Fd; ++ int start; ++ int count; ++ int unlock; ++ int res = 0; ++ ++ if (1 >= argc || !strcmp(argv[1], "-h") || !strcmp (argv[1], "--help") ) { ++ printf("Usage: flash_erase MTD-device [start] [cnt (# erase blocks)] [lock]\n" ++ " flash_erase -h | --help\n") ; ++ return 16 ; ++ } ++ ++ if (argc > 2) ++ start = strtol(argv[2], NULL, 0); ++ else ++ start = 0; ++ ++ if (argc > 3) ++ count = strtol(argv[3], NULL, 0); ++ else ++ count = 1; ++ ++ if(argc > 4) ++ unlock = strtol(argv[4], NULL, 0); ++ else ++ unlock = 0; ++ ++ ++ // Open and size the device ++ if ((Fd = open(argv[1],O_RDWR)) < 0) ++ { ++ fprintf(stderr,"File open error\n"); ++ return 8; ++ } ++ ++ printf("Erase Total %d Units\n", count); ++ ++ if (ioctl(Fd,MEMGETREGIONCOUNT,®count) == 0) ++ { ++ if(regcount == 0) ++ { ++ res = non_region_erase(Fd, start, count, unlock); ++ } ++ else ++ { ++ res = region_erase(Fd, start, count, unlock, regcount); ++ } ++ } ++ ++ return res; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_eraseall.c linux-2.6.24.7/drivers/mtd/mtd-utils/flash_eraseall.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_eraseall.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_eraseall.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,286 @@ ++/* eraseall.c -- erase the whole of a MTD device ++ ++ Copyright (C) 2000 Arcom Control System Ltd ++ ++ Renamed to flash_eraseall.c ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "crc32.h" ++ ++#include ++#include ++ ++#define PROGRAM "flash_eraseall" ++#define VERSION "$Revision: 1.1.1.1 $" ++ ++static const char *exe_name; ++static const char *mtd_device; ++static int quiet; /* true -- don't output progress */ ++static int jffs2; // format for jffs2 usage ++ ++static void process_options (int argc, char *argv[]); ++static void display_help (void); ++static void display_version (void); ++static struct jffs2_unknown_node cleanmarker; ++int target_endian = __BYTE_ORDER; ++ ++int main (int argc, char *argv[]) ++{ ++ mtd_info_t meminfo; ++ int fd, clmpos = 0, clmlen = 8; ++ erase_info_t erase; ++ int isNAND, bbtest = 1; ++ ++ process_options(argc, argv); ++ ++ ++ if ((fd = open(mtd_device, O_RDWR)) < 0) { ++ fprintf(stderr, "%s: %s: %s\n", exe_name, mtd_device, strerror(errno)); ++ exit(1); ++ } ++ ++ ++ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { ++ fprintf(stderr, "%s: %s: unable to get MTD device info\n", exe_name, mtd_device); ++ exit(1); ++ } ++ ++ erase.length = meminfo.erasesize; ++ isNAND = meminfo.type == MTD_NANDFLASH ? 1 : 0; ++ ++ if (jffs2) { ++ cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); ++ cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); ++ if (!isNAND) ++ cleanmarker.totlen = cpu_to_je32 (sizeof (struct jffs2_unknown_node)); ++ else { ++ struct nand_oobinfo oobinfo; ++ ++ if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) { ++ fprintf(stderr, "%s: %s: unable to get NAND oobinfo\n", exe_name, mtd_device); ++ exit(1); ++ } ++ ++ /* Check for autoplacement */ ++ if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { ++ /* Get the position of the free bytes */ ++ if (!oobinfo.oobfree[0][1]) { ++ fprintf (stderr, " Eeep. Autoplacement selected and no empty space in oob\n"); ++ exit(1); ++ } ++ clmpos = oobinfo.oobfree[0][0]; ++ clmlen = oobinfo.oobfree[0][1]; ++ if (clmlen > 8) ++ clmlen = 8; ++ } else { ++ /* Legacy mode */ ++ switch (meminfo.oobsize) { ++ case 8: ++ clmpos = 6; ++ clmlen = 2; ++ break; ++ case 16: ++ clmpos = 8; ++ clmlen = 8; ++ break; ++ case 64: ++ clmpos = 16; ++ clmlen = 8; ++ break; ++ } ++ } ++ cleanmarker.totlen = cpu_to_je32(8); ++ } ++ cleanmarker.hdr_crc = cpu_to_je32 (crc32 (0, &cleanmarker, sizeof (struct jffs2_unknown_node) - 4)); ++ } ++ ++ for (erase.start = 0; erase.start < meminfo.size; erase.start += meminfo.erasesize) { ++ if (bbtest) { ++ unsigned long long offset = erase.start; ++ int ret = ioctl(fd, MEMGETBADBLOCK, &offset); ++ if (ret > 0) { ++ if (!quiet) ++ printf ("\nSkipping bad block at 0x%09llx\n", erase.start); ++ continue; ++ } else if (ret < 0) { ++ if (errno == EOPNOTSUPP) { ++ bbtest = 0; ++ if (isNAND) { ++ fprintf(stderr, "%s: %s: Bad block check not available\n", exe_name, mtd_device); ++ exit(1); ++ } ++ } else { ++ fprintf(stderr, "\n%s: %s: MTD get bad block failed: %s\n", exe_name, mtd_device, strerror(errno)); ++ exit(1); ++ } ++ } ++ } ++ ++ if (!quiet) { ++ printf ++ ("\rErasing %d Kibyte @ %llx -- %2u %% complete.", ++ meminfo.erasesize / 1024, erase.start, ++ (unsigned long long) ++ erase.start * 100 / meminfo.size); ++ } ++ fflush(stdout); ++ ++ if (ioctl(fd, MEMERASE, &erase) != 0) { ++ fprintf(stderr, "\n%s: %s: MTD Erase failure: %s\n", exe_name, mtd_device, strerror(errno)); ++ continue; ++ } ++ ++ /* format for JFFS2 ? */ ++ if (!jffs2) ++ continue; ++ ++ /* write cleanmarker */ ++ if (isNAND) { ++ struct mtd_oob_buf oob; ++ oob.ptr = (unsigned char *) &cleanmarker; ++ oob.start = erase.start + clmpos; ++ oob.length = clmlen; ++ if (ioctl (fd, MEMWRITEOOB, &oob) != 0) { ++ fprintf(stderr, "\n%s: %s: MTD writeoob failure: %s\n", exe_name, mtd_device, strerror(errno)); ++ continue; ++ } ++ } else { ++ if (lseek (fd, erase.start, SEEK_SET) < 0) { ++ fprintf(stderr, "\n%s: %s: MTD lseek failure: %s\n", exe_name, mtd_device, strerror(errno)); ++ continue; ++ } ++ if (write (fd , &cleanmarker, sizeof (cleanmarker)) != sizeof (cleanmarker)) { ++ fprintf(stderr, "\n%s: %s: MTD write failure: %s\n", exe_name, mtd_device, strerror(errno)); ++ continue; ++ } ++ } ++ if (!quiet) ++ printf (" Cleanmarker written at %x.", erase.start); ++ } ++ if (!quiet) ++ printf("\n"); ++ ++ return 0; ++} ++ ++ ++void process_options (int argc, char *argv[]) ++{ ++ int error = 0; ++ ++ exe_name = argv[0]; ++ ++ for (;;) { ++ int option_index = 0; ++ static const char *short_options = "jq"; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 0}, ++ {"version", no_argument, 0, 0}, ++ {"jffs2", no_argument, 0, 'j'}, ++ {"quiet", no_argument, 0, 'q'}, ++ {"silent", no_argument, 0, 'q'}, ++ ++ {0, 0, 0, 0}, ++ }; ++ ++ int c = getopt_long(argc, argv, short_options, ++ long_options, &option_index); ++ if (c == EOF) { ++ break; ++ } ++ ++ switch (c) { ++ case 0: ++ switch (option_index) { ++ case 0: ++ display_help(); ++ break; ++ case 1: ++ display_version(); ++ break; ++ } ++ break; ++ case 'q': ++ quiet = 1; ++ break; ++ case 'j': ++ jffs2 = 1; ++ break; ++ case '?': ++ error = 1; ++ break; ++ } ++ } ++ if (optind == argc) { ++ fprintf(stderr, "%s: no MTD device specified\n", exe_name); ++ error = 1; ++ } ++ if (error) { ++ fprintf(stderr, "Try `%s --help' for more information.\n", ++ exe_name); ++ exit(1); ++ } ++ ++ mtd_device = argv[optind]; ++} ++ ++ ++void display_help (void) ++{ ++ printf("Usage: %s [OPTION] MTD_DEVICE\n" ++ "Erases all of the specified MTD device.\n" ++ "\n" ++ " -j, --jffs2 format the device for jffs2\n" ++ " -q, --quiet don't display progress messages\n" ++ " --silent same as --quiet\n" ++ " --help display this help and exit\n" ++ " --version output version information and exit\n", ++ exe_name); ++ exit(0); ++} ++ ++ ++void display_version (void) ++{ ++ printf(PROGRAM " " VERSION "\n" ++ "\n" ++ "Copyright (C) 2000 Arcom Control Systems Ltd\n" ++ "\n" ++ PROGRAM " comes with NO WARRANTY\n" ++ "to the extent permitted by law.\n" ++ "\n" ++ "You may redistribute copies of " PROGRAM "\n" ++ "under the terms of the GNU General Public Licence.\n" ++ "See the file `COPYING' for more information.\n"); ++ exit(0); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_info.c linux-2.6.24.7/drivers/mtd/mtd-utils/flash_info.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_info.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_info.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,55 @@ ++/* ++ * flash_info.c -- print info about a MTD device ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++int main(int argc,char *argv[]) ++{ ++ int regcount; ++ int Fd; ++ ++ if (1 >= argc) ++ { ++ fprintf(stderr,"Usage: flash_info device\n"); ++ return 16; ++ } ++ ++ // Open and size the device ++ if ((Fd = open(argv[1],O_RDONLY)) < 0) ++ { ++ fprintf(stderr,"File open error\n"); ++ return 8; ++ } ++ ++ if (ioctl(Fd,MEMGETREGIONCOUNT,®count) == 0) ++ { ++ int i; ++ region_info_t reginfo; ++ printf("Device %s has %d erase regions\n", argv[1], regcount); ++ for (i = 0; i < regcount; i++) ++ { ++ reginfo.regionindex = i; ++ if(ioctl(Fd, MEMGETREGIONINFO, ®info) == 0) ++ { ++ printf("Region %d is at 0x%x with size 0x%x and " ++ "has 0x%x blocks\n", i, reginfo.offset, ++ reginfo.erasesize, reginfo.numblocks); ++ } ++ else ++ { ++ printf("Strange can not read region %d from a %d region device\n", ++ i, regcount); ++ } ++ } ++ } ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_lock.c linux-2.6.24.7/drivers/mtd/mtd-utils/flash_lock.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_lock.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_lock.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,84 @@ ++/* ++ * FILE flash_lock.c ++ * ++ * This utility locks one or more sectors of flash device. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++int main(int argc, char *argv[]) ++{ ++ int fd; ++ struct mtd_info_user mtdInfo; ++ struct erase_info_user mtdLockInfo; ++ int num_sectors; ++ int ofs; ++ ++ /* ++ * Parse command line options ++ */ ++ if(argc != 4) ++ { ++ fprintf(stderr, "USAGE: %s \n", argv[0]); ++ exit(1); ++ } ++ else if(strncmp(argv[1], "/dev/mtd", 8) != 0) ++ { ++ fprintf(stderr, "'%s' is not a MTD device. Must specify mtd device: /dev/mtd?\n", argv[1]); ++ exit(1); ++ } ++ ++ fd = open(argv[1], O_RDWR); ++ if(fd < 0) ++ { ++ fprintf(stderr, "Could not open mtd device: %s\n", argv[1]); ++ exit(1); ++ } ++ ++ if(ioctl(fd, MEMGETINFO, &mtdInfo)) ++ { ++ fprintf(stderr, "Could not get MTD device info from %s\n", argv[1]); ++ close(fd); ++ exit(1); ++ } ++ sscanf(argv[2], "%x",&ofs); ++ sscanf(argv[3], "%d",&num_sectors); ++ if(ofs > mtdInfo.size - mtdInfo.erasesize) ++ { ++ fprintf(stderr, "%x is beyond device size %x\n",ofs,(unsigned int)(mtdInfo.size - mtdInfo.erasesize)); ++ exit(1); ++ } ++ ++ if (num_sectors == -1) { ++ num_sectors = mtdInfo.size/mtdInfo.erasesize; ++ } ++ else { ++ if(num_sectors > mtdInfo.size/mtdInfo.erasesize) ++ { ++ fprintf(stderr, "%d are too many sectors, device only has %d\n",num_sectors,(int)(mtdInfo.size/mtdInfo.erasesize)); ++ exit(1); ++ } ++ } ++ ++ mtdLockInfo.start = ofs; ++ mtdLockInfo.length = num_sectors * mtdInfo.erasesize; ++ if(ioctl(fd, MEMLOCK, &mtdLockInfo)) ++ { ++ fprintf(stderr, "Could not lock MTD device: %s\n", argv[1]); ++ close(fd); ++ exit(1); ++ } ++ ++ return 0; ++} ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_otp_dump.c linux-2.6.24.7/drivers/mtd/mtd-utils/flash_otp_dump.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_otp_dump.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_otp_dump.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,54 @@ ++/* ++ * flash_otp_dump.c -- display One-Time-Programm data ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++int main(int argc,char *argv[]) ++{ ++ int fd, val, i, offset, ret; ++ unsigned char buf[16]; ++ ++ if (argc != 3 || (strcmp(argv[1], "-f") && strcmp(argv[1], "-u"))) { ++ fprintf(stderr,"Usage: %s [ -f | -u ] \n", argv[0]); ++ return EINVAL; ++ } ++ ++ fd = open(argv[2], O_RDONLY); ++ if (fd < 0) { ++ perror(argv[2]); ++ return errno; ++ } ++ ++ val = argv[1][1] == 'f' ? MTD_OTP_FACTORY : MTD_OTP_USER; ++ ret = ioctl(fd, OTPSELECT, &val); ++ if (ret < 0) { ++ perror("OTPSELECT"); ++ return errno; ++ } ++ ++ printf("OTP %s data for %s\n", ++ argv[1][1] == 'f' ? "factory" : "user", argv[2]); ++ offset = 0; ++ while ((ret = read(fd, buf, sizeof(buf)))) { ++ if (ret < 0) { ++ perror("read()"); ++ return errno; ++ } ++ printf("0x%04x:", offset); ++ for (i = 0; i < ret; i++) ++ printf(" %02x", buf[i]); ++ printf("\n"); ++ offset += ret; ++ } ++ ++ close(fd); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_otp_info.c linux-2.6.24.7/drivers/mtd/mtd-utils/flash_otp_info.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_otp_info.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_otp_info.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,63 @@ ++/* ++ * flash_otp_info.c -- print info about One-Time-Programm data ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++int main(int argc,char *argv[]) ++{ ++ int fd, val, i, ret; ++ ++ if (argc != 3 || (strcmp(argv[1], "-f") && strcmp(argv[1], "-u"))) { ++ fprintf(stderr,"Usage: %s [ -f | -u ] \n", argv[0]); ++ return EINVAL; ++ } ++ ++ fd = open(argv[2], O_RDONLY); ++ if (fd < 0) { ++ perror(argv[2]); ++ return errno; ++ } ++ ++ val = argv[1][1] == 'f' ? MTD_OTP_FACTORY : MTD_OTP_USER; ++ ret = ioctl(fd, OTPSELECT, &val); ++ if (ret < 0) { ++ perror("OTPSELECT"); ++ return errno; ++ } ++ ++ ret = ioctl(fd, OTPGETREGIONCOUNT, &val); ++ if (ret < 0) { ++ perror("OTPGETREGIONCOUNT"); ++ return errno; ++ } ++ ++ printf("Number of OTP %s blocks on %s: %d\n", ++ argv[1][1] == 'f' ? "factory" : "user", argv[2], val); ++ ++ if (val > 0) { ++ struct otp_info info[val]; ++ ++ ret = ioctl(fd, OTPGETREGIONINFO, &info); ++ if (ret < 0) { ++ perror("OTPGETREGIONCOUNT"); ++ return errno; ++ } ++ ++ for (i = 0; i < val; i++) ++ printf("block %2d: offset = 0x%04x " ++ "size = %2d bytes %s\n", ++ i, info[i].start, info[i].length, ++ info[i].locked ? "[locked]" : "[unlocked]"); ++ } ++ ++ close(fd); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_otp_lock.c linux-2.6.24.7/drivers/mtd/mtd-utils/flash_otp_lock.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_otp_lock.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_otp_lock.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,70 @@ ++/* ++ * flash_otp_lock.c -- lock area of One-Time-Program data ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++int main(int argc,char *argv[]) ++{ ++ int fd, val, ret, offset, size; ++ char *p, buf[8]; ++ ++ if (argc != 5 || strcmp(argv[1], "-u")) { ++ fprintf(stderr, "Usage: %s -u \n", argv[0]); ++ fprintf(stderr, "offset and size must match on OTP region boundaries\n"); ++ fprintf(stderr, "CAUTION! ONCE LOCKED, OTP REGIONS CAN'T BE UNLOCKED!\n"); ++ return EINVAL; ++ } ++ ++ fd = open(argv[2], O_WRONLY); ++ if (fd < 0) { ++ perror(argv[2]); ++ return errno; ++ } ++ ++ val = MTD_OTP_USER; ++ ret = ioctl(fd, OTPSELECT, &val); ++ if (ret < 0) { ++ perror("OTPSELECT"); ++ return errno; ++ } ++ ++ offset = strtoul(argv[3], &p, 0); ++ if (argv[3][0] == 0 || *p != 0) { ++ fprintf(stderr, "%s: bad offset value\n", argv[0]); ++ return ERANGE; ++ } ++ ++ size = strtoul(argv[4], &p, 0); ++ if (argv[4][0] == 0 || *p != 0) { ++ fprintf(stderr, "%s: bad size value\n", argv[0]); ++ return ERANGE; ++ } ++ ++ printf("About to lock OTP user data on %s from 0x%x to 0x%x\n", ++ argv[2], offset, offset + size); ++ printf("Are you sure (yes|no)? "); ++ if (fgets(buf, sizeof(buf), stdin) && strcmp(buf, "yes\n") == 0) { ++ struct otp_info info; ++ info.start = offset; ++ info.length = size; ++ ret = ioctl(fd, OTPLOCK, &info); ++ if (ret < 0) { ++ perror("OTPLOCK"); ++ return errno; ++ } ++ printf("Done.\n"); ++ } else { ++ printf("Aborted\n"); ++ } ++ ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_otp_write.c linux-2.6.24.7/drivers/mtd/mtd-utils/flash_otp_write.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_otp_write.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_otp_write.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,96 @@ ++/* ++ * flash_otp_write.c -- write One-Time-Program data ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++int main(int argc,char *argv[]) ++{ ++ int fd, val, ret, size, wrote, len; ++ mtd_info_t mtdInfo; ++ off_t offset; ++ char *p, buf[2048]; ++ ++ if (argc != 4 || strcmp(argv[1], "-u")) { ++ fprintf(stderr, "Usage: %s -u \n", argv[0]); ++ fprintf(stderr, "the raw data to write should be provided on stdin\n"); ++ fprintf(stderr, "CAUTION! ONCE SET TO 0, OTP DATA BITS CAN'T BE ERASED!\n"); ++ return EINVAL; ++ } ++ ++ fd = open(argv[2], O_WRONLY); ++ if (fd < 0) { ++ perror(argv[2]); ++ return errno; ++ } ++ ++ val = MTD_OTP_USER; ++ ret = ioctl(fd, OTPSELECT, &val); ++ if (ret < 0) { ++ perror("OTPSELECT"); ++ return errno; ++ } ++ ++ if (ioctl(fd, MEMGETINFO, &mtdInfo)) { ++ perror("MEMGETINFO"); ++ return errno; ++ } ++ ++ offset = strtoul(argv[3], &p, 0); ++ if (argv[3][0] == 0 || *p != 0) { ++ fprintf(stderr, "%s: bad offset value\n", argv[0]); ++ return ERANGE; ++ } ++ ++ if (lseek(fd, offset, SEEK_SET) == (off_t)-1) { ++ perror("lseek()"); ++ return errno; ++ } ++ ++ printf("Writing OTP user data on %s at offset 0x%lx\n", argv[2], offset); ++ ++ if (mtdInfo.type == MTD_NANDFLASH) ++ len = mtdInfo.writesize; ++ else ++ len = 256; ++ ++ wrote = 0; ++ while ((size = read(0, buf, len))) { ++ if (size < 0) { ++ perror("read()"); ++ return errno; ++ } ++ p = buf; ++ while (size > 0) { ++ if (mtdInfo.type == MTD_NANDFLASH) { ++ /* Fill remain buffers with 0xff */ ++ memset(buf + size, 0xff, mtdInfo.writesize - size); ++ size = mtdInfo.writesize; ++ } ++ ret = write(fd, p, size); ++ if (ret < 0) { ++ perror("write()"); ++ return errno; ++ } ++ if (ret == 0) { ++ printf("write() returned 0 after writing %d bytes\n", wrote); ++ return 0; ++ } ++ p += ret; ++ wrote += ret; ++ size -= ret; ++ } ++ } ++ ++ printf("Wrote %d bytes of OTP user data\n", wrote); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_unlock.c linux-2.6.24.7/drivers/mtd/mtd-utils/flash_unlock.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flash_unlock.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/flash_unlock.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,64 @@ ++/* ++ * FILE flash_unlock.c ++ * ++ * This utility unlock all sectors of flash device. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++int main(int argc, char *argv[]) ++{ ++ int fd; ++ struct mtd_info_user mtdInfo; ++ struct erase_info_user mtdLockInfo; ++ ++ /* ++ * Parse command line options ++ */ ++ if(argc != 2) ++ { ++ fprintf(stderr, "USAGE: %s \n", argv[0]); ++ exit(1); ++ } ++ else if(strncmp(argv[1], "/dev/mtd", 8) != 0) ++ { ++ fprintf(stderr, "'%s' is not a MTD device. Must specify mtd device: /dev/mtd?\n", argv[1]); ++ exit(1); ++ } ++ ++ fd = open(argv[1], O_RDWR); ++ if(fd < 0) ++ { ++ fprintf(stderr, "Could not open mtd device: %s\n", argv[1]); ++ exit(1); ++ } ++ ++ if(ioctl(fd, MEMGETINFO, &mtdInfo)) ++ { ++ fprintf(stderr, "Could not get MTD device info from %s\n", argv[1]); ++ close(fd); ++ exit(1); ++ } ++ ++ mtdLockInfo.start = 0; ++ mtdLockInfo.length = mtdInfo.size; ++ if(ioctl(fd, MEMUNLOCK, &mtdLockInfo)) ++ { ++ fprintf(stderr, "Could not unlock MTD device: %s\n", argv[1]); ++ close(fd); ++ exit(1); ++ } ++ ++ return 0; ++} ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/flashcp.c linux-2.6.24.7/drivers/mtd/mtd-utils/flashcp.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/flashcp.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/flashcp.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,389 @@ ++/* ++ * Copyright (c) 2d3D, Inc. ++ * Written by Abraham vd Merwe ++ * All rights reserved. ++ * ++ * Renamed to flashcp.c to avoid conflicts with fcp from fsh package ++ * ++ * 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. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the author nor the names of other contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++typedef int bool; ++#define true 1 ++#define false 0 ++ ++#define EXIT_FAILURE 1 ++#define EXIT_SUCCESS 0 ++ ++/* for debugging purposes only */ ++#ifdef DEBUG ++#undef DEBUG ++#define DEBUG(fmt,args...) { log_printf (LOG_ERROR,"%d: ",__LINE__); log_printf (LOG_ERROR,fmt,## args); } ++#else ++#undef DEBUG ++#define DEBUG(fmt,args...) ++#endif ++ ++#define KB(x) ((x) / 1024) ++#define PERCENTAGE(x,total) (((x) * 100) / (total)) ++ ++/* size of read/write buffer */ ++#define BUFSIZE (10 * 1024) ++ ++/* cmd-line flags */ ++#define FLAG_NONE 0x00 ++#define FLAG_VERBOSE 0x01 ++#define FLAG_HELP 0x02 ++#define FLAG_FILENAME 0x04 ++#define FLAG_DEVICE 0x08 ++ ++/* error levels */ ++#define LOG_NORMAL 1 ++#define LOG_ERROR 2 ++ ++static void log_printf (int level,const char *fmt, ...) ++{ ++ FILE *fp = level == LOG_NORMAL ? stdout : stderr; ++ va_list ap; ++ va_start (ap,fmt); ++ vfprintf (fp,fmt,ap); ++ va_end (ap); ++ fflush (fp); ++} ++ ++static void showusage (const char *progname,bool error) ++{ ++ int level = error ? LOG_ERROR : LOG_NORMAL; ++ ++ log_printf (level, ++ "\n" ++ "Flash Copy - Written by Abraham van der Merwe \n" ++ "\n" ++ "usage: %s [ -v | --verbose ] \n" ++ " %s -h | --help\n" ++ "\n" ++ " -h | --help Show this help message\n" ++ " -v | --verbose Show progress reports\n" ++ " File which you want to copy to flash\n" ++ " Flash device to write to (e.g. /dev/mtd0, /dev/mtd1, etc.)\n" ++ "\n", ++ progname,progname); ++ ++ exit (error ? EXIT_FAILURE : EXIT_SUCCESS); ++} ++ ++static int safe_open (const char *pathname,int flags) ++{ ++ int fd; ++ ++ fd = open (pathname,flags); ++ if (fd < 0) ++ { ++ log_printf (LOG_ERROR,"While trying to open %s",pathname); ++ if (flags & O_RDWR) ++ log_printf (LOG_ERROR," for read/write access"); ++ else if (flags & O_RDONLY) ++ log_printf (LOG_ERROR," for read access"); ++ else if (flags & O_WRONLY) ++ log_printf (LOG_ERROR," for write access"); ++ log_printf (LOG_ERROR,": %m\n"); ++ exit (EXIT_FAILURE); ++ } ++ ++ return (fd); ++} ++ ++static void safe_read (int fd,const char *filename,void *buf,size_t count,bool verbose) ++{ ++ ssize_t result; ++ ++ result = read (fd,buf,count); ++ if (count != result) ++ { ++ if (verbose) log_printf (LOG_NORMAL,"\n"); ++ if (result < 0) ++ { ++ log_printf (LOG_ERROR,"While reading data from %s: %m\n",filename); ++ exit (EXIT_FAILURE); ++ } ++ log_printf (LOG_ERROR,"Short read count returned while reading from %s\n",filename); ++ exit (EXIT_FAILURE); ++ } ++} ++ ++static void safe_rewind (int fd,const char *filename) ++{ ++ if (lseek (fd,0L,SEEK_SET) < 0) ++ { ++ log_printf (LOG_ERROR,"While seeking to start of %s: %m\n",filename); ++ exit (EXIT_FAILURE); ++ } ++} ++ ++/******************************************************************************/ ++ ++static int dev_fd = -1,fil_fd = -1; ++ ++static void cleanup (void) ++{ ++ if (dev_fd > 0) close (dev_fd); ++ if (fil_fd > 0) close (fil_fd); ++} ++ ++int main (int argc,char *argv[]) ++{ ++ const char *progname,*filename = NULL,*device = NULL; ++ int i,flags = FLAG_NONE; ++ ssize_t result; ++ size_t size,written; ++ struct mtd_info_user mtd; ++ struct erase_info_user erase; ++ struct stat filestat; ++ unsigned char src[BUFSIZE],dest[BUFSIZE]; ++ ++ (progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]); ++ ++ /********************* ++ * parse cmd-line ++ *****************/ ++ ++ for (;;) { ++ int option_index = 0; ++ static const char *short_options = "hv"; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"verbose", no_argument, 0, 'v'}, ++ {0, 0, 0, 0}, ++ }; ++ ++ int c = getopt_long(argc, argv, short_options, ++ long_options, &option_index); ++ if (c == EOF) { ++ break; ++ } ++ ++ switch (c) { ++ case 'h': ++ flags |= FLAG_HELP; ++ DEBUG("Got FLAG_HELP\n"); ++ break; ++ case 'v': ++ flags |= FLAG_VERBOSE; ++ DEBUG("Got FLAG_VERBOSE\n"); ++ break; ++ default: ++ DEBUG("Unknown parameter: %s\n",argv[option_index]); ++ showusage (progname,true); ++ } ++ } ++ if (optind+2 == argc) { ++ flags |= FLAG_FILENAME; ++ filename = argv[optind]; ++ DEBUG("Got filename: %s\n",filename); ++ ++ flags |= FLAG_DEVICE; ++ device = argv[optind+1]; ++ DEBUG("Got device: %s\n",device); ++ } ++ ++ if (flags & FLAG_HELP || progname == NULL || device == NULL) ++ showusage (progname,flags != FLAG_HELP); ++ ++ atexit (cleanup); ++ ++ /* get some info about the flash device */ ++ dev_fd = safe_open (device,O_SYNC | O_RDWR); ++ if (ioctl (dev_fd,MEMGETINFO,&mtd) < 0) ++ { ++ DEBUG("ioctl(): %m\n"); ++ log_printf (LOG_ERROR,"This doesn't seem to be a valid MTD flash device!\n"); ++ exit (EXIT_FAILURE); ++ } ++ ++ /* get some info about the file we want to copy */ ++ fil_fd = safe_open (filename,O_RDONLY); ++ if (fstat (fil_fd,&filestat) < 0) ++ { ++ log_printf (LOG_ERROR,"While trying to get the file status of %s: %m\n",filename); ++ exit (EXIT_FAILURE); ++ } ++ ++ /* does it fit into the device/partition? */ ++ if (filestat.st_size > mtd.size) ++ { ++ log_printf (LOG_ERROR,"%s won't fit into %s!\n",filename,device); ++ exit (EXIT_FAILURE); ++ } ++ ++ /***************************************************** ++ * erase enough blocks so that we can write the file * ++ *****************************************************/ ++ ++#warning "Check for smaller erase regions" ++ ++ erase.start = 0; ++ erase.length = filestat.st_size & ~(mtd.erasesize - 1); ++ if (filestat.st_size % mtd.erasesize) erase.length += mtd.erasesize; ++ if (flags & FLAG_VERBOSE) ++ { ++ /* if the user wants verbose output, erase 1 block at a time and show him/her what's going on */ ++ int blocks = erase.length / mtd.erasesize; ++ erase.length = mtd.erasesize; ++ log_printf (LOG_NORMAL,"Erasing blocks: 0/%d (0%%)",blocks); ++ for (i = 1; i <= blocks; i++) ++ { ++ log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (%d%%)",i,blocks,PERCENTAGE (i,blocks)); ++ if (ioctl (dev_fd,MEMERASE,&erase) < 0) ++ { ++ log_printf (LOG_NORMAL,"\n"); ++ log_printf (LOG_ERROR, ++ "While erasing blocks 0x%.8x-0x%.8x on %s: %m\n", ++ (unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device); ++ exit (EXIT_FAILURE); ++ } ++ erase.start += mtd.erasesize; ++ } ++ log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (100%%)\n",blocks,blocks); ++ } ++ else ++ { ++ /* if not, erase the whole chunk in one shot */ ++ if (ioctl (dev_fd,MEMERASE,&erase) < 0) ++ { ++ log_printf (LOG_ERROR, ++ "While erasing blocks from 0x%.8x-0x%.8x on %s: %m\n", ++ (unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device); ++ exit (EXIT_FAILURE); ++ } ++ } ++ DEBUG("Erased %u / %luk bytes\n",erase.length,filestat.st_size); ++ ++ /********************************** ++ * write the entire file to flash * ++ **********************************/ ++ ++ if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Writing data: 0k/%luk (0%%)",KB (filestat.st_size)); ++ size = filestat.st_size; ++ i = BUFSIZE; ++ written = 0; ++ while (size) ++ { ++ if (size < BUFSIZE) i = size; ++ if (flags & FLAG_VERBOSE) ++ log_printf (LOG_NORMAL,"\rWriting data: %dk/%luk (%lu%%)", ++ KB (written + i), ++ KB (filestat.st_size), ++ PERCENTAGE (written + i,filestat.st_size)); ++ ++ /* read from filename */ ++ safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE); ++ ++ /* write to device */ ++ result = write (dev_fd,src,i); ++ if (i != result) ++ { ++ if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"\n"); ++ if (result < 0) ++ { ++ log_printf (LOG_ERROR, ++ "While writing data to 0x%.8x-0x%.8x on %s: %m\n", ++ written,written + i,device); ++ exit (EXIT_FAILURE); ++ } ++ log_printf (LOG_ERROR, ++ "Short write count returned while writing to x%.8x-0x%.8x on %s: %d/%lu bytes written to flash\n", ++ written,written + i,device,written + result,filestat.st_size); ++ exit (EXIT_FAILURE); ++ } ++ ++ written += i; ++ size -= i; ++ } ++ if (flags & FLAG_VERBOSE) ++ log_printf (LOG_NORMAL, ++ "\rWriting data: %luk/%luk (100%%)\n", ++ KB (filestat.st_size), ++ KB (filestat.st_size)); ++ DEBUG("Wrote %d / %luk bytes\n",written,filestat.st_size); ++ ++ /********************************** ++ * verify that flash == file data * ++ **********************************/ ++ ++ safe_rewind (fil_fd,filename); ++ safe_rewind (dev_fd,device); ++ size = filestat.st_size; ++ i = BUFSIZE; ++ written = 0; ++ if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Verifying data: 0k/%luk (0%%)",KB (filestat.st_size)); ++ while (size) ++ { ++ if (size < BUFSIZE) i = size; ++ if (flags & FLAG_VERBOSE) ++ log_printf (LOG_NORMAL, ++ "\rVerifying data: %dk/%luk (%lu%%)", ++ KB (written + i), ++ KB (filestat.st_size), ++ PERCENTAGE (written + i,filestat.st_size)); ++ ++ /* read from filename */ ++ safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE); ++ ++ /* read from device */ ++ safe_read (dev_fd,device,dest,i,flags & FLAG_VERBOSE); ++ ++ /* compare buffers */ ++ if (memcmp (src,dest,i)) ++ { ++ log_printf (LOG_ERROR, ++ "File does not seem to match flash data. First mismatch at 0x%.8x-0x%.8x\n", ++ written,written + i); ++ exit (EXIT_FAILURE); ++ } ++ ++ written += i; ++ size -= i; ++ } ++ if (flags & FLAG_VERBOSE) ++ log_printf (LOG_NORMAL, ++ "\rVerifying data: %luk/%luk (100%%)\n", ++ KB (filestat.st_size), ++ KB (filestat.st_size)); ++ DEBUG("Verified %d / %luk bytes\n",written,filestat.st_size); ++ ++ exit (EXIT_SUCCESS); ++} ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ftl_check.c linux-2.6.24.7/drivers/mtd/mtd-utils/ftl_check.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ftl_check.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ftl_check.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,232 @@ ++/* Ported to MTD system. ++ * Based on: ++ */ ++/*====================================================================== ++ ++ Utility to create an FTL partition in a memory region ++ ++ ftl_check.c 1.10 1999/10/25 20:01:35 ++ ++ The contents of this file are subject to the Mozilla Public ++ License Version 1.1 (the "License"); you may not use this file ++ except in compliance with the License. You may obtain a copy of ++ the License at http://www.mozilla.org/MPL/ ++ ++ Software distributed under the License is distributed on an "AS ++ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++ implied. See the License for the specific language governing ++ rights and limitations under the License. ++ ++ The initial developer of the original code is David A. Hinds ++ . Portions created by David A. Hinds ++ are Copyright (C) 1999 David A. Hinds. All Rights Reserved. ++ ++ Alternatively, the contents of this file may be used under the ++ terms of the GNU Public License version 2 (the "GPL"), in which ++ case the provisions of the GPL are applicable instead of the ++ above. If you wish to allow the use of your version of this file ++ only under the terms of the GPL and not to allow others to use ++ your version of this file under the MPL, indicate your decision ++ by deleting the provisions above and replace them with the notice ++ and other provisions required by the GPL. If you do not delete ++ the provisions above, a recipient may use your version of this ++ file under either the MPL or the GPL. ++ ++ ======================================================================*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#if __BYTE_ORDER == __LITTLE_ENDIAN ++# define TO_LE32(x) (x) ++# define TO_LE16(x) (x) ++#elif __BYTE_ORDER == __BIG_ENDIAN ++# define TO_LE32(x) (bswap_32(x)) ++# define TO_LE16(x) (bswap_16(x)) ++#else ++# error cannot detect endianess ++#endif ++ ++#define FROM_LE32(x) TO_LE32(x) ++#define FROM_LE16(x) TO_LE16(x) ++ ++/*====================================================================*/ ++ ++static void print_size(u_int s) ++{ ++ if ((s > 0x100000) && ((s % 0x100000) == 0)) ++ printf("%d mb", s / 0x100000); ++ else if ((s > 0x400) && ((s % 0x400) == 0)) ++ printf("%d kb", s / 0x400); ++ else ++ printf("%d bytes", s); ++} ++ ++/*====================================================================*/ ++ ++static void check_partition(int fd, int verbose) ++{ ++ mtd_info_t mtd; ++ erase_unit_header_t hdr, hdr2; ++ u_int i, j, nbam, *bam; ++ int control, data, free, deleted; ++ ++ /* Get partition size, block size */ ++ if (ioctl(fd, MEMGETINFO, &mtd) != 0) { ++ perror("get info failed"); ++ return; ++ } ++ ++ printf("Memory region info:\n"); ++ printf(" Region size = "); ++ print_size(mtd.size); ++ printf(" Erase block size = "); ++ print_size(mtd.erasesize); ++ printf("\n\n"); ++ ++ for (i = 0; i < mtd.size/mtd.erasesize; i++) { ++ if (lseek(fd, (i * mtd.erasesize), SEEK_SET) == -1) { ++ perror("seek failed"); ++ break; ++ } ++ read(fd, &hdr, sizeof(hdr)); ++ if ((FROM_LE32(hdr.FormattedSize) > 0) && ++ (FROM_LE32(hdr.FormattedSize) <= mtd.size) && ++ (FROM_LE16(hdr.NumEraseUnits) > 0) && ++ (FROM_LE16(hdr.NumEraseUnits) <= mtd.size/mtd.erasesize)) ++ break; ++ } ++ if (i == mtd.size/mtd.erasesize) { ++ fprintf(stderr, "No valid erase unit headers!\n"); ++ return; ++ } ++ ++ printf("Partition header:\n"); ++ printf(" Formatted size = "); ++ print_size(FROM_LE32(hdr.FormattedSize)); ++ printf(", erase units = %d, transfer units = %d\n", ++ FROM_LE16(hdr.NumEraseUnits), hdr.NumTransferUnits); ++ printf(" Erase unit size = "); ++ print_size(1 << hdr.EraseUnitSize); ++ printf(", virtual block size = "); ++ print_size(1 << hdr.BlockSize); ++ printf("\n"); ++ ++ /* Create basic block allocation table for control blocks */ ++ nbam = (mtd.erasesize >> hdr.BlockSize); ++ bam = malloc(nbam * sizeof(u_int)); ++ ++ for (i = 0; i < FROM_LE16(hdr.NumEraseUnits); i++) { ++ if (lseek(fd, (i << hdr.EraseUnitSize), SEEK_SET) == -1) { ++ perror("seek failed"); ++ break; ++ } ++ if (read(fd, &hdr2, sizeof(hdr2)) == -1) { ++ perror("read failed"); ++ break; ++ } ++ printf("\nErase unit %d:\n", i); ++ if ((hdr2.FormattedSize != hdr.FormattedSize) || ++ (hdr2.NumEraseUnits != hdr.NumEraseUnits) || ++ (hdr2.SerialNumber != hdr.SerialNumber)) ++ printf(" Erase unit header is corrupt.\n"); ++ else if (FROM_LE16(hdr2.LogicalEUN) == 0xffff) ++ printf(" Transfer unit, erase count = %d\n", FROM_LE32(hdr2.EraseCount)); ++ else { ++ printf(" Logical unit %d, erase count = %d\n", ++ FROM_LE16(hdr2.LogicalEUN), FROM_LE32(hdr2.EraseCount)); ++ if (lseek(fd, (i << hdr.EraseUnitSize)+FROM_LE32(hdr.BAMOffset), ++ SEEK_SET) == -1) { ++ perror("seek failed"); ++ break; ++ } ++ if (read(fd, bam, nbam * sizeof(u_int)) == -1) { ++ perror("read failed"); ++ break; ++ } ++ free = deleted = control = data = 0; ++ for (j = 0; j < nbam; j++) { ++ if (BLOCK_FREE(FROM_LE32(bam[j]))) ++ free++; ++ else if (BLOCK_DELETED(FROM_LE32(bam[j]))) ++ deleted++; ++ else switch (BLOCK_TYPE(FROM_LE32(bam[j]))) { ++ case BLOCK_CONTROL: control++; break; ++ case BLOCK_DATA: data++; break; ++ default: break; ++ } ++ } ++ printf(" Block allocation: %d control, %d data, %d free," ++ " %d deleted\n", control, data, free, deleted); ++ } ++ } ++} /* format_partition */ ++ ++/* Show usage information */ ++void showusage(char *pname) ++{ ++ fprintf(stderr, "usage: %s [-v] device\n", pname); ++ fprintf(stderr, "-v verbose messages\n"); ++} ++ ++/*====================================================================*/ ++ ++int main(int argc, char *argv[]) ++{ ++ int verbose; ++ int optch, errflg, fd; ++ struct stat buf; ++ ++ errflg = 0; ++ verbose = 0; ++ while ((optch = getopt(argc, argv, "vh")) != -1) { ++ switch (optch) { ++ case 'h': ++ errflg = 1; break; ++ case 'v': ++ verbose = 1; break; ++ default: ++ errflg = -1; break; ++ } ++ } ++ if (errflg || (optind != argc-1)) { ++ showusage(argv[0]); ++ exit(errflg > 0 ? 0 : EXIT_FAILURE); ++ } ++ ++ if (stat(argv[optind], &buf) != 0) { ++ perror("status check failed"); ++ exit(EXIT_FAILURE); ++ } ++ if (!(buf.st_mode & S_IFCHR)) { ++ fprintf(stderr, "%s is not a character special device\n", ++ argv[optind]); ++ exit(EXIT_FAILURE); ++ } ++ fd = open(argv[optind], O_RDONLY); ++ if (fd == -1) { ++ perror("open failed"); ++ exit(EXIT_FAILURE); ++ } ++ ++ check_partition(fd, verbose); ++ close(fd); ++ ++ exit(EXIT_SUCCESS); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ftl_format.c linux-2.6.24.7/drivers/mtd/mtd-utils/ftl_format.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ftl_format.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ftl_format.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,342 @@ ++/* Ported to MTD system. ++ * Based on: ++ */ ++/*====================================================================== ++ ++ Utility to create an FTL partition in a memory region ++ ++ ftl_format.c 1.13 1999/10/25 20:01:35 ++ ++ The contents of this file are subject to the Mozilla Public ++ License Version 1.1 (the "License"); you may not use this file ++ except in compliance with the License. You may obtain a copy of ++ the License at http://www.mozilla.org/MPL/ ++ ++ Software distributed under the License is distributed on an "AS ++ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++ implied. See the License for the specific language governing ++ rights and limitations under the License. ++ ++ The initial developer of the original code is David A. Hinds ++ . Portions created by David A. Hinds ++ are Copyright (C) 1999 David A. Hinds. All Rights Reserved. ++ ++ Alternatively, the contents of this file may be used under the ++ terms of the GNU Public License version 2 (the "GPL"), in which ++ case the provisions of the GPL are applicable instead of the ++ above. If you wish to allow the use of your version of this file ++ only under the terms of the GPL and not to allow others to use ++ your version of this file under the MPL, indicate your decision ++ by deleting the provisions above and replace them with the notice ++ and other provisions required by the GPL. If you do not delete ++ the provisions above, a recipient may use your version of this ++ file under either the MPL or the GPL. ++ ++ ======================================================================*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#if __BYTE_ORDER == __LITTLE_ENDIAN ++# define TO_LE32(x) (x) ++# define TO_LE16(x) (x) ++#elif __BYTE_ORDER == __BIG_ENDIAN ++# define TO_LE32(x) (bswap_32(x)) ++# define TO_LE16(x) (bswap_16(x)) ++#else ++# error cannot detect endianess ++#endif ++ ++#define FROM_LE32(x) TO_LE32(x) ++#define FROM_LE16(x) TO_LE16(x) ++ ++/*====================================================================*/ ++ ++static void print_size(u_int s) ++{ ++ if ((s > 0x100000) && ((s % 0x100000) == 0)) ++ printf("%d mb", s / 0x100000); ++ else if ((s > 0x400) && ((s % 0x400) == 0)) ++ printf("%d kb", s / 0x400); ++ else ++ printf("%d bytes", s); ++} ++ ++/*====================================================================*/ ++ ++static const char LinkTarget[] = { ++ 0x13, 0x03, 'C', 'I', 'S' ++}; ++static const char DataOrg[] = { ++ 0x46, 0x39, 0x00, 'F', 'T', 'L', '1', '0', '0', 0x00 ++}; ++ ++static void build_header(erase_unit_header_t *hdr, u_int RegionSize, ++ u_int BlockSize, u_int Spare, int Reserve, ++ u_int BootSize) ++{ ++ u_int i, BootUnits, nbam, __FormattedSize; ++ ++ /* Default everything to the erased state */ ++ memset(hdr, 0xff, sizeof(*hdr)); ++ memcpy(hdr->LinkTargetTuple, LinkTarget, 5); ++ memcpy(hdr->DataOrgTuple, DataOrg, 10); ++ hdr->EndTuple[0] = hdr->EndTuple[1] = 0xff; ++ BootSize = (BootSize + (BlockSize-1)) & ~(BlockSize-1); ++ BootUnits = BootSize / BlockSize; ++ ++ /* We only support 512-byte blocks */ ++ hdr->BlockSize = 9; ++ hdr->EraseUnitSize = 0; ++ for (i = BlockSize; i > 1; i >>= 1) ++ hdr->EraseUnitSize++; ++ hdr->EraseCount = TO_LE32(0); ++ hdr->FirstPhysicalEUN = TO_LE16(BootUnits); ++ hdr->NumEraseUnits = TO_LE16((RegionSize - BootSize) >> hdr->EraseUnitSize); ++ hdr->NumTransferUnits = Spare; ++ __FormattedSize = RegionSize - ((Spare + BootUnits) << hdr->EraseUnitSize); ++ /* Leave a little bit of space between the CIS and BAM */ ++ hdr->BAMOffset = TO_LE32(0x80); ++ /* Adjust size to account for BAM space */ ++ nbam = ((1 << (hdr->EraseUnitSize - hdr->BlockSize)) * sizeof(u_int) ++ + FROM_LE32(hdr->BAMOffset) + (1 << hdr->BlockSize) - 1) >> hdr->BlockSize; ++ ++ __FormattedSize -= ++ (FROM_LE16(hdr->NumEraseUnits) - Spare) * (nbam << hdr->BlockSize); ++ __FormattedSize -= ((__FormattedSize * Reserve / 100) & ~0xfff); ++ ++ hdr->FormattedSize = TO_LE32(__FormattedSize); ++ ++ /* hdr->FirstVMAddress defaults to erased state */ ++ hdr->NumVMPages = TO_LE16(0); ++ hdr->Flags = 0; ++ /* hdr->Code defaults to erased state */ ++ hdr->SerialNumber = TO_LE32(time(NULL)); ++ /* hdr->AltEUHOffset defaults to erased state */ ++ ++} /* build_header */ ++ ++/*====================================================================*/ ++ ++static int format_partition(int fd, int quiet, int interrogate, ++ u_int spare, int reserve, u_int bootsize) ++{ ++ mtd_info_t mtd; ++ erase_info_t erase; ++ erase_unit_header_t hdr; ++ u_int step, lun, i, nbam, *bam; ++ ++ /* Get partition size, block size */ ++ if (ioctl(fd, MEMGETINFO, &mtd) != 0) { ++ perror("get info failed"); ++ return -1; ++ } ++ ++#if 0 ++ /* Intel Series 100 Flash: skip first block */ ++ if ((region.JedecMfr == 0x89) && (region.JedecInfo == 0xaa) && ++ (bootsize == 0)) { ++ if (!quiet) ++ printf("Skipping first block to protect CIS info...\n"); ++ bootsize = 1; ++ } ++#endif ++ ++ /* Create header */ ++ build_header(&hdr, mtd.size, mtd.erasesize, ++ spare, reserve, bootsize); ++ ++ if (!quiet) { ++ printf("Partition size = "); ++ print_size(mtd.size); ++ printf(", erase unit size = "); ++ print_size(mtd.erasesize); ++ printf(", %d transfer units\n", spare); ++ if (bootsize != 0) { ++ print_size(FROM_LE16(hdr.FirstPhysicalEUN) << hdr.EraseUnitSize); ++ printf(" allocated for boot image\n"); ++ } ++ printf("Reserved %d%%, formatted size = ", reserve); ++ print_size(FROM_LE32(hdr.FormattedSize)); ++ printf("\n"); ++ fflush(stdout); ++ } ++ ++ if (interrogate) { ++ char str[3]; ++ printf("This will destroy all data on the target device. " ++ "Confirm (y/n): "); ++ if (fgets(str, 3, stdin) == NULL) ++ return -1; ++ if ((strcmp(str, "y\n") != 0) && (strcmp(str, "Y\n") != 0)) ++ return -1; ++ } ++ ++ /* Create basic block allocation table for control blocks */ ++ nbam = ((mtd.erasesize >> hdr.BlockSize) * sizeof(u_int) ++ + FROM_LE32(hdr.BAMOffset) + (1 << hdr.BlockSize) - 1) >> hdr.BlockSize; ++ bam = malloc(nbam * sizeof(u_int)); ++ for (i = 0; i < nbam; i++) ++ bam[i] = TO_LE32(BLOCK_CONTROL); ++ ++ /* Erase partition */ ++ if (!quiet) { ++ printf("Erasing all blocks...\n"); ++ fflush(stdout); ++ } ++ erase.length = mtd.erasesize; ++ erase.start = mtd.erasesize * FROM_LE16(hdr.FirstPhysicalEUN); ++ for (i = 0; i < FROM_LE16(hdr.NumEraseUnits); i++) { ++ if (ioctl(fd, MEMERASE, &erase) < 0) { ++ if (!quiet) { ++ putchar('\n'); ++ fflush(stdout); ++ } ++ perror("block erase failed"); ++ return -1; ++ } ++ erase.start += erase.length; ++ if (!quiet) { ++ if (mtd.size <= 0x800000) { ++ if (erase.start % 0x100000) { ++ if (!(erase.start % 0x20000)) putchar('-'); ++ } ++ else putchar('+'); ++ } ++ else { ++ if (erase.start % 0x800000) { ++ if (!(erase.start % 0x100000)) putchar('+'); ++ } ++ else putchar('*'); ++ } ++ fflush(stdout); ++ } ++ } ++ if (!quiet) putchar('\n'); ++ ++ /* Prepare erase units */ ++ if (!quiet) { ++ printf("Writing erase unit headers...\n"); ++ fflush(stdout); ++ } ++ lun = 0; ++ /* Distribute transfer units over the entire region */ ++ step = (spare) ? (FROM_LE16(hdr.NumEraseUnits)/spare) : (FROM_LE16(hdr.NumEraseUnits)+1); ++ for (i = 0; i < FROM_LE16(hdr.NumEraseUnits); i++) { ++ u_int ofs = (i + FROM_LE16(hdr.FirstPhysicalEUN)) << hdr.EraseUnitSize; ++ if (lseek(fd, ofs, SEEK_SET) == -1) { ++ perror("seek failed"); ++ break; ++ } ++ /* Is this a transfer unit? */ ++ if (((i+1) % step) == 0) ++ hdr.LogicalEUN = TO_LE16(0xffff); ++ else { ++ hdr.LogicalEUN = TO_LE16(lun); ++ lun++; ++ } ++ if (write(fd, &hdr, sizeof(hdr)) == -1) { ++ perror("write failed"); ++ break; ++ } ++ if (lseek(fd, ofs + FROM_LE32(hdr.BAMOffset), SEEK_SET) == -1) { ++ perror("seek failed"); ++ break; ++ } ++ if (write(fd, bam, nbam * sizeof(u_int)) == -1) { ++ perror("write failed"); ++ break; ++ } ++ } ++ if (i < FROM_LE16(hdr.NumEraseUnits)) ++ return -1; ++ else ++ return 0; ++} /* format_partition */ ++ ++/*====================================================================*/ ++ ++int main(int argc, char *argv[]) ++{ ++ int quiet, interrogate, reserve; ++ int optch, errflg, fd, ret; ++ u_int spare, bootsize; ++ char *s; ++ extern char *optarg; ++ struct stat buf; ++ ++ quiet = 0; ++ interrogate = 0; ++ spare = 1; ++ reserve = 5; ++ errflg = 0; ++ bootsize = 0; ++ ++ while ((optch = getopt(argc, argv, "qir:s:b:")) != -1) { ++ switch (optch) { ++ case 'q': ++ quiet = 1; break; ++ case 'i': ++ interrogate = 1; break; ++ case 's': ++ spare = strtoul(optarg, NULL, 0); break; ++ case 'r': ++ reserve = strtoul(optarg, NULL, 0); break; ++ case 'b': ++ bootsize = strtoul(optarg, &s, 0); ++ if ((*s == 'k') || (*s == 'K')) ++ bootsize *= 1024; ++ break; ++ default: ++ errflg = 1; break; ++ } ++ } ++ if (errflg || (optind != argc-1)) { ++ fprintf(stderr, "usage: %s [-q] [-i] [-s spare-blocks]" ++ " [-r reserve-percent] [-b bootsize] device\n", argv[0]); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (stat(argv[optind], &buf) != 0) { ++ perror("status check failed"); ++ exit(EXIT_FAILURE); ++ } ++ if (!(buf.st_mode & S_IFCHR)) { ++ fprintf(stderr, "%s is not a character special device\n", ++ argv[optind]); ++ exit(EXIT_FAILURE); ++ } ++ fd = open(argv[optind], O_RDWR); ++ if (fd == -1) { ++ perror("open failed"); ++ exit(EXIT_FAILURE); ++ } ++ ++ ret = format_partition(fd, quiet, interrogate, spare, reserve, ++ bootsize); ++ if (!quiet) { ++ if (ret) ++ printf("format failed.\n"); ++ else ++ printf("format successful.\n"); ++ } ++ close(fd); ++ ++ exit((ret) ? EXIT_FAILURE : EXIT_SUCCESS); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/linux/jffs2.h linux-2.6.24.7/drivers/mtd/mtd-utils/include/linux/jffs2.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/linux/jffs2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/linux/jffs2.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,218 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2001-2003 Red Hat, Inc. ++ * ++ * Created by David Woodhouse ++ * ++ * For licensing information, see the file 'LICENCE' in the ++ * jffs2 directory. ++ * ++ * $Id: jffs2.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ * ++ */ ++ ++#ifndef __LINUX_JFFS2_H__ ++#define __LINUX_JFFS2_H__ ++ ++/* You must include something which defines the C99 uintXX_t types. ++ We don't do it from here because this file is used in too many ++ different environments. */ ++ ++#define JFFS2_SUPER_MAGIC 0x72b6 ++ ++/* Values we may expect to find in the 'magic' field */ ++#define JFFS2_OLD_MAGIC_BITMASK 0x1984 ++#define JFFS2_MAGIC_BITMASK 0x1985 ++#define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */ ++#define JFFS2_EMPTY_BITMASK 0xffff ++#define JFFS2_DIRTY_BITMASK 0x0000 ++ ++/* Summary node MAGIC marker */ ++#define JFFS2_SUM_MAGIC 0x02851885 ++ ++/* We only allow a single char for length, and 0xFF is empty flash so ++ we don't want it confused with a real length. Hence max 254. ++*/ ++#define JFFS2_MAX_NAME_LEN 254 ++ ++/* How small can we sensibly write nodes? */ ++#define JFFS2_MIN_DATA_LEN 128 ++ ++#define JFFS2_COMPR_NONE 0x00 ++#define JFFS2_COMPR_ZERO 0x01 ++#define JFFS2_COMPR_RTIME 0x02 ++#define JFFS2_COMPR_RUBINMIPS 0x03 ++#define JFFS2_COMPR_COPY 0x04 ++#define JFFS2_COMPR_DYNRUBIN 0x05 ++#define JFFS2_COMPR_ZLIB 0x06 ++#define JFFS2_COMPR_LZO 0x07 ++/* Compatibility flags. */ ++#define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ ++#define JFFS2_NODE_ACCURATE 0x2000 ++/* INCOMPAT: Fail to mount the filesystem */ ++#define JFFS2_FEATURE_INCOMPAT 0xc000 ++/* ROCOMPAT: Mount read-only */ ++#define JFFS2_FEATURE_ROCOMPAT 0x8000 ++/* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */ ++#define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000 ++/* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */ ++#define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000 ++ ++#define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1) ++#define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2) ++#define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) ++#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4) ++ ++#define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6) ++ ++#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8) ++#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9) ++ ++/* XATTR Related */ ++#define JFFS2_XPREFIX_USER 1 /* for "user." */ ++#define JFFS2_XPREFIX_SECURITY 2 /* for "security." */ ++#define JFFS2_XPREFIX_ACL_ACCESS 3 /* for "system.posix_acl_access" */ ++#define JFFS2_XPREFIX_ACL_DEFAULT 4 /* for "system.posix_acl_default" */ ++#define JFFS2_XPREFIX_TRUSTED 5 /* for "trusted.*" */ ++ ++#define JFFS2_ACL_VERSION 0x0001 ++ ++// Maybe later... ++//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) ++//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4) ++ ++ ++#define JFFS2_INO_FLAG_PREREAD 1 /* Do read_inode() for this one at ++ mount time, don't wait for it to ++ happen later */ ++#define JFFS2_INO_FLAG_USERCOMPR 2 /* User has requested a specific ++ compression type */ ++ ++ ++/* These can go once we've made sure we've caught all uses without ++ byteswapping */ ++ ++typedef struct { ++ uint32_t v32; ++} __attribute__((packed)) jint32_t; ++ ++typedef struct { ++ uint32_t m; ++} __attribute__((packed)) jmode_t; ++ ++typedef struct { ++ uint16_t v16; ++} __attribute__((packed)) jint16_t; ++ ++struct jffs2_unknown_node ++{ ++ /* All start like this */ ++ jint16_t magic; ++ jint16_t nodetype; ++ jint32_t totlen; /* So we can skip over nodes we don't grok */ ++ jint32_t hdr_crc; ++} __attribute__((packed)); ++ ++struct jffs2_raw_dirent ++{ ++ jint16_t magic; ++ jint16_t nodetype; /* == JFFS2_NODETYPE_DIRENT */ ++ jint32_t totlen; ++ jint32_t hdr_crc; ++ jint32_t pino; ++ jint32_t version; ++ jint32_t ino; /* == zero for unlink */ ++ jint32_t mctime; ++ uint8_t nsize; ++ uint8_t type; ++ uint8_t unused[2]; ++ jint32_t node_crc; ++ jint32_t name_crc; ++ uint8_t name[0]; ++} __attribute__((packed)); ++ ++/* The JFFS2 raw inode structure: Used for storage on physical media. */ ++/* The uid, gid, atime, mtime and ctime members could be longer, but ++ are left like this for space efficiency. If and when people decide ++ they really need them extended, it's simple enough to add support for ++ a new type of raw node. ++*/ ++struct jffs2_raw_inode ++{ ++ jint16_t magic; /* A constant magic number. */ ++ jint16_t nodetype; /* == JFFS2_NODETYPE_INODE */ ++ jint32_t totlen; /* Total length of this node (inc data, etc.) */ ++ jint32_t hdr_crc; ++ jint32_t ino; /* Inode number. */ ++ jint32_t version; /* Version number. */ ++ jmode_t mode; /* The file's type or mode. */ ++ jint16_t uid; /* The file's owner. */ ++ jint16_t gid; /* The file's group. */ ++ jint32_t isize; /* Total resultant size of this inode (used for truncations) */ ++ jint32_t atime; /* Last access time. */ ++ jint32_t mtime; /* Last modification time. */ ++ jint32_t ctime; /* Change time. */ ++ jint32_t offset; /* Where to begin to write. */ ++ jint32_t csize; /* (Compressed) data size */ ++ jint32_t dsize; /* Size of the node's data. (after decompression) */ ++ uint8_t compr; /* Compression algorithm used */ ++ uint8_t usercompr; /* Compression algorithm requested by the user */ ++ jint16_t flags; /* See JFFS2_INO_FLAG_* */ ++ jint32_t data_crc; /* CRC for the (compressed) data. */ ++ jint32_t node_crc; /* CRC for the raw inode (excluding data) */ ++ uint8_t data[0]; ++} __attribute__((packed)); ++ ++struct jffs2_raw_xattr { ++ jint16_t magic; ++ jint16_t nodetype; /* = JFFS2_NODETYPE_XATTR */ ++ jint32_t totlen; ++ jint32_t hdr_crc; ++ jint32_t xid; /* XATTR identifier number */ ++ jint32_t version; ++ uint8_t xprefix; ++ uint8_t name_len; ++ jint16_t value_len; ++ jint32_t data_crc; ++ jint32_t node_crc; ++ uint8_t data[0]; ++} __attribute__((packed)); ++ ++struct jffs2_raw_xref ++{ ++ jint16_t magic; ++ jint16_t nodetype; /* = JFFS2_NODETYPE_XREF */ ++ jint32_t totlen; ++ jint32_t hdr_crc; ++ jint32_t ino; /* inode number */ ++ jint32_t xid; /* XATTR identifier number */ ++ jint32_t xseqno; /* xref sequencial number */ ++ jint32_t node_crc; ++} __attribute__((packed)); ++ ++struct jffs2_raw_summary ++{ ++ jint16_t magic; ++ jint16_t nodetype; /* = JFFS2_NODETYPE_SUMMARY */ ++ jint32_t totlen; ++ jint32_t hdr_crc; ++ jint32_t sum_num; /* number of sum entries*/ ++ jint32_t cln_mkr; /* clean marker size, 0 = no cleanmarker */ ++ jint32_t padded; /* sum of the size of padding nodes */ ++ jint32_t sum_crc; /* summary information crc */ ++ jint32_t node_crc; /* node crc */ ++ jint32_t sum[0]; /* inode summary info */ ++} __attribute__((packed)); ++ ++union jffs2_node_union ++{ ++ struct jffs2_raw_inode i; ++ struct jffs2_raw_dirent d; ++ struct jffs2_raw_xattr x; ++ struct jffs2_raw_xref r; ++ struct jffs2_raw_summary s; ++ struct jffs2_unknown_node u; ++}; ++ ++#endif /* __LINUX_JFFS2_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/ftl-user.h linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/ftl-user.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/ftl-user.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/ftl-user.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,76 @@ ++/* ++ * $Id: ftl-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ * ++ * Derived from (and probably identical to): ++ * ftl.h 1.7 1999/10/25 20:23:17 ++ * ++ * The contents of this file are subject to the Mozilla Public License ++ * Version 1.1 (the "License"); you may not use this file except in ++ * compliance with the License. You may obtain a copy of the License ++ * at http://www.mozilla.org/MPL/ ++ * ++ * Software distributed under the License is distributed on an "AS IS" ++ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See ++ * the License for the specific language governing rights and ++ * limitations under the License. ++ * ++ * The initial developer of the original code is David A. Hinds ++ * . Portions created by David A. Hinds ++ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. ++ * ++ * Alternatively, the contents of this file may be used under the ++ * terms of the GNU General Public License version 2 (the "GPL"), in ++ * which case the provisions of the GPL are applicable instead of the ++ * above. If you wish to allow the use of your version of this file ++ * only under the terms of the GPL and not to allow others to use ++ * your version of this file under the MPL, indicate your decision by ++ * deleting the provisions above and replace them with the notice and ++ * other provisions required by the GPL. If you do not delete the ++ * provisions above, a recipient may use your version of this file ++ * under either the MPL or the GPL. ++ */ ++ ++#ifndef __MTD_FTL_USER_H__ ++#define __MTD_FTL_USER_H__ ++ ++typedef struct erase_unit_header_t { ++ u_int8_t LinkTargetTuple[5]; ++ u_int8_t DataOrgTuple[10]; ++ u_int8_t NumTransferUnits; ++ u_int32_t EraseCount; ++ u_int16_t LogicalEUN; ++ u_int8_t BlockSize; ++ u_int8_t EraseUnitSize; ++ u_int16_t FirstPhysicalEUN; ++ u_int16_t NumEraseUnits; ++ u_int32_t FormattedSize; ++ u_int32_t FirstVMAddress; ++ u_int16_t NumVMPages; ++ u_int8_t Flags; ++ u_int8_t Code; ++ u_int32_t SerialNumber; ++ u_int32_t AltEUHOffset; ++ u_int32_t BAMOffset; ++ u_int8_t Reserved[12]; ++ u_int8_t EndTuple[2]; ++} erase_unit_header_t; ++ ++/* Flags in erase_unit_header_t */ ++#define HIDDEN_AREA 0x01 ++#define REVERSE_POLARITY 0x02 ++#define DOUBLE_BAI 0x04 ++ ++/* Definitions for block allocation information */ ++ ++#define BLOCK_FREE(b) ((b) == 0xffffffff) ++#define BLOCK_DELETED(b) (((b) == 0) || ((b) == 0xfffffffe)) ++ ++#define BLOCK_TYPE(b) ((b) & 0x7f) ++#define BLOCK_ADDRESS(b) ((b) & ~0x7f) ++#define BLOCK_NUMBER(b) ((b) >> 9) ++#define BLOCK_CONTROL 0x30 ++#define BLOCK_DATA 0x40 ++#define BLOCK_REPLACEMENT 0x60 ++#define BLOCK_BAD 0x70 ++ ++#endif /* __MTD_FTL_USER_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/inftl-user.h linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/inftl-user.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/inftl-user.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/inftl-user.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,91 @@ ++/* ++ * $Id: inftl-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ * ++ * Parts of INFTL headers shared with userspace ++ * ++ */ ++ ++#ifndef __MTD_INFTL_USER_H__ ++#define __MTD_INFTL_USER_H__ ++ ++#define OSAK_VERSION 0x5120 ++#define PERCENTUSED 98 ++ ++#define SECTORSIZE 512 ++ ++/* Block Control Information */ ++ ++struct inftl_bci { ++ uint8_t ECCsig[6]; ++ uint8_t Status; ++ uint8_t Status1; ++} __attribute__((packed)); ++ ++struct inftl_unithead1 { ++ uint16_t virtualUnitNo; ++ uint16_t prevUnitNo; ++ uint8_t ANAC; ++ uint8_t NACs; ++ uint8_t parityPerField; ++ uint8_t discarded; ++} __attribute__((packed)); ++ ++struct inftl_unithead2 { ++ uint8_t parityPerField; ++ uint8_t ANAC; ++ uint16_t prevUnitNo; ++ uint16_t virtualUnitNo; ++ uint8_t NACs; ++ uint8_t discarded; ++} __attribute__((packed)); ++ ++struct inftl_unittail { ++ uint8_t Reserved[4]; ++ uint16_t EraseMark; ++ uint16_t EraseMark1; ++} __attribute__((packed)); ++ ++union inftl_uci { ++ struct inftl_unithead1 a; ++ struct inftl_unithead2 b; ++ struct inftl_unittail c; ++}; ++ ++struct inftl_oob { ++ struct inftl_bci b; ++ union inftl_uci u; ++}; ++ ++ ++/* INFTL Media Header */ ++ ++struct INFTLPartition { ++ __u32 virtualUnits; ++ __u32 firstUnit; ++ __u32 lastUnit; ++ __u32 flags; ++ __u32 spareUnits; ++ __u32 Reserved0; ++ __u32 Reserved1; ++} __attribute__((packed)); ++ ++struct INFTLMediaHeader { ++ char bootRecordID[8]; ++ __u32 NoOfBootImageBlocks; ++ __u32 NoOfBinaryPartitions; ++ __u32 NoOfBDTLPartitions; ++ __u32 BlockMultiplierBits; ++ __u32 FormatFlags; ++ __u32 OsakVersion; ++ __u32 PercentUsed; ++ struct INFTLPartition Partitions[4]; ++} __attribute__((packed)); ++ ++/* Partition flag types */ ++#define INFTL_BINARY 0x20000000 ++#define INFTL_BDTL 0x40000000 ++#define INFTL_LAST 0x80000000 ++ ++#endif /* __MTD_INFTL_USER_H__ */ ++ ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/jffs2-user.h linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/jffs2-user.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/jffs2-user.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/jffs2-user.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,82 @@ ++/* ++ * $Id: jffs2-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ * ++ * JFFS2 definitions for use in user space only ++ */ ++ ++#ifndef __JFFS2_USER_H__ ++#define __JFFS2_USER_H__ ++ ++/* This file is blessed for inclusion by userspace */ ++#include ++#include ++#include ++ ++#undef cpu_to_je16 ++#undef cpu_to_je32 ++#undef cpu_to_jemode ++#undef je16_to_cpu ++#undef je32_to_cpu ++#undef jemode_to_cpu ++ ++extern int target_endian; ++ ++#define t16(x) ({ uint16_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); }) ++#define t32(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); }) ++ ++#define cpu_to_je16(x) ((jint16_t){t16(x)}) ++#define cpu_to_je32(x) ((jint32_t){t32(x)}) ++#define cpu_to_jemode(x) ((jmode_t){t32(x)}) ++ ++#define je16_to_cpu(x) (t16((x).v16)) ++#define je32_to_cpu(x) (t32((x).v32)) ++#define jemode_to_cpu(x) (t32((x).m)) ++ ++#define le16_to_cpu(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_16(x)) ++#define le32_to_cpu(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_32(x)) ++#define cpu_to_le16(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_16(x)) ++#define cpu_to_le32(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_32(x)) ++ ++/* XATTR/POSIX-ACL related definition */ ++/* Namespaces copied from xattr.h and posix_acl_xattr.h */ ++#define XATTR_USER_PREFIX "user." ++#define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1) ++#define XATTR_SECURITY_PREFIX "security." ++#define XATTR_SECURITY_PREFIX_LEN (sizeof (XATTR_SECURITY_PREFIX) - 1) ++#define POSIX_ACL_XATTR_ACCESS "system.posix_acl_access" ++#define POSIX_ACL_XATTR_ACCESS_LEN (sizeof (POSIX_ACL_XATTR_ACCESS) - 1) ++#define POSIX_ACL_XATTR_DEFAULT "system.posix_acl_default" ++#define POSIX_ACL_XATTR_DEFAULT_LEN (sizeof (POSIX_ACL_XATTR_DEFAULT) - 1) ++#define XATTR_TRUSTED_PREFIX "trusted." ++#define XATTR_TRUSTED_PREFIX_LEN (sizeof (XATTR_TRUSTED_PREFIX) - 1) ++ ++struct jffs2_acl_entry { ++ jint16_t e_tag; ++ jint16_t e_perm; ++ jint32_t e_id; ++}; ++ ++struct jffs2_acl_entry_short { ++ jint16_t e_tag; ++ jint16_t e_perm; ++}; ++ ++struct jffs2_acl_header { ++ jint32_t a_version; ++}; ++ ++/* copied from include/linux/posix_acl_xattr.h */ ++#define POSIX_ACL_XATTR_VERSION 0x0002 ++ ++struct posix_acl_xattr_entry { ++ uint16_t e_tag; ++ uint16_t e_perm; ++ uint32_t e_id; ++}; ++ ++struct posix_acl_xattr_header { ++ uint32_t a_version; ++ struct posix_acl_xattr_entry a_entries[0]; ++}; ++ ++#endif /* __JFFS2_USER_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/mtd-abi.h linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/mtd-abi.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/mtd-abi.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/mtd-abi.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,170 @@ ++/* ++ * $Id: mtd-abi.h,v 1.1.1.1 2008-10-30 14:29:21 lhhuang Exp $ ++ * ++ * Portions of MTD ABI definition which are shared by kernel and user space ++ */ ++ ++#ifndef __MTD_ABI_H__ ++#define __MTD_ABI_H__ ++ ++#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into ++ separate files was to avoid #ifdef __KERNEL__ */ ++#define __user ++#endif ++ ++typedef unsigned long long size_mtd_t; ++typedef unsigned long long loff_mtd_t; ++ ++struct erase_info_user { ++ uint64_t start; ++ uint64_t length; ++}; ++ ++struct mtd_oob_buf { ++ uint32_t start; ++ uint32_t length; ++ unsigned char __user *ptr; ++}; ++ ++struct mtd_page_buf { ++ uint32_t start; //page start address ++ uint32_t ooblength; ++ uint32_t datlength; ++ unsigned char __user *oobptr; ++ unsigned char __user *datptr; ++}; ++ ++#define MTD_ABSENT 0 ++#define MTD_RAM 1 ++#define MTD_ROM 2 ++#define MTD_NORFLASH 3 ++#define MTD_NANDFLASH 4 ++#define MTD_DATAFLASH 6 ++#define MTD_UBIVOLUME 7 ++ ++#define MTD_WRITEABLE 0x400 /* Device is writeable */ ++#define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ ++#define MTD_NO_ERASE 0x1000 /* No erase necessary */ ++#define MTD_STUPID_LOCK 0x2000 /* Always locked after reset */ ++ ++// Some common devices / combinations of capabilities ++#define MTD_CAP_ROM 0 ++#define MTD_CAP_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE) ++#define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE) ++#define MTD_CAP_NANDFLASH (MTD_WRITEABLE) ++ ++/* ECC byte placement */ ++#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended) ++#define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode) ++#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme ++#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read) ++#define MTD_NANDECC_AUTOPL_USR 4 // Use the given autoplacement scheme rather than using the default ++ ++/* OTP mode selection */ ++#define MTD_OTP_OFF 0 ++#define MTD_OTP_FACTORY 1 ++#define MTD_OTP_USER 2 ++ ++struct mtd_info_user { ++ uint8_t type; ++ uint32_t flags; ++ uint64_t size; // Total size of the MTD ++ uint32_t erasesize; ++ uint32_t writesize; ++ uint32_t oobsize; // Amount of OOB data per block (e.g. 16) ++ /* The below two fields are obsolete and broken, do not use them ++ * (TODO: remove at some point) */ ++ uint32_t ecctype; ++ uint32_t eccsize; ++}; ++ ++struct region_info_user { ++ uint64_t offset; /* At which this region starts, ++ * from the beginning of the MTD */ ++ uint32_t erasesize; /* For this region */ ++ uint32_t numblocks; /* Number of blocks in this region */ ++ uint32_t regionindex; ++}; ++ ++struct otp_info { ++ uint32_t start; ++ uint32_t length; ++ uint32_t locked; ++}; ++ ++#define MEMGETINFO _IOR('M', 1, struct mtd_info_user) ++#define MEMERASE _IOW('M', 2, struct erase_info_user) ++#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) ++#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) ++#define MEMLOCK _IOW('M', 5, struct erase_info_user) ++#define MEMUNLOCK _IOW('M', 6, struct erase_info_user) ++#define MEMGETREGIONCOUNT _IOR('M', 7, int) ++#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) ++#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) ++#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo) ++#define MEMGETBADBLOCK _IOW('M', 11, loff_mtd_t) ++#define MEMSETBADBLOCK _IOW('M', 12, loff_mtd_t) ++#define OTPSELECT _IOR('M', 13, int) ++#define OTPGETREGIONCOUNT _IOW('M', 14, int) ++#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info) ++#define OTPLOCK _IOR('M', 16, struct otp_info) ++#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) ++#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) ++#define MTDFILEMODE _IO('M', 19) ++#define MEMWRITEPAGE _IOWR('M', 20, struct mtd_page_buf) ++ ++/* ++ * Obsolete legacy interface. Keep it in order not to break userspace ++ * interfaces ++ */ ++struct nand_oobinfo { ++ uint32_t useecc; ++ uint32_t eccbytes; ++ uint32_t oobfree[8][2]; ++ uint32_t eccpos[104]; /* more fields(13*8) are required for ++ * 8-bit BCH ECC and 4KB pagesize nand, by Regen */ ++}; ++ ++struct nand_oobfree { ++ uint32_t offset; ++ uint32_t length; ++}; ++ ++#define MTD_MAX_OOBFREE_ENTRIES 8 ++/* ++ * ECC layout control structure. Exported to userspace for ++ * diagnosis and to allow creation of raw images ++ */ ++struct nand_ecclayout { ++ uint32_t eccbytes; ++ uint32_t eccpos[128]; ++ uint32_t oobavail; ++ struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; ++}; ++ ++/** ++ * struct mtd_ecc_stats - error correction stats ++ * ++ * @corrected: number of corrected bits ++ * @failed: number of uncorrectable errors ++ * @badblocks: number of bad blocks in this partition ++ * @bbtblocks: number of blocks reserved for bad block tables ++ */ ++struct mtd_ecc_stats { ++ uint32_t corrected; ++ uint32_t failed; ++ uint32_t badblocks; ++ uint32_t bbtblocks; ++}; ++ ++/* ++ * Read/write file modes for access to MTD ++ */ ++enum mtd_file_modes { ++ MTD_MODE_NORMAL = MTD_OTP_OFF, ++ MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY, ++ MTD_MODE_OTP_USER = MTD_OTP_USER, ++ MTD_MODE_RAW, ++}; ++ ++#endif /* __MTD_ABI_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/mtd-user.h linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/mtd-user.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/mtd-user.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/mtd-user.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,21 @@ ++/* ++ * $Id: mtd-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ * ++ * MTD ABI header for use by user space only. ++ */ ++ ++#ifndef __MTD_USER_H__ ++#define __MTD_USER_H__ ++ ++#include ++ ++/* This file is blessed for inclusion by userspace */ ++#include ++ ++typedef struct mtd_info_user mtd_info_t; ++typedef struct erase_info_user erase_info_t; ++typedef struct region_info_user region_info_t; ++typedef struct nand_oobinfo nand_oobinfo_t; ++typedef struct nand_ecclayout nand_ecclayout_t; ++ ++#endif /* __MTD_USER_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/nftl-user.h linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/nftl-user.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/nftl-user.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/nftl-user.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,76 @@ ++/* ++ * $Id: nftl-user.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ * ++ * Parts of NFTL headers shared with userspace ++ * ++ */ ++ ++#ifndef __MTD_NFTL_USER_H__ ++#define __MTD_NFTL_USER_H__ ++ ++/* Block Control Information */ ++ ++struct nftl_bci { ++ unsigned char ECCSig[6]; ++ uint8_t Status; ++ uint8_t Status1; ++}__attribute__((packed)); ++ ++/* Unit Control Information */ ++ ++struct nftl_uci0 { ++ uint16_t VirtUnitNum; ++ uint16_t ReplUnitNum; ++ uint16_t SpareVirtUnitNum; ++ uint16_t SpareReplUnitNum; ++} __attribute__((packed)); ++ ++struct nftl_uci1 { ++ uint32_t WearInfo; ++ uint16_t EraseMark; ++ uint16_t EraseMark1; ++} __attribute__((packed)); ++ ++struct nftl_uci2 { ++ uint16_t FoldMark; ++ uint16_t FoldMark1; ++ uint32_t unused; ++} __attribute__((packed)); ++ ++union nftl_uci { ++ struct nftl_uci0 a; ++ struct nftl_uci1 b; ++ struct nftl_uci2 c; ++}; ++ ++struct nftl_oob { ++ struct nftl_bci b; ++ union nftl_uci u; ++}; ++ ++/* NFTL Media Header */ ++ ++struct NFTLMediaHeader { ++ char DataOrgID[6]; ++ uint16_t NumEraseUnits; ++ uint16_t FirstPhysicalEUN; ++ uint32_t FormattedSize; ++ unsigned char UnitSizeFactor; ++} __attribute__((packed)); ++ ++#define MAX_ERASE_ZONES (8192 - 512) ++ ++#define ERASE_MARK 0x3c69 ++#define SECTOR_FREE 0xff ++#define SECTOR_USED 0x55 ++#define SECTOR_IGNORE 0x11 ++#define SECTOR_DELETED 0x00 ++ ++#define FOLD_MARK_IN_PROGRESS 0x5555 ++ ++#define ZONE_GOOD 0xff ++#define ZONE_BAD_ORIGINAL 0 ++#define ZONE_BAD_MARKED 7 ++ ++ ++#endif /* __MTD_NFTL_USER_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/ubi-header.h linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/ubi-header.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/ubi-header.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/ubi-header.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,372 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Thomas Gleixner ++ * Frank Haverkamp ++ * Oliver Lohmann ++ * Andreas Arnez ++ */ ++ ++/* ++ * This file defines the layout of UBI headers and all the other UBI on-flash ++ * data structures. May be included by user-space. ++ */ ++ ++#ifndef __UBI_HEADER_H__ ++#define __UBI_HEADER_H__ ++ ++#include ++ ++/* The version of UBI images supported by this implementation */ ++#define UBI_VERSION 1 ++ ++/* The highest erase counter value supported by this implementation */ ++#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF ++ ++/* The initial CRC32 value used when calculating CRC checksums */ ++#define UBI_CRC32_INIT 0xFFFFFFFFU ++ ++/* Erase counter header magic number (ASCII "UBI#") */ ++#define UBI_EC_HDR_MAGIC 0x55424923 ++/* Volume identifier header magic number (ASCII "UBI!") */ ++#define UBI_VID_HDR_MAGIC 0x55424921 ++ ++/* ++ * Volume type constants used in the volume identifier header. ++ * ++ * @UBI_VID_DYNAMIC: dynamic volume ++ * @UBI_VID_STATIC: static volume ++ */ ++enum { ++ UBI_VID_DYNAMIC = 1, ++ UBI_VID_STATIC = 2 ++}; ++ ++/* ++ * Volume flags used in the volume table record. ++ * ++ * @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume ++ * ++ * %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume ++ * table. UBI automatically re-sizes the volume which has this flag and makes ++ * the volume to be of largest possible size. This means that if after the ++ * initialization UBI finds out that there are available physical eraseblocks ++ * present on the device, it automatically appends all of them to the volume ++ * (the physical eraseblocks reserved for bad eraseblocks handling and other ++ * reserved physical eraseblocks are not taken). So, if there is a volume with ++ * the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical ++ * eraseblocks will be zero after UBI is loaded, because all of them will be ++ * reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared ++ * after the volume had been initialized. ++ * ++ * The auto-resize feature is useful for device production purposes. For ++ * example, different NAND flash chips may have different amount of initial bad ++ * eraseblocks, depending of particular chip instance. Manufacturers of NAND ++ * chips usually guarantee that the amount of initial bad eraseblocks does not ++ * exceed certain percent, e.g. 2%. When one creates an UBI image which will be ++ * flashed to the end devices in production, he does not know the exact amount ++ * of good physical eraseblocks the NAND chip on the device will have, but this ++ * number is required to calculate the volume sized and put them to the volume ++ * table of the UBI image. In this case, one of the volumes (e.g., the one ++ * which will store the root file system) is marked as "auto-resizable", and ++ * UBI will adjust its size on the first boot if needed. ++ * ++ * Note, first UBI reserves some amount of physical eraseblocks for bad ++ * eraseblock handling, and then re-sizes the volume, not vice-versa. This ++ * means that the pool of reserved physical eraseblocks will always be present. ++ */ ++enum { ++ UBI_VTBL_AUTORESIZE_FLG = 0x01, ++}; ++ ++/* ++ * Compatibility constants used by internal volumes. ++ * ++ * @UBI_COMPAT_DELETE: delete this internal volume before anything is written ++ * to the flash ++ * @UBI_COMPAT_RO: attach this device in read-only mode ++ * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its ++ * physical eraseblocks, don't allow the wear-leveling unit to move them ++ * @UBI_COMPAT_REJECT: reject this UBI image ++ */ ++enum { ++ UBI_COMPAT_DELETE = 1, ++ UBI_COMPAT_RO = 2, ++ UBI_COMPAT_PRESERVE = 4, ++ UBI_COMPAT_REJECT = 5 ++}; ++ ++/* Sizes of UBI headers */ ++#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr) ++#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr) ++ ++/* Sizes of UBI headers without the ending CRC */ ++#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(uint32_t)) ++#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(uint32_t)) ++ ++/** ++ * struct ubi_ec_hdr - UBI erase counter header. ++ * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC) ++ * @version: version of UBI implementation which is supposed to accept this ++ * UBI image ++ * @padding1: reserved for future, zeroes ++ * @ec: the erase counter ++ * @vid_hdr_offset: where the VID header starts ++ * @data_offset: where the user data start ++ * @padding2: reserved for future, zeroes ++ * @hdr_crc: erase counter header CRC checksum ++ * ++ * The erase counter header takes 64 bytes and has a plenty of unused space for ++ * future usage. The unused fields are zeroed. The @version field is used to ++ * indicate the version of UBI implementation which is supposed to be able to ++ * work with this UBI image. If @version is greater then the current UBI ++ * version, the image is rejected. This may be useful in future if something ++ * is changed radically. This field is duplicated in the volume identifier ++ * header. ++ * ++ * The @vid_hdr_offset and @data_offset fields contain the offset of the the ++ * volume identifier header and user data, relative to the beginning of the ++ * physical eraseblock. These values have to be the same for all physical ++ * eraseblocks. ++ */ ++struct ubi_ec_hdr { ++ uint32_t magic; ++ uint8_t version; ++ uint8_t padding1[3]; ++ uint64_t ec; /* Warning: the current limit is 31-bit anyway! */ ++ uint32_t vid_hdr_offset; ++ uint32_t data_offset; ++ uint8_t padding2[36]; ++ uint32_t hdr_crc; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubi_vid_hdr - on-flash UBI volume identifier header. ++ * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) ++ * @version: UBI implementation version which is supposed to accept this UBI ++ * image (%UBI_VERSION) ++ * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) ++ * @copy_flag: if this logical eraseblock was copied from another physical ++ * eraseblock (for wear-leveling reasons) ++ * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, ++ * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) ++ * @vol_id: ID of this volume ++ * @lnum: logical eraseblock number ++ * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be ++ * removed, kept only for not breaking older UBI users) ++ * @data_size: how many bytes of data this logical eraseblock contains ++ * @used_ebs: total number of used logical eraseblocks in this volume ++ * @data_pad: how many bytes at the end of this physical eraseblock are not ++ * used ++ * @data_crc: CRC checksum of the data stored in this logical eraseblock ++ * @padding1: reserved for future, zeroes ++ * @sqnum: sequence number ++ * @padding2: reserved for future, zeroes ++ * @hdr_crc: volume identifier header CRC checksum ++ * ++ * The @sqnum is the value of the global sequence counter at the time when this ++ * VID header was created. The global sequence counter is incremented each time ++ * UBI writes a new VID header to the flash, i.e. when it maps a logical ++ * eraseblock to a new physical eraseblock. The global sequence counter is an ++ * unsigned 64-bit integer and we assume it never overflows. The @sqnum ++ * (sequence number) is used to distinguish between older and newer versions of ++ * logical eraseblocks. ++ * ++ * There are 2 situations when there may be more then one physical eraseblock ++ * corresponding to the same logical eraseblock, i.e., having the same @vol_id ++ * and @lnum values in the volume identifier header. Suppose we have a logical ++ * eraseblock L and it is mapped to the physical eraseblock P. ++ * ++ * 1. Because UBI may erase physical eraseblocks asynchronously, the following ++ * situation is possible: L is asynchronously erased, so P is scheduled for ++ * erasure, then L is written to,i.e. mapped to another physical eraseblock P1, ++ * so P1 is written to, then an unclean reboot happens. Result - there are 2 ++ * physical eraseblocks P and P1 corresponding to the same logical eraseblock ++ * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the ++ * flash. ++ * ++ * 2. From time to time UBI moves logical eraseblocks to other physical ++ * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P ++ * to P1, and an unclean reboot happens before P is physically erased, there ++ * are two physical eraseblocks P and P1 corresponding to L and UBI has to ++ * select one of them when the flash is attached. The @sqnum field says which ++ * PEB is the original (obviously P will have lower @sqnum) and the copy. But ++ * it is not enough to select the physical eraseblock with the higher sequence ++ * number, because the unclean reboot could have happen in the middle of the ++ * copying process, so the data in P is corrupted. It is also not enough to ++ * just select the physical eraseblock with lower sequence number, because the ++ * data there may be old (consider a case if more data was added to P1 after ++ * the copying). Moreover, the unclean reboot may happen when the erasure of P ++ * was just started, so it result in unstable P, which is "mostly" OK, but ++ * still has unstable bits. ++ * ++ * UBI uses the @copy_flag field to indicate that this logical eraseblock is a ++ * copy. UBI also calculates data CRC when the data is moved and stores it at ++ * the @data_crc field of the copy (P1). So when UBI needs to pick one physical ++ * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is ++ * examined. If it is cleared, the situation* is simple and the newer one is ++ * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC ++ * checksum is correct, this physical eraseblock is selected (P1). Otherwise ++ * the older one (P) is selected. ++ * ++ * Note, there is an obsolete @leb_ver field which was used instead of @sqnum ++ * in the past. But it is not used anymore and we keep it in order to be able ++ * to deal with old UBI images. It will be removed at some point. ++ * ++ * There are 2 sorts of volumes in UBI: user volumes and internal volumes. ++ * Internal volumes are not seen from outside and are used for various internal ++ * UBI purposes. In this implementation there is only one internal volume - the ++ * layout volume. Internal volumes are the main mechanism of UBI extensions. ++ * For example, in future one may introduce a journal internal volume. Internal ++ * volumes have their own reserved range of IDs. ++ * ++ * The @compat field is only used for internal volumes and contains the "degree ++ * of their compatibility". It is always zero for user volumes. This field ++ * provides a mechanism to introduce UBI extensions and to be still compatible ++ * with older UBI binaries. For example, if someone introduced a journal in ++ * future, he would probably use %UBI_COMPAT_DELETE compatibility for the ++ * journal volume. And in this case, older UBI binaries, which know nothing ++ * about the journal volume, would just delete this volume and work perfectly ++ * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image ++ * - it just ignores the Ext3fs journal. ++ * ++ * The @data_crc field contains the CRC checksum of the contents of the logical ++ * eraseblock if this is a static volume. In case of dynamic volumes, it does ++ * not contain the CRC checksum as a rule. The only exception is when the ++ * data of the physical eraseblock was moved by the wear-leveling unit, then ++ * the wear-leveling unit calculates the data CRC and stores it in the ++ * @data_crc field. And of course, the @copy_flag is %in this case. ++ * ++ * The @data_size field is used only for static volumes because UBI has to know ++ * how many bytes of data are stored in this eraseblock. For dynamic volumes, ++ * this field usually contains zero. The only exception is when the data of the ++ * physical eraseblock was moved to another physical eraseblock for ++ * wear-leveling reasons. In this case, UBI calculates CRC checksum of the ++ * contents and uses both @data_crc and @data_size fields. In this case, the ++ * @data_size field contains data size. ++ * ++ * The @used_ebs field is used only for static volumes and indicates how many ++ * eraseblocks the data of the volume takes. For dynamic volumes this field is ++ * not used and always contains zero. ++ * ++ * The @data_pad is calculated when volumes are created using the alignment ++ * parameter. So, effectively, the @data_pad field reduces the size of logical ++ * eraseblocks of this volume. This is very handy when one uses block-oriented ++ * software (say, cramfs) on top of the UBI volume. ++ */ ++struct ubi_vid_hdr { ++ uint32_t magic; ++ uint8_t version; ++ uint8_t vol_type; ++ uint8_t copy_flag; ++ uint8_t compat; ++ uint32_t vol_id; ++ uint32_t lnum; ++ uint32_t leb_ver; /* obsolete, to be removed, don't use */ ++ uint32_t data_size; ++ uint32_t used_ebs; ++ uint32_t data_pad; ++ uint32_t data_crc; ++ uint8_t padding1[4]; ++ uint64_t sqnum; ++ uint8_t padding2[12]; ++ uint32_t hdr_crc; ++} __attribute__ ((packed)); ++ ++/* Internal UBI volumes count */ ++#define UBI_INT_VOL_COUNT 1 ++ ++/* ++ * Starting ID of internal volumes. There is reserved room for 4096 internal ++ * volumes. ++ */ ++#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) ++ ++/* The layout volume contains the volume table */ ++ ++#define UBI_LAYOUT_VOLUME_ID UBI_INTERNAL_VOL_START ++#define UBI_LAYOUT_VOLUME_TYPE UBI_VID_DYNAMIC ++#define UBI_LAYOUT_VOLUME_ALIGN 1 ++#define UBI_LAYOUT_VOLUME_EBS 2 ++#define UBI_LAYOUT_VOLUME_NAME "layout volume" ++#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT ++ ++/* The maximum number of volumes per one UBI device */ ++#define UBI_MAX_VOLUMES 128 ++ ++/* The maximum volume name length */ ++#define UBI_VOL_NAME_MAX 127 ++ ++/* Size of the volume table record */ ++#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record) ++ ++/* Size of the volume table record without the ending CRC */ ++#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(uint32_t)) ++ ++/** ++ * struct ubi_vtbl_record - a record in the volume table. ++ * @reserved_pebs: how many physical eraseblocks are reserved for this volume ++ * @alignment: volume alignment ++ * @data_pad: how many bytes are unused at the end of the each physical ++ * eraseblock to satisfy the requested alignment ++ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) ++ * @upd_marker: if volume update was started but not finished ++ * @name_len: volume name length ++ * @name: the volume name ++ * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG) ++ * @padding: reserved, zeroes ++ * @crc: a CRC32 checksum of the record ++ * ++ * The volume table records are stored in the volume table, which is stored in ++ * the layout volume. The layout volume consists of 2 logical eraseblock, each ++ * of which contains a copy of the volume table (i.e., the volume table is ++ * duplicated). The volume table is an array of &struct ubi_vtbl_record ++ * objects indexed by the volume ID. ++ * ++ * If the size of the logical eraseblock is large enough to fit ++ * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES ++ * records. Otherwise, it contains as many records as it can fit (i.e., size of ++ * logical eraseblock divided by sizeof(struct ubi_vtbl_record)). ++ * ++ * The @upd_marker flag is used to implement volume update. It is set to %1 ++ * before update and set to %0 after the update. So if the update operation was ++ * interrupted, UBI knows that the volume is corrupted. ++ * ++ * The @alignment field is specified when the volume is created and cannot be ++ * later changed. It may be useful, for example, when a block-oriented file ++ * system works on top of UBI. The @data_pad field is calculated using the ++ * logical eraseblock size and @alignment. The alignment must be multiple to the ++ * minimal flash I/O unit. If @alignment is 1, all the available space of ++ * the physical eraseblocks is used. ++ * ++ * Empty records contain all zeroes and the CRC checksum of those zeroes. ++ */ ++struct ubi_vtbl_record { ++ uint32_t reserved_pebs; ++ uint32_t alignment; ++ uint32_t data_pad; ++ uint8_t vol_type; ++ uint8_t upd_marker; ++ uint16_t name_len; ++ uint8_t name[UBI_VOL_NAME_MAX+1]; ++ uint8_t flags; ++ uint8_t padding[23]; ++ uint32_t crc; ++} __attribute__ ((packed)); ++ ++#endif /* !__UBI_HEADER_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/ubi-user.h linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/ubi-user.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd/ubi-user.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd/ubi-user.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,284 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author: Artem Bityutskiy (Битюцкий Ðртём) ++ */ ++ ++#ifndef __UBI_USER_H__ ++#define __UBI_USER_H__ ++ ++#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into ++ separate files was to avoid #ifdef __KERNEL__ */ ++#define __user ++#endif ++/* ++ * UBI device creation (the same as MTD device attachment) ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * MTD devices may be attached using %UBI_IOCATT ioctl command of the UBI ++ * control device. The caller has to properly fill and pass ++ * &struct ubi_attach_req object - UBI will attach the MTD device specified in ++ * the request and return the newly created UBI device number as the ioctl ++ * return value. ++ * ++ * UBI device deletion (the same as MTD device detachment) ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * An UBI device maybe deleted with %UBI_IOCDET ioctl command of the UBI ++ * control device. ++ * ++ * UBI volume creation ++ * ~~~~~~~~~~~~~~~~~~~ ++ * ++ * UBI volumes are created via the %UBI_IOCMKVOL IOCTL command of UBI character ++ * device. A &struct ubi_mkvol_req object has to be properly filled and a ++ * pointer to it has to be passed to the IOCTL. ++ * ++ * UBI volume deletion ++ * ~~~~~~~~~~~~~~~~~~~ ++ * ++ * To delete a volume, the %UBI_IOCRMVOL IOCTL command of the UBI character ++ * device should be used. A pointer to the 32-bit volume ID hast to be passed ++ * to the IOCTL. ++ * ++ * UBI volume re-size ++ * ~~~~~~~~~~~~~~~~~~ ++ * ++ * To re-size a volume, the %UBI_IOCRSVOL IOCTL command of the UBI character ++ * device should be used. A &struct ubi_rsvol_req object has to be properly ++ * filled and a pointer to it has to be passed to the IOCTL. ++ * ++ * UBI volume update ++ * ~~~~~~~~~~~~~~~~~ ++ * ++ * Volume update should be done via the %UBI_IOCVOLUP IOCTL command of the ++ * corresponding UBI volume character device. A pointer to a 64-bit update ++ * size should be passed to the IOCTL. After this, UBI expects user to write ++ * this number of bytes to the volume character device. The update is finished ++ * when the claimed number of bytes is passed. So, the volume update sequence ++ * is something like: ++ * ++ * fd = open("/dev/my_volume"); ++ * ioctl(fd, UBI_IOCVOLUP, &image_size); ++ * write(fd, buf, image_size); ++ * close(fd); ++ * ++ * Atomic eraseblock change ++ * ~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * Atomic eraseblock change operation is done via the %UBI_IOCEBCH IOCTL ++ * command of the corresponding UBI volume character device. A pointer to ++ * &struct ubi_leb_change_req has to be passed to the IOCTL. Then the user is ++ * expected to write the requested amount of bytes. This is similar to the ++ * "volume update" IOCTL. ++ */ ++ ++/* ++ * When a new UBI volume or UBI device is created, users may either specify the ++ * volume/device number they want to create or to let UBI automatically assign ++ * the number using these constants. ++ */ ++#define UBI_VOL_NUM_AUTO (-1) ++#define UBI_DEV_NUM_AUTO (-1) ++ ++/* Maximum volume name length */ ++#define UBI_MAX_VOLUME_NAME 127 ++ ++/* IOCTL commands of UBI character devices */ ++ ++#define UBI_IOC_MAGIC 'o' ++ ++/* Create an UBI volume */ ++#define UBI_IOCMKVOL _IOW(UBI_IOC_MAGIC, 0, struct ubi_mkvol_req) ++/* Remove an UBI volume */ ++#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, int32_t) ++/* Re-size an UBI volume */ ++#define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req) ++ ++/* IOCTL commands of the UBI control character device */ ++ ++#define UBI_CTRL_IOC_MAGIC 'o' ++ ++/* Attach an MTD device */ ++#define UBI_IOCATT _IOW(UBI_CTRL_IOC_MAGIC, 64, struct ubi_attach_req) ++/* Detach an MTD device */ ++#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t) ++ ++/* IOCTL commands of UBI volume character devices */ ++ ++#define UBI_VOL_IOC_MAGIC 'O' ++ ++/* Start UBI volume update */ ++#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t) ++/* An eraseblock erasure command, used for debugging, disabled by default */ ++#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t) ++/* An atomic eraseblock change command */ ++#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t) ++/* Start UBI leb read */ ++#define UBI_IOCLEBREAD _IOWR(UBI_VOL_IOC_MAGIC, 3, struct ubi_leb) ++ ++/* Maximum MTD device name length supported by UBI */ ++#define MAX_UBI_MTD_NAME_LEN 127 ++ ++/* ++ * UBI data type hint constants. ++ * ++ * UBI_LONGTERM: long-term data ++ * UBI_SHORTTERM: short-term data ++ * UBI_UNKNOWN: data persistence is unknown ++ * ++ * These constants are used when data is written to UBI volumes in order to ++ * help the UBI wear-leveling unit to find more appropriate physical ++ * eraseblocks. ++ */ ++enum { ++ UBI_LONGTERM = 1, ++ UBI_SHORTTERM = 2, ++ UBI_UNKNOWN = 3, ++}; ++ ++/* ++ * UBI volume type constants. ++ * ++ * @UBI_DYNAMIC_VOLUME: dynamic volume ++ * @UBI_STATIC_VOLUME: static volume ++ */ ++enum { ++ UBI_DYNAMIC_VOLUME = 3, ++ UBI_STATIC_VOLUME = 4, ++}; ++ ++/** ++ * struct ubi_attach_req - attach MTD device request. ++ * @ubi_num: UBI device number to create ++ * @mtd_num: MTD device number to attach ++ * @vid_hdr_offset: VID header offset (use defaults if %0) ++ * @padding: reserved for future, not used, has to be zeroed ++ * ++ * This data structure is used to specify MTD device UBI has to attach and the ++ * parameters it has to use. The number which should be assigned to the new UBI ++ * device is passed in @ubi_num. UBI may automatically assign the number if ++ * @UBI_DEV_NUM_AUTO is passed. In this case, the device number is returned in ++ * @ubi_num. ++ * ++ * Most applications should pass %0 in @vid_hdr_offset to make UBI use default ++ * offset of the VID header within physical eraseblocks. The default offset is ++ * the next min. I/O unit after the EC header. For example, it will be offset ++ * 512 in case of a 512 bytes page NAND flash with no sub-page support. Or ++ * it will be 512 in case of a 2KiB page NAND flash with 4 512-byte sub-pages. ++ * ++ * But in rare cases, if this optimizes things, the VID header may be placed to ++ * a different offset. For example, the boot-loader might do things faster if the ++ * VID header sits at the end of the first 2KiB NAND page with 4 sub-pages. As ++ * the boot-loader would not normally need to read EC headers (unless it needs ++ * UBI in RW mode), it might be faster to calculate ECC. This is weird example, ++ * but it real-life example. So, in this example, @vid_hdr_offer would be ++ * 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes ++ * aligned, which is OK, as UBI is clever enough to realize this is 4th sub-page ++ * of the first page and add needed padding. ++ */ ++struct ubi_attach_req { ++ int32_t ubi_num; ++ int32_t mtd_num; ++ int32_t vid_hdr_offset; ++ uint8_t padding[12]; ++}; ++ ++/** ++ * struct ubi_mkvol_req - volume description data structure used in ++ * volume creation requests. ++ * @vol_id: volume number ++ * @alignment: volume alignment ++ * @bytes: volume size in bytes ++ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) ++ * @padding1: reserved for future, not used, has to be zeroed ++ * @name_len: volume name length ++ * @padding2: reserved for future, not used, has to be zeroed ++ * @name: volume name ++ * ++ * This structure is used by user-space programs when creating new volumes. The ++ * @used_bytes field is only necessary when creating static volumes. ++ * ++ * The @alignment field specifies the required alignment of the volume logical ++ * eraseblock. This means, that the size of logical eraseblocks will be aligned ++ * to this number, i.e., ++ * (UBI device logical eraseblock size) mod (@alignment) = 0. ++ * ++ * To put it differently, the logical eraseblock of this volume may be slightly ++ * shortened in order to make it properly aligned. The alignment has to be ++ * multiple of the flash minimal input/output unit, or %1 to utilize the entire ++ * available space of logical eraseblocks. ++ * ++ * The @alignment field may be useful, for example, when one wants to maintain ++ * a block device on top of an UBI volume. In this case, it is desirable to fit ++ * an integer number of blocks in logical eraseblocks of this UBI volume. With ++ * alignment it is possible to update this volume using plane UBI volume image ++ * BLOBs, without caring about how to properly align them. ++ */ ++struct ubi_mkvol_req { ++ int32_t vol_id; ++ int32_t alignment; ++ int64_t bytes; ++ int8_t vol_type; ++ int8_t padding1; ++ int16_t name_len; ++ int8_t padding2[4]; ++ char name[UBI_MAX_VOLUME_NAME + 1]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubi_rsvol_req - a data structure used in volume re-size requests. ++ * @vol_id: ID of the volume to re-size ++ * @bytes: new size of the volume in bytes ++ * ++ * Re-sizing is possible for both dynamic and static volumes. But while dynamic ++ * volumes may be re-sized arbitrarily, static volumes cannot be made to be ++ * smaller then the number of bytes they bear. To arbitrarily shrink a static ++ * volume, it must be wiped out first (by means of volume update operation with ++ * zero number of bytes). ++ */ ++struct ubi_rsvol_req { ++ int64_t bytes; ++ int32_t vol_id; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubi_leb_change_req - a data structure used in atomic logical ++ * eraseblock change requests. ++ * @lnum: logical eraseblock number to change ++ * @bytes: how many bytes will be written to the logical eraseblock ++ * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN) ++ * @padding: reserved for future, not used, has to be zeroed ++ */ ++struct ubi_leb_change_req { ++ int32_t lnum; ++ int32_t bytes; ++ uint8_t dtype; ++ uint8_t padding[7]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubi_leb - a data structure describe LEB. ++ * @lnum: logical eraseblock number to dump ++ * @lebbuf: LEB data buffer ++ */ ++struct ubi_leb{ ++ unsigned int lnum; ++ char __user *buf; ++}; ++ ++#endif /* __UBI_USER_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd_swab.h linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd_swab.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/include/mtd_swab.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/include/mtd_swab.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,51 @@ ++#ifndef MTD_SWAB_H ++#define MTD_SWAB_H ++ ++#include ++ ++#define swab16(x) \ ++ ((uint16_t)( \ ++ (((uint16_t)(x) & (uint16_t)0x00ffU) << 8) | \ ++ (((uint16_t)(x) & (uint16_t)0xff00U) >> 8) )) ++#define swab32(x) \ ++ ((uint32_t)( \ ++ (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \ ++ (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \ ++ (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \ ++ (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24) )) ++ ++#define swab64(x) \ ++ ((uint64_t)( \ ++ (((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) | \ ++ (((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ ++ (((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ ++ (((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ ++ (((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ ++ (((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ ++ (((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ ++ (((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56) )) ++ ++ ++#if __BYTE_ORDER == __BIG_ENDIAN ++#define cpu_to_le16(x) ({ uint16_t _x = x; swab16(_x); }) ++#define cpu_to_le32(x) ({ uint32_t _x = x; swab32(_x); }) ++#define cpu_to_le64(x) ({ uint64_t _x = x; swab64(_x); }) ++#define cpu_to_be16(x) (x) ++#define cpu_to_be32(x) (x) ++#define cpu_to_be64(x) (x) ++#else ++#define cpu_to_le16(x) (x) ++#define cpu_to_le32(x) (x) ++#define cpu_to_le64(x) (x) ++#define cpu_to_be16(x) ({ uint16_t _x = x; swab16(_x); }) ++#define cpu_to_be32(x) ({ uint32_t _x = x; swab32(_x); }) ++#define cpu_to_be64(x) ({ uint64_t _x = x; swab64(_x); }) ++#endif ++#define le16_to_cpu(x) cpu_to_le16(x) ++#define be16_to_cpu(x) cpu_to_be16(x) ++#define le32_to_cpu(x) cpu_to_le32(x) ++#define be32_to_cpu(x) cpu_to_be32(x) ++#define le64_to_cpu(x) cpu_to_le64(x) ++#define be64_to_cpu(x) cpu_to_be64(x) ++ ++#endif +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/jffs-dump.c linux-2.6.24.7/drivers/mtd/mtd-utils/jffs-dump.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/jffs-dump.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/jffs-dump.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,359 @@ ++/* ++ * Dump JFFS filesystem. ++ * Useful when it buggers up. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define BLOCK_SIZE 1024 ++#define JFFS_MAGIC 0x34383931 /* "1984" */ ++#define JFFS_MAX_NAME_LEN 256 ++#define JFFS_MIN_INO 1 ++#define JFFS_TRACE_INDENT 4 ++#define JFFS_ALIGN_SIZE 4 ++#define MAX_CHUNK_SIZE 32768 ++ ++/* How many padding bytes should be inserted between two chunks of data ++ on the flash? */ ++#define JFFS_GET_PAD_BYTES(size) ((JFFS_ALIGN_SIZE \ ++ - ((uint32_t)(size) % JFFS_ALIGN_SIZE)) \ ++ % JFFS_ALIGN_SIZE) ++ ++#define JFFS_EMPTY_BITMASK 0xffffffff ++#define JFFS_MAGIC_BITMASK 0x34383931 ++#define JFFS_DIRTY_BITMASK 0x00000000 ++ ++#define min(x,y) (x) > (y) ? (y) : (x) ++ ++struct jffs_raw_inode ++{ ++ uint32_t magic; /* A constant magic number. */ ++ uint32_t ino; /* Inode number. */ ++ uint32_t pino; /* Parent's inode number. */ ++ uint32_t version; /* Version number. */ ++ uint32_t mode; /* file_type, mode */ ++ uint16_t uid; ++ uint16_t gid; ++ uint32_t atime; ++ uint32_t mtime; ++ uint32_t ctime; ++ uint32_t offset; /* Where to begin to write. */ ++ uint32_t dsize; /* Size of the file data. */ ++ uint32_t rsize; /* How much are going to be replaced? */ ++ uint8_t nsize; /* Name length. */ ++ uint8_t nlink; /* Number of links. */ ++ uint8_t spare : 6; /* For future use. */ ++ uint8_t rename : 1; /* Is this a special rename? */ ++ uint8_t deleted : 1; /* Has this file been deleted? */ ++ uint8_t accurate; /* The inode is obsolete if accurate == 0. */ ++ uint32_t dchksum; /* Checksum for the data. */ ++ uint16_t nchksum; /* Checksum for the name. */ ++ uint16_t chksum; /* Checksum for the raw_inode. */ ++}; ++ ++ ++struct jffs_file ++{ ++ struct jffs_raw_inode inode; ++ char *name; ++ unsigned char *data; ++}; ++ ++ ++char *root_directory_name = NULL; ++int fs_pos = 0; ++int verbose = 0; ++ ++#define ENDIAN_HOST 0 ++#define ENDIAN_BIG 1 ++#define ENDIAN_LITTLE 2 ++int endian = ENDIAN_HOST; ++ ++static uint32_t jffs_checksum(void *data, int size); ++void jffs_print_trace(const char *path, int depth); ++int make_root_dir(FILE *fs, int first_ino, const char *root_dir_path, ++ int depth); ++void write_file(struct jffs_file *f, FILE *fs, struct stat st); ++void read_data(struct jffs_file *f, const char *path, int offset); ++int mkfs(FILE *fs, const char *path, int ino, int parent, int depth); ++ ++ ++ static uint32_t ++jffs_checksum(void *data, int size) ++{ ++ uint32_t sum = 0; ++ uint8_t *ptr = (uint8_t *)data; ++ ++ while (size-- > 0) ++ { ++ sum += *ptr++; ++ } ++ ++ return sum; ++} ++ ++ ++ void ++jffs_print_trace(const char *path, int depth) ++{ ++ int path_len = strlen(path); ++ int out_pos = depth * JFFS_TRACE_INDENT; ++ int pos = path_len - 1; ++ char *out = (char *)alloca(depth * JFFS_TRACE_INDENT + path_len + 1); ++ ++ if (verbose >= 2) ++ { ++ fprintf(stderr, "jffs_print_trace(): path: \"%s\"\n", path); ++ } ++ ++ if (!out) { ++ fprintf(stderr, "jffs_print_trace(): Allocation failed.\n"); ++ fprintf(stderr, " path: \"%s\"\n", path); ++ fprintf(stderr, "depth: %d\n", depth); ++ exit(1); ++ } ++ ++ memset(out, ' ', depth * JFFS_TRACE_INDENT); ++ ++ if (path[pos] == '/') ++ { ++ pos--; ++ } ++ while (path[pos] && (path[pos] != '/')) ++ { ++ pos--; ++ } ++ for (pos++; path[pos] && (path[pos] != '/'); pos++) ++ { ++ out[out_pos++] = path[pos]; ++ } ++ out[out_pos] = '\0'; ++ fprintf(stderr, "%s\n", out); ++} ++ ++ ++/* Print the contents of a raw inode. */ ++ void ++jffs_print_raw_inode(struct jffs_raw_inode *raw_inode) ++{ ++ fprintf(stdout, "jffs_raw_inode: inode number: %u, version %u\n", raw_inode->ino, raw_inode->version); ++ fprintf(stdout, "{\n"); ++ fprintf(stdout, " 0x%08x, /* magic */\n", raw_inode->magic); ++ fprintf(stdout, " 0x%08x, /* ino */\n", raw_inode->ino); ++ fprintf(stdout, " 0x%08x, /* pino */\n", raw_inode->pino); ++ fprintf(stdout, " 0x%08x, /* version */\n", raw_inode->version); ++ fprintf(stdout, " 0x%08x, /* mode */\n", raw_inode->mode); ++ fprintf(stdout, " 0x%04x, /* uid */\n", raw_inode->uid); ++ fprintf(stdout, " 0x%04x, /* gid */\n", raw_inode->gid); ++ fprintf(stdout, " 0x%08x, /* atime */\n", raw_inode->atime); ++ fprintf(stdout, " 0x%08x, /* mtime */\n", raw_inode->mtime); ++ fprintf(stdout, " 0x%08x, /* ctime */\n", raw_inode->ctime); ++ fprintf(stdout, " 0x%08x, /* offset */\n", raw_inode->offset); ++ fprintf(stdout, " 0x%08x, /* dsize */\n", raw_inode->dsize); ++ fprintf(stdout, " 0x%08x, /* rsize */\n", raw_inode->rsize); ++ fprintf(stdout, " 0x%02x, /* nsize */\n", raw_inode->nsize); ++ fprintf(stdout, " 0x%02x, /* nlink */\n", raw_inode->nlink); ++ fprintf(stdout, " 0x%02x, /* spare */\n", ++ raw_inode->spare); ++ fprintf(stdout, " %u, /* rename */\n", ++ raw_inode->rename); ++ fprintf(stdout, " %u, /* deleted */\n", ++ raw_inode->deleted); ++ fprintf(stdout, " 0x%02x, /* accurate */\n", ++ raw_inode->accurate); ++ fprintf(stdout, " 0x%08x, /* dchksum */\n", raw_inode->dchksum); ++ fprintf(stdout, " 0x%04x, /* nchksum */\n", raw_inode->nchksum); ++ fprintf(stdout, " 0x%04x, /* chksum */\n", raw_inode->chksum); ++ fprintf(stdout, "}\n"); ++} ++ ++static void write_val32(uint32_t *adr, uint32_t val) ++{ ++ switch(endian) { ++ case ENDIAN_HOST: ++ *adr = val; ++ break; ++ case ENDIAN_LITTLE: ++ *adr = __cpu_to_le32(val); ++ break; ++ case ENDIAN_BIG: ++ *adr = __cpu_to_be32(val); ++ break; ++ } ++} ++ ++static void write_val16(uint16_t *adr, uint16_t val) ++{ ++ switch(endian) { ++ case ENDIAN_HOST: ++ *adr = val; ++ break; ++ case ENDIAN_LITTLE: ++ *adr = __cpu_to_le16(val); ++ break; ++ case ENDIAN_BIG: ++ *adr = __cpu_to_be16(val); ++ break; ++ } ++} ++ ++static uint32_t read_val32(uint32_t *adr) ++{ ++ uint32_t val; ++ ++ switch(endian) { ++ case ENDIAN_HOST: ++ val = *adr; ++ break; ++ case ENDIAN_LITTLE: ++ val = __le32_to_cpu(*adr); ++ break; ++ case ENDIAN_BIG: ++ val = __be32_to_cpu(*adr); ++ break; ++ } ++ return val; ++} ++ ++static uint16_t read_val16(uint16_t *adr) ++{ ++ uint16_t val; ++ ++ switch(endian) { ++ case ENDIAN_HOST: ++ val = *adr; ++ break; ++ case ENDIAN_LITTLE: ++ val = __le16_to_cpu(*adr); ++ break; ++ case ENDIAN_BIG: ++ val = __be16_to_cpu(*adr); ++ break; ++ } ++ return val; ++} ++ ++ int ++main(int argc, char **argv) ++{ ++ int fs; ++ struct stat sb; ++ uint32_t wordbuf; ++ off_t pos = 0; ++ off_t end; ++ struct jffs_raw_inode ino; ++ unsigned char namebuf[4096]; ++ int myino = -1; ++ ++ if (argc < 2) { ++ printf("no filesystem given\n"); ++ exit(1); ++ } ++ ++ fs = open(argv[1], O_RDONLY); ++ if (fs < 0) { ++ perror("open"); ++ exit(1); ++ } ++ ++ if (argc > 2) { ++ myino = atol(argv[2]); ++ printf("Printing ino #%d\n" , myino); ++ } ++ ++ if (fstat(fs, &sb) < 0) { ++ perror("stat"); ++ close(fs); ++ exit(1); ++ } ++ end = sb.st_size; ++ ++ while (pos < end) { ++ if (pread(fs, &wordbuf, 4, pos) < 0) { ++ perror("pread"); ++ exit(1); ++ } ++ ++ switch(wordbuf) { ++ case JFFS_EMPTY_BITMASK: ++ // printf("0xff started at 0x%lx\n", pos); ++ for (; pos < end && wordbuf == JFFS_EMPTY_BITMASK; pos += 4) { ++ if (pread(fs, &wordbuf, 4, pos) < 0) { ++ perror("pread"); ++ exit(1); ++ } ++ } ++ if (pos < end) ++ pos -= 4; ++ // printf("0xff ended at 0x%lx\n", pos); ++ continue; ++ ++ case JFFS_DIRTY_BITMASK: ++ // printf("0x00 started at 0x%lx\n", pos); ++ for (; pos < end && wordbuf == JFFS_DIRTY_BITMASK; pos += 4) { ++ if (pread(fs, &wordbuf, 4, pos) < 0) { ++ perror("pread"); ++ exit(1); ++ } ++ } ++ if (pos < end) ++ pos -=4; ++ // printf("0x00 ended at 0x%lx\n", pos); ++ continue; ++ ++ default: ++ printf("Argh. Dirty memory at 0x%lx\n", pos); ++ // file_hexdump(fs, pos, 128); ++ for (pos += 4; pos < end; pos += 4) { ++ if (pread(fs, &wordbuf, 4, pos) < 0) { ++ perror("pread"); ++ exit(1); ++ } ++ if (wordbuf == JFFS_MAGIC_BITMASK) ++ break; ++ } ++ ++ case JFFS_MAGIC_BITMASK: ++ if (pread(fs, &ino, sizeof(ino), pos) < 0) { ++ perror("pread"); ++ exit(1); ++ } ++ if (myino == -1 || ino.ino == myino) { ++ printf("Magic found at 0x%lx\n", pos); ++ jffs_print_raw_inode(&ino); ++ } ++ pos += sizeof(ino); ++ ++ if (myino == -1 || ino.ino == myino) { ++ if (ino.nsize) { ++ if (pread(fs, namebuf, min(ino.nsize, 4095), pos) < 0) { ++ perror("pread"); ++ exit(1); ++ } ++ if (ino.nsize < 4095) ++ namebuf[ino.nsize] = 0; ++ else ++ namebuf[4095] = 0; ++ printf("Name: \"%s\"\n", namebuf); ++ } else { ++ printf("No Name\n"); ++ } ++ } ++ pos += (ino.nsize + 3) & ~3; ++ ++ pos += (ino.dsize + 3) & ~3; ++ } ++ ++ ++ ++ } ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/jffs2dump.c linux-2.6.24.7/drivers/mtd/mtd-utils/jffs2dump.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/jffs2dump.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/jffs2dump.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,690 @@ ++/* ++ * dumpjffs2.c ++ * ++ * Copyright (C) 2003 Thomas Gleixner (tglx@linutronix.de) ++ * ++ * 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. ++ * ++ * Overview: ++ * This utility dumps the contents of a binary JFFS2 image ++ * ++ * ++ * Bug/ToDo: ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "crc32.h" ++#include "summary.h" ++ ++#define PROGRAM "jffs2dump" ++#define VERSION "$Revision: 1.1.1.1 $" ++ ++#define PAD(x) (((x)+3)&~3) ++ ++/* For outputting a byte-swapped version of the input image. */ ++#define cnv_e32(x) ((jint32_t){bswap_32(x.v32)}) ++#define cnv_e16(x) ((jint16_t){bswap_16(x.v16)}) ++ ++#define t32_backwards(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?bswap_32(__b):__b; }) ++#define cpu_to_e32(x) ((jint32_t){t32_backwards(x)}) ++ ++// Global variables ++long imglen; // length of image ++char *data; // image data ++ ++void display_help (void) ++{ ++ printf("Usage: dumpjffs2 [OPTION] INPUTFILE\n" ++ "Dumps the contents of a binary JFFS2 image.\n" ++ "\n" ++ " --help display this help and exit\n" ++ " --version output version information and exit\n" ++ "-b --bigendian image is big endian\n" ++ "-l --littleendian image is little endian\n" ++ "-c --content dump image contents\n" ++ "-e fname --endianconvert=fname convert image endianness, output to file fname\n" ++ "-r --recalccrc recalc name and data crc on endian conversion\n" ++ "-d len --datsize=len size of data chunks, when oob data in binary image (NAND only)\n" ++ "-o len --oobsize=len size of oob data chunk in binary image (NAND only)\n" ++ "-v --verbose verbose output\n"); ++ exit(0); ++} ++ ++void display_version (void) ++{ ++ printf(PROGRAM " " VERSION "\n" ++ "\n" ++ "Copyright (C) 2003 Thomas Gleixner \n" ++ "\n" ++ PROGRAM " comes with NO WARRANTY\n" ++ "to the extent permitted by law.\n" ++ "\n" ++ "You may redistribute copies of " PROGRAM "\n" ++ "under the terms of the GNU General Public Licence.\n" ++ "See the file `COPYING' for more information.\n"); ++ exit(0); ++} ++ ++// Option variables ++ ++int verbose; // verbose output ++char *img; // filename of image ++int dumpcontent; // dump image content ++int target_endian = __BYTE_ORDER; // image endianess ++int convertendian; // convert endianness ++int recalccrc; // recalc name and data crc's on endian conversion ++char cnvfile[256]; // filename for conversion output ++int datsize; // Size of data chunks, when oob data is inside the binary image ++int oobsize; // Size of oob chunks, when oob data is inside the binary image ++ ++void process_options (int argc, char *argv[]) ++{ ++ int error = 0; ++ ++ for (;;) { ++ int option_index = 0; ++ static const char *short_options = "blce:rd:o:v"; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 0}, ++ {"version", no_argument, 0, 0}, ++ {"bigendian", no_argument, 0, 'b'}, ++ {"littleendian", no_argument, 0, 'l'}, ++ {"content", no_argument, 0, 'c'}, ++ {"endianconvert", required_argument, 0, 'e'}, ++ {"datsize", required_argument, 0, 'd'}, ++ {"oobsize", required_argument, 0, 'o'}, ++ {"recalccrc", required_argument, 0, 'r'}, ++ {"verbose", no_argument, 0, 'v'}, ++ {0, 0, 0, 0}, ++ }; ++ ++ int c = getopt_long(argc, argv, short_options, ++ long_options, &option_index); ++ if (c == EOF) { ++ break; ++ } ++ ++ switch (c) { ++ case 0: ++ switch (option_index) { ++ case 0: ++ display_help(); ++ break; ++ case 1: ++ display_version(); ++ break; ++ } ++ break; ++ case 'v': ++ verbose = 1; ++ break; ++ case 'b': ++ target_endian = __BIG_ENDIAN; ++ break; ++ case 'l': ++ target_endian = __LITTLE_ENDIAN; ++ break; ++ case 'c': ++ dumpcontent = 1; ++ break; ++ case 'd': ++ datsize = atoi(optarg); ++ break; ++ case 'o': ++ oobsize = atoi(optarg); ++ break; ++ case 'e': ++ convertendian = 1; ++ strcpy (cnvfile, optarg); ++ break; ++ case 'r': ++ recalccrc = 1; ++ break; ++ case '?': ++ error = 1; ++ break; ++ } ++ } ++ ++ if ((argc - optind) != 1 || error) ++ display_help (); ++ ++ img = argv[optind]; ++} ++ ++ ++/* ++ * Dump image contents ++ */ ++void do_dumpcontent (void) ++{ ++ char *p = data, *p_free_begin; ++ union jffs2_node_union *node; ++ int empty = 0, dirty = 0; ++ char name[256]; ++ uint32_t crc; ++ uint16_t type; ++ int bitchbitmask = 0; ++ int obsolete; ++ ++ p_free_begin = NULL; ++ while ( p < (data + imglen)) { ++ node = (union jffs2_node_union*) p; ++ ++ /* Skip empty space */ ++ if (!p_free_begin) ++ p_free_begin = p; ++ if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { ++ p += 4; ++ empty += 4; ++ continue; ++ } ++ ++ if (p != p_free_begin) ++ printf("Empty space found from 0x%08x to 0x%08x\n", p_free_begin-data, p-data); ++ p_free_begin = NULL; ++ ++ if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { ++ if (!bitchbitmask++) ++ printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); ++ p += 4; ++ dirty += 4; ++ continue; ++ } ++ bitchbitmask = 0; ++ ++ type = je16_to_cpu(node->u.nodetype); ++ if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { ++ obsolete = 1; ++ type |= JFFS2_NODE_ACCURATE; ++ } else ++ obsolete = 0; ++ /* Set accurate for CRC check */ ++ node->u.nodetype = cpu_to_je16(type); ++ ++ crc = crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); ++ if (crc != je32_to_cpu (node->u.hdr_crc)) { ++ printf ("Wrong hdr_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); ++ p += 4; ++ dirty += 4; ++ continue; ++ } ++ ++ switch(je16_to_cpu(node->u.nodetype)) { ++ ++ case JFFS2_NODETYPE_INODE: ++ printf ("%8s Inode node at 0x%08x, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", ++ obsolete ? "Obsolete" : "", ++ p - data, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino), ++ je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize), ++ je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset)); ++ ++ crc = crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); ++ if (crc != je32_to_cpu (node->i.node_crc)) { ++ printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.node_crc), crc); ++ p += PAD(je32_to_cpu (node->i.totlen)); ++ dirty += PAD(je32_to_cpu (node->i.totlen));; ++ continue; ++ } ++ ++ crc = crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); ++ if (crc != je32_to_cpu(node->i.data_crc)) { ++ printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.data_crc), crc); ++ p += PAD(je32_to_cpu (node->i.totlen)); ++ dirty += PAD(je32_to_cpu (node->i.totlen));; ++ continue; ++ } ++ ++ p += PAD(je32_to_cpu (node->i.totlen)); ++ break; ++ ++ case JFFS2_NODETYPE_DIRENT: ++ memcpy (name, node->d.name, node->d.nsize); ++ name [node->d.nsize] = 0x0; ++ printf ("%8s Dirent node at 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n", ++ obsolete ? "Obsolete" : "", ++ p - data, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), ++ je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), ++ node->d.nsize, name); ++ ++ crc = crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); ++ if (crc != je32_to_cpu (node->d.node_crc)) { ++ printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.node_crc), crc); ++ p += PAD(je32_to_cpu (node->d.totlen)); ++ dirty += PAD(je32_to_cpu (node->d.totlen));; ++ continue; ++ } ++ ++ crc = crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); ++ if (crc != je32_to_cpu(node->d.name_crc)) { ++ printf ("Wrong name_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.name_crc), crc); ++ p += PAD(je32_to_cpu (node->d.totlen)); ++ dirty += PAD(je32_to_cpu (node->d.totlen));; ++ continue; ++ } ++ ++ p += PAD(je32_to_cpu (node->d.totlen)); ++ break; ++ ++ case JFFS2_NODETYPE_SUMMARY: { ++ ++ int i; ++ struct jffs2_sum_marker * sm; ++ ++ printf("%8s Inode Sum node at 0x%08x, totlen 0x%08x, sum_num %5d, cleanmarker size %5d\n", ++ obsolete ? "Obsolete" : "", ++ p - data, ++ je32_to_cpu (node->s.totlen), ++ je32_to_cpu (node->s.sum_num), ++ je32_to_cpu (node->s.cln_mkr)); ++ ++ crc = crc32 (0, node, sizeof (struct jffs2_raw_summary) - 8); ++ if (crc != je32_to_cpu (node->s.node_crc)) { ++ printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.node_crc), crc); ++ p += PAD(je32_to_cpu (node->s.totlen)); ++ dirty += PAD(je32_to_cpu (node->s.totlen));; ++ continue; ++ } ++ ++ crc = crc32(0, p + sizeof (struct jffs2_raw_summary), je32_to_cpu (node->s.totlen) - sizeof(struct jffs2_raw_summary)); ++ if (crc != je32_to_cpu(node->s.sum_crc)) { ++ printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.sum_crc), crc); ++ p += PAD(je32_to_cpu (node->s.totlen)); ++ dirty += PAD(je32_to_cpu (node->s.totlen));; ++ continue; ++ } ++ ++ if (verbose) { ++ void *sp; ++ sp = (p + sizeof(struct jffs2_raw_summary)); ++ ++ for(i=0; is.sum_num); i++) { ++ ++ switch(je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) { ++ case JFFS2_NODETYPE_INODE : { ++ ++ struct jffs2_sum_inode_flash *spi; ++ spi = sp; ++ ++ printf ("%14s #ino %5d, version %5d, offset 0x%08x, totlen 0x%08x\n", ++ "", ++ je32_to_cpu (spi->inode), ++ je32_to_cpu (spi->version), ++ je32_to_cpu (spi->offset), ++ je32_to_cpu (spi->totlen)); ++ ++ sp += JFFS2_SUMMARY_INODE_SIZE; ++ break; ++ } ++ ++ case JFFS2_NODETYPE_DIRENT : { ++ ++ char name[255]; ++ struct jffs2_sum_dirent_flash *spd; ++ spd = sp; ++ ++ memcpy(name,spd->name,spd->nsize); ++ name [spd->nsize] = 0x0; ++ ++ printf ("%14s dirent offset 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s \n", ++ "", ++ je32_to_cpu (spd->offset), ++ je32_to_cpu (spd->totlen), ++ je32_to_cpu (spd->pino), ++ je32_to_cpu (spd->version), ++ je32_to_cpu (spd->ino), ++ spd->nsize, ++ name); ++ ++ sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize); ++ break; ++ } ++ ++ default : ++ printf("Unknown summary node!\n"); ++ break; ++ } ++ } ++ ++ sm = (struct jffs2_sum_marker *) ((char *)p + je32_to_cpu(node->s.totlen) - sizeof(struct jffs2_sum_marker)); ++ ++ printf("%14s Sum Node Offset 0x%08x, Magic 0x%08x, Padded size 0x%08x\n", ++ "", ++ je32_to_cpu(sm->offset), ++ je32_to_cpu(sm->magic), ++ je32_to_cpu(node->s.padded)); ++ } ++ ++ p += PAD(je32_to_cpu (node->s.totlen)); ++ break; ++ } ++ ++ case JFFS2_NODETYPE_CLEANMARKER: ++ if (verbose) { ++ printf ("%8s Cleanmarker at 0x%08x, totlen 0x%08x\n", ++ obsolete ? "Obsolete" : "", ++ p - data, je32_to_cpu (node->u.totlen)); ++ } ++ p += PAD(je32_to_cpu (node->u.totlen)); ++ break; ++ ++ case JFFS2_NODETYPE_PADDING: ++ if (verbose) { ++ printf ("%8s Padding node at 0x%08x, totlen 0x%08x\n", ++ obsolete ? "Obsolete" : "", ++ p - data, je32_to_cpu (node->u.totlen)); ++ } ++ p += PAD(je32_to_cpu (node->u.totlen)); ++ break; ++ ++ case 0xffff: ++ p += 4; ++ empty += 4; ++ break; ++ ++ default: ++ if (verbose) { ++ printf ("%8s Unknown node at 0x%08x, totlen 0x%08x\n", ++ obsolete ? "Obsolete" : "", ++ p - data, je32_to_cpu (node->u.totlen)); ++ } ++ p += PAD(je32_to_cpu (node->u.totlen)); ++ dirty += PAD(je32_to_cpu (node->u.totlen)); ++ ++ } ++ } ++ ++ if (verbose) ++ printf ("Empty space: %d, dirty space: %d\n", empty, dirty); ++} ++ ++/* ++ * Convert endianess ++ */ ++void do_endianconvert (void) ++{ ++ char *p = data; ++ union jffs2_node_union *node, newnode; ++ int fd, len; ++ jint32_t mode; ++ uint32_t crc; ++ ++ fd = open (cnvfile, O_WRONLY | O_CREAT, 0644); ++ if (fd < 0) { ++ fprintf (stderr, "Cannot open / create file: %s\n", cnvfile); ++ return; ++ } ++ ++ while ( p < (data + imglen)) { ++ node = (union jffs2_node_union*) p; ++ ++ /* Skip empty space */ ++ if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { ++ write (fd, p, 4); ++ p += 4; ++ continue; ++ } ++ ++ if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { ++ printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); ++ newnode.u.magic = cnv_e16 (node->u.magic); ++ newnode.u.nodetype = cnv_e16 (node->u.nodetype); ++ write (fd, &newnode, 4); ++ p += 4; ++ continue; ++ } ++ ++ crc = crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); ++ if (crc != je32_to_cpu (node->u.hdr_crc)) { ++ printf ("Wrong hdr_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); ++ } ++ ++ switch(je16_to_cpu(node->u.nodetype)) { ++ ++ case JFFS2_NODETYPE_INODE: ++ ++ newnode.i.magic = cnv_e16 (node->i.magic); ++ newnode.i.nodetype = cnv_e16 (node->i.nodetype); ++ newnode.i.totlen = cnv_e32 (node->i.totlen); ++ newnode.i.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); ++ newnode.i.ino = cnv_e32 (node->i.ino); ++ newnode.i.version = cnv_e32 (node->i.version); ++ mode.v32 = node->i.mode.m; ++ mode = cnv_e32 (mode); ++ newnode.i.mode.m = mode.v32; ++ newnode.i.uid = cnv_e16 (node->i.uid); ++ newnode.i.gid = cnv_e16 (node->i.gid); ++ newnode.i.isize = cnv_e32 (node->i.isize); ++ newnode.i.atime = cnv_e32 (node->i.atime); ++ newnode.i.mtime = cnv_e32 (node->i.mtime); ++ newnode.i.ctime = cnv_e32 (node->i.ctime); ++ newnode.i.offset = cnv_e32 (node->i.offset); ++ newnode.i.csize = cnv_e32 (node->i.csize); ++ newnode.i.dsize = cnv_e32 (node->i.dsize); ++ newnode.i.compr = node->i.compr; ++ newnode.i.usercompr = node->i.usercompr; ++ newnode.i.flags = cnv_e16 (node->i.flags); ++ if (recalccrc) { ++ len = je32_to_cpu(node->i.csize); ++ newnode.i.data_crc = cpu_to_e32 ( crc32(0, p + sizeof (struct jffs2_raw_inode), len)); ++ } else ++ newnode.i.data_crc = cnv_e32 (node->i.data_crc); ++ ++ newnode.i.node_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_raw_inode) - 8)); ++ ++ write (fd, &newnode, sizeof (struct jffs2_raw_inode)); ++ write (fd, p + sizeof (struct jffs2_raw_inode), PAD (je32_to_cpu (node->i.totlen) - sizeof (struct jffs2_raw_inode))); ++ ++ p += PAD(je32_to_cpu (node->i.totlen)); ++ break; ++ ++ case JFFS2_NODETYPE_DIRENT: ++ newnode.d.magic = cnv_e16 (node->d.magic); ++ newnode.d.nodetype = cnv_e16 (node->d.nodetype); ++ newnode.d.totlen = cnv_e32 (node->d.totlen); ++ newnode.d.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); ++ newnode.d.pino = cnv_e32 (node->d.pino); ++ newnode.d.version = cnv_e32 (node->d.version); ++ newnode.d.ino = cnv_e32 (node->d.ino); ++ newnode.d.mctime = cnv_e32 (node->d.mctime); ++ newnode.d.nsize = node->d.nsize; ++ newnode.d.type = node->d.type; ++ newnode.d.unused[0] = node->d.unused[0]; ++ newnode.d.unused[1] = node->d.unused[1]; ++ newnode.d.node_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_raw_dirent) - 8)); ++ if (recalccrc) ++ newnode.d.name_crc = cpu_to_e32 ( crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize)); ++ else ++ newnode.d.name_crc = cnv_e32 (node->d.name_crc); ++ ++ write (fd, &newnode, sizeof (struct jffs2_raw_dirent)); ++ write (fd, p + sizeof (struct jffs2_raw_dirent), PAD (je32_to_cpu (node->d.totlen) - sizeof (struct jffs2_raw_dirent))); ++ p += PAD(je32_to_cpu (node->d.totlen)); ++ break; ++ ++ case JFFS2_NODETYPE_CLEANMARKER: ++ case JFFS2_NODETYPE_PADDING: ++ newnode.u.magic = cnv_e16 (node->u.magic); ++ newnode.u.nodetype = cnv_e16 (node->u.nodetype); ++ newnode.u.totlen = cnv_e32 (node->u.totlen); ++ newnode.u.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); ++ ++ write (fd, &newnode, sizeof (struct jffs2_unknown_node)); ++ len = PAD(je32_to_cpu (node->u.totlen) - sizeof (struct jffs2_unknown_node)); ++ if (len > 0) ++ write (fd, p + sizeof (struct jffs2_unknown_node), len); ++ ++ p += PAD(je32_to_cpu (node->u.totlen)); ++ break; ++ ++ case JFFS2_NODETYPE_SUMMARY : { ++ struct jffs2_sum_marker *sm_ptr; ++ int i,sum_len; ++ int counter = 0; ++ ++ newnode.s.magic = cnv_e16 (node->s.magic); ++ newnode.s.nodetype = cnv_e16 (node->s.nodetype); ++ newnode.s.totlen = cnv_e32 (node->s.totlen); ++ newnode.s.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4)); ++ newnode.s.sum_num = cnv_e32 (node->s.sum_num); ++ newnode.s.cln_mkr = cnv_e32 (node->s.cln_mkr); ++ newnode.s.padded = cnv_e32 (node->s.padded); ++ ++ newnode.s.node_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_raw_summary) - 8)); ++ ++ // summary header ++ p += sizeof (struct jffs2_raw_summary); ++ ++ // summary data ++ sum_len = je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary) - sizeof (struct jffs2_sum_marker); ++ ++ for (i=0; is.sum_num); i++) { ++ union jffs2_sum_flash *fl_ptr; ++ ++ fl_ptr = (union jffs2_sum_flash *) p; ++ ++ switch (je16_to_cpu (fl_ptr->u.nodetype)) { ++ case JFFS2_NODETYPE_INODE: ++ ++ fl_ptr->i.nodetype = cnv_e16 (fl_ptr->i.nodetype); ++ fl_ptr->i.inode = cnv_e32 (fl_ptr->i.inode); ++ fl_ptr->i.version = cnv_e32 (fl_ptr->i.version); ++ fl_ptr->i.offset = cnv_e32 (fl_ptr->i.offset); ++ fl_ptr->i.totlen = cnv_e32 (fl_ptr->i.totlen); ++ p += sizeof (struct jffs2_sum_inode_flash); ++ counter += sizeof (struct jffs2_sum_inode_flash); ++ break; ++ ++ case JFFS2_NODETYPE_DIRENT: ++ fl_ptr->d.nodetype = cnv_e16 (fl_ptr->d.nodetype); ++ fl_ptr->d.totlen = cnv_e32 (fl_ptr->d.totlen); ++ fl_ptr->d.offset = cnv_e32 (fl_ptr->d.offset); ++ fl_ptr->d.pino = cnv_e32 (fl_ptr->d.pino); ++ fl_ptr->d.version = cnv_e32 (fl_ptr->d.version); ++ fl_ptr->d.ino = cnv_e32 (fl_ptr->d.ino); ++ p += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize; ++ counter += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize; ++ break; ++ ++ default : ++ printf("Unknown node in summary information!!! nodetype(%x)\n", je16_to_cpu (fl_ptr->u.nodetype)); ++ exit(EXIT_FAILURE); ++ break; ++ } ++ ++ } ++ ++ //pad ++ p += sum_len - counter; ++ ++ // summary marker ++ sm_ptr = (struct jffs2_sum_marker *) p; ++ sm_ptr->offset = cnv_e32 (sm_ptr->offset); ++ sm_ptr->magic = cnv_e32 (sm_ptr->magic); ++ p += sizeof (struct jffs2_sum_marker); ++ ++ // generate new crc on sum data ++ newnode.s.sum_crc = cpu_to_e32 ( crc32(0, ((char *) node) + sizeof (struct jffs2_raw_summary), ++ je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary))); ++ ++ // write out new node header ++ write(fd, &newnode, sizeof (struct jffs2_raw_summary)); ++ // write out new summary data ++ write(fd, &node->s.sum, sum_len + sizeof (struct jffs2_sum_marker)); ++ ++ break; ++ } ++ ++ case 0xffff: ++ write (fd, p, 4); ++ p += 4; ++ break; ++ ++ default: ++ printf ("Unknown node type: 0x%04x at 0x%08x, totlen 0x%08x\n", je16_to_cpu (node->u.nodetype), p - data, je32_to_cpu (node->u.totlen)); ++ p += PAD(je32_to_cpu (node->u.totlen)); ++ ++ } ++ } ++ ++ close (fd); ++ ++} ++ ++/* ++ * Main program ++ */ ++int main(int argc, char **argv) ++{ ++ int fd; ++ ++ process_options(argc, argv); ++ ++ /* Open the input file */ ++ if ((fd = open(img, O_RDONLY)) == -1) { ++ perror("open input file"); ++ exit(1); ++ } ++ ++ // get image length ++ imglen = lseek(fd, 0, SEEK_END); ++ lseek (fd, 0, SEEK_SET); ++ ++ data = malloc (imglen); ++ if (!data) { ++ perror("out of memory"); ++ close (fd); ++ exit(1); ++ } ++ ++ if (datsize && oobsize) { ++ int idx = 0; ++ long len = imglen; ++ uint8_t oob[oobsize]; ++ printf ("Peeling data out of combined data/oob image\n"); ++ while (len) { ++ // read image data ++ read (fd, &data[idx], datsize); ++ read (fd, oob, oobsize); ++ idx += datsize; ++ imglen -= oobsize; ++ len -= datsize + oobsize; ++ } ++ ++ } else { ++ // read image data ++ read (fd, data, imglen); ++ } ++ // Close the input file ++ close(fd); ++ ++ if (dumpcontent) ++ do_dumpcontent (); ++ ++ if (convertendian) ++ do_endianconvert (); ++ ++ // free memory ++ free (data); ++ ++ // Return happy ++ exit (0); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/jffs2reader.c linux-2.6.24.7/drivers/mtd/mtd-utils/jffs2reader.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/jffs2reader.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/jffs2reader.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,939 @@ ++/* vi: set sw=4 ts=4: */ ++/* ++ * jffs2reader v0.0.18 A jffs2 image reader ++ * ++ * Copyright (c) 2001 Jari Kirma ++ * ++ * This software is provided 'as-is', without any express or implied ++ * warranty. In no event will the author be held liable for any damages ++ * arising from the use of this software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose, including commercial applications, and to alter it and ++ * redistribute it freely, subject to the following restrictions: ++ * ++ * 1. The origin of this software must not be misrepresented; you must ++ * not claim that you wrote the original software. If you use this ++ * software in a product, an acknowledgment in the product ++ * documentation would be appreciated but is not required. ++ * ++ * 2. Altered source versions must be plainly marked as such, and must ++ * not be misrepresented as being the original software. ++ * ++ * 3. This notice may not be removed or altered from any source ++ * distribution. ++ * ++ * ++ ********* ++ * This code was altered September 2001 ++ * Changes are Copyright (c) Erik Andersen ++ * ++ * In compliance with (2) above, this is hereby marked as an altered ++ * version of this software. It has been altered as follows: ++ * *) Listing a directory now mimics the behavior of 'ls -l' ++ * *) Support for recursive listing has been added ++ * *) Without options, does a recursive 'ls' on the whole filesystem ++ * *) option parsing now uses getopt() ++ * *) Now uses printf, and error messages go to stderr. ++ * *) The copyright notice has been cleaned up and reformatted ++ * *) The code has been reformatted ++ * *) Several twisty code paths have been fixed so I can understand them. ++ * -Erik, 1 September 2001 ++ * ++ * *) Made it show major/minor numbers for device nodes ++ * *) Made it show symlink targets ++ * -Erik, 13 September 2001 ++ */ ++ ++ ++/* ++TODO: ++ ++- Add CRC checking code to places marked with XXX. ++- Add support for other node compression types. ++ ++- Test with real life images. ++- Maybe port into bootloader. ++ */ ++ ++/* ++BUGS: ++ ++- Doesn't check CRC checksums. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SCRATCH_SIZE (5*1024*1024) ++ ++#ifndef MAJOR ++/* FIXME: I am using illicit insider knowledge of ++ * kernel major/minor representation... */ ++#define MAJOR(dev) (((dev)>>8)&0xff) ++#define MINOR(dev) ((dev)&0xff) ++#endif ++ ++ ++#define DIRENT_INO(dirent) ((dirent)!=NULL?(dirent)->ino:0) ++#define DIRENT_PINO(dirent) ((dirent)!=NULL?(dirent)->pino:0) ++ ++struct dir { ++ struct dir *next; ++ uint8_t type; ++ uint8_t nsize; ++ uint32_t ino; ++ char name[256]; ++}; ++ ++void putblock(char *, size_t, size_t *, struct jffs2_raw_inode *); ++struct dir *putdir(struct dir *, struct jffs2_raw_dirent *); ++void printdir(char *o, size_t size, struct dir *d, char *path, ++ int recurse); ++void freedir(struct dir *); ++ ++struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino); ++struct jffs2_raw_dirent *resolvedirent(char *, size_t, uint32_t, uint32_t, ++ char *, uint8_t); ++struct jffs2_raw_dirent *resolvename(char *, size_t, uint32_t, char *, uint8_t); ++struct jffs2_raw_dirent *resolveinode(char *, size_t, uint32_t); ++ ++struct jffs2_raw_dirent *resolvepath0(char *, size_t, uint32_t, char *, ++ uint32_t *, int); ++struct jffs2_raw_dirent *resolvepath(char *, size_t, uint32_t, char *, ++ uint32_t *); ++ ++void lsdir(char *, size_t, char *, int); ++void catfile(char *, size_t, char *, char *, size_t, size_t *); ++ ++int main(int, char **); ++ ++/* writes file node into buffer, to the proper position. */ ++/* reading all valid nodes in version order reconstructs the file. */ ++ ++/* ++ b - buffer ++ bsize - buffer size ++ rsize - result size ++ n - node ++ */ ++ ++void putblock(char *b, size_t bsize, size_t * rsize, ++ struct jffs2_raw_inode *n) ++{ ++ uLongf dlen = n->dsize; ++ ++ if (n->isize > bsize || (n->offset + dlen) > bsize) { ++ fprintf(stderr, "File does not fit into buffer!\n"); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (*rsize < n->isize) ++ bzero(b + *rsize, n->isize - *rsize); ++ ++ switch (n->compr) { ++ case JFFS2_COMPR_ZLIB: ++ uncompress((Bytef *) b + n->offset, &dlen, ++ (Bytef *) ((char *) n) + sizeof(struct jffs2_raw_inode), ++ (uLongf) n->csize); ++ break; ++ ++ case JFFS2_COMPR_NONE: ++ memcpy(b + n->offset, ++ ((char *) n) + sizeof(struct jffs2_raw_inode), dlen); ++ break; ++ ++ case JFFS2_COMPR_ZERO: ++ bzero(b + n->offset, dlen); ++ break; ++ ++ /* [DYN]RUBIN support required! */ ++ ++ default: ++ fprintf(stderr, "Unsupported compression method!\n"); ++ exit(EXIT_FAILURE); ++ } ++ ++ *rsize = n->isize; ++} ++ ++/* adds/removes directory node into dir struct. */ ++/* reading all valid nodes in version order reconstructs the directory. */ ++ ++/* ++ dd - directory struct being processed ++ n - node ++ ++ return value: directory struct value replacing dd ++ */ ++ ++struct dir *putdir(struct dir *dd, struct jffs2_raw_dirent *n) ++{ ++ struct dir *o, *d, *p; ++ ++ o = dd; ++ ++ if (n->ino) { ++ if (dd == NULL) { ++ d = malloc(sizeof(struct dir)); ++ d->type = n->type; ++ memcpy(d->name, n->name, n->nsize); ++ d->nsize = n->nsize; ++ d->ino = n->ino; ++ d->next = NULL; ++ ++ return d; ++ } ++ ++ while (1) { ++ if (n->nsize == dd->nsize && ++ !memcmp(n->name, dd->name, n->nsize)) { ++ dd->type = n->type; ++ dd->ino = n->ino; ++ ++ return o; ++ } ++ ++ if (dd->next == NULL) { ++ dd->next = malloc(sizeof(struct dir)); ++ dd->next->type = n->type; ++ memcpy(dd->next->name, n->name, n->nsize); ++ dd->next->nsize = n->nsize; ++ dd->next->ino = n->ino; ++ dd->next->next = NULL; ++ ++ return o; ++ } ++ ++ dd = dd->next; ++ } ++ } else { ++ if (dd == NULL) ++ return NULL; ++ ++ if (n->nsize == dd->nsize && !memcmp(n->name, dd->name, n->nsize)) { ++ d = dd->next; ++ free(dd); ++ return d; ++ } ++ ++ while (1) { ++ p = dd; ++ dd = dd->next; ++ ++ if (dd == NULL) ++ return o; ++ ++ if (n->nsize == dd->nsize && ++ !memcmp(n->name, dd->name, n->nsize)) { ++ p->next = dd->next; ++ free(dd); ++ ++ return o; ++ } ++ } ++ } ++} ++ ++ ++#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) ++#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) ++ ++/* The special bits. If set, display SMODE0/1 instead of MODE0/1 */ ++static const mode_t SBIT[] = { ++ 0, 0, S_ISUID, ++ 0, 0, S_ISGID, ++ 0, 0, S_ISVTX ++}; ++ ++/* The 9 mode bits to test */ ++static const mode_t MBIT[] = { ++ S_IRUSR, S_IWUSR, S_IXUSR, ++ S_IRGRP, S_IWGRP, S_IXGRP, ++ S_IROTH, S_IWOTH, S_IXOTH ++}; ++ ++static const char MODE1[] = "rwxrwxrwx"; ++static const char MODE0[] = "---------"; ++static const char SMODE1[] = "..s..s..t"; ++static const char SMODE0[] = "..S..S..T"; ++ ++/* ++ * Return the standard ls-like mode string from a file mode. ++ * This is static and so is overwritten on each call. ++ */ ++const char *mode_string(int mode) ++{ ++ static char buf[12]; ++ ++ int i; ++ ++ buf[0] = TYPECHAR(mode); ++ for (i = 0; i < 9; i++) { ++ if (mode & SBIT[i]) ++ buf[i + 1] = (mode & MBIT[i]) ? SMODE1[i] : SMODE0[i]; ++ else ++ buf[i + 1] = (mode & MBIT[i]) ? MODE1[i] : MODE0[i]; ++ } ++ return buf; ++} ++ ++/* prints contents of directory structure */ ++ ++/* ++ d - dir struct ++ */ ++ ++void printdir(char *o, size_t size, struct dir *d, char *path, int recurse) ++{ ++ char m; ++ char *filetime; ++ time_t age; ++ struct jffs2_raw_inode *ri; ++ ++ if (!path) ++ return; ++ if (strlen(path) == 1 && *path == '/') ++ path++; ++ ++ while (d != NULL) { ++ switch (d->type) { ++ case DT_REG: ++ m = ' '; ++ break; ++ ++ case DT_FIFO: ++ m = '|'; ++ break; ++ ++ case DT_CHR: ++ m = ' '; ++ break; ++ ++ case DT_BLK: ++ m = ' '; ++ break; ++ ++ case DT_DIR: ++ m = '/'; ++ break; ++ ++ case DT_LNK: ++ m = ' '; ++ break; ++ ++ case DT_SOCK: ++ m = '='; ++ break; ++ ++ default: ++ m = '?'; ++ } ++ ri = find_raw_inode(o, size, d->ino); ++ if (!ri) { ++ fprintf(stderr, "bug: raw_inode missing!\n"); ++ d = d->next; ++ continue; ++ } ++ ++ filetime = ctime((const time_t *) &(ri->ctime)); ++ age = time(NULL) - ri->ctime; ++ printf("%s %-4d %-8d %-8d ", mode_string(ri->mode), ++ 1, ri->uid, ri->gid); ++ if ( d->type==DT_BLK || d->type==DT_CHR ) { ++ dev_t rdev; ++ size_t devsize; ++ putblock((char*)&rdev, sizeof(rdev), &devsize, ri); ++ printf("%4d, %3d ", (int)MAJOR(rdev), (int)MINOR(rdev)); ++ } else { ++ printf("%9ld ", (long)ri->dsize); ++ } ++ d->name[d->nsize]='\0'; ++ if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { ++ /* hh:mm if less than 6 months old */ ++ printf("%6.6s %5.5s %s/%s%c", filetime + 4, filetime + 11, path, d->name, m); ++ } else { ++ printf("%6.6s %4.4s %s/%s%c", filetime + 4, filetime + 20, path, d->name, m); ++ } ++ if (d->type == DT_LNK) { ++ char symbuf[1024]; ++ size_t symsize; ++ putblock(symbuf, sizeof(symbuf), &symsize, ri); ++ symbuf[symsize] = 0; ++ printf(" -> %s", symbuf); ++ } ++ printf("\n"); ++ ++ if (d->type == DT_DIR && recurse) { ++ char *tmp; ++ tmp = malloc(BUFSIZ); ++ if (!tmp) { ++ fprintf(stderr, "memory exhausted\n"); ++ exit(EXIT_FAILURE); ++ } ++ sprintf(tmp, "%s/%s", path, d->name); ++ lsdir(o, size, tmp, recurse); /* Go recursive */ ++ free(tmp); ++ } ++ ++ d = d->next; ++ } ++} ++ ++/* frees memory used by directory structure */ ++ ++/* ++ d - dir struct ++ */ ++ ++void freedir(struct dir *d) ++{ ++ struct dir *t; ++ ++ while (d != NULL) { ++ t = d->next; ++ free(d); ++ d = t; ++ } ++} ++ ++/* collects directory/file nodes in version order. */ ++ ++/* ++ f - file flag. ++ if zero, collect file, compare ino to inode ++ otherwise, collect directory, compare ino to parent inode ++ o - filesystem image pointer ++ size - size of filesystem image ++ ino - inode to compare against. see f. ++ ++ return value: a jffs2_raw_inode that corresponds the the specified ++ inode, or NULL ++ */ ++ ++struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino) ++{ ++ /* aligned! */ ++ union jffs2_node_union *n; ++ union jffs2_node_union *e = (union jffs2_node_union *) (o + size); ++ union jffs2_node_union *lr; /* last block position */ ++ union jffs2_node_union *mp = NULL; /* minimum position */ ++ ++ uint32_t vmin, vmint, vmaxt, vmax, vcur, v; ++ ++ vmin = 0; /* next to read */ ++ vmax = ~((uint32_t) 0); /* last to read */ ++ vmint = ~((uint32_t) 0); ++ vmaxt = 0; /* found maximum */ ++ vcur = 0; /* XXX what is smallest version number used? */ ++ /* too low version number can easily result excess log rereading */ ++ ++ n = (union jffs2_node_union *) o; ++ lr = n; ++ ++ do { ++ while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK) ++ ((char *) n) += 4; ++ ++ if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) { ++ if (n->u.nodetype == JFFS2_NODETYPE_INODE && ++ n->i.ino == ino && (v = n->i.version) > vcur) { ++ /* XXX crc check */ ++ ++ if (vmaxt < v) ++ vmaxt = v; ++ if (vmint > v) { ++ vmint = v; ++ mp = n; ++ } ++ ++ if (v == (vcur + 1)) ++ return (&(n->i)); ++ } ++ ++ ((char *) n) += ((n->u.totlen + 3) & ~3); ++ } else ++ n = (union jffs2_node_union *) o; /* we're at the end, rewind to the beginning */ ++ ++ if (lr == n) { /* whole loop since last read */ ++ vmax = vmaxt; ++ vmin = vmint; ++ vmint = ~((uint32_t) 0); ++ ++ if (vcur < vmax && vcur < vmin) ++ return (&(mp->i)); ++ } ++ } while (vcur < vmax); ++ ++ return NULL; ++} ++ ++/* collects dir struct for selected inode */ ++ ++/* ++ o - filesystem image pointer ++ size - size of filesystem image ++ pino - inode of the specified directory ++ d - input directory structure ++ ++ return value: result directory structure, replaces d. ++ */ ++ ++struct dir *collectdir(char *o, size_t size, uint32_t ino, struct dir *d) ++{ ++ /* aligned! */ ++ union jffs2_node_union *n; ++ union jffs2_node_union *e = (union jffs2_node_union *) (o + size); ++ union jffs2_node_union *lr; /* last block position */ ++ union jffs2_node_union *mp = NULL; /* minimum position */ ++ ++ uint32_t vmin, vmint, vmaxt, vmax, vcur, v; ++ ++ vmin = 0; /* next to read */ ++ vmax = ~((uint32_t) 0); /* last to read */ ++ vmint = ~((uint32_t) 0); ++ vmaxt = 0; /* found maximum */ ++ vcur = 0; /* XXX what is smallest version number used? */ ++ /* too low version number can easily result excess log rereading */ ++ ++ n = (union jffs2_node_union *) o; ++ lr = n; ++ ++ do { ++ while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK) ++ ((char *) n) += 4; ++ ++ if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) { ++ if (n->u.nodetype == JFFS2_NODETYPE_DIRENT && ++ n->d.pino == ino && (v = n->d.version) > vcur) { ++ /* XXX crc check */ ++ ++ if (vmaxt < v) ++ vmaxt = v; ++ if (vmint > v) { ++ vmint = v; ++ mp = n; ++ } ++ ++ if (v == (vcur + 1)) { ++ d = putdir(d, &(n->d)); ++ ++ lr = n; ++ vcur++; ++ vmint = ~((uint32_t) 0); ++ } ++ } ++ ++ ((char *) n) += ((n->u.totlen + 3) & ~3); ++ } else ++ n = (union jffs2_node_union *) o; /* we're at the end, rewind to the beginning */ ++ ++ if (lr == n) { /* whole loop since last read */ ++ vmax = vmaxt; ++ vmin = vmint; ++ vmint = ~((uint32_t) 0); ++ ++ if (vcur < vmax && vcur < vmin) { ++ d = putdir(d, &(mp->d)); ++ ++ lr = n = ++ (union jffs2_node_union *) (((char *) mp) + ++ ((mp->u.totlen + 3) & ~3)); ++ ++ vcur = vmin; ++ } ++ } ++ } while (vcur < vmax); ++ ++ return d; ++} ++ ++ ++ ++/* resolve dirent based on criteria */ ++ ++/* ++ o - filesystem image pointer ++ size - size of filesystem image ++ ino - if zero, ignore, ++ otherwise compare against dirent inode ++ pino - if zero, ingore, ++ otherwise compare against parent inode ++ and use name and nsize as extra criteria ++ name - name of wanted dirent, used if pino!=0 ++ nsize - length of name of wanted dirent, used if pino!=0 ++ ++ return value: pointer to relevant dirent structure in ++ filesystem image or NULL ++ */ ++ ++struct jffs2_raw_dirent *resolvedirent(char *o, size_t size, ++ uint32_t ino, uint32_t pino, ++ char *name, uint8_t nsize) ++{ ++ /* aligned! */ ++ union jffs2_node_union *n; ++ union jffs2_node_union *e = (union jffs2_node_union *) (o + size); ++ ++ struct jffs2_raw_dirent *dd = NULL; ++ ++ uint32_t vmax, v; ++ ++ if (!pino && ino <= 1) ++ return dd; ++ ++ vmax = 0; ++ ++ n = (union jffs2_node_union *) o; ++ ++ do { ++ while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK) ++ ((char *) n) += 4; ++ ++ if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) { ++ if (n->u.nodetype == JFFS2_NODETYPE_DIRENT && ++ (!ino || n->d.ino == ino) && ++ (v = n->d.version) > vmax && ++ (!pino || (n->d.pino == pino && ++ nsize == n->d.nsize && ++ !memcmp(name, n->d.name, nsize)))) { ++ /* XXX crc check */ ++ ++ if (vmax < v) { ++ vmax = v; ++ dd = &(n->d); ++ } ++ } ++ ++ ((char *) n) += ((n->u.totlen + 3) & ~3); ++ } else ++ return dd; ++ } while (1); ++} ++ ++/* resolve name under certain parent inode to dirent */ ++ ++/* ++ o - filesystem image pointer ++ size - size of filesystem image ++ pino - requested parent inode ++ name - name of wanted dirent ++ nsize - length of name of wanted dirent ++ ++ return value: pointer to relevant dirent structure in ++ filesystem image or NULL ++ */ ++ ++struct jffs2_raw_dirent *resolvename(char *o, size_t size, uint32_t pino, ++ char *name, uint8_t nsize) ++{ ++ return resolvedirent(o, size, 0, pino, name, nsize); ++} ++ ++/* resolve inode to dirent */ ++ ++/* ++ o - filesystem image pointer ++ size - size of filesystem image ++ ino - compare against dirent inode ++ ++ return value: pointer to relevant dirent structure in ++ filesystem image or NULL ++ */ ++ ++struct jffs2_raw_dirent *resolveinode(char *o, size_t size, uint32_t ino) ++{ ++ return resolvedirent(o, size, ino, 0, NULL, 0); ++} ++ ++/* resolve slash-style path into dirent and inode. ++ slash as first byte marks absolute path (root=inode 1). ++ . and .. are resolved properly, and symlinks are followed. ++ */ ++ ++/* ++ o - filesystem image pointer ++ size - size of filesystem image ++ ino - root inode, used if path is relative ++ p - path to be resolved ++ inos - result inode, zero if failure ++ recc - recursion count, to detect symlink loops ++ ++ return value: pointer to dirent struct in file system image. ++ note that root directory doesn't have dirent struct ++ (return value is NULL), but it has inode (*inos=1) ++ */ ++ ++struct jffs2_raw_dirent *resolvepath0(char *o, size_t size, uint32_t ino, ++ char *p, uint32_t * inos, int recc) ++{ ++ struct jffs2_raw_dirent *dir = NULL; ++ ++ int d = 1; ++ uint32_t tino; ++ ++ char *next; ++ ++ char *path, *pp; ++ ++ char symbuf[1024]; ++ size_t symsize; ++ ++ if (recc > 16) { ++ /* probably symlink loop */ ++ *inos = 0; ++ return NULL; ++ } ++ ++ pp = path = strdup(p); ++ ++ if (*path == '/') { ++ path++; ++ ino = 1; ++ } ++ ++ if (ino > 1) { ++ dir = resolveinode(o, size, ino); ++ ++ ino = DIRENT_INO(dir); ++ } ++ ++ next = path - 1; ++ ++ while (ino && next != NULL && next[1] != 0 && d) { ++ path = next + 1; ++ next = strchr(path, '/'); ++ ++ if (next != NULL) ++ *next = 0; ++ ++ if (*path == '.' && path[1] == 0) ++ continue; ++ if (*path == '.' && path[1] == '.' && path[2] == 0) { ++ if (DIRENT_PINO(dir) == 1) { ++ ino = 1; ++ dir = NULL; ++ } else { ++ dir = resolveinode(o, size, DIRENT_PINO(dir)); ++ ino = DIRENT_INO(dir); ++ } ++ ++ continue; ++ } ++ ++ dir = resolvename(o, size, ino, path, (uint8_t) strlen(path)); ++ ++ if (DIRENT_INO(dir) == 0 || ++ (next != NULL && ++ !(dir->type == DT_DIR || dir->type == DT_LNK))) { ++ free(pp); ++ ++ *inos = 0; ++ ++ return NULL; ++ } ++ ++ if (dir->type == DT_LNK) { ++ struct jffs2_raw_inode *ri; ++ ri = find_raw_inode(o, size, DIRENT_INO(dir)); ++ putblock(symbuf, sizeof(symbuf), &symsize, ri); ++ symbuf[symsize] = 0; ++ ++ tino = ino; ++ ino = 0; ++ ++ dir = resolvepath0(o, size, tino, symbuf, &ino, ++recc); ++ ++ if (dir != NULL && next != NULL && ++ !(dir->type == DT_DIR || dir->type == DT_LNK)) { ++ free(pp); ++ ++ *inos = 0; ++ return NULL; ++ } ++ } ++ if (dir != NULL) ++ ino = DIRENT_INO(dir); ++ } ++ ++ free(pp); ++ ++ *inos = ino; ++ ++ return dir; ++} ++ ++/* resolve slash-style path into dirent and inode. ++ slash as first byte marks absolute path (root=inode 1). ++ . and .. are resolved properly, and symlinks are followed. ++ */ ++ ++/* ++ o - filesystem image pointer ++ size - size of filesystem image ++ ino - root inode, used if path is relative ++ p - path to be resolved ++ inos - result inode, zero if failure ++ ++ return value: pointer to dirent struct in file system image. ++ note that root directory doesn't have dirent struct ++ (return value is NULL), but it has inode (*inos=1) ++ */ ++ ++struct jffs2_raw_dirent *resolvepath(char *o, size_t size, uint32_t ino, ++ char *p, uint32_t * inos) ++{ ++ return resolvepath0(o, size, ino, p, inos, 0); ++} ++ ++/* lists files on directory specified by path */ ++ ++/* ++ o - filesystem image pointer ++ size - size of filesystem image ++ p - path to be resolved ++ */ ++ ++void lsdir(char *o, size_t size, char *path, int recurse) ++{ ++ struct jffs2_raw_dirent *dd; ++ struct dir *d = NULL; ++ ++ uint32_t ino; ++ ++ dd = resolvepath(o, size, 1, path, &ino); ++ ++ if (ino == 0 || ++ (dd == NULL && ino == 0) || (dd != NULL && dd->type != DT_DIR)) { ++ fprintf(stderr, "jffs2reader: %s: No such file or directory\n", ++ path); ++ exit(EXIT_FAILURE); ++ } ++ ++ d = collectdir(o, size, ino, d); ++ printdir(o, size, d, path, recurse); ++ freedir(d); ++} ++ ++/* writes file specified by path to the buffer */ ++ ++/* ++ o - filesystem image pointer ++ size - size of filesystem image ++ p - path to be resolved ++ b - file buffer ++ bsize - file buffer size ++ rsize - file result size ++ */ ++ ++void catfile(char *o, size_t size, char *path, char *b, size_t bsize, ++ size_t * rsize) ++{ ++ struct jffs2_raw_dirent *dd; ++ struct jffs2_raw_inode *ri; ++ uint32_t ino; ++ ++ dd = resolvepath(o, size, 1, path, &ino); ++ ++ if (ino == 0) { ++ fprintf(stderr, "%s: No such file or directory\n", path); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (dd == NULL || dd->type != DT_REG) { ++ fprintf(stderr, "%s: Not a regular file\n", path); ++ exit(EXIT_FAILURE); ++ } ++ ++ ri = find_raw_inode(o, size, ino); ++ putblock(b, bsize, rsize, ri); ++ ++ write(1, b, *rsize); ++} ++ ++/* usage example */ ++ ++int main(int argc, char **argv) ++{ ++ int fd, opt, recurse = 0; ++ struct stat st; ++ ++ char *scratch, *dir = NULL, *file = NULL; ++ size_t ssize = 0; ++ ++ char *buf; ++ ++ while ((opt = getopt(argc, argv, "rd:f:")) > 0) { ++ switch (opt) { ++ case 'd': ++ dir = optarg; ++ break; ++ case 'f': ++ file = optarg; ++ break; ++ case 'r': ++ recurse++; ++ break; ++ default: ++ fprintf(stderr, ++ "Usage: jffs2reader [-d|-f] < path > \n"); ++ exit(EXIT_FAILURE); ++ } ++ } ++ ++ fd = open(argv[optind], O_RDONLY); ++ if (fd == -1) { ++ fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno)); ++ exit(2); ++ } ++ ++ if (fstat(fd, &st)) { ++ fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno)); ++ exit(3); ++ } ++ ++ buf = malloc((size_t) st.st_size); ++ if (buf == NULL) { ++ fprintf(stderr, "%s: memory exhausted\n", argv[optind]); ++ exit(4); ++ } ++ ++ if (read(fd, buf, st.st_size) != (ssize_t) st.st_size) { ++ fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno)); ++ exit(5); ++ } ++ ++ if (dir) ++ lsdir(buf, st.st_size, dir, recurse); ++ ++ if (file) { ++ scratch = malloc(SCRATCH_SIZE); ++ if (scratch == NULL) { ++ fprintf(stderr, "%s: memory exhausted\n", argv[optind]); ++ exit(6); ++ } ++ ++ catfile(buf, st.st_size, file, scratch, SCRATCH_SIZE, &ssize); ++ free(scratch); ++ } ++ ++ if (!dir && !file) ++ lsdir(buf, st.st_size, "/", 1); ++ ++ ++ free(buf); ++ exit(EXIT_SUCCESS); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/load_nandsim.sh linux-2.6.24.7/drivers/mtd/mtd-utils/load_nandsim.sh +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/load_nandsim.sh 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/load_nandsim.sh 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,123 @@ ++#!/bin/bash ++ ++# ++# This script inserts NAND simulator module to emulate NAND flash of specified ++# size. ++# ++# Author: Artem Bityutskiy ++# ++ ++# Check if nandsim module is loaded ++function nandsim_loaded() ++{ ++ local NANDSIM=`lsmod | grep nandsim` ++ if [ -n "$NANDSIM" ]; then ++ return 1 ++ fi ++ return 0 ++} ++ ++nandsim_loaded ++if (( $? != 0 )); then ++ echo "Error: nandsim is already loaded" ++ exit 1 ++fi ++ ++if (( $# < 1 )); then ++ echo "Load NAND simulator to simulate flash of a specified size." ++ echo "" ++ echo "Usage: ./load_nandsim.sh " ++ echo " " ++ echo "" ++ echo "Only the first parameter is mandatory. Default eraseblock size" ++ echo "is 16KiB, default NAND page size is 512 bytes." ++ echo "" ++ echo "Only the following combinations are supported:" ++ echo "--------------------------------------------------" ++ echo "| size (MiB) | EB size (KiB) | Page size (bytes) |" ++ echo "--------------------------------------------------" ++ echo "| 16 | 16 | 512 |" ++ echo "| 32 | 16 | 512 |" ++ echo "| 64 | 16 | 512 |" ++ echo "| 128 | 16 | 512 |" ++ echo "| 256 | 16 | 512 |" ++ echo "| 64 | 64 | 2048 |" ++ echo "| 64 | 128 | 2048 |" ++ echo "| 64 | 256 | 2048 |" ++ echo "| 64 | 512 | 2048 |" ++ echo "| 128 | 64 | 2048 |" ++ echo "| 128 | 128 | 2048 |" ++ echo "| 128 | 256 | 2048 |" ++ echo "| 128 | 512 | 2048 |" ++ echo "| 256 | 64 | 2048 |" ++ echo "| 256 | 128 | 2048 |" ++ echo "| 256 | 256 | 2048 |" ++ echo "| 256 | 512 | 2048 |" ++ echo "| 512 | 64 | 2048 |" ++ echo "| 512 | 128 | 2048 |" ++ echo "| 512 | 256 | 2048 |" ++ echo "| 512 | 512 | 2048 |" ++ echo "| 1024 | 64 | 2048 |" ++ echo "| 1024 | 128 | 2048 |" ++ echo "| 1024 | 256 | 2048 |" ++ echo "| 1024 | 512 | 2048 |" ++ echo "--------------------------------------------------" ++ exit 1 ++fi ++ ++SZ=$1 ++EBSZ=$2 ++PGSZ=$3 ++if [[ $# == '1' ]]; then ++ EBSZ=16 ++ PGSZ=512 ++elif [[ $# == '2' ]]; then ++ PGSZ=512 ++fi ++ ++if (( $PGSZ == 512 && $EBSZ != 16 )); then ++ echo "Error: only 16KiB eraseblocks are possible in case of 512 bytes page" ++ exit 1 ++fi ++ ++if (( $PGSZ == 512 )); then ++ case $SZ in ++ 16) modprobe nandsim first_id_byte=0x20 second_id_byte=0x33 ;; ++ 32) modprobe nandsim first_id_byte=0x20 second_id_byte=0x35 ;; ++ 64) modprobe nandsim first_id_byte=0x20 second_id_byte=0x36 ;; ++ 128) modprobe nandsim first_id_byte=0x20 second_id_byte=0x78 ;; ++ 256) modprobe nandsim first_id_byte=0x20 second_id_byte=0x71 ;; ++ *) echo "Flash size ${SZ}MiB is not supported, try 16, 32, 64 or 256" ++ exit 1 ;; ++ esac ++elif (( $PGSZ == 2048 )); then ++ case $EBSZ in ++ 64) FOURTH=0x05 ;; ++ 128) FOURTH=0x15 ;; ++ 256) FOURTH=0x25 ;; ++ 512) FOURTH=0x35 ;; ++ *) echo "Eraseblock ${EBSZ}KiB is not supported" ++ exit 1 ++ esac ++ ++ case $SZ in ++ 64) modprobe nandsim first_id_byte=0x20 second_id_byte=0xa2 third_id_byte=0x00 fourth_id_byte=$FOURTH ;; ++ 128) modprobe nandsim first_id_byte=0xec second_id_byte=0xa1 third_id_byte=0x00 fourth_id_byte=$FOURTH ;; ++ 256) modprobe nandsim first_id_byte=0x20 second_id_byte=0xaa third_id_byte=0x00 fourth_id_byte=$FOURTH ;; ++ 512) modprobe nandsim first_id_byte=0x20 second_id_byte=0xac third_id_byte=0x00 fourth_id_byte=$FOURTH ;; ++ 1024) modprobe nandsim first_id_byte=0xec second_id_byte=0xd3 third_id_byte=0x51 fourth_id_byte=$FOURTH ;; ++ *) echo "Unable to emulate ${SZ}MiB flash with ${EBSZ}KiB eraseblock" ++ exit 1 ++ esac ++else ++ echo "Error: bad NAND page size ${PGSZ}KiB, it has to be either 512 or 2048" ++ exit 1 ++fi ++ ++if (( $? != 0 )); then ++ echo "Error: cannot load nandsim" ++ exit 1 ++fi ++ ++echo "Loaded NAND simulator (${SZ}MiB, ${EBSZ}KiB eraseblock, $PGSZ bytes NAND page)" ++exit 0 +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/mcast_image.h linux-2.6.24.7/drivers/mtd/mtd-utils/mcast_image.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/mcast_image.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/mcast_image.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,54 @@ ++#include ++ ++#define PKT_SIZE 2820 ++ ++struct image_pkt_hdr { ++ uint32_t resend; ++ uint32_t totcrc; ++ uint32_t nr_blocks; ++ uint32_t blocksize; ++ uint32_t block_crc; ++ uint32_t block_nr; ++ uint32_t pkt_sequence; ++ uint16_t pkt_nr; ++ uint16_t nr_pkts; ++ uint32_t thislen; ++ uint32_t thiscrc; ++}; ++ ++struct image_pkt { ++ struct image_pkt_hdr hdr; ++ unsigned char data[PKT_SIZE]; ++}; ++ ++struct fec_parms; ++ ++/* k - number of actual data packets ++ * n - total number of packets including data and redundant packets ++ * (actual packet size isn't relevant here) */ ++struct fec_parms *fec_new(int k, int n); ++void fec_free(struct fec_parms *p); ++ ++/* src - array of (n) pointers to data packets ++ * fec - buffer for packet to be generated ++ * index - index of packet to be generated (0 <= index < n) ++ * sz - data packet size ++ * ++ * _linear version just takes a pointer to the raw data; no ++ * mucking about with packet pointers. ++ */ ++void fec_encode(struct fec_parms *code, unsigned char *src[], ++ unsigned char *fec, int index, int sz); ++void fec_encode_linear(struct fec_parms *code, unsigned char *src, ++ unsigned char *fec, int index, int sz); ++ ++/* data - array of (k) pointers to data packets, in arbitrary order (see i) ++ * i - indices of (data) packets ++ * sz - data packet size ++ * ++ * Will never fail as long as you give it (k) individual data packets. ++ * Will re-order the (data) pointers but not the indices -- data packets ++ * are ordered on return. ++ */ ++int fec_decode(struct fec_parms *code, unsigned char *data[], ++ int i[], int sz); +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/mkfs.jffs2.1 linux-2.6.24.7/drivers/mtd/mtd-utils/mkfs.jffs2.1 +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/mkfs.jffs2.1 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/mkfs.jffs2.1 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,259 @@ ++.TH MKFS.JFFS2 1 ++.SH NAME ++mkfs.jffs2 \- Create a JFFS2 file system image from directory ++.SH SYNOPSIS ++.B mkfs.jffs2 ++[ ++.B -p,--pad[=SIZE] ++] ++[ ++.B -r,-d,--root ++.I directory ++] ++[ ++.B -s,--pagesize=SIZE ++] ++[ ++.B -e,--eraseblock=SIZE ++] ++[ ++.B -c,--cleanmarker=SIZE ++] ++[ ++.B -n,--no-cleanmarkers ++] ++[ ++.B -o,--output ++.I image.jffs2 ++] ++[ ++.B -l,--little-endian ++] ++[ ++.B -b,--big-endian ++] ++[ ++.B -D,--devtable=FILE ++] ++[ ++.B -f,--faketime ++] ++[ ++.B -q,--squash ++] ++[ ++.B -U,--squash-uids ++] ++[ ++.B -P,--squash-perms ++] ++[ ++.B --with-xattr ++] ++[ ++.B --with-selinux ++] ++[ ++.B --with-posix-acl ++] ++[ ++.B -m,--compression-mode=MODE ++] ++[ ++.B -x,--disable-compressor=NAME ++] ++[ ++.B -X,--enable-compressor=NAME ++] ++[ ++.B -y,--compressor-priority=PRIORITY:NAME ++] ++[ ++.B -L,--list-compressors ++] ++[ ++.B -t,--test-compression ++] ++[ ++.B -h,--help ++] ++[ ++.B -v,--verbose ++] ++[ ++.B -V,--version ++] ++[ ++.B -i,--incremental ++.I image.jffs2 ++] ++ ++.SH DESCRIPTION ++The program ++.B mkfs.jffs2 ++creates a JFFS2 (Second Journalling Flash File System) file system ++image and writes the resulting image to the file specified by the ++.B -o ++option or by default to the standard output, unless the standard ++output is a terminal device in which case mkfs.jffs2 will abort. ++ ++The file system image is created using the files and directories ++contained in the directory specified by the option ++.B -r ++or the present directory, if the ++.B -r ++option is not specified. ++ ++Each block of the files to be placed into the file system image ++are compressed using one of the avaiable compressors depending ++on the selected compression mode. ++ ++File systems are created with the same endianness as the host, ++unless the ++.B -b ++or ++.B -l ++options are specified. JFFS2 driver in the 2.4 Linux kernel only ++supported images having the same endianness as the CPU. As of 2.5.48, ++the kernel can be changed with a #define to accept images of the ++non-native endianness. Full bi-endian support in the kernel is not ++planned. ++ ++It is unlikely that JFFS2 images are useful except in conjuction ++with the MTD (Memory Technology Device) drivers in the Linux ++kernel, since the JFFS2 file system driver in the kernel requires ++MTD devices. ++.SH OPTIONS ++Options that take SIZE arguments can be specified as either ++decimal (e.g., 65536), octal (0200000), or hexidecimal (0x1000). ++.TP ++.B -p, --pad[=SIZE] ++Pad output to SIZE bytes with 0xFF. If SIZE is not specified, ++the output is padded to the end of the final erase block. ++.TP ++.B -r, -d, --root=DIR ++Build file system from directory DIR. The default is the current ++directory. ++.TP ++.B -s, --pagesize=SIZE ++Use page size SIZE. The default is 4 KiB. This size is the ++maximum size of a data node. ++.TP ++.B -e, --eraseblock=SIZE ++Use erase block size SIZE. The default is 64 KiB. If you use a erase ++block size different than the erase block size of the target MTD ++device, JFFS2 may not perform optimally. If the SIZE specified is ++below 4096, the units are assumed to be KiB. ++.TP ++.B -c, --cleanmarker=SIZE ++Write \'CLEANMARKER\' nodes with the size specified. It is not ++normally appropriate to specify a size other than the default 12 ++bytes. ++.TP ++.B -n, --no-cleanmarkers ++Do not write \'CLEANMARKER\' nodes to the beginning of each erase ++block. This option can be useful for creating JFFS2 images for ++use on NAND flash, and for creating images which are to be used ++on a variety of hardware with differing eraseblock sizes. ++.TP ++.B -o, --output=FILE ++Write JFFS2 image to file FILE. Default is the standard output. ++.TP ++.B -l, --little-endian ++Create a little-endian JFFS2 image. Default is to make an image ++with the same endianness as the host. ++.TP ++.B -b, --big-endian ++Create a big-endian JFFS2 image. Default is to make an image ++with the same endianness as the host. ++.TP ++.B -D, --devtable=FILE ++Use the named FILE as a device table file, for including devices and ++changing permissions in the created image when the user does not have ++appropriate permissions to create them on the file system used as ++source. ++.TP ++.B -f, --faketime ++Change all file timestamps to \'0\' for regression testing. ++.TP ++.B -q, --squash ++Squash permissions and owners, making all files be owned by root and ++removing write permission for \'group\' and \'other\'. ++.TP ++.B -U, --squash-uids ++Squash owners making all files be owned by root. ++.TP ++.B -P, --squash-perms ++Squash permissions, removing write permission for \'group\' and \'other\'. ++.TP ++.B --with-xattr ++Enables xattr, stuff all xattr entries into jffs2 image file. ++.TP ++.B --with-selinux ++Enables xattr, stuff only SELinux Labels into jffs2 image file. ++.TP ++.B --with-posix-acl ++Enable xattr, stuff only POSIX ACL entries into jffs2 image file. ++.TP ++.B -m, --compression-mode=MODE ++Set the default compression mode. The default mode is ++.B priority ++which tries the compressors in a predefinied order and chooses the first ++successful one. The alternatives are: ++.B none ++(mkfs will not compress) and ++.B size ++(mkfs will try all compressor and chooses the one which have the smallest result). ++.TP ++.B -x, --disable-compressor=NAME ++Disable a compressor. Use ++.B -L ++to see the list of the avaiable compressors and their default states. ++.TP ++.B -X, --enable-compressor=NAME ++Enable a compressor. Use ++.B -L ++to see the list of the avaiable compressors and their default states. ++.TP ++.B -y, --compressor-priority=PRIORITY:NAME ++Set the priority of a compressor. Use ++.B -L ++to see the list of the avaiable compressors and their default priority. ++Priorities are used by priority compression mode. ++.TP ++.B -L, --list-compressors ++Show the list of the avaiable compressors and their states. ++.TP ++.B -t, --test-compression ++Call decompress after every compress - and compare the result with the original data -, and ++some other check. ++.TP ++.B -h, --help ++Display help text. ++.TP ++.B -v, --verbose ++Verbose operation. ++.TP ++.B -V, --version ++Display version information. ++.TP ++.B -i, --incremental=FILE ++Generate an appendage image for FILE. If FILE is written to flash and flash ++is appended with the output, then it seems as if it was one thing. ++ ++.SH BUGS ++JFFS2 limits device major and minor numbers to 8 bits each. Some ++consider this a bug. ++ ++.B mkfs.jffs2 ++does not properly handle hard links in the input directory structure. ++Currently, hard linked files will be expanded to multiple identical ++files in the output image. ++.SH AUTHORS ++David Woodhouse ++.br ++Manual page written by David Schleef ++.SH SEE ALSO ++.BR mkfs (8), ++.BR mkfs.jffs (1), ++.BR fakeroot (1) +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/mkfs.jffs2.c linux-2.6.24.7/drivers/mtd/mtd-utils/mkfs.jffs2.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/mkfs.jffs2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/mkfs.jffs2.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1902 @@ ++/* vi: set sw=4 ts=4: */ ++/* ++ * Build a JFFS2 image in a file, from a given directory tree. ++ * ++ * Copyright 2001, 2002 Red Hat, Inc. ++ * 2001 David A. Schleef ++ * 2002 Axis Communications AB ++ * 2001, 2002 Erik Andersen ++ * 2004 University of Szeged, Hungary ++ * 2006 KaiGai Kohei ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Cross-endian support added by David Schleef . ++ * ++ * Major architectural rewrite by Erik Andersen ++ * to allow support for making hard links (though hard links support is ++ * not yet implemented), and for munging file permissions and ownership ++ * on the fly using --faketime, --squash, --devtable. And I plugged a ++ * few memory leaks, adjusted the error handling and fixed some little ++ * nits here and there. ++ * ++ * I also added a sample device table file. See device_table.txt ++ * -Erik, September 2001 ++ * ++ * Cleanmarkers support added by Axis Communications AB ++ * ++ * Rewritten again. Cleanly separated host and target filsystem ++ * activities (mainly so I can reuse all the host handling stuff as I ++ * rewrite other mkfs utils). Added a verbose option to list types ++ * and attributes as files are added to the file system. Major cleanup ++ * and scrubbing of the code so it can be read, understood, and ++ * modified by mere mortals. ++ * ++ * -Erik, November 2002 ++ */ ++ ++#define _GNU_SOURCE ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifndef WITHOUT_XATTR ++#include ++#include ++#endif ++#include ++#define crc32 __complete_crap ++#include ++#undef crc32 ++#include "crc32.h" ++#include "rbtree.h" ++ ++/* Do not use the weird XPG version of basename */ ++#undef basename ++ ++//#define DMALLOC ++//#define mkfs_debug_msg error_msg ++#define mkfs_debug_msg(a...) { } ++#define min(x,y) ({ typeof((x)) _x = (x); typeof((y)) _y = (y); (_x>_y)?_y:_x; }) ++ ++#define PAD(x) (((x)+3)&~3) ++ ++struct filesystem_entry { ++ char *name; /* Name of this directory (think basename) */ ++ char *path; /* Path of this directory (think dirname) */ ++ char *fullname; /* Full name of this directory (i.e. path+name) */ ++ char *hostname; /* Full path to this file on the host filesystem */ ++ uint32_t ino; /* Inode number of this file in JFFS2 */ ++ struct stat sb; /* Stores directory permissions and whatnot */ ++ char *link; /* Target a symlink points to. */ ++ struct filesystem_entry *parent; /* Parent directory */ ++ struct filesystem_entry *prev; /* Only relevant to non-directories */ ++ struct filesystem_entry *next; /* Only relevant to non-directories */ ++ struct filesystem_entry *files; /* Only relevant to directories */ ++ struct rb_node hardlink_rb; ++}; ++ ++struct rb_root hardlinks; ++static int out_fd = -1; ++static int in_fd = -1; ++static char default_rootdir[] = "."; ++static char *rootdir = default_rootdir; ++static int verbose = 0; ++static int squash_uids = 0; ++static int squash_perms = 0; ++static int fake_times = 0; ++int target_endian = __BYTE_ORDER; ++static const char *const app_name = "mkfs.jffs2"; ++static const char *const memory_exhausted = "memory exhausted"; ++ ++uint32_t find_hardlink(struct filesystem_entry *e) ++{ ++ struct filesystem_entry *f; ++ struct rb_node **n = &hardlinks.rb_node; ++ struct rb_node *parent = NULL; ++ ++ while (*n) { ++ parent = *n; ++ f = rb_entry(parent, struct filesystem_entry, hardlink_rb); ++ ++ if ((f->sb.st_dev < e->sb.st_dev) || ++ (f->sb.st_dev == e->sb.st_dev && ++ f->sb.st_ino < e->sb.st_ino)) ++ n = &parent->rb_left; ++ else if ((f->sb.st_dev > e->sb.st_dev) || ++ (f->sb.st_dev == e->sb.st_dev && ++ f->sb.st_ino > e->sb.st_ino)) { ++ n = &parent->rb_right; ++ } else ++ return f->ino; ++ } ++ ++ rb_link_node(&e->hardlink_rb, parent, n); ++ rb_insert_color(&e->hardlink_rb, &hardlinks); ++ return 0; ++} ++ ++static void verror_msg(const char *s, va_list p) ++{ ++ fflush(stdout); ++ fprintf(stderr, "%s: ", app_name); ++ vfprintf(stderr, s, p); ++} ++static void error_msg(const char *s, ...) ++{ ++ va_list p; ++ ++ va_start(p, s); ++ verror_msg(s, p); ++ va_end(p); ++ putc('\n', stderr); ++} ++ ++static void error_msg_and_die(const char *s, ...) ++{ ++ va_list p; ++ ++ va_start(p, s); ++ verror_msg(s, p); ++ va_end(p); ++ putc('\n', stderr); ++ exit(EXIT_FAILURE); ++} ++ ++static void vperror_msg(const char *s, va_list p) ++{ ++ int err = errno; ++ ++ if (s == 0) ++ s = ""; ++ verror_msg(s, p); ++ if (*s) ++ s = ": "; ++ fprintf(stderr, "%s%s\n", s, strerror(err)); ++} ++ ++static void perror_msg(const char *s, ...) ++{ ++ va_list p; ++ ++ va_start(p, s); ++ vperror_msg(s, p); ++ va_end(p); ++} ++ ++static void perror_msg_and_die(const char *s, ...) ++{ ++ va_list p; ++ ++ va_start(p, s); ++ vperror_msg(s, p); ++ va_end(p); ++ exit(EXIT_FAILURE); ++} ++ ++#ifndef DMALLOC ++extern void *xmalloc(size_t size) ++{ ++ void *ptr = malloc(size); ++ ++ if (ptr == NULL && size != 0) ++ error_msg_and_die(memory_exhausted); ++ return ptr; ++} ++ ++extern void *xcalloc(size_t nmemb, size_t size) ++{ ++ void *ptr = calloc(nmemb, size); ++ ++ if (ptr == NULL && nmemb != 0 && size != 0) ++ error_msg_and_die(memory_exhausted); ++ return ptr; ++} ++ ++extern void *xrealloc(void *ptr, size_t size) ++{ ++ ptr = realloc(ptr, size); ++ if (ptr == NULL && size != 0) ++ error_msg_and_die(memory_exhausted); ++ return ptr; ++} ++ ++extern char *xstrdup(const char *s) ++{ ++ char *t; ++ ++ if (s == NULL) ++ return NULL; ++ t = strdup(s); ++ if (t == NULL) ++ error_msg_and_die(memory_exhausted); ++ return t; ++} ++#endif ++ ++extern char *xreadlink(const char *path) ++{ ++ static const int GROWBY = 80; /* how large we will grow strings by */ ++ ++ char *buf = NULL; ++ int bufsize = 0, readsize = 0; ++ ++ do { ++ buf = xrealloc(buf, bufsize += GROWBY); ++ readsize = readlink(path, buf, bufsize); /* 1st try */ ++ if (readsize == -1) { ++ perror_msg("%s:%s", app_name, path); ++ return NULL; ++ } ++ } ++ while (bufsize < readsize + 1); ++ ++ buf[readsize] = '\0'; ++ ++ return buf; ++} ++static FILE *xfopen(const char *path, const char *mode) ++{ ++ FILE *fp; ++ if ((fp = fopen(path, mode)) == NULL) ++ perror_msg_and_die("%s", path); ++ return fp; ++} ++ ++static struct filesystem_entry *find_filesystem_entry( ++ struct filesystem_entry *dir, char *fullname, uint32_t type) ++{ ++ struct filesystem_entry *e = dir; ++ ++ if (S_ISDIR(dir->sb.st_mode)) { ++ e = dir->files; ++ } ++ while (e) { ++ /* Only bother to do the expensive strcmp on matching file types */ ++ if (type == (e->sb.st_mode & S_IFMT)) { ++ if (S_ISDIR(e->sb.st_mode)) { ++ int len = strlen(e->fullname); ++ ++ /* Check if we are a parent of the correct path */ ++ if (strncmp(e->fullname, fullname, len) == 0) { ++ /* Is this an _exact_ match? */ ++ if (strcmp(fullname, e->fullname) == 0) { ++ return (e); ++ } ++ /* Looks like we found a parent of the correct path */ ++ if (fullname[len] == '/') { ++ if (e->files) { ++ return (find_filesystem_entry (e, fullname, type)); ++ } else { ++ return NULL; ++ } ++ } ++ } ++ } else { ++ if (strcmp(fullname, e->fullname) == 0) { ++ return (e); ++ } ++ } ++ } ++ e = e->next; ++ } ++ return (NULL); ++} ++ ++static struct filesystem_entry *add_host_filesystem_entry( ++ char *name, char *path, unsigned long uid, unsigned long gid, ++ unsigned long mode, dev_t rdev, struct filesystem_entry *parent) ++{ ++ int status; ++ char *tmp; ++ struct stat sb; ++ time_t timestamp = time(NULL); ++ struct filesystem_entry *entry; ++ ++ memset(&sb, 0, sizeof(struct stat)); ++ status = lstat(path, &sb); ++ ++ if (status >= 0) { ++ /* It is ok for some types of files to not exit on disk (such as ++ * device nodes), but if they _do_ exist the specified mode had ++ * better match the actual file or strange things will happen.... */ ++ if ((mode & S_IFMT) != (sb.st_mode & S_IFMT)) { ++ error_msg_and_die ("%s: file type does not match specified type!", path); ++ } ++ timestamp = sb.st_mtime; ++ } else { ++ /* If this is a regular file, it _must_ exist on disk */ ++ if ((mode & S_IFMT) == S_IFREG) { ++ error_msg_and_die("%s: does not exist!", path); ++ } ++ } ++ ++ /* Squash all permissions so files are owned by root, all ++ * timestamps are _right now_, and file permissions ++ * have group and other write removed */ ++ if (squash_uids) { ++ uid = gid = 0; ++ } ++ if (squash_perms) { ++ if (!S_ISLNK(mode)) { ++ mode &= ~(S_IWGRP | S_IWOTH); ++ mode &= ~(S_ISUID | S_ISGID); ++ } ++ } ++ if (fake_times) { ++ timestamp = 0; ++ } ++ ++ entry = xcalloc(1, sizeof(struct filesystem_entry)); ++ ++ entry->hostname = xstrdup(path); ++ entry->fullname = xstrdup(name); ++ tmp = xstrdup(name); ++ entry->name = xstrdup(basename(tmp)); ++ free(tmp); ++ tmp = xstrdup(name); ++ entry->path = xstrdup(dirname(tmp)); ++ free(tmp); ++ ++ entry->sb.st_ino = sb.st_ino; ++ entry->sb.st_dev = sb.st_dev; ++ entry->sb.st_nlink = sb.st_nlink; ++ ++ entry->sb.st_uid = uid; ++ entry->sb.st_gid = gid; ++ entry->sb.st_mode = mode; ++ entry->sb.st_rdev = rdev; ++ entry->sb.st_atime = entry->sb.st_ctime = ++ entry->sb.st_mtime = timestamp; ++ if (S_ISREG(mode)) { ++ entry->sb.st_size = sb.st_size; ++ } ++ if (S_ISLNK(mode)) { ++ entry->link = xreadlink(path); ++ entry->sb.st_size = strlen(entry->link); ++ } ++ ++ /* This happens only for root */ ++ if (!parent) ++ return (entry); ++ ++ /* Hook the file into the parent directory */ ++ entry->parent = parent; ++ if (!parent->files) { ++ parent->files = entry; ++ } else { ++ struct filesystem_entry *prev; ++ for (prev = parent->files; prev->next; prev = prev->next); ++ prev->next = entry; ++ entry->prev = prev; ++ } ++ ++ return (entry); ++} ++ ++static struct filesystem_entry *recursive_add_host_directory( ++ struct filesystem_entry *parent, char *targetpath, char *hostpath) ++{ ++ int i, n; ++ struct stat sb; ++ char *hpath, *tpath; ++ struct dirent *dp, **namelist; ++ struct filesystem_entry *entry; ++ ++ ++ if (lstat(hostpath, &sb)) { ++ perror_msg_and_die("%s", hostpath); ++ } ++ ++ entry = add_host_filesystem_entry(targetpath, hostpath, ++ sb.st_uid, sb.st_gid, sb.st_mode, 0, parent); ++ ++ n = scandir(hostpath, &namelist, 0, alphasort); ++ if (n < 0) { ++ perror_msg_and_die("opening directory %s", hostpath); ++ } ++ ++ for (i=0; id_name[0] == '.' && (dp->d_name[1] == 0 || ++ (dp->d_name[1] == '.' && dp->d_name[2] == 0))) ++ { ++ free(dp); ++ continue; ++ } ++ ++ asprintf(&hpath, "%s/%s", hostpath, dp->d_name); ++ if (lstat(hpath, &sb)) { ++ perror_msg_and_die("%s", hpath); ++ } ++ if (strcmp(targetpath, "/") == 0) { ++ asprintf(&tpath, "%s%s", targetpath, dp->d_name); ++ } else { ++ asprintf(&tpath, "%s/%s", targetpath, dp->d_name); ++ } ++ ++ switch (sb.st_mode & S_IFMT) { ++ case S_IFDIR: ++ recursive_add_host_directory(entry, tpath, hpath); ++ break; ++ ++ case S_IFREG: ++ case S_IFSOCK: ++ case S_IFIFO: ++ case S_IFLNK: ++ case S_IFCHR: ++ case S_IFBLK: ++ add_host_filesystem_entry(tpath, hpath, sb.st_uid, ++ sb.st_gid, sb.st_mode, sb.st_rdev, entry); ++ break; ++ ++ default: ++ error_msg("Unknown file type %o for %s", sb.st_mode, hpath); ++ break; ++ } ++ free(dp); ++ free(hpath); ++ free(tpath); ++ } ++ free(namelist); ++ return (entry); ++} ++ ++/* the GNU C library has a wonderful scanf("%as", string) which will ++ allocate the string with the right size, good to avoid buffer overruns. ++ the following macros use it if available or use a hacky workaround... ++ */ ++ ++#ifdef __GNUC__ ++#define SCANF_PREFIX "a" ++#define SCANF_STRING(s) (&s) ++#define GETCWD_SIZE 0 ++#else ++#define SCANF_PREFIX "511" ++#define SCANF_STRING(s) (s = malloc(512)) ++#define GETCWD_SIZE -1 ++inline int snprintf(char *str, size_t n, const char *fmt, ...) ++{ ++ int ret; ++ va_list ap; ++ ++ va_start(ap, fmt); ++ ret = vsprintf(str, fmt, ap); ++ va_end(ap); ++ return ret; ++} ++#endif ++ ++/* device table entries take the form of: ++ ++ /dev/mem c 640 0 0 1 1 0 0 - ++ ++ type can be one of: ++ f A regular file ++ d Directory ++ c Character special device file ++ b Block special device file ++ p Fifo (named pipe) ++ ++ I don't bother with symlinks (permissions are irrelevant), hard ++ links (special cases of regular files), or sockets (why bother). ++ ++ Regular files must exist in the target root directory. If a char, ++ block, fifo, or directory does not exist, it will be created. ++ */ ++static int interpret_table_entry(struct filesystem_entry *root, char *line) ++{ ++ char *hostpath; ++ char type, *name = NULL, *tmp, *dir; ++ unsigned long mode = 0755, uid = 0, gid = 0, major = 0, minor = 0; ++ unsigned long start = 0, increment = 1, count = 0; ++ struct filesystem_entry *parent, *entry; ++ ++ if (sscanf (line, "%" SCANF_PREFIX "s %c %lo %lu %lu %lu %lu %lu %lu %lu", ++ SCANF_STRING(name), &type, &mode, &uid, &gid, &major, &minor, ++ &start, &increment, &count) < 0) ++ { ++ return 1; ++ } ++ ++ if (!strcmp(name, "/")) { ++ error_msg_and_die("Device table entries require absolute paths"); ++ } ++ ++ asprintf(&hostpath, "%s%s", rootdir, name); ++ ++ /* Check if this file already exists... */ ++ switch (type) { ++ case 'd': ++ mode |= S_IFDIR; ++ break; ++ case 'f': ++ mode |= S_IFREG; ++ break; ++ case 'p': ++ mode |= S_IFIFO; ++ break; ++ case 'c': ++ mode |= S_IFCHR; ++ break; ++ case 'b': ++ mode |= S_IFBLK; ++ break; ++ default: ++ error_msg_and_die("Unsupported file type"); ++ } ++ entry = find_filesystem_entry(root, name, mode); ++ if (entry) { ++ /* Ok, we just need to fixup the existing entry ++ * and we will be all done... */ ++ entry->sb.st_uid = uid; ++ entry->sb.st_gid = gid; ++ entry->sb.st_mode = mode; ++ if (major && minor) { ++ entry->sb.st_rdev = makedev(major, minor); ++ } ++ } else { ++ /* If parent is NULL (happens with device table entries), ++ * try and find our parent now) */ ++ tmp = strdup(name); ++ dir = dirname(tmp); ++ parent = find_filesystem_entry(root, dir, S_IFDIR); ++ free(tmp); ++ if (parent == NULL) { ++ error_msg ("skipping device_table entry '%s': no parent directory!", name); ++ free(name); ++ free(hostpath); ++ return 1; ++ } ++ ++ switch (type) { ++ case 'd': ++ add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); ++ break; ++ case 'f': ++ add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); ++ break; ++ case 'p': ++ add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent); ++ break; ++ case 'c': ++ case 'b': ++ if (count > 0) { ++ dev_t rdev; ++ unsigned long i; ++ char *dname, *hpath; ++ ++ for (i = start; i < count; i++) { ++ asprintf(&dname, "%s%lu", name, i); ++ asprintf(&hpath, "%s/%s%lu", rootdir, name, i); ++ rdev = makedev(major, minor + (i * increment - start)); ++ add_host_filesystem_entry(dname, hpath, uid, gid, ++ mode, rdev, parent); ++ free(dname); ++ free(hpath); ++ } ++ } else { ++ dev_t rdev = makedev(major, minor); ++ add_host_filesystem_entry(name, hostpath, uid, gid, ++ mode, rdev, parent); ++ } ++ break; ++ default: ++ error_msg_and_die("Unsupported file type"); ++ } ++ } ++ free(name); ++ free(hostpath); ++ return 0; ++} ++ ++static int parse_device_table(struct filesystem_entry *root, FILE * file) ++{ ++ char *line; ++ int status = 0; ++ size_t length = 0; ++ ++ /* Turn off squash, since we must ensure that values ++ * entered via the device table are not squashed */ ++ squash_uids = 0; ++ squash_perms = 0; ++ ++ /* Looks ok so far. The general plan now is to read in one ++ * line at a time, check for leading comment delimiters ('#'), ++ * then try and parse the line as a device table. If we fail ++ * to parse things, try and help the poor fool to fix their ++ * device table with a useful error msg... */ ++ line = NULL; ++ while (getline(&line, &length, file) != -1) { ++ /* First trim off any whitespace */ ++ int len = strlen(line); ++ ++ /* trim trailing whitespace */ ++ while (len > 0 && isspace(line[len - 1])) ++ line[--len] = '\0'; ++ /* trim leading whitespace */ ++ memmove(line, &line[strspn(line, " \n\r\t\v")], len); ++ ++ /* How long are we after trimming? */ ++ len = strlen(line); ++ ++ /* If this is NOT a comment line, try to interpret it */ ++ if (len && *line != '#') { ++ if (interpret_table_entry(root, line)) ++ status = 1; ++ } ++ ++ free(line); ++ line = NULL; ++ } ++ fclose(file); ++ ++ return status; ++} ++ ++static void cleanup(struct filesystem_entry *dir) ++{ ++ struct filesystem_entry *e, *prev; ++ ++ e = dir->files; ++ while (e) { ++ if (e->name) ++ free(e->name); ++ if (e->path) ++ free(e->path); ++ if (e->fullname) ++ free(e->fullname); ++ e->next = NULL; ++ e->name = NULL; ++ e->path = NULL; ++ e->fullname = NULL; ++ e->prev = NULL; ++ prev = e; ++ if (S_ISDIR(e->sb.st_mode)) { ++ cleanup(e); ++ } ++ e = e->next; ++ free(prev); ++ } ++} ++ ++/* Here is where we do the actual creation of the file system */ ++#include "mtd/jffs2-user.h" ++ ++#define JFFS2_MAX_FILE_SIZE 0xFFFFFFFF ++#ifndef JFFS2_MAX_SYMLINK_LEN ++#define JFFS2_MAX_SYMLINK_LEN 254 ++#endif ++ ++static uint32_t ino = 0; ++static uint8_t *file_buffer = NULL; /* file buffer contains the actual erase block*/ ++static int out_ofs = 0; ++static int erase_block_size = 65536; ++static int pad_fs_size = 0; ++static int add_cleanmarkers = 1; ++static struct jffs2_unknown_node cleanmarker; ++static int cleanmarker_size = sizeof(cleanmarker); ++static unsigned char ffbuf[16] = ++{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, 0xff ++}; ++ ++/* We set this at start of main() using sysconf(), -1 means we don't know */ ++/* When building an fs for non-native systems, use --pagesize=SIZE option */ ++int page_size = -1; ++ ++#include "compr.h" ++ ++static void full_write(int fd, const void *buf, int len) ++{ ++ int ret; ++ ++ while (len > 0) { ++ ret = write(fd, buf, len); ++ ++ if (ret < 0) ++ perror_msg_and_die("write"); ++ ++ if (ret == 0) ++ perror_msg_and_die("write returned zero"); ++ ++ len -= ret; ++ buf += ret; ++ out_ofs += ret; ++ } ++} ++ ++static void padblock(void) ++{ ++ while (out_ofs % erase_block_size) { ++ full_write(out_fd, ffbuf, min(sizeof(ffbuf), ++ erase_block_size - (out_ofs % erase_block_size))); ++ } ++} ++ ++static void pad(int req) ++{ ++ while (req) { ++ if (req > sizeof(ffbuf)) { ++ full_write(out_fd, ffbuf, sizeof(ffbuf)); ++ req -= sizeof(ffbuf); ++ } else { ++ full_write(out_fd, ffbuf, req); ++ req = 0; ++ } ++ } ++} ++ ++static inline void padword(void) ++{ ++ if (out_ofs % 4) { ++ full_write(out_fd, ffbuf, 4 - (out_ofs % 4)); ++ } ++} ++ ++static inline void pad_block_if_less_than(int req) ++{ ++ if (add_cleanmarkers) { ++ if ((out_ofs % erase_block_size) == 0) { ++ full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); ++ pad(cleanmarker_size - sizeof(cleanmarker)); ++ padword(); ++ } ++ } ++ if ((out_ofs % erase_block_size) + req > erase_block_size) { ++ padblock(); ++ } ++ if (add_cleanmarkers) { ++ if ((out_ofs % erase_block_size) == 0) { ++ full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); ++ pad(cleanmarker_size - sizeof(cleanmarker)); ++ padword(); ++ } ++ } ++} ++ ++static void write_dirent(struct filesystem_entry *e) ++{ ++ char *name = e->name; ++ struct jffs2_raw_dirent rd; ++ struct stat *statbuf = &(e->sb); ++ static uint32_t version = 0; ++ ++ memset(&rd, 0, sizeof(rd)); ++ ++ rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); ++ rd.totlen = cpu_to_je32(sizeof(rd) + strlen(name)); ++ rd.hdr_crc = cpu_to_je32(crc32(0, &rd, ++ sizeof(struct jffs2_unknown_node) - 4)); ++ rd.pino = cpu_to_je32((e->parent) ? e->parent->ino : 1); ++ rd.version = cpu_to_je32(version++); ++ rd.ino = cpu_to_je32(e->ino); ++ rd.mctime = cpu_to_je32(statbuf->st_mtime); ++ rd.nsize = strlen(name); ++ rd.type = IFTODT(statbuf->st_mode); ++ //rd.unused[0] = 0; ++ //rd.unused[1] = 0; ++ rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd) - 8)); ++ rd.name_crc = cpu_to_je32(crc32(0, name, strlen(name))); ++ ++ pad_block_if_less_than(sizeof(rd) + rd.nsize); ++ full_write(out_fd, &rd, sizeof(rd)); ++ full_write(out_fd, name, rd.nsize); ++ padword(); ++} ++ ++static unsigned int write_regular_file(struct filesystem_entry *e) ++{ ++ int fd, len; ++ uint32_t ver; ++ unsigned int offset; ++ unsigned char *buf, *cbuf, *wbuf; ++ struct jffs2_raw_inode ri; ++ struct stat *statbuf; ++ unsigned int totcomp = 0; ++ ++ statbuf = &(e->sb); ++ if (statbuf->st_size >= JFFS2_MAX_FILE_SIZE) { ++ error_msg("Skipping file \"%s\" too large.", e->path); ++ return -1; ++ } ++ fd = open(e->hostname, O_RDONLY); ++ if (fd == -1) { ++ perror_msg_and_die("%s: open file", e->hostname); ++ } ++ ++ e->ino = ++ino; ++ mkfs_debug_msg("writing file '%s' ino=%lu parent_ino=%lu", ++ e->name, (unsigned long) e->ino, ++ (unsigned long) e->parent->ino); ++ write_dirent(e); ++ ++ buf = xmalloc(page_size); ++ cbuf = NULL; ++ ++ ver = 0; ++ offset = 0; ++ ++ memset(&ri, 0, sizeof(ri)); ++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ++ ri.ino = cpu_to_je32(e->ino); ++ ri.mode = cpu_to_jemode(statbuf->st_mode); ++ ri.uid = cpu_to_je16(statbuf->st_uid); ++ ri.gid = cpu_to_je16(statbuf->st_gid); ++ ri.atime = cpu_to_je32(statbuf->st_atime); ++ ri.ctime = cpu_to_je32(statbuf->st_ctime); ++ ri.mtime = cpu_to_je32(statbuf->st_mtime); ++ ri.isize = cpu_to_je32(statbuf->st_size); ++ ++ while ((len = read(fd, buf, page_size))) { ++ unsigned char *tbuf = buf; ++ ++ if (len < 0) { ++ perror_msg_and_die("read"); ++ } ++ ++ while (len) { ++ uint32_t dsize, space; ++ uint16_t compression; ++ ++ pad_block_if_less_than(sizeof(ri) + JFFS2_MIN_DATA_LEN); ++ ++ dsize = len; ++ space = ++ erase_block_size - (out_ofs % erase_block_size) - ++ sizeof(ri); ++ if (space > dsize) ++ space = dsize; ++ ++ compression = jffs2_compress(tbuf, &cbuf, &dsize, &space); ++ ++ ri.compr = compression & 0xff; ++ ri.usercompr = (compression >> 8) & 0xff; ++ ++ if (ri.compr) { ++ wbuf = cbuf; ++ } else { ++ wbuf = tbuf; ++ dsize = space; ++ } ++ ++ ri.totlen = cpu_to_je32(sizeof(ri) + space); ++ ri.hdr_crc = cpu_to_je32(crc32(0, ++ &ri, sizeof(struct jffs2_unknown_node) - 4)); ++ ++ ri.version = cpu_to_je32(++ver); ++ ri.offset = cpu_to_je32(offset); ++ ri.csize = cpu_to_je32(space); ++ ri.dsize = cpu_to_je32(dsize); ++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8)); ++ ri.data_crc = cpu_to_je32(crc32(0, wbuf, space)); ++ ++ full_write(out_fd, &ri, sizeof(ri)); ++ totcomp += sizeof(ri); ++ full_write(out_fd, wbuf, space); ++ totcomp += space; ++ padword(); ++ ++ if (tbuf != cbuf) { ++ free(cbuf); ++ cbuf = NULL; ++ } ++ ++ tbuf += dsize; ++ len -= dsize; ++ offset += dsize; ++ ++ } ++ } ++ if (!je32_to_cpu(ri.version)) { ++ /* Was empty file */ ++ pad_block_if_less_than(sizeof(ri)); ++ ++ ri.version = cpu_to_je32(++ver); ++ ri.totlen = cpu_to_je32(sizeof(ri)); ++ ri.hdr_crc = cpu_to_je32(crc32(0, ++ &ri, sizeof(struct jffs2_unknown_node) - 4)); ++ ri.csize = cpu_to_je32(0); ++ ri.dsize = cpu_to_je32(0); ++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8)); ++ ++ full_write(out_fd, &ri, sizeof(ri)); ++ padword(); ++ } ++ free(buf); ++ close(fd); ++ return totcomp; ++} ++ ++static void write_symlink(struct filesystem_entry *e) ++{ ++ int len; ++ struct stat *statbuf; ++ struct jffs2_raw_inode ri; ++ ++ statbuf = &(e->sb); ++ e->ino = ++ino; ++ mkfs_debug_msg("writing symlink '%s' ino=%lu parent_ino=%lu", ++ e->name, (unsigned long) e->ino, ++ (unsigned long) e->parent->ino); ++ write_dirent(e); ++ ++ len = strlen(e->link); ++ if (len > JFFS2_MAX_SYMLINK_LEN) { ++ error_msg("symlink too large. Truncated to %d chars.", ++ JFFS2_MAX_SYMLINK_LEN); ++ len = JFFS2_MAX_SYMLINK_LEN; ++ } ++ ++ memset(&ri, 0, sizeof(ri)); ++ ++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri.totlen = cpu_to_je32(sizeof(ri) + len); ++ ri.hdr_crc = cpu_to_je32(crc32(0, ++ &ri, sizeof(struct jffs2_unknown_node) - 4)); ++ ++ ri.ino = cpu_to_je32(e->ino); ++ ri.mode = cpu_to_jemode(statbuf->st_mode); ++ ri.uid = cpu_to_je16(statbuf->st_uid); ++ ri.gid = cpu_to_je16(statbuf->st_gid); ++ ri.atime = cpu_to_je32(statbuf->st_atime); ++ ri.ctime = cpu_to_je32(statbuf->st_ctime); ++ ri.mtime = cpu_to_je32(statbuf->st_mtime); ++ ri.isize = cpu_to_je32(statbuf->st_size); ++ ri.version = cpu_to_je32(1); ++ ri.csize = cpu_to_je32(len); ++ ri.dsize = cpu_to_je32(len); ++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8)); ++ ri.data_crc = cpu_to_je32(crc32(0, e->link, len)); ++ ++ pad_block_if_less_than(sizeof(ri) + len); ++ full_write(out_fd, &ri, sizeof(ri)); ++ full_write(out_fd, e->link, len); ++ padword(); ++} ++ ++static void write_pipe(struct filesystem_entry *e) ++{ ++ struct stat *statbuf; ++ struct jffs2_raw_inode ri; ++ ++ statbuf = &(e->sb); ++ e->ino = ++ino; ++ if (S_ISDIR(statbuf->st_mode)) { ++ mkfs_debug_msg("writing dir '%s' ino=%lu parent_ino=%lu", ++ e->name, (unsigned long) e->ino, ++ (unsigned long) (e->parent) ? e->parent->ino : 1); ++ } ++ write_dirent(e); ++ ++ memset(&ri, 0, sizeof(ri)); ++ ++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri.totlen = cpu_to_je32(sizeof(ri)); ++ ri.hdr_crc = cpu_to_je32(crc32(0, ++ &ri, sizeof(struct jffs2_unknown_node) - 4)); ++ ++ ri.ino = cpu_to_je32(e->ino); ++ ri.mode = cpu_to_jemode(statbuf->st_mode); ++ ri.uid = cpu_to_je16(statbuf->st_uid); ++ ri.gid = cpu_to_je16(statbuf->st_gid); ++ ri.atime = cpu_to_je32(statbuf->st_atime); ++ ri.ctime = cpu_to_je32(statbuf->st_ctime); ++ ri.mtime = cpu_to_je32(statbuf->st_mtime); ++ ri.isize = cpu_to_je32(0); ++ ri.version = cpu_to_je32(1); ++ ri.csize = cpu_to_je32(0); ++ ri.dsize = cpu_to_je32(0); ++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8)); ++ ri.data_crc = cpu_to_je32(0); ++ ++ pad_block_if_less_than(sizeof(ri)); ++ full_write(out_fd, &ri, sizeof(ri)); ++ padword(); ++} ++ ++static void write_special_file(struct filesystem_entry *e) ++{ ++ jint16_t kdev; ++ struct stat *statbuf; ++ struct jffs2_raw_inode ri; ++ ++ statbuf = &(e->sb); ++ e->ino = ++ino; ++ write_dirent(e); ++ ++ kdev = cpu_to_je16((major(statbuf->st_rdev) << 8) + ++ minor(statbuf->st_rdev)); ++ ++ memset(&ri, 0, sizeof(ri)); ++ ++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri.totlen = cpu_to_je32(sizeof(ri) + sizeof(kdev)); ++ ri.hdr_crc = cpu_to_je32(crc32(0, ++ &ri, sizeof(struct jffs2_unknown_node) - 4)); ++ ++ ri.ino = cpu_to_je32(e->ino); ++ ri.mode = cpu_to_jemode(statbuf->st_mode); ++ ri.uid = cpu_to_je16(statbuf->st_uid); ++ ri.gid = cpu_to_je16(statbuf->st_gid); ++ ri.atime = cpu_to_je32(statbuf->st_atime); ++ ri.ctime = cpu_to_je32(statbuf->st_ctime); ++ ri.mtime = cpu_to_je32(statbuf->st_mtime); ++ ri.isize = cpu_to_je32(statbuf->st_size); ++ ri.version = cpu_to_je32(1); ++ ri.csize = cpu_to_je32(sizeof(kdev)); ++ ri.dsize = cpu_to_je32(sizeof(kdev)); ++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8)); ++ ri.data_crc = cpu_to_je32(crc32(0, &kdev, sizeof(kdev))); ++ ++ pad_block_if_less_than(sizeof(ri) + sizeof(kdev)); ++ full_write(out_fd, &ri, sizeof(ri)); ++ full_write(out_fd, &kdev, sizeof(kdev)); ++ padword(); ++} ++ ++#ifndef WITHOUT_XATTR ++typedef struct xattr_entry { ++ struct xattr_entry *next; ++ uint32_t xid; ++ int xprefix; ++ char *xname; ++ char *xvalue; ++ int name_len; ++ int value_len; ++} xattr_entry_t; ++ ++#define XATTR_BUFFER_SIZE (64 * 1024) /* 64KB */ ++static uint32_t enable_xattr = 0; ++static uint32_t highest_xid = 0; ++static uint32_t highest_xseqno = 0; ++ ++static struct { ++ int xprefix; ++ char *string; ++ int length; ++} xprefix_tbl[] = { ++ { JFFS2_XPREFIX_USER, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN }, ++ { JFFS2_XPREFIX_SECURITY, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN }, ++ { JFFS2_XPREFIX_ACL_ACCESS, POSIX_ACL_XATTR_ACCESS, POSIX_ACL_XATTR_ACCESS_LEN }, ++ { JFFS2_XPREFIX_ACL_DEFAULT, POSIX_ACL_XATTR_DEFAULT, POSIX_ACL_XATTR_DEFAULT_LEN }, ++ { JFFS2_XPREFIX_TRUSTED, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN }, ++ { 0, NULL, 0 } ++}; ++ ++static void formalize_posix_acl(void *xvalue, int *value_len) ++{ ++ struct posix_acl_xattr_header *pacl_header; ++ struct posix_acl_xattr_entry *pent, *plim; ++ struct jffs2_acl_header *jacl_header; ++ struct jffs2_acl_entry *jent; ++ struct jffs2_acl_entry_short *jent_s; ++ char buffer[XATTR_BUFFER_SIZE]; ++ int offset = 0; ++ ++ pacl_header = xvalue;; ++ pent = pacl_header->a_entries; ++ plim = xvalue + *value_len; ++ ++ jacl_header = (struct jffs2_acl_header *)buffer; ++ offset += sizeof(struct jffs2_acl_header); ++ jacl_header->a_version = cpu_to_je32(JFFS2_ACL_VERSION); ++ ++ while (pent < plim) { ++ switch(le16_to_cpu(pent->e_tag)) { ++ case ACL_USER_OBJ: ++ case ACL_GROUP_OBJ: ++ case ACL_MASK: ++ case ACL_OTHER: ++ jent_s = (struct jffs2_acl_entry_short *)(buffer + offset); ++ offset += sizeof(struct jffs2_acl_entry_short); ++ jent_s->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); ++ jent_s->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); ++ break; ++ case ACL_USER: ++ case ACL_GROUP: ++ jent = (struct jffs2_acl_entry *)(buffer + offset); ++ offset += sizeof(struct jffs2_acl_entry); ++ jent->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); ++ jent->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); ++ jent->e_id = cpu_to_je32(le32_to_cpu(pent->e_id)); ++ break; ++ default: ++ printf("%04x : Unknown XATTR entry tag.\n", le16_to_cpu(pent->e_tag)); ++ exit(1); ++ } ++ pent++; ++ } ++ if (offset > *value_len) { ++ printf("Length of JFFS2 ACL expression(%u) is longer than general one(%u).\n", ++ offset, *value_len); ++ exit(1); ++ } ++ memcpy(xvalue, buffer, offset); ++ *value_len = offset; ++} ++ ++static xattr_entry_t *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) ++{ ++ xattr_entry_t *xe; ++ struct jffs2_raw_xattr rx; ++ int name_len; ++ ++ /* create xattr entry */ ++ name_len = strlen(xname); ++ xe = xcalloc(1, sizeof(xattr_entry_t) + name_len + 1 + value_len); ++ xe->next = NULL; ++ xe->xid = ++highest_xid; ++ xe->xprefix = xprefix; ++ xe->xname = ((char *)xe) + sizeof(xattr_entry_t); ++ xe->xvalue = xe->xname + name_len + 1; ++ xe->name_len = name_len; ++ xe->value_len = value_len; ++ strcpy(xe->xname, xname); ++ memcpy(xe->xvalue, xvalue, value_len); ++ ++ /* write xattr node */ ++ memset(&rx, 0, sizeof(rx)); ++ rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR); ++ rx.totlen = cpu_to_je32(PAD(sizeof(rx) + xe->name_len + 1 + xe->value_len)); ++ rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); ++ ++ rx.xid = cpu_to_je32(xe->xid); ++ rx.version = cpu_to_je32(1); /* initial version */ ++ rx.xprefix = xprefix; ++ rx.name_len = xe->name_len; ++ rx.value_len = cpu_to_je16(xe->value_len); ++ rx.data_crc = cpu_to_je32(crc32(0, xe->xname, xe->name_len + 1 + xe->value_len)); ++ rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(rx) - 4)); ++ ++ pad_block_if_less_than(sizeof(rx) + xe->name_len + 1 + xe->value_len); ++ full_write(out_fd, &rx, sizeof(rx)); ++ full_write(out_fd, xe->xname, xe->name_len + 1 + xe->value_len); ++ padword(); ++ ++ return xe; ++} ++ ++#define XATTRENTRY_HASHSIZE 57 ++static xattr_entry_t *find_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) ++{ ++ static xattr_entry_t **xentry_hash = NULL; ++ xattr_entry_t *xe; ++ int index, name_len; ++ ++ /* create hash table */ ++ if (!xentry_hash) ++ xentry_hash = xcalloc(1, sizeof(xe) * XATTRENTRY_HASHSIZE); ++ ++ if (xprefix == JFFS2_XPREFIX_ACL_ACCESS ++ || xprefix == JFFS2_XPREFIX_ACL_DEFAULT) ++ formalize_posix_acl(xvalue, &value_len); ++ ++ name_len = strlen(xname); ++ index = (crc32(0, xname, name_len) ^ crc32(0, xvalue, value_len)) % XATTRENTRY_HASHSIZE; ++ for (xe = xentry_hash[index]; xe; xe = xe->next) { ++ if (xe->xprefix == xprefix ++ && xe->value_len == value_len ++ && !strcmp(xe->xname, xname) ++ && !memcmp(xe->xvalue, xvalue, value_len)) ++ break; ++ } ++ if (!xe) { ++ xe = create_xattr_entry(xprefix, xname, xvalue, value_len); ++ xe->next = xentry_hash[index]; ++ xentry_hash[index] = xe; ++ } ++ return xe; ++} ++ ++static void write_xattr_entry(struct filesystem_entry *e) ++{ ++ struct jffs2_raw_xref ref; ++ struct xattr_entry *xe; ++ char xlist[XATTR_BUFFER_SIZE], xvalue[XATTR_BUFFER_SIZE]; ++ char *xname, *prefix_str; ++ int i, xprefix, prefix_len; ++ int list_sz, offset, name_len, value_len; ++ ++ if (!enable_xattr) ++ return; ++ ++ list_sz = llistxattr(e->hostname, xlist, XATTR_BUFFER_SIZE); ++ if (list_sz < 0) { ++ if (verbose) ++ printf("llistxattr('%s') = %d : %s\n", ++ e->hostname, errno, strerror(errno)); ++ return; ++ } ++ ++ for (offset = 0; offset < list_sz; offset += name_len) { ++ xname = xlist + offset; ++ name_len = strlen(xname) + 1; ++ ++ for (i = 0; (xprefix = xprefix_tbl[i].xprefix); i++) { ++ prefix_str = xprefix_tbl[i].string; ++ prefix_len = xprefix_tbl[i].length; ++ if (prefix_str[prefix_len - 1] == '.') { ++ if (!strncmp(xname, prefix_str, prefix_len - 1)) ++ break; ++ } else { ++ if (!strcmp(xname, prefix_str)) ++ break; ++ } ++ } ++ if (!xprefix) { ++ if (verbose) ++ printf("%s: xattr '%s' is not supported.\n", ++ e->hostname, xname); ++ continue; ++ } ++ if ((enable_xattr & (1 << xprefix)) == 0) ++ continue; ++ ++ value_len = lgetxattr(e->hostname, xname, xvalue, XATTR_BUFFER_SIZE); ++ if (value_len < 0) { ++ if (verbose) ++ printf("lgetxattr('%s', '%s') = %d : %s\n", ++ e->hostname, xname, errno, strerror(errno)); ++ continue; ++ } ++ xe = find_xattr_entry(xprefix, xname + prefix_len, xvalue, value_len); ++ if (!xe) { ++ if (verbose) ++ printf("%s : xattr '%s' was ignored.\n", ++ e->hostname, xname); ++ continue; ++ } ++ ++ memset(&ref, 0, sizeof(ref)); ++ ref.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ref.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF); ++ ref.totlen = cpu_to_je32(sizeof(ref)); ++ ref.hdr_crc = cpu_to_je32(crc32(0, &ref, sizeof(struct jffs2_unknown_node) - 4)); ++ ref.ino = cpu_to_je32(e->ino); ++ ref.xid = cpu_to_je32(xe->xid); ++ ref.xseqno = cpu_to_je32(highest_xseqno += 2); ++ ref.node_crc = cpu_to_je32(crc32(0, &ref, sizeof(ref) - 4)); ++ ++ pad_block_if_less_than(sizeof(ref)); ++ full_write(out_fd, &ref, sizeof(ref)); ++ padword(); ++ } ++} ++ ++#else /* WITHOUT_XATTR */ ++#define write_xattr_entry(x) ++#endif ++ ++static void recursive_populate_directory(struct filesystem_entry *dir) ++{ ++ struct filesystem_entry *e; ++ unsigned int wrote; ++ ++ if (verbose) { ++ printf("%s\n", dir->fullname); ++ } ++ write_xattr_entry(dir); /* for '/' */ ++ ++ e = dir->files; ++ while (e) { ++ if (e->sb.st_nlink >= 1 && ++ (e->ino = find_hardlink(e))) { ++ ++ write_dirent(e); ++ if (verbose) { ++ printf("\tL %04o %9lu %5d:%-3d %s\n", ++ e->sb.st_mode & ~S_IFMT, (unsigned long) e->ino, ++ (int) (e->sb.st_uid), (int) (e->sb.st_gid), ++ e->name); ++ } ++ } else switch (e->sb.st_mode & S_IFMT) { ++ case S_IFDIR: ++ if (verbose) { ++ printf("\td %04o %9lu %5d:%-3d %s\n", ++ e->sb.st_mode & ~S_IFMT, e->sb.st_size, ++ (int) (e->sb.st_uid), (int) (e->sb.st_gid), ++ e->name); ++ } ++ write_pipe(e); ++ write_xattr_entry(e); ++ break; ++ case S_IFSOCK: ++ if (verbose) { ++ printf("\ts %04o %9lu %5d:%-3d %s\n", ++ e->sb.st_mode & ~S_IFMT, e->sb.st_size, ++ (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); ++ } ++ write_pipe(e); ++ write_xattr_entry(e); ++ break; ++ case S_IFIFO: ++ if (verbose) { ++ printf("\tp %04o %9lu %5d:%-3d %s\n", ++ e->sb.st_mode & ~S_IFMT, e->sb.st_size, ++ (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); ++ } ++ write_pipe(e); ++ write_xattr_entry(e); ++ break; ++ case S_IFCHR: ++ if (verbose) { ++ printf("\tc %04o %4d,%4d %5d:%-3d %s\n", ++ e->sb.st_mode & ~S_IFMT, major(e->sb.st_rdev), ++ minor(e->sb.st_rdev), (int) e->sb.st_uid, ++ (int) e->sb.st_gid, e->name); ++ } ++ write_special_file(e); ++ write_xattr_entry(e); ++ break; ++ case S_IFBLK: ++ if (verbose) { ++ printf("\tb %04o %4d,%4d %5d:%-3d %s\n", ++ e->sb.st_mode & ~S_IFMT, major(e->sb.st_rdev), ++ minor(e->sb.st_rdev), (int) e->sb.st_uid, ++ (int) e->sb.st_gid, e->name); ++ } ++ write_special_file(e); ++ write_xattr_entry(e); ++ break; ++ case S_IFLNK: ++ if (verbose) { ++ printf("\tl %04o %9lu %5d:%-3d %s -> %s\n", ++ e->sb.st_mode & ~S_IFMT, e->sb.st_size, ++ (int) e->sb.st_uid, (int) e->sb.st_gid, e->name, ++ e->link); ++ } ++ write_symlink(e); ++ write_xattr_entry(e); ++ break; ++ case S_IFREG: ++ wrote = write_regular_file(e); ++ write_xattr_entry(e); ++ if (verbose) { ++ printf("\tf %04o %9lu (%9u) %5d:%-3d %s\n", ++ e->sb.st_mode & ~S_IFMT, e->sb.st_size, wrote, ++ (int) e->sb.st_uid, (int) e->sb.st_gid, e->name); ++ } ++ break; ++ default: ++ error_msg("Unknown mode %o for %s", e->sb.st_mode, ++ e->fullname); ++ break; ++ } ++ e = e->next; ++ } ++ ++ e = dir->files; ++ while (e) { ++ if (S_ISDIR(e->sb.st_mode)) { ++ if (e->files) { ++ recursive_populate_directory(e); ++ } else if (verbose) { ++ printf("%s\n", e->fullname); ++ } ++ } ++ e = e->next; ++ } ++} ++ ++static void create_target_filesystem(struct filesystem_entry *root) ++{ ++ cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); ++ cleanmarker.totlen = cpu_to_je32(cleanmarker_size); ++ cleanmarker.hdr_crc = cpu_to_je32(crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); ++ ++ if (ino == 0) ++ ino = 1; ++ ++ root->ino = 1; ++ recursive_populate_directory(root); ++ ++ if (pad_fs_size == -1) { ++ padblock(); ++ } else { ++ if (pad_fs_size && add_cleanmarkers){ ++ padblock(); ++ while (out_ofs < pad_fs_size) { ++ full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); ++ pad(cleanmarker_size - sizeof(cleanmarker)); ++ padblock(); ++ } ++ } else { ++ while (out_ofs < pad_fs_size) { ++ full_write(out_fd, ffbuf, min(sizeof(ffbuf), pad_fs_size - out_ofs)); ++ } ++ ++ } ++ } ++} ++ ++static struct option long_options[] = { ++ {"pad", 2, NULL, 'p'}, ++ {"root", 1, NULL, 'r'}, ++ {"pagesize", 1, NULL, 's'}, ++ {"eraseblock", 1, NULL, 'e'}, ++ {"output", 1, NULL, 'o'}, ++ {"help", 0, NULL, 'h'}, ++ {"verbose", 0, NULL, 'v'}, ++ {"version", 0, NULL, 'V'}, ++ {"big-endian", 0, NULL, 'b'}, ++ {"little-endian", 0, NULL, 'l'}, ++ {"no-cleanmarkers", 0, NULL, 'n'}, ++ {"cleanmarker", 1, NULL, 'c'}, ++ {"squash", 0, NULL, 'q'}, ++ {"squash-uids", 0, NULL, 'U'}, ++ {"squash-perms", 0, NULL, 'P'}, ++ {"faketime", 0, NULL, 'f'}, ++ {"devtable", 1, NULL, 'D'}, ++ {"compression-mode", 1, NULL, 'm'}, ++ {"disable-compressor", 1, NULL, 'x'}, ++ {"test-compression", 0, NULL, 't'}, ++ {"compressor-priority", 1, NULL, 'y'}, ++ {"incremental", 1, NULL, 'i'}, ++#ifndef WITHOUT_XATTR ++ {"with-xattr", 0, NULL, 1000 }, ++ {"with-selinux", 0, NULL, 1001 }, ++ {"with-posix-acl", 0, NULL, 1002 }, ++#endif ++ {NULL, 0, NULL, 0} ++}; ++ ++static char *helptext = ++"Usage: mkfs.jffs2 [OPTIONS]\n" ++"Make a JFFS2 file system image from an existing directory tree\n\n" ++"Options:\n" ++" -p, --pad[=SIZE] Pad output to SIZE bytes with 0xFF. If SIZE is\n" ++" not specified, the output is padded to the end of\n" ++" the final erase block\n" ++" -r, -d, --root=DIR Build file system from directory DIR (default: cwd)\n" ++" -s, --pagesize=SIZE Use page size (max data node size) SIZE (default: 4KiB)\n" ++" -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)\n" ++" -c, --cleanmarker=SIZE Size of cleanmarker (default 12)\n" ++" -m, --compr-mode=MODE Select compression mode (default: priortiry)\n" ++" -x, --disable-compressor=COMPRESSOR_NAME\n" ++" Disable a compressor\n" ++" -X, --enable-compressor=COMPRESSOR_NAME\n" ++" Enable a compressor\n" ++" -y, --compressor-priority=PRIORITY:COMPRESSOR_NAME\n" ++" Set the priority of a compressor\n" ++" -L, --list-compressors Show the list of the avaiable compressors\n" ++" -t, --test-compression Call decompress and compare with the original (for test)\n" ++" -n, --no-cleanmarkers Don't add a cleanmarker to every eraseblock\n" ++" -o, --output=FILE Output to FILE (default: stdout)\n" ++" -l, --little-endian Create a little-endian filesystem\n" ++" -b, --big-endian Create a big-endian filesystem\n" ++" -D, --devtable=FILE Use the named FILE as a device table file\n" ++" -f, --faketime Change all file times to '0' for regression testing\n" ++" -q, --squash Squash permissions and owners making all files be owned by root\n" ++" -U, --squash-uids Squash owners making all files be owned by root\n" ++" -P, --squash-perms Squash permissions on all files\n" ++#ifndef WITHOUT_XATTR ++" --with-xattr stuff all xattr entries into image\n" ++" --with-selinux stuff only SELinux Labels into jffs2 image\n" ++" --with-posix-acl stuff only POSIX ACL entries into jffs2 image\n" ++#endif ++" -h, --help Display this help text\n" ++" -v, --verbose Verbose operation\n" ++" -V, --version Display version information\n" ++" -i, --incremental=FILE Parse FILE and generate appendage output for it\n\n"; ++ ++static char *revtext = "1.60"; ++ ++int load_next_block() { ++ ++ int ret; ++ ret = read(in_fd, file_buffer, erase_block_size); ++ ++ if(verbose) ++ printf("Load next block : %d bytes read\n",ret); ++ ++ return ret; ++} ++ ++void process_buffer(int inp_size) { ++ uint8_t *p = file_buffer; ++ union jffs2_node_union *node; ++ uint16_t type; ++ int bitchbitmask = 0; ++ int obsolete; ++ ++ char name[256]; ++ ++ while ( p < (file_buffer + inp_size)) { ++ ++ node = (union jffs2_node_union *) p; ++ ++ /* Skip empty space */ ++ if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { ++ p += 4; ++ continue; ++ } ++ ++ if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { ++ if (!bitchbitmask++) ++ printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - file_buffer, je16_to_cpu (node->u.magic)); ++ p += 4; ++ continue; ++ } ++ ++ bitchbitmask = 0; ++ ++ type = je16_to_cpu(node->u.nodetype); ++ if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { ++ obsolete = 1; ++ type |= JFFS2_NODE_ACCURATE; ++ } else ++ obsolete = 0; ++ ++ node->u.nodetype = cpu_to_je16(type); ++ ++ switch(je16_to_cpu(node->u.nodetype)) { ++ ++ case JFFS2_NODETYPE_INODE: ++ if(verbose) ++ printf ("%8s Inode node at 0x%08x, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", ++ obsolete ? "Obsolete" : "", ++ p - file_buffer, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino), ++ je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize), ++ je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset)); ++ ++ if ( je32_to_cpu (node->i.ino) > ino ) ++ ino = je32_to_cpu (node->i.ino); ++ ++ p += PAD(je32_to_cpu (node->i.totlen)); ++ break; ++ ++ case JFFS2_NODETYPE_DIRENT: ++ memcpy (name, node->d.name, node->d.nsize); ++ name [node->d.nsize] = 0x0; ++ ++ if(verbose) ++ printf ("%8s Dirent node at 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n", ++ obsolete ? "Obsolete" : "", ++ p - file_buffer, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), ++ je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), ++ node->d.nsize, name); ++ ++ p += PAD(je32_to_cpu (node->d.totlen)); ++ break; ++ ++ case JFFS2_NODETYPE_CLEANMARKER: ++ if (verbose) { ++ printf ("%8s Cleanmarker at 0x%08x, totlen 0x%08x\n", ++ obsolete ? "Obsolete" : "", ++ p - file_buffer, je32_to_cpu (node->u.totlen)); ++ } ++ ++ p += PAD(je32_to_cpu (node->u.totlen)); ++ break; ++ ++ case JFFS2_NODETYPE_PADDING: ++ if (verbose) { ++ printf ("%8s Padding node at 0x%08x, totlen 0x%08x\n", ++ obsolete ? "Obsolete" : "", ++ p - file_buffer, je32_to_cpu (node->u.totlen)); ++ } ++ ++ p += PAD(je32_to_cpu (node->u.totlen)); ++ break; ++ ++ case 0xffff: ++ p += 4; ++ break; ++ ++ default: ++ if (verbose) { ++ printf ("%8s Unknown node at 0x%08x, totlen 0x%08x\n", ++ obsolete ? "Obsolete" : "", ++ p - file_buffer, je32_to_cpu (node->u.totlen)); ++ } ++ ++ p += PAD(je32_to_cpu (node->u.totlen)); ++ } ++ } ++} ++ ++void parse_image(){ ++ int ret; ++ ++ file_buffer = malloc(erase_block_size); ++ ++ if (!file_buffer) { ++ perror("out of memory"); ++ close (in_fd); ++ close (out_fd); ++ exit(1); ++ } ++ ++ while ((ret = load_next_block())) { ++ process_buffer(ret); ++ } ++ ++ if (file_buffer) ++ free(file_buffer); ++ ++ close(in_fd); ++} ++ ++int main(int argc, char **argv) ++{ ++ int c, opt; ++ char *cwd; ++ struct stat sb; ++ FILE *devtable = NULL; ++ struct filesystem_entry *root; ++ char *compr_name = NULL; ++ int compr_prior = -1; ++ int warn_page_size = 0; ++ ++ page_size = sysconf(_SC_PAGESIZE); ++ if (page_size < 0) /* System doesn't know so ... */ ++ page_size = 4096; /* ... we make an educated guess */ ++ if (page_size != 4096) ++ warn_page_size = 1; /* warn user if page size not 4096 */ ++ ++ jffs2_compressors_init(); ++ ++ while ((opt = getopt_long(argc, argv, ++ "D:d:r:s:o:qUPfh?vVe:lbp::nc:m:x:X:Lty:i:", long_options, &c)) >= 0) ++ { ++ switch (opt) { ++ case 'D': ++ devtable = xfopen(optarg, "r"); ++ if (fstat(fileno(devtable), &sb) < 0) ++ perror_msg_and_die(optarg); ++ if (sb.st_size < 10) ++ error_msg_and_die("%s: not a proper device table file", optarg); ++ break; ++ ++ case 'r': ++ case 'd': /* for compatibility with mkfs.jffs, genext2fs, etc... */ ++ if (rootdir != default_rootdir) { ++ error_msg_and_die("root directory specified more than once"); ++ } ++ rootdir = xstrdup(optarg); ++ break; ++ ++ case 's': ++ page_size = strtol(optarg, NULL, 0); ++ warn_page_size = 0; /* set by user, so don't need to warn */ ++ break; ++ ++ case 'o': ++ if (out_fd != -1) { ++ error_msg_and_die("output filename specified more than once"); ++ } ++ out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); ++ if (out_fd == -1) { ++ perror_msg_and_die("open output file"); ++ } ++ break; ++ ++ case 'q': ++ squash_uids = 1; ++ squash_perms = 1; ++ break; ++ ++ case 'U': ++ squash_uids = 1; ++ break; ++ ++ case 'P': ++ squash_perms = 1; ++ break; ++ ++ case 'f': ++ fake_times = 1; ++ break; ++ ++ case 'h': ++ case '?': ++ error_msg_and_die(helptext); ++ ++ case 'v': ++ verbose = 1; ++ break; ++ ++ case 'V': ++ error_msg_and_die("revision %s\n", revtext); ++ ++ case 'e': { ++ char *next; ++ unsigned units = 0; ++ erase_block_size = strtol(optarg, &next, 0); ++ if (!erase_block_size) ++ error_msg_and_die("Unrecognisable erase size\n"); ++ ++ if (*next) { ++ if (!strcmp(next, "KiB")) { ++ units = 1024; ++ } else if (!strcmp(next, "MiB")) { ++ units = 1024 * 1024; ++ } else { ++ error_msg_and_die("Unknown units in erasesize\n"); ++ } ++ } else { ++ if (erase_block_size < 0x1000) ++ units = 1024; ++ else ++ units = 1; ++ } ++ erase_block_size *= units; ++ ++ /* If it's less than 8KiB, they're not allowed */ ++ if (erase_block_size < 0x2000) { ++ fprintf(stderr, "Erase size 0x%x too small. Increasing to 8KiB minimum\n", ++ erase_block_size); ++ erase_block_size = 0x2000; ++ } ++ break; ++ } ++ ++ case 'l': ++ target_endian = __LITTLE_ENDIAN; ++ break; ++ ++ case 'b': ++ target_endian = __BIG_ENDIAN; ++ break; ++ ++ case 'p': ++ if (optarg) ++ pad_fs_size = strtol(optarg, NULL, 0); ++ else ++ pad_fs_size = -1; ++ break; ++ case 'n': ++ add_cleanmarkers = 0; ++ break; ++ case 'c': ++ cleanmarker_size = strtol(optarg, NULL, 0); ++ if (cleanmarker_size < sizeof(cleanmarker)) { ++ error_msg_and_die("cleanmarker size must be >= 12"); ++ } ++ if (cleanmarker_size >= erase_block_size) { ++ error_msg_and_die("cleanmarker size must be < eraseblock size"); ++ } ++ break; ++ case 'm': ++ if (jffs2_set_compression_mode_name(optarg)) { ++ error_msg_and_die("Unknown compression mode %s", optarg); ++ } ++ break; ++ case 'x': ++ if (jffs2_disable_compressor_name(optarg)) { ++ error_msg_and_die("Unknown compressor name %s",optarg); ++ } ++ break; ++ case 'X': ++ if (jffs2_enable_compressor_name(optarg)) { ++ error_msg_and_die("Unknown compressor name %s",optarg); ++ } ++ break; ++ case 'L': ++ error_msg_and_die("\n%s",jffs2_list_compressors()); ++ break; ++ case 't': ++ jffs2_compression_check_set(1); ++ break; ++ case 'y': ++ compr_name = malloc(strlen(optarg)); ++ sscanf(optarg,"%d:%s",&compr_prior,compr_name); ++ if ((compr_prior>=0)&&(compr_name)) { ++ if (jffs2_set_compressor_priority(compr_name, compr_prior)) ++ exit(EXIT_FAILURE); ++ } ++ else { ++ error_msg_and_die("Cannot parse %s",optarg); ++ } ++ free(compr_name); ++ break; ++ case 'i': ++ if (in_fd != -1) { ++ error_msg_and_die("(incremental) filename specified more than once"); ++ } ++ in_fd = open(optarg, O_RDONLY); ++ if (in_fd == -1) { ++ perror_msg_and_die("cannot open (incremental) file"); ++ } ++ break; ++#ifndef WITHOUT_XATTR ++ case 1000: /* --with-xattr */ ++ enable_xattr |= (1 << JFFS2_XPREFIX_USER) ++ | (1 << JFFS2_XPREFIX_SECURITY) ++ | (1 << JFFS2_XPREFIX_ACL_ACCESS) ++ | (1 << JFFS2_XPREFIX_ACL_DEFAULT) ++ | (1 << JFFS2_XPREFIX_TRUSTED); ++ break; ++ case 1001: /* --with-selinux */ ++ enable_xattr |= (1 << JFFS2_XPREFIX_SECURITY); ++ break; ++ case 1002: /* --with-posix-acl */ ++ enable_xattr |= (1 << JFFS2_XPREFIX_ACL_ACCESS) ++ | (1 << JFFS2_XPREFIX_ACL_DEFAULT); ++ break; ++#endif ++ } ++ } ++ if (warn_page_size) { ++ error_msg("Page size for this system is by default %d", page_size); ++ error_msg("Use the --pagesize=SIZE option if this is not what you want"); ++ } ++ if (out_fd == -1) { ++ if (isatty(1)) { ++ error_msg_and_die(helptext); ++ } ++ out_fd = 1; ++ } ++ if (lstat(rootdir, &sb)) { ++ perror_msg_and_die("%s", rootdir); ++ } ++ if (chdir(rootdir)) ++ perror_msg_and_die("%s", rootdir); ++ ++ if (!(cwd = getcwd(0, GETCWD_SIZE))) ++ perror_msg_and_die("getcwd failed"); ++ ++ if(in_fd != -1) ++ parse_image(); ++ ++ root = recursive_add_host_directory(NULL, "/", cwd); ++ ++ if (devtable) ++ parse_device_table(root, devtable); ++ ++ create_target_filesystem(root); ++ ++ cleanup(root); ++ ++ if (rootdir != default_rootdir) ++ free(rootdir); ++ ++ close(out_fd); ++ ++ if (verbose) { ++ char *s = jffs2_stats(); ++ fprintf(stderr,"\n\n%s",s); ++ free(s); ++ } ++ if ((verbose)||(jffs2_compression_check_get()&&(jffs2_compression_check_errorcnt_get()))) { ++ fprintf(stderr,"Compression errors: %d\n",jffs2_compression_check_errorcnt_get()); ++ } ++ ++ jffs2_compressors_exit(); ++ ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/mtd-utils.spec linux-2.6.24.7/drivers/mtd/mtd-utils/mtd-utils.spec +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/mtd-utils.spec 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/mtd-utils.spec 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,40 @@ ++Summary: Tools for maintaining Memory Technology Devices ++Name: mtd-utils ++Version: 1.0 ++Release: 1 ++License: GPL ++Group: Applications/System ++URL: http://www.linux-mtd.infradead.org/ ++Source0: %{name}-%{version}.tar.gz ++BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root ++ ++%description ++This package contains tools for erasing and formatting flash devices, ++including JFFS2, M-Systems DiskOnChip devices, etc. ++ ++%prep ++%setup -q ++ ++%build ++make -C util ++ ++%install ++rm -rf $RPM_BUILD_ROOT ++make DESTDIR=$RPM_BUILD_ROOT -C util install ++ ++%clean ++rm -rf $RPM_BUILD_ROOT ++ ++ ++%files ++%defattr(-,root,root,-) ++/usr/sbin ++/usr/man/man1/mkfs.jffs2.1.gz ++/usr/include/mtd ++%doc ++ ++ ++%changelog ++* Wed May 5 2004 - 1.0 ++- Initial build. ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/mtd_debug.c linux-2.6.24.7/drivers/mtd/mtd-utils/mtd_debug.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/mtd_debug.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/mtd_debug.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,418 @@ ++/* ++ * Copyright (c) 2d3D, Inc. ++ * Written by Abraham vd Merwe ++ * 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. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the author nor the names of other contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * MEMGETINFO ++ */ ++static int getmeminfo (int fd,struct mtd_info_user *mtd) ++{ ++ return (ioctl (fd,MEMGETINFO,mtd)); ++} ++ ++/* ++ * MEMERASE ++ */ ++static int memerase (int fd,struct erase_info_user *erase) ++{ ++ return (ioctl (fd,MEMERASE,erase)); ++} ++ ++/* ++ * MEMGETREGIONCOUNT ++ * MEMGETREGIONINFO ++ */ ++static int getregions (int fd,struct region_info_user *regions,int *n) ++{ ++ int i,err; ++ err = ioctl (fd,MEMGETREGIONCOUNT,n); ++ if (err) return (err); ++ for (i = 0; i < *n; i++) ++ { ++ regions[i].regionindex = i; ++ err = ioctl (fd,MEMGETREGIONINFO,®ions[i]); ++ if (err) return (err); ++ } ++ return (0); ++} ++ ++int erase_flash (int fd,u_int32_t offset,u_int32_t bytes) ++{ ++ int err; ++ struct erase_info_user erase; ++ erase.start = offset; ++ erase.length = bytes; ++ err = memerase (fd,&erase); ++ if (err < 0) ++ { ++ perror ("MEMERASE"); ++ return (1); ++ } ++ fprintf (stderr,"Erased %d bytes from address 0x%.8x in flash\n",bytes,offset); ++ return (0); ++} ++ ++void printsize (u_int32_t x) ++{ ++ int i; ++ static const char *flags = "KMGT"; ++ printf ("%u ",x); ++ for (i = 0; x >= 1024 && flags[i] != '\0'; i++) x /= 1024; ++ i--; ++ if (i >= 0) printf ("(%u%c)",x,flags[i]); ++} ++ ++int flash_to_file (int fd,u_int32_t offset,size_t len,const char *filename) ++{ ++ u_int8_t *buf = NULL; ++ int outfd,err; ++ int size = len * sizeof (u_int8_t); ++ int n = len; ++ ++ if (offset != lseek (fd,offset,SEEK_SET)) ++ { ++ perror ("lseek()"); ++ goto err0; ++ } ++ outfd = creat (filename,O_WRONLY); ++ if (outfd < 0) ++ { ++ perror ("creat()"); ++ goto err1; ++ } ++ ++retry: ++ if ((buf = (u_int8_t *) malloc (size)) == NULL) ++ { ++#define BUF_SIZE (64 * 1024 * sizeof (u_int8_t)) ++ fprintf (stderr, "%s: malloc(%#x)\n", __FUNCTION__, size); ++ if (size != BUF_SIZE) { ++ size = BUF_SIZE; ++ fprintf (stderr, "%s: trying buffer size %#x\n", __FUNCTION__, size); ++ goto retry; ++ } ++ perror ("malloc()"); ++ goto err0; ++ } ++ do { ++ if (n <= size) ++ size = n; ++ err = read (fd,buf,size); ++ if (err < 0) ++ { ++ fprintf (stderr, "%s: read, size %#x, n %#x\n", __FUNCTION__, size, n); ++ perror ("read()"); ++ goto err2; ++ } ++ err = write (outfd,buf,size); ++ if (err < 0) ++ { ++ fprintf (stderr, "%s: write, size %#x, n %#x\n", __FUNCTION__, size, n); ++ perror ("write()"); ++ goto err2; ++ } ++ if (err != size) ++ { ++ fprintf (stderr,"Couldn't copy entire buffer to %s. (%d/%d bytes copied)\n",filename,err,size); ++ goto err2; ++ } ++ n -= size; ++ } while (n > 0); ++ ++ if (buf != NULL) ++ free (buf); ++ close (outfd); ++ printf ("Copied %d bytes from address 0x%.8x in flash to %s\n",len,offset,filename); ++ return (0); ++ ++err2: ++ close (outfd); ++err1: ++ if (buf != NULL) ++ free (buf); ++err0: ++ return (1); ++} ++ ++int file_to_flash (int fd,u_int32_t offset,u_int32_t len,const char *filename) ++{ ++ u_int8_t *buf = NULL; ++ FILE *fp; ++ int err; ++ int size = len * sizeof (u_int8_t); ++ int n = len; ++ ++ if (offset != lseek (fd,offset,SEEK_SET)) ++ { ++ perror ("lseek()"); ++ return (1); ++ } ++ if ((fp = fopen (filename,"r")) == NULL) ++ { ++ perror ("fopen()"); ++ return (1); ++ } ++retry: ++ if ((buf = (u_int8_t *) malloc (size)) == NULL) ++ { ++ fprintf (stderr, "%s: malloc(%#x) failed\n", __FUNCTION__, size); ++ if (size != BUF_SIZE) { ++ size = BUF_SIZE; ++ fprintf (stderr, "%s: trying buffer size %#x\n", __FUNCTION__, size); ++ goto retry; ++ } ++ perror ("malloc()"); ++ fclose (fp); ++ return (1); ++ } ++ do { ++ if (n <= size) ++ size = n; ++ if (fread (buf,size,1,fp) != 1 || ferror (fp)) ++ { ++ fprintf (stderr, "%s: fread, size %#x, n %#x\n", __FUNCTION__, size, n); ++ perror ("fread()"); ++ free (buf); ++ fclose (fp); ++ return (1); ++ } ++ err = write (fd,buf,size); ++ if (err < 0) ++ { ++ fprintf (stderr, "%s: write, size %#x, n %#x\n", __FUNCTION__, size, n); ++ perror ("write()"); ++ free (buf); ++ fclose (fp); ++ return (1); ++ } ++ n -= size; ++ } while (n > 0); ++ ++ if (buf != NULL) ++ free (buf); ++ fclose (fp); ++ printf ("Copied %d bytes from %s to address 0x%.8x in flash\n",len,filename,offset); ++ return (0); ++} ++ ++int showinfo (int fd) ++{ ++ int i,err,n; ++ struct mtd_info_user mtd; ++ static struct region_info_user region[1024]; ++ ++ err = getmeminfo (fd,&mtd); ++ if (err < 0) ++ { ++ perror ("MEMGETINFO"); ++ return (1); ++ } ++ ++ err = getregions (fd,region,&n); ++ if (err < 0) ++ { ++ perror ("MEMGETREGIONCOUNT"); ++ return (1); ++ } ++ ++ printf ("mtd.type = "); ++ switch (mtd.type) ++ { ++ case MTD_ABSENT: ++ printf ("MTD_ABSENT"); ++ break; ++ case MTD_RAM: ++ printf ("MTD_RAM"); ++ break; ++ case MTD_ROM: ++ printf ("MTD_ROM"); ++ break; ++ case MTD_NORFLASH: ++ printf ("MTD_NORFLASH"); ++ break; ++ case MTD_NANDFLASH: ++ printf ("MTD_NANDFLASH"); ++ break; ++ case MTD_DATAFLASH: ++ printf ("MTD_DATAFLASH"); ++ break; ++ case MTD_UBIVOLUME: ++ printf ("MTD_UBIVOLUME"); ++ default: ++ printf ("(unknown type - new MTD API maybe?)"); ++ } ++ ++ printf ("\nmtd.flags = "); ++ if (mtd.flags == MTD_CAP_ROM) ++ printf ("MTD_CAP_ROM"); ++ else if (mtd.flags == MTD_CAP_RAM) ++ printf ("MTD_CAP_RAM"); ++ else if (mtd.flags == MTD_CAP_NORFLASH) ++ printf ("MTD_CAP_NORFLASH"); ++ else if (mtd.flags == MTD_CAP_NANDFLASH) ++ printf ("MTD_CAP_NANDFLASH"); ++ else if (mtd.flags == MTD_WRITEABLE) ++ printf ("MTD_WRITEABLE"); ++ else ++ { ++ int first = 1; ++ static struct ++ { ++ const char *name; ++ int value; ++ } flags[] = ++ { ++ { "MTD_WRITEABLE", MTD_WRITEABLE }, ++ { "MTD_BIT_WRITEABLE", MTD_BIT_WRITEABLE }, ++ { "MTD_NO_ERASE", MTD_NO_ERASE }, ++ { "MTD_STUPID_LOCK", MTD_STUPID_LOCK }, ++ { NULL, -1 } ++ }; ++ for (i = 0; flags[i].name != NULL; i++) ++ if (mtd.flags & flags[i].value) ++ { ++ if (first) ++ { ++ printf (flags[i].name); ++ first = 0; ++ } ++ else printf (" | %s",flags[i].name); ++ } ++ } ++ ++ printf ("\nmtd.size = "); ++ printsize (mtd.size); ++ ++ printf ("\nmtd.erasesize = "); ++ printsize (mtd.erasesize); ++ ++ printf ("\nmtd.writesize = "); ++ printsize (mtd.writesize); ++ ++ printf ("\nmtd.oobsize = "); ++ printsize (mtd.oobsize); ++ ++ printf ("\n" ++ "regions = %d\n" ++ "\n", ++ n); ++ ++ for (i = 0; i < n; i++) ++ { ++ printf ("region[%d].offset = 0x%.8x\n" ++ "region[%d].erasesize = ", ++ i,region[i].offset,i); ++ printsize (region[i].erasesize); ++ printf ("\nregion[%d].numblocks = %d\n" ++ "region[%d].regionindex = %d\n", ++ i,region[i].numblocks, ++ i,region[i].regionindex); ++ } ++ return (0); ++} ++ ++void showusage (const char *progname) ++{ ++ fprintf (stderr, ++ "usage: %s info \n" ++ " %s read \n" ++ " %s write \n" ++ " %s erase \n", ++ progname, ++ progname, ++ progname, ++ progname); ++ exit (1); ++} ++ ++#define OPT_INFO 1 ++#define OPT_READ 2 ++#define OPT_WRITE 3 ++#define OPT_ERASE 4 ++ ++int main (int argc,char *argv[]) ++{ ++ const char *progname; ++ int err = 0,fd,option = OPT_INFO; ++ int open_flag; ++ (progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]); ++ ++ /* parse command-line options */ ++ if (argc == 3 && !strcmp (argv[1],"info")) ++ option = OPT_INFO; ++ else if (argc == 6 && !strcmp (argv[1],"read")) ++ option = OPT_READ; ++ else if (argc == 6 && !strcmp (argv[1],"write")) ++ option = OPT_WRITE; ++ else if (argc == 5 && !strcmp (argv[1],"erase")) ++ option = OPT_ERASE; ++ else ++ showusage (progname); ++ ++ /* open device */ ++ open_flag = (option==OPT_INFO || option==OPT_READ) ? O_RDONLY : O_RDWR; ++ if ((fd = open (argv[2],O_SYNC | open_flag)) < 0) ++ { ++ perror ("open()"); ++ exit (1); ++ } ++ ++ switch (option) ++ { ++ case OPT_INFO: ++ showinfo (fd); ++ break; ++ case OPT_READ: ++ err = flash_to_file (fd,strtol (argv[3],NULL,0),strtol (argv[4],NULL,0),argv[5]); ++ break; ++ case OPT_WRITE: ++ err = file_to_flash (fd,strtol (argv[3],NULL,0),strtol (argv[4],NULL,0),argv[5]); ++ break; ++ case OPT_ERASE: ++ err = erase_flash (fd,strtol (argv[3],NULL,0),strtol (argv[4],NULL,0)); ++ break; ++ } ++ ++ /* close device */ ++ if (close (fd) < 0) ++ perror ("close()"); ++ ++ exit (err); ++} ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/nanddump.c linux-2.6.24.7/drivers/mtd/mtd-utils/nanddump.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/nanddump.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/nanddump.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,403 @@ ++/* ++ * nanddump.c ++ * ++ * Copyright (C) 2000 David Woodhouse (dwmw2@infradead.org) ++ * Steven J. Hill (sjhill@realitydiluted.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. ++ * ++ * Overview: ++ * This utility dumps the contents of raw NAND chips or NAND ++ * chips contained in DoC devices. ++ */ ++ ++#define _GNU_SOURCE ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define PROGRAM "nanddump" ++#define VERSION "$Revision: 1.1.1.1 $" ++ ++struct nand_oobinfo none_oobinfo = { ++ .useecc = MTD_NANDECC_OFF, ++}; ++ ++void display_help (void) ++{ ++ printf("Usage: nanddump [OPTIONS] MTD-device\n" ++ "Dumps the contents of a nand mtd partition.\n" ++ "\n" ++ " --help display this help and exit\n" ++ " --version output version information and exit\n" ++ "-f file --file=file dump to file\n" ++ "-i --ignoreerrors ignore errors\n" ++ "-l length --length=length length\n" ++ "-n --noecc read without error correction\n" ++ "-o --omitoob omit oob data\n" ++ "-b --omitbad omit bad blocks from the dump\n" ++ "-p --prettyprint print nice (hexdump)\n" ++ "-s addr --startaddress=addr start address\n"); ++ exit(0); ++} ++ ++void display_version (void) ++{ ++ printf(PROGRAM " " VERSION "\n" ++ "\n" ++ PROGRAM " comes with NO WARRANTY\n" ++ "to the extent permitted by law.\n" ++ "\n" ++ "You may redistribute copies of " PROGRAM "\n" ++ "under the terms of the GNU General Public Licence.\n" ++ "See the file `COPYING' for more information.\n"); ++ exit(0); ++} ++ ++// Option variables ++ ++int ignoreerrors; // ignore errors ++int pretty_print; // print nice in ascii ++int noecc; // don't error correct ++int omitoob; // omit oob data ++unsigned long start_addr; // start address ++unsigned long length; // dump length ++char *mtddev; // mtd device name ++char *dumpfile; // dump file name ++int omitbad; ++ ++void process_options (int argc, char *argv[]) ++{ ++ int error = 0; ++ ++ for (;;) { ++ int option_index = 0; ++ static const char *short_options = "bs:f:il:opn"; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 0}, ++ {"version", no_argument, 0, 0}, ++ {"file", required_argument, 0, 'f'}, ++ {"ignoreerrors", no_argument, 0, 'i'}, ++ {"prettyprint", no_argument, 0, 'p'}, ++ {"omitoob", no_argument, 0, 'o'}, ++ {"omitbad", no_argument, 0, 'b'}, ++ {"startaddress", required_argument, 0, 's'}, ++ {"length", required_argument, 0, 'l'}, ++ {"noecc", no_argument, 0, 'n'}, ++ {0, 0, 0, 0}, ++ }; ++ ++ int c = getopt_long(argc, argv, short_options, ++ long_options, &option_index); ++ if (c == EOF) { ++ break; ++ } ++ ++ switch (c) { ++ case 0: ++ switch (option_index) { ++ case 0: ++ display_help(); ++ break; ++ case 1: ++ display_version(); ++ break; ++ } ++ break; ++ case 'b': ++ omitbad = 1; ++ break; ++ case 's': ++ start_addr = strtol(optarg, NULL, 0); ++ break; ++ case 'f': ++ if (!(dumpfile = strdup(optarg))) { ++ perror("stddup"); ++ exit(1); ++ } ++ break; ++ case 'i': ++ ignoreerrors = 1; ++ break; ++ case 'l': ++ length = strtol(optarg, NULL, 0); ++ break; ++ case 'o': ++ omitoob = 1; ++ break; ++ case 'p': ++ pretty_print = 1; ++ break; ++ case 'n': ++ noecc = 1; ++ break; ++ case '?': ++ error = 1; ++ break; ++ } ++ } ++ ++ if ((argc - optind) != 1 || error) ++ display_help (); ++ ++ mtddev = argv[optind]; ++} ++ ++/* ++ * Buffers for reading data from flash ++ */ ++unsigned char readbuf[8192]; ++unsigned char oobbuf[256]; ++ ++/* ++ * Main program ++ */ ++int main(int argc, char **argv) ++{ ++ unsigned long ofs, end_addr = 0; ++ unsigned long long blockstart = 1; ++ int ret, i, fd, ofd, bs, badblock = 0; ++ struct mtd_oob_buf oob = {0, 16, oobbuf}; ++ mtd_info_t meminfo; ++ char pretty_buf[80]; ++ int oobinfochanged = 0 ; ++ struct nand_oobinfo old_oobinfo; ++ struct mtd_ecc_stats stat1, stat2; ++ int eccstats = 0; ++ ++ process_options(argc, argv); ++ ++ /* Open MTD device */ ++ if ((fd = open(mtddev, O_RDONLY)) == -1) { ++ perror("open flash"); ++ exit (1); ++ } ++ ++ /* Fill in MTD device capability structure */ ++ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { ++ perror("MEMGETINFO"); ++ close(fd); ++ exit (1); ++ } ++ ++ /* Make sure device page sizes are valid */ ++ if (!(meminfo.oobsize == 256 && meminfo.writesize == 8192) && ++ !(meminfo.oobsize == 128 && meminfo.writesize == 4096) && ++ !(meminfo.oobsize == 64 && meminfo.writesize == 2048) && ++ !(meminfo.oobsize == 32 && meminfo.writesize == 1024) && ++ !(meminfo.oobsize == 16 && meminfo.writesize == 512) && ++ !(meminfo.oobsize == 8 && meminfo.writesize == 256)) { ++ fprintf(stderr, "Unknown flash (not normal NAND)\n"); ++ close(fd); ++ exit(1); ++ } ++ /* Read the real oob length */ ++ oob.length = meminfo.oobsize; ++ ++ if (noecc) { ++ ret = ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW); ++ if (ret == 0) { ++ oobinfochanged = 2; ++ } else { ++ switch (errno) { ++ case ENOTTY: ++ if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { ++ perror ("MEMGETOOBSEL"); ++ close (fd); ++ exit (1); ++ } ++ if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) { ++ perror ("MEMSETOOBSEL"); ++ close (fd); ++ exit (1); ++ } ++ oobinfochanged = 1; ++ break; ++ default: ++ perror ("MTDFILEMODE"); ++ close (fd); ++ exit (1); ++ } ++ } ++ } else { ++ ++ /* check if we can read ecc stats */ ++ if (!ioctl(fd, ECCGETSTATS, &stat1)) { ++ eccstats = 1; ++ fprintf(stderr, "ECC failed: %d\n", stat1.failed); ++ fprintf(stderr, "ECC corrected: %d\n", stat1.corrected); ++ fprintf(stderr, "Number of bad blocks: %d\n", stat1.badblocks); ++ fprintf(stderr, "Number of bbt blocks: %d\n", stat1.bbtblocks); ++ } else ++ perror("No ECC status information available"); ++ } ++ ++ /* Open output file for writing. If file name is "-", write to standard ++ * output. */ ++ if (!dumpfile) { ++ ofd = STDOUT_FILENO; ++ } else if ((ofd = open(dumpfile, O_WRONLY | O_TRUNC | O_CREAT, 0644))== -1) { ++ perror ("open outfile"); ++ close(fd); ++ exit(1); ++ } ++ ++ /* Initialize start/end addresses and block size */ ++ if (length) ++ end_addr = start_addr + length; ++ if (!length || end_addr > meminfo.size) ++ end_addr = meminfo.size; ++ ++ bs = meminfo.writesize; ++ ++ /* Print informative message */ ++ fprintf(stderr, "Block size %u, page size %u, OOB size %u\n", ++ meminfo.erasesize, meminfo.writesize, meminfo.oobsize); ++ fprintf(stderr, ++ "Dumping data starting at 0x%08x and ending at 0x%08x...\n", ++ (unsigned int) start_addr, (unsigned int) end_addr); ++ ++ /* Dump the flash contents */ ++ for (ofs = start_addr; ofs < end_addr ; ofs+=bs) { ++ ++ // new eraseblock , check for bad block ++ if (blockstart != (ofs & (~meminfo.erasesize + 1))) { ++ blockstart = ofs & (~meminfo.erasesize + 1); ++ if ((badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart)) < 0) { ++ perror("ioctl(MEMGETBADBLOCK)"); ++ goto closeall; ++ } ++ } ++ ++ if (badblock) { ++ if (omitbad) ++ continue; ++ memset (readbuf, 0xff, bs); ++ } else { ++ /* Read page data and exit on failure */ ++ if (pread(fd, readbuf, bs, ofs) != bs) { ++ perror("pread"); ++ goto closeall; ++ } ++ } ++ ++ /* ECC stats available ? */ ++ if (eccstats) { ++ if (ioctl(fd, ECCGETSTATS, &stat2)) { ++ perror("ioctl(ECCGETSTATS)"); ++ goto closeall; ++ } ++ if (stat1.failed != stat2.failed) ++ fprintf(stderr, "ECC: %d uncorrectable bitflip(s)" ++ " at offset 0x%08lx\n", ++ stat2.failed - stat1.failed, ofs); ++ if (stat1.corrected != stat2.corrected) ++ fprintf(stderr, "ECC: %d corrected bitflip(s) at" ++ " offset 0x%08lx\n", ++ stat2.corrected - stat1.corrected, ofs); ++ stat1 = stat2; ++ } ++ ++ /* Write out page data */ ++ if (pretty_print) { ++ for (i = 0; i < bs; i += 16) { ++ sprintf(pretty_buf, ++ "0x%08x: %02x %02x %02x %02x %02x %02x %02x " ++ "%02x %02x %02x %02x %02x %02x %02x %02x %02x\n", ++ (unsigned int) (ofs + i), readbuf[i], ++ readbuf[i+1], readbuf[i+2], ++ readbuf[i+3], readbuf[i+4], ++ readbuf[i+5], readbuf[i+6], ++ readbuf[i+7], readbuf[i+8], ++ readbuf[i+9], readbuf[i+10], ++ readbuf[i+11], readbuf[i+12], ++ readbuf[i+13], readbuf[i+14], ++ readbuf[i+15]); ++ write(ofd, pretty_buf, 60); ++ } ++ } else ++ write(ofd, readbuf, bs); ++ ++ ++ ++ if (omitoob) ++ continue; ++ ++ if (badblock) { ++ memset (readbuf, 0xff, meminfo.oobsize); ++ } else { ++ /* Read OOB data and exit on failure */ ++ oob.start = ofs; ++ if (ioctl(fd, MEMREADOOB, &oob) != 0) { ++ perror("ioctl(MEMREADOOB)"); ++ goto closeall; ++ } ++ } ++ ++ /* Write out OOB data */ ++ if (pretty_print) { ++ if (meminfo.oobsize < 16) { ++ sprintf(pretty_buf, " OOB Data: %02x %02x %02x %02x %02x %02x " ++ "%02x %02x\n", ++ oobbuf[0], oobbuf[1], oobbuf[2], ++ oobbuf[3], oobbuf[4], oobbuf[5], ++ oobbuf[6], oobbuf[7]); ++ write(ofd, pretty_buf, 48); ++ continue; ++ } ++ ++ for (i = 0; i < meminfo.oobsize; i += 16) { ++ sprintf(pretty_buf, " OOB Data: %02x %02x %02x %02x %02x %02x " ++ "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", ++ oobbuf[i], oobbuf[i+1], oobbuf[i+2], ++ oobbuf[i+3], oobbuf[i+4], oobbuf[i+5], ++ oobbuf[i+6], oobbuf[i+7], oobbuf[i+8], ++ oobbuf[i+9], oobbuf[i+10], oobbuf[i+11], ++ oobbuf[i+12], oobbuf[i+13], oobbuf[i+14], ++ oobbuf[i+15]); ++ write(ofd, pretty_buf, 60); ++ } ++ } else ++ write(ofd, oobbuf, meminfo.oobsize); ++ } ++ ++ /* reset oobinfo */ ++ if (oobinfochanged == 1) { ++ if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { ++ perror ("MEMSETOOBSEL"); ++ close(fd); ++ close(ofd); ++ return 1; ++ } ++ } ++ /* Close the output file and MTD device */ ++ close(fd); ++ close(ofd); ++ ++ /* Exit happy */ ++ return 0; ++ ++closeall: ++ /* The new mode change is per file descriptor ! */ ++ if (oobinfochanged == 1) { ++ if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { ++ perror ("MEMSETOOBSEL"); ++ } ++ } ++ close(fd); ++ close(ofd); ++ exit(1); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/nanddump_vfat.c linux-2.6.24.7/drivers/mtd/mtd-utils/nanddump_vfat.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/nanddump_vfat.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/nanddump_vfat.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,365 @@ ++/* ++ * nanddump.c ++ * ++ * Copyright (C) 2000 David Woodhouse (dwmw2@infradead.org) ++ * Steven J. Hill (sjhill@realitydiluted.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. ++ * ++ * Overview: ++ * This utility dumps the contents of raw NAND chips or NAND ++ * chips contained in DoC devices. ++ */ ++ ++#define _GNU_SOURCE ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define PROGRAM "nanddump" ++#define VERSION "$Revision: 1.1.1.1 $" ++ ++struct nand_oobinfo none_oobinfo = { ++ .useecc = MTD_NANDECC_OFF, ++}; ++ ++void display_help (void) ++{ ++ printf("Usage: nanddump [OPTIONS] MTD-device\n" ++ "Dumps the contents of a nand mtd partition.\n" ++ "\n" ++ " --help display this help and exit\n" ++ " --version output version information and exit\n" ++ "-f file --file=file dump to file\n" ++ "-i --ignoreerrors ignore errors\n" ++ "-l length --length=length length\n" ++ "-n --noecc read without error correction\n" ++ "-o --omitoob omit oob data\n" ++ "-b --omitbad omit bad blocks from the dump\n" ++ "-p --prettyprint print nice (hexdump)\n" ++ "-s addr --startaddress=addr start address\n"); ++ exit(0); ++} ++ ++void display_version (void) ++{ ++ printf(PROGRAM " " VERSION "\n" ++ "\n" ++ PROGRAM " comes with NO WARRANTY\n" ++ "to the extent permitted by law.\n" ++ "\n" ++ "You may redistribute copies of " PROGRAM "\n" ++ "under the terms of the GNU General Public Licence.\n" ++ "See the file `COPYING' for more information.\n"); ++ exit(0); ++} ++ ++// Option variables ++ ++int ignoreerrors; // ignore errors ++int pretty_print; // print nice in ascii ++int noecc; // don't error correct ++int omitoob; // omit oob data ++unsigned long start_addr; // start address ++unsigned long length; // dump length ++char *mtddev; // mtd device name ++char *dumpfile; // dump file name ++int omitbad; ++ ++void process_options (int argc, char *argv[]) ++{ ++ int error = 0; ++ ++ for (;;) { ++ int option_index = 0; ++ static const char *short_options = "bs:f:il:opn"; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 0}, ++ {"version", no_argument, 0, 0}, ++ {"file", required_argument, 0, 'f'}, ++ {"ignoreerrors", no_argument, 0, 'i'}, ++ {"prettyprint", no_argument, 0, 'p'}, ++ {"omitoob", no_argument, 0, 'o'}, ++ {"omitbad", no_argument, 0, 'b'}, ++ {"startaddress", required_argument, 0, 's'}, ++ {"length", required_argument, 0, 'l'}, ++ {"noecc", no_argument, 0, 'n'}, ++ {0, 0, 0, 0}, ++ }; ++ ++ int c = getopt_long(argc, argv, short_options, ++ long_options, &option_index); ++ if (c == EOF) { ++ break; ++ } ++ ++ switch (c) { ++ case 0: ++ switch (option_index) { ++ case 0: ++ display_help(); ++ break; ++ case 1: ++ display_version(); ++ break; ++ } ++ break; ++ case 'b': ++ omitbad = 1; ++ break; ++ case 's': ++ start_addr = strtol(optarg, NULL, 0); ++ break; ++ case 'f': ++ if (!(dumpfile = strdup(optarg))) { ++ perror("stddup"); ++ exit(1); ++ } ++ break; ++ case 'i': ++ ignoreerrors = 1; ++ break; ++ case 'l': ++ length = strtol(optarg, NULL, 0); ++ break; ++ case 'o': ++ omitoob = 1; ++ break; ++ case 'p': ++ pretty_print = 1; ++ break; ++ case 'n': ++ noecc = 1; ++ break; ++ case '?': ++ error = 1; ++ break; ++ } ++ } ++ ++ if ((argc - optind) != 1 || error) ++ display_help (); ++ ++ mtddev = argv[optind]; ++} ++ ++/* ++ * Buffers for reading data from flash ++ */ ++unsigned char readbuf[8192]; ++unsigned char oobbuf[256]; ++ ++/* ++ * Main program ++ */ ++int main(int argc, char **argv) ++{ ++ unsigned long ofs, end_addr = 0; ++ unsigned long long blockstart = 1; ++ int ret, fd, ofd, bs, badblock = 0; ++ struct mtd_oob_buf oob = {0, 16, oobbuf}; ++ mtd_info_t meminfo; ++ int oobinfochanged = 0 ; ++ struct nand_oobinfo old_oobinfo; ++ struct mtd_ecc_stats stat1, stat2; ++ int eccstats = 0; ++ ++ process_options(argc, argv); ++ ++ /* Open MTD device */ ++ if ((fd = open(mtddev, O_RDONLY)) == -1) { ++ perror("open flash"); ++ exit (1); ++ } ++ ++ /* Fill in MTD device capability structure */ ++ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { ++ perror("MEMGETINFO"); ++ close(fd); ++ exit (1); ++ } ++ ++ /* Make sure device page sizes are valid */ ++ if (!(meminfo.oobsize == 256 && meminfo.writesize == 8192) && ++ !(meminfo.oobsize == 128 && meminfo.writesize == 4096) && ++ !(meminfo.oobsize == 64 && meminfo.writesize == 2048) && ++ !(meminfo.oobsize == 32 && meminfo.writesize == 1024) && ++ !(meminfo.oobsize == 16 && meminfo.writesize == 512) && ++ !(meminfo.oobsize == 8 && meminfo.writesize == 256)) { ++ fprintf(stderr, "Unknown flash (not normal NAND)\n"); ++ close(fd); ++ exit(1); ++ } ++ /* Read the real oob length */ ++ oob.length = meminfo.oobsize; ++ ++ if (noecc) { ++ ret = ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW); ++ if (ret == 0) { ++ oobinfochanged = 2; ++ } else { ++ switch (errno) { ++ case ENOTTY: ++ if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { ++ perror ("MEMGETOOBSEL"); ++ close (fd); ++ exit (1); ++ } ++ if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) { ++ perror ("MEMSETOOBSEL"); ++ close (fd); ++ exit (1); ++ } ++ oobinfochanged = 1; ++ break; ++ default: ++ perror ("MTDFILEMODE"); ++ close (fd); ++ exit (1); ++ } ++ } ++ } else { ++ ++ /* check if we can read ecc stats */ ++ if (!ioctl(fd, ECCGETSTATS, &stat1)) { ++ eccstats = 1; ++ fprintf(stderr, "ECC failed: %d\n", stat1.failed); ++ fprintf(stderr, "ECC corrected: %d\n", stat1.corrected); ++ fprintf(stderr, "Number of bad blocks: %d\n", stat1.badblocks); ++ fprintf(stderr, "Number of bbt blocks: %d\n", stat1.bbtblocks); ++ } else ++ perror("No ECC status information available"); ++ } ++ ++ /* Open output file for writing. If file name is "-", write to standard ++ * output. */ ++ if (!dumpfile) { ++ ofd = STDOUT_FILENO; ++ } else if ((ofd = open(dumpfile, O_WRONLY | O_TRUNC | O_CREAT, 0644))== -1) { ++ perror ("open outfile"); ++ close(fd); ++ exit(1); ++ } ++ ++ /* Initialize start/end addresses and block size */ ++ if (length) ++ end_addr = start_addr + length; ++ if (!length || end_addr > meminfo.size) ++ end_addr = meminfo.size; ++ ++ bs = meminfo.writesize; ++ ++ /* Print informative message */ ++ fprintf(stderr, "Block size %u, page size %u, OOB size %u\n", ++ meminfo.erasesize, meminfo.writesize, meminfo.oobsize); ++ fprintf(stderr, ++ "Dumping data starting at 0x%08x and ending at 0x%08x...\n", ++ (unsigned int) start_addr, (unsigned int) end_addr); ++ ++ /* Dump the flash contents */ ++ for (ofs = start_addr; ofs < end_addr ; ofs+=bs) { ++ ++ // new eraseblock , check for bad block ++ if (blockstart != (ofs & (~meminfo.erasesize + 1))) { ++ blockstart = ofs & (~meminfo.erasesize + 1); ++ if ((badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart)) < 0) { ++ perror("ioctl(MEMGETBADBLOCK)"); ++ goto closeall; ++ } ++ } ++ ++ if (badblock) { ++ //skip bad block; ++ ofs += meminfo.erasesize - bs; ++ continue; ++ } else { ++ /* Read page data and exit on failure */ ++ if (pread(fd, readbuf, bs, ofs) != bs) { ++ perror("pread"); ++ goto closeall; ++ } ++ } ++ ++ /* ECC stats available ? */ ++ if (eccstats) { ++ if (ioctl(fd, ECCGETSTATS, &stat2)) { ++ perror("ioctl(ECCGETSTATS)"); ++ goto closeall; ++ } ++ if (stat1.failed != stat2.failed) ++ fprintf(stderr, "ECC: %d uncorrectable bitflip(s)" ++ " at offset 0x%08lx\n", ++ stat2.failed - stat1.failed, ofs); ++ if (stat1.corrected != stat2.corrected) ++ fprintf(stderr, "ECC: %d corrected bitflip(s) at" ++ " offset 0x%08lx\n", ++ stat2.corrected - stat1.corrected, ofs); ++ stat1 = stat2; ++ } ++ ++ if (badblock) { ++ memset (readbuf, 0xff, meminfo.oobsize); ++ } else { ++ /* Read OOB data and exit on failure */ ++ oob.start = ofs; ++ if (ioctl(fd, MEMREADOOB, &oob) != 0) { ++ perror("ioctl(MEMREADOOB)"); ++ goto closeall; ++ } ++ if(oobbuf[2]==0xff && oobbuf[3]==0xff && oobbuf[4]==0xff && oobbuf[5]==0xff){ ++ //skip free block; ++ ofs += meminfo.erasesize - bs; ++ continue; ++ } ++ } ++ ++ /* Write out page data */ ++ write(ofd, readbuf, bs); ++ ++ if (omitoob) ++ continue; ++ ++ /* Write out OOB data */ ++ write(ofd, oobbuf, meminfo.oobsize); ++ } ++ ++ /* reset oobinfo */ ++ if (oobinfochanged == 1) { ++ if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { ++ perror ("MEMSETOOBSEL"); ++ close(fd); ++ close(ofd); ++ return 1; ++ } ++ } ++ /* Close the output file and MTD device */ ++ close(fd); ++ close(ofd); ++ ++ /* Exit happy */ ++ return 0; ++ ++closeall: ++ /* The new mode change is per file descriptor ! */ ++ if (oobinfochanged == 1) { ++ if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { ++ perror ("MEMSETOOBSEL"); ++ } ++ } ++ close(fd); ++ close(ofd); ++ exit(1); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/nandtest.c linux-2.6.24.7/drivers/mtd/mtd-utils/nandtest.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/nandtest.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/nandtest.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,284 @@ ++#define _GNU_SOURCE ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "mtd/mtd-user.h" ++ ++void usage(void) ++{ ++ fprintf(stderr, "usage: nandtest [OPTIONS] \n\n" ++ " -h, --help Display this help output\n" ++ " -m, --markbad Mark blocks bad if they appear so\n" ++ " -s, --seed Supply random seed\n" ++ " -p, --passes Number of passes\n" ++ " -o, --offset Start offset on flash\n" ++ " -l, --length Length of flash to test\n" ++ " -k, --keep Restore existing contents after test\n" ++ "Warning: it is just used for SLC NAND!\n"); ++ ++ exit(1); ++} ++ ++struct mtd_info_user meminfo; ++struct mtd_ecc_stats oldstats, newstats; ++int fd; ++int markbad=0; ++int seed; ++ ++int erase_and_write(loff_mtd_t ofs, unsigned char *data, unsigned char *rbuf) ++{ ++ struct erase_info_user er; ++ ssize_t len; ++ int i; ++ ++ printf("\r%09llx: erasing... ", (loff_mtd_t)ofs); ++ fflush(stdout); ++ ++ er.start = ofs; ++ er.length = meminfo.erasesize; ++ ++ if (ioctl(fd, MEMERASE, &er)) { ++ perror("MEMERASE"); ++ if (markbad) { ++ printf("Mark block bad at %09llx\n", (loff_mtd_t)ofs); ++ ioctl(fd, MEMSETBADBLOCK, &ofs); ++ } ++ return 1; ++ } ++ ++ printf("\r%09llx: writing...", (loff_mtd_t)ofs); ++ fflush(stdout); ++ ++ len = pwrite(fd, data, meminfo.erasesize, ofs); ++ if (len < 0) { ++ printf("\n"); ++ perror("write"); ++ if (markbad) { ++ printf("Mark block bad at %09llx\n", (loff_mtd_t)ofs); ++ ioctl(fd, MEMSETBADBLOCK, &ofs); ++ } ++ return 1; ++ } ++ if (len < meminfo.erasesize) { ++ printf("\n"); ++ fprintf(stderr, "Short write (%d bytes)\n", len); ++ exit(1); ++ } ++ ++ printf("\r%09llx: reading...", (loff_mtd_t)ofs); ++ fflush(stdout); ++ ++ len = pread(fd, rbuf, meminfo.erasesize, ofs); ++ if (len < meminfo.erasesize) { ++ printf("\n"); ++ if (len) ++ fprintf(stderr, "Short read (%d bytes)\n", len); ++ else ++ perror("read"); ++ exit(1); ++ } ++ ++ if (ioctl(fd, ECCGETSTATS, &newstats)) { ++ printf("\n"); ++ perror("ECCGETSTATS"); ++ close(fd); ++ exit(1); ++ } ++ ++ if (newstats.corrected > oldstats.corrected) { ++ printf("\nECC corrected at %09llx\n", (loff_mtd_t) ofs); ++ oldstats.corrected = newstats.corrected; ++ } ++ if (newstats.failed > oldstats.failed) { ++ printf("\nECC failed at %09llx\n", (loff_mtd_t) ofs); ++ oldstats.corrected = newstats.corrected; ++ } ++ if (len < meminfo.erasesize) ++ exit(1); ++ ++ printf("\r%09llx: checking...", (loff_mtd_t)ofs); ++ fflush(stdout); ++ ++ if (memcmp(data, rbuf, meminfo.erasesize)) { ++ printf("\n"); ++ fprintf(stderr, "compare failed. seed %d\n", seed); ++ for (i=0; i meminfo.size) { ++ fprintf(stderr, "Length %09llx + offset %09llx exceeds device size %09llx\n", ++ length, offset, meminfo.size); ++ exit(1); ++ } ++ ++ wbuf = malloc(meminfo.erasesize * 3); ++ if (!wbuf) { ++ fprintf(stderr, "Could not allocate %d bytes for buffer\n", ++ meminfo.erasesize * 2); ++ exit(1); ++ } ++ rbuf = wbuf + meminfo.erasesize; ++ kbuf = rbuf + meminfo.erasesize; ++ ++ if (ioctl(fd, ECCGETSTATS, &oldstats)) { ++ perror("ECCGETSTATS"); ++ close(fd); ++ exit(1); ++ } ++ ++ printf("ECC corrections: %d\n", oldstats.corrected); ++ printf("ECC failures : %d\n", oldstats.failed); ++ printf("Bad blocks : %d\n", oldstats.badblocks); ++ printf("BBT blocks : %d\n", oldstats.bbtblocks); ++ ++ for (pass = 0; pass < nr_passes; pass++) { ++ loff_mtd_t test_ofs; ++ ++ for (test_ofs = offset; test_ofs < offset+length; test_ofs += meminfo.erasesize) { ++ ssize_t len; ++ ++ seed = rand(); ++ srand(seed); ++ ++ if (ioctl(fd, MEMGETBADBLOCK, &test_ofs)) { ++ printf("\rBad block at 0x%09llx\n", test_ofs); ++ continue; ++ } ++ ++ for (i=0; i ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "mtd/mtd-user.h" ++ ++#define PROGRAM "nandwrite" ++#define VERSION "$Revision: 1.1.1.1 $" ++ ++#define MAX_PAGE_SIZE 4096 ++#define MAX_OOB_SIZE 128 ++ ++/* ++ * Buffer array used for writing data ++ */ ++unsigned char writebuf[MAX_PAGE_SIZE]; ++unsigned char oobbuf[MAX_OOB_SIZE]; ++unsigned char oobreadbuf[MAX_OOB_SIZE]; ++ ++// oob layouts to pass into the kernel as default ++struct nand_oobinfo none_oobinfo = { ++ .useecc = MTD_NANDECC_OFF, ++}; ++ ++struct nand_oobinfo jffs2_oobinfo = { ++ .useecc = MTD_NANDECC_PLACE, ++ .eccbytes = 6, ++ .eccpos = { 0, 1, 2, 3, 6, 7 } ++}; ++ ++struct nand_oobinfo yaffs_oobinfo = { ++ .useecc = MTD_NANDECC_PLACE, ++ .eccbytes = 6, ++ .eccpos = { 8, 9, 10, 13, 14, 15} ++}; ++ ++struct nand_oobinfo autoplace_oobinfo = { ++ .useecc = MTD_NANDECC_AUTOPLACE ++}; ++ ++void display_help (void) ++{ ++ printf("Usage: nandwrite [OPTION] MTD_DEVICE INPUTFILE\n" ++ "Writes to the specified MTD device.\n" ++ "\n" ++ " -a, --autoplace Use auto oob layout\n" ++ " -j, --jffs2 force jffs2 oob layout (legacy support)\n" ++ " -y, --yaffs force yaffs oob layout (legacy support)\n" ++ " -f, --forcelegacy force legacy support on autoplacement enabled mtd device\n" ++ " -m, --markbad mark blocks bad if write fails\n" ++ " -n, --noecc write without ecc\n" ++ " -o, --oob image contains oob data\n" ++ " -s addr, --start=addr set start address (default is 0)\n" ++ " -p, --pad pad to page size\n" ++ " -b, --blockalign=1|2|4 set multiple of eraseblocks to align to\n" ++ " -q, --quiet don't display progress messages\n" ++ " --help display this help and exit\n" ++ " --version output version information and exit\n"); ++ exit(0); ++} ++ ++void display_version (void) ++{ ++ printf(PROGRAM " " VERSION "\n" ++ "\n" ++ "Copyright (C) 2003 Thomas Gleixner \n" ++ "\n" ++ PROGRAM " comes with NO WARRANTY\n" ++ "to the extent permitted by law.\n" ++ "\n" ++ "You may redistribute copies of " PROGRAM "\n" ++ "under the terms of the GNU General Public Licence.\n" ++ "See the file `COPYING' for more information.\n"); ++ exit(0); ++} ++ ++char *mtd_device, *img; ++int mtdoffset = 0; ++int quiet = 0; ++int writeoob = 0; ++int markbad = 0; ++int autoplace = 0; ++int forcejffs2 = 0; ++int forceyaffs = 0; ++int forcelegacy = 0; ++int noecc = 0; ++int pad = 0; ++int blockalign = 1; /*default to using 16K block size */ ++ ++void process_options (int argc, char *argv[]) ++{ ++ int error = 0; ++ ++ for (;;) { ++ int option_index = 0; ++ static const char *short_options = "ab:fjmnopqs:y"; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 0}, ++ {"version", no_argument, 0, 0}, ++ {"autoplace", no_argument, 0, 'a'}, ++ {"blockalign", required_argument, 0, 'b'}, ++ {"forcelegacy", no_argument, 0, 'f'}, ++ {"jffs2", no_argument, 0, 'j'}, ++ {"markbad", no_argument, 0, 'm'}, ++ {"noecc", no_argument, 0, 'n'}, ++ {"oob", no_argument, 0, 'o'}, ++ {"pad", no_argument, 0, 'p'}, ++ {"quiet", no_argument, 0, 'q'}, ++ {"start", required_argument, 0, 's'}, ++ {"yaffs", no_argument, 0, 'y'}, ++ {0, 0, 0, 0}, ++ }; ++ ++ int c = getopt_long(argc, argv, short_options, ++ long_options, &option_index); ++ if (c == EOF) { ++ break; ++ } ++ ++ switch (c) { ++ case 0: ++ switch (option_index) { ++ case 0: ++ display_help(); ++ break; ++ case 1: ++ display_version(); ++ break; ++ } ++ break; ++ case 'q': ++ quiet = 1; ++ break; ++ case 'a': ++ autoplace = 1; ++ break; ++ case 'j': ++ forcejffs2 = 1; ++ break; ++ case 'y': ++ forceyaffs = 1; ++ break; ++ case 'f': ++ forcelegacy = 1; ++ break; ++ case 'n': ++ noecc = 1; ++ break; ++ case 'm': ++ markbad = 1; ++ break; ++ case 'o': ++ writeoob = 1; ++ break; ++ case 'p': ++ pad = 1; ++ break; ++ case 's': ++ mtdoffset = strtol (optarg, NULL, 0); ++ break; ++ case 'b': ++ blockalign = atoi (optarg); ++ break; ++ case '?': ++ error = 1; ++ break; ++ } ++ } ++ ++ if ((argc - optind) != 2 || error) ++ display_help (); ++ ++ mtd_device = argv[optind++]; ++ img = argv[optind]; ++} ++ ++/* ++ * Main program ++ */ ++int main(int argc, char **argv) ++{ ++ int cnt, fd, ifd, imglen = 0, pagelen, baderaseblock, blockstart = -1; ++ struct mtd_info_user meminfo; ++ struct mtd_oob_buf oob; ++ loff_mtd_t offs; ++ int ret, readlen; ++ int oobinfochanged = 0; ++ struct nand_oobinfo old_oobinfo; ++ ++ printf("Warning: nandwrite_mlc instead of nandwrite is used for MLC NAND!\n"); ++ ++ process_options(argc, argv); ++ ++ memset(oobbuf, 0xff, sizeof(oobbuf)); ++ ++ if (pad && writeoob) { ++ fprintf(stderr, "Can't pad when oob data is present.\n"); ++ exit(1); ++ } ++ ++ /* Open the device */ ++ if ((fd = open(mtd_device, O_RDWR)) == -1) { ++ perror("open flash"); ++ exit(1); ++ } ++ ++ /* Fill in MTD device capability structure */ ++ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { ++ perror("MEMGETINFO"); ++ close(fd); ++ exit(1); ++ } ++ ++ /* Set erasesize to specified number of blocks - to match jffs2 ++ * (virtual) block size */ ++ meminfo.erasesize *= blockalign; ++ ++ /* Make sure device page sizes are valid */ ++ if (!(meminfo.oobsize == 16 && meminfo.writesize == 512) && ++ !(meminfo.oobsize == 8 && meminfo.writesize == 256) && ++ !(meminfo.oobsize == 64 && meminfo.writesize == 2048) && ++ !(meminfo.oobsize == 128 && meminfo.writesize == 4096)) { ++ fprintf(stderr, "Unknown flash (not normal NAND)\n"); ++ close(fd); ++ exit(1); ++ } ++ ++ if (autoplace) { ++ /* Read the current oob info */ ++ if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { ++ perror ("MEMGETOOBSEL"); ++ close (fd); ++ exit (1); ++ } ++ ++ // autoplace ECC ? ++ if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { ++ ++ if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) { ++ perror ("MEMSETOOBSEL"); ++ close (fd); ++ exit (1); ++ } ++ oobinfochanged = 1; ++ } ++ } ++ ++ if (noecc) { ++ ret = ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW); ++ if (ret == 0) { ++ oobinfochanged = 2; ++ } else { ++ switch (errno) { ++ case ENOTTY: ++ if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { ++ perror ("MEMGETOOBSEL"); ++ close (fd); ++ exit (1); ++ } ++ if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) { ++ perror ("MEMSETOOBSEL"); ++ close (fd); ++ exit (1); ++ } ++ oobinfochanged = 1; ++ break; ++ default: ++ perror ("MTDFILEMODE"); ++ close (fd); ++ exit (1); ++ } ++ } ++ } ++ ++ /* ++ * force oob layout for jffs2 or yaffs ? ++ * Legacy support ++ */ ++ if (forcejffs2 || forceyaffs) { ++ struct nand_oobinfo *oobsel = forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; ++ ++ if (autoplace) { ++ fprintf(stderr, "Autoplacement is not possible for legacy -j/-y options\n"); ++ goto restoreoob; ++ } ++ if ((old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE) && !forcelegacy) { ++ fprintf(stderr, "Use -f option to enforce legacy placement on autoplacement enabled mtd device\n"); ++ goto restoreoob; ++ } ++ if (meminfo.oobsize == 8) { ++ if (forceyaffs) { ++ fprintf (stderr, "YAFSS cannot operate on 256 Byte page size"); ++ goto restoreoob; ++ } ++ /* Adjust number of ecc bytes */ ++ jffs2_oobinfo.eccbytes = 3; ++ } ++ ++ if (ioctl (fd, MEMSETOOBSEL, oobsel) != 0) { ++ perror ("MEMSETOOBSEL"); ++ goto restoreoob; ++ } ++ } ++ ++ oob.length = meminfo.oobsize; ++ oob.ptr = noecc ? oobreadbuf : oobbuf; ++ ++ /* Open the input file */ ++ if ((ifd = open(img, O_RDONLY)) == -1) { ++ perror("open input file"); ++ goto restoreoob; ++ } ++ ++ // get image length ++ imglen = lseek(ifd, 0, SEEK_END); ++ lseek (ifd, 0, SEEK_SET); ++ ++ pagelen = meminfo.writesize + ((writeoob == 1) ? meminfo.oobsize : 0); ++ ++ // Check, if file is pagealigned ++ if ((!pad) && ((imglen % pagelen) != 0)) { ++ fprintf (stderr, "Input file is not page aligned\n"); ++ goto closeall; ++ } ++ ++ // Check, if length fits into device ++ if ( ((imglen / pagelen) * meminfo.writesize) > (meminfo.size - mtdoffset)) { ++ fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %llu bytes\n", ++ imglen, pagelen, meminfo.writesize, meminfo.size); ++ perror ("Input file does not fit into device"); ++ goto closeall; ++ } ++ ++ /* Get data from input and write to the device */ ++ while (imglen && (mtdoffset < meminfo.size)) { ++ // new eraseblock , check for bad block(s) ++ // Stay in the loop to be sure if the mtdoffset changes because ++ // of a bad block, that the next block that will be written to ++ // is also checked. Thus avoiding errors if the block(s) after the ++ // skipped block(s) is also bad (number of blocks depending on ++ // the blockalign ++ while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) { ++ blockstart = mtdoffset & (~meminfo.erasesize + 1); ++ offs = blockstart; ++ baderaseblock = 0; ++ if (!quiet) ++ fprintf (stdout, "Writing data to block %x\n", blockstart); ++ ++ /* Check all the blocks in an erase block for bad blocks */ ++ do { ++ if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) { ++ perror("ioctl(MEMGETBADBLOCK)"); ++ goto closeall; ++ } ++ ++ if (ret == 1) { ++ baderaseblock = 1; ++ if (!quiet) ++ fprintf (stderr, "Bad block at %x, %u block(s) " ++ "from %x will be skipped\n", ++ (int) offs, blockalign, blockstart); ++ } ++ ++ if (baderaseblock) { ++ mtdoffset = blockstart + meminfo.erasesize; ++ } ++ offs += meminfo.erasesize / blockalign ; ++ } while ( offs < blockstart + meminfo.erasesize ); ++ ++ } ++ ++ readlen = meminfo.writesize; ++ if (pad && (imglen < readlen)) ++ { ++ readlen = imglen; ++ memset(writebuf + readlen, 0xff, meminfo.writesize - readlen); ++ } ++ ++ /* Read Page Data from input file */ ++ if ((cnt = read(ifd, writebuf, readlen)) != readlen) { ++ if (cnt == 0) // EOF ++ break; ++ perror ("File I/O error on input file"); ++ goto closeall; ++ } ++ ++ if (writeoob) { ++ /* Read OOB data from input file, exit on failure */ ++ if ((cnt = read(ifd, oobreadbuf, meminfo.oobsize)) != meminfo.oobsize) { ++ perror ("File I/O error on input file"); ++ goto closeall; ++ } ++ if (!noecc) { ++ int i, start, len; ++ /* ++ * We use autoplacement and have the oobinfo with the autoplacement ++ * information from the kernel available ++ * ++ * Modified to support out of order oobfree segments, ++ * such as the layout used by diskonchip.c ++ */ ++ if (!oobinfochanged && (old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE)) { ++ for (i = 0;old_oobinfo.oobfree[i][1]; i++) { ++ /* Set the reserved bytes to 0xff */ ++ start = old_oobinfo.oobfree[i][0]; ++ len = old_oobinfo.oobfree[i][1]; ++ memcpy(oobbuf + start, ++ oobreadbuf + start, ++ len); ++ } ++ } else { ++ /* Set at least the ecc byte positions to 0xff */ ++ start = old_oobinfo.eccbytes; ++ len = meminfo.oobsize - start; ++ memcpy(oobbuf + start, ++ oobreadbuf + start, ++ len); ++ } ++ } ++ /* Write OOB data first, as ecc will be placed in there*/ ++ oob.start = mtdoffset; ++ if (ioctl(fd, MEMWRITEOOB, &oob) != 0) { ++ perror ("ioctl(MEMWRITEOOB)"); ++ goto closeall; ++ } ++ imglen -= meminfo.oobsize; ++ } ++ ++ /* Write out the Page data */ ++ if (pwrite(fd, writebuf, meminfo.writesize, mtdoffset) != meminfo.writesize) { ++ int rewind_blocks; ++ off_t rewind_bytes; ++ erase_info_t erase; ++ ++ perror ("pwrite"); ++ /* Must rewind to blockstart if we can */ ++ rewind_blocks = (mtdoffset - blockstart) / meminfo.writesize; /* Not including the one we just attempted */ ++ rewind_bytes = (rewind_blocks * meminfo.writesize) + readlen; ++ if (writeoob) ++ rewind_bytes += (rewind_blocks + 1) * meminfo.oobsize; ++ if (lseek(ifd, -rewind_bytes, SEEK_CUR) == -1) { ++ perror("lseek"); ++ fprintf(stderr, "Failed to seek backwards to recover from write error\n"); ++ goto closeall; ++ } ++ erase.start = blockstart; ++ erase.length = meminfo.erasesize; ++ fprintf(stderr, "Erasing failed write from 0x%09llx-0x%09llx\n", ++ erase.start, erase.start+erase.length-1); ++ if (ioctl(fd, MEMERASE, &erase) != 0) { ++ perror("MEMERASE"); ++ goto closeall; ++ } ++ ++ if (markbad) { ++ loff_mtd_t bad_addr = mtdoffset & (~(meminfo.erasesize / blockalign) + 1); ++ fprintf(stderr, "Marking block at %09llx bad\n", (long long)bad_addr); ++ if (ioctl(fd, MEMSETBADBLOCK, &bad_addr)) { ++ perror("MEMSETBADBLOCK"); ++ /* But continue anyway */ ++ } ++ } ++ mtdoffset = blockstart + meminfo.erasesize; ++ imglen += rewind_blocks * meminfo.writesize; ++ ++ continue; ++ } ++ imglen -= readlen; ++ mtdoffset += meminfo.writesize; ++ } ++ ++closeall: ++ close(ifd); ++ ++restoreoob: ++ if (oobinfochanged == 1) { ++ if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { ++ perror ("MEMSETOOBSEL"); ++ close (fd); ++ exit (1); ++ } ++ } ++ ++ close(fd); ++ ++ if (imglen > 0) { ++ perror ("Data was only partially written due to error\n"); ++ exit (1); ++ } ++ ++ /* Return happy */ ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/nandwrite_mlc.c linux-2.6.24.7/drivers/mtd/mtd-utils/nandwrite_mlc.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/nandwrite_mlc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/nandwrite_mlc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,446 @@ ++/* ++ * nandwrite.c ++ * ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) ++ * 2003 Thomas Gleixner (tglx@linutronix.de) ++ * ++ * 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. ++ * ++ * Overview: ++ * This utility writes a binary image directly to a NAND flash ++ * chip or NAND chips contained in DoC devices. This is the ++ * "inverse operation" of nanddump. ++ * ++ * tglx: Major rewrite to handle bad blocks, write data with or without ECC ++ * write oob data only on request ++ * ++ * Bug/ToDo: ++ */ ++ ++#define _GNU_SOURCE ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "mtd/mtd-user.h" ++ ++#define PROGRAM "nandwrite" ++#define VERSION "$Revision: 1.1.1.1 $" ++ ++#define MAX_PAGE_SIZE 8192 ++#define MAX_OOB_SIZE 256 ++/* ++ * Buffer array used for writing data ++ */ ++unsigned char writebuf[MAX_PAGE_SIZE]; ++unsigned char oobreadbuf[MAX_OOB_SIZE]; ++ ++// oob layouts to pass into the kernel as default ++struct nand_oobinfo none_oobinfo = { ++ .useecc = MTD_NANDECC_OFF, ++}; ++ ++struct nand_oobinfo jffs2_oobinfo = { ++ .useecc = MTD_NANDECC_PLACE, ++ .eccbytes = 6, ++ .eccpos = { 0, 1, 2, 3, 6, 7 } ++}; ++ ++struct nand_oobinfo yaffs_oobinfo = { ++ .useecc = MTD_NANDECC_PLACE, ++ .eccbytes = 6, ++ .eccpos = { 8, 9, 10, 13, 14, 15} ++}; ++ ++struct nand_oobinfo autoplace_oobinfo = { ++ .useecc = MTD_NANDECC_AUTOPLACE, ++ .eccbytes = 36 ++}; ++ ++void display_help (void) ++{ ++ printf("Usage: nandwrite [OPTION] MTD_DEVICE INPUTFILE\n" ++ "Writes to the specified MTD device.\n" ++ "\n" ++ " -a, --autoplace Use auto oob layout\n" ++ " -j, --jffs2 force jffs2 oob layout (legacy support)\n" ++ " -y, --yaffs force yaffs oob layout (legacy support)\n" ++ " -f, --forcelegacy force legacy support on autoplacement enabled mtd device\n" ++ " -m, --markbad mark blocks bad if write fails\n" ++ " -n, --noecc write without ecc\n" ++ " -o, --oob image contains oob data\n" ++ " -s addr, --start=addr set start address (default is 0)\n" ++ " -p, --pad pad to page size\n" ++ " -b, --blockalign=1|2|4 set multiple of eraseblocks to align to\n" ++ " -q, --quiet don't display progress messages\n" ++ " --help display this help and exit\n" ++ " --version output version information and exit\n"); ++ exit(0); ++} ++ ++void display_version (void) ++{ ++ printf(PROGRAM " " VERSION "\n" ++ "\n" ++ "Copyright (C) 2003 Thomas Gleixner \n" ++ "\n" ++ PROGRAM " comes with NO WARRANTY\n" ++ "to the extent permitted by law.\n" ++ "\n" ++ "You may redistribute copies of " PROGRAM "\n" ++ "under the terms of the GNU General Public Licence.\n" ++ "See the file `COPYING' for more information.\n"); ++ exit(0); ++} ++ ++char *mtd_device, *img; ++unsigned long long mtdoffset = 0; ++int quiet = 0; ++int writeoob = 0; ++int markbad = 1; ++int autoplace = 0; ++int forcejffs2 = 0; ++int forceyaffs = 0; ++int forcelegacy = 0; ++int noecc = 0; ++int pad = 0; ++int blockalign = 1; /*default to using 16K block size */ ++ ++void process_options (int argc, char *argv[]) ++{ ++ int error = 0; ++ ++ for (;;) { ++ int option_index = 0; ++ static const char *short_options = "ab:fjmnopqs:y"; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 0}, ++ {"version", no_argument, 0, 0}, ++ {"autoplace", no_argument, 0, 'a'}, ++ {"blockalign", required_argument, 0, 'b'}, ++ {"forcelegacy", no_argument, 0, 'f'}, ++ {"jffs2", no_argument, 0, 'j'}, ++ {"markbad", no_argument, 0, 'm'}, ++ {"noecc", no_argument, 0, 'n'}, ++ {"oob", no_argument, 0, 'o'}, ++ {"pad", no_argument, 0, 'p'}, ++ {"quiet", no_argument, 0, 'q'}, ++ {"start", required_argument, 0, 's'}, ++ {"yaffs", no_argument, 0, 'y'}, ++ {0, 0, 0, 0}, ++ }; ++ ++ int c = getopt_long(argc, argv, short_options, ++ long_options, &option_index); ++ if (c == EOF) { ++ break; ++ } ++ ++ switch (c) { ++ case 0: ++ switch (option_index) { ++ case 0: ++ display_help(); ++ break; ++ case 1: ++ display_version(); ++ break; ++ } ++ break; ++ case 'q': ++ quiet = 1; ++ break; ++ case 'a': ++ autoplace = 1; ++ break; ++ case 'j': ++ forcejffs2 = 1; ++ break; ++ case 'y': ++ forceyaffs = 1; ++ break; ++ case 'f': ++ forcelegacy = 1; ++ break; ++ case 'n': ++ noecc = 1; ++ break; ++ case 'm': ++ markbad = 1; ++ break; ++ case 'o': ++ writeoob = 1; ++ break; ++ case 'p': ++ pad = 1; ++ break; ++ case 's': ++ mtdoffset = strtol (optarg, NULL, 0); ++ break; ++ case 'b': ++ blockalign = atoi (optarg); ++ break; ++ case '?': ++ error = 1; ++ break; ++ } ++ } ++ ++ if ((argc - optind) != 2 || error) ++ display_help (); ++ ++ mtd_device = argv[optind++]; ++ img = argv[optind]; ++} ++ ++/* ++ * Main program ++ */ ++int main(int argc, char **argv) ++{ ++ int cnt, fd, ifd, imglen = 0, pagelen, baderaseblock, blockstart = -1; ++ struct mtd_info_user meminfo; ++ struct mtd_page_buf oob; ++ loff_mtd_t offs; ++ int ret, readlen; ++ int oobinfochanged = 0; ++ struct nand_oobinfo old_oobinfo; ++ int i; ++ ++ process_options(argc, argv); ++ ++ if (pad && writeoob) { ++ fprintf(stderr, "Can't pad when oob data is present.\n"); ++ exit(1); ++ } ++ ++ /* Open the device */ ++ if ((fd = open(mtd_device, O_RDWR)) == -1) { ++ perror("open flash"); ++ exit(1); ++ } ++ ++ /* Fill in MTD device capability structure */ ++ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { ++ perror("MEMGETINFO"); ++ close(fd); ++ exit(1); ++ } ++ ++ /* Set erasesize to specified number of blocks - to match jffs2 ++ * (virtual) block size */ ++ meminfo.erasesize *= blockalign; ++ ++ /* Make sure device page sizes are valid */ ++ if (!(meminfo.oobsize == 16 && meminfo.writesize == 512) && ++ !(meminfo.oobsize == 8 && meminfo.writesize == 256) && ++ !(meminfo.oobsize == 64 && meminfo.writesize == 2048) && ++ !(meminfo.oobsize == 128 && meminfo.writesize == 4096) && ++ !(meminfo.oobsize == 256 && meminfo.writesize == 8192)) { ++ fprintf(stderr, "Unknown flash (not normal NAND)\n"); ++ close(fd); ++ exit(1); ++ } ++ ++ if (autoplace) { ++ /* Read the current oob info */ ++ if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { ++ perror ("MEMGETOOBSEL"); ++ close (fd); ++ exit (1); ++ } ++ ++ // autoplace ECC ? ++ if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { ++ ++ if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) { ++ perror ("MEMSETOOBSEL"); ++ close (fd); ++ exit (1); ++ } ++ oobinfochanged = 1; ++ } ++ } ++ ++ memset(oobreadbuf, 0xff, MAX_OOB_SIZE); ++ ++ if (autoplace) { ++ oob.ooblength = meminfo.oobsize-old_oobinfo.eccbytes; /* Get ooblength from kernel */ ++ printf("oobsize=%d eccbytes=%d\n", meminfo.oobsize, old_oobinfo.eccbytes); ++ } else { ++ oob.ooblength = meminfo.oobsize-autoplace_oobinfo.eccbytes; ++ printf("oobsize=%d eccbytes=%d\n", meminfo.oobsize, autoplace_oobinfo.eccbytes); ++ } ++ ++ oob.oobptr = oobreadbuf; ++ oob.datptr = writebuf; ++ ++ /* Open the input file */ ++ if ((ifd = open(img, O_RDONLY)) == -1) { ++ perror("open input file"); ++ goto restoreoob; ++ } ++ ++ // get image length ++ imglen = lseek(ifd, 0, SEEK_END); ++ lseek (ifd, 0, SEEK_SET); ++ ++ pagelen = meminfo.writesize + ((writeoob == 1) ? meminfo.oobsize : 0); ++ ++ // Check, if file is pagealigned ++ if ((!pad) && ((imglen % pagelen) != 0)) { ++ fprintf (stderr, "Input file is not page aligned\n"); ++ goto closeall; ++ } ++ ++ // Check, if length fits into device ++ if ( ((imglen / pagelen) * meminfo.writesize) > (meminfo.size - mtdoffset)) { ++ fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %lld bytes\n", ++ imglen, pagelen, meminfo.writesize, meminfo.size); ++ perror ("Input file does not fit into device"); ++ goto closeall; ++ } ++ ++ /* Get data from input and write to the device */ ++ while (imglen && (mtdoffset < meminfo.size)) { ++ // new eraseblock , check for bad block(s) ++ // Stay in the loop to be sure if the mtdoffset changes because ++ // of a bad block, that the next block that will be written to ++ // is also checked. Thus avoiding errors if the block(s) after the ++ // skipped block(s) is also bad (number of blocks depending on ++ // the blockalign ++ while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) { ++ blockstart = mtdoffset & (~meminfo.erasesize + 1); ++ offs = blockstart; ++ baderaseblock = 0; ++ i=0; ++ if (!quiet) ++ fprintf (stdout, "Writing data to block 0x%x\n", blockstart); ++ ++ /* Check all the blocks in an erase block for bad blocks */ ++ do { ++ if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) { ++ perror("ioctl(MEMGETBADBLOCK)"); ++ goto closeall; ++ } ++ if (ret == 1) { ++ baderaseblock = 1; ++ if (!quiet) ++ fprintf (stderr, "Bad block at 0x%llx, %u block(s) " ++ "from 0x%x will be skipped\n", ++ offs, blockalign, blockstart); ++ } ++ ++ if (baderaseblock) { ++ mtdoffset = blockstart + meminfo.erasesize; ++ } ++ offs += meminfo.erasesize / blockalign ; ++ } while ( offs < blockstart + meminfo.erasesize ); ++ ++ } ++ ++ readlen = meminfo.writesize; ++ if (pad && (imglen < readlen)) ++ { ++ readlen = imglen; ++ memset(writebuf + readlen, 0xff, meminfo.writesize - readlen); ++ } ++ ++ /* Read Page Data from input file */ ++ if ((cnt = read(ifd, writebuf, readlen)) != readlen) { ++ if (cnt == 0) // EOF ++ break; ++ perror ("File I/O error 1 on input file"); ++ goto closeall; ++ } ++ ++ /* Read OOB data from input file, exit on failure */ ++ if(writeoob) { ++ if ((cnt = read(ifd, oobreadbuf, meminfo.oobsize)) != meminfo.oobsize) { ++ perror ("File I/O error 2 on input file"); ++ goto closeall; ++ } ++ } ++ oob.start = mtdoffset; ++ ++ // write a page include its oob to nand ++ ioctl(fd, MEMWRITEPAGE, &oob); ++ if(oob.datlength != meminfo.writesize){ ++ perror ("ioctl(MEMWRITEPAGE)"); ++ ++ int rewind_blocks; ++ off_t rewind_bytes; ++ erase_info_t erase; ++ ++ /* Must rewind to blockstart if we can */ ++ rewind_blocks = (mtdoffset - blockstart) / meminfo.writesize; /* Not including the one we just attempted */ ++ rewind_bytes = (rewind_blocks * meminfo.writesize) + readlen; ++ if (writeoob) ++ rewind_bytes += (rewind_blocks + 1) * meminfo.oobsize; ++ if (lseek(ifd, -rewind_bytes, SEEK_CUR) == -1) { ++ perror("lseek"); ++ fprintf(stderr, "Failed to seek backwards to recover from write error\n"); ++ goto closeall; ++ } ++ erase.start = blockstart; ++ erase.length = meminfo.erasesize; ++ fprintf(stderr, "Erasing failed write from 0x%09llx-0x%09llx\n", ++ erase.start, erase.start+erase.length-1); ++ if (ioctl(fd, MEMERASE, &erase) != 0) { ++ perror("MEMERASE"); ++ goto closeall; ++ } ++ ++ if (markbad) { ++ loff_mtd_t bad_addr = mtdoffset & (~(meminfo.erasesize / blockalign) + 1); ++ fprintf(stderr, "Marking block at %09llx bad\n", (long long)bad_addr); ++ if (ioctl(fd, MEMSETBADBLOCK, &bad_addr)) { ++ perror("MEMSETBADBLOCK"); ++ /* But continue anyway */ ++ } ++ } ++ mtdoffset = blockstart + meminfo.erasesize; ++ imglen += rewind_blocks * meminfo.writesize; ++ ++ continue; ++ } ++ if(writeoob) ++ imglen -= meminfo.oobsize; ++ imglen -= readlen; ++ mtdoffset += meminfo.writesize; ++ } ++ ++closeall: ++ close(ifd); ++ ++restoreoob: ++ if (oobinfochanged == 1) { ++ if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { ++ perror ("MEMSETOOBSEL"); ++ close (fd); ++ exit (1); ++ } ++ } ++ ++ close(fd); ++ ++ if (imglen > 0) { ++ perror ("Data was only partially written due to error\n"); ++ exit (1); ++ } ++ ++ /* Return happy */ ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/nftl_format.c linux-2.6.24.7/drivers/mtd/mtd-utils/nftl_format.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/nftl_format.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/nftl_format.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,419 @@ ++/* ++ * nftl_format.c: Creating a NFTL/INFTL partition on an MTD device ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * ToDo: ++ * 1. UnitSizeFactor != 0xFF cases ++ * 2. test, test, and test !!! ++ */ ++ ++#define _XOPEN_SOURCE 500 /* for pread/pwrite */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++unsigned char BadUnitTable[MAX_ERASE_ZONES]; ++unsigned char *readbuf; ++unsigned char *writebuf[4]; ++ ++mtd_info_t meminfo; ++erase_info_t erase; ++int fd; ++struct NFTLMediaHeader *NFTLhdr; ++struct INFTLMediaHeader *INFTLhdr; ++ ++static int do_oobcheck = 1; ++static int do_rwecheck = 1; ++ ++static unsigned char check_block_1(unsigned long block) ++{ ++ unsigned char oobbuf[16]; ++ struct mtd_oob_buf oob = { 0, 16, oobbuf }; ++ ++ oob.start = block * meminfo.erasesize; ++ if (ioctl(fd, MEMREADOOB, &oob)) ++ return ZONE_BAD_ORIGINAL; ++ ++ if(oobbuf[5] == 0) ++ return ZONE_BAD_ORIGINAL; ++ ++ oob.start = block * meminfo.erasesize + 512 /* FIXME */; ++ if (ioctl(fd, MEMREADOOB, &oob)) ++ return ZONE_BAD_ORIGINAL; ++ ++ if(oobbuf[5] == 0) ++ return ZONE_BAD_ORIGINAL; ++ ++ return ZONE_GOOD; ++} ++ ++static unsigned char check_block_2(unsigned long block) ++{ ++ unsigned long ofs = block * meminfo.erasesize; ++ unsigned long blockofs; ++ ++ /* Erase test */ ++ erase.start = ofs; ++ ++ for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { ++ pread(fd, readbuf, 512, ofs + blockofs); ++ if (memcmp(readbuf, writebuf[0], 512)) { ++ /* Block wasn't 0xff after erase */ ++ printf(": Block not 0xff after erase\n"); ++ return ZONE_BAD_ORIGINAL; ++ } ++ ++ pwrite(fd, writebuf[1], 512, blockofs + ofs); ++ pread(fd, readbuf, 512, blockofs + ofs); ++ if (memcmp(readbuf, writebuf[1], 512)) { ++ printf(": Block not zero after clearing\n"); ++ return ZONE_BAD_ORIGINAL; ++ } ++ } ++ ++ /* Write test */ ++ if (ioctl(fd, MEMERASE, &erase) != 0) { ++ printf(": Second erase failed (%s)\n", strerror(errno)); ++ return ZONE_BAD_ORIGINAL; ++ } ++ for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { ++ pwrite(fd, writebuf[2], 512, blockofs + ofs); ++ pread(fd, readbuf, 512, blockofs + ofs); ++ if (memcmp(readbuf, writebuf[2], 512)) { ++ printf(": Block not 0x5a after writing\n"); ++ return ZONE_BAD_ORIGINAL; ++ } ++ } ++ ++ if (ioctl(fd, MEMERASE, &erase) != 0) { ++ printf(": Third erase failed (%s)\n", strerror(errno)); ++ return ZONE_BAD_ORIGINAL; ++ } ++ for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { ++ pwrite(fd, writebuf[3], 512, blockofs + ofs); ++ pread(fd, readbuf, 512, blockofs + ofs); ++ if (memcmp(readbuf, writebuf[3], 512)) { ++ printf(": Block not 0xa5 after writing\n"); ++ return ZONE_BAD_ORIGINAL; ++ } ++ } ++ if (ioctl(fd, MEMERASE, &erase) != 0) { ++ printf(": Fourth erase failed (%s)\n", strerror(errno)); ++ return ZONE_BAD_ORIGINAL; ++ } ++ return ZONE_GOOD; ++} ++ ++static unsigned char erase_block(unsigned long block) ++{ ++ unsigned char status; ++ int ret; ++ ++ status = (do_oobcheck) ? check_block_1(block) : ZONE_GOOD; ++ erase.start = block * meminfo.erasesize; ++ ++ if (status != ZONE_GOOD) { ++ printf("\rSkipping bad zone (factory marked) #%ld @ 0x%x\n", block, erase.start); ++ fflush(stdout); ++ return status; ++ } ++ ++ printf("\r\t Erasing Zone #%ld @ 0x%x", block, erase.start); ++ fflush(stdout); ++ ++ if ((ret=ioctl(fd, MEMERASE, &erase)) != 0) { ++ printf(": Erase failed (%s)\n", strerror(errno)); ++ return ZONE_BAD_ORIGINAL; ++ } ++ ++ if (do_rwecheck) { ++ printf("\r\tChecking Zone #%ld @ 0x%x", block, erase.start); ++ fflush(stdout); ++ status = check_block_2(block); ++ if (status != ZONE_GOOD) { ++ printf("\rSkipping bad zone (RWE test failed) #%ld @ 0x%x\n", block, erase.start); ++ fflush(stdout); ++ } ++ } ++ return status; ++} ++ ++static int checkbbt(void) ++{ ++ unsigned char bbt[512]; ++ unsigned char bits; ++ int i, addr; ++ ++ if (pread(fd, bbt, 512, 0x800) < 0) { ++ printf("nftl_format: failed to read BBT, errno=%d\n", errno); ++ return (-1); ++ } ++ ++ ++ for (i = 0; (i < 512); i++) { ++ addr = i / 4; ++ bits = 0x3 << ((i % 4) * 2); ++ if ((bbt[addr] & bits) == 0) { ++ BadUnitTable[i] = ZONE_BAD_ORIGINAL; ++ } ++ } ++ ++ return (0); ++} ++ ++void usage(int rc) ++{ ++ fprintf(stderr, "Usage: nftl_format [-ib] [ []]\n"); ++ exit(rc); ++} ++ ++int main(int argc, char **argv) ++{ ++ unsigned long startofs = 0, part_size = 0; ++ unsigned long ezones = 0, ezone = 0, bad_zones = 0; ++ unsigned char unit_factor = 0xFF; ++ long MediaUnit1 = -1, MediaUnit2 = -1; ++ long MediaUnitOff1 = 0, MediaUnitOff2 = 0; ++ unsigned char oobbuf[16]; ++ struct mtd_oob_buf oob = {0, 16, oobbuf}; ++ char *mtddevice, *nftl; ++ int c, do_inftl = 0, do_bbt = 0; ++ ++ ++ printf("version 1.24 2005/11/07 11:15:13 gleixner\n"); ++ ++ if (argc < 2) ++ usage(1); ++ ++ nftl = "NFTL"; ++ ++ while ((c = getopt(argc, argv, "?hib")) > 0) { ++ switch (c) { ++ case 'i': ++ nftl = "INFTL"; ++ do_inftl = 1; ++ break; ++ case 'b': ++ do_bbt = 1; ++ break; ++ case 'h': ++ case '?': ++ usage(0); ++ break; ++ default: ++ usage(1); ++ break; ++ } ++ } ++ ++ mtddevice = argv[optind++]; ++ if (argc > optind) { ++ startofs = strtoul(argv[optind++], NULL, 0); ++ } ++ if (argc > optind) { ++ part_size = strtoul(argv[optind++], NULL, 0); ++ } ++ ++ // Open and size the device ++ if ((fd = open(mtddevice, O_RDWR)) < 0) { ++ perror("Open flash device"); ++ return 1; ++ } ++ ++ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { ++ perror("ioctl(MEMGETINFO)"); ++ close(fd); ++ return 1; ++ } ++ ++ switch (meminfo.erasesize) { ++ case 0x1000: ++ case 0x2000: ++ case 0x4000: ++ case 0x8000: ++ break; ++ default: ++ printf("Unrecognized Erase size, 0x%x - I'm confused\n", ++ meminfo.erasesize); ++ close(fd); ++ return 1; ++ } ++ writebuf[0] = malloc(meminfo.erasesize * 5); ++ if (!writebuf[0]) { ++ printf("Malloc failed\n"); ++ close(fd); ++ return 1; ++ } ++ writebuf[1] = writebuf[0] + meminfo.erasesize; ++ writebuf[2] = writebuf[1] + meminfo.erasesize; ++ writebuf[3] = writebuf[2] + meminfo.erasesize; ++ readbuf = writebuf[3] + meminfo.erasesize; ++ memset(writebuf[0], 0xff, meminfo.erasesize); ++ memset(writebuf[1], 0x00, meminfo.erasesize); ++ memset(writebuf[2], 0x5a, meminfo.erasesize); ++ memset(writebuf[3], 0xa5, meminfo.erasesize); ++ memset(BadUnitTable, ZONE_GOOD, MAX_ERASE_ZONES); ++ ++ if (part_size == 0 || (part_size > meminfo.size - startofs)) ++ /* the user doest not or incorrectly specify NFTL partition size */ ++ part_size = meminfo.size - startofs; ++ ++ erase.length = meminfo.erasesize; ++ ezones = part_size / meminfo.erasesize; ++ ++ if (ezones > MAX_ERASE_ZONES) { ++ /* Ought to change the UnitSizeFactor. But later. */ ++ part_size = meminfo.erasesize * MAX_ERASE_ZONES; ++ ezones = MAX_ERASE_ZONES; ++ unit_factor = 0xFF; ++ } ++ ++ /* If using device BBT then parse that now */ ++ if (do_bbt) { ++ checkbbt(); ++ do_oobcheck = 0; ++ do_rwecheck = 0; ++ } ++ ++ /* Phase 1. Erasing and checking each erase zones in the NFTL partition. ++ N.B. Erase Zones not used by the NFTL partition are untouched and marked ZONE_GOOD */ ++ printf("Phase 1. Checking and erasing Erase Zones from 0x%08lx to 0x%08lx\n", ++ startofs, startofs + part_size); ++ for (ezone = startofs / meminfo.erasesize; ++ ezone < (ezones + startofs / meminfo.erasesize); ezone++) { ++ if (BadUnitTable[ezone] != ZONE_GOOD) ++ continue; ++ if ((BadUnitTable[ezone] = erase_block(ezone)) == ZONE_GOOD) { ++ if (MediaUnit1 == -1) { ++ MediaUnit1 = ezone; ++ } else if (MediaUnit2 == -1) { ++ MediaUnit2 = ezone; ++ } ++ } else { ++ bad_zones++; ++ } ++ } ++ printf("\n"); ++ ++ /* N.B. from dump of M-System original chips, NumEraseUnits counts the 2 Erase Unit used ++ by MediaHeader and the FirstPhysicalEUN starts from the MediaHeader */ ++ if (do_inftl) { ++ unsigned long maxzones, pezstart, pezend, numvunits; ++ ++ INFTLhdr = (struct INFTLMediaHeader *) (writebuf[0]); ++ strcpy(INFTLhdr->bootRecordID, "BNAND"); ++ INFTLhdr->NoOfBootImageBlocks = cpu_to_le32(0); ++ INFTLhdr->NoOfBinaryPartitions = cpu_to_le32(0); ++ INFTLhdr->NoOfBDTLPartitions = cpu_to_le32(1); ++ INFTLhdr->BlockMultiplierBits = cpu_to_le32(0); ++ INFTLhdr->FormatFlags = cpu_to_le32(0); ++ INFTLhdr->OsakVersion = cpu_to_le32(OSAK_VERSION); ++ INFTLhdr->PercentUsed = cpu_to_le32(PERCENTUSED); ++ /* ++ * Calculate number of virtual units we will have to work ++ * with. I am calculating out the known bad units here, not ++ * sure if that is what M-Systems do... ++ */ ++ MediaUnit2 = MediaUnit1; ++ MediaUnitOff2 = 4096; ++ maxzones = meminfo.size / meminfo.erasesize; ++ pezstart = startofs / meminfo.erasesize + 1; ++ pezend = startofs / meminfo.erasesize + ezones - 1; ++ numvunits = (ezones - 2) * PERCENTUSED / 100; ++ for (ezone = pezstart; ezone < maxzones; ezone++) { ++ if (BadUnitTable[ezone] != ZONE_GOOD) { ++ if (numvunits > 1) ++ numvunits--; ++ } ++ } ++ ++ INFTLhdr->Partitions[0].virtualUnits = cpu_to_le32(numvunits); ++ INFTLhdr->Partitions[0].firstUnit = cpu_to_le32(pezstart); ++ INFTLhdr->Partitions[0].lastUnit = cpu_to_le32(pezend); ++ INFTLhdr->Partitions[0].flags = cpu_to_le32(INFTL_BDTL); ++ INFTLhdr->Partitions[0].spareUnits = cpu_to_le32(0); ++ INFTLhdr->Partitions[0].Reserved0 = INFTLhdr->Partitions[0].firstUnit; ++ INFTLhdr->Partitions[0].Reserved1 = cpu_to_le32(0); ++ ++ } else { ++ ++ NFTLhdr = (struct NFTLMediaHeader *) (writebuf[0]); ++ strcpy(NFTLhdr->DataOrgID, "ANAND"); ++ NFTLhdr->NumEraseUnits = cpu_to_le16(part_size / meminfo.erasesize); ++ NFTLhdr->FirstPhysicalEUN = cpu_to_le16(MediaUnit1); ++ /* N.B. we reserve 2 more Erase Units for "folding" of Virtual Unit Chain */ ++ NFTLhdr->FormattedSize = cpu_to_le32(part_size - ( (5+bad_zones) * meminfo.erasesize)); ++ NFTLhdr->UnitSizeFactor = unit_factor; ++ } ++ ++ /* Phase 2. Writing NFTL Media Headers and Bad Unit Table */ ++ printf("Phase 2.a Writing %s Media Header and Bad Unit Table\n", nftl); ++ pwrite(fd, writebuf[0], 512, MediaUnit1 * meminfo.erasesize + MediaUnitOff1); ++ for (ezone = 0; ezone < (meminfo.size / meminfo.erasesize); ezone += 512) { ++ pwrite(fd, BadUnitTable + ezone, 512, ++ (MediaUnit1 * meminfo.erasesize) + 512 * (1 + ezone / 512)); ++ } ++ ++#if 0 ++ printf(" MediaHeader contents:\n"); ++ printf(" NumEraseUnits: %d\n", le16_to_cpu(NFTLhdr->NumEraseUnits)); ++ printf(" FirstPhysicalEUN: %d\n", le16_to_cpu(NFTLhdr->FirstPhysicalEUN)); ++ printf(" FormattedSize: %d (%d sectors)\n", le32_to_cpu(NFTLhdr->FormattedSize), ++ le32_to_cpu(NFTLhdr->FormattedSize)/512); ++#endif ++ printf("Phase 2.b Writing Spare %s Media Header and Spare Bad Unit Table\n", nftl); ++ pwrite(fd, writebuf[0], 512, MediaUnit2 * meminfo.erasesize + MediaUnitOff2); ++ for (ezone = 0; ezone < (meminfo.size / meminfo.erasesize); ezone += 512) { ++ pwrite(fd, BadUnitTable + ezone, 512, ++ (MediaUnit2 * meminfo.erasesize + MediaUnitOff2) + 512 * (1 + ezone / 512)); ++ } ++ ++ /* UCI #1 for newly erased Erase Unit */ ++ memset(oobbuf, 0xff, 16); ++ oobbuf[11] = oobbuf[10] = oobbuf[9] = 0; ++ oobbuf[8] = (do_inftl) ? 0x00 : 0x03; ++ oobbuf[12] = oobbuf[14] = 0x69; ++ oobbuf[13] = oobbuf[15] = 0x3c; ++ ++ /* N.B. The Media Header and Bad Erase Unit Table are considered as Free Erase Unit ++ by M-System i.e. their Virtual Unit Number == 0xFFFF in the Unit Control Information #0, ++ but their Block Status is BLOCK_USED (0x5555) in their Block Control Information */ ++ /* Phase 3. Writing Unit Control Information for each Erase Unit */ ++ printf("Phase 3. Writing Unit Control Information to each Erase Unit\n"); ++ for (ezone = MediaUnit1; ezone < (ezones + startofs / meminfo.erasesize); ezone++) { ++ /* write UCI #1 to each Erase Unit */ ++ if (BadUnitTable[ezone] != ZONE_GOOD) ++ continue; ++ oob.start = (ezone * meminfo.erasesize) + 512 + (do_inftl * 512); ++ if (ioctl(fd, MEMWRITEOOB, &oob)) ++ printf("MEMWRITEOOB at %lx: %s\n", (unsigned long)oob.start, strerror(errno)); ++ } ++ ++ exit(0); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/nftldump.c linux-2.6.24.7/drivers/mtd/mtd-utils/nftldump.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/nftldump.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/nftldump.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,281 @@ ++/* ++ * nftldump.c: Dumping the content of NFTL partitions on a "Physical Disk" ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * ToDo: ++ * 1. UnitSizeFactor != 0xFF cases ++ * 2. test, test, and test !!! ++ */ ++ ++#define _XOPEN_SOURCE 500 /* For pread */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++static struct NFTLMediaHeader MedHead[2]; ++static mtd_info_t meminfo; ++ ++static struct nftl_oob oobbuf; ++static struct mtd_oob_buf oob = {0, 16, (unsigned char *)&oobbuf}; ++ ++static int fd, ofd = -1;; ++static int NumMedHeads; ++ ++static unsigned char BadUnitTable[MAX_ERASE_ZONES]; ++ ++#if __BYTE_ORDER == __LITTLE_ENDIAN ++#define SWAP16(x) do { ; } while(0) ++#define SWAP32(x) do { ; } while(0) ++#else ++#define SWAP16(x) do { x = swab16(x); } while(0) ++#define SWAP32(x) do { x = swab32(x); } while(0) ++#endif ++ ++/* VUCtable, store the Erase Unit Number of the first Erase Unit in the chain */ ++static unsigned short *VUCtable; ++ ++/* FixMe: make this dynamic allocated */ ++#define ERASESIZE 0x2000 ++#define NUMVUNITS ((40*1024*1024) / ERASESIZE) ++static union nftl_uci UCItable[NUMVUNITS][3]; ++ ++static unsigned short nextEUN(unsigned short curEUN) ++{ ++ return UCItable[curEUN][0].a.ReplUnitNum; ++} ++ ++static unsigned int find_media_headers(void) ++{ ++ int i; ++ static unsigned long ofs = 0; ++ ++ NumMedHeads = 0; ++ while (ofs < meminfo.size) { ++ pread(fd, &MedHead[NumMedHeads], sizeof(struct NFTLMediaHeader), ofs); ++ if (!strncmp(MedHead[NumMedHeads].DataOrgID, "ANAND", 6)) { ++ SWAP16(MedHead[NumMedHeads].NumEraseUnits); ++ SWAP16(MedHead[NumMedHeads].FirstPhysicalEUN); ++ SWAP32(MedHead[NumMedHeads].FormattedSize); ++ ++ if (NumMedHeads == 0) { ++ printf("NFTL Media Header found at offset 0x%08lx:\n", ofs); ++ printf("NumEraseUnits: %d\n", ++ MedHead[NumMedHeads].NumEraseUnits); ++ printf("FirstPhysicalEUN: %d\n", ++ MedHead[NumMedHeads].FirstPhysicalEUN); ++ printf("Formatted Size: %d\n", ++ MedHead[NumMedHeads].FormattedSize); ++ printf("UnitSizeFactor: 0x%x\n", ++ MedHead[NumMedHeads].UnitSizeFactor); ++ ++ /* read BadUnitTable, I don't know why pread() does not work for ++ larger (7680 bytes) chunks */ ++ for (i = 0; i < MAX_ERASE_ZONES; i += 512) ++ pread(fd, &BadUnitTable[i], 512, ofs + 512 + i); ++ } else ++ printf("Second NFTL Media Header found at offset 0x%08lx\n",ofs); ++ NumMedHeads++; ++ } ++ ++ ofs += meminfo.erasesize; ++ if (NumMedHeads == 2) { ++ if (strncmp((char *)&MedHead[0], (char *)&MedHead[1], sizeof(struct NFTLMediaHeader)) != 0) { ++ printf("warning: NFTL Media Header is not consistent with " ++ "Spare NFTL Media Header\n"); ++ } ++ break; ++ } ++ } ++ ++ /* allocate Virtual Unit Chain table for this NFTL partition */ ++ VUCtable = calloc(MedHead[0].NumEraseUnits, sizeof(unsigned short)); ++ return NumMedHeads; ++} ++ ++static void dump_erase_units(void) ++{ ++ int i, j; ++ unsigned long ofs; ++ ++ for (i = MedHead[0].FirstPhysicalEUN; i < MedHead[0].FirstPhysicalEUN + ++ MedHead[0].NumEraseUnits; i++) { ++ /* For each Erase Unit */ ++ ofs = i * meminfo.erasesize; ++ ++ /* read the Unit Control Information */ ++ for (j = 0; j < 3; j++) { ++ oob.start = ofs + (j * 512); ++ if (ioctl(fd, MEMREADOOB, &oob)) ++ printf("MEMREADOOB at %lx: %s\n", ++ (unsigned long) oob.start, strerror(errno)); ++ memcpy(&UCItable[i][j], &oobbuf.u, 8); ++ } ++ if (UCItable[i][1].b.EraseMark != cpu_to_le16(0x3c69)) { ++ printf("EraseMark not present in unit %d: %x\n", ++ i, UCItable[i][1].b.EraseMark); ++ } else { ++ /* a properly formatted unit */ ++ SWAP16(UCItable[i][0].a.VirtUnitNum); ++ SWAP16(UCItable[i][0].a.ReplUnitNum); ++ SWAP16(UCItable[i][0].a.SpareVirtUnitNum); ++ SWAP16(UCItable[i][0].a.SpareReplUnitNum); ++ SWAP32(UCItable[i][1].b.WearInfo); ++ SWAP16(UCItable[i][1].b.EraseMark); ++ SWAP16(UCItable[i][1].b.EraseMark1); ++ SWAP16(UCItable[i][2].c.FoldMark); ++ SWAP16(UCItable[i][2].c.FoldMark1); ++ ++ if (!(UCItable[i][0].a.VirtUnitNum & 0x8000)) { ++ /* If this is the first in a chain, store the EUN in the VUC table */ ++ if (VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff]) { ++ printf("Duplicate start of chain for VUC %d: " ++ "Unit %d replaces Unit %d\n", ++ UCItable[i][0].a.VirtUnitNum & 0x7fff, ++ i, VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff]); ++ } ++ VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff] = i; ++ } ++ } ++ ++ switch (BadUnitTable[i]) { ++ case ZONE_BAD_ORIGINAL: ++ printf("Unit %d is marked as ZONE_BAD_ORIGINAL\n", i); ++ continue; ++ case ZONE_BAD_MARKED: ++ printf("Unit %d is marked as ZONE_BAD_MARKED\n", i); ++ continue; ++ } ++ ++ /* ZONE_GOOD */ ++ if (UCItable[i][0].a.VirtUnitNum == 0xffff) ++ printf("Unit %d is free\n", i); ++ else ++ printf("Unit %d is in chain %d and %s a replacement\n", i, ++ UCItable[i][0].a.VirtUnitNum & 0x7fff, ++ UCItable[i][0].a.VirtUnitNum & 0x8000 ? "is" : "is not"); ++ } ++} ++ ++static void dump_virtual_units(void) ++{ ++ int i, j; ++ char readbuf[512]; ++ ++ for (i = 0; i < (MedHead[0].FormattedSize / meminfo.erasesize); i++) { ++ unsigned short curEUN = VUCtable[i]; ++ ++ printf("Virtual Unit #%d: ", i); ++ if (!curEUN) { ++ printf("Not present\n"); ++ continue; ++ } ++ printf("%d", curEUN); ++ ++ /* walk through the Virtual Unit Chain */ ++ while ((curEUN = nextEUN(curEUN)) != 0xffff) { ++ printf(", %d", curEUN & 0x7fff); ++ } ++ printf("\n"); ++ ++ if (ofd != -1) { ++ /* Actually write out the data */ ++ for (j = 0; j < meminfo.erasesize / 512; j++) { ++ /* For each sector in the block */ ++ unsigned short lastgoodEUN = 0xffff, thisEUN = VUCtable[i]; ++ unsigned int status; ++ ++ if (thisEUN == 0xffff) thisEUN = 0; ++ ++ while (thisEUN && (thisEUN & 0x7fff) != 0x7fff) { ++ oob.start = (thisEUN * ERASESIZE) + (j * 512); ++ ioctl(fd, MEMREADOOB, &oob); ++ status = oobbuf.b.Status | oobbuf.b.Status1; ++ ++ switch (status) { ++ case SECTOR_FREE: ++ /* This is still free. Don't look any more */ ++ thisEUN = 0; ++ break; ++ ++ case SECTOR_USED: ++ /* SECTOR_USED. This is a good one. */ ++ lastgoodEUN = thisEUN; ++ break; ++ } ++ ++ /* Find the next erase unit in this chain, if any */ ++ if (thisEUN) ++ thisEUN = nextEUN(thisEUN) & 0x7fff; ++ } ++ ++ if (lastgoodEUN == 0xffff) ++ memset(readbuf, 0, 512); ++ else ++ pread(fd, readbuf, 512, ++ (lastgoodEUN * ERASESIZE) + (j * 512)); ++ ++ write(ofd, readbuf, 512); ++ } ++ ++ } ++ } ++} ++ ++int main(int argc, char **argv) ++{ ++ if (argc < 2) { ++ printf("Usage: %s []\n", argv[0]); ++ exit(1); ++ } ++ fd = open(argv[1], O_RDONLY); ++ if (fd == -1) { ++ perror("open flash"); ++ exit (1); ++ } ++ ++ if (argc > 2) { ++ ofd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0644); ++ if (ofd == -1) ++ perror ("open outfile"); ++ } ++ ++ /* get size information of the MTD device */ ++ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { ++ perror("ioctl(MEMGETINFO)"); ++ close(fd); ++ return 1; ++ } ++ ++ while (find_media_headers() != 0) { ++ dump_erase_units(); ++ dump_virtual_units(); ++ free(VUCtable); ++ } ++ ++ exit(0); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/rbtree.c linux-2.6.24.7/drivers/mtd/mtd-utils/rbtree.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/rbtree.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/rbtree.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,390 @@ ++/* ++ Red Black Trees ++ (C) 1999 Andrea Arcangeli ++ (C) 2002 David Woodhouse ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++ linux/lib/rbtree.c ++*/ ++ ++#include ++#include "rbtree.h" ++ ++static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) ++{ ++ struct rb_node *right = node->rb_right; ++ struct rb_node *parent = rb_parent(node); ++ ++ if ((node->rb_right = right->rb_left)) ++ rb_set_parent(right->rb_left, node); ++ right->rb_left = node; ++ ++ rb_set_parent(right, parent); ++ ++ if (parent) ++ { ++ if (node == parent->rb_left) ++ parent->rb_left = right; ++ else ++ parent->rb_right = right; ++ } ++ else ++ root->rb_node = right; ++ rb_set_parent(node, right); ++} ++ ++static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) ++{ ++ struct rb_node *left = node->rb_left; ++ struct rb_node *parent = rb_parent(node); ++ ++ if ((node->rb_left = left->rb_right)) ++ rb_set_parent(left->rb_right, node); ++ left->rb_right = node; ++ ++ rb_set_parent(left, parent); ++ ++ if (parent) ++ { ++ if (node == parent->rb_right) ++ parent->rb_right = left; ++ else ++ parent->rb_left = left; ++ } ++ else ++ root->rb_node = left; ++ rb_set_parent(node, left); ++} ++ ++void rb_insert_color(struct rb_node *node, struct rb_root *root) ++{ ++ struct rb_node *parent, *gparent; ++ ++ while ((parent = rb_parent(node)) && rb_is_red(parent)) ++ { ++ gparent = rb_parent(parent); ++ ++ if (parent == gparent->rb_left) ++ { ++ { ++ register struct rb_node *uncle = gparent->rb_right; ++ if (uncle && rb_is_red(uncle)) ++ { ++ rb_set_black(uncle); ++ rb_set_black(parent); ++ rb_set_red(gparent); ++ node = gparent; ++ continue; ++ } ++ } ++ ++ if (parent->rb_right == node) ++ { ++ register struct rb_node *tmp; ++ __rb_rotate_left(parent, root); ++ tmp = parent; ++ parent = node; ++ node = tmp; ++ } ++ ++ rb_set_black(parent); ++ rb_set_red(gparent); ++ __rb_rotate_right(gparent, root); ++ } else { ++ { ++ register struct rb_node *uncle = gparent->rb_left; ++ if (uncle && rb_is_red(uncle)) ++ { ++ rb_set_black(uncle); ++ rb_set_black(parent); ++ rb_set_red(gparent); ++ node = gparent; ++ continue; ++ } ++ } ++ ++ if (parent->rb_left == node) ++ { ++ register struct rb_node *tmp; ++ __rb_rotate_right(parent, root); ++ tmp = parent; ++ parent = node; ++ node = tmp; ++ } ++ ++ rb_set_black(parent); ++ rb_set_red(gparent); ++ __rb_rotate_left(gparent, root); ++ } ++ } ++ ++ rb_set_black(root->rb_node); ++} ++ ++static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, ++ struct rb_root *root) ++{ ++ struct rb_node *other; ++ ++ while ((!node || rb_is_black(node)) && node != root->rb_node) ++ { ++ if (parent->rb_left == node) ++ { ++ other = parent->rb_right; ++ if (rb_is_red(other)) ++ { ++ rb_set_black(other); ++ rb_set_red(parent); ++ __rb_rotate_left(parent, root); ++ other = parent->rb_right; ++ } ++ if ((!other->rb_left || rb_is_black(other->rb_left)) && ++ (!other->rb_right || rb_is_black(other->rb_right))) ++ { ++ rb_set_red(other); ++ node = parent; ++ parent = rb_parent(node); ++ } ++ else ++ { ++ if (!other->rb_right || rb_is_black(other->rb_right)) ++ { ++ struct rb_node *o_left; ++ if ((o_left = other->rb_left)) ++ rb_set_black(o_left); ++ rb_set_red(other); ++ __rb_rotate_right(other, root); ++ other = parent->rb_right; ++ } ++ rb_set_color(other, rb_color(parent)); ++ rb_set_black(parent); ++ if (other->rb_right) ++ rb_set_black(other->rb_right); ++ __rb_rotate_left(parent, root); ++ node = root->rb_node; ++ break; ++ } ++ } ++ else ++ { ++ other = parent->rb_left; ++ if (rb_is_red(other)) ++ { ++ rb_set_black(other); ++ rb_set_red(parent); ++ __rb_rotate_right(parent, root); ++ other = parent->rb_left; ++ } ++ if ((!other->rb_left || rb_is_black(other->rb_left)) && ++ (!other->rb_right || rb_is_black(other->rb_right))) ++ { ++ rb_set_red(other); ++ node = parent; ++ parent = rb_parent(node); ++ } ++ else ++ { ++ if (!other->rb_left || rb_is_black(other->rb_left)) ++ { ++ register struct rb_node *o_right; ++ if ((o_right = other->rb_right)) ++ rb_set_black(o_right); ++ rb_set_red(other); ++ __rb_rotate_left(other, root); ++ other = parent->rb_left; ++ } ++ rb_set_color(other, rb_color(parent)); ++ rb_set_black(parent); ++ if (other->rb_left) ++ rb_set_black(other->rb_left); ++ __rb_rotate_right(parent, root); ++ node = root->rb_node; ++ break; ++ } ++ } ++ } ++ if (node) ++ rb_set_black(node); ++} ++ ++void rb_erase(struct rb_node *node, struct rb_root *root) ++{ ++ struct rb_node *child, *parent; ++ int color; ++ ++ if (!node->rb_left) ++ child = node->rb_right; ++ else if (!node->rb_right) ++ child = node->rb_left; ++ else ++ { ++ struct rb_node *old = node, *left; ++ ++ node = node->rb_right; ++ while ((left = node->rb_left) != NULL) ++ node = left; ++ child = node->rb_right; ++ parent = rb_parent(node); ++ color = rb_color(node); ++ ++ if (child) ++ rb_set_parent(child, parent); ++ if (parent == old) { ++ parent->rb_right = child; ++ parent = node; ++ } else ++ parent->rb_left = child; ++ ++ node->rb_parent_color = old->rb_parent_color; ++ node->rb_right = old->rb_right; ++ node->rb_left = old->rb_left; ++ ++ if (rb_parent(old)) ++ { ++ if (rb_parent(old)->rb_left == old) ++ rb_parent(old)->rb_left = node; ++ else ++ rb_parent(old)->rb_right = node; ++ } else ++ root->rb_node = node; ++ ++ rb_set_parent(old->rb_left, node); ++ if (old->rb_right) ++ rb_set_parent(old->rb_right, node); ++ goto color; ++ } ++ ++ parent = rb_parent(node); ++ color = rb_color(node); ++ ++ if (child) ++ rb_set_parent(child, parent); ++ if (parent) ++ { ++ if (parent->rb_left == node) ++ parent->rb_left = child; ++ else ++ parent->rb_right = child; ++ } ++ else ++ root->rb_node = child; ++ ++ color: ++ if (color == RB_BLACK) ++ __rb_erase_color(child, parent, root); ++} ++ ++/* ++ * This function returns the first node (in sort order) of the tree. ++ */ ++struct rb_node *rb_first(struct rb_root *root) ++{ ++ struct rb_node *n; ++ ++ n = root->rb_node; ++ if (!n) ++ return NULL; ++ while (n->rb_left) ++ n = n->rb_left; ++ return n; ++} ++ ++struct rb_node *rb_last(struct rb_root *root) ++{ ++ struct rb_node *n; ++ ++ n = root->rb_node; ++ if (!n) ++ return NULL; ++ while (n->rb_right) ++ n = n->rb_right; ++ return n; ++} ++ ++struct rb_node *rb_next(struct rb_node *node) ++{ ++ struct rb_node *parent; ++ ++ if (rb_parent(node) == node) ++ return NULL; ++ ++ /* If we have a right-hand child, go down and then left as far ++ as we can. */ ++ if (node->rb_right) { ++ node = node->rb_right; ++ while (node->rb_left) ++ node=node->rb_left; ++ return node; ++ } ++ ++ /* No right-hand children. Everything down and left is ++ smaller than us, so any 'next' node must be in the general ++ direction of our parent. Go up the tree; any time the ++ ancestor is a right-hand child of its parent, keep going ++ up. First time it's a left-hand child of its parent, said ++ parent is our 'next' node. */ ++ while ((parent = rb_parent(node)) && node == parent->rb_right) ++ node = parent; ++ ++ return parent; ++} ++ ++struct rb_node *rb_prev(struct rb_node *node) ++{ ++ struct rb_node *parent; ++ ++ if (rb_parent(node) == node) ++ return NULL; ++ ++ /* If we have a left-hand child, go down and then right as far ++ as we can. */ ++ if (node->rb_left) { ++ node = node->rb_left; ++ while (node->rb_right) ++ node=node->rb_right; ++ return node; ++ } ++ ++ /* No left-hand children. Go up till we find an ancestor which ++ is a right-hand child of its parent */ ++ while ((parent = rb_parent(node)) && node == parent->rb_left) ++ node = parent; ++ ++ return parent; ++} ++ ++void rb_replace_node(struct rb_node *victim, struct rb_node *new, ++ struct rb_root *root) ++{ ++ struct rb_node *parent = rb_parent(victim); ++ ++ /* Set the surrounding nodes to point to the replacement */ ++ if (parent) { ++ if (victim == parent->rb_left) ++ parent->rb_left = new; ++ else ++ parent->rb_right = new; ++ } else { ++ root->rb_node = new; ++ } ++ if (victim->rb_left) ++ rb_set_parent(victim->rb_left, new); ++ if (victim->rb_right) ++ rb_set_parent(victim->rb_right, new); ++ ++ /* Copy the pointers/colour from the victim to the replacement */ ++ *new = *victim; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/rbtree.h linux-2.6.24.7/drivers/mtd/mtd-utils/rbtree.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/rbtree.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/rbtree.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,168 @@ ++/* ++ Red Black Trees ++ (C) 1999 Andrea Arcangeli ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++ linux/include/linux/rbtree.h ++ ++ To use rbtrees you'll have to implement your own insert and search cores. ++ This will avoid us to use callbacks and to drop drammatically performances. ++ I know it's not the cleaner way, but in C (not in C++) to get ++ performances and genericity... ++ ++ Some example of insert and search follows here. The search is a plain ++ normal search over an ordered tree. The insert instead must be implemented ++ int two steps: as first thing the code must insert the element in ++ order as a red leaf in the tree, then the support library function ++ rb_insert_color() must be called. Such function will do the ++ not trivial work to rebalance the rbtree if necessary. ++ ++----------------------------------------------------------------------- ++static inline struct page * rb_search_page_cache(struct inode * inode, ++ unsigned long offset) ++{ ++ struct rb_node * n = inode->i_rb_page_cache.rb_node; ++ struct page * page; ++ ++ while (n) ++ { ++ page = rb_entry(n, struct page, rb_page_cache); ++ ++ if (offset < page->offset) ++ n = n->rb_left; ++ else if (offset > page->offset) ++ n = n->rb_right; ++ else ++ return page; ++ } ++ return NULL; ++} ++ ++static inline struct page * __rb_insert_page_cache(struct inode * inode, ++ unsigned long offset, ++ struct rb_node * node) ++{ ++ struct rb_node ** p = &inode->i_rb_page_cache.rb_node; ++ struct rb_node * parent = NULL; ++ struct page * page; ++ ++ while (*p) ++ { ++ parent = *p; ++ page = rb_entry(parent, struct page, rb_page_cache); ++ ++ if (offset < page->offset) ++ p = &(*p)->rb_left; ++ else if (offset > page->offset) ++ p = &(*p)->rb_right; ++ else ++ return page; ++ } ++ ++ rb_link_node(node, parent, p); ++ ++ return NULL; ++} ++ ++static inline struct page * rb_insert_page_cache(struct inode * inode, ++ unsigned long offset, ++ struct rb_node * node) ++{ ++ struct page * ret; ++ if ((ret = __rb_insert_page_cache(inode, offset, node))) ++ goto out; ++ rb_insert_color(node, &inode->i_rb_page_cache); ++ out: ++ return ret; ++} ++----------------------------------------------------------------------- ++*/ ++ ++#ifndef _LINUX_RBTREE_H ++#define _LINUX_RBTREE_H ++ ++#include ++#include ++ ++struct rb_node ++{ ++ unsigned long rb_parent_color; ++#define RB_RED 0 ++#define RB_BLACK 1 ++ struct rb_node *rb_right; ++ struct rb_node *rb_left; ++} __attribute__((aligned(sizeof(long)))); ++ /* The alignment might seem pointless, but allegedly CRIS needs it */ ++ ++struct rb_root ++{ ++ struct rb_node *rb_node; ++}; ++ ++ ++#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) ++#define rb_color(r) ((r)->rb_parent_color & 1) ++#define rb_is_red(r) (!rb_color(r)) ++#define rb_is_black(r) rb_color(r) ++#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) ++#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) ++ ++static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) ++{ ++ rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; ++} ++static inline void rb_set_color(struct rb_node *rb, int color) ++{ ++ rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; ++} ++ ++#define RB_ROOT (struct rb_root) { NULL, } ++ ++#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) ++ ++#define container_of(ptr, type, member) ({ \ ++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ ++ (type *)( (char *)__mptr - offsetof(type,member) );}) ++ ++#define rb_entry(ptr, type, member) container_of(ptr, type, member) ++ ++#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) ++#define RB_EMPTY_NODE(node) (rb_parent(node) == node) ++#define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) ++ ++extern void rb_insert_color(struct rb_node *, struct rb_root *); ++extern void rb_erase(struct rb_node *, struct rb_root *); ++ ++/* Find logical next and previous nodes in a tree */ ++extern struct rb_node *rb_next(struct rb_node *); ++extern struct rb_node *rb_prev(struct rb_node *); ++extern struct rb_node *rb_first(struct rb_root *); ++extern struct rb_node *rb_last(struct rb_root *); ++ ++/* Fast replacement of a single node without remove/rebalance/add/rebalance */ ++extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, ++ struct rb_root *root); ++ ++static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, ++ struct rb_node ** rb_link) ++{ ++ node->rb_parent_color = (unsigned long )parent; ++ node->rb_left = node->rb_right = NULL; ++ ++ *rb_link = node; ++} ++ ++#endif /* _LINUX_RBTREE_H */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/recv_image.c linux-2.6.24.7/drivers/mtd/mtd-utils/recv_image.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/recv_image.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/recv_image.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,484 @@ ++ ++#define _XOPEN_SOURCE 500 ++#define _USE_MISC ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "crc32.h" ++#include "mtd/mtd-user.h" ++#include "mcast_image.h" ++ ++#define min(x,y) ( (x)>(y)?(y):(x) ) ++ ++#define WBUF_SIZE 4096 ++struct eraseblock { ++ uint32_t flash_offset; ++ unsigned char wbuf[WBUF_SIZE]; ++ int wbuf_ofs; ++ int nr_pkts; ++ int *pkt_indices; ++ uint32_t crc; ++}; ++ ++int main(int argc, char **argv) ++{ ++ struct addrinfo *ai; ++ struct addrinfo hints; ++ struct addrinfo *runp; ++ int ret; ++ int sock; ++ size_t len; ++ int flfd; ++ struct mtd_info_user meminfo; ++ unsigned char *eb_buf, *decode_buf, **src_pkts; ++ int nr_blocks = 0; ++ int pkts_per_block; ++ int block_nr = -1; ++ uint32_t image_crc; ++ int total_pkts = 0; ++ int ignored_pkts = 0; ++ loff_t mtdoffset = 0; ++ int badcrcs = 0; ++ int duplicates = 0; ++ int file_mode = 0; ++ struct fec_parms *fec; ++ int i; ++ struct eraseblock *eraseblocks = NULL; ++ uint32_t start_seq; ++ struct timeval start, now; ++ unsigned long fec_time = 0, flash_time = 0, crc_time = 0, ++ rflash_time = 0, erase_time = 0, net_time = 0; ++ ++ if (argc != 4) { ++ fprintf(stderr, "usage: %s \n", ++ (strrchr(argv[0], '/')?:argv[0]-1)+1); ++ exit(1); ++ } ++ /* Open the device */ ++ flfd = open(argv[3], O_RDWR); ++ ++ if (flfd >= 0) { ++ /* Fill in MTD device capability structure */ ++ if (ioctl(flfd, MEMGETINFO, &meminfo) != 0) { ++ perror("MEMGETINFO"); ++ close(flfd); ++ flfd = -1; ++ } else { ++ printf("Receive to MTD device %s with erasesize %d\n", ++ argv[3], meminfo.erasesize); ++ } ++ } ++ if (flfd == -1) { ++ /* Try again, as if it's a file */ ++ flfd = open(argv[3], O_CREAT|O_TRUNC|O_RDWR, 0644); ++ if (flfd < 0) { ++ perror("open"); ++ exit(1); ++ } ++ meminfo.erasesize = 131072; ++ file_mode = 1; ++ printf("Receive to file %s with (assumed) erasesize %d\n", ++ argv[3], meminfo.erasesize); ++ } ++ ++ pkts_per_block = (meminfo.erasesize + PKT_SIZE - 1) / PKT_SIZE; ++ ++ eb_buf = malloc(pkts_per_block * PKT_SIZE); ++ decode_buf = malloc(pkts_per_block * PKT_SIZE); ++ if (!eb_buf && !decode_buf) { ++ fprintf(stderr, "No memory for eraseblock buffer\n"); ++ exit(1); ++ } ++ src_pkts = malloc(sizeof(unsigned char *) * pkts_per_block); ++ if (!src_pkts) { ++ fprintf(stderr, "No memory for decode packet pointers\n"); ++ exit(1); ++ } ++ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_flags = AI_ADDRCONFIG; ++ hints.ai_socktype = SOCK_DGRAM; ++ ++ ret = getaddrinfo(argv[1], argv[2], &hints, &ai); ++ if (ret) { ++ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); ++ exit(1); ++ } ++ runp = ai; ++ for (runp = ai; runp; runp = runp->ai_next) { ++ sock = socket(runp->ai_family, runp->ai_socktype, ++ runp->ai_protocol); ++ if (sock == -1) { ++ perror("socket"); ++ continue; ++ } ++ if (runp->ai_family == AF_INET && ++ IN_MULTICAST( ntohl(((struct sockaddr_in *)runp->ai_addr)->sin_addr.s_addr))) { ++ struct ip_mreq rq; ++ rq.imr_multiaddr = ((struct sockaddr_in *)runp->ai_addr)->sin_addr; ++ rq.imr_interface.s_addr = INADDR_ANY; ++ if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &rq, sizeof(rq))) { ++ perror("IP_ADD_MEMBERSHIP"); ++ close(sock); ++ continue; ++ } ++ ++ } else if (runp->ai_family == AF_INET6 && ++ ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr.s6_addr[0] == 0xff) { ++ struct ipv6_mreq rq; ++ rq.ipv6mr_multiaddr = ((struct sockaddr_in6 *)runp->ai_addr)->sin6_addr; ++ rq.ipv6mr_interface = 0; ++ if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &rq, sizeof(rq))) { ++ perror("IPV6_ADD_MEMBERSHIP"); ++ close(sock); ++ continue; ++ } ++ } ++ if (bind(sock, runp->ai_addr, runp->ai_addrlen)) { ++ perror("bind"); ++ close(sock); ++ continue; ++ } ++ break; ++ } ++ if (!runp) ++ exit(1); ++ ++ while (1) { ++ struct image_pkt thispkt; ++ ++ len = read(sock, &thispkt, sizeof(thispkt)); ++ ++ if (len < 0) { ++ perror("read socket"); ++ break; ++ } ++ if (len < sizeof(thispkt)) { ++ fprintf(stderr, "Wrong length %d bytes (expected %d)\n", ++ len, sizeof(thispkt)); ++ continue; ++ } ++ if (!eraseblocks) { ++ image_crc = thispkt.hdr.totcrc; ++ start_seq = ntohl(thispkt.hdr.pkt_sequence); ++ ++ if (meminfo.erasesize != ntohl(thispkt.hdr.blocksize)) { ++ fprintf(stderr, "Erasesize mismatch (0x%x not 0x%x)\n", ++ ntohl(thispkt.hdr.blocksize), meminfo.erasesize); ++ exit(1); ++ } ++ nr_blocks = ntohl(thispkt.hdr.nr_blocks); ++ ++ fec = fec_new(pkts_per_block, ntohs(thispkt.hdr.nr_pkts)); ++ ++ eraseblocks = malloc(nr_blocks * sizeof(*eraseblocks)); ++ if (!eraseblocks) { ++ fprintf(stderr, "No memory for block map\n"); ++ exit(1); ++ } ++ for (i = 0; i < nr_blocks; i++) { ++ eraseblocks[i].pkt_indices = malloc(sizeof(int) * pkts_per_block); ++ if (!eraseblocks[i].pkt_indices) { ++ fprintf(stderr, "Failed to allocate packet indices\n"); ++ exit(1); ++ } ++ eraseblocks[i].nr_pkts = 0; ++ if (!file_mode) { ++ if (mtdoffset >= meminfo.size) { ++ fprintf(stderr, "Run out of space on flash\n"); ++ exit(1); ++ } ++#if 1 /* Deliberately use bad blocks... test write failures */ ++ while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { ++ printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); ++ mtdoffset += meminfo.erasesize; ++ } ++#endif ++ } ++ eraseblocks[i].flash_offset = mtdoffset; ++ mtdoffset += meminfo.erasesize; ++ eraseblocks[i].wbuf_ofs = 0; ++ } ++ gettimeofday(&start, NULL); ++ } ++ if (image_crc != thispkt.hdr.totcrc) { ++ fprintf(stderr, "\nImage CRC changed from 0x%x to 0x%x. Aborting\n", ++ ntohl(image_crc), ntohl(thispkt.hdr.totcrc)); ++ exit(1); ++ } ++ ++ block_nr = ntohl(thispkt.hdr.block_nr); ++ if (block_nr >= nr_blocks) { ++ fprintf(stderr, "\nErroneous block_nr %d (> %d)\n", ++ block_nr, nr_blocks); ++ exit(1); ++ } ++ for (i=0; i= pkts_per_block) { ++ /* We have a block which we didn't really need */ ++ eraseblocks[block_nr].nr_pkts++; ++ ignored_pkts++; ++ continue; ++ } ++ ++ if (crc32(-1, thispkt.data, PKT_SIZE) != ntohl(thispkt.hdr.thiscrc)) { ++ printf("\nDiscard %08x pkt %d with bad CRC (%08x not %08x)\n", ++ block_nr * meminfo.erasesize, ntohs(thispkt.hdr.pkt_nr), ++ crc32(-1, thispkt.data, PKT_SIZE), ++ ntohl(thispkt.hdr.thiscrc)); ++ badcrcs++; ++ continue; ++ } ++ pkt_again: ++ eraseblocks[block_nr].pkt_indices[eraseblocks[block_nr].nr_pkts++] = ++ ntohs(thispkt.hdr.pkt_nr); ++ total_pkts++; ++ if (!(total_pkts % 50) || total_pkts == pkts_per_block * nr_blocks) { ++ uint32_t pkts_sent = ntohl(thispkt.hdr.pkt_sequence) - start_seq + 1; ++ long time_msec; ++ gettimeofday(&now, NULL); ++ ++ time_msec = ((now.tv_usec - start.tv_usec) / 1000) + ++ (now.tv_sec - start.tv_sec) * 1000; ++ ++ printf("\rReceived %d/%d (%d%%) in %lds @%ldKiB/s, %d lost (%d%%), %d dup/xs ", ++ total_pkts, nr_blocks * pkts_per_block, ++ total_pkts * 100 / nr_blocks / pkts_per_block, ++ time_msec / 1000, ++ total_pkts * PKT_SIZE / 1024 * 1000 / time_msec, ++ pkts_sent - total_pkts - duplicates - ignored_pkts, ++ (pkts_sent - total_pkts - duplicates - ignored_pkts) * 100 / pkts_sent, ++ duplicates + ignored_pkts); ++ fflush(stdout); ++ } ++ ++ if (eraseblocks[block_nr].wbuf_ofs + PKT_SIZE < WBUF_SIZE) { ++ /* New packet doesn't full the wbuf */ ++ memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs, ++ thispkt.data, PKT_SIZE); ++ eraseblocks[block_nr].wbuf_ofs += PKT_SIZE; ++ } else { ++ int fits = WBUF_SIZE - eraseblocks[block_nr].wbuf_ofs; ++ ssize_t wrotelen; ++ static int faked = 1; ++ ++ memcpy(eraseblocks[block_nr].wbuf + eraseblocks[block_nr].wbuf_ofs, ++ thispkt.data, fits); ++ wrotelen = pwrite(flfd, eraseblocks[block_nr].wbuf, WBUF_SIZE, ++ eraseblocks[block_nr].flash_offset); ++ ++ if (wrotelen < WBUF_SIZE || (block_nr == 5 && eraseblocks[block_nr].nr_pkts == 5 && !faked)) { ++ faked = 1; ++ if (wrotelen < 0) ++ perror("\npacket write"); ++ else ++ fprintf(stderr, "\nshort write of packet wbuf\n"); ++ ++ if (!file_mode) { ++ struct erase_info_user erase; ++ /* FIXME: Perhaps we should store pkt crcs and try ++ to recover data from the offending eraseblock */ ++ ++ /* We have increased nr_pkts but not yet flash_offset */ ++ erase.start = eraseblocks[block_nr].flash_offset & ++ ~(meminfo.erasesize - 1); ++ erase.length = meminfo.erasesize; ++ ++ printf("Will erase at %08lx len %08lx (bad write was at %08lx)\n", ++ erase.start, erase.length, eraseblocks[block_nr].flash_offset); ++ if (ioctl(flfd, MEMERASE, &erase)) { ++ perror("MEMERASE"); ++ exit(1); ++ } ++ if (mtdoffset >= meminfo.size) { ++ fprintf(stderr, "Run out of space on flash\n"); ++ exit(1); ++ } ++ while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { ++ printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); ++ mtdoffset += meminfo.erasesize; ++ if (mtdoffset >= meminfo.size) { ++ fprintf(stderr, "Run out of space on flash\n"); ++ exit(1); ++ } ++ } ++ eraseblocks[block_nr].flash_offset = mtdoffset; ++ printf("Block #%d will now be at %08lx\n", block_nr, (long)mtdoffset); ++ total_pkts -= eraseblocks[block_nr].nr_pkts; ++ eraseblocks[block_nr].nr_pkts = 0; ++ eraseblocks[block_nr].wbuf_ofs = 0; ++ mtdoffset += meminfo.erasesize; ++ goto pkt_again; ++ } ++ else /* Usually nothing we can do in file mode */ ++ exit(1); ++ } ++ eraseblocks[block_nr].flash_offset += WBUF_SIZE; ++ /* Copy the remainder into the wbuf */ ++ memcpy(eraseblocks[block_nr].wbuf, &thispkt.data[fits], PKT_SIZE - fits); ++ eraseblocks[block_nr].wbuf_ofs = PKT_SIZE - fits; ++ } ++ ++ if (eraseblocks[block_nr].nr_pkts == pkts_per_block) { ++ eraseblocks[block_nr].crc = ntohl(thispkt.hdr.block_crc); ++ ++ if (total_pkts == nr_blocks * pkts_per_block) ++ break; ++ } ++ } ++ printf("\n"); ++ gettimeofday(&now, NULL); ++ net_time = (now.tv_usec - start.tv_usec) / 1000; ++ net_time += (now.tv_sec - start.tv_sec) * 1000; ++ close(sock); ++ for (block_nr = 0; block_nr < nr_blocks; block_nr++) { ++ ssize_t rwlen; ++ gettimeofday(&start, NULL); ++ eraseblocks[block_nr].flash_offset -= meminfo.erasesize; ++ rwlen = pread(flfd, eb_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset); ++ ++ gettimeofday(&now, NULL); ++ rflash_time += (now.tv_usec - start.tv_usec) / 1000; ++ rflash_time += (now.tv_sec - start.tv_sec) * 1000; ++ if (rwlen < 0) { ++ perror("read"); ++ /* Argh. Perhaps we could go back and try again, but if the flash is ++ going to fail to read back what we write to it, and the whole point ++ in this program is to write to it, what's the point? */ ++ fprintf(stderr, "Packets we wrote to flash seem to be unreadable. Aborting\n"); ++ exit(1); ++ } ++ ++ memcpy(eb_buf + meminfo.erasesize, eraseblocks[block_nr].wbuf, ++ eraseblocks[block_nr].wbuf_ofs); ++ ++ for (i=0; i < pkts_per_block; i++) ++ src_pkts[i] = &eb_buf[i * PKT_SIZE]; ++ ++ gettimeofday(&start, NULL); ++ if (fec_decode(fec, src_pkts, eraseblocks[block_nr].pkt_indices, PKT_SIZE)) { ++ /* Eep. This cannot happen */ ++ printf("The world is broken. fec_decode() returned error\n"); ++ exit(1); ++ } ++ gettimeofday(&now, NULL); ++ fec_time += (now.tv_usec - start.tv_usec) / 1000; ++ fec_time += (now.tv_sec - start.tv_sec) * 1000; ++ ++ for (i=0; i < pkts_per_block; i++) ++ memcpy(&decode_buf[i*PKT_SIZE], src_pkts[i], PKT_SIZE); ++ ++ /* Paranoia */ ++ gettimeofday(&start, NULL); ++ if (crc32(-1, decode_buf, meminfo.erasesize) != eraseblocks[block_nr].crc) { ++ printf("\nCRC mismatch for block #%d: want %08x got %08x\n", ++ block_nr, eraseblocks[block_nr].crc, ++ crc32(-1, decode_buf, meminfo.erasesize)); ++ exit(1); ++ } ++ gettimeofday(&now, NULL); ++ crc_time += (now.tv_usec - start.tv_usec) / 1000; ++ crc_time += (now.tv_sec - start.tv_sec) * 1000; ++ start = now; ++ ++ if (!file_mode) { ++ struct erase_info_user erase; ++ ++ erase.start = eraseblocks[block_nr].flash_offset; ++ erase.length = meminfo.erasesize; ++ ++ printf("\rErasing block at %08x...", erase.start); ++ ++ if (ioctl(flfd, MEMERASE, &erase)) { ++ perror("MEMERASE"); ++ /* This block has dirty data on it. If the erase failed, we're screwed */ ++ fprintf(stderr, "Erase to clean FEC data from flash failed. Aborting\n"); ++ exit(1); ++ } ++ gettimeofday(&now, NULL); ++ erase_time += (now.tv_usec - start.tv_usec) / 1000; ++ erase_time += (now.tv_sec - start.tv_sec) * 1000; ++ start = now; ++ } ++ else printf("\r"); ++ write_again: ++ rwlen = pwrite(flfd, decode_buf, meminfo.erasesize, eraseblocks[block_nr].flash_offset); ++ if (rwlen < meminfo.erasesize) { ++ if (rwlen < 0) { ++ perror("\ndecoded data write"); ++ } else ++ fprintf(stderr, "\nshort write of decoded data\n"); ++ ++ if (!file_mode) { ++ struct erase_info_user erase; ++ erase.start = eraseblocks[block_nr].flash_offset; ++ erase.length = meminfo.erasesize; ++ ++ printf("Erasing failed block at %08x\n", ++ eraseblocks[block_nr].flash_offset); ++ ++ if (ioctl(flfd, MEMERASE, &erase)) { ++ perror("MEMERASE"); ++ exit(1); ++ } ++ if (mtdoffset >= meminfo.size) { ++ fprintf(stderr, "Run out of space on flash\n"); ++ exit(1); ++ } ++ while (ioctl(flfd, MEMGETBADBLOCK, &mtdoffset) > 0) { ++ printf("Skipping flash bad block at %08x\n", (uint32_t)mtdoffset); ++ mtdoffset += meminfo.erasesize; ++ if (mtdoffset >= meminfo.size) { ++ fprintf(stderr, "Run out of space on flash\n"); ++ exit(1); ++ } ++ } ++ printf("Will try again at %08lx...", (long)mtdoffset); ++ eraseblocks[block_nr].flash_offset = mtdoffset; ++ ++ goto write_again; ++ } ++ else /* Usually nothing we can do in file mode */ ++ exit(1); ++ } ++ gettimeofday(&now, NULL); ++ flash_time += (now.tv_usec - start.tv_usec) / 1000; ++ flash_time += (now.tv_sec - start.tv_sec) * 1000; ++ ++ printf("wrote image block %08x (%d pkts) ", ++ block_nr * meminfo.erasesize, eraseblocks[block_nr].nr_pkts); ++ fflush(stdout); ++ } ++ close(flfd); ++ printf("Net rx %ld.%03lds\n", net_time / 1000, net_time % 1000); ++ printf("flash rd %ld.%03lds\n", rflash_time / 1000, rflash_time % 1000); ++ printf("FEC time %ld.%03lds\n", fec_time / 1000, fec_time % 1000); ++ printf("CRC time %ld.%03lds\n", crc_time / 1000, crc_time % 1000); ++ printf("flash wr %ld.%03lds\n", flash_time / 1000, flash_time % 1000); ++ printf("flash er %ld.%03lds\n", erase_time / 1000, erase_time % 1000); ++ ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/rfddump.c linux-2.6.24.7/drivers/mtd/mtd-utils/rfddump.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/rfddump.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/rfddump.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,336 @@ ++/* ++ * rfddump.c ++ * ++ * Copyright (C) 2005 Sean Young ++ * ++ * 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. ++ */ ++ ++#define _XOPEN_SOURCE 500 /* For pread */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* next is an array of mapping for each corresponding sector */ ++#define RFD_MAGIC 0x9193 ++#define HEADER_MAP_OFFSET 3 ++#define SECTOR_DELETED 0x0000 ++#define SECTOR_ZERO 0xfffe ++#define SECTOR_FREE 0xffff ++ ++#define SECTOR_SIZE 512 ++ ++#define SECTORS_PER_TRACK 63 ++ ++ ++struct rfd { ++ int block_size; ++ int block_count; ++ int header_sectors; ++ int data_sectors; ++ int header_size; ++ uint16_t *header; ++ int sector_count; ++ int *sector_map; ++ const char *mtd_filename; ++ const char *out_filename; ++ int verbose; ++}; ++ ++#define PROGRAM "rfddump" ++#define VERSION "$Revision 1.0 $" ++ ++void display_help(void) ++{ ++ printf("Usage: " PROGRAM " [OPTIONS] MTD-device filename\n" ++ "Dumps the contents of a resident flash disk\n" ++ "\n" ++ "-h --help display this help and exit\n" ++ "-V --version output version information and exit\n" ++ "-v --verbose Be verbose\n" ++ "-b size --blocksize Block size (defaults to erase unit)\n"); ++ exit(0); ++} ++ ++void display_version(void) ++{ ++ printf(PROGRAM " " VERSION "\n" ++ "\n" ++ "This is free software; see the source for copying conditions. There is NO\n" ++ "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); ++ ++ exit(0); ++} ++ ++void process_options(int argc, char *argv[], struct rfd *rfd) ++{ ++ int error = 0; ++ ++ rfd->block_size = 0; ++ rfd->verbose = 0; ++ ++ for (;;) { ++ int option_index = 0; ++ static const char *short_options = "hvVb:"; ++ static const struct option long_options[] = { ++ { "help", no_argument, 0, 'h' }, ++ { "version", no_argument, 0, 'V', }, ++ { "blocksize", required_argument, 0, 'b' }, ++ { "verbose", no_argument, 0, 'v' }, ++ { NULL, 0, 0, 0 } ++ }; ++ ++ int c = getopt_long(argc, argv, short_options, ++ long_options, &option_index); ++ if (c == EOF) ++ break; ++ ++ switch (c) { ++ case 'h': ++ display_help(); ++ break; ++ case 'V': ++ display_version(); ++ break; ++ case 'v': ++ rfd->verbose = 1; ++ break; ++ case 'b': ++ rfd->block_size = atoi(optarg); ++ break; ++ case '?': ++ error = 1; ++ break; ++ } ++ } ++ ++ if ((argc - optind) != 2 || error) ++ display_help(); ++ ++ rfd->mtd_filename = argv[optind]; ++ rfd->out_filename = argv[optind + 1]; ++} ++ ++int build_block_map(struct rfd *rfd, int fd, int block) ++{ ++ int i; ++ int sectors; ++ ++ if (pread(fd, rfd->header, rfd->header_size, block * rfd->block_size) ++ != rfd->header_size) { ++ return -1; ++ } ++ ++ if (le16_to_cpu(rfd->header[0]) != RFD_MAGIC) { ++ if (rfd->verbose) ++ printf("Block #%02d: Magic missing\n", block); ++ ++ return 0; ++ } ++ ++ sectors = 0; ++ for (i=0; idata_sectors; i++) { ++ uint16_t entry = le16_to_cpu(rfd->header[i + HEADER_MAP_OFFSET]); ++ ++ if (entry == SECTOR_FREE || entry == SECTOR_DELETED) ++ continue; ++ ++ if (entry == SECTOR_ZERO) ++ entry = 0; ++ ++ if (entry >= rfd->sector_count) { ++ fprintf(stderr, "%s: warning: sector %d out of range\n", ++ rfd->mtd_filename, entry); ++ continue; ++ } ++ ++ if (rfd->sector_map[entry] != -1) { ++ fprintf(stderr, "%s: warning: more than one entry " ++ "for sector %d\n", rfd->mtd_filename, entry); ++ continue; ++ } ++ ++ rfd->sector_map[entry] = rfd->block_size * block + ++ (i + rfd->header_sectors) * SECTOR_SIZE; ++ sectors++; ++ } ++ ++ if (rfd->verbose) ++ printf("Block #%02d: %d sectors\n", block, sectors); ++ ++ return 1; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int fd, sectors_per_block; ++ mtd_info_t mtd_info; ++ struct rfd rfd; ++ int i, blocks_found; ++ int out_fd = 0; ++ uint8_t sector[512]; ++ int blank, rc, cylinders; ++ ++ process_options(argc, argv, &rfd); ++ ++ fd = open(rfd.mtd_filename, O_RDONLY); ++ if (fd == -1) { ++ perror(rfd.mtd_filename); ++ return 1; ++ } ++ ++ if (rfd.block_size == 0) { ++ if (ioctl(fd, MEMGETINFO, &mtd_info)) { ++ perror(rfd.mtd_filename); ++ close(fd); ++ return 1; ++ } ++ ++ if (mtd_info.type != MTD_NORFLASH) { ++ fprintf(stderr, "%s: wrong type\n", rfd.mtd_filename); ++ close(fd); ++ return 2; ++ } ++ ++ sectors_per_block = mtd_info.erasesize / SECTOR_SIZE; ++ ++ rfd.block_size = mtd_info.erasesize; ++ rfd.block_count = mtd_info.size / mtd_info.erasesize; ++ } else { ++ struct stat st; ++ ++ if (fstat(fd, &st) == -1) { ++ perror(rfd.mtd_filename); ++ close(fd); ++ return 1; ++ } ++ ++ if (st.st_size % SECTOR_SIZE) ++ fprintf(stderr, "%s: warning: not a multiple of sectors (512 bytes)\n", rfd.mtd_filename); ++ ++ sectors_per_block = rfd.block_size / SECTOR_SIZE; ++ ++ if (st.st_size % rfd.block_size) ++ fprintf(stderr, "%s: warning: not a multiple of block size\n", rfd.mtd_filename); ++ ++ rfd.block_count = st.st_size / rfd.block_size; ++ ++ if (!rfd.block_count) { ++ fprintf(stderr, "%s: not large enough for one block\n", rfd.mtd_filename); ++ close(fd); ++ return 2; ++ } ++ } ++ ++ rfd.header_sectors = ++ ((HEADER_MAP_OFFSET + sectors_per_block) * ++ sizeof(uint16_t) + SECTOR_SIZE - 1) / SECTOR_SIZE; ++ rfd.data_sectors = sectors_per_block - rfd.header_sectors; ++ cylinders = ((rfd.block_count - 1) * rfd.data_sectors - 1) ++ / SECTORS_PER_TRACK; ++ rfd.sector_count = cylinders * SECTORS_PER_TRACK; ++ rfd.header_size = ++ (HEADER_MAP_OFFSET + rfd.data_sectors) * sizeof(uint16_t); ++ ++ rfd.header = malloc(rfd.header_size); ++ if (!rfd.header) { ++ perror(PROGRAM); ++ close(fd); ++ return 2; ++ } ++ rfd.sector_map = malloc(rfd.sector_count * sizeof(int)); ++ if (!rfd.sector_map) { ++ perror(PROGRAM); ++ close(fd); ++ free(rfd.sector_map); ++ return 2; ++ } ++ ++ rfd.mtd_filename = rfd.mtd_filename; ++ ++ for (i=0; i 0) ++ blocks_found++; ++ if (rc < 0) ++ goto err; ++ } ++ ++ if (!blocks_found) { ++ fprintf(stderr, "%s: no RFD blocks found\n", rfd.mtd_filename); ++ goto err; ++ } ++ ++ for (i=0; i ++ * ++ * 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 is very easy: just erase all the blocks and put the magic at ++ * the beginning of each block. ++ */ ++ ++#define _XOPEN_SOURCE 500 /* For pread/pwrite */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define PROGRAM "rfdformat" ++#define VERSION "$Revision 1.0 $" ++ ++void display_help(void) ++{ ++ printf("Usage: " PROGRAM " [OPTIONS] MTD-device\n" ++ "Formats NOR flash for resident flash disk\n" ++ "\n" ++ "-h --help display this help and exit\n" ++ "-V --version output version information and exit\n"); ++ exit(0); ++} ++ ++void display_version(void) ++{ ++ printf(PROGRAM " " VERSION "\n" ++ "\n" ++ "This is free software; see the source for copying conditions. There is NO\n" ++ "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); ++ ++ exit(0); ++} ++ ++void process_options(int argc, char *argv[], const char **mtd_filename) ++{ ++ int error = 0; ++ ++ for (;;) { ++ int option_index = 0; ++ static const char *short_options = "hV"; ++ static const struct option long_options[] = { ++ { "help", no_argument, 0, 'h' }, ++ { "version", no_argument, 0, 'V', }, ++ { NULL, 0, 0, 0 } ++ }; ++ ++ int c = getopt_long(argc, argv, short_options, ++ long_options, &option_index); ++ if (c == EOF) ++ break; ++ ++ switch (c) { ++ case 'h': ++ display_help(); ++ break; ++ case 'V': ++ display_version(); ++ break; ++ case '?': ++ error = 1; ++ break; ++ } ++ } ++ ++ if ((argc - optind) != 1 || error) ++ display_help(); ++ ++ *mtd_filename = argv[optind]; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ static const uint8_t magic[] = { 0x93, 0x91 }; ++ int fd, block_count, i; ++ struct mtd_info_user mtd_info; ++ char buf[512]; ++ const char *mtd_filename; ++ ++ process_options(argc, argv, &mtd_filename); ++ ++ fd = open(mtd_filename, O_RDWR); ++ if (fd == -1) { ++ perror(mtd_filename); ++ return 1; ++ } ++ ++ if (ioctl(fd, MEMGETINFO, &mtd_info)) { ++ perror(mtd_filename); ++ close(fd); ++ return 1; ++ } ++ ++ if (mtd_info.type != MTD_NORFLASH) { ++ fprintf(stderr, "%s: not NOR flash\n", mtd_filename); ++ close(fd); ++ return 2; ++ } ++ ++ if (mtd_info.size > 32*1024*1024) { ++ fprintf(stderr, "%s: flash larger than 32MiB not supported\n", ++ mtd_filename); ++ close(fd); ++ return 2; ++ } ++ ++ block_count = mtd_info.size / mtd_info.erasesize; ++ ++ if (block_count < 2) { ++ fprintf(stderr, "%s: at least two erase units required\n", ++ mtd_filename); ++ close(fd); ++ return 2; ++ } ++ ++ for (i=0; i ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "crc32.h" ++#include "mcast_image.h" ++ ++int tx_rate = 80000; ++int pkt_delay; ++ ++#undef RANDOMDROP ++ ++int main(int argc, char **argv) ++{ ++ struct addrinfo *ai; ++ struct addrinfo hints; ++ struct addrinfo *runp; ++ int ret; ++ int sock; ++ struct image_pkt pktbuf; ++ int rfd; ++ struct stat st; ++ int writeerrors = 0; ++ uint32_t erasesize; ++ unsigned char *image, *blockptr = NULL; ++ uint32_t block_nr, pkt_nr; ++ int nr_blocks; ++ struct timeval then, now, nextpkt; ++ long time_msecs; ++ int pkts_per_block; ++ int total_pkts_per_block; ++ struct fec_parms *fec; ++ unsigned char *last_block; ++ uint32_t *block_crcs; ++ long tosleep; ++ uint32_t sequence = 0; ++ ++ if (argc == 6) { ++ tx_rate = atol(argv[5]) * 1024; ++ if (tx_rate < PKT_SIZE || tx_rate > 20000000) { ++ fprintf(stderr, "Bogus TX rate %d KiB/s\n", tx_rate); ++ exit(1); ++ } ++ argc = 5; ++ } ++ if (argc != 5) { ++ fprintf(stderr, "usage: %s []\n", ++ (strrchr(argv[0], '/')?:argv[0]-1)+1); ++ exit(1); ++ } ++ pkt_delay = (sizeof(pktbuf) * 1000000) / tx_rate; ++ printf("Inter-packet delay (avg): %dµs\n", pkt_delay); ++ printf("Transmit rate: %d KiB/s\n", tx_rate / 1024); ++ ++ erasesize = atol(argv[4]); ++ if (!erasesize) { ++ fprintf(stderr, "erasesize cannot be zero\n"); ++ exit(1); ++ } ++ ++ pkts_per_block = (erasesize + PKT_SIZE - 1) / PKT_SIZE; ++ total_pkts_per_block = pkts_per_block * 3 / 2; ++ ++ /* We have to pad it with zeroes, so can't use it in-place */ ++ last_block = malloc(pkts_per_block * PKT_SIZE); ++ if (!last_block) { ++ fprintf(stderr, "Failed to allocate last-block buffer\n"); ++ exit(1); ++ } ++ ++ fec = fec_new(pkts_per_block, total_pkts_per_block); ++ if (!fec) { ++ fprintf(stderr, "Error initialising FEC\n"); ++ exit(1); ++ } ++ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_flags = AI_ADDRCONFIG; ++ hints.ai_socktype = SOCK_DGRAM; ++ ++ ret = getaddrinfo(argv[1], argv[2], &hints, &ai); ++ if (ret) { ++ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); ++ exit(1); ++ } ++ runp = ai; ++ for (runp = ai; runp; runp = runp->ai_next) { ++ sock = socket(runp->ai_family, runp->ai_socktype, ++ runp->ai_protocol); ++ if (sock == -1) { ++ perror("socket"); ++ continue; ++ } ++ if (connect(sock, runp->ai_addr, runp->ai_addrlen) == 0) ++ break; ++ perror("connect"); ++ close(sock); ++ } ++ if (!runp) ++ exit(1); ++ ++ rfd = open(argv[3], O_RDONLY); ++ if (rfd < 0) { ++ perror("open"); ++ exit(1); ++ } ++ ++ if (fstat(rfd, &st)) { ++ perror("fstat"); ++ exit(1); ++ } ++ ++ if (st.st_size % erasesize) { ++ fprintf(stderr, "Image size %ld bytes is not a multiple of erasesize %d bytes\n", ++ st.st_size, erasesize); ++ exit(1); ++ } ++ image = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, rfd, 0); ++ if (image == MAP_FAILED) { ++ perror("mmap"); ++ exit(1); ++ } ++ ++ nr_blocks = st.st_size / erasesize; ++ ++ block_crcs = malloc(nr_blocks * sizeof(uint32_t)); ++ if (!block_crcs) { ++ fprintf(stderr, "Failed to allocate memory for CRCs\n"); ++ exit(1); ++ } ++ ++ memcpy(last_block, image + (nr_blocks - 1) * erasesize, erasesize); ++ memset(last_block + erasesize, 0, (PKT_SIZE * pkts_per_block) - erasesize); ++ ++ printf("Checking CRC...."); ++ fflush(stdout); ++ ++ pktbuf.hdr.resend = 0; ++ pktbuf.hdr.totcrc = htonl(crc32(-1, image, st.st_size)); ++ pktbuf.hdr.nr_blocks = htonl(nr_blocks); ++ pktbuf.hdr.blocksize = htonl(erasesize); ++ pktbuf.hdr.thislen = htonl(PKT_SIZE); ++ pktbuf.hdr.nr_pkts = htons(total_pkts_per_block); ++ ++ printf("%08x\n", ntohl(pktbuf.hdr.totcrc)); ++ printf("Checking block CRCs...."); ++ fflush(stdout); ++ for (block_nr=0; block_nr < nr_blocks; block_nr++) { ++ printf("\rChecking block CRCS.... %d/%d", ++ block_nr + 1, nr_blocks); ++ fflush(stdout); ++ block_crcs[block_nr] = crc32(-1, image + (block_nr * erasesize), erasesize); ++ } ++ ++ printf("\nImage size %ld KiB (0x%08lx). %d blocks at %d pkts/block\n" ++ "Estimated transmit time per cycle: %ds\n", ++ (long)st.st_size / 1024, (long) st.st_size, ++ nr_blocks, pkts_per_block, ++ nr_blocks * pkts_per_block * pkt_delay / 1000000); ++ gettimeofday(&then, NULL); ++ nextpkt = then; ++ ++#ifdef RANDOMDROP ++ srand((unsigned)then.tv_usec); ++ printf("Random seed %u\n", (unsigned)then.tv_usec); ++#endif ++ while (1) for (pkt_nr=0; pkt_nr < total_pkts_per_block; pkt_nr++) { ++ ++ if (blockptr && pkt_nr == 0) { ++ unsigned long amt_sent = total_pkts_per_block * nr_blocks * sizeof(pktbuf); ++ gettimeofday(&now, NULL); ++ ++ time_msecs = (now.tv_sec - then.tv_sec) * 1000; ++ time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000; ++ printf("\n%ld KiB sent in %ldms (%ld KiB/s)\n", ++ amt_sent / 1024, time_msecs, ++ amt_sent / 1024 * 1000 / time_msecs); ++ then = now; ++ } ++ ++ for (block_nr = 0; block_nr < nr_blocks; block_nr++) { ++ ++ int actualpkt; ++ ++ /* Calculating the redundant FEC blocks is expensive; ++ the first $pkts_per_block are cheap enough though ++ because they're just copies. So alternate between ++ simple and complex stuff, so that we don't start ++ to choke and fail to keep up with the expected ++ bitrate in the second half of the sequence */ ++ if (block_nr & 1) ++ actualpkt = pkt_nr; ++ else ++ actualpkt = total_pkts_per_block - 1 - pkt_nr; ++ ++ blockptr = image + (erasesize * block_nr); ++ if (block_nr == nr_blocks - 1) ++ blockptr = last_block; ++ ++ fec_encode_linear(fec, blockptr, pktbuf.data, actualpkt, PKT_SIZE); ++ ++ pktbuf.hdr.thiscrc = htonl(crc32(-1, pktbuf.data, PKT_SIZE)); ++ pktbuf.hdr.block_crc = htonl(block_crcs[block_nr]); ++ pktbuf.hdr.block_nr = htonl(block_nr); ++ pktbuf.hdr.pkt_nr = htons(actualpkt); ++ pktbuf.hdr.pkt_sequence = htonl(sequence++); ++ ++ printf("\rSending data block %08x packet %3d/%d", ++ block_nr * erasesize, ++ pkt_nr, total_pkts_per_block); ++ ++ if (pkt_nr && !block_nr) { ++ unsigned long amt_sent = pkt_nr * nr_blocks * sizeof(pktbuf); ++ ++ gettimeofday(&now, NULL); ++ ++ time_msecs = (now.tv_sec - then.tv_sec) * 1000; ++ time_msecs += ((int)(now.tv_usec - then.tv_usec)) / 1000; ++ printf(" (%ld KiB/s) ", ++ amt_sent / 1024 * 1000 / time_msecs); ++ } ++ ++ fflush(stdout); ++ ++#ifdef RANDOMDROP ++ if ((rand() % 1000) < 20) { ++ printf("\nDropping packet %d of block %08x\n", pkt_nr+1, block_nr * erasesize); ++ continue; ++ } ++#endif ++ gettimeofday(&now, NULL); ++#if 1 ++ tosleep = nextpkt.tv_usec - now.tv_usec + ++ (1000000 * (nextpkt.tv_sec - now.tv_sec)); ++ ++ /* We need hrtimers for this to actually work */ ++ if (tosleep > 0) { ++ struct timespec req; ++ ++ req.tv_nsec = (tosleep % 1000000) * 1000; ++ req.tv_sec = tosleep / 1000000; ++ ++ nanosleep(&req, NULL); ++ } ++#else ++ while (now.tv_sec < nextpkt.tv_sec || ++ (now.tv_sec == nextpkt.tv_sec && ++ now.tv_usec < nextpkt.tv_usec)) { ++ gettimeofday(&now, NULL); ++ } ++#endif ++ nextpkt.tv_usec += pkt_delay; ++ if (nextpkt.tv_usec >= 1000000) { ++ nextpkt.tv_sec += nextpkt.tv_usec / 1000000; ++ nextpkt.tv_usec %= 1000000; ++ } ++ ++ /* If the time for the next packet has already ++ passed (by some margin), then we've lost time ++ Adjust our expected timings accordingly. If ++ we're only a little way behind, don't slip yet */ ++ if (now.tv_usec > (now.tv_usec + (5 * pkt_delay) + ++ 1000000 * (nextpkt.tv_sec - now.tv_sec))) { ++ nextpkt = now; ++ } ++ ++ if (write(sock, &pktbuf, sizeof(pktbuf)) < 0) { ++ perror("write"); ++ writeerrors++; ++ if (writeerrors > 10) { ++ fprintf(stderr, "Too many consecutive write errors\n"); ++ exit(1); ++ } ++ } else ++ writeerrors = 0; ++ ++ ++ ++ } ++ } ++ munmap(image, st.st_size); ++ close(rfd); ++ close(sock); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/summary.h linux-2.6.24.7/drivers/mtd/mtd-utils/summary.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/summary.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/summary.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,178 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2004 Ferenc Havasi , ++ * Zoltan Sogor , ++ * Patrik Kluba , ++ * University of Szeged, Hungary ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ */ ++ ++#ifndef JFFS2_SUMMARY_H ++#define JFFS2_SUMMARY_H ++ ++#include ++#include ++ ++#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \ ++ c->free_size -= _x; c->dirty_size += _x; \ ++ jeb->free_size -= _x ; jeb->dirty_size += _x; \ ++}while(0) ++#define USED_SPACE(x) do { typeof(x) _x = (x); \ ++ c->free_size -= _x; c->used_size += _x; \ ++ jeb->free_size -= _x ; jeb->used_size += _x; \ ++}while(0) ++#define WASTED_SPACE(x) do { typeof(x) _x = (x); \ ++ c->free_size -= _x; c->wasted_size += _x; \ ++ jeb->free_size -= _x ; jeb->wasted_size += _x; \ ++}while(0) ++#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \ ++ c->free_size -= _x; c->unchecked_size += _x; \ ++ jeb->free_size -= _x ; jeb->unchecked_size += _x; \ ++}while(0) ++ ++#define BLK_STATE_ALLFF 0 ++#define BLK_STATE_CLEAN 1 ++#define BLK_STATE_PARTDIRTY 2 ++#define BLK_STATE_CLEANMARKER 3 ++#define BLK_STATE_ALLDIRTY 4 ++#define BLK_STATE_BADBLOCK 5 ++ ++#define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff ++#define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash)) ++#define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x)) ++#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash)) ++#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash)) ++ ++/* Summary structures used on flash */ ++ ++struct jffs2_sum_unknown_flash ++{ ++ jint16_t nodetype; /* node type */ ++} __attribute__((packed)); ++ ++struct jffs2_sum_inode_flash ++{ ++ jint16_t nodetype; /* node type */ ++ jint32_t inode; /* inode number */ ++ jint32_t version; /* inode version */ ++ jint32_t offset; /* offset on jeb */ ++ jint32_t totlen; /* record length */ ++} __attribute__((packed)); ++ ++struct jffs2_sum_dirent_flash ++{ ++ jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ ++ jint32_t totlen; /* record length */ ++ jint32_t offset; /* ofset on jeb */ ++ jint32_t pino; /* parent inode */ ++ jint32_t version; /* dirent version */ ++ jint32_t ino; /* == zero for unlink */ ++ uint8_t nsize; /* dirent name size */ ++ uint8_t type; /* dirent type */ ++ uint8_t name[0]; /* dirent name */ ++} __attribute__((packed)); ++ ++struct jffs2_sum_xattr_flash ++{ ++ jint16_t nodetype; /* == JFFS2_NODETYPE_XATR */ ++ jint32_t xid; /* xattr identifier */ ++ jint32_t version; /* version number */ ++ jint32_t offset; /* offset on jeb */ ++ jint32_t totlen; /* node length */ ++} __attribute__((packed)); ++ ++struct jffs2_sum_xref_flash ++{ ++ jint16_t nodetype; /* == JFFS2_NODETYPE_XREF */ ++ jint32_t offset; /* offset on jeb */ ++} __attribute__((packed)); ++ ++union jffs2_sum_flash ++{ ++ struct jffs2_sum_unknown_flash u; ++ struct jffs2_sum_inode_flash i; ++ struct jffs2_sum_dirent_flash d; ++ struct jffs2_sum_xattr_flash x; ++ struct jffs2_sum_xref_flash r; ++}; ++ ++/* Summary structures used in the memory */ ++ ++struct jffs2_sum_unknown_mem ++{ ++ union jffs2_sum_mem *next; ++ jint16_t nodetype; /* node type */ ++} __attribute__((packed)); ++ ++struct jffs2_sum_inode_mem ++{ ++ union jffs2_sum_mem *next; ++ jint16_t nodetype; /* node type */ ++ jint32_t inode; /* inode number */ ++ jint32_t version; /* inode version */ ++ jint32_t offset; /* offset on jeb */ ++ jint32_t totlen; /* record length */ ++} __attribute__((packed)); ++ ++struct jffs2_sum_dirent_mem ++{ ++ union jffs2_sum_mem *next; ++ jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ ++ jint32_t totlen; /* record length */ ++ jint32_t offset; /* ofset on jeb */ ++ jint32_t pino; /* parent inode */ ++ jint32_t version; /* dirent version */ ++ jint32_t ino; /* == zero for unlink */ ++ uint8_t nsize; /* dirent name size */ ++ uint8_t type; /* dirent type */ ++ uint8_t name[0]; /* dirent name */ ++} __attribute__((packed)); ++ ++struct jffs2_sum_xattr_mem ++{ ++ union jffs2_sum_mem *next; ++ jint16_t nodetype; ++ jint32_t xid; ++ jint32_t version; ++ jint32_t offset; ++ jint32_t totlen; ++} __attribute__((packed)); ++ ++struct jffs2_sum_xref_mem ++{ ++ union jffs2_sum_mem *next; ++ jint16_t nodetype; ++ jint32_t offset; ++} __attribute__((packed)); ++ ++union jffs2_sum_mem ++{ ++ struct jffs2_sum_unknown_mem u; ++ struct jffs2_sum_inode_mem i; ++ struct jffs2_sum_dirent_mem d; ++ struct jffs2_sum_xattr_mem x; ++ struct jffs2_sum_xref_mem r; ++}; ++ ++struct jffs2_summary ++{ ++ uint32_t sum_size; ++ uint32_t sum_num; ++ uint32_t sum_padded; ++ union jffs2_sum_mem *sum_list_head; ++ union jffs2_sum_mem *sum_list_tail; ++}; ++ ++/* Summary marker is stored at the end of every sumarized erase block */ ++ ++struct jffs2_sum_marker ++{ ++ jint32_t offset; /* offset of the summary node in the jeb */ ++ jint32_t magic; /* == JFFS2_SUM_MAGIC */ ++}; ++ ++#define JFFS2_SUMMARY_FRAME_SIZE (sizeof(struct jffs2_raw_summary) + sizeof(struct jffs2_sum_marker)) ++ ++#endif +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/sumtool.c linux-2.6.24.7/drivers/mtd/mtd-utils/sumtool.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/sumtool.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/sumtool.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,951 @@ ++/* ++ * sumtool.c ++ * ++ * Copyright (C) 2004 Zoltan Sogor , ++ * Ferenc Havasi ++ * University of Szeged, Hungary ++ * 2006 KaiGai Kohei ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Overview: ++ * This is a utility insert summary information into JFFS2 image for ++ * faster mount time ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "crc32.h" ++#include "summary.h" ++ ++#define PAD(x) (((x)+3)&~3) ++ ++static const char *const app_name = "sumtool"; ++ ++static struct jffs2_summary *sum_collected = NULL; ++ ++static int verbose = 0; ++static int padto = 0; /* pad the output with 0xFF to the end of the final eraseblock */ ++static int add_cleanmarkers = 1; /* add cleanmarker to output */ ++static int use_input_cleanmarker_size = 1; /* use input file's cleanmarker size (default) */ ++static int found_cleanmarkers = 0; /* cleanmarker found in input file */ ++static struct jffs2_unknown_node cleanmarker; ++static int cleanmarker_size = sizeof(cleanmarker); ++static const char *short_options = "o:i:e:hvVblnc:p"; ++static int erase_block_size = 65536; ++static int out_fd = -1; ++static int in_fd = -1; ++ ++static uint8_t *data_buffer = NULL; /* buffer for inodes */ ++static unsigned int data_ofs = 0; /* inode buffer offset */ ++ ++static uint8_t *file_buffer = NULL; /* file buffer contains the actual erase block*/ ++static unsigned int file_ofs = 0; /* position in the buffer */ ++ ++int target_endian = __BYTE_ORDER; ++ ++static struct option long_options[] = { ++ {"output", 1, NULL, 'o'}, ++ {"input", 1, NULL, 'i'}, ++ {"eraseblock", 1, NULL, 'e'}, ++ {"help", 0, NULL, 'h'}, ++ {"verbose", 0, NULL, 'v'}, ++ {"version", 0, NULL, 'V'}, ++ {"bigendian", 0, NULL, 'b'}, ++ {"littleendian", 0, NULL, 'l'}, ++ {"no-cleanmarkers", 0, NULL, 'n'}, ++ {"cleanmarker", 1, NULL, 'c'}, ++ {"pad", 0, NULL, 'p'}, ++ {NULL, 0, NULL, 0} ++}; ++ ++static char *helptext = ++"Usage: sumtool [OPTIONS] -i inputfile -o outputfile\n\n" ++"Convert the input JFFS2 image to a summarized JFFS2 image\n" ++"Summary makes mounting faster - if summary support enabled in your kernel\n\n" ++"Options:\n" ++" -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)\n" ++" (usually 16KiB on NAND)\n" ++" -c, --cleanmarker=SIZE Size of cleanmarker (default 12).\n" ++" (usually 16 bytes on NAND, and will be set to\n" ++" this value if left at the default 12). Will be\n" ++" stored in OOB after each physical page composing\n" ++" a physical eraseblock.\n" ++" -n, --no-cleanmarkers Don't add a cleanmarker to every eraseblock\n" ++" -o, --output=FILE Output to FILE \n" ++" -i, --input=FILE Input from FILE \n" ++" -b, --bigendian Image is big endian\n" ++" -l --littleendian Image is little endian\n" ++" -h, --help Display this help text\n" ++" -v, --verbose Verbose operation\n" ++" -V, --version Display version information\n" ++" -p, --pad Pad the OUTPUT with 0xFF to the end of the final\n" ++" eraseblock\n\n"; ++ ++ ++static char *revtext = "$Revision: 1.1.1.1 $"; ++ ++static unsigned char ffbuf[16] = { ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ++}; ++ ++static void verror_msg(const char *s, va_list p) ++{ ++ fflush(stdout); ++ fprintf(stderr, "%s: ", app_name); ++ vfprintf(stderr, s, p); ++} ++ ++static void error_msg_and_die(const char *s, ...) ++{ ++ va_list p; ++ ++ va_start(p, s); ++ verror_msg(s, p); ++ va_end(p); ++ putc('\n', stderr); ++ exit(EXIT_FAILURE); ++} ++ ++static void vperror_msg(const char *s, va_list p) ++{ ++ int err = errno; ++ ++ if (s == 0) ++ s = ""; ++ verror_msg(s, p); ++ if (*s) ++ s = ": "; ++ fprintf(stderr, "%s%s\n", s, strerror(err)); ++} ++ ++static void perror_msg_and_die(const char *s, ...) ++{ ++ va_list p; ++ ++ va_start(p, s); ++ vperror_msg(s, p); ++ va_end(p); ++ exit(EXIT_FAILURE); ++} ++ ++ ++ ++static void full_write(void *target_buff, const void *buf, int len); ++ ++void setup_cleanmarker() ++{ ++ cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); ++ cleanmarker.totlen = cpu_to_je32(cleanmarker_size); ++ cleanmarker.hdr_crc = cpu_to_je32(crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); ++} ++ ++void process_options (int argc, char **argv) ++{ ++ int opt,c; ++ ++ while ((opt = getopt_long(argc, argv, short_options, long_options, &c)) >= 0) { ++ switch (opt) { ++ case 'o': ++ if (out_fd != -1) ++ error_msg_and_die("output filename specified more than once"); ++ out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644); ++ if (out_fd == -1) ++ perror_msg_and_die("open output file"); ++ break; ++ ++ case 'i': ++ if (in_fd != -1) ++ error_msg_and_die("input filename specified more than once"); ++ in_fd = open(optarg, O_RDONLY); ++ if (in_fd == -1) ++ perror_msg_and_die("open input file"); ++ break; ++ case 'b': ++ target_endian = __BIG_ENDIAN; ++ break; ++ case 'l': ++ target_endian = __LITTLE_ENDIAN; ++ break; ++ case 'h': ++ case '?': ++ error_msg_and_die(helptext); ++ case 'v': ++ verbose = 1; ++ break; ++ ++ case 'V': ++ error_msg_and_die("revision %.*s\n", ++ (int) strlen(revtext) - 13, revtext + 11); ++ ++ case 'e': { ++ char *next; ++ unsigned units = 0; ++ erase_block_size = strtol(optarg, &next, 0); ++ if (!erase_block_size) ++ error_msg_and_die("Unrecognisable erase size\n"); ++ ++ if (*next) { ++ if (!strcmp(next, "KiB")) { ++ units = 1024; ++ } else if (!strcmp(next, "MiB")) { ++ units = 1024 * 1024; ++ } else { ++ error_msg_and_die("Unknown units in erasesize\n"); ++ } ++ } else { ++ if (erase_block_size < 0x1000) ++ units = 1024; ++ else ++ units = 1; ++ } ++ erase_block_size *= units; ++ ++ /* If it's less than 8KiB, they're not allowed */ ++ if (erase_block_size < 0x2000) { ++ fprintf(stderr, "Erase size 0x%x too small. Increasing to 8KiB minimum\n", ++ erase_block_size); ++ erase_block_size = 0x2000; ++ } ++ break; ++ } ++ ++ case 'n': ++ add_cleanmarkers = 0; ++ break; ++ case 'c': ++ cleanmarker_size = strtol(optarg, NULL, 0); ++ ++ if (cleanmarker_size < sizeof(cleanmarker)) { ++ error_msg_and_die("cleanmarker size must be >= 12"); ++ } ++ if (cleanmarker_size >= erase_block_size) { ++ error_msg_and_die("cleanmarker size must be < eraseblock size"); ++ } ++ ++ use_input_cleanmarker_size = 0; ++ found_cleanmarkers = 1; ++ setup_cleanmarker(); ++ ++ break; ++ case 'p': ++ padto = 1; ++ break; ++ } ++ } ++} ++ ++ ++void init_buffers() ++{ ++ data_buffer = malloc(erase_block_size); ++ ++ if (!data_buffer) { ++ perror("out of memory"); ++ close (in_fd); ++ close (out_fd); ++ exit(1); ++ } ++ ++ file_buffer = malloc(erase_block_size); ++ ++ if (!file_buffer) { ++ perror("out of memory"); ++ close (in_fd); ++ close (out_fd); ++ exit(1); ++ } ++} ++ ++void init_sumlist() ++{ ++ sum_collected = (struct jffs2_summary *) malloc (sizeof(struct jffs2_summary)); ++ ++ if (!sum_collected) ++ error_msg_and_die("Can't allocate memory for jffs2_summary!\n"); ++ ++ memset(sum_collected, 0, sizeof(struct jffs2_summary)); ++} ++ ++void clean_buffers() ++{ ++ if (data_buffer) ++ free(data_buffer); ++ if (file_buffer) ++ free(file_buffer); ++} ++ ++void clean_sumlist() ++{ ++ union jffs2_sum_mem *temp; ++ ++ if (sum_collected) { ++ ++ while (sum_collected->sum_list_head) { ++ temp = sum_collected->sum_list_head; ++ sum_collected->sum_list_head = sum_collected->sum_list_head->u.next; ++ free(temp); ++ sum_collected->sum_num--; ++ } ++ ++ if (sum_collected->sum_num != 0) ++ printf("Ooops, something wrong happened! sum_num != 0, but sum_list = null ???"); ++ ++ free(sum_collected); ++ } ++} ++ ++int load_next_block() ++{ ++ int ret; ++ ret = read(in_fd, file_buffer, erase_block_size); ++ file_ofs = 0; ++ ++ if (verbose) ++ printf("Load next block : %d bytes read\n",ret); ++ ++ return ret; ++} ++ ++void write_buff_to_file() ++{ ++ int ret; ++ int len = data_ofs; ++ ++ uint8_t *buf = NULL; ++ ++ buf = data_buffer; ++ while (len > 0) { ++ ret = write(out_fd, buf, len); ++ ++ if (ret < 0) ++ perror_msg_and_die("write"); ++ ++ if (ret == 0) ++ perror_msg_and_die("write returned zero"); ++ ++ len -= ret; ++ buf += ret; ++ } ++ ++ data_ofs = 0; ++} ++ ++void dump_sum_records() ++{ ++ ++ struct jffs2_raw_summary isum; ++ struct jffs2_sum_marker *sm; ++ union jffs2_sum_mem *temp; ++ jint32_t offset; ++ jint32_t *tpage; ++ void *wpage; ++ int datasize, infosize, padsize; ++ jint32_t magic = cpu_to_je32(JFFS2_SUM_MAGIC); ++ ++ if (!sum_collected->sum_num || !sum_collected->sum_list_head) ++ return; ++ ++ datasize = sum_collected->sum_size + sizeof(struct jffs2_sum_marker); ++ infosize = sizeof(struct jffs2_raw_summary) + datasize; ++ padsize = erase_block_size - data_ofs - infosize; ++ infosize += padsize; datasize += padsize; ++ offset = cpu_to_je32(data_ofs); ++ ++ tpage = (jint32_t *) malloc(datasize); ++ ++ if(!tpage) ++ error_msg_and_die("Can't allocate memory to dump summary information!\n"); ++ ++ memset(tpage, 0xff, datasize); ++ memset(&isum, 0, sizeof(isum)); ++ ++ isum.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ isum.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY); ++ isum.totlen = cpu_to_je32(infosize); ++ isum.hdr_crc = cpu_to_je32(crc32(0, &isum, sizeof(struct jffs2_unknown_node) - 4)); ++ isum.padded = cpu_to_je32(0); ++ ++ if (add_cleanmarkers && found_cleanmarkers) { ++ isum.cln_mkr = cpu_to_je32(cleanmarker_size); ++ } else { ++ isum.cln_mkr = cpu_to_je32(0); ++ } ++ ++ isum.sum_num = cpu_to_je32(sum_collected->sum_num); ++ wpage = tpage; ++ ++ while (sum_collected->sum_num) { ++ switch(je16_to_cpu(sum_collected->sum_list_head->u.nodetype)) { ++ ++ case JFFS2_NODETYPE_INODE : { ++ struct jffs2_sum_inode_flash *sino_ptr = wpage; ++ ++ sino_ptr->nodetype = sum_collected->sum_list_head->i.nodetype; ++ sino_ptr->inode = sum_collected->sum_list_head->i.inode; ++ sino_ptr->version = sum_collected->sum_list_head->i.version; ++ sino_ptr->offset = sum_collected->sum_list_head->i.offset; ++ sino_ptr->totlen = sum_collected->sum_list_head->i.totlen; ++ ++ wpage += JFFS2_SUMMARY_INODE_SIZE; ++ break; ++ } ++ ++ case JFFS2_NODETYPE_DIRENT : { ++ struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage; ++ ++ sdrnt_ptr->nodetype = sum_collected->sum_list_head->d.nodetype; ++ sdrnt_ptr->totlen = sum_collected->sum_list_head->d.totlen; ++ sdrnt_ptr->offset = sum_collected->sum_list_head->d.offset; ++ sdrnt_ptr->pino = sum_collected->sum_list_head->d.pino; ++ sdrnt_ptr->version = sum_collected->sum_list_head->d.version; ++ sdrnt_ptr->ino = sum_collected->sum_list_head->d.ino; ++ sdrnt_ptr->nsize = sum_collected->sum_list_head->d.nsize; ++ sdrnt_ptr->type = sum_collected->sum_list_head->d.type; ++ ++ memcpy(sdrnt_ptr->name, sum_collected->sum_list_head->d.name, ++ sum_collected->sum_list_head->d.nsize); ++ ++ wpage += JFFS2_SUMMARY_DIRENT_SIZE(sum_collected->sum_list_head->d.nsize); ++ break; ++ } ++ ++ case JFFS2_NODETYPE_XATTR: { ++ struct jffs2_sum_xattr_flash *sxattr_ptr = wpage; ++ ++ sxattr_ptr->nodetype = sum_collected->sum_list_head->x.nodetype; ++ sxattr_ptr->xid = sum_collected->sum_list_head->x.xid; ++ sxattr_ptr->version = sum_collected->sum_list_head->x.version; ++ sxattr_ptr->offset = sum_collected->sum_list_head->x.offset; ++ sxattr_ptr->totlen = sum_collected->sum_list_head->x.totlen; ++ ++ wpage += JFFS2_SUMMARY_XATTR_SIZE; ++ break; ++ } ++ ++ case JFFS2_NODETYPE_XREF: { ++ struct jffs2_sum_xref_flash *sxref_ptr = wpage; ++ ++ sxref_ptr->nodetype = sum_collected->sum_list_head->r.nodetype; ++ sxref_ptr->offset = sum_collected->sum_list_head->r.offset; ++ ++ wpage += JFFS2_SUMMARY_XREF_SIZE; ++ break; ++ } ++ ++ default : { ++ printf("Unknown node type!\n"); ++ } ++ } ++ ++ temp = sum_collected->sum_list_head; ++ sum_collected->sum_list_head = sum_collected->sum_list_head->u.next; ++ free(temp); ++ ++ sum_collected->sum_num--; ++ } ++ ++ sum_collected->sum_size = 0; ++ sum_collected->sum_num = 0; ++ sum_collected->sum_list_tail = NULL; ++ ++ wpage += padsize; ++ ++ sm = wpage; ++ sm->offset = offset; ++ sm->magic = magic; ++ ++ isum.sum_crc = cpu_to_je32(crc32(0, tpage, datasize)); ++ isum.node_crc = cpu_to_je32(crc32(0, &isum, sizeof(isum) - 8)); ++ ++ full_write(data_buffer + data_ofs, &isum, sizeof(isum)); ++ full_write(data_buffer + data_ofs, tpage, datasize); ++ ++ free(tpage); ++} ++ ++static void full_write(void *target_buff, const void *buf, int len) ++{ ++ memcpy(target_buff, buf, len); ++ data_ofs += len; ++} ++ ++static void pad(int req) ++{ ++ while (req) { ++ if (req > sizeof(ffbuf)) { ++ full_write(data_buffer + data_ofs, ffbuf, sizeof(ffbuf)); ++ req -= sizeof(ffbuf); ++ } else { ++ full_write(data_buffer + data_ofs, ffbuf, req); ++ req = 0; ++ } ++ } ++} ++ ++static inline void padword() ++{ ++ if (data_ofs % 4) ++ full_write(data_buffer + data_ofs, ffbuf, 4 - (data_ofs % 4)); ++} ++ ++ ++static inline void pad_block_if_less_than(int req,int plus) ++{ ++ ++ int datasize = req + plus + sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8; ++ datasize += (4 - (datasize % 4)) % 4; ++ ++ if (data_ofs + req > erase_block_size - datasize) { ++ dump_sum_records(); ++ write_buff_to_file(); ++ } ++ ++ if (add_cleanmarkers && found_cleanmarkers) { ++ if (!data_ofs) { ++ full_write(data_buffer, &cleanmarker, sizeof(cleanmarker)); ++ pad(cleanmarker_size - sizeof(cleanmarker)); ++ padword(); ++ } ++ } ++} ++ ++void flush_buffers() ++{ ++ ++ if ((add_cleanmarkers == 1) && (found_cleanmarkers == 1)) { /* CLEANMARKER */ ++ if (data_ofs != cleanmarker_size) { /* INODE BUFFER */ ++ ++ int datasize = sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8; ++ datasize += (4 - (datasize % 4)) % 4; ++ ++ /* If we have a full inode buffer, then write out inode and summary data */ ++ if (data_ofs + sizeof(struct jffs2_raw_inode) + 2*JFFS2_MIN_DATA_LEN > erase_block_size - datasize) { ++ dump_sum_records(); ++ write_buff_to_file(); ++ } else { /* else just write out inode data */ ++ if (padto) ++ pad(erase_block_size - data_ofs); ++ write_buff_to_file(); ++ } ++ } ++ } else { /* NO CLEANMARKER */ ++ if (data_ofs != 0) { /* INODE BUFFER */ ++ ++ int datasize = sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8; ++ datasize += (4 - (datasize % 4)) % 4; ++ ++ /* If we have a full inode buffer, then write out inode and summary data */ ++ if (data_ofs + sizeof(struct jffs2_raw_inode) + 2*JFFS2_MIN_DATA_LEN > erase_block_size - datasize) { ++ dump_sum_records(); ++ write_buff_to_file(); ++ } else { /* Else just write out inode data */ ++ if(padto) ++ pad(erase_block_size - data_ofs); ++ write_buff_to_file(); ++ } ++ } ++ } ++} ++ ++int add_sum_mem(union jffs2_sum_mem *item) ++{ ++ ++ if (!sum_collected->sum_list_head) ++ sum_collected->sum_list_head = (union jffs2_sum_mem *) item; ++ if (sum_collected->sum_list_tail) ++ sum_collected->sum_list_tail->u.next = (union jffs2_sum_mem *) item; ++ sum_collected->sum_list_tail = (union jffs2_sum_mem *) item; ++ ++ switch (je16_to_cpu(item->u.nodetype)) { ++ case JFFS2_NODETYPE_INODE: ++ sum_collected->sum_size += JFFS2_SUMMARY_INODE_SIZE; ++ sum_collected->sum_num++; ++ break; ++ ++ case JFFS2_NODETYPE_DIRENT: ++ sum_collected->sum_size += JFFS2_SUMMARY_DIRENT_SIZE(item->d.nsize); ++ sum_collected->sum_num++; ++ break; ++ ++ case JFFS2_NODETYPE_XATTR: ++ sum_collected->sum_size += JFFS2_SUMMARY_XATTR_SIZE; ++ sum_collected->sum_num++; ++ break; ++ ++ case JFFS2_NODETYPE_XREF: ++ sum_collected->sum_size += JFFS2_SUMMARY_XREF_SIZE; ++ sum_collected->sum_num++; ++ break; ++ ++ default: ++ error_msg_and_die("__jffs2_add_sum_mem(): UNKNOWN node type %d\n", je16_to_cpu(item->u.nodetype)); ++ } ++ return 0; ++} ++ ++void add_sum_inode_mem(union jffs2_node_union *node) ++{ ++ struct jffs2_sum_inode_mem *temp = (struct jffs2_sum_inode_mem *) malloc(sizeof(struct jffs2_sum_inode_mem)); ++ ++ if (!temp) ++ error_msg_and_die("Can't allocate memory for summary information!\n"); ++ ++ temp->nodetype = node->i.nodetype; ++ temp->inode = node->i.ino; ++ temp->version = node->i.version; ++ temp->offset = cpu_to_je32(data_ofs); ++ temp->totlen = node->i.totlen; ++ temp->next = NULL; ++ ++ add_sum_mem((union jffs2_sum_mem *) temp); ++} ++ ++void add_sum_dirent_mem(union jffs2_node_union *node) ++{ ++ struct jffs2_sum_dirent_mem *temp = (struct jffs2_sum_dirent_mem *) ++ malloc(sizeof(struct jffs2_sum_dirent_mem) + node->d.nsize); ++ ++ if (!temp) ++ error_msg_and_die("Can't allocate memory for summary information!\n"); ++ ++ temp->nodetype = node->d.nodetype; ++ temp->totlen = node->d.totlen; ++ temp->offset = cpu_to_je32(data_ofs); ++ temp->pino = node->d.pino; ++ temp->version = node->d.version; ++ temp->ino = node->d.ino; ++ temp->nsize = node->d.nsize; ++ temp->type = node->d.type; ++ temp->next = NULL; ++ ++ memcpy(temp->name,node->d.name,node->d.nsize); ++ add_sum_mem((union jffs2_sum_mem *) temp); ++} ++ ++void add_sum_xattr_mem(union jffs2_node_union *node) ++{ ++ struct jffs2_sum_xattr_mem *temp = (struct jffs2_sum_xattr_mem *) ++ malloc(sizeof(struct jffs2_sum_xattr_mem)); ++ if (!temp) ++ error_msg_and_die("Can't allocate memory for summary information!\n"); ++ ++ temp->nodetype = node->x.nodetype; ++ temp->xid = node->x.xid; ++ temp->version = node->x.version; ++ temp->offset = cpu_to_je32(data_ofs); ++ temp->totlen = node->x.totlen; ++ temp->next = NULL; ++ ++ add_sum_mem((union jffs2_sum_mem *) temp); ++} ++ ++void add_sum_xref_mem(union jffs2_node_union *node) ++{ ++ struct jffs2_sum_xref_mem *temp = (struct jffs2_sum_xref_mem *) ++ malloc(sizeof(struct jffs2_sum_xref_mem)); ++ if (!temp) ++ error_msg_and_die("Can't allocate memory for summary information!\n"); ++ ++ temp->nodetype = node->r.nodetype; ++ temp->offset = cpu_to_je32(data_ofs); ++ temp->next = NULL; ++ ++ add_sum_mem((union jffs2_sum_mem *) temp); ++} ++ ++void write_dirent_to_buff(union jffs2_node_union *node) ++{ ++ pad_block_if_less_than(je32_to_cpu (node->d.totlen),JFFS2_SUMMARY_DIRENT_SIZE(node->d.nsize)); ++ add_sum_dirent_mem(node); ++ full_write(data_buffer + data_ofs, &(node->d), je32_to_cpu (node->d.totlen)); ++ padword(); ++} ++ ++ ++void write_inode_to_buff(union jffs2_node_union *node) ++{ ++ pad_block_if_less_than(je32_to_cpu (node->i.totlen),JFFS2_SUMMARY_INODE_SIZE); ++ add_sum_inode_mem(node); /* Add inode summary mem to summary list */ ++ full_write(data_buffer + data_ofs, &(node->i), je32_to_cpu (node->i.totlen)); /* Write out the inode to inode_buffer */ ++ padword(); ++} ++ ++void write_xattr_to_buff(union jffs2_node_union *node) ++{ ++ pad_block_if_less_than(je32_to_cpu(node->x.totlen), JFFS2_SUMMARY_XATTR_SIZE); ++ add_sum_xattr_mem(node); /* Add xdatum summary mem to summary list */ ++ full_write(data_buffer + data_ofs, &(node->x), je32_to_cpu(node->x.totlen)); ++ padword(); ++} ++ ++void write_xref_to_buff(union jffs2_node_union *node) ++{ ++ pad_block_if_less_than(je32_to_cpu(node->r.totlen), JFFS2_SUMMARY_XREF_SIZE); ++ add_sum_xref_mem(node); /* Add xref summary mem to summary list */ ++ full_write(data_buffer + data_ofs, &(node->r), je32_to_cpu(node->r.totlen)); ++ padword(); ++} ++ ++void create_summed_image(int inp_size) ++{ ++ uint8_t *p = file_buffer; ++ union jffs2_node_union *node; ++ uint32_t crc, length; ++ uint16_t type; ++ int bitchbitmask = 0; ++ int obsolete; ++ char name[256]; ++ ++ while ( p < (file_buffer + inp_size)) { ++ ++ node = (union jffs2_node_union *) p; ++ ++ /* Skip empty space */ ++ if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { ++ p += 4; ++ continue; ++ } ++ ++ if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { ++ if (!bitchbitmask++) ++ printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - file_buffer, je16_to_cpu (node->u.magic)); ++ p += 4; ++ continue; ++ } ++ ++ bitchbitmask = 0; ++ ++ type = je16_to_cpu(node->u.nodetype); ++ if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { ++ obsolete = 1; ++ type |= JFFS2_NODE_ACCURATE; ++ } else { ++ obsolete = 0; ++ } ++ ++ node->u.nodetype = cpu_to_je16(type); ++ ++ crc = crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); ++ if (crc != je32_to_cpu (node->u.hdr_crc)) { ++ printf ("Wrong hdr_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->u.hdr_crc), crc); ++ p += 4; ++ continue; ++ } ++ ++ switch(je16_to_cpu(node->u.nodetype)) { ++ case JFFS2_NODETYPE_INODE: ++ if (verbose) ++ printf ("%8s Inode node at 0x%08x, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", ++ obsolete ? "Obsolete" : "", ++ p - file_buffer, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino), ++ je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize), ++ je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset)); ++ ++ crc = crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); ++ if (crc != je32_to_cpu (node->i.node_crc)) { ++ printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->i.node_crc), crc); ++ p += PAD(je32_to_cpu (node->i.totlen)); ++ continue; ++ } ++ ++ crc = crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); ++ if (crc != je32_to_cpu(node->i.data_crc)) { ++ printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->i.data_crc), crc); ++ p += PAD(je32_to_cpu (node->i.totlen)); ++ continue; ++ } ++ ++ write_inode_to_buff(node); ++ ++ p += PAD(je32_to_cpu (node->i.totlen)); ++ break; ++ ++ case JFFS2_NODETYPE_DIRENT: ++ memcpy (name, node->d.name, node->d.nsize); ++ name [node->d.nsize] = 0x0; ++ ++ if (verbose) ++ printf ("%8s Dirent node at 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n", ++ obsolete ? "Obsolete" : "", ++ p - file_buffer, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), ++ je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), ++ node->d.nsize, name); ++ ++ crc = crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); ++ if (crc != je32_to_cpu (node->d.node_crc)) { ++ printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->d.node_crc), crc); ++ p += PAD(je32_to_cpu (node->d.totlen)); ++ continue; ++ } ++ ++ crc = crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); ++ if (crc != je32_to_cpu(node->d.name_crc)) { ++ printf ("Wrong name_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - file_buffer, je32_to_cpu (node->d.name_crc), crc); ++ p += PAD(je32_to_cpu (node->d.totlen)); ++ continue; ++ } ++ ++ write_dirent_to_buff(node); ++ ++ p += PAD(je32_to_cpu (node->d.totlen)); ++ break; ++ ++ case JFFS2_NODETYPE_XATTR: ++ if (je32_to_cpu(node->x.node_crc) == 0xffffffff) ++ obsolete = 1; ++ if (verbose) ++ printf("%8s Xdatum node at 0x%08x, totlen 0x%08x, " ++ "#xid %5u, version %5u\n", ++ obsolete ? "Obsolete" : "", ++ p - file_buffer, je32_to_cpu (node->x.totlen), ++ je32_to_cpu(node->x.xid), je32_to_cpu(node->x.version)); ++ crc = crc32(0, node, sizeof (struct jffs2_raw_xattr) - 4); ++ if (crc != je32_to_cpu(node->x.node_crc)) { ++ printf("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", ++ p - file_buffer, je32_to_cpu(node->x.node_crc), crc); ++ p += PAD(je32_to_cpu (node->x.totlen)); ++ continue; ++ } ++ length = node->x.name_len + 1 + je16_to_cpu(node->x.value_len); ++ crc = crc32(0, node->x.data, length); ++ if (crc != je32_to_cpu(node->x.data_crc)) { ++ printf("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", ++ p - file_buffer, je32_to_cpu(node->x.data_crc), crc); ++ p += PAD(je32_to_cpu (node->x.totlen)); ++ continue; ++ } ++ ++ write_xattr_to_buff(node); ++ p += PAD(je32_to_cpu (node->x.totlen)); ++ break; ++ ++ case JFFS2_NODETYPE_XREF: ++ if (je32_to_cpu(node->r.node_crc) == 0xffffffff) ++ obsolete = 1; ++ if (verbose) ++ printf("%8s Xref node at 0x%08x, totlen 0x%08x, " ++ "#ino %5u, xid %5u\n", ++ obsolete ? "Obsolete" : "", ++ p - file_buffer, je32_to_cpu(node->r.totlen), ++ je32_to_cpu(node->r.ino), je32_to_cpu(node->r.xid)); ++ crc = crc32(0, node, sizeof (struct jffs2_raw_xref) - 4); ++ if (crc != je32_to_cpu(node->r.node_crc)) { ++ printf("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", ++ p - file_buffer, je32_to_cpu(node->r.node_crc), crc); ++ p += PAD(je32_to_cpu (node->r.totlen)); ++ continue; ++ } ++ ++ write_xref_to_buff(node); ++ p += PAD(je32_to_cpu (node->r.totlen)); ++ break; ++ ++ case JFFS2_NODETYPE_CLEANMARKER: ++ if (verbose) { ++ printf ("%8s Cleanmarker at 0x%08x, totlen 0x%08x\n", ++ obsolete ? "Obsolete" : "", ++ p - file_buffer, je32_to_cpu (node->u.totlen)); ++ } ++ ++ if (!found_cleanmarkers) { ++ found_cleanmarkers = 1; ++ ++ if (add_cleanmarkers == 1 && use_input_cleanmarker_size == 1){ ++ cleanmarker_size = je32_to_cpu (node->u.totlen); ++ setup_cleanmarker(); ++ } ++ } ++ ++ p += PAD(je32_to_cpu (node->u.totlen)); ++ break; ++ ++ case JFFS2_NODETYPE_PADDING: ++ if (verbose) { ++ printf ("%8s Padding node at 0x%08x, totlen 0x%08x\n", ++ obsolete ? "Obsolete" : "", ++ p - file_buffer, je32_to_cpu (node->u.totlen)); ++ } ++ p += PAD(je32_to_cpu (node->u.totlen)); ++ break; ++ ++ case 0xffff: ++ p += 4; ++ break; ++ ++ default: ++ if (verbose) { ++ printf ("%8s Unknown node at 0x%08x, totlen 0x%08x\n", ++ obsolete ? "Obsolete" : "", ++ p - file_buffer, je32_to_cpu (node->u.totlen)); ++ } ++ ++ p += PAD(je32_to_cpu (node->u.totlen)); ++ } ++ } ++} ++ ++int main(int argc, char **argv) ++{ ++ int ret; ++ ++ process_options(argc,argv); ++ ++ if ((in_fd == -1) || (out_fd == -1)) { ++ if(in_fd != -1) ++ close(in_fd); ++ if(out_fd != -1) ++ close(out_fd); ++ fprintf(stderr,helptext); ++ error_msg_and_die("You must specify input and output files!\n"); ++ } ++ ++ init_buffers(); ++ init_sumlist(); ++ ++ while ((ret = load_next_block())) { ++ create_summed_image(ret); ++ } ++ ++ flush_buffers(); ++ clean_buffers(); ++ clean_sumlist(); ++ ++ if (in_fd != -1) ++ close(in_fd); ++ if (out_fd != -1) ++ close(out_fd); ++ ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/Makefile linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/Makefile +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,14 @@ ++ ++all: checkfs makefiles ++ ++checkfs: checkfs.c Makefile common.h comm.o ++ gcc -g -Wall checkfs.c comm.o -o checkfs ++ ++comm.o: comm.c Makefile ++ gcc -g -Wall -c comm.c -o comm.o ++ ++makefiles: makefiles.c Makefile common.h ++ gcc -g -Wall makefiles.c -o makefiles ++ ++clean: ++ rm -f makefiles checkfs *~ *.o +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/README linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/README +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/README 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/README 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,173 @@ ++$Id: README,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++$Log: not supported by cvs2svn $ ++Revision 1.2 2001/06/21 23:07:06 dwmw2 ++Initial import to MTD CVS ++ ++Revision 1.1 2001/06/11 19:34:40 vipin ++Added README file to dir. ++ ++ ++This is the README file for the "checkfs" power fail test program. ++By: Vipin Malik ++ ++NOTE: This program requires an external "power cycling box" ++connected to one of the com ports of the system under test. ++This power cycling box should wait for a random amount of time ++after it receives a "ok to power me down" message over the ++serial port, and then yank power to the system under test. ++(The box that I rigged up tested with waits anywhere from ++0 to ~40 seconds). ++ ++ ++It should then restore power after a few seconds and wait for the ++message again. ++ ++ ++ABOUT: ++ ++This program's primary purpose it to test the reliiability ++of various file systems under Linux. ++ ++SETUP: ++ ++You need to setup the file system you want to test and run the ++"makefiles" program ONCE. This creates a set of files that are ++required by the "checkfs" program. ++ ++Also copy the "checkfs" executable program to the same dir. ++ ++Then you need to make sure that the program "checkfs" is called ++automatically on startup. You can customise the operation of ++the "checkfs" program by passing it various cmd line arguments. ++run "checkfs -?" for more details. ++ ++****NOTE******* ++Make sure that you call the checkfs program only after you have ++mounted the file system you want to test (this is obvious), but ++also after you have run any "scan" utilities to check for and ++fix any file systems errors. The e2fsck is one utility for the ++ext2 file system. For an automated setup you of course need to ++provide these scan programs to run in standalone mode (-f -y ++flags for e2fsck for example). ++ ++File systems like JFFS and JFFS2 do not have any such external ++utilities and you may call "checkfs" right after you have mounted ++the respective file system under test. ++ ++There are two ways you can mount the file system under test: ++ ++1. Mount your root fs on a "standard" fs like ext2 and then ++mount the file system under test (which may be ext2 on another ++partition or device) and then run "checkfs" on this mounted ++partition OR ++ ++2. Make your fs AND device that you have put this fs as your ++root fs and run "checkfs" on the root device (i.e. "/"). ++You can of course still run checkfs under a separate dir ++under your "/" root dir. ++ ++I have found the second method to be a particularly stringent ++arrangement (and thus preferred when you are trying to break ++something). ++ ++Using this arrangement I was able to find that JFFS clobbered ++some "sister" files on the root fs even though "checkfs" would ++run fine through all its own check files. ++ ++(I found this out when one of the clobbered sister file happened ++to be /bin/bash. The system refused to run rc.local thus ++preventing my "checkfs" program from being launched :) ++ ++"checkfs": ++ ++The "formatting" reliability of the fs as well as the file data integrity ++of files on the fs can be checked using this program. ++ ++"formatiing" reliability can only be checked via an indirect method. ++If there is severe formatting reliability issues with the file system, ++it will most likely cause other system failures that will prevent this ++program from running successfully on a power up. This will prevent ++a "ok to power me down" message from going out to the power cycling ++black box and prevent power being turned off again. ++ ++File data reliability is checked more directly. A fixed number of ++files are created in the current dir (using the program "makefiles"). ++ ++Each file has a random number of bytes in it (set by using the ++-s cmd line flag). The number of "ints" in the file is stored as the ++first "int" in it (note: 0 length files are not allowed). Each file ++is then filled with random data and a 16 bit CRC appended at the end. ++ ++When "checkfs" is run, it runs through all files (with predetermined ++file names)- one at a time- and checks for the number of "int's" ++in it as well as the ending CRC. ++ ++The program exits if the numbers of files that are corrupt are greater ++that a user specified parameter (set by using the -e cmd line flag). ++ ++If the number of corrupt files is less than this parameter, the corrupt ++files are repaired and operation resumes as explained below. ++ ++The idea behind allowing a user specified amount of corrupt files is as ++follows: ++ ++If you are testing for "formatting" reliability of a fs, and for ++the data reliability of "other" files present of the fs, use -e 1. ++"other" files are defined as sister files on the fs, not being written to ++by the "checkfs" test program. ++ ++As mentioned, in this case you would set -e 1, or allow at most 1 file ++to be corrupt each time after a power fail. This would be the file ++that was probably being written to when power failed (and CRC was not ++updated to reflect the new data being written). You would check file ++systems like ext2 etc. with such a configuration. ++(As you have no hope that these file systems provide for either your ++new data or old data to be present in the file if power failed during ++the write. This is called "roll back and recover".) ++ ++With JFFS2 I tested for such "roll back and recover" file data reliability ++by setting -e 0 and making sure that all writes to the file being ++updated are done in a *single* write(). ++ ++This is how I found that JFFS2 (yet) does NOT support this functionality. ++(There was a great debate if this was a bug or a feature that was lacking ++or even an issue at all. See the mtd archives for more details). ++ ++In other words, JFFS2 will partially update a file on FLASH even before ++the write() command has completed, thus leaving part old data part new ++data in your file if power failed in the middle of a write(). ++ ++This is bad functionality if you are updating a binary structure or a ++CRC protected file (as in our case). ++ ++ ++If All Files Check Out OK: ++ ++On the startup scan, if there are less errors than specified by the "-e flag" ++a "ok to power me down message" is sent via the specified com port. ++ ++The actual format of this message will depend on the format expected ++by the power cycling box that will receive this message. One may customise ++the actual message that goes out in the "do_pwr_dn)" routine in "comm.c". ++ ++This file is called with an open file descriptor to the comm port that ++this message needs to go out over and the count of the current power ++cycle (in case your power cycling box can display/log this count). ++ ++After this message has been sent out, the checkfs program goes into ++a while(1) loop of writing new data (with CRC), one at a time, into ++all the "check files" in the dir. ++ ++Its life comes to a sudden end when power is asynchronously pulled from ++under its feet (by your external power cycling box). ++ ++It comes back to life when power is restored and the system boots and ++checkfs is called from the rc.local script file. ++ ++The cycle then repeats till a problem is detected, at which point ++the "ok to power me down" message is not sent and the cycle stops ++waiting for the user to examine the system. ++ ++ ++ ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/checkfs.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/checkfs.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/checkfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/checkfs.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,695 @@ ++/* ++ ++ * Copyright Daniel Industries. ++ * ++ * Created by: Vipin Malik (vipin.malik@daniel.com) ++ * ++ * This code is released under the GPL version 2. See the file COPYING ++ * for more details. ++ * ++ * Software distributed under the Licence is distributed on an "AS IS" ++ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. ++ * See the Licence for the specific language governing rights and ++ * limitations under the Licence. ++ ++ This program opens files in progression (file00001, file00002 etc), ++ upto MAX_NUM_FILES and checks their CRC. If a file is not found or the ++ CRC does not match it stops it's operation. ++ ++ Everything is logged in a logfile called './logfile'. ++ ++ If everything is ok this program sends a signal, via com1, to the remote ++ power control box to power cycle this computer. ++ ++ This program then proceeds to create new files file0....file ++ in a endless loop and checksum each before closing them. ++ ++ STRUCTURE OF THE FILES: ++ The fist int is the size of the file in bytes. ++ The last 2 bytes are the CRC for the entire file. ++ There is random data in between. ++ ++ The files are opened in the current dir. ++ ++ $Id: checkfs.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ $Log: not supported by cvs2svn $ ++ Revision 1.8 2005/11/07 11:15:17 gleixner ++ [MTD / JFFS2] Clean up trailing white spaces ++ ++ Revision 1.7 2001/06/21 23:04:17 dwmw2 ++ Initial import to MTD CVS ++ ++ Revision 1.6 2001/06/08 22:26:05 vipin ++ Split the modbus comm part of the program (that sends the ok to pwr me down ++ message) into another file "comm.c" ++ ++ Revision 1.5 2001/06/08 21:29:56 vipin ++ fixed small issue with write() checking for < 0 instead of < (bytes to be written). ++ Now it does the latter (as it should). ++ ++ Revision 1.4 2001/05/11 22:29:40 vipin ++ Added a test to check and err out if the first int in file (which tells us ++ how many bytes there are in the file) is zero. This will prevent a corrupt ++ file with zero's in it from passing the crc test. ++ ++ Revision 1.3 2001/05/11 21:33:54 vipin ++ Changed to use write() rather than fwrite() when creating new file. Additionally, ++ and more important, it now does a single write() for the entire data. This will ++ enable us to use this program to test for power fail *data* reliability when ++ writing over an existing file, specially on powr fail "safe" file systems as ++ jffs/jffs2. Also added a new cmdline parameter "-e" that specifies the max # of ++ errors that can be tolerated. This should be set to ZERO to test for the above, ++ as old data should be reliabily maintained if the newer write never "took" before ++ power failed. If the write did succeed, then the newer data will have its own ++ CRC in place when it gets checked => hence no error. In theory at least! ++ ++ ++ Revision 1.2 2001/05/11 19:27:33 vipin ++ Added cmd line args to change serial port, and specify max size of ++ random files created. Some cleanup. Added -Wall to Makefile. ++ ++ Revision 1.1 2001/05/11 16:06:28 vipin ++ Importing checkfs (the power fail test program) into CVS. This was ++ originally done for NEWS. NEWS had a lot of version, this is ++ based off the last one done for NEWS. The "makefiles" program ++ is run once initially to create the files in the current dir. ++ "checkfs" is then run on every powerup to check consistancy ++ of the files. See checkfs.c for more details. ++ ++ ++*/ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "common.h" ++ ++ ++ ++extern int do_pwr_dn(int fd, int cycleCnt); ++ ++#define CMDLINE_PORT "-p" ++#define CMDLINE_MAXFILEBYTES "-s" ++#define CMDLINE_MAXERROR "-e" ++#define CMDLINE_HELPSHORT "-?" ++#define CMDLINE_HELPLONG "--help" ++ ++ ++int CycleCount; ++ ++char SerialDevice[255] = "/dev/ttyS0"; /* default, can be changed ++ through cmd line. */ ++ ++#define MAX_INTS_ALLOW 100000 /* max # of int's in the file written. ++ Statis limit to size struct. */ ++float FileSizeMax = 1024.0; /*= (file size in bytes), MUST be float*/ ++ ++int MaxErrAllowed = 1; /* default, can ge changed thru cmd line*/ ++ ++ ++/* Needed for CRC generation/checking */ ++static const unsigned short crc_ccitt_table[] = { ++ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, ++ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, ++ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, ++ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, ++ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, ++ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, ++ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, ++ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, ++ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, ++ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, ++ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, ++ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, ++ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, ++ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, ++ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, ++ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, ++ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, ++ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, ++ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, ++ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, ++ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, ++ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, ++ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, ++ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, ++ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, ++ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, ++ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, ++ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, ++ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, ++ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, ++ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, ++ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 ++}; ++ ++ ++/* ++ Set's up the Linux serial port. Must be passed string to device to ++ open. Parameters are fixed to 9600,e,1 ++ ++ [A possible enhancement to this program would be to pass these ++ parameters via the command line.] ++ ++ Returns file descriptor to open port. Use this fd to write to port ++ and close it later, when done. ++*/ ++int setupSerial (const char *dev) { ++ int i, fd; ++ struct termios tios; ++ ++ fd = open(dev,O_RDWR | O_NDELAY ); ++ if (fd < 0) { ++ fprintf(stderr, "%s: %s\n", dev, sys_errlist[errno]); ++ exit(1); ++ } ++ if (tcgetattr(fd, &tios) < 0) { ++ fprintf(stderr,"Could not get terminal attributes: %s",sys_errlist[errno]); ++ exit(1); ++ } ++ ++ tios.c_cflag = ++ CS7 | ++ CREAD | // Enable Receiver ++ HUPCL | // Hangup after close ++ CLOCAL | // Ignore modem control lines ++ PARENB; // Enable parity (even by default) ++ ++ ++ ++ tios.c_iflag = IGNBRK; // Ignore break ++ tios.c_oflag = 0; ++ tios.c_lflag = 0; ++ for(i = 0; i < NCCS; i++) { ++ tios.c_cc[i] = '\0'; // no special characters ++ } ++ tios.c_cc[VMIN] = 1; ++ tios.c_cc[VTIME] = 0; ++ ++ cfsetospeed (&tios, B9600); ++ cfsetispeed (&tios, B9600); ++ ++ if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) { ++ fprintf(stderr,"Could not set attributes: ,%s",sys_errlist[errno]); ++ exit(1); ++ } ++ return fd; ++} ++ ++ ++ ++ ++ ++//A portion of this code was taken from the AX.25 HDLC packet driver ++//in LINUX. Once I test and have a better understanding of what ++//it is doing, it will be better commented. ++ ++//For now one can speculate that the CRC routine always expects the ++//CRC to calculate out to 0xf0b8 (the hardcoded value at the end) ++//and returns TRUE if it is and FALSE if it doesn't. ++//Why don't people document better!!!! ++int check_crc_ccitt(char *filename) ++{ ++ FILE *fp; ++ FILE *logfp; ++ unsigned short crc = 0xffff; ++ int len; ++ char dataByte; ++ int retry; ++ char done; ++ ++ fp = fopen(filename,"rb"); ++ if(!fp){ ++ logfp = fopen("logfile","a"); /*open for appending only.*/ ++ fprintf(logfp, "Verify checksum:Error! Cannot open filename passed for verify checksum: %s\n",filename); ++ fclose(logfp); ++ return FALSE; ++ } ++ ++ ++ /*the first int contains an int that is the length of the file in long.*/ ++ if(fread(&len, sizeof(int), 1, fp) != 1){ ++ logfp = fopen("logfile","a"); /*open for appending only.*/ ++ fprintf(logfp, "verify checksum:Error reading from file: %s\n", filename); ++ fclose(fp); ++ fclose(logfp); ++ return FALSE; ++ } ++ ++ /* printf("Checking %i bytes for CRC in \"%s\".\n", len, filename); */ ++ ++ /* Make sure that we did not read 0 as the number of bytes in file. This ++ check prevents a corrupt file with zero's in it from passing the ++ CRC test. A good file will always have some data in it. */ ++ if(len == 0) ++ { ++ ++ logfp = fopen("logfile","a"); /*open for appending only.*/ ++ fprintf(logfp, "verify checksum: first int claims there are 0 data in file. Error!: %s\n", filename); ++ fclose(fp); ++ fclose(logfp); ++ return FALSE; ++ } ++ ++ ++ rewind(fp); ++ len+=2; /*the file has two extra bytes at the end, it's checksum. Those ++ two MUST also be included in the checksum calculation. ++ */ ++ ++ for (;len>0;len--){ ++ retry=5; /*retry 5 times*/ ++ done = FALSE; ++ while(!done){ ++ if(fread(&dataByte, sizeof(char), 1, fp) != 1){ ++ retry--; ++ }else{ ++ done = TRUE; ++ } ++ if(retry == 0){ ++ done = TRUE; ++ } ++ } ++ if(!retry){ ++ logfp = fopen("logfile","a"); /*open for appending only.*/ ++ fprintf(logfp, "Unexpected end of file: %s\n", filename); ++ fprintf(logfp, "...bytes left to be read %i.\n",len); ++ fclose(logfp); ++ fclose(fp); ++ return FALSE; ++ } ++ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ dataByte) & 0xff]; ++ } ++ fclose(fp); ++ if( (crc & 0xffff) != 0xf0b8){ ++ /*printf("The CRC of read file:%x\n", crc); */ ++ return FALSE; ++ } ++ return TRUE; ++}/*end check_crc_ccitt() */ ++ ++ ++ ++/* ++ Sends "OK to power me down" message to the remote ++ power cycling box, via the serial port. ++ Also updates the num power cycle count in a local ++ file. ++ This file "./cycleCnt" must be present. This is ++ initially (and once) created by the separate "makefiles.c" ++ program. ++*/ ++void send_pwrdn_ok(void){ ++ ++ int fd; ++ FILE *cyclefp; ++ int cycle_fd; ++ ++ cyclefp = fopen("cycleCnt","rb"); ++ if(!cyclefp){ ++ printf("expecting file \"cycleCnt\". Cannot continue.\n"); ++ exit(1); ++ } ++ if(fread(&CycleCount, sizeof(CycleCount),1,cyclefp) != 1){ ++ fprintf(stderr, "Error! Unexpected end of file cycleCnt.\n"); ++ exit(1); ++ } ++ fclose(cyclefp); ++ ++ CycleCount++; ++ ++ /*now write this puppy back*/ ++ cyclefp = fopen("cycleCnt","wb"); ++ cycle_fd = fileno(cyclefp); ++ if(!cyclefp){ ++ fprintf(stderr, "Error! cannot open file for write:\"cycleCnt\". Cannot continue.\n"); ++ exit(1); ++ } ++ if(fwrite(&CycleCount, sizeof(CycleCount), 1,cyclefp) !=1){ ++ fprintf(stderr, "Error writing to file cycleCnt. Cannot continue.\n"); ++ exit(1); ++ } ++ if(fdatasync(cycle_fd)){ ++ fprintf(stderr, "Error! cannot sync file buffer with disk.\n"); ++ exit(1); ++ } ++ ++ fclose(cyclefp); ++ (void)sync(); ++ ++ printf("\n\n Sending Power down command to the remote box.\n"); ++ fd = setupSerial(SerialDevice); ++ ++ if(do_pwr_dn(fd, CycleCount) < 0) ++ { ++ fprintf(stderr, "Error sending power down command.\n"); ++ exit(1); ++ } ++ ++ close(fd); ++}//end send_pwrnd_ok() ++ ++ ++ ++ ++/* ++ Appends 16bit CRC at the end of numBytes long buffer. ++ Make sure buf, extends at least 2 bytes beyond. ++ */ ++void appendChecksum(char *buf, int numBytes){ ++ ++ unsigned short crc = 0xffff; ++ int index = 0; ++ ++ /* printf("Added CRC (2 bytes) to %i bytes.\n", numBytes); */ ++ ++ for (; numBytes > 0; numBytes--){ ++ ++ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ buf[index++]) & 0xff]; ++ } ++ crc ^= 0xffff; ++ /*printf("The CRC: %x\n\n", crc);*/ ++ ++ buf[index++] = crc; ++ buf[index++] = crc >> 8; ++ ++ ++ ++}/*end checksum()*/ ++ ++ ++ ++ ++ ++/* ++ This guy make a new "random data file" with the filename ++ passed to it. This file is checksummed with the checksum ++ stored at the end. The first "int" in the file is the ++ number of int's in it (this is needed to know how much ++ data to read and checksum later). ++*/ ++void make_new_file(char *filename){ ++ ++ ++ int dfd; /* data file descriptor */ ++ int rand_data; ++ int data_size; ++ int temp_size; ++ int dataIndex = 0; ++ int err; ++ ++ ++ struct { ++ int sizeInBytes; /* must be int */ ++ int dataInt[MAX_INTS_ALLOW+1]; /* how many int's can we write? */ ++ }__attribute((packed)) dataBuf; ++ ++ ++ fprintf(stderr, "Creating File:%s. ", filename); ++ ++ if((dfd = open(filename, O_RDWR | O_CREAT | O_SYNC)) <= 0) ++ { ++ printf("Error! Cannot open file: %s\n",filename); ++ perror("Error"); ++ exit(1); ++ } ++ ++ /*now write a bunch of random binary data to the file*/ ++ /*first figure out how much data to write. That is random also.*/ ++ ++ /*file should not be less than 5 ints long. (so that we have decent length files, ++ that's all)*/ ++ while( ++ ((data_size = (int)(1+(int)((FileSizeMax/sizeof(int))*rand()/(RAND_MAX+1.0)))) < 5) ++ ); ++ ++ /* printf("Writing %i ints to the file.\n", data_size); */ ++ ++ temp_size = data_size * sizeof(int); ++ ++ /* Make sure that all data is written in one go! This is important to ++ check for reliability of file systems like JFFS/JFFS that purport to ++ have "reliable" writes during powre fail. ++ */ ++ ++ dataBuf.sizeInBytes = temp_size; ++ ++ data_size--; /*one alrady written*/ ++ dataIndex = 0; ++ ++ while(data_size--){ ++ rand_data = (int)(1 + (int)(10000.0*rand()/(RAND_MAX+1.0))); ++ ++ dataBuf.dataInt[dataIndex++] = rand_data; ++ ++ } ++ ++ /*now calculate the file checksum and append it to the end*/ ++ appendChecksum((char *)&dataBuf, dataBuf.sizeInBytes); ++ ++ /* Don't forget to increase the size of data written by the 2 chars of CRC at end. ++ These 2 bytes are NOT included in the sizeInBytes field. */ ++ if((err = write(dfd, (void *)&dataBuf, dataBuf.sizeInBytes + sizeof(short))) < ++ (dataBuf.sizeInBytes + sizeof(short)) ++ ) ++ { ++ printf("Error writing data buffer to file. Written %i bytes rather than %i bytes.", ++ err, dataBuf.sizeInBytes); ++ perror("Error"); ++ exit(1); ++ } ++ ++ /* Now that the data is (hopefully) safely written. I can truncate the file to the new ++ length so that I can reclaim any unused space, if the older file was larger. ++ */ ++ if(ftruncate(dfd, dataBuf.sizeInBytes + sizeof(short)) < 0) ++ { ++ perror("Error: Unable to truncate file."); ++ exit(1); ++ } ++ ++ ++ close(dfd); ++ ++ ++}//end make_new_file() ++ ++ ++ ++/* ++ Show's help on stdout ++ */ ++void printHelp(char **argv) ++{ ++ printf("Usage:%s \n", argv[0]); ++ printf("%s : Set com port to send ok to pwr dn msg on\n", ++ CMDLINE_PORT); ++ printf("%s : Set Max size in bytes of each file to be created.\n", ++ CMDLINE_MAXFILEBYTES); ++ printf("%s : Set Max errors allowed when checking all files for CRC on start.\n", ++ CMDLINE_MAXERROR); ++ printf("%s or %s: This Help screen.\n", CMDLINE_HELPSHORT, ++ CMDLINE_HELPLONG); ++ ++}/* end printHelp()*/ ++ ++ ++ ++void processCmdLine(int argc, char **argv) ++{ ++ ++ int cnt; ++ ++ /* skip past name of this program, process rest */ ++ for(cnt = 1; cnt < argc; cnt++) ++ { ++ if(strcmp(argv[cnt], CMDLINE_PORT) == 0) ++ { ++ strncpy(SerialDevice, argv[++cnt], sizeof(SerialDevice)); ++ continue; ++ }else ++ if(strcmp(argv[cnt], CMDLINE_MAXFILEBYTES) == 0) ++ { ++ FileSizeMax = (float)atoi(argv[++cnt]); ++ if(FileSizeMax > (MAX_INTS_ALLOW*sizeof(int))) ++ { ++ printf("Max file size allowd is %i.\n", ++ MAX_INTS_ALLOW*sizeof(int)); ++ exit(0); ++ } ++ ++ continue; ++ }else ++ if(strcmp(argv[cnt], CMDLINE_HELPSHORT) == 0) ++ { ++ printHelp(argv); ++ exit(0); ++ ++ }else ++ if(strcmp(argv[cnt], CMDLINE_HELPLONG) == 0) ++ { ++ printHelp(argv); ++ exit(0); ++ }else ++ ++ if(strcmp(argv[cnt], CMDLINE_MAXERROR) == 0) ++ { ++ MaxErrAllowed = atoi(argv[++cnt]); ++ } ++ else ++ { ++ printf("Unknown cmd line option:%s\n", argv[cnt]); ++ printHelp(argv); ++ exit(0); ++ ++ } ++ } ++ ++ ++}/* end processCmdLine() */ ++ ++ ++ ++ ++ ++int main(int argc, char **argv){ ++ ++ FILE *logfp; ++ int log_fd; ++ char filename[30]; ++ short filenameCounter = 0; ++ unsigned short counter; ++ unsigned short numberFiles; ++ char error = FALSE; ++ short errorCnt = 0; ++ time_t timep; ++ char * time_string; ++ unsigned int seed; ++ ++ ++ numberFiles = MAX_NUM_FILES; ++ ++ if(argc >= 1) ++ { ++ processCmdLine(argc, argv); ++ } ++ ++ ++ /* ++ First open MAX_NUM_FILES and make sure that the checksum is ok. ++ Also make an intry into the logfile. ++ */ ++ /* timestamp! */ ++ time(&timep); ++ time_string = (char *)ctime((time_t *)&timep); ++ ++ /*start a new check, make a log entry and continue*/ ++ logfp = fopen("logfile","a"); /*open for appending only.*/ ++ log_fd = fileno(logfp); ++ fprintf(logfp,"%s", time_string); ++ fprintf(logfp,"Starting new check.\n"); ++ if(fdatasync(log_fd) == -1){ ++ fprintf(stderr,"Error! Cannot sync file data with disk.\n"); ++ exit(1); ++ } ++ ++ fclose(logfp); ++ (void)sync(); ++ ++ /* ++ Now check all random data files in this dir. ++ */ ++ for(counter=0;counter MaxErrAllowed){ ++ logfp = fopen("logfile","a"); /*open for appending only.*/ ++ log_fd = fileno(logfp); ++ fprintf(logfp,"\nMax Error count exceed. Stopping!\n"); ++ if(fdatasync(log_fd) == -1){ ++ fprintf(stderr,"Error! Cannot sync file data with disk.\n"); ++ exit(1); ++ } ++ fclose(logfp); ++ (void)sync(); ++ ++ fprintf(stderr, "Too many errors. See \"logfile\".\n"); ++ exit(1); ++ }/* if too many errors */ ++ ++ /*we have decided to continue, however first repair this file ++ so that we do not cumulate errors across power cycles.*/ ++ make_new_file(filename); ++ } ++ }//for ++ ++ /*all files checked, make a log entry and continue*/ ++ logfp = fopen("logfile","a"); /*open for appending only.*/ ++ log_fd = fileno(logfp); ++ fprintf(logfp,"All files checked. Total errors found: %i\n\n", errorCnt); ++ if(fdatasync(log_fd)){ ++ fprintf(stderr, "Error! cannot sync file buffer with disk.\n"); ++ exit(1); ++ } ++ ++ fclose(logfp); ++ (void)sync(); ++ ++ /*now send a message to the remote power box and have it start a random ++ pwer down timer after which power will be killed to this unit. ++ */ ++ send_pwrdn_ok(); ++ ++ /*now go into a forever loop of writing to files and CRC'ing them on ++ a continious basis.*/ ++ ++ /*start from a random file #*/ ++ /*seed rand based on the current time*/ ++ seed = (unsigned int)time(NULL); ++ srand(seed); ++ ++ filenameCounter=(int)(1+(int)((float)(MAX_NUM_FILES-1)*rand()/(RAND_MAX+1.0))); ++ ++ while(1){ ++ ++ for(;filenameCounter ++#include ++#include ++ ++ ++ ++/* ++ This is the routine that forms and ++ sends the "ok to pwr me down" message ++ to the remote power cycling "black box". ++ ++ */ ++int do_pwr_dn(int fd, int cycleCnt) ++{ ++ ++ char buf[200]; ++ ++ sprintf(buf, "ok to power me down!\nCount = %i\n", cycleCnt); ++ ++ if(write(fd, buf, strlen(buf)) < strlen(buf)) ++ { ++ perror("write error"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/common.h linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/common.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/common.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/common.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,7 @@ ++/* $Id: common.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ */ ++//this .h file is common to both the file creation utility and ++//the file checking utility. ++#define TRUE 1 ++#define FALSE 0 ++ ++#define MAX_NUM_FILES 100 +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/makefiles.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/makefiles.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/checkfs/makefiles.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/checkfs/makefiles.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,264 @@ ++/* ++ ++ * Copyright Daniel Industries. ++ ++ * Created by: Vipin Malik (vipin.malik@daniel.com) ++ * ++ * This is GPL code. See the file COPYING for more details ++ * ++ * Software distributed under the Licence is distributed on an "AS IS" ++ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. ++ * See the Licence for the specific language governing rights and ++ * limitations under the Licence. ++ ++ * $Id: makefiles.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ ++This program creates MAX_NUM_FILES files (file00001, file00002 etc) and ++fills them with random numbers till they are a random length. Then it checksums ++the files (with the checksum as the last two bytes) and closes the file. ++ ++The fist int is the size of the file in bytes. ++ ++It then opens another file and the process continues. ++ ++The files are opened in the current dir. ++ ++*/ ++#include ++#include ++#include ++#include ++#include ++#include "common.h" ++ ++#define FILESIZE_MAX 20000.0 /* for each file in sizeof(int). Must be a float # ++ Hence, 20000.0 = 20000*4 = 80KB max file size ++ */ ++ ++static const unsigned short crc_ccitt_table[] = { ++ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, ++ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, ++ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, ++ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, ++ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, ++ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, ++ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, ++ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, ++ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, ++ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, ++ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, ++ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, ++ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, ++ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, ++ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, ++ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, ++ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, ++ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, ++ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, ++ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, ++ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, ++ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, ++ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, ++ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, ++ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, ++ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, ++ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, ++ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, ++ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, ++ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, ++ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, ++ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 ++}; ++ ++//This code was taken from the AX.25 HDLC packet driver ++//in LINUX. Once I test and have a better understanding of what ++//it is doing, it will be better commented. ++ ++//For now one can speculate that the CRC routine always expects the ++//CRC to calculate out to 0xf0b8 (the hardcoded value at the end) ++//and returns TRUE if it is and FALSE if it doesn't. ++//Why don't people document better!!!! ++void check_crc_ccitt(char *filename) ++{ ++ FILE *fp; ++ unsigned short crc = 0xffff; ++ int len; ++ char dataByte; ++ int retry; ++ ++ fp = fopen(filename,"rb"); ++ if(!fp){ ++ printf("Verify checksum:Error! Cannot open filename passed for verify checksum: %s\n",filename); ++ exit(1); ++ } ++ /*the first int contains an int that is the length of the file in long.*/ ++ if(fread(&len, sizeof(int), 1, fp) != 1){ ++ printf("verify checksum:Error reading from file: %s", filename); ++ fclose(fp); ++ exit(1); ++ } ++ rewind(fp); ++ len+=2; /*the file has two extra bytes at the end, it's checksum. Those ++ two MUST also be included in the checksum calculation. ++ */ ++ ++ for (;len>0;len--){ ++ retry=5; /*retry 5 times*/ ++ while(!fread(&dataByte, sizeof(char), 1, fp) && retry--); ++ if(!retry){ ++ printf("Unexpected error reading from file: %s\n", filename); ++ printf("...bytes left to be read %i.\n\n",len); ++ fclose(fp); ++ exit(1); ++ } ++ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ dataByte) & 0xff]; ++ } ++ fclose(fp); ++ if( (crc & 0xffff) != 0xf0b8){ ++ printf("Verify checksum: Error in file %s.\n\n",filename); ++ exit(1); ++ } ++}//end check_crc_ccitt() ++ ++ ++ ++/*this routine opens a file 'filename' and checksumn's the entire ++ contents, and then appends the checksum at the end of the file, ++ closes the file and returns. ++*/ ++void checksum(char *filename){ ++ ++ FILE *fp; ++ unsigned short crc = 0xffff; ++ int len; ++ char dataByte; ++ int retry; ++ ++ fp = fopen(filename,"rb"); ++ if(!fp){ ++ printf("Error! Cannot open filename passed for checksum: %s\n",filename); ++ exit(1); ++ } ++ /*the first int contains an int that is the length of the file in longs.*/ ++ if(fread(&len, sizeof(int), 1, fp) != 1){ ++ printf("Error reading from file: %s", filename); ++ fclose(fp); ++ exit(1); ++ } ++ printf("Calculating checksum on %i bytes.\n",len); ++ rewind(fp); /*the # of bytes int is also included in the checksum.*/ ++ ++ for (;len>0;len--){ ++ retry=5; /*retry 5 times*/ ++ while(!fread(&dataByte, sizeof(char), 1, fp) && retry--); ++ if(!retry){ ++ printf("Unexpected error reading from file: %s\n", filename); ++ printf("...bytes left to be read %i.\n\n",len); ++ fclose(fp); ++ exit(1); ++ } ++ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ dataByte) & 0xff]; ++ } ++ crc ^= 0xffff; ++ printf("The CRC: %x\n\n", crc); ++ ++ /*the CRC has been calculated. now close the file and open it in append mode*/ ++ fclose(fp); ++ ++ fp = fopen(filename,"ab"); /*open in append mode. CRC goes at the end.*/ ++ if(!fp){ ++ printf("Error! Cannot open filename to update checksum: %s\n",filename); ++ exit(1); ++ } ++ if(fwrite(&crc, sizeof(crc), 1, fp) != 1){ ++ printf("error! unable to update the file for checksum.\n"); ++ fclose(fp); ++ exit(1); ++ } ++ fflush(fp); ++ fclose(fp); ++ ++ ++}/*end checksum()*/ ++ ++ ++ ++int main(void){ ++ ++ FILE *fp, *cyclefp; ++ int cycleCount; ++ int rand_data; ++ int data_size; ++ int temp_size; ++ char filename[30]; ++ short filenameCounter = 0; ++ unsigned short counter; ++ unsigned short numberFiles; ++ ++ numberFiles = MAX_NUM_FILES; ++ ++ for(counter=0;counter ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++/* Structures to store data written to the test file system, ++ so that we can check whether the file system is correct. */ ++ ++struct write_info /* Record of random data written into a file */ ++{ ++ struct write_info *next; ++ off_t offset; /* Where in the file the data was written */ ++ size_t size; /* Number of bytes written */ ++ unsigned random_seed; /* Seed for rand() to create random data */ ++ off_t random_offset; /* Call rand() this number of times first */ ++}; ++ ++struct file_info /* Each file has one of these */ ++{ ++ char *name; ++ struct dir_info *parent; /* Parent directory */ ++ struct write_info *writes; /* Record accumulated writes to the file */ ++ struct write_info *raw_writes; ++ /* Record in order all writes to the file */ ++ struct fd_info *fds; /* All open file descriptors for this file */ ++ off_t length; ++ int deleted; /* File has been deleted but is still open */ ++ int no_space_error; /* File has incurred a ENOSPC error */ ++}; ++ ++struct dir_info /* Each directory has one of these */ ++{ ++ char *name; ++ struct dir_info *parent; /* Parent directory or null ++ for our top directory */ ++ unsigned number_of_entries; ++ struct dir_entry_info *first; ++}; ++ ++struct dir_entry_info /* Each entry in a directory has one of these */ ++{ ++ struct dir_entry_info *next; ++ char type; /* f => file, d=> dir */ ++ int checked; /* Temporary flag used when checking */ ++ union entry_ ++ { ++ struct file_info *file; ++ struct dir_info *dir; ++ } entry; ++}; ++ ++struct fd_info /* We keep a number of files open */ ++{ ++ struct fd_info *next; ++ struct file_info *file; ++ int fd; ++}; ++ ++struct open_file_info /* We keep a list of open files */ ++{ ++ struct open_file_info *next; ++ struct fd_info *fdi; ++}; ++ ++static struct dir_info *top_dir = NULL; /* Our top directory */ ++ ++static struct open_file_info *open_files = NULL; /* We keep a list of ++ open files */ ++static size_t open_files_count = 0; ++ ++static int grow = 1; /* Should we try to grow files and directories */ ++static int shrink = 0; /* Should we try to shrink files and directories */ ++static int full = 0; /* Flag that the file system is full */ ++static uint64_t operation_count = 0; /* Number of operations used to fill ++ up the file system */ ++static uint64_t initial_free_space = 0; /* Free space on file system when ++ test starts */ ++static unsigned log10_initial_free_space = 0; /* log10 of initial_free_space */ ++ ++static char *copy_string(const char *s) ++{ ++ char *str; ++ ++ if (!s) ++ return NULL; ++ str = (char *) malloc(strlen(s) + 1); ++ CHECK(str != NULL); ++ strcpy(str, s); ++ return str; ++} ++ ++static char *cat_strings(const char *a, const char *b) ++{ ++ char *str; ++ size_t sz; ++ ++ if (a && !b) ++ return copy_string(a); ++ if (b && !a) ++ return copy_string(b); ++ if (!a && !b) ++ return NULL; ++ sz = strlen(a) + strlen(b) + 1; ++ str = (char *) malloc(sz); ++ CHECK(str != NULL); ++ strcpy(str, a); ++ strcat(str, b); ++ return str; ++} ++ ++static char *cat_paths(const char *a, const char *b) ++{ ++ char *str; ++ size_t sz; ++ int as, bs; ++ size_t na, nb; ++ ++ if (a && !b) ++ return copy_string(a); ++ if (b && !a) ++ return copy_string(b); ++ if (!a && !b) ++ return NULL; ++ ++ as = 0; ++ bs = 0; ++ na = strlen(a); ++ nb = strlen(b); ++ if (na && a[na - 1] == '/') ++ as = 1; ++ if (nb && b[0] == '/') ++ bs = 1; ++ if ((as && !bs) || (!as && bs)) ++ return cat_strings(a, b); ++ if (as && bs) ++ return cat_strings(a, b + 1); ++ ++ sz = na + nb + 2; ++ str = (char *) malloc(sz); ++ CHECK(str != NULL); ++ strcpy(str, a); ++ strcat(str, "/"); ++ strcat(str, b); ++ return str; ++} ++ ++static char *dir_path(struct dir_info *parent, const char *name) ++{ ++ char *parent_path; ++ char *path; ++ ++ if (!parent) ++ return cat_paths(tests_file_system_mount_dir, name); ++ parent_path = dir_path(parent->parent, parent->name); ++ path = cat_paths(parent_path, name); ++ free(parent_path); ++ return path; ++} ++ ++static struct dir_entry_info *dir_entry_new(void) ++{ ++ struct dir_entry_info *entry; ++ size_t sz; ++ ++ sz = sizeof(struct dir_entry_info); ++ entry = (struct dir_entry_info *) malloc(sz); ++ CHECK(entry != NULL); ++ memset(entry, 0, sz); ++ return entry; ++} ++ ++static void open_file_add(struct fd_info *fdi) ++{ ++ struct open_file_info *ofi; ++ size_t sz; ++ ++ sz = sizeof(struct open_file_info); ++ ofi = (struct open_file_info *) malloc(sz); ++ CHECK(ofi != NULL); ++ memset(ofi, 0, sz); ++ ofi->next = open_files; ++ ofi->fdi = fdi; ++ open_files = ofi; ++ open_files_count += 1; ++} ++ ++static void open_file_remove(struct fd_info *fdi) ++{ ++ struct open_file_info *ofi; ++ struct open_file_info **prev; ++ ++ prev = &open_files; ++ for (ofi = open_files; ofi; ofi = ofi->next) { ++ if (ofi->fdi == fdi) { ++ *prev = ofi->next; ++ free(ofi); ++ open_files_count -= 1; ++ return; ++ } ++ prev = &ofi->next; ++ } ++ CHECK(0); /* We are trying to remove something that is not there */ ++} ++ ++static struct fd_info *fd_new(struct file_info *file, int fd) ++{ ++ struct fd_info *fdi; ++ size_t sz; ++ ++ sz = sizeof(struct fd_info); ++ fdi = (struct fd_info *) malloc(sz); ++ CHECK(fdi != NULL); ++ memset(fdi, 0, sz); ++ fdi->next = file->fds; ++ fdi->file = file; ++ fdi->fd = fd; ++ file->fds = fdi; ++ open_file_add(fdi); ++ return fdi; ++} ++ ++static struct dir_info *dir_new(struct dir_info *parent, const char *name) ++{ ++ struct dir_info *dir; ++ size_t sz; ++ char *path; ++ ++ path = dir_path(parent, name); ++ if (mkdir(path, 0777) == -1) { ++ CHECK(errno == ENOSPC); ++ full = 1; ++ free(path); ++ return NULL; ++ } ++ free(path); ++ ++ sz = sizeof(struct dir_info); ++ dir = (struct dir_info *) malloc(sz); ++ CHECK(dir != NULL); ++ memset(dir, 0, sz); ++ dir->name = copy_string(name); ++ dir->parent = parent; ++ if (parent) { ++ struct dir_entry_info *entry; ++ ++ entry = dir_entry_new(); ++ entry->type = 'd'; ++ entry->entry.dir = dir; ++ entry->next = parent->first; ++ parent->first = entry; ++ parent->number_of_entries += 1; ++ } ++ return dir; ++} ++ ++static void file_delete(struct file_info *file); ++ ++static void dir_remove(struct dir_info *dir) ++{ ++ char *path; ++ struct dir_entry_info *entry; ++ struct dir_entry_info **prev; ++ int found; ++ ++ /* Remove directory contents */ ++ while (dir->first) { ++ struct dir_entry_info *entry; ++ ++ entry = dir->first; ++ if (entry->type == 'd') ++ dir_remove(entry->entry.dir); ++ else if (entry->type == 'f') ++ file_delete(entry->entry.file); ++ else ++ CHECK(0); /* Invalid struct dir_entry_info */ ++ } ++ /* Remove entry from parent directory */ ++ found = 0; ++ prev = &dir->parent->first; ++ for (entry = dir->parent->first; entry; entry = entry->next) { ++ if (entry->type == 'd' && entry->entry.dir == dir) { ++ dir->parent->number_of_entries -= 1; ++ *prev = entry->next; ++ free(entry); ++ found = 1; ++ break; ++ } ++ prev = &entry->next; ++ } ++ CHECK(found); /* Check the file is in the parent directory */ ++ /* Remove directory itself */ ++ path = dir_path(dir->parent, dir->name); ++ CHECK(rmdir(path) != -1); ++} ++ ++static struct file_info *file_new(struct dir_info *parent, const char *name) ++{ ++ struct file_info *file = NULL; ++ char *path; ++ mode_t mode; ++ int fd; ++ size_t sz; ++ struct dir_entry_info *entry; ++ ++ CHECK(parent != NULL); ++ ++ path = dir_path(parent, name); ++ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; ++ fd = open(path, O_CREAT | O_EXCL | O_RDWR, mode); ++ if (fd == -1) { ++ CHECK(errno == ENOSPC); ++ free(path); ++ full = 1; ++ return NULL; ++ } ++ free(path); ++ ++ sz = sizeof(struct file_info); ++ file = (struct file_info *) malloc(sz); ++ CHECK(file != NULL); ++ memset(file, 0, sz); ++ file->name = copy_string(name); ++ file->parent = parent; ++ ++ fd_new(file, fd); ++ ++ entry = dir_entry_new(); ++ entry->type = 'f'; ++ entry->entry.file = file; ++ entry->next = parent->first; ++ parent->first = entry; ++ parent->number_of_entries += 1; ++ ++ return file; ++} ++ ++static void file_delete(struct file_info *file) ++{ ++ char *path; ++ struct dir_entry_info *entry; ++ struct dir_entry_info **prev; ++ int found; ++ ++ /* Remove file entry from parent directory */ ++ found = 0; ++ prev = &file->parent->first; ++ for (entry = file->parent->first; entry; entry = entry->next) { ++ if (entry->type == 'f' && entry->entry.file == file) { ++ file->parent->number_of_entries -= 1; ++ *prev = entry->next; ++ free(entry); ++ found = 1; ++ break; ++ } ++ prev = &entry->next; ++ } ++ CHECK(found); /* Check the file is in the parent directory */ ++ ++ /* Delete the file */ ++ path = dir_path(file->parent, file->name); ++ CHECK(unlink(path) != -1); ++ free(path); ++ ++ /* Free struct file_info if file is not open */ ++ if (!file->fds) { ++ struct write_info *w, *next; ++ ++ free(file->name); ++ w = file->writes; ++ while (w) { ++ next = w->next; ++ free(w); ++ w = next; ++ } ++ free(file); ++ } else ++ file->deleted = 1; ++} ++ ++static void file_info_display(struct file_info *file) ++{ ++ struct write_info *w; ++ unsigned wcnt; ++ ++ fprintf(stderr, "File Info:\n"); ++ fprintf(stderr, " Name: %s\n", file->name); ++ fprintf(stderr, " Directory: %s\n", file->parent->name); ++ fprintf(stderr, " Length: %u\n", (unsigned) file->length); ++ fprintf(stderr, " File was open: %s\n", ++ (file->fds == NULL) ? "false" : "true"); ++ fprintf(stderr, " File was deleted: %s\n", ++ (file->deleted == 0) ? "false" : "true"); ++ fprintf(stderr, " File was out of space: %s\n", ++ (file->no_space_error == 0) ? "false" : "true"); ++ fprintf(stderr, " Write Info:\n"); ++ wcnt = 0; ++ w = file->writes; ++ while (w) { ++ fprintf(stderr, " Offset: %u Size: %u Seed: %u" ++ " R.Off: %u\n", ++ (unsigned) w->offset, ++ (unsigned) w->size, ++ (unsigned) w->random_seed, ++ (unsigned) w->random_offset); ++ wcnt += 1; ++ w = w->next; ++ } ++ fprintf(stderr, " %u writes\n", wcnt); ++ fprintf(stderr, " ============================================\n"); ++ fprintf(stderr, " Raw Write Info:\n"); ++ wcnt = 0; ++ w = file->raw_writes; ++ while (w) { ++ fprintf(stderr, " Offset: %u Size: %u Seed: %u" ++ " R.Off: %u\n", ++ (unsigned) w->offset, ++ (unsigned) w->size, ++ (unsigned) w->random_seed, ++ (unsigned) w->random_offset); ++ wcnt += 1; ++ w = w->next; ++ } ++ fprintf(stderr, " %u writes\n", wcnt); ++ fprintf(stderr, " ============================================\n"); ++} ++ ++static struct fd_info *file_open(struct file_info *file) ++{ ++ int fd; ++ char *path; ++ ++ path = dir_path(file->parent, file->name); ++ fd = open(path, O_RDWR); ++ CHECK(fd != -1); ++ free(path); ++ return fd_new(file, fd); ++} ++ ++#define BUFFER_SIZE 32768 ++ ++static size_t file_write_data( struct file_info *file, ++ int fd, ++ off_t offset, ++ size_t size, ++ unsigned seed) ++{ ++ size_t remains, actual, block; ++ ssize_t written; ++ char buf[BUFFER_SIZE]; ++ ++ srand(seed); ++ CHECK(lseek(fd, offset, SEEK_SET) != (off_t) -1); ++ remains = size; ++ actual = 0; ++ written = BUFFER_SIZE; ++ while (remains) { ++ /* Fill up buffer with random data */ ++ if (written < BUFFER_SIZE) ++ memmove(buf, buf + written, BUFFER_SIZE - written); ++ else ++ written = 0; ++ for (; written < BUFFER_SIZE; ++written) ++ buf[written] = rand(); ++ /* Write a block of data */ ++ if (remains > BUFFER_SIZE) ++ block = BUFFER_SIZE; ++ else ++ block = remains; ++ written = write(fd, buf, block); ++ if (written < 0) { ++ CHECK(errno == ENOSPC); /* File system full */ ++ full = 1; ++ file->no_space_error = 1; ++ break; ++ } ++ remains -= written; ++ actual += written; ++ } ++ return actual; ++} ++ ++static void file_write_info(struct file_info *file, ++ off_t offset, ++ size_t size, ++ unsigned seed) ++{ ++ struct write_info *new_write, *w, **prev, *tmp; ++ int inserted; ++ size_t sz; ++ off_t end, chg; ++ ++ /* Create struct write_info */ ++ sz = sizeof(struct write_info); ++ new_write = (struct write_info *) malloc(sz); ++ CHECK(new_write != NULL); ++ memset(new_write, 0, sz); ++ new_write->offset = offset; ++ new_write->size = size; ++ new_write->random_seed = seed; ++ ++ w = (struct write_info *) malloc(sz); ++ CHECK(w != NULL); ++ memset(w, 0, sz); ++ w->next = file->raw_writes; ++ w->offset = offset; ++ w->size = size; ++ w->random_seed = seed; ++ file->raw_writes = w; ++ ++ /* Insert it into file->writes */ ++ inserted = 0; ++ end = offset + size; ++ w = file->writes; ++ prev = &file->writes; ++ while (w) { ++ if (w->offset >= end) { ++ /* w comes after new_write, so insert before it */ ++ new_write->next = w; ++ *prev = new_write; ++ inserted = 1; ++ break; ++ } ++ /* w does not come after new_write */ ++ if (w->offset + w->size > offset) { ++ /* w overlaps new_write */ ++ if (w->offset < offset) { ++ /* w begins before new_write begins */ ++ if (w->offset + w->size <= end) ++ /* w ends before new_write ends */ ++ w->size = offset - w->offset; ++ else { ++ /* w ends after new_write ends */ ++ /* Split w */ ++ tmp = (struct write_info *) malloc(sz); ++ CHECK(tmp != NULL); ++ *tmp = *w; ++ chg = end - tmp->offset; ++ tmp->offset += chg; ++ tmp->random_offset += chg; ++ tmp->size -= chg; ++ w->size = offset - w->offset; ++ /* Insert new struct write_info */ ++ w->next = new_write; ++ new_write->next = tmp; ++ inserted = 1; ++ break; ++ } ++ } else { ++ /* w begins after new_write begins */ ++ if (w->offset + w->size <= end) { ++ /* w is completely overlapped, ++ so remove it */ ++ *prev = w->next; ++ tmp = w; ++ w = w->next; ++ free(tmp); ++ continue; ++ } ++ /* w ends after new_write ends */ ++ chg = end - w->offset; ++ w->offset += chg; ++ w->random_offset += chg; ++ w->size -= chg; ++ continue; ++ } ++ } ++ prev = &w->next; ++ w = w->next; ++ } ++ if (!inserted) ++ *prev = new_write; ++ /* Update file length */ ++ if (end > file->length) ++ file->length = end; ++} ++ ++/* Randomly select offset and and size to write in a file */ ++static void get_offset_and_size(struct file_info *file, ++ off_t *offset, ++ size_t *size) ++{ ++ size_t r, n; ++ ++ r = tests_random_no(100); ++ if (r == 0 && grow) ++ /* 1 time in 100, when growing, write off the end of the file */ ++ *offset = file->length + tests_random_no(10000000); ++ else if (r < 4) ++ /* 3 (or 4) times in 100, write at the beginning of file */ ++ *offset = 0; ++ else if (r < 52 || !grow) ++ /* 48 times in 100, write into the file */ ++ *offset = tests_random_no(file->length); ++ else ++ /* 48 times in 100, write at the end of the file */ ++ *offset = file->length; ++ /* Distribute the size logarithmically */ ++ if (tests_random_no(1000) == 0) ++ r = tests_random_no(log10_initial_free_space + 2); ++ else ++ r = tests_random_no(log10_initial_free_space); ++ n = 1; ++ while (r--) ++ n *= 10; ++ *size = tests_random_no(n); ++ if (!grow && *offset + *size > file->length) ++ *size = file->length - *offset; ++ if (*size == 0) ++ *size = 1; ++} ++ ++static void file_truncate_info(struct file_info *file, size_t new_length); ++static void file_close(struct fd_info *fdi); ++ ++static int file_ftruncate(struct file_info *file, int fd, off_t new_length) ++{ ++ if (ftruncate(fd, new_length) == -1) { ++ CHECK(errno = ENOSPC); ++ file->no_space_error = 1; ++ /* Delete errored files */ ++ if (!file->deleted) { ++ struct fd_info *fdi; ++ ++ fdi = file->fds; ++ while (fdi) { ++ file_close(fdi); ++ fdi = file->fds; ++ } ++ file_delete(file); ++ } ++ return 0; ++ } ++ return 1; ++} ++ ++static void file_write(struct file_info *file, int fd) ++{ ++ off_t offset; ++ size_t size, actual; ++ unsigned seed; ++ int truncate = 0; ++ ++ get_offset_and_size(file, &offset, &size); ++ seed = tests_random_no(10000000); ++ actual = file_write_data(file, fd, offset, size, seed); ++ ++ if (offset + actual <= file->length && shrink) ++ /* 1 time in 100, when shrinking ++ truncate after the write */ ++ if (tests_random_no(100) == 0) ++ truncate = 1; ++ ++ if (actual != 0) ++ file_write_info(file, offset, actual, seed); ++ ++ /* Delete errored files */ ++ if (file->no_space_error) { ++ if (!file->deleted) { ++ struct fd_info *fdi; ++ ++ fdi = file->fds; ++ while (fdi) { ++ file_close(fdi); ++ fdi = file->fds; ++ } ++ file_delete(file); ++ } ++ return; ++ } ++ ++ if (truncate) { ++ size_t new_length = offset + actual; ++ if (file_ftruncate(file, fd, new_length)) ++ file_truncate_info(file, new_length); ++ } ++} ++ ++static void file_write_file(struct file_info *file) ++{ ++ int fd; ++ char *path; ++ ++ path = dir_path(file->parent, file->name); ++ fd = open(path, O_WRONLY); ++ CHECK(fd != -1); ++ file_write(file, fd); ++ CHECK(close(fd) != -1); ++ free(path); ++} ++ ++static void file_truncate_info(struct file_info *file, size_t new_length) ++{ ++ struct write_info *w, **prev, *tmp; ++ ++ /* Remove / truncate file->writes */ ++ w = file->writes; ++ prev = &file->writes; ++ while (w) { ++ if (w->offset >= new_length) { ++ /* w comes after eof, so remove it */ ++ *prev = w->next; ++ tmp = w; ++ w = w->next; ++ free(tmp); ++ continue; ++ } ++ if (w->offset + w->size > new_length) ++ w->size = new_length - w->offset; ++ prev = &w->next; ++ w = w->next; ++ } ++ /* Update file length */ ++ file->length = new_length; ++} ++ ++static void file_truncate(struct file_info *file, int fd) ++{ ++ size_t new_length; ++ ++ new_length = tests_random_no(file->length); ++ ++ if (file_ftruncate(file, fd, new_length)) ++ file_truncate_info(file, new_length); ++} ++ ++static void file_truncate_file(struct file_info *file) ++{ ++ int fd; ++ char *path; ++ ++ path = dir_path(file->parent, file->name); ++ fd = open(path, O_WRONLY); ++ CHECK(fd != -1); ++ file_truncate(file, fd); ++ CHECK(close(fd) != -1); ++ free(path); ++} ++ ++static void file_close(struct fd_info *fdi) ++{ ++ struct file_info *file; ++ struct fd_info *fdp; ++ struct fd_info **prev; ++ ++ /* Close file */ ++ CHECK(close(fdi->fd) != -1); ++ /* Remove struct fd_info */ ++ open_file_remove(fdi); ++ file = fdi->file; ++ prev = &file->fds; ++ for (fdp = file->fds; fdp; fdp = fdp->next) { ++ if (fdp == fdi) { ++ *prev = fdi->next; ++ free(fdi); ++ if (file->deleted && !file->fds) { ++ /* Closing deleted file */ ++ struct write_info *w, *next; ++ ++ w = file->writes; ++ while (w) { ++ next = w->next; ++ free(w); ++ w = next; ++ } ++ free(file->name); ++ free(file); ++ } ++ return; ++ } ++ prev = &fdp->next; ++ } ++ CHECK(0); /* Didn't find struct fd_info */ ++} ++ ++static void file_rewrite_data(int fd, struct write_info *w, char *buf) ++{ ++ size_t remains, block; ++ ssize_t written; ++ off_t r; ++ ++ srand(w->random_seed); ++ for (r = 0; r < w->random_offset; ++r) ++ rand(); ++ CHECK(lseek(fd, w->offset, SEEK_SET) != (off_t) -1); ++ remains = w->size; ++ written = BUFFER_SIZE; ++ while (remains) { ++ /* Fill up buffer with random data */ ++ if (written < BUFFER_SIZE) ++ memmove(buf, buf + written, BUFFER_SIZE - written); ++ else ++ written = 0; ++ for (; written < BUFFER_SIZE; ++written) ++ buf[written] = rand(); ++ /* Write a block of data */ ++ if (remains > BUFFER_SIZE) ++ block = BUFFER_SIZE; ++ else ++ block = remains; ++ written = write(fd, buf, block); ++ CHECK(written == block); ++ remains -= written; ++ } ++} ++ ++static void save_file(int fd, struct file_info *file) ++{ ++ int w_fd; ++ struct write_info *w; ++ char buf[BUFFER_SIZE]; ++ char name[256]; ++ ++ /* Open file to save contents to */ ++ strcpy(name, "/tmp/"); ++ strcat(name, file->name); ++ strcat(name, ".integ.sav.read"); ++ fprintf(stderr, "Saving %s\n", name); ++ w_fd = open(name, O_CREAT | O_WRONLY, 0777); ++ CHECK(w_fd != -1); ++ ++ /* Start at the beginning */ ++ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1); ++ ++ for (;;) { ++ ssize_t r = read(fd, buf, BUFFER_SIZE); ++ CHECK(r != -1); ++ if (!r) ++ break; ++ CHECK(write(w_fd, buf, r) == r); ++ } ++ CHECK(close(w_fd) != -1); ++ ++ /* Open file to save contents to */ ++ strcpy(name, "/tmp/"); ++ strcat(name, file->name); ++ strcat(name, ".integ.sav.written"); ++ fprintf(stderr, "Saving %s\n", name); ++ w_fd = open(name, O_CREAT | O_WRONLY, 0777); ++ CHECK(w_fd != -1); ++ ++ for (w = file->writes; w; w = w->next) ++ file_rewrite_data(w_fd, w, buf); ++ ++ CHECK(close(w_fd) != -1); ++} ++ ++static void file_check_hole( struct file_info *file, ++ int fd, off_t offset, ++ size_t size) ++{ ++ size_t remains, block, i; ++ char buf[BUFFER_SIZE]; ++ ++ CHECK(lseek(fd, offset, SEEK_SET) != (off_t) -1); ++ remains = size; ++ while (remains) { ++ if (remains > BUFFER_SIZE) ++ block = BUFFER_SIZE; ++ else ++ block = remains; ++ CHECK(read(fd, buf, block) == block); ++ for (i = 0; i < block; ++i) { ++ if (buf[i] != 0) { ++ fprintf(stderr, "file_check_hole failed at %u " ++ "checking hole at %u size %u\n", ++ (unsigned) (size - remains + i), ++ (unsigned) offset, ++ (unsigned) size); ++ file_info_display(file); ++ save_file(fd, file); ++ } ++ CHECK(buf[i] == 0); ++ } ++ remains -= block; ++ } ++} ++ ++static void file_check_data( struct file_info *file, ++ int fd, ++ struct write_info *w) ++{ ++ size_t remains, block, i; ++ off_t r; ++ char buf[BUFFER_SIZE]; ++ ++ srand(w->random_seed); ++ for (r = 0; r < w->random_offset; ++r) ++ rand(); ++ CHECK(lseek(fd, w->offset, SEEK_SET) != (off_t) -1); ++ remains = w->size; ++ while (remains) { ++ if (remains > BUFFER_SIZE) ++ block = BUFFER_SIZE; ++ else ++ block = remains; ++ CHECK(read(fd, buf, block) == block); ++ for (i = 0; i < block; ++i) { ++ char c = (char) rand(); ++ if (buf[i] != c) { ++ fprintf(stderr, "file_check_data failed at %u " ++ "checking data at %u size %u\n", ++ (unsigned) (w->size - remains + i), ++ (unsigned) w->offset, ++ (unsigned) w->size); ++ file_info_display(file); ++ save_file(fd, file); ++ } ++ CHECK(buf[i] == c); ++ } ++ remains -= block; ++ } ++} ++ ++static void file_check(struct file_info *file, int fd) ++{ ++ int open_and_close = 0; ++ char *path = NULL; ++ off_t pos; ++ struct write_info *w; ++ ++ /* Do not check files that have errored */ ++ if (file->no_space_error) ++ return; ++ if (fd == -1) ++ open_and_close = 1; ++ if (open_and_close) { ++ /* Open file */ ++ path = dir_path(file->parent, file->name); ++ fd = open(path, O_RDONLY); ++ CHECK(fd != -1); ++ } ++ /* Check length */ ++ pos = lseek(fd, 0, SEEK_END); ++ if (pos != file->length) { ++ fprintf(stderr, "file_check failed checking length " ++ "expected %u actual %u\n", ++ (unsigned) file->length, ++ (unsigned) pos); ++ file_info_display(file); ++ save_file(fd, file); ++ } ++ CHECK(pos == file->length); ++ /* Check each write */ ++ pos = 0; ++ for (w = file->writes; w; w = w->next) { ++ if (w->offset > pos) ++ file_check_hole(file, fd, pos, w->offset - pos); ++ file_check_data(file, fd, w); ++ pos = w->offset + w->size; ++ } ++ if (file->length > pos) ++ file_check_hole(file, fd, pos, file->length - pos); ++ if (open_and_close) { ++ CHECK(close(fd) != -1); ++ free(path); ++ } ++} ++ ++static const char *dir_entry_name(const struct dir_entry_info *entry) ++{ ++ CHECK(entry != NULL); ++ if (entry->type == 'd') ++ return entry->entry.dir->name; ++ else if (entry->type == 'f') ++ return entry->entry.file->name; ++ else { ++ CHECK(0); ++ return NULL; ++ } ++} ++ ++static int search_comp(const void *pa, const void *pb) ++{ ++ const struct dirent *a = (const struct dirent *) pa; ++ const struct dir_entry_info *b = * (const struct dir_entry_info **) pb; ++ return strcmp(a->d_name, dir_entry_name(b)); ++} ++ ++static void dir_entry_check(struct dir_entry_info **entry_array, ++ size_t number_of_entries, ++ struct dirent *ent) ++{ ++ struct dir_entry_info **found; ++ struct dir_entry_info *entry; ++ size_t sz; ++ ++ sz = sizeof(struct dir_entry_info *); ++ found = bsearch(ent, entry_array, number_of_entries, sz, search_comp); ++ CHECK(found != NULL); ++ entry = *found; ++ CHECK(!entry->checked); ++ entry->checked = 1; ++} ++ ++static int sort_comp(const void *pa, const void *pb) ++{ ++ const struct dir_entry_info *a = * (const struct dir_entry_info **) pa; ++ const struct dir_entry_info *b = * (const struct dir_entry_info **) pb; ++ return strcmp(dir_entry_name(a), dir_entry_name(b)); ++} ++ ++static void dir_check(struct dir_info *dir) ++{ ++ struct dir_entry_info **entry_array, **p; ++ size_t sz, n; ++ struct dir_entry_info *entry; ++ DIR *d; ++ struct dirent *ent; ++ unsigned checked = 0; ++ char *path; ++ ++ /* Create an array of entries */ ++ sz = sizeof(struct dir_entry_info *); ++ n = dir->number_of_entries; ++ entry_array = (struct dir_entry_info **) malloc(sz * n); ++ CHECK(entry_array != NULL); ++ ++ entry = dir->first; ++ p = entry_array; ++ while (entry) { ++ *p++ = entry; ++ entry->checked = 0; ++ entry = entry->next; ++ } ++ ++ /* Sort it by name */ ++ qsort(entry_array, n, sz, sort_comp); ++ ++ /* Go through directory on file system checking entries match */ ++ path = dir_path(dir->parent, dir->name); ++ d = opendir(path); ++ CHECK(d != NULL); ++ for (;;) { ++ errno = 0; ++ ent = readdir(d); ++ if (ent) { ++ if (strcmp(".",ent->d_name) != 0 && ++ strcmp("..",ent->d_name) != 0) { ++ dir_entry_check(entry_array, n, ent); ++ checked += 1; ++ } ++ } else { ++ CHECK(errno == 0); ++ break; ++ } ++ } ++ CHECK(closedir(d) != -1); ++ CHECK(checked == dir->number_of_entries); ++ free(path); ++ ++ /* Now check each entry */ ++ entry = dir->first; ++ while (entry) { ++ if (entry->type == 'd') ++ dir_check(entry->entry.dir); ++ else if (entry->type == 'f') ++ file_check(entry->entry.file, -1); ++ else ++ CHECK(0); ++ entry = entry->next; ++ } ++ ++ free(entry_array); ++} ++ ++static void check_deleted_files(void) ++{ ++ struct open_file_info *ofi; ++ ++ for (ofi = open_files; ofi; ofi = ofi->next) ++ if (ofi->fdi->file->deleted) ++ file_check(ofi->fdi->file, ofi->fdi->fd); ++} ++ ++static void close_open_files(void) ++{ ++ struct open_file_info *ofi; ++ ++ for (ofi = open_files; ofi; ofi = open_files) ++ file_close(ofi->fdi); ++} ++ ++static char *make_name(struct dir_info *dir) ++{ ++ static char name[256]; ++ struct dir_entry_info *entry; ++ int found; ++ ++ do { ++ found = 0; ++ sprintf(name, "%u", (unsigned) tests_random_no(1000000)); ++ for (entry = dir->first; entry; entry = entry->next) { ++ if (strcmp(dir_entry_name(entry), name) == 0) { ++ found = 1; ++ break; ++ } ++ } ++ } while (found); ++ return name; ++} ++ ++static void operate_on_dir(struct dir_info *dir); ++static void operate_on_file(struct file_info *file); ++ ++/* Randomly select something to do with a directory entry */ ++static void operate_on_entry(struct dir_entry_info *entry) ++{ ++ /* If shrinking, 1 time in 50, remove a directory */ ++ if (entry->type == 'd') { ++ if (shrink && tests_random_no(50) == 0) { ++ dir_remove(entry->entry.dir); ++ return; ++ } ++ operate_on_dir(entry->entry.dir); ++ } ++ /* If shrinking, 1 time in 10, remove a file */ ++ if (entry->type == 'f') { ++ if (shrink && tests_random_no(10) == 0) { ++ file_delete(entry->entry.file); ++ return; ++ } ++ operate_on_file(entry->entry.file); ++ } ++} ++ ++/* Randomly select something to do with a directory */ ++static void operate_on_dir(struct dir_info *dir) ++{ ++ size_t r; ++ struct dir_entry_info *entry; ++ ++ r = tests_random_no(12); ++ if (r == 0 && grow) ++ /* When growing, 1 time in 12 create a file */ ++ file_new(dir, make_name(dir)); ++ else if (r == 1 && grow) ++ /* When growing, 1 time in 12 create a directory */ ++ dir_new(dir, make_name(dir)); ++ else { ++ /* Otherwise randomly select an entry to operate on */ ++ r = tests_random_no(dir->number_of_entries); ++ entry = dir->first; ++ while (entry && r) { ++ entry = entry->next; ++ --r; ++ } ++ if (entry) ++ operate_on_entry(entry); ++ } ++} ++ ++/* Randomly select something to do with a file */ ++static void operate_on_file(struct file_info *file) ++{ ++ /* Try to keep at least 10 files open */ ++ if (open_files_count < 10) { ++ file_open(file); ++ return; ++ } ++ /* Try to keep about 20 files open */ ++ if (open_files_count < 20 && tests_random_no(2) == 0) { ++ file_open(file); ++ return; ++ } ++ /* Try to keep up to 40 files open */ ++ if (open_files_count < 40 && tests_random_no(20) == 0) { ++ file_open(file); ++ return; ++ } ++ /* Occasionly truncate */ ++ if (shrink && tests_random_no(100) == 0) { ++ file_truncate_file(file); ++ return; ++ } ++ /* Mostly just write */ ++ file_write_file(file); ++} ++ ++/* Randomly select something to do with an open file */ ++static void operate_on_open_file(struct fd_info *fdi) ++{ ++ size_t r; ++ ++ r = tests_random_no(1000); ++ if (shrink && r == 0) ++ file_truncate(fdi->file, fdi->fd); ++ else if (r < 21) ++ file_close(fdi); ++ else if (shrink && r < 121 && !fdi->file->deleted) ++ file_delete(fdi->file); ++ else ++ file_write(fdi->file, fdi->fd); ++} ++ ++/* Select an open file at random */ ++static void operate_on_an_open_file(void) ++{ ++ size_t r; ++ struct open_file_info *ofi; ++ ++ /* Close any open files that have errored */ ++ ofi = open_files; ++ while (ofi) { ++ if (ofi->fdi->file->no_space_error) { ++ struct fd_info *fdi; ++ ++ fdi = ofi->fdi; ++ ofi = ofi->next; ++ file_close(fdi); ++ } else ++ ofi = ofi->next; ++ } ++ r = tests_random_no(open_files_count); ++ for (ofi = open_files; ofi; ofi = ofi->next, --r) ++ if (!r) { ++ operate_on_open_file(ofi->fdi); ++ return; ++ } ++} ++ ++static void do_an_operation(void) ++{ ++ /* Half the time operate on already open files */ ++ if (tests_random_no(100) < 50) ++ operate_on_dir(top_dir); ++ else ++ operate_on_an_open_file(); ++} ++ ++static void create_test_data(void) ++{ ++ uint64_t i; ++ ++ grow = 1; ++ shrink = 0; ++ full = 0; ++ operation_count = 0; ++ while (!full) { ++ do_an_operation(); ++ ++operation_count; ++ } ++ grow = 0; ++ shrink = 1; ++ /* Drop to less than 90% full */ ++ for (;;) { ++ uint64_t free; ++ uint64_t total; ++ for (i = 0; i < 10; ++i) ++ do_an_operation(); ++ free = tests_get_free_space(); ++ total = tests_get_total_space(); ++ if ((free * 100) / total >= 10) ++ break; ++ } ++ grow = 0; ++ shrink = 0; ++ full = 0; ++ for (i = 0; i < operation_count * 2; ++i) ++ do_an_operation(); ++} ++ ++static void update_test_data(void) ++{ ++ uint64_t i; ++ ++ grow = 1; ++ shrink = 0; ++ full = 0; ++ while (!full) ++ do_an_operation(); ++ grow = 0; ++ shrink = 1; ++ /* Drop to less than 50% full */ ++ for (;;) { ++ uint64_t free; ++ uint64_t total; ++ for (i = 0; i < 10; ++i) ++ do_an_operation(); ++ free = tests_get_free_space(); ++ total = tests_get_total_space(); ++ if ((free * 100) / total >= 50) ++ break; ++ } ++ grow = 0; ++ shrink = 0; ++ full = 0; ++ for (i = 0; i < operation_count * 2; ++i) ++ do_an_operation(); ++} ++ ++void integck(void) ++{ ++ pid_t pid; ++ int64_t rpt; ++ uint64_t z; ++ char dir_name[256]; ++ ++ /* Make our top directory */ ++ pid = getpid(); ++ printf("pid is %u\n", (unsigned) pid); ++ tests_cat_pid(dir_name, "integck_test_dir_", pid); ++ if (chdir(dir_name) != -1) { ++ /* Remove it if it is already there */ ++ tests_clear_dir("."); ++ CHECK(chdir("..") != -1); ++ CHECK(rmdir(dir_name) != -1); ++ } ++ initial_free_space = tests_get_free_space(); ++ log10_initial_free_space = 0; ++ for (z = initial_free_space; z >= 10; z /= 10) ++ ++log10_initial_free_space; ++ top_dir = dir_new(NULL, dir_name); ++ ++ if (!top_dir) ++ return; ++ ++ srand(pid); ++ ++ create_test_data(); ++ ++ if (!tests_fs_is_rootfs()) { ++ close_open_files(); ++ tests_remount(); /* Requires root access */ ++ } ++ ++ /* Check everything */ ++ dir_check(top_dir); ++ check_deleted_files(); ++ ++ for (rpt = 0; tests_repeat_parameter == 0 || ++ rpt < tests_repeat_parameter; ++rpt) { ++ update_test_data(); ++ ++ if (!tests_fs_is_rootfs()) { ++ close_open_files(); ++ tests_remount(); /* Requires root access */ ++ } ++ ++ /* Check everything */ ++ dir_check(top_dir); ++ check_deleted_files(); ++ } ++ ++ /* Tidy up by removing everything */ ++ close_open_files(); ++ tests_clear_dir(dir_name); ++ CHECK(rmdir(dir_name) != -1); ++} ++ ++/* Title of this test */ ++ ++const char *integck_get_title(void) ++{ ++ return "Test file system integrity"; ++} ++ ++/* Description of this test */ ++ ++const char *integck_get_description(void) ++{ ++ return ++ "Create a directory named integck_test_dir_pid " \ ++ "where pid is the process id. " \ ++ "Randomly create and delete files and directories. " \ ++ "Randomly write to and truncate files. " \ ++ "Un-mount and re-mount test file " \ ++ "system (if it is not the root file system ). " \ ++ "Check data. Make more random changes. " \ ++ "Un-mount and re-mount again. Check again. " \ ++ "Repeat some number of times. " ++ "The repeat count is set by the -n or --repeat option, " \ ++ "otherwise it defaults to 1. " \ ++ "A repeat count of zero repeats forever."; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Set default test repetition */ ++ tests_repeat_parameter = 1; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, integck_get_title(), ++ integck_get_description(), "n"); ++ if (!run_test) ++ return 1; ++ /* Change directory to the file system and check it is ok for testing */ ++ tests_check_test_file_system(); ++ /* Do the actual test */ ++ integck(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/lib/Makefile linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/lib/Makefile +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/lib/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/lib/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,18 @@ ++ ++ifeq ($(origin CC),default) ++CC = gcc ++endif ++ ++CFLAGS := $(CFLAGS) -Wall -g -O2 ++ ++LDFLAGS := $(LDFLAGS) ++ ++all: tests.o ++ ++tests.o: tests.h ++ ++clean: ++ rm -f *.o ++ ++tests: ++ echo +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/lib/tests.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/lib/tests.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/lib/tests.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/lib/tests.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1091 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++char *tests_file_system_mount_dir = TESTS_DEFAULT_FILE_SYSTEM_MOUNT_DIR; ++ ++char *tests_file_system_type = TESTS_DEFAULT_FILE_SYSTEM_TYPE; ++ ++int tests_ok_to_sync = 0; /* Whether to use fsync */ ++ ++/* General purpose test parameter to specify some aspect of test size. ++ May be used by different tests in different ways or not at all. ++ Set by the -z or --size option. */ ++int64_t tests_size_parameter = 0; ++ ++/* General purpose test parameter to specify some aspect of test repetition. ++ May be used by different tests in different ways or not at all. ++ Set by the -n, --repeat options. */ ++int64_t tests_repeat_parameter = 0; ++ ++/* General purpose test parameter to specify some aspect of test sleeping. ++ May be used by different tests in different ways or not at all. ++ Set by the -p, --sleep options. */ ++int64_t tests_sleep_parameter = 0; ++ ++/* Program name from argv[0] */ ++char *program_name = "unknown"; ++ ++/* General purpose test parameter to specify a file should be unlinked. ++ May be used by different tests in different ways or not at all. */ ++int tests_unlink_flag = 0; ++ ++/* General purpose test parameter to specify a file should be closed. ++ May be used by different tests in different ways or not at all. */ ++int tests_close_flag = 0; ++ ++/* General purpose test parameter to specify a file should be deleted. ++ May be used by different tests in different ways or not at all. */ ++int tests_delete_flag = 0; ++ ++/* General purpose test parameter to specify a file have a hole. ++ May be used by different tests in different ways or not at all. */ ++int tests_hole_flag = 0; ++ ++/* Whether it is ok to test on the root file system */ ++static int rootok = 0; ++ ++/* Function invoked by the CHECK macro */ ++void tests_test(int test,const char *msg,const char *file,unsigned line) ++{ ++ int eno; ++ time_t t; ++ ++ if (test) ++ return; ++ eno = errno; ++ time(&t); ++ fprintf(stderr, "Test failed: %s on %s" ++ "Test failed: %s in %s at line %u\n", ++ program_name, ctime(&t), msg, file, line); ++ if (eno) { ++ fprintf(stderr,"errno = %d\n",eno); ++ fprintf(stderr,"strerror = %s\n",strerror(eno)); ++ } ++ exit(1); ++} ++ ++static int is_zero(const char *p) ++{ ++ for (;*p;++p) ++ if (*p != '0') ++ return 0; ++ return 1; ++} ++ ++static void fold(const char *text, int width) ++{ ++ int pos, bpos = 0; ++ const char *p; ++ char line[1024]; ++ ++ if (width > 1023) { ++ printf("%s\n", text); ++ return; ++ } ++ p = text; ++ pos = 0; ++ while (p[pos]) { ++ while (!isspace(p[pos])) { ++ line[pos] = p[pos]; ++ if (!p[pos]) ++ break; ++ ++pos; ++ if (pos == width) { ++ line[pos] = '\0'; ++ printf("%s\n", line); ++ p += pos; ++ pos = 0; ++ } ++ } ++ while (pos < width) { ++ line[pos] = p[pos]; ++ if (!p[pos]) { ++ bpos = pos; ++ break; ++ } ++ if (isspace(p[pos])) ++ bpos = pos; ++ ++pos; ++ } ++ line[bpos] = '\0'; ++ printf("%s\n", line); ++ p += bpos; ++ pos = 0; ++ while (p[pos] && isspace(p[pos])) ++ ++p; ++ } ++} ++ ++/* Handle common program options */ ++int tests_get_args(int argc, ++ char *argv[], ++ const char *title, ++ const char *desc, ++ const char *opts) ++{ ++ int run_test = 0; ++ int display_help = 0; ++ int display_title = 0; ++ int display_description = 0; ++ int i; ++ char *s; ++ ++ program_name = argv[0]; ++ ++ s = getenv("TEST_FILE_SYSTEM_MOUNT_DIR"); ++ if (s) ++ tests_file_system_mount_dir = strdup(s); ++ s = getenv("TEST_FILE_SYSTEM_TYPE"); ++ if (s) ++ tests_file_system_type = strdup(s); ++ ++ run_test = 1; ++ rootok = 1; ++ for (i = 1; i < argc; ++i) { ++ if (strcmp(argv[i], "--help") == 0 || ++ strcmp(argv[i], "-h") == 0) ++ display_help = 1; ++ else if (strcmp(argv[i], "--title") == 0 || ++ strcmp(argv[i], "-t") == 0) ++ display_title = 1; ++ else if (strcmp(argv[i], "--description") == 0 || ++ strcmp(argv[i], "-d") == 0) ++ display_description = 1; ++ else if (strcmp(argv[i], "--sync") == 0 || ++ strcmp(argv[i], "-s") == 0) ++ tests_ok_to_sync = 1; ++ else if (strncmp(argv[i], "--size", 6) == 0 || ++ strncmp(argv[i], "-z", 2) == 0) { ++ int64_t n; ++ char *p; ++ if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1])) ++ ++i; ++ p = argv[i]; ++ while (*p && !isdigit(*p)) ++ ++p; ++ n = atoll(p); ++ if (n) ++ tests_size_parameter = n; ++ else { ++ int all_zero = 1; ++ for (; all_zero && *p; ++p) ++ if (*p != '0') ++ all_zero = 0; ++ if (all_zero) ++ tests_size_parameter = 0; ++ else ++ display_help = 1; ++ } ++ } else if (strncmp(argv[i], "--repeat", 8) == 0 || ++ strncmp(argv[i], "-n", 2) == 0) { ++ int64_t n; ++ char *p; ++ if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1])) ++ ++i; ++ p = argv[i]; ++ while (*p && !isdigit(*p)) ++ ++p; ++ n = atoll(p); ++ if (n || is_zero(p)) ++ tests_repeat_parameter = n; ++ else ++ display_help = 1; ++ } else if (strncmp(argv[i], "--sleep", 7) == 0 || ++ strncmp(argv[i], "-p", 2) == 0) { ++ int64_t n; ++ char *p; ++ if (i+1 < argc && !isdigit(argv[i][strlen(argv[i])-1])) ++ ++i; ++ p = argv[i]; ++ while (*p && !isdigit(*p)) ++ ++p; ++ n = atoll(p); ++ if (n || is_zero(p)) ++ tests_sleep_parameter = n; ++ else ++ display_help = 1; ++ } else if (strcmp(argv[i], "--unlink") == 0 || ++ strcmp(argv[i], "-u") == 0) ++ tests_unlink_flag = 1; ++ else if (strcmp(argv[i], "--hole") == 0 || ++ strcmp(argv[i], "-o") == 0) ++ tests_hole_flag = 1; ++ else if (strcmp(argv[i], "--close") == 0 || ++ strcmp(argv[i], "-c") == 0) ++ tests_close_flag = 1; ++ else if (strcmp(argv[i], "--delete") == 0 || ++ strcmp(argv[i], "-e") == 0) ++ tests_delete_flag = 1; ++ else ++ display_help = 1; ++ } ++ ++ if (display_help) { ++ run_test = 0; ++ display_title = 0; ++ display_description = 0; ++ if (!opts) ++ opts = ""; ++ printf("File System Test Program\n\n"); ++ printf("Test Title: %s\n\n", title); ++ printf("Usage is: %s [ options ]\n",argv[0]); ++ printf(" Options are:\n"); ++ printf(" -h, --help "); ++ printf("Display this help\n"); ++ printf(" -t, --title "); ++ printf("Display the test title\n"); ++ printf(" -d, --description "); ++ printf("Display the test description\n"); ++ if (strchr(opts, 's')) { ++ printf(" -s, --sync "); ++ printf("Make use of fsync\n"); ++ } ++ if (strchr(opts, 'z')) { ++ printf(" -z, --size "); ++ printf("Set size parameter\n"); ++ } ++ if (strchr(opts, 'n')) { ++ printf(" -n, --repeat "); ++ printf("Set repeat parameter\n"); ++ } ++ if (strchr(opts, 'p')) { ++ printf(" -p, --sleep "); ++ printf("Set sleep parameter\n"); ++ } ++ if (strchr(opts, 'u')) { ++ printf(" -u, --unlink "); ++ printf("Unlink file\n"); ++ } ++ if (strchr(opts, 'o')) { ++ printf(" -o, --hole "); ++ printf("Create a hole in a file\n"); ++ } ++ if (strchr(opts, 'c')) { ++ printf(" -c, --close "); ++ printf("Close file\n"); ++ } ++ if (strchr(opts, 'e')) { ++ printf(" -e, --delete "); ++ printf("Delete file\n"); ++ } ++ printf("\nBy default, testing is done in directory "); ++ printf("/mnt/test_file_system. To change this\nuse "); ++ printf("environmental variable "); ++ printf("TEST_FILE_SYSTEM_MOUNT_DIR. By default, "); ++ printf("the file\nsystem tested is jffs2. To change this "); ++ printf("set TEST_FILE_SYSTEM_TYPE.\n\n"); ++ printf("Test Description:\n"); ++ fold(desc, 80); ++ } else { ++ if (display_title) ++ printf("%s\n", title); ++ if (display_description) ++ printf("%s\n", desc); ++ if (display_title || display_description) ++ if (argc == 2 || (argc == 3 && ++ display_title && ++ display_description)) ++ run_test = 0; ++ } ++ return run_test; ++} ++ ++/* Return the number of files (or directories) in the given directory */ ++unsigned tests_count_files_in_dir(const char *dir_name) ++{ ++ DIR *dir; ++ struct dirent *entry; ++ unsigned count = 0; ++ ++ dir = opendir(dir_name); ++ CHECK(dir != NULL); ++ for (;;) { ++ errno = 0; ++ entry = readdir(dir); ++ if (entry) { ++ if (strcmp(".",entry->d_name) != 0 && ++ strcmp("..",entry->d_name) != 0) ++ ++count; ++ } else { ++ CHECK(errno == 0); ++ break; ++ } ++ } ++ CHECK(closedir(dir) != -1); ++ return count; ++} ++ ++/* Change to the file system mount directory, check that it is empty, ++ matches the file system type, and is not the root file system */ ++void tests_check_test_file_system(void) ++{ ++ struct statfs fs_info; ++ struct stat f_info; ++ struct stat root_f_info; ++ ++ if (chdir(tests_file_system_mount_dir) == -1 || ++ statfs(tests_file_system_mount_dir, &fs_info) == -1) { ++ fprintf(stderr, "Invalid test file system mount directory:" ++ " %s\n", tests_file_system_mount_dir); ++ fprintf(stderr, "Use environment variable " ++ "TEST_FILE_SYSTEM_MOUNT_DIR\n"); ++ CHECK(0); ++ } ++ if (strcmp(tests_file_system_type, "jffs2") == 0 && ++ fs_info.f_type != JFFS2_SUPER_MAGIC) { ++ fprintf(stderr, "File system type is not jffs2\n"); ++ CHECK(0); ++ } ++ /* Check that the test file system is not the root file system */ ++ if (!rootok) { ++ CHECK(stat(tests_file_system_mount_dir, &f_info) != -1); ++ CHECK(stat("/", &root_f_info) != -1); ++ CHECK(f_info.st_dev != root_f_info.st_dev); ++ } ++} ++ ++/* Get the free space for the file system of the current directory */ ++uint64_t tests_get_free_space(void) ++{ ++ struct statvfs fs_info; ++ ++ CHECK(statvfs(tests_file_system_mount_dir, &fs_info) != -1); ++ return (uint64_t) fs_info.f_bavail * (uint64_t) fs_info.f_frsize; ++} ++ ++/* Get the total space for the file system of the current directory */ ++uint64_t tests_get_total_space(void) ++{ ++ struct statvfs fs_info; ++ ++ CHECK(statvfs(tests_file_system_mount_dir, &fs_info) != -1); ++ return (uint64_t) fs_info.f_blocks * (uint64_t) fs_info.f_frsize; ++} ++ ++#define WRITE_BUFFER_SIZE 32768 ++ ++static char write_buffer[WRITE_BUFFER_SIZE]; ++ ++static void init_write_buffer() ++{ ++ static int init = 0; ++ ++ if (!init) { ++ int i, d; ++ uint64_t u; ++ ++ u = RAND_MAX; ++ u += 1; ++ u /= 256; ++ d = (int) u; ++ srand(1); ++ for (i = 0; i < WRITE_BUFFER_SIZE; ++i) ++ write_buffer[i] = rand() / d; ++ init = 1; ++ } ++} ++ ++/* Write size random bytes into file descriptor fd at the current position, ++ returning the number of bytes actually written */ ++uint64_t tests_fill_file(int fd, uint64_t size) ++{ ++ ssize_t written; ++ size_t sz; ++ unsigned start = 0, length; ++ uint64_t remains; ++ uint64_t actual_size = 0; ++ ++ init_write_buffer(); ++ remains = size; ++ while (remains > 0) { ++ length = WRITE_BUFFER_SIZE - start; ++ if (remains > length) ++ sz = length; ++ else ++ sz = (size_t) remains; ++ written = write(fd, write_buffer + start, sz); ++ if (written <= 0) { ++ CHECK(errno == ENOSPC); /* File system full */ ++ errno = 0; ++ break; ++ } ++ remains -= written; ++ actual_size += written; ++ if (written == sz) ++ start = 0; ++ else ++ start += written; ++ } ++ tests_maybe_sync(fd); ++ return actual_size; ++} ++ ++/* Write size random bytes into file descriptor fd at offset, ++ returning the number of bytes actually written */ ++uint64_t tests_write_filled_file(int fd, off_t offset, uint64_t size) ++{ ++ ssize_t written; ++ size_t sz; ++ unsigned start = 0, length; ++ uint64_t remains; ++ uint64_t actual_size = 0; ++ ++ CHECK(lseek(fd, offset, SEEK_SET) == offset); ++ ++ init_write_buffer(); ++ remains = size; ++ start = offset % WRITE_BUFFER_SIZE; ++ while (remains > 0) { ++ length = WRITE_BUFFER_SIZE - start; ++ if (remains > length) ++ sz = length; ++ else ++ sz = (size_t) remains; ++ written = write(fd, write_buffer + start, sz); ++ if (written <= 0) { ++ CHECK(errno == ENOSPC); /* File system full */ ++ errno = 0; ++ break; ++ } ++ remains -= written; ++ actual_size += written; ++ if (written == sz) ++ start = 0; ++ else ++ start += written; ++ } ++ tests_maybe_sync(fd); ++ return actual_size; ++} ++ ++/* Check that a file written using tests_fill_file() and/or ++ tests_write_filled_file() and/or tests_create_file() ++ contains the expected random data */ ++void tests_check_filled_file_fd(int fd) ++{ ++ ssize_t sz; ++ char buf[WRITE_BUFFER_SIZE]; ++ ++ CHECK(lseek(fd, 0, SEEK_SET) == 0); ++ do { ++ sz = read(fd, buf, WRITE_BUFFER_SIZE); ++ CHECK(sz >= 0); ++ CHECK(memcmp(buf, write_buffer, sz) == 0); ++ } while (sz); ++} ++ ++/* Check that a file written using tests_fill_file() and/or ++ tests_write_filled_file() and/or tests_create_file() ++ contains the expected random data */ ++void tests_check_filled_file(const char *file_name) ++{ ++ int fd; ++ ++ fd = open(file_name, O_RDONLY); ++ CHECK(fd != -1); ++ tests_check_filled_file_fd(fd); ++ CHECK(close(fd) != -1); ++} ++ ++void tests_sync_directory(const char *file_name) ++{ ++ char *path; ++ char *dir; ++ int fd; ++ ++ if (!tests_ok_to_sync) ++ return; ++ ++ path = strdup(file_name); ++ dir = dirname(path); ++ fd = open(dir,O_RDONLY | tests_maybe_sync_flag()); ++ CHECK(fd != -1); ++ CHECK(fsync(fd) != -1); ++ CHECK(close(fd) != -1); ++ free(path); ++} ++ ++/* Delete a file */ ++void tests_delete_file(const char *file_name) ++{ ++ CHECK(unlink(file_name) != -1); ++ tests_sync_directory(file_name); ++} ++ ++/* Create a file of size file_size */ ++uint64_t tests_create_file(const char *file_name, uint64_t file_size) ++{ ++ int fd; ++ int flags; ++ mode_t mode; ++ uint64_t actual_size; /* Less than size if the file system is full */ ++ ++ flags = O_CREAT | O_TRUNC | O_WRONLY | tests_maybe_sync_flag(); ++ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; ++ fd = open(file_name, flags, mode); ++ if (fd == -1 && errno == ENOSPC) { ++ errno = 0; ++ return 0; /* File system full */ ++ } ++ CHECK(fd != -1); ++ actual_size = tests_fill_file(fd, file_size); ++ CHECK(close(fd) != -1); ++ if (file_size != 0 && actual_size == 0) ++ tests_delete_file(file_name); ++ else ++ tests_sync_directory(file_name); ++ return actual_size; ++} ++ ++/* Calculate: free_space * numerator / denominator */ ++uint64_t tests_get_big_file_size(unsigned numerator, unsigned denominator) ++{ ++ if (denominator == 0) ++ denominator = 1; ++ if (numerator > denominator) ++ numerator = denominator; ++ return numerator * (tests_get_free_space() / denominator); ++} ++ ++/* Create file "fragment_n" where n is the file_number, and unlink it */ ++int tests_create_orphan(unsigned file_number) ++{ ++ int fd; ++ int flags; ++ mode_t mode; ++ char file_name[256]; ++ ++ sprintf(file_name, "fragment_%u", file_number); ++ flags = O_CREAT | O_TRUNC | O_RDWR | tests_maybe_sync_flag(); ++ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; ++ fd = open(file_name, flags, mode); ++ if (fd == -1 && (errno == ENOSPC || errno == EMFILE)) ++ return fd; /* File system full or too many open files */ ++ CHECK(fd != -1); ++ tests_sync_directory(file_name); ++ CHECK(unlink(file_name) != -1); ++ return fd; ++} ++ ++/* Write size bytes at offset to the file "fragment_n" where n is the ++ file_number and file_number also determines the random data written ++ i.e. seed for random numbers */ ++unsigned tests_write_fragment_file(unsigned file_number, ++ int fd, ++ off_t offset, ++ unsigned size) ++{ ++ int i, d; ++ uint64_t u; ++ ssize_t written; ++ off_t pos; ++ char buf[WRITE_BUFFER_SIZE]; ++ ++ if (size > WRITE_BUFFER_SIZE) ++ size = WRITE_BUFFER_SIZE; ++ ++ pos = lseek(fd, 0, SEEK_END); ++ CHECK(pos != (off_t) -1); ++ if (offset > pos) ++ offset = pos; ++ ++ pos = lseek(fd, offset, SEEK_SET); ++ CHECK(pos != (off_t) -1); ++ CHECK(pos == offset); ++ ++ srand(file_number); ++ while (offset--) ++ rand(); ++ ++ u = RAND_MAX; ++ u += 1; ++ u /= 256; ++ d = (int) u; ++ for (i = 0; i < size; ++i) ++ buf[i] = rand() / d; ++ ++ written = write(fd, buf, size); ++ if (written <= 0) { ++ CHECK(errno == ENOSPC); /* File system full */ ++ errno = 0; ++ written = 0; ++ } ++ tests_maybe_sync(fd); ++ return (unsigned) written; ++} ++ ++/* Write size bytes to the end of file descriptor fd using file_number ++ to determine the random data written i.e. seed for random numbers */ ++unsigned tests_fill_fragment_file(unsigned file_number, int fd, unsigned size) ++{ ++ off_t offset; ++ ++ offset = lseek(fd, 0, SEEK_END); ++ CHECK(offset != (off_t) -1); ++ ++ return tests_write_fragment_file(file_number, fd, offset, size); ++} ++ ++/* Write size bytes to the end of file "fragment_n" where n is the file_number ++ and file_number also determines the random data written ++ i.e. seed for random numbers */ ++unsigned tests_append_to_fragment_file(unsigned file_number, ++ unsigned size, ++ int create) ++{ ++ int fd; ++ int flags; ++ mode_t mode; ++ unsigned actual_growth; ++ char file_name[256]; ++ ++ sprintf(file_name, "fragment_%u", file_number); ++ if (create) ++ flags = O_CREAT | O_EXCL | O_WRONLY | tests_maybe_sync_flag(); ++ else ++ flags = O_WRONLY | tests_maybe_sync_flag(); ++ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; ++ fd = open(file_name, flags, mode); ++ if (fd == -1 && errno == ENOSPC) { ++ errno = 0; ++ return 0; /* File system full */ ++ } ++ CHECK(fd != -1); ++ actual_growth = tests_fill_fragment_file(file_number, fd, size); ++ CHECK(close(fd) != -1); ++ if (create && !actual_growth) ++ tests_delete_fragment_file(file_number); ++ return actual_growth; ++} ++ ++/* Write size bytes at offset to the file "fragment_n" where n is the ++ file_number and file_number also determines the random data written ++ i.e. seed for random numbers */ ++unsigned tests_overwite_fragment_file( unsigned file_number, ++ off_t offset, ++ unsigned size) ++{ ++ int fd; ++ unsigned actual_size; ++ char file_name[256]; ++ ++ sprintf(file_name, "fragment_%u", file_number); ++ fd = open(file_name, O_RDWR | tests_maybe_sync_flag()); ++ if (fd == -1 && errno == ENOSPC) { ++ errno = 0; ++ return 0; /* File system full */ ++ } ++ CHECK(fd != -1); ++ actual_size = tests_write_fragment_file(file_number, ++ fd, offset, size); ++ CHECK(close(fd) != -1); ++ return actual_size; ++} ++ ++/* Delete file "fragment_n" where n is the file_number */ ++void tests_delete_fragment_file(unsigned file_number) ++{ ++ char file_name[256]; ++ ++ sprintf(file_name, "fragment_%u", file_number); ++ tests_delete_file(file_name); ++} ++ ++/* Check the random data in file "fragment_n" is what is expected */ ++void tests_check_fragment_file_fd(unsigned file_number, int fd) ++{ ++ ssize_t sz, i; ++ int d; ++ uint64_t u; ++ char buf[8192]; ++ ++ CHECK(lseek(fd, 0, SEEK_SET) == 0); ++ srand(file_number); ++ u = RAND_MAX; ++ u += 1; ++ u /= 256; ++ d = (int) u; ++ for (;;) { ++ sz = read(fd, buf, 8192); ++ if (sz == 0) ++ break; ++ CHECK(sz >= 0); ++ for (i = 0; i < sz; ++i) ++ CHECK(buf[i] == (char) (rand() / d)); ++ } ++} ++ ++/* Check the random data in file "fragment_n" is what is expected */ ++void tests_check_fragment_file(unsigned file_number) ++{ ++ int fd; ++ ssize_t sz, i; ++ int d; ++ uint64_t u; ++ char file_name[256]; ++ char buf[8192]; ++ ++ sprintf(file_name, "fragment_%u", file_number); ++ fd = open(file_name, O_RDONLY); ++ CHECK(fd != -1); ++ srand(file_number); ++ u = RAND_MAX; ++ u += 1; ++ u /= 256; ++ d = (int) u; ++ for (;;) { ++ sz = read(fd, buf, 8192); ++ if (sz == 0) ++ break; ++ CHECK(sz >= 0); ++ for (i = 0; i < sz; ++i) ++ CHECK(buf[i] == (char) (rand() / d)); ++ } ++ CHECK(close(fd) != -1); ++} ++ ++/* Central point to decide whether to use fsync */ ++void tests_maybe_sync(int fd) ++{ ++ if (tests_ok_to_sync) ++ CHECK(fsync(fd) != -1); ++} ++ ++/* Return O_SYNC if ok to sync otherwise return 0 */ ++int tests_maybe_sync_flag(void) ++{ ++ if (tests_ok_to_sync) ++ return O_SYNC; ++ return 0; ++} ++ ++/* Return random number from 0 to n - 1 */ ++size_t tests_random_no(size_t n) ++{ ++ uint64_t a, b; ++ ++ if (!n) ++ return 0; ++ if (n - 1 <= RAND_MAX) { ++ a = rand(); ++ b = RAND_MAX; ++ b += 1; ++ } else { ++ const uint64_t u = 1 + (uint64_t) RAND_MAX; ++ a = rand(); ++ a *= u; ++ a += rand(); ++ b = u * u; ++ CHECK(n <= b); ++ } ++ if (RAND_MAX <= UINT32_MAX && n <= UINT32_MAX) ++ return a * n / b; ++ else /*if (RAND_MAX <= UINT64_MAX && n <= UINT64_MAX)*/ { ++ uint64_t x, y; ++ if (a < n) { ++ x = a; ++ y = n; ++ } else { ++ x = n; ++ y = a; ++ } ++ return (x * (y / b)) + ((x * (y % b)) / b); ++ } ++} ++ ++/* Make a directory empty */ ++void tests_clear_dir(const char *dir_name) ++{ ++ DIR *dir; ++ struct dirent *entry; ++ char buf[4096]; ++ ++ dir = opendir(dir_name); ++ CHECK(dir != NULL); ++ CHECK(getcwd(buf, 4096) != NULL); ++ CHECK(chdir(dir_name) != -1); ++ for (;;) { ++ errno = 0; ++ entry = readdir(dir); ++ if (entry) { ++ if (strcmp(".",entry->d_name) != 0 && ++ strcmp("..",entry->d_name) != 0) { ++ if (entry->d_type == DT_DIR) { ++ tests_clear_dir(entry->d_name); ++ CHECK(rmdir(entry->d_name) != -1); ++ } else ++ CHECK(unlink(entry->d_name) != -1); ++ } ++ } else { ++ CHECK(errno == 0); ++ break; ++ } ++ } ++ CHECK(chdir(buf) != -1); ++ CHECK(closedir(dir) != -1); ++} ++ ++/* Create an empty sub-directory or small file in the current directory */ ++int64_t tests_create_entry(char *return_name) ++{ ++ int fd; ++ char name[256]; ++ ++ for (;;) { ++ sprintf(name, "%u", (unsigned) tests_random_no(10000000)); ++ fd = open(name, O_RDONLY); ++ if (fd == -1) ++ break; ++ close(fd); ++ } ++ if (return_name) ++ strcpy(return_name, name); ++ if (tests_random_no(2)) { ++ return tests_create_file(name, tests_random_no(4096)); ++ } else { ++ if (mkdir(name, 0777) == -1) { ++ CHECK(errno == ENOSPC); ++ errno = 0; ++ return 0; ++ } ++ return TESTS_EMPTY_DIR_SIZE; ++ } ++} ++ ++/* Remove a random file of empty sub-directory from the current directory */ ++int64_t tests_remove_entry(void) ++{ ++ DIR *dir; ++ struct dirent *entry; ++ unsigned count = 0, pos; ++ int64_t result = 0; ++ ++ dir = opendir("."); ++ CHECK(dir != NULL); ++ for (;;) { ++ errno = 0; ++ entry = readdir(dir); ++ if (entry) { ++ if (strcmp(".",entry->d_name) != 0 && ++ strcmp("..",entry->d_name) != 0) ++ ++count; ++ } else { ++ CHECK(errno == 0); ++ break; ++ } ++ } ++ pos = tests_random_no(count); ++ count = 0; ++ rewinddir(dir); ++ for (;;) { ++ errno = 0; ++ entry = readdir(dir); ++ if (!entry) { ++ CHECK(errno == 0); ++ break; ++ } ++ if (strcmp(".",entry->d_name) != 0 && ++ strcmp("..",entry->d_name) != 0) { ++ if (count == pos) { ++ if (entry->d_type == DT_DIR) { ++ tests_clear_dir(entry->d_name); ++ CHECK(rmdir(entry->d_name) != -1); ++ result = TESTS_EMPTY_DIR_SIZE; ++ } else { ++ struct stat st; ++ CHECK(stat(entry->d_name, &st) != -1); ++ result = st.st_size; ++ CHECK(unlink(entry->d_name) != -1); ++ } ++ } ++ ++count; ++ } ++ } ++ CHECK(closedir(dir) != -1); ++ return result; ++} ++ ++/* Read mount information from /proc/mounts or /etc/mtab */ ++int tests_get_mount_info(struct mntent *info) ++{ ++ FILE *f; ++ struct mntent *entry; ++ int found = 0; ++ ++ f = fopen("/proc/mounts", "rb"); ++ if (!f) ++ f = fopen("/etc/mtab", "rb"); ++ CHECK(f != NULL); ++ while (!found) { ++ entry = getmntent(f); ++ if (entry) { ++ if (strcmp(entry->mnt_dir, ++ tests_file_system_mount_dir) == 0) { ++ found = 1; ++ *info = *entry; ++ } ++ } else ++ break; ++ } ++ CHECK(fclose(f) == 0); ++ return found; ++} ++ ++/* Un-mount and re-mount test file system */ ++void tests_remount(void) ++{ ++ struct mntent mount_info; ++ char *source; ++ char *target; ++ char *filesystemtype; ++ unsigned long mountflags; ++ void *data; ++ char cwd[4096]; ++ ++ CHECK(tests_get_mount_info(&mount_info)); ++ ++ if (strcmp(mount_info.mnt_dir,"/") == 0) ++ return; ++ ++ CHECK(getcwd(cwd, 4096) != NULL); ++ CHECK(chdir("/") != -1); ++ ++ CHECK(umount(tests_file_system_mount_dir) != -1); ++ ++ source = mount_info.mnt_fsname; ++ target = tests_file_system_mount_dir; ++ filesystemtype = tests_file_system_type; ++ mountflags = 0; ++ data = NULL; ++ ++ CHECK(mount(source, target, filesystemtype, mountflags, data) != -1); ++ ++ CHECK(chdir(cwd) != -1); ++} ++ ++/* Check whether the test file system is also the root file system */ ++int tests_fs_is_rootfs(void) ++{ ++ struct stat f_info; ++ struct stat root_f_info; ++ ++ CHECK(stat(tests_file_system_mount_dir, &f_info) != -1); ++ CHECK(stat("/", &root_f_info) != -1); ++ if (f_info.st_dev == root_f_info.st_dev) ++ return 1; ++ else ++ return 0; ++} ++ ++/* Try to make a directory empty */ ++void tests_try_to_clear_dir(const char *dir_name) ++{ ++ DIR *dir; ++ struct dirent *entry; ++ char buf[4096]; ++ ++ dir = opendir(dir_name); ++ if (dir == NULL) ++ return; ++ if (getcwd(buf, 4096) == NULL || chdir(dir_name) == -1) { ++ closedir(dir); ++ return; ++ } ++ for (;;) { ++ errno = 0; ++ entry = readdir(dir); ++ if (entry) { ++ if (strcmp(".",entry->d_name) != 0 && ++ strcmp("..",entry->d_name) != 0) { ++ if (entry->d_type == DT_DIR) { ++ tests_try_to_clear_dir(entry->d_name); ++ rmdir(entry->d_name); ++ } else ++ unlink(entry->d_name); ++ } ++ } else { ++ CHECK(errno == 0); ++ break; ++ } ++ } ++ chdir(buf); ++ closedir(dir); ++} ++ ++/* Check whether the test file system is also the current file system */ ++int tests_fs_is_currfs(void) ++{ ++ struct stat f_info; ++ struct stat curr_f_info; ++ ++ CHECK(stat(tests_file_system_mount_dir, &f_info) != -1); ++ CHECK(stat(".", &curr_f_info) != -1); ++ if (f_info.st_dev == curr_f_info.st_dev) ++ return 1; ++ else ++ return 0; ++} ++ ++#define PID_BUF_SIZE 64 ++ ++/* Concatenate a pid to a string in a signal safe way */ ++void tests_cat_pid(char *buf, const char *name, pid_t pid) ++{ ++ char *p; ++ unsigned x; ++ const char digits[] = "0123456789"; ++ char pid_buf[PID_BUF_SIZE]; ++ ++ x = (unsigned) pid; ++ p = pid_buf + PID_BUF_SIZE; ++ *--p = '\0'; ++ if (x) ++ while (x) { ++ *--p = digits[x % 10]; ++ x /= 10; ++ } ++ else ++ *--p = '0'; ++ buf[0] = '\0'; ++ strcat(buf, name); ++ strcat(buf, p); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/lib/tests.h linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/lib/tests.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/lib/tests.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/lib/tests.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,199 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#ifndef included_tests_tests_h__ ++#define included_tests_tests_h__ ++ ++#include ++ ++/* Main macro for testing */ ++#define CHECK(x) tests_test((x),__func__,__FILE__,__LINE__) ++ ++/* The default directory in which tests are conducted */ ++#define TESTS_DEFAULT_FILE_SYSTEM_MOUNT_DIR "/mnt/test_file_system" ++ ++/* The default file system type to test */ ++#define TESTS_DEFAULT_FILE_SYSTEM_TYPE "jffs2" ++ ++/* Estimated size of an empty directory */ ++#define TESTS_EMPTY_DIR_SIZE 128 ++ ++/* Function invoked by the CHECK macro */ ++void tests_test(int test,const char *msg,const char *file,unsigned line); ++ ++/* Handle common program options */ ++int tests_get_args(int argc, ++ char *argv[], ++ const char *title, ++ const char *desc, ++ const char *opts); ++ ++/* Return the number of files (or directories) in the given directory */ ++unsigned tests_count_files_in_dir(const char *dir_name); ++ ++/* Change to the file system mount directory, check that it is empty, ++ matches the file system type, and is not the root file system */ ++void tests_check_test_file_system(void); ++ ++/* Get the free space for the file system of the current directory */ ++uint64_t tests_get_free_space(void); ++ ++/* Get the total space for the file system of the current directory */ ++uint64_t tests_get_total_space(void); ++ ++/* Write size random bytes into file descriptor fd at the current position, ++ returning the number of bytes actually written */ ++uint64_t tests_fill_file(int fd, uint64_t size); ++ ++/* Write size random bytes into file descriptor fd at offset, ++ returning the number of bytes actually written */ ++uint64_t tests_write_filled_file(int fd, off_t offset, uint64_t size); ++ ++/* Check that a file written using tests_fill_file() and/or ++ tests_write_filled_file() and/or tests_create_file() ++ contains the expected random data */ ++void tests_check_filled_file_fd(int fd); ++ ++/* Check that a file written using tests_fill_file() and/or ++ tests_write_filled_file() and/or tests_create_file() ++ contains the expected random data */ ++void tests_check_filled_file(const char *file_name); ++ ++/* Delete a file */ ++void tests_delete_file(const char *file_name); ++ ++/* Create a file of size file_size */ ++uint64_t tests_create_file(const char *file_name, uint64_t file_size); ++ ++/* Calculate: free_space * numerator / denominator */ ++uint64_t tests_get_big_file_size(unsigned numerator, unsigned denominator); ++ ++/* Create file "fragment_n" where n is the file_number, and unlink it */ ++int tests_create_orphan(unsigned file_number); ++ ++/* Write size bytes at offset to the file "fragment_n" where n is the ++ file_number and file_number also determines the random data written ++ i.e. seed for random numbers */ ++unsigned tests_write_fragment_file(unsigned file_number, ++ int fd, ++ off_t offset, ++ unsigned size); ++ ++/* Write size bytes to the end of file descriptor fd using file_number ++ to determine the random data written i.e. seed for random numbers */ ++unsigned tests_fill_fragment_file(unsigned file_number, ++ int fd, ++ unsigned size); ++ ++/* Write size bytes to the end of file "fragment_n" where n is the file_number ++ and file_number also determines the random data written ++ i.e. seed for random numbers */ ++unsigned tests_append_to_fragment_file(unsigned file_number, ++ unsigned size, ++ int create); ++ ++/* Write size bytes at offset to the file "fragment_n" where n is the ++ file_number and file_number also determines the random data written ++ i.e. seed for random numbers */ ++unsigned tests_overwite_fragment_file( unsigned file_number, ++ off_t offset, ++ unsigned size); ++ ++/* Delete file "fragment_n" where n is the file_number */ ++void tests_delete_fragment_file(unsigned file_number); ++ ++/* Check the random data in file "fragment_n" is what is expected */ ++void tests_check_fragment_file_fd(unsigned file_number, int fd); ++ ++/* Check the random data in file "fragment_n" is what is expected */ ++void tests_check_fragment_file(unsigned file_number); ++ ++/* Central point to decide whether to use fsync */ ++void tests_maybe_sync(int fd); ++ ++/* Return O_SYNC if ok to sync otherwise return 0 */ ++int tests_maybe_sync_flag(void); ++ ++/* Return random number from 0 to n - 1 */ ++size_t tests_random_no(size_t n); ++ ++/* Make a directory empty */ ++void tests_clear_dir(const char *dir_name); ++ ++/* Create an empty sub-directory or small file in the current directory */ ++int64_t tests_create_entry(char *return_name); ++ ++/* Remove a random file of empty sub-directory from the current directory */ ++int64_t tests_remove_entry(void); ++ ++/* Un-mount and re-mount test file system */ ++void tests_remount(void); ++ ++/* Check whether the test file system is also the root file system */ ++int tests_fs_is_rootfs(void); ++ ++/* Try to make a directory empty */ ++void tests_try_to_clear_dir(const char *dir_name); ++ ++/* Check whether the test file system is also the current file system */ ++int tests_fs_is_currfs(void); ++ ++/* Concatenate a pid to a string in a signal safe way */ ++void tests_cat_pid(char *buf, const char *name, pid_t pid); ++ ++extern char *tests_file_system_mount_dir; ++ ++extern char *tests_file_system_type; ++ ++/* General purpose test parameter to specify some aspect of test size. ++ May be used by different tests in different ways. ++ Set by the -z, --size options. */ ++extern int64_t tests_size_parameter; ++ ++/* General purpose test parameter to specify some aspect of test repetition. ++ May be used by different tests in different ways. ++ Set by the -n, --repeat options. */ ++extern int64_t tests_repeat_parameter; ++ ++/* General purpose test parameter to specify some aspect of test sleeping. ++ May be used by different tests in different ways. ++ Set by the -p, --sleep options. */ ++extern int64_t tests_sleep_parameter; ++ ++/* General purpose test parameter to specify a file should be unlinked. ++ May be used by different tests in different ways or not at all. */ ++extern int tests_unlink_flag; ++ ++/* General purpose test parameter to specify a file should be closed. ++ May be used by different tests in different ways or not at all. */ ++extern int tests_close_flag; ++ ++/* General purpose test parameter to specify a file should be deleted. ++ May be used by different tests in different ways or not at all. */ ++extern int tests_delete_flag; ++ ++/* General purpose test parameter to specify a file have a hole. ++ May be used by different tests in different ways or not at all. */ ++extern int tests_hole_flag; ++ ++/* Program name from argv[0] */ ++extern char *program_name; ++ ++#endif +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/run_all.sh linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/run_all.sh +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/run_all.sh 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/run_all.sh 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,49 @@ ++#!/bin/sh ++ ++TEST_DIR=$TEST_FILE_SYSTEM_MOUNT_DIR ++if test -z "$TEST_DIR"; ++then ++ TEST_DIR="/mnt/test_file_system" ++fi ++ ++rm -rf ${TEST_DIR}/* ++ ++./simple/test_1 || exit 1 ++ ++rm -rf ${TEST_DIR}/* ++ ++./simple/test_2 || exit 1 ++ ++rm -rf ${TEST_DIR}/* ++ ++./integrity/integck || exit 1 ++ ++rm -rf ${TEST_DIR}/* ++ ++./stress/atoms/rndrm00 -z0 || exit 1 ++ ++rm -rf ${TEST_DIR}/* ++ ++./stress/atoms/rmdir00 -z0 || exit 1 ++ ++rm -rf ${TEST_DIR}/* ++ ++./stress/atoms/stress_1 -z10000000 -e || exit 1 ++ ++rm -rf ${TEST_DIR}/* ++ ++./stress/atoms/stress_2 -z10000000 || exit 1 ++ ++rm -rf ${TEST_DIR}/* ++ ++./stress/atoms/stress_3 -z1000000000 -e || exit 1 ++ ++rm -rf ${TEST_DIR}/* ++ ++cd stress || exit 1 ++ ++./stress00.sh 3600 || exit 1 ++ ++./stress01.sh 3600 || exit 1 ++ ++cd .. || exit 1 +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/Makefile linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/Makefile +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,28 @@ ++ ++ifeq ($(origin CC),default) ++CC = gcc ++endif ++ ++CFLAGS := $(CFLAGS) -Wall -g -O2 -I../lib ++ ++LDFLAGS := $(LDFLAGS) ++ ++TARGETS = test_1 \ ++ test_2 \ ++ ftrunc \ ++ orph ++ ++all: $(TARGETS) ++ ++$(TARGETS): ../lib/tests.o ++ ++../lib/tests.o: ../lib/tests.h ++ ++clean: ++ rm -f *.o $(TARGETS) ++ ++tests: all ++ ./test_1 --sync ++ ./test_2 --sync ++ ./ftrunc ++ ./orph --sync +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/ftrunc.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/ftrunc.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/ftrunc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/ftrunc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,111 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++#define WRITE_BUFFER_SIZE 32768 ++ ++void ftrunc(void) ++{ ++ int fd, i; ++ pid_t pid; ++ ssize_t written; ++ int64_t remains; ++ size_t block; ++ char *file_name; ++ off_t actual; ++ char buf[WRITE_BUFFER_SIZE]; ++ ++ file_name = "ftrunc_test_file"; ++ fd = open(file_name, O_CREAT | O_WRONLY, ++ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); ++ CHECK(fd != -1); ++ pid = getpid(); ++ srand(pid); ++ for (i = 0; i < WRITE_BUFFER_SIZE;++i) ++ buf[i] = rand(); ++ remains = tests_size_parameter; ++ actual = 0; ++ while (remains > 0) { ++ if (remains > WRITE_BUFFER_SIZE) ++ block = WRITE_BUFFER_SIZE; ++ else ++ block = remains; ++ written = write(fd, buf, block); ++ if (written <= 0) { ++ CHECK(errno == ENOSPC); /* File system full */ ++ errno = 0; ++ break; ++ } ++ remains -= written; ++ actual += written; ++ } ++ CHECK(ftruncate(fd, (actual ? actual - 1 : actual)) != -1); ++ CHECK(close(fd) != -1); ++ CHECK(unlink(file_name) != -1); ++} ++ ++/* Title of this test */ ++ ++const char *ftrunc_get_title(void) ++{ ++ return "Truncate a large test file"; ++} ++ ++/* Description of this test */ ++ ++const char *ftrunc_get_description(void) ++{ ++ return ++ "Create a file named ftrunc_test_file. " \ ++ "Truncate the file to reduce its length by 1. " \ ++ "Then remove the truncated file. " ++ "The size is given by the -z or --size option, " \ ++ "otherwise it defaults to 1000000."; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Set default test file size */ ++ tests_size_parameter = 1000000; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, ftrunc_get_title(), ++ ftrunc_get_description(), "z"); ++ if (!run_test) ++ return 1; ++ /* Change directory to the file system and check it is ok for testing */ ++ tests_check_test_file_system(); ++ /* Do the actual test */ ++ ftrunc(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/orph.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/orph.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/orph.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/orph.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,184 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++#define MAX_ORPHANS 1000000 ++ ++void orph(void) ++{ ++ pid_t pid; ++ unsigned i, j, k, n; ++ int fd, done, full; ++ int64_t repeat; ++ ssize_t sz; ++ char dir_name[256]; ++ int fds[MAX_ORPHANS]; ++ ++ /* Create a directory to test in */ ++ pid = getpid(); ++ tests_cat_pid(dir_name, "orph_test_dir_", pid); ++ if (chdir(dir_name) == -1) ++ CHECK(mkdir(dir_name, 0777) != -1); ++ CHECK(chdir(dir_name) != -1); ++ ++ repeat = tests_repeat_parameter; ++ for (;;) { ++ full = 0; ++ done = 0; ++ n = 0; ++ while (n + 100 < MAX_ORPHANS && !done) { ++ /* Make 100 more orphans */ ++ for (i = 0; i < 100; i++) { ++ fd = tests_create_orphan(n + i); ++ if (fd < 0) { ++ done = 1; ++ if (errno == ENOSPC) ++ full = 1; ++ else if (errno != EMFILE) ++ CHECK(0); ++ errno = 0; ++ break; ++ } ++ fds[n + i] = fd; ++ } ++ if (!full) { ++ /* Write to orphans just created */ ++ k = i; ++ for (i = 0; i < k; i++) { ++ if (tests_write_fragment_file(n + i, ++ fds[n+i], ++ 0, 1000) ++ != 1000) { ++ /* ++ * Out of space, so close ++ * remaining files ++ */ ++ for (j = i; j < k; j++) ++ CHECK(close(fds[n + j]) ++ != -1); ++ done = 1; ++ break; ++ } ++ } ++ } ++ if (!done) ++ CHECK(tests_count_files_in_dir(".") == 0); ++ n += i; ++ } ++ /* Check the data in the files */ ++ for (i = 0; i < n; i++) ++ tests_check_fragment_file_fd(i, fds[i]); ++ if (!full && n) { ++ /* Ensure the file system is full */ ++ n -= 1; ++ do { ++ sz = write(fds[n], fds, 4096); ++ if (sz == -1 && errno == ENOSPC) { ++ errno = 0; ++ break; ++ } ++ CHECK(sz >= 0); ++ } while (sz == 4096); ++ CHECK(close(fds[n]) != -1); ++ } ++ /* Check the data in the files */ ++ for (i = 0; i < n; i++) ++ tests_check_fragment_file_fd(i, fds[i]); ++ /* Sleep */ ++ if (tests_sleep_parameter > 0) { ++ unsigned us = tests_sleep_parameter * 1000; ++ unsigned rand_divisor = RAND_MAX / us; ++ unsigned s = (us / 2) + (rand() / rand_divisor); ++ usleep(s); ++ } ++ /* Close orphans */ ++ for (i = 0; i < n; i++) ++ CHECK(close(fds[i]) != -1); ++ /* Break if repeat count exceeded */ ++ if (tests_repeat_parameter > 0 && --repeat <= 0) ++ break; ++ } ++ CHECK(tests_count_files_in_dir(".") == 0); ++ CHECK(chdir("..") != -1); ++ CHECK(rmdir(dir_name) != -1); ++} ++ ++/* Title of this test */ ++ ++const char *orph_get_title(void) ++{ ++ return "Create many open unlinked files"; ++} ++ ++/* Description of this test */ ++ ++const char *orph_get_description(void) ++{ ++ return ++ "Create a directory named orph_test_dir_pid, where " \ ++ "pid is the process id. Within that directory, " \ ++ "create files and keep them open and unlink them. " \ ++ "Create as many files as possible until the file system is " \ ++ "full or the maximum allowed open files is reached. " \ ++ "If a sleep value is specified, the process sleeps. " \ ++ "The sleep value is given by the -p or --sleep option, " \ ++ "otherwise it defaults to 0. " \ ++ "Sleep is specified in milliseconds. " \ ++ "Then close the files. " \ ++ "If a repeat count is specified, then the task repeats " \ ++ "that number of times. " \ ++ "The repeat count is given by the -n or --repeat option, " \ ++ "otherwise it defaults to 1. " \ ++ "A repeat count of zero repeats forever. " \ ++ "Finally remove the directory."; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Set default test repetition */ ++ tests_repeat_parameter = 1; ++ ++ /* Set default test sleep */ ++ tests_sleep_parameter = 0; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, orph_get_title(), ++ orph_get_description(), "nps"); ++ if (!run_test) ++ return 1; ++ /* Change directory to the file system and check it is ok for testing */ ++ tests_check_test_file_system(); ++ /* Do the actual test */ ++ orph(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/test_1.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/test_1.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/test_1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/test_1.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,150 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++void test_1(void) ++{ ++ int fd; ++ pid_t pid; ++ uint64_t i; ++ uint64_t block; ++ uint64_t actual_size; ++ char name[256]; ++ char old[16]; ++ char buf[16]; ++ off_t old_len; ++ char dir_name[256]; ++ ++ /* Create a directory to test in */ ++ pid = getpid(); ++ tests_cat_pid(dir_name, "test_1_test_dir_", pid); ++ if (chdir(dir_name) == -1) ++ CHECK(mkdir(dir_name, 0777) != -1); ++ CHECK(chdir(dir_name) != -1); ++ /* Create a file that fills half the free space on the file system */ ++ tests_create_file("big_file", tests_get_big_file_size(1,2)); ++ CHECK(tests_count_files_in_dir(".") == 1); ++ fd = open("big_file", O_RDWR | tests_maybe_sync_flag()); ++ CHECK(fd != -1); ++ CHECK(read(fd, old, 5) == 5); ++ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1); ++ CHECK(write(fd,"start", 5) == 5); ++ CHECK(lseek(fd,0,SEEK_END) != (off_t) -1); ++ CHECK(write(fd, "end", 3) == 3); ++ tests_maybe_sync(fd); ++ /* Delete the file while it is still open */ ++ tests_delete_file("big_file"); ++ CHECK(tests_count_files_in_dir(".") == 0); ++ /* Create files to file up the file system */ ++ for (block = 1000000, i = 1; ; block /= 10) { ++ while (i != 0) { ++ sprintf(name, "fill_up_%llu", i); ++ actual_size = tests_create_file(name, block); ++ if (actual_size != 0) ++ ++i; ++ if (actual_size != block) ++ break; ++ } ++ if (block == 1) ++ break; ++ } ++ /* Check the big file */ ++ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1); ++ CHECK(read(fd, buf, 5) == 5); ++ CHECK(strncmp(buf, "start", 5) == 0); ++ CHECK(lseek(fd, -3, SEEK_END) != (off_t) -1); ++ CHECK(read(fd, buf, 3) == 3); ++ CHECK(strncmp(buf, "end", 3) == 0); ++ /* Check the other files and delete them */ ++ i -= 1; ++ CHECK(tests_count_files_in_dir(".") == i); ++ for (; i > 0; --i) { ++ sprintf(name, "fill_up_%llu", i); ++ tests_check_filled_file(name); ++ tests_delete_file(name); ++ } ++ CHECK(tests_count_files_in_dir(".") == 0); ++ /* Check the big file again */ ++ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1); ++ CHECK(read(fd, buf, 5) == 5); ++ CHECK(strncmp(buf, "start", 5) == 0); ++ CHECK(lseek(fd, -3, SEEK_END) != (off_t) -1); ++ CHECK(read(fd, buf, 3) == 3); ++ CHECK(strncmp(buf, "end", 3) == 0); ++ CHECK(lseek(fd, 0, SEEK_SET) != (off_t) -1); ++ CHECK(write(fd,old, 5) == 5); ++ old_len = lseek(fd, -3, SEEK_END); ++ CHECK(old_len != (off_t) -1); ++ CHECK(ftruncate(fd,old_len) != -1); ++ tests_check_filled_file_fd(fd); ++ /* Close the big file*/ ++ CHECK(close(fd) != -1); ++ CHECK(tests_count_files_in_dir(".") == 0); ++ CHECK(chdir("..") != -1); ++ CHECK(rmdir(dir_name) != -1); ++} ++ ++/* Title of this test */ ++ ++const char *test_1_get_title(void) ++{ ++ return "Fill file system while holding deleted big file descriptor"; ++} ++ ++/* Description of this test */ ++ ++const char *test_1_get_description(void) ++{ ++ return ++ "Create a directory named test_1_test_dir_pid, where " \ ++ "pid is the process id. Within that directory, " \ ++ "create a big file (approx. half the file system in size), " \ ++ "open it, and unlink it. " \ ++ "Create many smaller files until the file system is full. " \ ++ "Check the big file is ok. " \ ++ "Delete all the smaller files. " \ ++ "Check the big file again. " \ ++ "Finally delete the big file and directory."; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, test_1_get_title(), ++ test_1_get_description(), "s"); ++ if (!run_test) ++ return 1; ++ /* Change directory to the file system and check it is ok for testing */ ++ tests_check_test_file_system(); ++ /* Do the actual test */ ++ test_1(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/test_2.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/test_2.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/simple/test_2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/simple/test_2.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,201 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++void test_2(void) ++{ ++ pid_t pid; ++ int create, full; ++ unsigned i, number_of_files; ++ unsigned growth; ++ unsigned size; ++ uint64_t big_file_size; ++ int fd; ++ off_t offset; ++ char dir_name[256]; ++ ++ /* Create a directory to test in */ ++ pid = getpid(); ++ tests_cat_pid(dir_name, "test_2_test_dir_", pid); ++ if (chdir(dir_name) == -1) ++ CHECK(mkdir(dir_name, 0777) != -1); ++ CHECK(chdir(dir_name) != -1); ++ /* Create up to 1000 files appending 400 bytes at a time to each file */ ++ /* until the file system is full.*/ ++ create = 1; ++ full = 0; ++ number_of_files = 1000; ++ while (!full) { ++ for (i = 0; i < number_of_files; ++i) { ++ growth = tests_append_to_fragment_file(i, 400, create); ++ if (!growth) { ++ full = 1; ++ if (create) ++ number_of_files = i; ++ break; ++ } ++ } ++ create = 0; ++ } ++ /* Check the files */ ++ CHECK(tests_count_files_in_dir(".") == number_of_files); ++ for (i = 0; i < number_of_files; ++i) ++ tests_check_fragment_file(i); ++ /* Delete half of them */ ++ for (i = 1; i < number_of_files; i += 2) ++ tests_delete_fragment_file(i); ++ /* Check them again */ ++ CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2); ++ for (i = 0; i < number_of_files; i += 2) ++ tests_check_fragment_file(i); ++ CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2); ++ /* Create a big file that fills two thirds of the free space */ ++ big_file_size = tests_get_big_file_size(2,3); ++ /* Check the big file */ ++ tests_create_file("big_file", big_file_size); ++ CHECK(tests_count_files_in_dir(".") == 1 + (number_of_files + 1) / 2); ++ tests_check_filled_file("big_file"); ++ /* Open the big file */ ++ fd = open("big_file",O_RDWR | tests_maybe_sync_flag()); ++ CHECK(fd != -1); ++ /* Delete the big file while it is still open */ ++ tests_delete_file("big_file"); ++ /* Check the big file again */ ++ CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2); ++ tests_check_filled_file_fd(fd); ++ ++ /* Write parts of the files and check them */ ++ ++ offset = 100; /* Offset to write at, in the small files */ ++ size = 200; /* Number of bytes to write at the offset */ ++ ++ for (i = 0; i < number_of_files; i += 2) ++ tests_overwite_fragment_file(i, offset, size); ++ /* Rewrite the big file entirely */ ++ tests_write_filled_file(fd, 0, big_file_size); ++ for (i = 0; i < number_of_files; i += 2) ++ tests_check_fragment_file(i); ++ tests_check_filled_file_fd(fd); ++ ++ offset = 300; /* Offset to write at, in the small files */ ++ size = 400; /* Number of bytes to write at the offset */ ++ ++ for (i = 0; i < number_of_files; i += 2) ++ tests_overwite_fragment_file(i, offset, size); ++ /* Rewrite the big file entirely */ ++ tests_write_filled_file(fd, 0, big_file_size); ++ for (i = 0; i < number_of_files; i += 2) ++ tests_check_fragment_file(i); ++ tests_check_filled_file_fd(fd); ++ ++ offset = 110; /* Offset to write at, in the small files */ ++ size = 10; /* Number of bytes to write at the offset */ ++ ++ for (i = 0; i < number_of_files; i += 2) ++ tests_overwite_fragment_file(i, offset, size); ++ /* Rewrite the big file entirely */ ++ tests_write_filled_file(fd, 0, big_file_size); ++ for (i = 0; i < number_of_files; i += 2) ++ tests_check_fragment_file(i); ++ tests_check_filled_file_fd(fd); ++ ++ offset = 10; /* Offset to write at, in the small files */ ++ size = 1000; /* Number of bytes to write at the offset */ ++ ++ for (i = 0; i < number_of_files; i += 2) ++ tests_overwite_fragment_file(i, offset, size); ++ /* Rewrite the big file entirely */ ++ tests_write_filled_file(fd, 0, big_file_size); ++ for (i = 0; i < number_of_files; i += 2) ++ tests_check_fragment_file(i); ++ tests_check_filled_file_fd(fd); ++ ++ offset = 0; /* Offset to write at, in the small files */ ++ size = 100000; /* Number of bytes to write at the offset */ ++ ++ for (i = 0; i < number_of_files; i += 2) ++ tests_overwite_fragment_file(i, offset, size); ++ /* Rewrite the big file entirely */ ++ tests_write_filled_file(fd, 0, big_file_size); ++ for (i = 0; i < number_of_files; i += 2) ++ tests_check_fragment_file(i); ++ tests_check_filled_file_fd(fd); ++ ++ /* Close the big file*/ ++ CHECK(close(fd) != -1); ++ /* Check the small files */ ++ CHECK(tests_count_files_in_dir(".") == (number_of_files + 1) / 2); ++ for (i = 0; i < number_of_files; i += 2) ++ tests_check_fragment_file(i); ++ /* Delete the small files */ ++ for (i = 0; i < number_of_files; i += 2) ++ tests_delete_fragment_file(i); ++ CHECK(tests_count_files_in_dir(".") == 0); ++ CHECK(chdir("..") != -1); ++ CHECK(rmdir(dir_name) != -1); ++} ++ ++/* Title of this test */ ++ ++const char *test_2_get_title(void) ++{ ++ return "Repeated write many small files and one big deleted file"; ++} ++ ++/* Description of this test */ ++ ++const char *test_2_get_description(void) ++{ ++ return ++ "Create a directory named test_2_test_dir_pid, where " \ ++ "pid is the process id. Within that directory, " \ ++ "create about 1000 files. Append 400 bytes to each until " \ ++ "the file system is full. Then delete half of them. Then " \ ++ "create a big file that uses about 2/3 of the remaining free " \ ++ "space. Get a file descriptor for the big file, and delete " \ ++ "the big file. Then repeatedly write to the small files " \ ++ "and the big file. " \ ++ "Finally delete the big file and directory."; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, test_2_get_title(), ++ test_2_get_description(), "s"); ++ if (!run_test) ++ return 1; ++ /* Change directory to the file system and check it is ok for testing */ ++ tests_check_test_file_system(); ++ /* Do the actual test */ ++ test_2(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/Makefile linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/Makefile +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,11 @@ ++ ++SUBDIRS = atoms ++ ++all tests: $(SUBDIRS) ++ ++clean: $(SUBDIRS) ++ rm -rf run_pdf_test_file_* ++ ++.PHONY: $(SUBDIRS) ++$(SUBDIRS): ++ $(MAKE) -C $@ $(MAKECMDGOALS) +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/Makefile linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/Makefile +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,40 @@ ++ ++ifeq ($(origin CC),default) ++CC = gcc ++endif ++ ++CFLAGS := $(CFLAGS) -Wall -g -O2 -I../../lib ++ ++LDFLAGS := $(LDFLAGS) ++ ++TARGETS = stress_1 \ ++ stress_2 \ ++ stress_3 \ ++ pdfrun \ ++ rndwrite00 \ ++ fwrite00 \ ++ rmdir00 \ ++ rndrm00 \ ++ rndrm99 \ ++ gcd_hupper ++ ++all: $(TARGETS) ++ ++$(TARGETS): ../../lib/tests.o ++ ++../lib/tests.o: ../../lib/tests.h ++ ++clean: ++ rm -f *.o $(TARGETS) run_pdf_test_file ++ ++tests: all ++ ./stress_1 -e ++ ./stress_2 ++ ./stress_3 -e ++ ./pdfrun ++ ./rndwrite00 -e ++ ./fwrite00 ++ ./rmdir00 ++ ./rndrm00 ++ ./rndrm99 ++ ./gcd_hupper +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/fwrite00.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/fwrite00.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/fwrite00.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/fwrite00.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,209 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++#define WRITE_BUFFER_SIZE 32768 ++ ++#define HOLE_BLOCK_SIZE 10000000 ++ ++void filestress00(void) ++{ ++ int fd, i, deleted; ++ pid_t pid; ++ ssize_t written; ++ int64_t remains; ++ int64_t repeat; ++ size_t block; ++ char file_name[256]; ++ char buf[WRITE_BUFFER_SIZE]; ++ ++ fd = -1; ++ deleted = 1; ++ pid = getpid(); ++ tests_cat_pid(file_name, "filestress00_test_file_", pid); ++ srand(pid); ++ repeat = tests_repeat_parameter; ++ for (;;) { ++ /* Open the file */ ++ if (fd == -1) { ++ fd = open(file_name, O_CREAT | O_WRONLY, ++ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); ++ CHECK(fd != -1); ++ deleted = 0; ++ if (tests_unlink_flag) { ++ CHECK(unlink(file_name) != -1); ++ deleted = 1; ++ } ++ } ++ /* Get a different set of random data */ ++ for (i = 0; i < WRITE_BUFFER_SIZE;++i) ++ buf[i] = rand(); ++ if (tests_hole_flag) { ++ /* Make a hole */ ++ CHECK(lseek(fd, tests_size_parameter, SEEK_SET) != -1); ++ written = write(fd, "!", 1); ++ if (written <= 0) { ++ /* File system full */ ++ CHECK(errno == ENOSPC); ++ errno = 0; ++ } ++ CHECK(lseek(fd, 0, SEEK_SET) != -1); ++ /* Write at set points into the hole */ ++ remains = tests_size_parameter; ++ while (remains > HOLE_BLOCK_SIZE) { ++ CHECK(lseek(fd, HOLE_BLOCK_SIZE, ++ SEEK_CUR) != -1); ++ written = write(fd, "!", 1); ++ remains -= HOLE_BLOCK_SIZE; ++ if (written <= 0) { ++ /* File system full */ ++ CHECK(errno == ENOSPC); ++ errno = 0; ++ break; ++ } ++ } ++ } else { ++ /* Write data into the file */ ++ CHECK(lseek(fd, 0, SEEK_SET) != -1); ++ remains = tests_size_parameter; ++ while (remains > 0) { ++ if (remains > WRITE_BUFFER_SIZE) ++ block = WRITE_BUFFER_SIZE; ++ else ++ block = remains; ++ written = write(fd, buf, block); ++ if (written <= 0) { ++ /* File system full */ ++ CHECK(errno == ENOSPC); ++ errno = 0; ++ break; ++ } ++ remains -= written; ++ } ++ } ++ /* Break if repeat count exceeded */ ++ if (tests_repeat_parameter > 0 && --repeat <= 0) ++ break; ++ /* Close if tests_close_flag */ ++ if (tests_close_flag) { ++ CHECK(close(fd) != -1); ++ fd = -1; ++ } ++ /* Sleep */ ++ if (tests_sleep_parameter > 0) { ++ unsigned us = tests_sleep_parameter * 1000; ++ unsigned rand_divisor = RAND_MAX / us; ++ unsigned s = (us / 2) + (rand() / rand_divisor); ++ usleep(s); ++ } ++ /* Delete if tests_delete flag */ ++ if (!deleted && tests_delete_flag) { ++ CHECK(unlink(file_name) != -1); ++ deleted = 1; ++ } ++ } ++ CHECK(close(fd) != -1); ++ /* Sleep */ ++ if (tests_sleep_parameter > 0) { ++ unsigned us = tests_sleep_parameter * 1000; ++ unsigned rand_divisor = RAND_MAX / us; ++ unsigned s = (us / 2) + (rand() / rand_divisor); ++ usleep(s); ++ } ++ /* Tidy up */ ++ if (!deleted) ++ CHECK(unlink(file_name) != -1); ++} ++ ++/* Title of this test */ ++ ++const char *filestress00_get_title(void) ++{ ++ return "File stress test 00"; ++} ++ ++/* Description of this test */ ++ ++const char *filestress00_get_description(void) ++{ ++ return ++ "Create a file named filestress00_test_file_pid, where " \ ++ "pid is the process id. If the unlink option " \ ++ "(-u or --unlink) is specified, " \ ++ "unlink the file while holding the open file descriptor. " \ ++ "If the hole option (-o or --hole) is specified, " \ ++ "write a single character at the end of the file, creating a " \ ++ "hole. " \ ++ "Write a single character in the hole every 10 million " \ ++ "bytes. " \ ++ "If the hole option is not specified, then the file is " \ ++ "filled with random data. " \ ++ "If the close option (-c or --close) is specified the file " \ ++ "is closed. " \ ++ "If a sleep value is specified, the process sleeps. " \ ++ "If the delete option (-e or --delete) is specified, then " \ ++ "the file is deleted. " \ ++ "If a repeat count is specified, then the task repeats " \ ++ "that number of times. " \ ++ "The repeat count is given by the -n or --repeat option, " \ ++ "otherwise it defaults to 1. " \ ++ "A repeat count of zero repeats forever. " \ ++ "The file size is given by the -z or --size option, " \ ++ "otherwise it defaults to 1000000. " \ ++ "The sleep value is given by the -p or --sleep option, " \ ++ "otherwise it defaults to 0. " \ ++ "Sleep is specified in milliseconds."; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Set default test file size */ ++ tests_size_parameter = 1000000; ++ ++ /* Set default test repetition */ ++ tests_repeat_parameter = 1; ++ ++ /* Set default test sleep */ ++ tests_sleep_parameter = 0; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, filestress00_get_title(), ++ filestress00_get_description(), "znpuoce"); ++ if (!run_test) ++ return 1; ++ /* Change directory to the file system and check it is ok for testing */ ++ tests_check_test_file_system(); ++ /* Do the actual test */ ++ filestress00(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/gcd_hupper.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/gcd_hupper.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/gcd_hupper.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/gcd_hupper.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,259 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++#define MAX_NAME_SIZE 1024 ++ ++struct gcd_pid ++{ ++ struct gcd_pid *next; ++ int pid; ++ char *name; ++ int mtd_index; ++}; ++ ++struct gcd_pid *gcd_pid_list = NULL; ++ ++int add_gcd_pid(const char *number) ++{ ++ int pid; ++ FILE *f; ++ char file_name[MAX_NAME_SIZE]; ++ char program_name[MAX_NAME_SIZE]; ++ ++ pid = atoi(number); ++ if (pid <= 0) ++ return 0; ++ snprintf(file_name, MAX_NAME_SIZE, "/proc/%s/stat", number); ++ f = fopen(file_name, "r"); ++ if (f == NULL) ++ return 0; ++ if (fscanf(f, "%d %s", &pid, program_name) != 2) { ++ fclose(f); ++ return 0; ++ } ++ if (strncmp(program_name, "(jffs2_gcd_mtd", 14) != 0) ++ pid = 0; ++ if (pid) { ++ size_t sz; ++ struct gcd_pid *g; ++ ++ sz = sizeof(struct gcd_pid); ++ g = (struct gcd_pid *) malloc(sz); ++ g->pid = pid; ++ g->name = (char *) malloc(strlen(program_name) + 1); ++ if (g->name) ++ strcpy(g->name, program_name); ++ else ++ exit(1); ++ g->mtd_index = atoi(program_name + 14); ++ g->next = gcd_pid_list; ++ gcd_pid_list = g; ++ } ++ fclose(f); ++ return pid; ++} ++ ++int get_pid_list(void) ++{ ++ DIR *dir; ++ struct dirent *entry; ++ ++ dir = opendir("/proc"); ++ if (dir == NULL) ++ return 1; ++ for (;;) { ++ entry = readdir(dir); ++ if (entry) { ++ if (strcmp(".",entry->d_name) != 0 && ++ strcmp("..",entry->d_name) != 0) ++ add_gcd_pid(entry->d_name); ++ } else ++ break; ++ } ++ closedir(dir); ++ return 0; ++} ++ ++int parse_index_number(const char *name) ++{ ++ const char *p, *q; ++ int all_zero; ++ int index; ++ ++ p = name; ++ while (*p && !isdigit(*p)) ++ ++p; ++ if (!*p) ++ return -1; ++ all_zero = 1; ++ for (q = p; *q; ++q) { ++ if (!isdigit(*q)) ++ return -1; ++ if (*q != '0') ++ all_zero = 0; ++ } ++ if (all_zero) ++ return 0; ++ index = atoi(p); ++ if (index <= 0) ++ return -1; ++ return index; ++} ++ ++int get_mtd_index(void) ++{ ++ FILE *f; ++ struct mntent *entry; ++ struct stat f_info; ++ struct stat curr_f_info; ++ int found; ++ int mtd_index = -1; ++ ++ if (stat(tests_file_system_mount_dir, &f_info) == -1) ++ return -1; ++ f = fopen("/proc/mounts", "rb"); ++ if (!f) ++ f = fopen("/etc/mtab", "rb"); ++ if (f == NULL) ++ return -1; ++ found = 0; ++ for (;;) { ++ entry = getmntent(f); ++ if (!entry) ++ break; ++ if (stat(entry->mnt_dir, &curr_f_info) == -1) ++ continue; ++ if (f_info.st_dev == curr_f_info.st_dev) { ++ int i; ++ ++ i = parse_index_number(entry->mnt_fsname); ++ if (i != -1) { ++ if (found && i != mtd_index) ++ return -1; ++ found = 1; ++ mtd_index = i; ++ } ++ } ++ } ++ fclose(f); ++ return mtd_index; ++} ++ ++int get_gcd_pid() ++{ ++ struct gcd_pid *g; ++ int mtd_index; ++ ++ if (get_pid_list()) ++ return 0; ++ mtd_index = get_mtd_index(); ++ if (mtd_index == -1) ++ return 0; ++ for (g = gcd_pid_list; g; g = g->next) ++ if (g->mtd_index == mtd_index) ++ return g->pid; ++ return 0; ++} ++ ++void gcd_hupper(void) ++{ ++ int64_t repeat; ++ int pid; ++ ++ pid = get_gcd_pid(); ++ CHECK(pid != 0); ++ repeat = tests_repeat_parameter; ++ for (;;) { ++ CHECK(kill(pid, SIGHUP) != -1); ++ /* Break if repeat count exceeded */ ++ if (tests_repeat_parameter > 0 && --repeat <= 0) ++ break; ++ /* Sleep */ ++ if (tests_sleep_parameter > 0) { ++ unsigned us = tests_sleep_parameter * 1000; ++ unsigned rand_divisor = RAND_MAX / us; ++ unsigned s = (us / 2) + (rand() / rand_divisor); ++ usleep(s); ++ } ++ } ++} ++ ++/* Title of this test */ ++ ++const char *gcd_hupper_get_title(void) ++{ ++ return "Send HUP signals to gcd"; ++} ++ ++/* Description of this test */ ++ ++const char *gcd_hupper_get_description(void) ++{ ++ return ++ "Determine the PID of the gcd process. " \ ++ "Send it SIGHUP (may require root privileges). " \ ++ "If a sleep value is specified, the process sleeps. " \ ++ "If a repeat count is specified, then the task repeats " \ ++ "that number of times. " \ ++ "The repeat count is given by the -n or --repeat option, " \ ++ "otherwise it defaults to 1. " \ ++ "A repeat count of zero repeats forever. " \ ++ "The sleep value is given by the -p or --sleep option, " \ ++ "otherwise it defaults to 1. " ++ "Sleep is specified in milliseconds."; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Set default test repetition */ ++ tests_repeat_parameter = 1; ++ ++ /* Set default test sleep */ ++ tests_sleep_parameter = 1; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, gcd_hupper_get_title(), ++ gcd_hupper_get_description(), "np"); ++ if (!run_test) ++ return 1; ++ /* Change directory to the file system and check it is ok for testing */ ++ tests_check_test_file_system(); ++ /* Do the actual test */ ++ gcd_hupper(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/pdfrun.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/pdfrun.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/pdfrun.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/pdfrun.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,143 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++#define WRITE_BUFFER_SIZE 32768 ++ ++void adjust_size(void) ++{ ++ char dummy[1024]; ++ unsigned long total_memory; ++ FILE *f; ++ ++ total_memory = 0; ++ f = fopen("/proc/meminfo", "r"); ++ fscanf(f, "%s %lu", dummy, &total_memory); ++ fclose(f); ++ if (total_memory > 0 && tests_size_parameter > total_memory / 2) ++ tests_size_parameter = total_memory / 2; ++} ++ ++void run_pdf(void) ++{ ++ int fd, i; ++ pid_t pid; ++ int64_t repeat; ++ ssize_t written; ++ int64_t remains; ++ size_t block; ++ char file_name[256]; ++ char buf[WRITE_BUFFER_SIZE]; ++ ++ if (tests_fs_is_currfs()) ++ return; ++ adjust_size(); ++ pid = getpid(); ++ tests_cat_pid(file_name, "run_pdf_test_file_", pid); ++ fd = open(file_name, O_CREAT | O_WRONLY, ++ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); ++ CHECK(fd != -1); ++ pid = getpid(); ++ srand(pid); ++ repeat = tests_repeat_parameter; ++ for (;;) { ++ for (i = 0; i < WRITE_BUFFER_SIZE;++i) ++ buf[i] = rand(); ++ remains = tests_size_parameter; ++ while (remains > 0) { ++ if (remains > WRITE_BUFFER_SIZE) ++ block = WRITE_BUFFER_SIZE; ++ else ++ block = remains; ++ written = write(fd, buf, block); ++ if (written <= 0) { ++ CHECK(errno == ENOSPC); /* File system full */ ++ errno = 0; ++ break; ++ } ++ remains -= written; ++ } ++ /* Break if repeat count exceeded */ ++ if (tests_repeat_parameter > 0 && --repeat <= 0) ++ break; ++ CHECK(lseek(fd, 0, SEEK_SET) == 0); ++ } ++ CHECK(close(fd) != -1); ++ CHECK(unlink(file_name) != -1); ++} ++ ++/* Title of this test */ ++ ++const char *run_pdf_get_title(void) ++{ ++ return "Create / overwrite a large file in the current directory"; ++} ++ ++/* Description of this test */ ++ ++const char *run_pdf_get_description(void) ++{ ++ return ++ "Create a file named run_pdf_test_file_pid, " \ ++ "where pid is the process id. The file is created " \ ++ "in the current directory, " \ ++ "if the current directory is NOT on the test " \ ++ "file system, otherwise no action is taken. " \ ++ "If a repeat count is specified, then the task repeats " \ ++ "that number of times. " \ ++ "The repeat count is given by the -n or --repeat option, " \ ++ "otherwise it defaults to 1. " \ ++ "A repeat count of zero repeats forever. " \ ++ "The size is given by the -z or --size option, " \ ++ "otherwise it defaults to 1000000. " \ ++ "The size is adjusted so that it is not more than " \ ++ "half the size of total memory."; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Set default test file size */ ++ tests_size_parameter = 1000000; ++ ++ /* Set default test repetition */ ++ tests_repeat_parameter = 1; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, run_pdf_get_title(), ++ run_pdf_get_description(), "zn"); ++ if (!run_test) ++ return 1; ++ /* Do the actual test */ ++ run_pdf(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rmdir00.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rmdir00.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rmdir00.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rmdir00.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,133 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++void rmdir00(void) ++{ ++ int64_t repeat; ++ int64_t size, this_size; ++ pid_t pid; ++ char dir_name[256]; ++ ++ /* Create a directory to test in */ ++ pid = getpid(); ++ tests_cat_pid(dir_name, "rmdir00_test_dir_", pid); ++ if (chdir(dir_name) == -1) ++ CHECK(mkdir(dir_name, 0777) != -1); ++ CHECK(chdir(dir_name) != -1); ++ /* Repeat loop */ ++ repeat = tests_repeat_parameter; ++ size = 0; ++ for (;;) { ++ /* Remove everything in the directory */ ++ tests_clear_dir("."); ++ /* Fill with sub-dirs and small files */ ++ do { ++ this_size = tests_create_entry(NULL); ++ if (!this_size) ++ break; ++ size += this_size; ++ } while (this_size && ++ (tests_size_parameter == 0 || ++ size < tests_size_parameter)); ++ /* Break if repeat count exceeded */ ++ if (tests_repeat_parameter > 0 && --repeat <= 0) ++ break; ++ /* Sleep */ ++ if (tests_sleep_parameter > 0) { ++ unsigned us = tests_sleep_parameter * 1000; ++ unsigned rand_divisor = RAND_MAX / us; ++ unsigned s = (us / 2) + (rand() / rand_divisor); ++ usleep(s); ++ } ++ } ++ /* Tidy up by removing everything */ ++ tests_clear_dir("."); ++ CHECK(chdir("..") != -1); ++ CHECK(rmdir(dir_name) != -1); ++} ++ ++/* Title of this test */ ++ ++const char *rmdir00_get_title(void) ++{ ++ return "Create and remove directories and files"; ++} ++ ++/* Description of this test */ ++ ++const char *rmdir00_get_description(void) ++{ ++ return ++ "Create a directory named rmdir00_test_dir_pid, where " \ ++ "pid is the process id. Within that directory, create " \ ++ "a number of sub-directories and small files. " \ ++ "The total size of all sub-directories and files " \ ++ "is specified by the size parameter. " \ ++ "The size parameter is given by the -z or --size option, " \ ++ "otherwise it defaults to 1000000. " \ ++ "A size of zero fills the file system until there is no " ++ "space left. " \ ++ "The task repeats, sleeping in between each iteration, " \ ++ "and then removing the sub-directories and files created " \ ++ "during the last iteration. " \ ++ "The repeat count is set by the -n or --repeat option, " \ ++ "otherwise it defaults to 1. " \ ++ "A repeat count of zero repeats forever. " \ ++ "The sleep value is given by the -p or --sleep option, " \ ++ "otherwise it defaults to 0. " ++ "Sleep is specified in milliseconds."; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Set default test size */ ++ tests_size_parameter = 1000000; ++ ++ /* Set default test repetition */ ++ tests_repeat_parameter = 1; ++ ++ /* Set default test sleep */ ++ tests_sleep_parameter = 0; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, rmdir00_get_title(), ++ rmdir00_get_description(), "znp"); ++ if (!run_test) ++ return 1; ++ /* Change directory to the file system and check it is ok for testing */ ++ tests_check_test_file_system(); ++ /* Do the actual test */ ++ rmdir00(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndrm00.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndrm00.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndrm00.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndrm00.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,157 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++void rndrm00(void) ++{ ++ int64_t repeat; ++ int64_t size, this_size; ++ pid_t pid; ++ char dir_name[256]; ++ ++ /* Create a directory to test in */ ++ pid = getpid(); ++ tests_cat_pid(dir_name, "rndrm00_test_dir_", pid); ++ if (chdir(dir_name) == -1) ++ CHECK(mkdir(dir_name, 0777) != -1); ++ CHECK(chdir(dir_name) != -1); ++ /* Repeat loop */ ++ repeat = tests_repeat_parameter; ++ size = 0; ++ for (;;) { ++ /* Create and remove sub-dirs and small files, */ ++ /* but tending to grow */ ++ do { ++ if (tests_random_no(3)) { ++ this_size = tests_create_entry(NULL); ++ if (!this_size) ++ break; ++ size += this_size; ++ } else { ++ this_size = tests_remove_entry(); ++ size -= this_size; ++ if (size < 0) ++ size = 0; ++ if (!this_size) ++ this_size = 1; ++ } ++ } while (this_size && ++ (tests_size_parameter == 0 || ++ size < tests_size_parameter)); ++ /* Create and remove sub-dirs and small files, but */ ++ /* but tending to shrink */ ++ do { ++ if (!tests_random_no(3)) { ++ this_size = tests_create_entry(NULL); ++ size += this_size; ++ } else { ++ this_size = tests_remove_entry(); ++ size -= this_size; ++ if (size < 0) ++ size = 0; ++ } ++ } while ((tests_size_parameter != 0 && ++ size > tests_size_parameter / 10) || ++ (tests_size_parameter == 0 && size > 100000)); ++ /* Break if repeat count exceeded */ ++ if (tests_repeat_parameter > 0 && --repeat <= 0) ++ break; ++ /* Sleep */ ++ if (tests_sleep_parameter > 0) { ++ unsigned us = tests_sleep_parameter * 1000; ++ unsigned rand_divisor = RAND_MAX / us; ++ unsigned s = (us / 2) + (rand() / rand_divisor); ++ usleep(s); ++ } ++ } ++ /* Tidy up by removing everything */ ++ tests_clear_dir("."); ++ CHECK(chdir("..") != -1); ++ CHECK(rmdir(dir_name) != -1); ++} ++ ++/* Title of this test */ ++ ++const char *rndrm00_get_title(void) ++{ ++ return "Randomly create and remove directories and files"; ++} ++ ++/* Description of this test */ ++ ++const char *rndrm00_get_description(void) ++{ ++ return ++ "Create a directory named rndrm00_test_dir_pid, where " \ ++ "pid is the process id. Within that directory, " \ ++ "randomly create and remove " \ ++ "a number of sub-directories and small files, " \ ++ "but do more creates than removes. " \ ++ "When the total size of all sub-directories and files " \ ++ "is greater than the size specified by the size parameter, " \ ++ "start to do more removes than creates. " \ ++ "The size parameter is given by the -z or --size option, " \ ++ "otherwise it defaults to 1000000. " \ ++ "A size of zero fills the file system until there is no " ++ "space left. " \ ++ "The task repeats, sleeping in between each iteration. " \ ++ "The repeat count is set by the -n or --repeat option, " \ ++ "otherwise it defaults to 1. " \ ++ "A repeat count of zero repeats forever. " \ ++ "The sleep value is given by the -p or --sleep option, " \ ++ "otherwise it defaults to 0. " ++ "Sleep is specified in milliseconds."; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Set default test size */ ++ tests_size_parameter = 1000000; ++ ++ /* Set default test repetition */ ++ tests_repeat_parameter = 1; ++ ++ /* Set default test sleep */ ++ tests_sleep_parameter = 0; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, rndrm00_get_title(), ++ rndrm00_get_description(), "znp"); ++ if (!run_test) ++ return 1; ++ /* Change directory to the file system and check it is ok for testing */ ++ tests_check_test_file_system(); ++ /* Do the actual test */ ++ rndrm00(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndrm99.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndrm99.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndrm99.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndrm99.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,431 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++uint32_t files_created = 0; ++uint32_t files_removed = 0; ++uint32_t dirs_created = 0; ++uint32_t dirs_removed = 0; ++int64_t *size_ptr = 0; ++ ++void display_stats(void) ++{ ++ printf( "\nrndrm99 stats:\n" ++ "\tNumber of files created = %u\n" ++ "\tNumber of files deleted = %u\n" ++ "\tNumber of directories created = %u\n" ++ "\tNumber of directories deleted = %u\n" ++ "\tCurrent net size of creates and deletes = %lld\n", ++ (unsigned) files_created, ++ (unsigned) files_removed, ++ (unsigned) dirs_created, ++ (unsigned) dirs_removed, ++ (long long) (size_ptr ? *size_ptr : 0)); ++ fflush(stdout); ++} ++ ++struct timeval tv_before; ++struct timeval tv_after; ++ ++void before(void) ++{ ++ CHECK(gettimeofday(&tv_before, NULL) != -1); ++} ++ ++void after(const char *msg) ++{ ++ time_t diff; ++ CHECK(gettimeofday(&tv_after, NULL) != -1); ++ diff = tv_after.tv_sec - tv_before.tv_sec; ++ if (diff >= 8) { ++ printf("\nrndrm99: the following fn took more than 8 seconds: %s (took %u secs)\n",msg,(unsigned) diff); ++ fflush(stdout); ++ display_stats(); ++ } ++} ++ ++#define WRITE_BUFFER_SIZE 32768 ++ ++static char write_buffer[WRITE_BUFFER_SIZE]; ++ ++static void init_write_buffer() ++{ ++ static int init = 0; ++ ++ if (!init) { ++ int i, d; ++ uint64_t u; ++ ++ u = RAND_MAX; ++ u += 1; ++ u /= 256; ++ d = (int) u; ++ srand(1); ++ for (i = 0; i < WRITE_BUFFER_SIZE; ++i) ++ write_buffer[i] = rand() / d; ++ init = 1; ++ } ++} ++ ++/* Write size random bytes into file descriptor fd at the current position, ++ returning the number of bytes actually written */ ++uint64_t fill_file(int fd, uint64_t size) ++{ ++ ssize_t written; ++ size_t sz; ++ unsigned start = 0, length; ++ uint64_t remains; ++ uint64_t actual_size = 0; ++ ++ init_write_buffer(); ++ remains = size; ++ while (remains > 0) { ++ length = WRITE_BUFFER_SIZE - start; ++ if (remains > length) ++ sz = length; ++ else ++ sz = (size_t) remains; ++ before(); ++ written = write(fd, write_buffer + start, sz); ++ if (written <= 0) { ++ CHECK(errno == ENOSPC); /* File system full */ ++ errno = 0; ++ after("write"); ++ fprintf(stderr,"\nrndrm99: write failed with ENOSPC\n");fflush(stderr); ++ display_stats(); ++ break; ++ } ++ after("write"); ++ remains -= written; ++ actual_size += written; ++ if ((size_t) written == sz) ++ start = 0; ++ else ++ start += written; ++ } ++ return actual_size; ++} ++ ++/* Create a file of size file_size */ ++uint64_t create_file(const char *file_name, uint64_t file_size) ++{ ++ int fd; ++ int flags; ++ mode_t mode; ++ uint64_t actual_size; /* Less than size if the file system is full */ ++ ++ flags = O_CREAT | O_TRUNC | O_WRONLY; ++ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; ++ before(); ++ fd = open(file_name, flags, mode); ++ if (fd == -1 && errno == ENOSPC) { ++ errno = 0; ++ after("open"); ++ fprintf(stderr,"\nrndrm99: open failed with ENOSPC\n");fflush(stderr); ++ display_stats(); ++ return 0; /* File system full */ ++ } ++ CHECK(fd != -1); ++ after("open"); ++ actual_size = fill_file(fd, file_size); ++ before(); ++ CHECK(close(fd) != -1); ++ after("close"); ++ if (file_size != 0 && actual_size == 0) { ++ printf("\nrndrm99: unlinking zero size file\n");fflush(stdout); ++ before(); ++ CHECK(unlink(file_name) != -1); ++ after("unlink (create_file)"); ++ } ++ return actual_size; ++} ++ ++/* Create an empty sub-directory or small file in the current directory */ ++int64_t create_entry(char *return_name) ++{ ++ int fd; ++ char name[256]; ++ int64_t res; ++ ++ for (;;) { ++ sprintf(name, "%u", (unsigned) tests_random_no(10000000)); ++ before(); ++ fd = open(name, O_RDONLY); ++ after("open (create_entry)"); ++ if (fd == -1) ++ break; ++ before(); ++ close(fd); ++ after("close (create_entry)"); ++ } ++ if (return_name) ++ strcpy(return_name, name); ++ if (tests_random_no(2)) { ++ res = create_file(name, tests_random_no(4096)); ++ if (res > 0) ++ files_created += 1; ++ return res; ++ } else { ++ before(); ++ if (mkdir(name, 0777) == -1) { ++ CHECK(errno == ENOSPC); ++ after("mkdir"); ++ errno = 0; ++ fprintf(stderr,"\nrndrm99: mkdir failed with ENOSPC\n");fflush(stderr); ++ display_stats(); ++ return 0; ++ } ++ after("mkdir"); ++ dirs_created += 1; ++ return TESTS_EMPTY_DIR_SIZE; ++ } ++} ++ ++/* Remove a random file of empty sub-directory from the current directory */ ++int64_t remove_entry(void) ++{ ++ DIR *dir; ++ struct dirent *entry; ++ unsigned count = 0, pos; ++ int64_t result = 0; ++ ++ before(); ++ dir = opendir("."); ++ CHECK(dir != NULL); ++ after("opendir"); ++ for (;;) { ++ errno = 0; ++ before(); ++ entry = readdir(dir); ++ if (entry) { ++ after("readdir 1"); ++ if (strcmp(".",entry->d_name) != 0 && ++ strcmp("..",entry->d_name) != 0) ++ ++count; ++ } else { ++ CHECK(errno == 0); ++ after("readdir 1"); ++ break; ++ } ++ } ++ pos = tests_random_no(count); ++ count = 0; ++ before(); ++ rewinddir(dir); ++ after("rewinddir"); ++ for (;;) { ++ errno = 0; ++ before(); ++ entry = readdir(dir); ++ if (!entry) { ++ CHECK(errno == 0); ++ after("readdir 2"); ++ break; ++ } ++ after("readdir 2"); ++ if (strcmp(".",entry->d_name) != 0 && ++ strcmp("..",entry->d_name) != 0) { ++ if (count == pos) { ++ if (entry->d_type == DT_DIR) { ++ before(); ++ tests_clear_dir(entry->d_name); ++ after("tests_clear_dir"); ++ before(); ++ CHECK(rmdir(entry->d_name) != -1); ++ after("rmdir"); ++ result = TESTS_EMPTY_DIR_SIZE; ++ dirs_removed += 1; ++ } else { ++ struct stat st; ++ before(); ++ CHECK(stat(entry->d_name, &st) != -1); ++ after("stat"); ++ result = st.st_size; ++ before(); ++ CHECK(unlink(entry->d_name) != -1); ++ after("unlink"); ++ files_removed += 1; ++ } ++ } ++ ++count; ++ } ++ } ++ before(); ++ CHECK(closedir(dir) != -1); ++ after("closedir"); ++ return result; ++} ++ ++void rndrm99(void) ++{ ++ int64_t repeat, loop_cnt; ++ int64_t size, this_size; ++ pid_t pid; ++ char dir_name[256]; ++ ++ size_ptr = &size; ++ /* Create a directory to test in */ ++ pid = getpid(); ++ tests_cat_pid(dir_name, "rndrm99_test_dir_", pid); ++ if (chdir(dir_name) == -1) ++ CHECK(mkdir(dir_name, 0777) != -1); ++ CHECK(chdir(dir_name) != -1); ++ /* Repeat loop */ ++ repeat = tests_repeat_parameter; ++ size = 0; ++ for (;;) { ++ /* Create and remove sub-dirs and small files, */ ++ /* but tending to grow */ ++ printf("\nrndrm99: growing\n");fflush(stdout); ++ loop_cnt = 0; ++ do { ++ if (loop_cnt++ % 2000 == 0) ++ display_stats(); ++ if (tests_random_no(3)) { ++ this_size = create_entry(NULL); ++ if (!this_size) ++ break; ++ size += this_size; ++ } else { ++ this_size = remove_entry(); ++ size -= this_size; ++ if (size < 0) ++ size = 0; ++ if (!this_size) ++ this_size = 1; ++ } ++ } while (this_size && ++ (tests_size_parameter == 0 || ++ size < tests_size_parameter)); ++ /* Create and remove sub-dirs and small files, but */ ++ /* but tending to shrink */ ++ printf("\nrndrm99: shrinking\n");fflush(stdout); ++ loop_cnt = 0; ++ do { ++ if (loop_cnt++ % 2000 == 0) ++ display_stats(); ++ if (!tests_random_no(3)) { ++ this_size = create_entry(NULL); ++ size += this_size; ++ } else { ++ this_size = remove_entry(); ++ size -= this_size; ++ if (size < 0) ++ size = 0; ++ } ++ } while ((tests_size_parameter != 0 && ++ size > tests_size_parameter / 10) || ++ (tests_size_parameter == 0 && size > 100000)); ++ /* Break if repeat count exceeded */ ++ if (tests_repeat_parameter > 0 && --repeat <= 0) ++ break; ++ /* Sleep */ ++ if (tests_sleep_parameter > 0) { ++ unsigned us = tests_sleep_parameter * 1000; ++ unsigned rand_divisor = RAND_MAX / us; ++ unsigned s = (us / 2) + (rand() / rand_divisor); ++ printf("\nrndrm99: sleeping\n");fflush(stdout); ++ usleep(s); ++ } ++ } ++ printf("\nrndrm99: tidying\n");fflush(stdout); ++ display_stats(); ++ /* Tidy up by removing everything */ ++ tests_clear_dir("."); ++ CHECK(chdir("..") != -1); ++ CHECK(rmdir(dir_name) != -1); ++ size_ptr = 0; ++} ++ ++/* Title of this test */ ++ ++const char *rndrm99_get_title(void) ++{ ++ return "Randomly create and remove directories and files"; ++} ++ ++/* Description of this test */ ++ ++const char *rndrm99_get_description(void) ++{ ++ return ++ "Create a directory named rndrm99_test_dir_pid, where " \ ++ "pid is the process id. Within that directory, " \ ++ "randomly create and remove " \ ++ "a number of sub-directories and small files, " \ ++ "but do more creates than removes. " \ ++ "When the total size of all sub-directories and files " \ ++ "is greater than the size specified by the size parameter, " \ ++ "start to do more removes than creates. " \ ++ "The size parameter is given by the -z or --size option, " \ ++ "otherwise it defaults to 1000000. " \ ++ "A size of zero fills the file system until there is no " ++ "space left. " \ ++ "The task repeats, sleeping in between each iteration. " \ ++ "The repeat count is set by the -n or --repeat option, " \ ++ "otherwise it defaults to 1. " \ ++ "A repeat count of zero repeats forever. " \ ++ "The sleep value is given by the -p or --sleep option, " \ ++ "otherwise it defaults to 0. " ++ "Sleep is specified in milliseconds."; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Set default test size */ ++ tests_size_parameter = 1000000; ++ ++ /* Set default test repetition */ ++ tests_repeat_parameter = 1; ++ ++ /* Set default test sleep */ ++ tests_sleep_parameter = 0; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, rndrm99_get_title(), ++ rndrm99_get_description(), "znp"); ++ if (!run_test) ++ return 1; ++ /* Change directory to the file system and check it is ok for testing */ ++ tests_check_test_file_system(); ++ /* Do the actual test */ ++ rndrm99(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndwrite00.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndwrite00.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndwrite00.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/rndwrite00.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,201 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++#define BLOCK_SIZE 32768 ++#define BUFFER_SIZE 32768 ++ ++static void check_file(int fd, char *data, size_t length) ++{ ++ size_t n, i; ++ char buf[BUFFER_SIZE]; ++ ++ CHECK(lseek(fd, 0, SEEK_SET) != -1); ++ n = 0; ++ for (;;) { ++ i = read(fd, buf, BUFFER_SIZE); ++ CHECK(i >= 0); ++ if (i == 0) ++ break; ++ CHECK(memcmp(buf, data + n, i) == 0); ++ n += i; ++ } ++ CHECK(n == length); ++} ++ ++void rndwrite00(void) ++{ ++ int fd; ++ pid_t pid; ++ ssize_t written; ++ size_t remains; ++ size_t block; ++ size_t actual_size; ++ size_t check_every; ++ char *data, *p, *q; ++ off_t offset; ++ size_t size; ++ int64_t repeat; ++ char file_name[256]; ++ char buf[4096]; ++ ++ /* Create file */ ++ pid = getpid(); ++ tests_cat_pid(file_name, "rndwrite00_test_file_", pid); ++ fd = open(file_name, O_CREAT | O_RDWR | O_TRUNC, ++ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); ++ CHECK(fd != -1); ++ /* Allocate memory to hold file data */ ++ CHECK(tests_size_parameter > 0); ++ CHECK(tests_size_parameter <= SIZE_MAX); ++ data = (char *) malloc(tests_size_parameter); ++ CHECK(data != NULL); ++ /* Fill with random data */ ++ srand(pid); ++ for (p = data, q = data + tests_size_parameter; p != q; ++p) ++ *p = rand(); ++ /* Write to file */ ++ p = data; ++ remains = tests_size_parameter; ++ while (remains > 0) { ++ if (remains > BLOCK_SIZE) ++ block = BLOCK_SIZE; ++ else ++ block = remains; ++ written = write(fd, p, block); ++ if (written <= 0) { ++ CHECK(errno == ENOSPC); /* File system full */ ++ errno = 0; ++ break; ++ } ++ remains -= written; ++ p += written; ++ } ++ actual_size = p - data; ++ /* Repeating bit */ ++ repeat = tests_repeat_parameter; ++ check_every = actual_size / 8192; ++ for (;;) { ++ offset = tests_random_no(actual_size); ++ size = tests_random_no(4096); ++ /* Don't change the file size */ ++ if (offset + size > actual_size) ++ size = actual_size - offset; ++ if (!size) ++ continue; ++ for (p = buf, q = p + size; p != q; ++p) ++ *p = rand(); ++ CHECK(lseek(fd, offset, SEEK_SET) != -1); ++ written = write(fd, buf, size); ++ if (written <= 0) { ++ CHECK(errno == ENOSPC); /* File system full */ ++ errno = 0; ++ } else ++ memcpy(data + offset, buf, written); ++ /* Break if repeat count exceeded */ ++ if (tests_repeat_parameter > 0 && --repeat <= 0) ++ break; ++ if (repeat % check_every == 0) ++ check_file(fd, data, actual_size); ++ /* Sleep */ ++ if (tests_sleep_parameter > 0) { ++ unsigned us = tests_sleep_parameter * 1000; ++ unsigned rand_divisor = RAND_MAX / us; ++ unsigned s = (us / 2) + (rand() / rand_divisor); ++ usleep(s); ++ } ++ } ++ /* Check and close file */ ++ check_file(fd, data, actual_size); ++ CHECK(close(fd) != -1); ++ if (tests_delete_flag) ++ CHECK(unlink(file_name) != -1); ++} ++ ++/* Title of this test */ ++ ++const char *rndwrite00_get_title(void) ++{ ++ return "Randomly write a large test file"; ++} ++ ++/* Description of this test */ ++ ++const char *rndwrite00_get_description(void) ++{ ++ return ++ "Create a file named rndwrite00_test_file_pid, where " \ ++ "pid is the process id. " \ ++ "The file is filled with random data. " \ ++ "The size of the file is given by the -z or --size option, " \ ++ "otherwise it defaults to 1000000. " \ ++ "Then a randomly sized block of random data is written at a " \ ++ "random location in the file. "\ ++ "The block size is always in the range 1 to 4095. " \ ++ "If a sleep value is specified, the process sleeps. " \ ++ "The number of writes is given by the repeat count. " \ ++ "The repeat count is set by the -n or --repeat option, " \ ++ "otherwise it defaults to 10000. " \ ++ "A repeat count of zero repeats forever. " \ ++ "The sleep value is given by the -p or --sleep option, " \ ++ "otherwise it defaults to 0. " ++ "Sleep is specified in milliseconds. " \ ++ "Periodically the data in the file is checked with a copy " \ ++ "held in memory. " \ ++ "If the delete option is specified the file is finally " \ ++ "deleted."; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Set default test file size */ ++ tests_size_parameter = 1000000; ++ ++ /* Set default test repetition */ ++ tests_repeat_parameter = 10000; ++ ++ /* Set default test sleep */ ++ tests_sleep_parameter = 0; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, rndwrite00_get_title(), ++ rndwrite00_get_description(), "zne"); ++ if (!run_test) ++ return 1; ++ /* Change directory to the file system and check it is ok for testing */ ++ tests_check_test_file_system(); ++ /* Do the actual test */ ++ rndwrite00(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_1.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_1.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_1.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,109 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++#define WRITE_BUFFER_SIZE 32768 ++ ++void stress_1(void) ++{ ++ int fd, i; ++ pid_t pid; ++ ssize_t written; ++ int64_t remains; ++ size_t block; ++ char file_name[256]; ++ char buf[WRITE_BUFFER_SIZE]; ++ ++ pid = getpid(); ++ tests_cat_pid(file_name, "stress_1_test_file_", pid); ++ fd = open(file_name, O_CREAT | O_WRONLY, ++ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); ++ CHECK(fd != -1); ++ srand(pid); ++ for (i = 0; i < WRITE_BUFFER_SIZE;++i) ++ buf[i] = rand(); ++ remains = tests_size_parameter; ++ while (remains > 0) { ++ if (remains > WRITE_BUFFER_SIZE) ++ block = WRITE_BUFFER_SIZE; ++ else ++ block = remains; ++ written = write(fd, buf, block); ++ if (written <= 0) { ++ CHECK(errno == ENOSPC); /* File system full */ ++ errno = 0; ++ break; ++ } ++ remains -= written; ++ } ++ CHECK(close(fd) != -1); ++ if (tests_delete_flag) ++ CHECK(unlink(file_name) != -1); ++} ++ ++/* Title of this test */ ++ ++const char *stress_1_get_title(void) ++{ ++ return "Create / overwrite a large file"; ++} ++ ++/* Description of this test */ ++ ++const char *stress_1_get_description(void) ++{ ++ return ++ "Create a file named stress_1_test_file_pid, " \ ++ "where pid is the process id. " \ ++ "The size is given by the -z or --size option, " \ ++ "otherwise it defaults to 1000000. " \ ++ "The file will be deleted if the delete option " \ ++ "is specified. "; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Set default test file size */ ++ tests_size_parameter = 1000000; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, stress_1_get_title(), ++ stress_1_get_description(), "ze"); ++ if (!run_test) ++ return 1; ++ /* Change directory to the file system and check it is ok for testing */ ++ tests_check_test_file_system(); ++ /* Do the actual test */ ++ stress_1(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_2.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_2.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_2.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,120 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++#define WRITE_BUFFER_SIZE 32768 ++ ++void stress_2(void) ++{ ++ int fd, i; ++ pid_t pid; ++ ssize_t written; ++ int64_t remains; ++ int64_t repeat; ++ size_t block; ++ char *file_name; ++ char buf[WRITE_BUFFER_SIZE]; ++ ++ file_name = "stress_2_test_file"; ++ fd = open(file_name, O_CREAT | O_WRONLY, ++ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); ++ CHECK(fd != -1); ++ CHECK(unlink(file_name) != -1); ++ pid = getpid(); ++ srand(pid); ++ repeat = tests_repeat_parameter; ++ for (;;) { ++ for (i = 0; i < WRITE_BUFFER_SIZE;++i) ++ buf[i] = rand(); ++ CHECK(lseek(fd, 0, SEEK_SET) != -1); ++ remains = tests_size_parameter; ++ while (remains > 0) { ++ if (remains > WRITE_BUFFER_SIZE) ++ block = WRITE_BUFFER_SIZE; ++ else ++ block = remains; ++ written = write(fd, buf, block); ++ if (written <= 0) { ++ CHECK(errno == ENOSPC); /* File system full */ ++ errno = 0; ++ break; ++ } ++ remains -= written; ++ } ++ if (tests_repeat_parameter > 0 && --repeat <= 0) ++ break; ++ } ++ CHECK(close(fd) != -1); ++} ++ ++/* Title of this test */ ++ ++const char *stress_2_get_title(void) ++{ ++ return "Create / overwrite a large deleted file"; ++} ++ ++/* Description of this test */ ++ ++const char *stress_2_get_description(void) ++{ ++ return ++ "Create a file named stress_2_test_file. " \ ++ "Open it, delete it while holding the open file descriptor, " \ ++ "then fill it with random data. " \ ++ "Repeated re-write the file some number of times. " \ ++ "The repeat count is given by the -n or --repeat option, " \ ++ "otherwise it defaults to 10. " \ ++ "The file size is given by the -z or --size option, " \ ++ "otherwise it defaults to 1000000."; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Set default test file size */ ++ tests_size_parameter = 1000000; ++ ++ /* Set default test repetition */ ++ tests_repeat_parameter = 10; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, stress_2_get_title(), ++ stress_2_get_description(), "zn"); ++ if (!run_test) ++ return 1; ++ /* Change directory to the file system and check it is ok for testing */ ++ tests_check_test_file_system(); ++ /* Do the actual test */ ++ stress_2(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_3.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_3.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_3.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/atoms/stress_3.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,122 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests.h" ++ ++#define WRITE_BUFFER_SIZE 32768 ++ ++void stress_3(void) ++{ ++ int fd, i; ++ pid_t pid; ++ ssize_t written; ++ int64_t remains; ++ size_t block; ++ char file_name[256]; ++ char buf[WRITE_BUFFER_SIZE]; ++ ++ pid = getpid(); ++ tests_cat_pid(file_name, "stress_3_test_file_", pid); ++ fd = open(file_name, O_CREAT | O_WRONLY, ++ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); ++ CHECK(fd != -1); ++ pid = getpid(); ++ srand(pid); ++ for (i = 0; i < WRITE_BUFFER_SIZE;++i) ++ buf[i] = rand(); ++ CHECK(lseek(fd, tests_size_parameter, SEEK_SET) != -1); ++ CHECK(write(fd, "!", 1) == 1); ++ CHECK(lseek(fd, 0, SEEK_SET) != -1); ++ remains = tests_size_parameter; ++ while (remains > 0) { ++ if (remains > WRITE_BUFFER_SIZE) ++ block = WRITE_BUFFER_SIZE; ++ else ++ block = remains; ++ written = write(fd, buf, block); ++ if (written <= 0) { ++ CHECK(errno == ENOSPC); /* File system full */ ++ errno = 0; ++ break; ++ } ++ remains -= written; ++ } ++ if (ftruncate(fd, 0) == -1) { ++ CHECK(errno == ENOSPC); /* File system full */ ++ errno = 0; ++ } ++ CHECK(close(fd) != -1); ++ if (tests_delete_flag) ++ CHECK(unlink(file_name) != -1); ++} ++ ++/* Title of this test */ ++ ++const char *stress_3_get_title(void) ++{ ++ return "Create a file with a large hole and fill it"; ++} ++ ++/* Description of this test */ ++ ++const char *stress_3_get_description(void) ++{ ++ return ++ "Create a file named stress_3_test_file_pid, " \ ++ "where pid is the process id. " \ ++ "Write a single character past the end of the file, " \ ++ "based on the specified file size, " \ ++ "which creates a hole in the file. " ++ "Fill the hole with random data. " \ ++ "Then truncate the file length to zero. " \ ++ "The size is given by the -z or --size option, " \ ++ "otherwise it defaults to 1000000. " \ ++ "The file will be deleted if the delete option " \ ++ "is specified."; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int run_test; ++ ++ /* Set default test file size */ ++ tests_size_parameter = 1000000; ++ ++ /* Handle common arguments */ ++ run_test = tests_get_args(argc, argv, stress_3_get_title(), ++ stress_3_get_description(), "ze"); ++ if (!run_test) ++ return 1; ++ /* Change directory to the file system and check it is ok for testing */ ++ tests_check_test_file_system(); ++ /* Do the actual test */ ++ stress_3(); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/stress00.sh linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/stress00.sh +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/stress00.sh 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/stress00.sh 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,52 @@ ++#!/bin/sh ++ ++TEST_DIR=$TEST_FILE_SYSTEM_MOUNT_DIR ++if test -z "$TEST_DIR"; ++then ++ TEST_DIR="/mnt/test_file_system" ++fi ++ ++FREESPACE=`../utils/free_space "$TEST_DIR"` ++ ++if test -z "$FREESPACE"; ++then ++ echo "Failed to determine free space" ++ exit 1 ++fi ++ ++if test -n "$1"; ++then ++ DURATION="-d$1"; ++else ++ DURATION=""; ++fi ++ ++FWRITE00=atoms/fwrite00 ++RNDWR=atoms/rndwrite00 ++GCHUP=atoms/gcd_hupper ++PDFLUSH=atoms/pdfrun ++FSIZE=$(( $FREESPACE/15 )); ++ ++../utils/fstest_monitor $DURATION \ ++"$FWRITE00 -z $FSIZE -n0 -p 20" \ ++"$FWRITE00 -z $FSIZE -n0 -p 10 -s" \ ++"$FWRITE00 -z $FSIZE -n0 -p 20 -u" \ ++"$FWRITE00 -z $FSIZE -n0 -p 70 -o" \ ++"$FWRITE00 -z $FSIZE -n0 -p 15 -s -o -u" \ ++"$FWRITE00 -z $FSIZE -n0 -p 10 -u -c" \ ++"$FWRITE00 -z $FSIZE -n0 -p 10 -u -o -c" \ ++"$FWRITE00 -z $FSIZE -n0 -p 10 -o -c" \ ++"$FWRITE00 -z $FSIZE -n0 -p 100 -o -u" \ ++"$FWRITE00 -z $FSIZE -n0 -p 100 -s -o -u -c" \ ++"$FWRITE00 -z $FSIZE -n0 -p 100 -o -u" \ ++"$FWRITE00 -z $FSIZE -n0 -p 100 -u" \ ++"$FWRITE00 -z $FSIZE -n0 -p 100 -s -o" \ ++"$RNDWR -z $FSIZE -n0 -p 10 -e" \ ++"$RNDWR -z $FSIZE -n0 -p 100 -e" \ ++"$PDFLUSH -z 1073741824 -n0" ++ ++STATUS=$? ++ ++rm -rf ${TEST_DIR}/* ++ ++exit $STATUS +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/stress01.sh linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/stress01.sh +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/stress/stress01.sh 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/stress/stress01.sh 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,40 @@ ++#!/bin/sh ++ ++TEST_DIR=$TEST_FILE_SYSTEM_MOUNT_DIR ++if test -z "$TEST_DIR"; ++then ++ TEST_DIR="/mnt/test_file_system" ++fi ++ ++FREESPACE=`../utils/free_space "$TEST_DIR"` ++ ++if test -z "$FREESPACE"; ++then ++ echo "Failed to determine free space" ++ exit 1 ++fi ++ ++if test -n "$1"; ++then ++ DURATION="-d$1"; ++else ++ DURATION=""; ++fi ++ ++FWRITE00=atoms/fwrite00 ++RNDWR=atoms/rndwrite00 ++PDFLUSH=atoms/pdfrun ++FSIZE=$(( $FREESPACE/15 )); ++ ++../utils/fstest_monitor $DURATION \ ++"$FWRITE00 -z $FSIZE -n0 -p 300" \ ++"$FWRITE00 -z $FSIZE -n0 -u" \ ++"$FWRITE00 -z $FSIZE -n0 -u -c" \ ++"$FWRITE00 -z $FSIZE -n0 -s -o" \ ++"$RNDWR -z $FSIZE -n0 -e" ++ ++STATUS=$? ++ ++rm -rf ${TEST_DIR}/* ++ ++exit $STATUS +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/utils/Makefile linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/utils/Makefile +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/utils/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/utils/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,19 @@ ++ ++ifeq ($(origin CC),default) ++CC = gcc ++endif ++ ++CFLAGS := $(CFLAGS) -Wall -g -O2 -I../lib ++ ++LDFLAGS := $(LDFLAGS) ++ ++TARGETS = fstest_monitor free_space ++ ++all: $(TARGETS) ++ ++clean: ++ rm -f *.o $(TARGETS) ++ ++tests: all ++ ./fstest_monitor ++ ./free_space > /dev/null +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/utils/free_space.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/utils/free_space.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/utils/free_space.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/utils/free_space.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,56 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++ ++int main(int argc, char *argv[]) ++{ ++ char *dir_name = "."; ++ uint64_t free_space; ++ struct statvfs fs_info; ++ ++ if (argc > 1) { ++ if (strncmp(argv[1], "--help", 6) == 0 || ++ strncmp(argv[1], "-h", 2) == 0) { ++ printf( "Usage is: " ++ "free_space [directory]\n" ++ "\n" ++ "Display the free space of the file system " ++ "of the directory given\n" ++ "or the current directory if no " ++ "directory is given.\nThe value output is " ++ "in bytes.\n" ++ ); ++ return 1; ++ } ++ dir_name = argv[1]; ++ } ++ if (statvfs(dir_name, &fs_info) == -1) ++ return 1; ++ ++ free_space = (uint64_t) fs_info.f_bavail * (uint64_t) fs_info.f_frsize; ++ ++ printf("%llu\n", (unsigned long long) free_space); ++ ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/utils/fstest_monitor.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/utils/fstest_monitor.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/fs-tests/utils/fstest_monitor.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/fs-tests/utils/fstest_monitor.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,281 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct child_info { ++ struct child_info *next; ++ pid_t pid; ++ int terminated; ++ int killed; ++ int gone; ++}; ++ ++struct child_info *children = 0; ++ ++void kill_children(void) ++{ ++ struct child_info *child; ++ ++ child = children; ++ while (child) { ++ if (!child->gone) { ++ if (!child->terminated) { ++ child->terminated = 1; ++ kill(child->pid, SIGTERM); ++ } /*else if (!child->killed) { ++ child->killed = 1; ++ kill(child->pid, SIGKILL); ++ }*/ ++ } ++ child = child->next; ++ } ++} ++ ++void add_child(pid_t child_pid) ++{ ++ struct child_info *child; ++ size_t sz; ++ ++ sz = sizeof(struct child_info); ++ child = (struct child_info *) malloc(sz); ++ memset(child, 0, sz); ++ child->pid = child_pid; ++ child->next = children; ++ children = child; ++} ++ ++void mark_child_gone(pid_t child_pid) ++{ ++ struct child_info *child; ++ ++ child = children; ++ while (child) { ++ if (child->pid == child_pid) { ++ child->gone = 1; ++ break; ++ } ++ child = child->next; ++ } ++} ++ ++int have_children(void) ++{ ++ struct child_info *child; ++ ++ child = children; ++ while (child) { ++ if (!child->gone) ++ return 1; ++ child = child->next; ++ } ++ return 0; ++} ++ ++int parse_command_line(char *cmdline, int *pargc, char ***pargv) ++{ ++ char **tmp; ++ char *p, *v, *q; ++ size_t sz; ++ int argc = 0; ++ int state = 0; ++ char *argv[1024]; ++ ++ if (!cmdline) ++ return 1; ++ q = v = (char *) malloc(strlen(cmdline) + 1024); ++ if (!v) ++ return 1; ++ p = cmdline; ++ for (;;) { ++ char c = *p++; ++ if (!c) { ++ *v++ = 0; ++ break; ++ } ++ switch (state) { ++ case 0: /* Between args */ ++ if (isspace(c)) ++ break; ++ argv[argc++] = v; ++ if (c == '"') { ++ state = 2; ++ break; ++ } else if (c == '\'') { ++ state = 3; ++ break; ++ } ++ state = 1; ++ case 1: /* Not quoted */ ++ if (c == '\\') { ++ if (*p) ++ *v++ = *p; ++ } else if (isspace(c)) { ++ *v++ = 0; ++ state = 0; ++ } else ++ *v++ = c; ++ break; ++ case 2: /* Double quoted */ ++ if (c == '\\' && *p == '"') { ++ *v++ = '"'; ++ ++p; ++ } else if (c == '"') { ++ *v++ = 0; ++ state = 0; ++ } else ++ *v++ = c; ++ break; ++ case 3: /* Single quoted */ ++ if (c == '\'') { ++ *v++ = 0; ++ state = 0; ++ } else ++ *v++ = c; ++ break; ++ } ++ } ++ argv[argc] = 0; ++ sz = sizeof(char *) * (argc + 1); ++ tmp = (char **) malloc(sz); ++ if (!tmp) { ++ free(q); ++ return 1; ++ } ++ if (argc == 0) ++ free(q); ++ memcpy(tmp, argv, sz); ++ *pargc = argc; ++ *pargv = tmp; ++ return 0; ++} ++ ++void signal_handler(int signum) ++{ ++ kill_children(); ++} ++ ++int result = 0; ++int alarm_gone_off = 0; ++ ++void alarm_handler(int signum) ++{ ++ if (!result) ++ alarm_gone_off = 1; ++ kill_children(); ++} ++ ++int main(int argc, char *argv[], char **env) ++{ ++ int p; ++ pid_t child_pid; ++ int status; ++ int duration = 0; ++ ++ p = 1; ++ if (argc > 1) { ++ if (strncmp(argv[p], "--help", 6) == 0 || ++ strncmp(argv[p], "-h", 2) == 0) { ++ printf( "Usage is: " ++ "fstest_monitor options programs...\n" ++ " Options are:\n" ++ " -h, --help " ++ "This help message\n" ++ " -d, --duration arg " ++ "Stop after arg seconds\n" ++ "\n" ++ "Run programs and wait for them." ++ " If duration is specified,\n" ++ "kill all programs" ++ " after that number of seconds have elapsed.\n" ++ "Example: " ++ "fstest_monitor \"/bin/ls -l\" /bin/date\n" ++ ); ++ return 1; ++ } ++ if (strncmp(argv[p], "--duration", 10) == 0 || ++ strncmp(argv[p], "-d", 2) == 0) { ++ char *s; ++ if (p+1 < argc && !isdigit(argv[p][strlen(argv[p])-1])) ++ ++p; ++ s = argv[p]; ++ while (*s && !isdigit(*s)) ++ ++s; ++ duration = atoi(s); ++ ++p; ++ } ++ } ++ ++ signal(SIGTERM, signal_handler); ++ signal(SIGINT, signal_handler); ++ for (; p < argc; ++p) { ++ child_pid = fork(); ++ if (child_pid) { ++ /* Parent */ ++ if (child_pid == (pid_t) -1) { ++ kill_children(); ++ result = 1; ++ break; ++ } ++ add_child(child_pid); ++ } else { ++ /* Child */ ++ int cargc; ++ char **cargv; ++ ++ if (parse_command_line(argv[p], &cargc, &cargv)) ++ return 1; ++ execve(cargv[0], cargv, env); ++ return 1; ++ } ++ } ++ if (!result && duration > 0) { ++ signal(SIGALRM, alarm_handler); ++ alarm(duration); ++ } ++ while (have_children()) { ++ status = 0; ++ child_pid = wait(&status); ++ if (child_pid == (pid_t) -1) { ++ if (errno == EINTR) ++ continue; ++ kill_children(); ++ return 1; ++ } ++ mark_child_gone(child_pid); ++ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { ++ result = 1; ++ kill_children(); ++ } ++ } ++ ++ if (alarm_gone_off) ++ return 0; ++ ++ return result; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/COPYING linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/COPYING +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/COPYING 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/COPYING 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,340 @@ ++ GNU GENERAL PUBLIC LICENSE ++ Version 2, June 1991 ++ ++ Copyright (C) 1989, 1991 Free Software Foundation, Inc. ++ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ Everyone is permitted to copy and distribute verbatim copies ++ of this license document, but changing it is not allowed. ++ ++ Preamble ++ ++ The licenses for most software are designed to take away your ++freedom to share and change it. By contrast, the GNU General Public ++License is intended to guarantee your freedom to share and change free ++software--to make sure the software is free for all its users. This ++General Public License applies to most of the Free Software ++Foundation's software and to any other program whose authors commit to ++using it. (Some other Free Software Foundation software is covered by ++the GNU Library General Public License instead.) You can apply it to ++your programs, too. ++ ++ When we speak of free software, we are referring to freedom, not ++price. Our General Public Licenses are designed to make sure that you ++have the freedom to distribute copies of free software (and charge for ++this service if you wish), that you receive source code or can get it ++if you want it, that you can change the software or use pieces of it ++in new free programs; and that you know you can do these things. ++ ++ To protect your rights, we need to make restrictions that forbid ++anyone to deny you these rights or to ask you to surrender the rights. ++These restrictions translate to certain responsibilities for you if you ++distribute copies of the software, or if you modify it. ++ ++ For example, if you distribute copies of such a program, whether ++gratis or for a fee, you must give the recipients all the rights that ++you have. You must make sure that they, too, receive or can get the ++source code. And you must show them these terms so they know their ++rights. ++ ++ We protect your rights with two steps: (1) copyright the software, and ++(2) offer you this license which gives you legal permission to copy, ++distribute and/or modify the software. ++ ++ Also, for each author's protection and ours, we want to make certain ++that everyone understands that there is no warranty for this free ++software. If the software is modified by someone else and passed on, we ++want its recipients to know that what they have is not the original, so ++that any problems introduced by others will not reflect on the original ++authors' reputations. ++ ++ Finally, any free program is threatened constantly by software ++patents. We wish to avoid the danger that redistributors of a free ++program will individually obtain patent licenses, in effect making the ++program proprietary. To prevent this, we have made it clear that any ++patent must be licensed for everyone's free use or not licensed at all. ++ ++ The precise terms and conditions for copying, distribution and ++modification follow. ++ ++ GNU GENERAL PUBLIC LICENSE ++ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ++ ++ 0. This License applies to any program or other work which contains ++a notice placed by the copyright holder saying it may be distributed ++under the terms of this General Public License. The "Program", below, ++refers to any such program or work, and a "work based on the Program" ++means either the Program or any derivative work under copyright law: ++that is to say, a work containing the Program or a portion of it, ++either verbatim or with modifications and/or translated into another ++language. (Hereinafter, translation is included without limitation in ++the term "modification".) Each licensee is addressed as "you". ++ ++Activities other than copying, distribution and modification are not ++covered by this License; they are outside its scope. The act of ++running the Program is not restricted, and the output from the Program ++is covered only if its contents constitute a work based on the ++Program (independent of having been made by running the Program). ++Whether that is true depends on what the Program does. ++ ++ 1. You may copy and distribute verbatim copies of the Program's ++source code as you receive it, in any medium, provided that you ++conspicuously and appropriately publish on each copy an appropriate ++copyright notice and disclaimer of warranty; keep intact all the ++notices that refer to this License and to the absence of any warranty; ++and give any other recipients of the Program a copy of this License ++along with the Program. ++ ++You may charge a fee for the physical act of transferring a copy, and ++you may at your option offer warranty protection in exchange for a fee. ++ ++ 2. You may modify your copy or copies of the Program or any portion ++of it, thus forming a work based on the Program, and copy and ++distribute such modifications or work under the terms of Section 1 ++above, provided that you also meet all of these conditions: ++ ++ a) You must cause the modified files to carry prominent notices ++ stating that you changed the files and the date of any change. ++ ++ b) You must cause any work that you distribute or publish, that in ++ whole or in part contains or is derived from the Program or any ++ part thereof, to be licensed as a whole at no charge to all third ++ parties under the terms of this License. ++ ++ c) If the modified program normally reads commands interactively ++ when run, you must cause it, when started running for such ++ interactive use in the most ordinary way, to print or display an ++ announcement including an appropriate copyright notice and a ++ notice that there is no warranty (or else, saying that you provide ++ a warranty) and that users may redistribute the program under ++ these conditions, and telling the user how to view a copy of this ++ License. (Exception: if the Program itself is interactive but ++ does not normally print such an announcement, your work based on ++ the Program is not required to print an announcement.) ++ ++These requirements apply to the modified work as a whole. If ++identifiable sections of that work are not derived from the Program, ++and can be reasonably considered independent and separate works in ++themselves, then this License, and its terms, do not apply to those ++sections when you distribute them as separate works. But when you ++distribute the same sections as part of a whole which is a work based ++on the Program, the distribution of the whole must be on the terms of ++this License, whose permissions for other licensees extend to the ++entire whole, and thus to each and every part regardless of who wrote it. ++ ++Thus, it is not the intent of this section to claim rights or contest ++your rights to work written entirely by you; rather, the intent is to ++exercise the right to control the distribution of derivative or ++collective works based on the Program. ++ ++In addition, mere aggregation of another work not based on the Program ++with the Program (or with a work based on the Program) on a volume of ++a storage or distribution medium does not bring the other work under ++the scope of this License. ++ ++ 3. You may copy and distribute the Program (or a work based on it, ++under Section 2) in object code or executable form under the terms of ++Sections 1 and 2 above provided that you also do one of the following: ++ ++ a) Accompany it with the complete corresponding machine-readable ++ source code, which must be distributed under the terms of Sections ++ 1 and 2 above on a medium customarily used for software interchange; or, ++ ++ b) Accompany it with a written offer, valid for at least three ++ years, to give any third party, for a charge no more than your ++ cost of physically performing source distribution, a complete ++ machine-readable copy of the corresponding source code, to be ++ distributed under the terms of Sections 1 and 2 above on a medium ++ customarily used for software interchange; or, ++ ++ c) Accompany it with the information you received as to the offer ++ to distribute corresponding source code. (This alternative is ++ allowed only for noncommercial distribution and only if you ++ received the program in object code or executable form with such ++ an offer, in accord with Subsection b above.) ++ ++The source code for a work means the preferred form of the work for ++making modifications to it. For an executable work, complete source ++code means all the source code for all modules it contains, plus any ++associated interface definition files, plus the scripts used to ++control compilation and installation of the executable. However, as a ++special exception, the source code distributed need not include ++anything that is normally distributed (in either source or binary ++form) with the major components (compiler, kernel, and so on) of the ++operating system on which the executable runs, unless that component ++itself accompanies the executable. ++ ++If distribution of executable or object code is made by offering ++access to copy from a designated place, then offering equivalent ++access to copy the source code from the same place counts as ++distribution of the source code, even though third parties are not ++compelled to copy the source along with the object code. ++ ++ 4. You may not copy, modify, sublicense, or distribute the Program ++except as expressly provided under this License. Any attempt ++otherwise to copy, modify, sublicense or distribute the Program is ++void, and will automatically terminate your rights under this License. ++However, parties who have received copies, or rights, from you under ++this License will not have their licenses terminated so long as such ++parties remain in full compliance. ++ ++ 5. You are not required to accept this License, since you have not ++signed it. However, nothing else grants you permission to modify or ++distribute the Program or its derivative works. These actions are ++prohibited by law if you do not accept this License. Therefore, by ++modifying or distributing the Program (or any work based on the ++Program), you indicate your acceptance of this License to do so, and ++all its terms and conditions for copying, distributing or modifying ++the Program or works based on it. ++ ++ 6. Each time you redistribute the Program (or any work based on the ++Program), the recipient automatically receives a license from the ++original licensor to copy, distribute or modify the Program subject to ++these terms and conditions. You may not impose any further ++restrictions on the recipients' exercise of the rights granted herein. ++You are not responsible for enforcing compliance by third parties to ++this License. ++ ++ 7. If, as a consequence of a court judgment or allegation of patent ++infringement or for any other reason (not limited to patent issues), ++conditions are imposed on you (whether by court order, agreement or ++otherwise) that contradict the conditions of this License, they do not ++excuse you from the conditions of this License. If you cannot ++distribute so as to satisfy simultaneously your obligations under this ++License and any other pertinent obligations, then as a consequence you ++may not distribute the Program at all. For example, if a patent ++license would not permit royalty-free redistribution of the Program by ++all those who receive copies directly or indirectly through you, then ++the only way you could satisfy both it and this License would be to ++refrain entirely from distribution of the Program. ++ ++If any portion of this section is held invalid or unenforceable under ++any particular circumstance, the balance of the section is intended to ++apply and the section as a whole is intended to apply in other ++circumstances. ++ ++It is not the purpose of this section to induce you to infringe any ++patents or other property right claims or to contest validity of any ++such claims; this section has the sole purpose of protecting the ++integrity of the free software distribution system, which is ++implemented by public license practices. Many people have made ++generous contributions to the wide range of software distributed ++through that system in reliance on consistent application of that ++system; it is up to the author/donor to decide if he or she is willing ++to distribute software through any other system and a licensee cannot ++impose that choice. ++ ++This section is intended to make thoroughly clear what is believed to ++be a consequence of the rest of this License. ++ ++ 8. If the distribution and/or use of the Program is restricted in ++certain countries either by patents or by copyrighted interfaces, the ++original copyright holder who places the Program under this License ++may add an explicit geographical distribution limitation excluding ++those countries, so that distribution is permitted only in or among ++countries not thus excluded. In such case, this License incorporates ++the limitation as if written in the body of this License. ++ ++ 9. The Free Software Foundation may publish revised and/or new versions ++of the General Public License from time to time. Such new versions will ++be similar in spirit to the present version, but may differ in detail to ++address new problems or concerns. ++ ++Each version is given a distinguishing version number. If the Program ++specifies a version number of this License which applies to it and "any ++later version", you have the option of following the terms and conditions ++either of that version or of any later version published by the Free ++Software Foundation. If the Program does not specify a version number of ++this License, you may choose any version ever published by the Free Software ++Foundation. ++ ++ 10. If you wish to incorporate parts of the Program into other free ++programs whose distribution conditions are different, write to the author ++to ask for permission. For software which is copyrighted by the Free ++Software Foundation, write to the Free Software Foundation; we sometimes ++make exceptions for this. Our decision will be guided by the two goals ++of preserving the free status of all derivatives of our free software and ++of promoting the sharing and reuse of software generally. ++ ++ NO WARRANTY ++ ++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY ++FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN ++OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES ++PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED ++OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS ++TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE ++PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, ++REPAIR OR CORRECTION. ++ ++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING ++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR ++REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, ++INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING ++OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED ++TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY ++YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER ++PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGES. ++ ++ END OF TERMS AND CONDITIONS ++ ++ How to Apply These Terms to Your New Programs ++ ++ If you develop a new program, and you want it to be of the greatest ++possible use to the public, the best way to achieve this is to make it ++free software which everyone can redistribute and change under these terms. ++ ++ To do so, attach the following notices to the program. It is safest ++to attach them to the start of each source file to most effectively ++convey the exclusion of warranty; and each file should have at least ++the "copyright" line and a pointer to where the full notice is found. ++ ++ ++ Copyright (C) 19yy ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++ ++Also add information on how to contact you by electronic and paper mail. ++ ++If the program is interactive, make it output a short notice like this ++when it starts in an interactive mode: ++ ++ Gnomovision version 69, Copyright (C) 19yy name of author ++ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. ++ This is free software, and you are welcome to redistribute it ++ under certain conditions; type `show c' for details. ++ ++The hypothetical commands `show w' and `show c' should show the appropriate ++parts of the General Public License. Of course, the commands you use may ++be called something other than `show w' and `show c'; they could even be ++mouse-clicks or menu items--whatever suits your program. ++ ++You should also get your employer (if you work as a programmer) or your ++school, if any, to sign a "copyright disclaimer" for the program, if ++necessary. Here is a sample; alter the names: ++ ++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program ++ `Gnomovision' (which makes passes at compilers) written by James Hacker. ++ ++ , 1 April 1989 ++ Ty Coon, President of Vice ++ ++This General Public License does not permit incorporating your program into ++proprietary programs. If your program is a subroutine library, you may ++consider it more useful to permit linking proprietary applications with the ++library. If this is what you want to do, use the GNU Library General ++Public License instead of this License. +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/JitterTest.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/JitterTest.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/JitterTest.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/JitterTest.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1044 @@ ++/*********************************************************************** ++ * ++ * Copyright: Daniel Measurement and Control, Inc. ++ * 9753 Pine Lake Drive ++ * Houston, TX 77055 ++ * ++ * Created by: Vipin Malik and Gail Murray ++ * Released under GPL by permission of Daniel Industries. ++ * ++ * This software is licensed under the GPL version 2. Plese see the file ++ * COPYING for details on the license. ++ * ++ * NO WARRANTY: Absolutely no claims of warranty or fitness of purpose ++ * are made in this software. Please use at your own risk. ++ * ++ * Filename: JitterTest.c ++ * ++ * Description: Program to be used to measure wake jitter. ++ * See README file for more info. ++ * ++ * ++ * Revision History: ++ * $Id: JitterTest.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ * $Log: not supported by cvs2svn $ ++ * Revision 1.13 2005/11/07 11:15:20 gleixner ++ * [MTD / JFFS2] Clean up trailing white spaces ++ * ++ * Revision 1.12 2001/08/10 19:23:11 vipin ++ * Ready to be released under GPL! Added proper headers etc. ++ * ++ * Revision 1.11 2001/07/09 15:35:50 vipin ++ * Couple of new features:1. The program runs by default as a "regular" ++ * (SCHED_OTHER) task by default, unless the -p n cmd line parameter is ++ * specified. It then runs as SCHED_RR at that priority. ++ * 2. Added ability to send SIGSTOP/SIGCONT to a specified PID. This ++ * would presumably be the PID of the JFFS2 GC task. SIGSTOP is sent ++ * before writing to the fs, and a SIGCONT after the write is done. ++ * 3. The "pad" data now written to the file on the "fs under test" is ++ * random, not sequential as previously. ++ * ++ * Revision 1.10 2001/06/27 19:14:24 vipin ++ * Now console file to log at can be specified from cmd line. This can enable ++ * you to run two instances of the program- one logging to the /dev/console ++ * and another to a regular file (if you want the data) or /dev/null if you don't. ++ * ++ * Revision 1.9 2001/06/25 20:21:31 vipin ++ * This is the latest version, NOT the one last checked in! ++ * ++ * Revision 1.7 2001/06/18 22:36:19 vipin ++ * Fix minor typo that excluded '\n' from msg on console. ++ * ++ * Revision 1.6 2001/06/18 21:17:50 vipin ++ * Added option to specify the amount of data written to outfile each period. ++ * The regular info is written, but is then padded to the requested size. ++ * This will enable testing of different sized writes to JFFS fs. ++ * ++ * Revision 1.5 2001/06/08 19:36:23 vipin ++ * All write() are now checked for return err code. ++ * ++ * Revision 1.4 2001/06/06 23:10:31 vipin ++ * Added README file. ++ * In JitterTest.c: Changed operation of periodic timer to one shot. The timer is now ++ * reset when the task wakes. This way every "jitter" is from the last time and ++ * jitters from previous times are not accumulated and screw aroud with our display. ++ * ++ * All jitter is now +ve. (as it should be). Also added a "read file" functionality ++ * to test for jitter in task if we have to read from JFFS fs. ++ * The program now also prints data to console- where it can be logged, interspersed with ++ * other "interesting" printk's from the kernel and drivers (flash sector erases etc.) ++ * ++ * Revision 1.3 2001/03/01 19:20:39 gmurray ++ * Add priority scheduling. Shortened name of default output file. ++ * Changed default interrupt period. Output delta time and jitter ++ * instead of time of day. ++ * ++ * Revision 1.2 2001/02/28 16:20:19 vipin ++ * Added version control Id and log fields. ++ * ++ ***********************************************************************/ ++/*************************** Included Files ***************************/ ++#include /* fopen, printf, fprintf, fclose */ ++#include /* strcpy, strcmp */ ++#include /* exit, atol, atoi */ ++#include /* setitimer, settimeofday, gettimeofday */ ++#include /* signal */ ++#include /* sched_setscheduler, sched_get_priority_min,*/ ++/* sched_get_priority_max */ ++#include /* gettimeofday, sleep */ ++#include ++#include ++#include ++#include ++ ++ ++ ++/**************************** Enumerations ****************************/ ++enum timerActions ++ { ++ ONESHOT, ++ AUTORESETTING ++ }; ++ ++ ++ ++/****************************** Constants *****************************/ ++/* Exit error codes */ ++#define EXIT_FILE_OPEN_ERR (1) /* error opening output file*/ ++#define EXIT_REG_SIGALRM_ERR (2) /* error registering SIGALRM*/ ++#define EXIT_REG_SIGINT_ERR (3) /* error registering SIGINT */ ++#define EXIT_INV_INT_PERIOD (4) /* error invalid int. period*/ ++#define EXIT_MIN_PRIORITY_ERR (5) /* error, minimum priority */ ++#define EXIT_MAX_PRIORITY_ERR (6) /* error, maximum priority */ ++#define EXIT_INV_SCHED_PRIORITY (7) /* error, invalid priority */ ++#define EXIT_SET_SCHEDULER_ERR (8) /* error, set scheduler */ ++#define EXIT_PREV_TIME_OF_DAY_ERR (9) /* error, init. prev. */ ++/* time of day */ ++ ++#define MAX_FILE_NAME_LEN (32) /* maximum file name length */ ++ ++#define STRINGS_EQUAL ((int) 0) /* strcmp value if equal */ ++ ++#define MIN_INT_PERIOD_MILLISEC ( 5L) /* minimum interrupt period */ ++#define MAX_INT_PERIOD_MILLISEC (5000L) /* maximum interrupt period */ ++#define DEF_INT_PERIOD_MILLISEC ( 10L) /* default interrupt period */ ++ ++#define READ_FILE_MESSAGE "This is a junk file. Must contain at least 1 byte though!\n" ++ ++/* The user can specify that the program pad out the write file to ++ a given number of bytes. But a minimum number needs to be written. This ++ will contain the jitter info. ++*/ ++#define MIN_WRITE_BYTES 30 ++#define DEFAULT_WRITE_BYTES 30 ++#define MAX_WRITE_BYTES 4096 ++ ++/* used for gen a printable ascii random # between spc and '~' */ ++#define MIN_ASCII 32 /* can be char*/ ++#define MAX_ASCII 126.0 /* needs to be float. See man rand() */ ++ ++/*---------------------------------------------------------------------- ++ * It appears that the preprocessor can't handle multi-line #if ++ * statements. Thus, the check on the default is divided into two ++ * #if statements. ++ *---------------------------------------------------------------------*/ ++#if (DEF_INT_PERIOD_MILLISEC < MIN_INT_PERIOD_MILLISEC) ++#error *** Invalid default interrupt period. *** ++#endif ++ ++#if (DEF_INT_PERIOD_MILLISEC > MAX_INT_PERIOD_MILLISEC) ++#error *** Invalid default interrupt period. *** ++#endif ++ ++ ++#define TRUE 1 /* Boolean true value */ ++#define FALSE 0 ++ ++/* Time conversion constants. */ ++#define MILLISEC_PER_SEC (1000L) /* milliseconds per second */ ++#define MICROSEC_PER_MILLISEC (1000L) /* microsecs per millisec */ ++#define MICROSEC_PER_SEC (1000000L) /* microsecs per second */ ++ ++#define PRIORITY_POLICY ((int) SCHED_RR) /* If specified iwth "-p" */ ++ ++ ++ ++/************************** Module Variables **************************/ ++/* version identifier (value supplied by CVS)*/ ++static const char Version[] = "$Id: JitterTest.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $"; ++ ++static char OutFileName[MAX_FILE_NAME_LEN+1]; /* output file name */ ++static char LogFile[MAX_FILE_NAME_LEN+1] = "/dev/console"; /* default */ ++static char ReadFile[MAX_FILE_NAME_LEN+1]; /* This file is read. Should ++ contain at least 1 byte */ ++ ++static int WriteBytes = DEFAULT_WRITE_BYTES; /* pad out file to these many bytes. */ ++static int Fd1; /* fd where the above buffer if o/p */ ++static int Cfd; /* fd to console (or file specified) */ ++static int Fd2; /* fd for the ReadFile */ ++static int DoRead = FALSE; /* should we attempt to ReadFile?*/ ++static long InterruptPeriodMilliSec; /* interrupt period, millisec */ ++static int MinPriority; /* minimum scheduler priority */ ++static int MaxPriority; /* maximum scheduler priority */ ++static int RequestedPriority; /* requested priority */ ++static struct itimerval ITimer; /* interrupt timer structure */ ++static struct timeval PrevTimeVal; /* previous time structure */ ++static struct timeval CurrTimeVal; /* current time structure */ ++static long LastMaxDiff = 0; /* keeps track of worst jitter encountered */ ++ ++static int GrabKProfile = FALSE; /* To help determine system bottle necks ++ this parameter can be set. This causes ++ the /proc/profile file to be read and ++ stored in unique filenames in current ++ dir, and indication to be o/p on the ++ /dev/console also. ++ */ ++static long ProfileTriggerMSecs = 15000l; /* Jitter time in seconds that triggers ++ a snapshot of the profile to be taken ++ ++ */ ++static int SignalGCTask = FALSE; /* should be signal SIGSTOP/SIGCONT to gc task?*/ ++static int GCTaskPID; ++ ++static int RunAsRTTask = FALSE; /* default action unless priority is ++ specified on cmd line */ ++ ++ ++/********************* Local Function Prototypes **********************/ ++void HandleCmdLineArgs(int argc, char *argv[]); ++void SetFileName(char * pFileName); ++void SetInterruptPeriod(char * pASCIIInterruptPeriodMilliSec); ++void SetSchedulerPriority(char * pASCIISchedulerPriority); ++ ++void PrintVersionInfo(void); ++void PrintHelpInfo(void); ++ ++int Write(int fd, void *buf, size_t bytes, int lineNo); ++ ++void InitITimer(struct itimerval * pITimer, int action); ++ ++/* For catching timer interrupts (SIGALRM). */ ++void AlarmHandler(int sigNum); ++ ++/* For catching Ctrl+C SIGINT. */ ++void SignalHandler(int sigNum); ++ ++ ++ ++/*********************************************************************** ++ * main function ++ * return: exit code ++ ***********************************************************************/ ++int main( ++ int argc, ++ char *argv[]) ++{ ++ struct sched_param schedParam; ++ ++ int mypri; ++ char tmpBuf[200]; ++ ++ ++ strcpy(OutFileName,"jitter.dat"); ++ InterruptPeriodMilliSec = MIN_INT_PERIOD_MILLISEC; ++ ++ /* Get the minimum and maximum priorities. */ ++ MinPriority = sched_get_priority_min(PRIORITY_POLICY); ++ MaxPriority = sched_get_priority_max(PRIORITY_POLICY); ++ if (MinPriority == -1) { ++ printf("\n*** Unable to get minimum priority. ***\n"); ++ exit(EXIT_MIN_PRIORITY_ERR); ++ } ++ if (MaxPriority == -1) { ++ printf("\n*** Unable to get maximum priority. ***\n"); ++ exit(EXIT_MAX_PRIORITY_ERR); ++ } ++ ++ /* Set requested priority to minimum value as default. */ ++ RequestedPriority = MinPriority; ++ ++ HandleCmdLineArgs(argc, argv); ++ ++ if(mlockall(MCL_CURRENT|MCL_FUTURE) < 0){ ++ printf("Could not lock task into memory. Bye\n"); ++ perror("Error"); ++ } ++ ++ if(RunAsRTTask){ ++ ++ /* Set the priority. */ ++ schedParam.sched_priority = RequestedPriority; ++ if (sched_setscheduler( ++ 0, ++ PRIORITY_POLICY, ++ &schedParam) != (int) 0) { ++ printf("Exiting: Unsuccessful sched_setscheduler.\n"); ++ close(Fd1); ++ exit(EXIT_SET_SCHEDULER_ERR); ++ } ++ ++ ++ /* Double check as I have some doubts that it's really ++ running at realtime priority! */ ++ if((mypri = sched_getscheduler(0)) != RequestedPriority) ++ { ++ printf("Not running with request priority %i. running with priority %i instead!\n", ++ RequestedPriority, mypri); ++ }else ++ { ++ printf("Running with %i priority. Good!\n", mypri); ++ } ++ ++ } ++ ++ /*------------------------- Initializations --------------------------*/ ++ if((Fd1 = open(OutFileName, O_RDWR|O_CREAT|O_SYNC)) <= 0) ++ { ++ perror("Cannot open outfile for write:"); ++ exit(1); ++ } ++ ++ /* If a request is made to read from a specified file, then create that ++ file and fill with junk data so that there is something there to read. ++ */ ++ if(DoRead) ++ { ++ ++ if((Fd2 = open(ReadFile, O_RDWR|O_CREAT|O_SYNC|O_TRUNC)) <= 0) ++ { ++ perror("cannot open read file:"); ++ exit(1); ++ }else ++ { ++ ++ /* Don't really care how much we write here */ ++ if(write(Fd2, READ_FILE_MESSAGE, strlen(READ_FILE_MESSAGE)) < 0) ++ { ++ perror("Problems writing to readfile:"); ++ exit(1); ++ } ++ lseek(Fd2, 0, SEEK_SET); /* position back to byte #0 */ ++ } ++ } ++ ++ ++ ++ /* Also log output to console. This way we can capture it ++ on a serial console to a log file. ++ */ ++ if((Cfd = open(LogFile, O_WRONLY|O_SYNC)) <= 0) ++ { ++ perror("cannot open o/p logfile:"); ++ exit(1); ++ } ++ ++ ++ /* Set-up handler for periodic interrupt. */ ++ if (signal(SIGALRM, &AlarmHandler) == SIG_ERR) { ++ printf("Couldn't register signal handler for SIGALRM.\n"); ++ sprintf(tmpBuf, ++ "Couldn't register signal handler for SIGALRM.\n"); ++ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); ++ ++ close(Fd1); ++ exit(EXIT_REG_SIGALRM_ERR); ++ } ++ ++ /* Set-up handler for Ctrl+C to exit the program. */ ++ if (signal(SIGINT, &SignalHandler) == SIG_ERR) { ++ printf("Couldn't register signal handler for SIGINT.\n"); ++ sprintf(tmpBuf, ++ "Couldn't register signal handler for SIGINT.\n"); ++ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); ++ ++ close(Fd1); ++ exit(EXIT_REG_SIGINT_ERR); ++ } ++ ++ printf("Press Ctrl+C to exit the program.\n"); ++ printf("Output File: %s\n", OutFileName); ++ printf("Scheduler priority: %d\n", RequestedPriority); ++ sprintf(tmpBuf, "\nScheduler priority: %d\n", ++ RequestedPriority); ++ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); ++ ++ Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); ++ ++ printf("Interrupt period: %ld milliseconds\n", ++ InterruptPeriodMilliSec); ++ sprintf(tmpBuf, "Interrupt period: %ld milliseconds\n", ++ InterruptPeriodMilliSec); ++ ++ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); ++ ++ Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); ++ ++ ++ fflush(0); ++ ++ ++ ++ /* Initialize the periodic timer. */ ++ InitITimer(&ITimer, ONESHOT); ++ ++ /* Initialize "previous" time. */ ++ if (gettimeofday(&PrevTimeVal, NULL) != (int) 0) { ++ printf("Exiting - "); ++ printf("Unable to initialize previous time of day.\n"); ++ sprintf(tmpBuf, "Exiting - "); ++ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); ++ ++ sprintf(tmpBuf, ++ "Unable to initialize previous time of day.\n"); ++ ++ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); ++ ++ } ++ ++ /* Start the periodic timer. */ ++ setitimer(ITIMER_REAL, &ITimer, NULL); ++ ++ ++ while(TRUE) { /* Intentional infinite loop. */ ++ /* Sleep for one second. */ ++ sleep((unsigned int) 1); ++ } ++ ++ /* Just in case. File should be closed in SignalHandler. */ ++ close(Fd1); ++ close(Cfd); ++ ++ return 0; ++} ++ ++ ++ ++ ++/*********************************************************************** ++ * SignalHandler ++ * This is a handler for the SIGINT signal. It is assumed that the ++ * SIGINT is due to the user pressing Ctrl+C to halt the program. ++ * output: N/A ++ ***********************************************************************/ ++void SignalHandler( ++ int sigNum) ++{ ++ ++ char tmpBuf[200]; ++ ++ /* Note sigNum not used. */ ++ printf("Ctrl+C detected. Worst Jitter time was:%fms.\nJitterTest exiting.\n", ++ (float)LastMaxDiff/1000.0); ++ ++ sprintf(tmpBuf, ++ "\nCtrl+C detected. Worst Jitter time was:%fms\nJitterTest exiting.\n", ++ (float)LastMaxDiff/1000.0); ++ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); ++ ++ Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); ++ ++ close(Fd1); ++ close(Cfd); ++ exit(0); ++} ++ ++ ++ ++ ++ ++/* ++ A snapshot of the /proc/profile needs to be taken. ++ This is stored as a new file every time, and the ++ stats reset by doing a (any) write to the /proc/profile ++ file. ++ */ ++void doGrabKProfile(int jitterusec, char *fileName) ++{ ++ int fdSnapshot; ++ int fdProfile; ++ int readBytes; ++ char readBuf[4096]; ++ ++ if((fdSnapshot = open(fileName, O_WRONLY | O_CREAT)) <= 0) ++ { ++ fprintf(stderr, "Could not open file %s.\n", fileName); ++ perror("Error:"); ++ return; ++ } ++ ++ if((fdProfile = open("/proc/profile", O_RDWR)) <= 0) ++ { ++ fprintf(stderr, "Could not open file /proc/profile. Make sure you booted with profile=2\n"); ++ close(fdSnapshot); ++ return; ++ } ++ ++ while((readBytes = read(fdProfile, readBuf, sizeof(readBuf))) > 0) ++ { ++ write(fdSnapshot, readBuf, readBytes); ++ } ++ ++ close(fdSnapshot); ++ ++ if(write(fdProfile, readBuf, 1) != 1) ++ { ++ perror("Could Not clear profile by writing to /proc/profile:"); ++ } ++ ++ close(fdProfile); ++ ++ ++ ++}/* end doGrabKProfile()*/ ++ ++ ++/* ++ Call this routine to clear the kernel profiling buffer /proc/profile ++*/ ++void clearProfileBuf(void){ ++ ++ ++ int fdProfile; ++ char readBuf[10]; ++ ++ ++ if((fdProfile = open("/proc/profile", O_RDWR)) <= 0) ++ { ++ fprintf(stderr, "Could not open file /proc/profile. Make sure you booted with profile=2\n"); ++ return; ++ } ++ ++ ++ if(write(fdProfile, readBuf, 1) != 1) ++ { ++ perror("Could Not clear profile by writing to /proc/profile:"); ++ } ++ ++ close(fdProfile); ++ ++ ++}/* end clearProfileBuf() */ ++ ++ ++ ++ ++ ++/*********************************************************************** ++ * AlarmHandler ++ * This is a handler for the SIGALRM signal (due to the periodic ++ * timer interrupt). It prints the time (seconds) to ++ * the output file. ++ * output: N/A ++ ***********************************************************************/ ++void AlarmHandler( ++ int sigNum) /* signal number (not used) */ ++{ ++ ++ long timeDiffusec; /* diff time in micro seconds */ ++ long intervalusec; ++ ++ ++ char tmpBuf[MAX_WRITE_BYTES]; ++ int cntr; ++ char padChar; ++ ++ static int profileFileNo = 0; ++ char profileFileName[150]; ++ ++ static int seedStarter = 0; /* carries over rand info to next time ++ where time() will be the same as this time ++ if invoked < 1sec apart. ++ */ ++ ++ if (gettimeofday(&CurrTimeVal, NULL) == (int) 0) { ++ ++ /*---------------------------------------------------------------- ++ * Compute the elapsed time between the current and previous ++ * time of day values and store the result. ++ * ++ * Print the elapsed time to the output file. ++ *---------------------------------------------------------------*/ ++ ++ timeDiffusec = (long)(((((long long)CurrTimeVal.tv_sec) * 1000000L) + CurrTimeVal.tv_usec) - ++ (((long long)PrevTimeVal.tv_sec * 1000000L) + PrevTimeVal.tv_usec)); ++ ++ sprintf(tmpBuf," %f ms ", (float)timeDiffusec/1000.0); ++ ++ intervalusec = InterruptPeriodMilliSec * 1000L; ++ ++ timeDiffusec = timeDiffusec - intervalusec; ++ ++ sprintf(&tmpBuf[strlen(tmpBuf)]," %f ms", (float)timeDiffusec/1000.0); ++ ++ ++ /* should we send a SIGSTOP/SIGCONT to the specified PID? */ ++ if(SignalGCTask){ ++ ++ if(kill(GCTaskPID, SIGSTOP) < 0){ ++ ++ perror("error:"); ++ } ++ } ++ ++ ++ /* Store some historical #'s */ ++ if(abs(timeDiffusec) > LastMaxDiff) ++ { ++ LastMaxDiff = abs(timeDiffusec); ++ sprintf(&tmpBuf[strlen(tmpBuf)],"!"); ++ ++ if((GrabKProfile == TRUE) && (ProfileTriggerMSecs < (abs(timeDiffusec)/1000))) ++ { ++ sprintf(profileFileName, "JitterTest.profilesnap-%i", profileFileNo); ++ ++ /* go and grab the kernel performance profile. */ ++ doGrabKProfile(timeDiffusec, profileFileName); ++ profileFileNo++; /* unique name next time */ ++ ++ /* Say so on the console so that a marker gets put in the console log */ ++ sprintf(&tmpBuf[strlen(tmpBuf)],"", ++ profileFileName); ++ ++ } ++ ++ } ++ ++ ++ ++ ++ sprintf(&tmpBuf[strlen(tmpBuf)],"\n"); /* CR for the data going out of the console */ ++ ++ Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__); ++ ++ ++ /* The "-1" below takes out the '\n' at the end that we appended for the msg printed on ++ the console.*/ ++ sprintf(&tmpBuf[strlen(tmpBuf)-1]," PadBytes:"); ++ ++ /* Now pad the output file if requested by user. */ ++ if(WriteBytes > MIN_WRITE_BYTES) ++ { ++ ++ /* start from a new place every time */ ++ srand(time(NULL) + seedStarter); ++ ++ /* already written MIN_WRITE_BYTES by now */ ++ for(cntr = strlen(tmpBuf); cntr < WriteBytes - 1 ; cntr++) /* "-1" adj for '\n' at end */ ++ { ++ /* don't accept any # < 'SPACE' */ ++ padChar = (char)(MIN_ASCII+(int)((MAX_ASCII-(float)MIN_ASCII)*rand()/(RAND_MAX+1.0))); ++ ++ ++ /* ++ padChar = (cntr % (126-33)) + 33; ++ */ ++ ++ tmpBuf[cntr] = padChar; ++ } ++ ++ seedStarter = tmpBuf[cntr-1]; /* save for next time */ ++ ++ tmpBuf[cntr] = '\n'; /* CR for the data going into the outfile. */ ++ tmpBuf[cntr+1] = '\0'; /* NULL terminate the string */ ++ } ++ ++ /* write out the entire line to the output file. */ ++ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); ++ ++ ++ /* Read a byte from the specified file */ ++ if(DoRead) ++ { ++ ++ read(Fd2, tmpBuf, 1); ++ lseek(Fd2, 0, SEEK_SET); /* back to start */ ++ } ++ ++ ++ /* Start the periodic timer again. */ ++ setitimer(ITIMER_REAL, &ITimer, NULL); ++ ++ ++ /* Update previous time with current time. */ ++ PrevTimeVal.tv_sec = CurrTimeVal.tv_sec; ++ PrevTimeVal.tv_usec = CurrTimeVal.tv_usec; ++ } ++ ++ else { ++ sprintf(tmpBuf, "gettimeofday error \n"); ++ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__); ++ ++ printf("gettimeofday error \n"); ++ } ++ ++ /* now clear the profiling buffer */ ++ if(GrabKProfile == TRUE){ ++ ++ clearProfileBuf(); ++ } ++ ++ /* should we send a SIGSTOP/SIGCONT to the specified PID? */ ++ if(SignalGCTask){ ++ ++ if(kill(GCTaskPID, SIGCONT) < 0){ ++ ++ perror("error:"); ++ } ++ } ++ ++ ++ return; ++} ++ ++ ++ ++/*********************************************************************** ++ * InitITimer ++ * This function initializes the 'struct itimerval' structure whose ++ * address is passed to interrupt every InterruptPeriodMilliSec. ++ * output: N/A ++ ***********************************************************************/ ++void InitITimer( ++ struct itimerval * pITimer, /* pointer to interrupt timer struct*/ ++ int action) /* ONESHOT or autosetting? */ ++{ ++ long seconds; /* seconds portion of int. period */ ++ long microSeconds; /* microsec. portion of int. period */ ++ ++ /*-------------------------------------------------------------------- ++ * Divide the desired interrupt period into its seconds and ++ * microseconds components. ++ *-------------------------------------------------------------------*/ ++ if (InterruptPeriodMilliSec < MILLISEC_PER_SEC) { ++ seconds = 0L; ++ microSeconds = InterruptPeriodMilliSec * MICROSEC_PER_MILLISEC; ++ } ++ else { ++ seconds = InterruptPeriodMilliSec / MILLISEC_PER_SEC; ++ microSeconds = ++ (InterruptPeriodMilliSec - (seconds * MILLISEC_PER_SEC)) * ++ MICROSEC_PER_MILLISEC; ++ } ++ ++ /* Initialize the interrupt period structure. */ ++ pITimer->it_value.tv_sec = seconds; ++ pITimer->it_value.tv_usec = microSeconds; ++ ++ if(action == ONESHOT) ++ { ++ /* This will (should) prevent the timer from restarting itself */ ++ pITimer->it_interval.tv_sec = 0; ++ pITimer->it_interval.tv_usec = 0; ++ }else ++ { ++ pITimer->it_interval.tv_sec = seconds; ++ pITimer->it_interval.tv_usec = microSeconds; ++ ++ } ++ ++ return; ++} ++ ++ ++/*********************************************************************** ++ * HandleCmdLineArgs ++ * This function handles the command line arguments. ++ * output: stack size ++ ***********************************************************************/ ++void HandleCmdLineArgs( ++ int argc, /* number of command-line arguments */ ++ char *argv[]) /* ptrs to command-line arguments */ ++{ ++ int argNum; /* argument number */ ++ ++ if (argc > (int) 1) { ++ ++ for (argNum = (int) 1; argNum < argc; argNum++) { ++ ++ /* The command line contains an argument. */ ++ ++ if ((strcmp(argv[argNum],"--version") == STRINGS_EQUAL) || ++ (strcmp(argv[argNum],"-v") == STRINGS_EQUAL)) { ++ /* Print version information and exit. */ ++ PrintVersionInfo(); ++ exit(0); ++ } ++ ++ else if ((strcmp(argv[argNum],"--help") == STRINGS_EQUAL) || ++ (strcmp(argv[argNum],"-h") == STRINGS_EQUAL) || ++ (strcmp(argv[argNum],"-?") == STRINGS_EQUAL)) { ++ /* Print help information and exit. */ ++ PrintHelpInfo(); ++ exit(0); ++ } ++ ++ else if ((strcmp(argv[argNum],"--file") == STRINGS_EQUAL) || ++ (strcmp(argv[argNum],"-f") == STRINGS_EQUAL)) { ++ /* Set the name of the output file. */ ++ ++argNum; ++ if (argNum < argc) { ++ SetFileName(argv[argNum]); ++ } ++ else { ++ printf("*** Output file name not specified. ***\n"); ++ printf("Default output file name will be used.\n"); ++ } ++ } ++ ++ else if ((strcmp(argv[argNum],"--time") == STRINGS_EQUAL) || ++ (strcmp(argv[argNum],"-t") == STRINGS_EQUAL)) { ++ /* Set the interrupt period. */ ++ ++argNum; ++ if (argNum < argc) { ++ SetInterruptPeriod(argv[argNum]); ++ } ++ else { ++ printf("*** Interrupt period not specified. ***\n"); ++ printf("Default interrupt period will be used.\n"); ++ } ++ ++ } ++ ++ else if ((strcmp(argv[argNum],"--priority") == ++ STRINGS_EQUAL) || ++ (strcmp(argv[argNum],"-p") == STRINGS_EQUAL)) { ++ /* Set the scheduler priority. */ ++ ++argNum; ++ if (argNum < argc) { ++ SetSchedulerPriority(argv[argNum]); ++ } ++ else { ++ printf("*** Scheduler priority not specified. ***\n"); ++ printf("Default scheduler priority will be used.\n"); ++ } ++ ++ } ++ ++ else if ((strcmp(argv[argNum],"--readfile") == ++ STRINGS_EQUAL) || ++ (strcmp(argv[argNum],"-r") == STRINGS_EQUAL)) { ++ /* Set the file to read*/ ++ ++argNum; ++ ++ strncpy(ReadFile, argv[argNum], sizeof(ReadFile)); ++ DoRead = TRUE; ++ } ++ ++ else if ((strcmp(argv[argNum],"--write_bytes") == ++ STRINGS_EQUAL) || ++ (strcmp(argv[argNum],"-w") == STRINGS_EQUAL)) { ++ /* Set the file to read*/ ++ ++argNum; ++ ++ WriteBytes = atoi(argv[argNum]); ++ ++ if(WriteBytes < MIN_WRITE_BYTES) ++ { ++ printf("Writing less than %i bytes is not allowed. Bye.\n", ++ MIN_WRITE_BYTES); ++ exit(0); ++ } ++ ++ ++ } ++ ++ else if ((strcmp(argv[argNum],"--consolefile") == ++ STRINGS_EQUAL) || ++ (strcmp(argv[argNum],"-c") == STRINGS_EQUAL)) { ++ /* Set the file to log console log on. */ ++ ++argNum; ++ ++ strncpy(LogFile, argv[argNum], sizeof(LogFile)); ++ } ++ ++ else if ((strcmp(argv[argNum],"--grab_kprofile") == ++ STRINGS_EQUAL)) ++ { ++ /* We will read the /proc/profile file on configurable timeout */ ++ GrabKProfile = TRUE; ++ ++ ++argNum; ++ ++ /* If the jittter is > this #, then the profile is grabbed. */ ++ ProfileTriggerMSecs = (long) atoi(argv[argNum]); ++ ++ if(ProfileTriggerMSecs <= 0){ ++ ++ printf("Illegal value for profile trigger threshold.\n"); ++ exit(0); ++ } ++ } ++ ++ else if ((strcmp(argv[argNum],"--siggc") == ++ STRINGS_EQUAL)) ++ { ++ /* We will SIGSTOP/SIGCONT the specified pid */ ++ SignalGCTask = TRUE; ++ ++ ++argNum; ++ ++ GCTaskPID = atoi(argv[argNum]); ++ ++ if(ProfileTriggerMSecs <= 0){ ++ ++ printf("Illegal value for JFFS(2) GC task pid.\n"); ++ exit(0); ++ } ++ } ++ ++ ++ else { ++ /* Unknown argument. Print help information and exit. */ ++ printf("Invalid option %s\n", argv[argNum]); ++ printf("Try 'JitterTest --help' for more information.\n"); ++ exit(0); ++ } ++ } ++ } ++ ++ return; ++} ++ ++ ++/*********************************************************************** ++ * SetFileName ++ * This function sets the output file name. ++ * output: N/A ++ ***********************************************************************/ ++void SetFileName( ++ char * pFileName) /* ptr to desired output file name */ ++{ ++ size_t fileNameLen; /* file name length (bytes) */ ++ ++ /* Check file name length. */ ++ fileNameLen = strlen(pFileName); ++ if (fileNameLen > (size_t) MAX_FILE_NAME_LEN) { ++ printf("File name %s exceeds maximum length %d.\n", ++ pFileName, MAX_FILE_NAME_LEN); ++ exit(0); ++ } ++ ++ /* File name length is OK so save the file name. */ ++ strcpy(OutFileName, pFileName); ++ ++ return; ++} ++ ++ ++/*********************************************************************** ++ * SetInterruptPeriod ++ * This function sets the interrupt period. ++ * output: N/A ++ ***********************************************************************/ ++void SetInterruptPeriod( ++ char * /* ptr to desired interrupt period */ ++ pASCIIInterruptPeriodMilliSec) ++{ ++ long period; /* interrupt period */ ++ ++ period = atol(pASCIIInterruptPeriodMilliSec); ++ if ((period < MIN_INT_PERIOD_MILLISEC) || ++ (period > MAX_INT_PERIOD_MILLISEC)) { ++ printf("Invalid interrupt period: %ld ms.\n", period); ++ exit(EXIT_INV_INT_PERIOD); ++ } ++ else { ++ InterruptPeriodMilliSec = period; ++ } ++ return; ++} ++ ++ ++/*********************************************************************** ++ * SetSchedulerPriority ++ * This function sets the desired scheduler priority. ++ * output: N/A ++ ***********************************************************************/ ++void SetSchedulerPriority( ++ char * pASCIISchedulerPriority) /* ptr to desired scheduler priority*/ ++{ ++ int priority; /* desired scheduler priority value */ ++ ++ priority = atoi(pASCIISchedulerPriority); ++ if ((priority < MinPriority) || ++ (priority > MaxPriority)) { ++ printf("Scheduler priority %d outside of range [%d, %d]\n", ++ priority, MinPriority, MaxPriority); ++ exit(EXIT_INV_SCHED_PRIORITY); ++ } ++ else { ++ RequestedPriority = priority; ++ RunAsRTTask = TRUE; /* We shall run as a POSIX real time task */ ++ } ++ return; ++} ++ ++ ++/*********************************************************************** ++ * PrintVersionInfo ++ * This function prints version information. ++ * output: N/A ++ ***********************************************************************/ ++void PrintVersionInfo(void) ++{ ++ printf("JitterTest version %s\n", Version); ++ printf("Copyright (c) 2001, Daniel Industries, Inc.\n"); ++ return; ++} ++ ++ ++/*********************************************************************** ++ * PrintHelpInfo ++ * This function prints help information. ++ * output: N/A ++ ***********************************************************************/ ++void PrintHelpInfo(void) ++{ ++ printf("Usage: JitterTest [options]\n"); ++ printf(" *** Requires root privileges. ***\n"); ++ printf("Option:\n"); ++ printf(" [-h, --help, -?] Print this message and exit.\n"); ++ printf(" [-v, --version] "); ++ printf( "Print the version number of JitterTest and exit.\n"); ++ printf(" [-f FILE, --file FILE] Set output file name to FILE. Typically you would put this on the fs under test\n"); ++ printf(" [-c FILE, --consolefile] Set device or file to write the console log to.\n\tTypically you would set this to /dev/console and save it on another computer.\n"); ++ printf(" [-w BYTES, --write_bytes BYTES Write BYTES to FILE each period.\n"); ++ printf(" [-r FILE, --readfile FILE] Also read 1 byte every cycle from FILE. FILE will be created and filled with data.\n"); ++ printf(" [-t , --time ] "); ++ printf( "Set interrupt period to milliseconds.\n"); ++ printf(" "); ++ printf( "Range: [%ld, %ld] milliseconds.\n", ++ MIN_INT_PERIOD_MILLISEC, MAX_INT_PERIOD_MILLISEC); ++ printf(" [-p , --priority ] "); ++ printf( "Set scheduler priority to .\n"); ++ printf(" "); ++ printf( "Range: [%d, %d] (higher number = higher priority)\n", ++ MinPriority, MaxPriority); ++ printf(" [--grab_kprofile ] Read the /proc/profile if jitter is > THRESHOLD and store in file.\n"); ++ printf(" [--siggc ] Before writing to fs send SIGSTOP to PID. After write send SIGCONT\n"); ++ return; ++ ++} ++ ++ ++/* A common write that checks for write errors and exits. Pass it __LINE__ for lineNo */ ++int Write(int fd, void *buf, size_t bytes, int lineNo) ++{ ++ ++ int err; ++ ++ err = write(fd, buf, bytes); ++ ++ if(err < bytes) ++ { ++ ++ printf("Write Error at line %i! Wanted to write %i bytes, but wrote only %i bytes.\n", ++ lineNo, bytes, err); ++ perror("Write did not complete. Error. Bye:"); /* show error from errno. */ ++ exit(1); ++ ++ } ++ ++ return err; ++ ++}/* end Write*/ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/Makefile linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/Makefile +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,46 @@ ++CC=gcc ++# uncomment following for performance ++CCFLAGS=-O3 -Wall -m486 -fomit-frame-pointer ++ ++# uncomment following for debugging. Uncomment either this or the one above. Not both. ++# CCFLAGS=-Wall -g ++ ++ ++all: JitterTest plotJittervsFill ++ ++JitterTest: JitterTest.c Makefile ++ gcc $(CCFLAGS) -lm JitterTest.c -o JitterTest ++ ++plotJittervsFill: plotJittervsFill.c Makefile ++ gcc $(CCFLAGS) plotJittervsFill.c -o plotJittervsFill ++ ++clean: ++ rm -rf *~ ++ rm -rf core ++ rm -rf *.o ++ rm -rf JitterTest ++ ++ ++dep: ++ makedepend -I./ *.c ++# DO NOT DELETE ++ ++JitterTest.o: /usr/include/stdio.h /usr/include/features.h ++JitterTest.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h ++JitterTest.o: /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h ++JitterTest.o: /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stdarg.h ++JitterTest.o: /usr/include/bits/types.h /usr/include/libio.h ++JitterTest.o: /usr/include/_G_config.h /usr/include/bits/stdio_lim.h ++JitterTest.o: /usr/include/string.h /usr/include/stdlib.h ++JitterTest.o: /usr/include/sys/types.h /usr/include/time.h ++JitterTest.o: /usr/include/endian.h /usr/include/bits/endian.h ++JitterTest.o: /usr/include/sys/select.h /usr/include/bits/select.h ++JitterTest.o: /usr/include/bits/sigset.h /usr/include/sys/sysmacros.h ++JitterTest.o: /usr/include/alloca.h /usr/include/sys/time.h ++JitterTest.o: /usr/include/bits/time.h /usr/include/signal.h ++JitterTest.o: /usr/include/bits/signum.h /usr/include/bits/siginfo.h ++JitterTest.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h ++JitterTest.o: /usr/include/asm/sigcontext.h /usr/include/bits/sigstack.h ++JitterTest.o: /usr/include/sched.h /usr/include/bits/sched.h ++JitterTest.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h ++JitterTest.o: /usr/include/bits/confname.h /usr/include/getopt.h +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/README linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/README +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/README 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/README 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,197 @@ ++$Id: README,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ ++This is the README file for the JitterTest (and friends) ++program. ++ ++This program is used to measure what the jitter of a ++real time task would be under "standard" Linux. ++ ++More particularly, what is the effect of running ++a real time task under Linux with background ++JFFS file system activity. ++ ++The jitter is measured in milli seconds (ms) from ++the expected time of arrival of a signal from a ++periodic timer (set by the task) to when the ++task actually gets the signal. ++ ++This jitter is then stored in a file specified ++(or the default output file "jitter.dat"). ++ ++The data may also be sent out to the console by ++writing to /dev/console (See help options. This is ++highly desirable specially if you have redirected ++your console to the serial port and are storing it ++as a minicom log on another computer for later analysis ++using some tools provided here). ++ ++This is particularly useful if you have a serial ++console and are outputting "interesting" info ++from inside some kernel task or driver. ++(or something as simple as a "df" program running ++periodically and redirecting o/p to the console). ++ ++One "interesting" thing that I have measured ++is the effect of FLASH chip erases on the jitter ++of a real time task. ++ ++One can do that by putting a printk at the ++beginning of the flash erase routine in the MTD ++flash chip driver. ++ ++Now you will get jitter data interspersed with ++flash sector erase events. Other printk's can also ++be placed at suspected jitter causing locations in ++the system. ++ ++ ++ ++EXECUTING THE PROGRAM "JitterTest" ++ ++You may specify a file to be read by the ++program every time it wakes up (every cycle). ++This file is created and filled with some junk ++data. The purpose of this is to test the jitter ++of the program if it were reading from- say ++a JFFS (Journaling Flash File System) file system. ++ ++By specifying the complete paths of the read and write ++(o/p) files you can test the jitter a POSIX type ++real time task will experience under Linux, under ++various conditions. ++ ++These can be as follows: ++ ++1. O/P file on ram file system, no i/p file. ++ ++ In this case you would presumably generate other ++"typical" background activity for your system and ++examine the worst case jitter experienced by ++a task that is neither reading nor writing to ++a file system. ++ ++Other cases could be: ++ ++2. O/P to ram fs, I/P from JFFS (type) fs: ++ ++ This is specially useful to test the proper ++operation of erase suspend type of operation ++in JFFS file systems (with an MTD layer that ++supports it). ++ ++ In this test you would generate some background ++write/erase type activity that would generate ++chip erases. Since this program is reading from ++the same file system, you contrast the latencies ++with those obtained with writes going to the same ++fs. ++ ++3. Both read and writes to (or just write to) JFFS ++file system: ++ ++ Here you would test for latencies experienced by ++a program if it were writing (and optionally also ++reading) from a JFFS fs. ++ ++ ++ ++ ++Grabing a kernel profile: ++ ++This program can also conditionally grab a kernel profile. ++Specify --grab_kprofile on the cmd line as well as ++a "threshold" parameter (see help options by -?). ++ ++Any jitter greater than this "threshold" will cause the ++program to read the /proc/profile file and dump it in ++a local file with increasing file numbers. It will also ++output the filename at that time to the console file specified. ++This will allow you to corelate later in time the various profiles ++with data in your console file and what was going on at that time. ++ ++These profile files may then be later examined by running them through ++ksymoops. ++ ++Make sure you specify "profile=2" on the kernel command line ++when you boot the kernel if you want to use this functionality. ++ ++ ++ ++Signalling the JFFS[2] GC task: ++ ++You can also force this program to send a SIGSTOP/SIGCONT to the ++JFFS (or JFFS2) gc task by specifing --siggc on the cmd line. ++ ++This will let you investigate the effect of forcing the gc task to ++wake up and do its thing when you are not writing to the fs and to ++force it to sleep when you want to write to the fs. ++ ++These are just various tools to investigate the possibility of ++achieving minimal read/write latency when using JFFS[2]. ++ ++You need to manually do a "ps aux" and look up the PID of the gc ++thread and provide it to the program. ++ ++ ++ ++ ++EXECUTING THE PROGRAM "plotJittervsFill" ++ ++This program is a post processing tool that will extract the jitter ++times as printed by the JitterTest program in its console log file ++as well as the data printed by the "df" command. ++ ++This "df" data happens to be in the console log because you will ++run the shell file fillJffs2.sh on a console when you are doing ++your jitter test. ++ ++This shell script copies a specified file to another specified file ++every programmable seconds. It also does a "df" and redirects output ++to /dev/console where it is mixed with the output from JitterTest. ++ ++All this console data is stored on another computer, as all this data ++is being outputted to the serial port as you have redirected the console ++to the serial port (that is the only guaranteed way to not loose any ++console log or printk data). ++ ++You can then run this saved console log through this program and it ++will output a very nice text file with the %fill in one col and ++corrosponding jitter values in the second. gnuplot then does a ++beautifull plot of this resulting file showing you graphically the ++jitters encountered at different fill % of your JFFS[2] fs. ++ ++ ++ ++OTHER COMMENTS: ++ ++Use the "-w BYTES" cmd line parameter to simulate your test case. ++Not everyone has the same requirements. Someone may want to measure ++the jitter of JFFS2 with 500 bytes being written every 500ms. Others ++may want to measure the system performance writing 2048 bytes every ++5 seconds. ++ ++RUNNING MULTIPLE INSTANCES: ++ ++Things get real interesting when you run multiple instances of this ++program *at the same time*. ++ ++You could have one version running as a real time task (by specifing ++the priority with the -p cmd line parameter), not interacting with ++any fs or at the very least not reading and writing to JFFS[2]. ++ ++At the same time you could have another version running as a regular ++task (by not specifing any priority) but reading and writing to JFFS[2]. ++ ++This way you can easily measure the blocking performance of the real time ++task while another non-real time task interacts with JFFS[2] in the back ground. ++ ++You get the idea. ++ ++ ++WATCH OUT! ++ ++Be particularly careful of running this program as a real time task AND ++writing to JFFS[2]. Any blocks will cause your whole system to block till ++any garbage collect initiated by writes by this task complete. I have measured ++these blocks to be of the order of 40-50 seconds on a reasonably powerful ++32 bit embedded system. +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/filljffs2.sh linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/filljffs2.sh +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/filljffs2.sh 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/filljffs2.sh 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,16 @@ ++#!/bin/bash ++ ++# Pass following cmd line: ++# 1st - file to copy ++# 2nd - file to copy to ++# 3rd - time to sleep between copies ++ ++while [ $(( 1 )) -gt $(( 0 )) ] ++do ++ cp $1 $2 ++ rm $2 ++ df |grep mtd > /dev/console ++ echo "sleeping $3" ++ sleep $3 ++done ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/plotJittervsFill.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/plotJittervsFill.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/jittertest/plotJittervsFill.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/jittertest/plotJittervsFill.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,312 @@ ++/* ++ *********************************************************************** ++ * ++ * Copyright: Daniel Measurement and Control, Inc. ++ * 9753 Pine Lake Drive ++ * Houston, TX 77055 ++ * ++ * Created by: Vipin Malik ++ * Released under GPL by permission of Daniel Industries. ++ * ++ * This software is licensed under the GPL version 2. Plese see the file ++ * COPYING for details on the license. ++ * ++ * NO WARRANTY: Absolutely no claims of warranty or fitness of purpose ++ * are made in this software. Please use at your own risk. ++ * ++ File: plotJittervsFill.c ++ By: Vipin Malik ++ ++ About: This program reads in a jitter log file as created ++ by the JitterTest.c program and extracts all the jitters ++ in the file that are greater than a threshold specified ++ as a parameter on the cmd line. It also extracts the ++ amount of disk space at (form the "df" out that should also ++ be present in the log file) after the jitter extracted. ++ ++ It writes the data to the stderr (where you may redirect it). ++ It is suitable for plotting, as the data is written as ++ COL1=UsedSpace COL2=Jitter ++ ++ $Id: plotJittervsFill.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ $Log: not supported by cvs2svn $ ++ Revision 1.6 2005/11/07 11:15:21 gleixner ++ [MTD / JFFS2] Clean up trailing white spaces ++ ++ Revision 1.5 2001/08/10 19:23:11 vipin ++ Ready to be released under GPL! Added proper headers etc. ++ ++ Revision 1.4 2001/07/02 22:25:40 vipin ++ Fixed couple of minor cosmetic typos. ++ ++ Revision 1.3 2001/07/02 14:46:46 vipin ++ Added a debug option where it o/p's line numbers to debug funky values. ++ ++ Revision 1.2 2001/06/26 19:48:57 vipin ++ Now prints out jitter values found at end of log file, after which ++ no new "df" disk usage values were encountered. The last "df" disk usage ++ encountered is printed for these orphaned values. ++ ++ Revision 1.1 2001/06/25 19:13:55 vipin ++ Added new file- plotJittervsFill.c- that mines the data log file ++ outputed from the fillFlash.sh script file and JitterTest.c file ++ and produces output suitable to be plotted. ++ This output plot may be examined to see visually the relationship ++ of the Jitter vs disk usage of the fs under test. ++ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static char Version_string[] = "$Id: plotJittervsFill.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $"; ++static char LogFile[250] = "InputLogFile.log"; ++ ++static int JitterThreshold_ms = 1000; ++static int Debug = 0; /* Debug level. Each "-d" on the cmd line increases the level */ ++ ++#define TRUE 1 ++#define FALSE 0 ++ ++#define MIN_JITTER_THRESHOLD 1 /* ms minimum jitter threshold */ ++ ++void PrintHelpInfo(void) ++{ ++ printf("Usage: plotJittervsFill [options] -f [--file] -t [--jitter_threshold] \n"); ++ printf("[options]:\n-v [--version] Print version and exit\n"); ++ printf("-d Debug. Prints input file line number for each data point picked up.\n"); ++ printf("-h [--help] [-?] Print this help screen and exit.\n"); ++} ++ ++ ++ ++/*********************************************************************** ++ * HandleCmdLineArgs ++ * This function handles the command line arguments. ++ * output: stack size ++ ***********************************************************************/ ++void HandleCmdLineArgs( ++ int argc, /* number of command-line arguments */ ++ char *argv[]) /* ptrs to command-line arguments */ ++{ ++ int argNum; /* argument number */ ++ ++ if (argc > (int) 1) { ++ ++ for (argNum = (int) 1; argNum < argc; argNum++) { ++ ++ /* The command line contains an argument. */ ++ ++ if ((strcmp(argv[argNum],"--version") == 0) || ++ (strcmp(argv[argNum],"-v") == 0)) { ++ /* Print version information and exit. */ ++ printf("%s\n", Version_string); ++ exit(0); ++ } ++ ++ else if ((strcmp(argv[argNum],"--help") == 0) || ++ (strcmp(argv[argNum],"-h") == 0) || ++ (strcmp(argv[argNum],"-?") == 0)) { ++ /* Print help information and exit. */ ++ PrintHelpInfo(); ++ exit(0); ++ } ++ ++ else if ((strcmp(argv[argNum],"--file") == 0) || ++ (strcmp(argv[argNum],"-f") == 0)) { ++ /* Set the name of the output file. */ ++ ++argNum; ++ if (argNum < argc) { ++ strncpy(LogFile, argv[argNum], sizeof(LogFile)); ++ } ++ else { ++ printf("*** Input file name not specified. ***\n"); ++ exit(0); ++ } ++ } ++ ++ else if ((strcmp(argv[argNum],"--jitter_threshold") == 0) || ++ (strcmp(argv[argNum],"-t") == 0)) { ++ /* Set the file to read*/ ++ ++argNum; ++ ++ JitterThreshold_ms = atoi(argv[argNum]); ++ ++ if(JitterThreshold_ms < MIN_JITTER_THRESHOLD) ++ { ++ printf("A jitter threshold less than %i ms is not allowed. Bye.\n", ++ MIN_JITTER_THRESHOLD); ++ exit(0); ++ } ++ } ++ ++ else if ((strcmp(argv[argNum],"-d") == 0)) ++ { ++ /* Increment debug level */ ++ ++ Debug++; ++ } ++ ++ else { ++ /* Unknown argument. Print help information and exit. */ ++ printf("Invalid option %s\n", argv[argNum]); ++ printf("Try 'plotJittervsFill --help' for more information.\n"); ++ exit(0); ++ } ++ } ++ } ++ ++ return; ++} ++ ++ ++ ++ ++ ++int main( ++ int argc, ++ char *argv[]) ++{ ++ ++ char lineBuf[1024]; /* how long a single line be? */ ++ int converted; ++ int lineNo = 0; ++ int cnt; ++ ++ FILE *fp; ++ ++ int junkInt1, junkInt2, junkInt3; ++ float junkFloat1; ++ float jitter_ms; ++ ++#define MAX_SAVE_BUFFER 1000 /* How many values will be picked up while searching for ++ a % disk full line (i.e. before they can be printed out) ++ */ ++ int saveJitter[MAX_SAVE_BUFFER]; /* lets us record multiple jitter values that exceed ++ our threshold till we find a "df" field- which is when ++ we can o/p all these values. ++ */ ++ int dataLineNo[MAX_SAVE_BUFFER]; /* The saved line #'s for the above. Printed if debug specified. */ ++ ++ int saveJitterCnt = 0; ++ int lookFor_df = FALSE; ++ int dfPercent = -1; /* will be >= 0 if at least one found. The init value is a flag. */ ++ ++ char junkStr1[500], junkStr2[500]; ++ ++ HandleCmdLineArgs(argc, argv); ++ ++ if((fp = fopen(LogFile, "r")) == NULL) ++ { ++ printf("Unable to open input log file %s for read.\b", LogFile); ++ perror("Error:"); ++ exit(1); ++ } ++ ++ ++ ++ while(fgets(lineBuf, sizeof(lineBuf), fp) != NULL) ++ { ++ lineNo++; ++ ++ ++ /* Are we looking for a "df" o/p line? (to see how full ++ the flash is?)*/ ++ ++ /* is there a "%" in this line? */ ++ if((strstr(lineBuf, "%") != NULL) && (lookFor_df)) ++ { ++ converted = sscanf(lineBuf, "%s %i %i %i %i\n", ++ junkStr1, &junkInt1, &junkInt2, &junkInt3, &dfPercent); ++ if(converted < 5) ++ { ++ printf("Line %i contains \"%%\", but expected fileds not found. Skipping.\n", lineNo); ++ }else ++ { ++ /* Now print out the saved jitter values (in col2) with this dfPercent value as the col1. */ ++ for(cnt = 0; cnt < saveJitterCnt; cnt++) ++ { ++ if(Debug) ++ { ++ fprintf(stderr, "%i\t%i\t%i\n", (int)dataLineNo[cnt], ++ dfPercent, (int)saveJitter[cnt]); ++ }else ++ { ++ fprintf(stderr, "%i\t%i\n", dfPercent, (int)saveJitter[cnt]); ++ } ++ ++ ++ } ++ ++ saveJitterCnt = 0; /* all flushed. Reset for next saves. */ ++ lookFor_df = FALSE; ++ } ++ ++ } ++ ++ ++ /* is there a "ms" in this line?*/ ++ if(strstr(lineBuf, "ms") == NULL) ++ { ++ continue; ++ } ++ ++ /* grab the ms jitter value */ ++ converted = sscanf(lineBuf, "%f %s %f %s\n", &junkFloat1, junkStr1, &jitter_ms, junkStr2); ++ if(converted < 4) ++ { ++ printf("Line %i contains \"ms\", but expected fileds not found. Converted %i, Skipping.", ++ lineNo, converted); ++ printf("1=%i, 2=%s.\n", junkInt1, junkStr1); ++ continue; /* not our jitter line*/ ++ } ++ ++ /* Is the jitter value > threshold value? */ ++ if(abs(jitter_ms) > JitterThreshold_ms) ++ { ++ /* Found a jitter line that matches our crietrion. ++ Now set flag to be on the look out for the next ++ "df" output so that we can see how full the flash is. ++ */ ++ ++ if(saveJitterCnt < MAX_SAVE_BUFFER) ++ { ++ saveJitter[saveJitterCnt] = (int)abs(jitter_ms); /* why keep the (ms) jitter in float */ ++ dataLineNo[saveJitterCnt] = lineNo; ++ saveJitterCnt++; ++ lookFor_df = TRUE; ++ } ++ else ++ { ++ printf("Oops! I've run out of buffer space before I found a %% use line. Dropping itter value. Increase MAX_SAVE_BUFFER and recompile.\n"); ++ } ++ ++ ++ } ++ ++ } ++ ++ ++ /* Now print out any saved jitter values that were not printed out because we did not find ++ and "df" after these were picked up. Only print if a "df" disk usage was ever found. ++ */ ++ if(lookFor_df && (dfPercent >= 0)) ++ { ++ /* Now print out the saved jitter values (in col2) with this dfPercent value as the col1. */ ++ for(cnt = 0; cnt < saveJitterCnt; cnt++) ++ { ++ fprintf(stderr, "%i\t%i\n", dfPercent, (int)saveJitter[cnt]); ++ } ++ } ++ ++ return 0; ++ ++ ++}/* end main() */ ++ ++ ++ ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/Makefile linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/Makefile +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,48 @@ ++LIBUBI_PATH=../../ubi-utils/new-utils/ ++LIBUBI_SRC_PATH=../../ubi-utils/new-utils/src/ ++LIBUBI_HEADER_PATH=../../ubi-utils/new-utils/include ++UBI_HEADERS_PATH=../../include/ ++UBIUTILS_PATH=../../ubi-utils/new-utils/ ++ ++CC := $(CROSS)gcc ++ ++TESTS=io_update volrefcnt integ io_paral io_read io_basic \ ++ mkvol_basic mkvol_bad mkvol_paral rsvol ++ ++HELPER_NAMES=ubiupdatevol ++HELPERS=$(addprefix helpers/, $(HELPER_NAMES)) ++ ++# Because of implicite rules we use make treats .o files as intermediate, thus ++# it removes the. If you want to prevent the removal, uncomment the below ++#.SECONDARY: $(addsuffix .o, $(TESTS)) $(addsuffix .o, $(HELPERS)) ++ ++CFLAGS += -Wall -I$(LIBUBI_HEADER_PATH) -I $(UBI_HEADERS_PATH) -L. -O2 ++ ++all: ubi-utils libubi $(TESTS) $(HELPERS) ++ ++# Compile ubilib with the udevsettle hack ++libubi: $(LIBUBI_SRC_PATH)/libubi.c $(LIBUBI_HEADER_PATH)/libubi.h $(LIBUBI_SRC_PATH)/libubi_int.h ++ $(CC) $(CFLAGS) -I $(LIBUBI_SRC_PATH) -I../../include -DUDEV_SETTLE_HACK -c $(LIBUBI_SRC_PATH)/libubi.c -o libubi.o ++ ar cr libubi.a libubi.o ++ ++# The below cancels existing implicite rule to make programs from .c files, ++# in order to force make using our rule defined below ++%: %.c ++ ++# The below is the rule to get an .o file from a .c file ++%.o: %.c ++ $(CC) $(CFLAGS) $< -c -o $@ ++ ++# And the below is the rule to get final test executable from its .o and common.o ++%: %.o common.o ++ $(CC) $(CFLAGS) $^ -lubi -o $@ ++ ++# *paral tests require libpthread, thus the below rule for them ++%paral: %paral.o common.o ++ $(CC) $(CFLAGS) $^ -lubi -lpthread -o $@ ++ ++ubi-utils: ++ make -C $(UBIUTILS_PATH) ++ ++clean: ++ rm -f $(TESTS) $(addsuffix .o, $(TESTS)) libubi.* $(HELPERS) $(addsuffix .o, $(HELPERS)) +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/README.udev linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/README.udev +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/README.udev 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/README.udev 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,25 @@ ++There is a problem with udev: when a volume is created, there is a delay ++before corresponding /dev/ubiX_Y device node is created by udev, so some ++tests fail because of this. The symptom is error messages like ++"cannot open /dev/ubi0_0". ++ ++One possible solution of this problem is to pre-create UBI device and volume ++nodes. There is even a script which may be used for this in ubi-utils/scripts/. ++But this is not enough because udev will still remove and re-create the nodes ++and tests will still fail. So you need to stop removing device nodes using ++the following udev rule: ++ ++ KERNEL=="ubi*_*", ACTION=="remove", OPTIONS+="ignore_device" ++ ++In our Ubuntu distribution we put that to new file: ++/etc/udev/rules.d/50-local.rules ++ ++Another possibility is to call udevsettle utility in libubi after the volume ++has been created See src/libubi.c - the call is compiled in only if ++UDEV_SETTLE_HACK is defined. This is anyway an ugly hack, but works, although ++makes the tests slower. Suggestions are welcome. ++ ++So, if you have udevsettel unility in your system, you do not have to do ++anyting, and the tests should work, because we compile libubi with ++UDEV_SETTLE_HACK. Otherwise, you should remove -D UDEV_SETTLE_HACK ++from the makefile and pre-create UBI device nodes. +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/common.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/common.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/common.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/common.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,336 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem B. Bityutskiy ++ * ++ * The stuff which is common for many tests. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "libubi.h" ++#include "common.h" ++ ++/** ++ * __initial_check - check that common prerequisites which are required to run ++ * tests. ++ * ++ * @test test name ++ * @argc count of command-line arguments ++ * @argv command-line arguments ++ * ++ * This function returns %0 if all is fine and test may be run and %-1 if not. ++ */ ++int __initial_check(const char *test, int argc, char * const argv[]) ++{ ++ libubi_t libubi; ++ struct ubi_dev_info dev_info; ++ ++ /* ++ * All tests require UBI character device name as the first parameter, ++ * check this. ++ */ ++ if (argc < 2) { ++ __err_msg(test, __FUNCTION__, __LINE__, ++ "UBI character device node is not specified"); ++ return -1; ++ } ++ ++ libubi = libubi_open(); ++ if (libubi == NULL) { ++ __failed(test, __FUNCTION__, __LINE__, "libubi_open"); ++ return -1; ++ } ++ ++ if (ubi_get_dev_info(libubi, argv[1], &dev_info)) { ++ __failed(test, __FUNCTION__, __LINE__, "ubi_get_dev_info"); ++ goto close; ++ } ++ ++ if (dev_info.avail_lebs < MIN_AVAIL_EBS) { ++ __err_msg(test, __FUNCTION__, __LINE__, ++ "insufficient available eraseblocks %d on UBI " ++ "device, required %d", ++ dev_info.avail_lebs, MIN_AVAIL_EBS); ++ goto close; ++ } ++ ++ if (dev_info.vol_count != 0) { ++ __err_msg(test, __FUNCTION__, __LINE__, ++ "device %s is not empty", argv[1]); ++ goto close; ++ } ++ ++ libubi_close(libubi); ++ return 0; ++ ++close: ++ libubi_close(libubi); ++ return -1; ++} ++ ++/** ++ * __err_msg - print a message to stderr. ++ * ++ * @test test name ++ * @func function name ++ * @line line number ++ * @fmt format string ++ */ ++void __err_msg(const char *test, const char *func, int line, ++ const char *fmt, ...) ++{ ++ va_list args; ++ ++ fprintf(stderr, "[%s] %s():%d: ", test, func, line); ++ va_start(args, fmt); ++ vfprintf(stderr, fmt, args); ++ fprintf(stderr, "\n"); ++ va_end(args); ++} ++ ++/** ++ * __failed - print function fail message. ++ * ++ * @test test name ++ * @func calling function name ++ * @line line number ++ * @failed failed function name ++ */ ++void __failed(const char *test, const char *func, int line, ++ const char *failed) ++{ ++ fprintf(stderr, "[%s] %s():%d: function %s() failed with error %d (%s)\n", ++ test, func, line, failed, errno, strerror(errno)); ++} ++ ++/** ++ * __check_volume - check volume information. ++ * ++ * @libubi libubi descriptor ++ * @dev_info UBI device description ++ * @test test name ++ * @func function name ++ * @line line number ++ * @vol_id ID of existing volume to check ++ * @req volume creation request to compare with ++ * ++ * This function checks if a volume created using @req request has exactly the ++ * requested characteristics. Returns 0 in case of success and %-1 in case of ++ * error. ++ */ ++int __check_volume(libubi_t libubi, struct ubi_dev_info *dev_info, ++ const char *test, const char *func, int line, int vol_id, ++ const struct ubi_mkvol_request *req) ++{ ++ int ret; ++ struct ubi_vol_info vol_info; ++ int leb_size; ++ long long rsvd_bytes; ++ ++ ret = ubi_get_vol_info1(libubi, dev_info->dev_num, vol_id, &vol_info); ++ if (ret) { ++ __failed(test, func, line, "ubi_get_vol_info"); ++ return -1; ++ } ++ ++ if (req->alignment != vol_info.alignment) { ++ __err_msg(test, func, line, ++ "bad alignment: requested %d, got %d", ++ req->alignment, vol_info.alignment); ++ return -1; ++ } ++ if (req->vol_type != vol_info.type) { ++ __err_msg(test, func, line, "bad type: requested %d, got %d", ++ req->vol_type, vol_info.type); ++ return -1; ++ } ++ if (strlen(req->name) != strlen(vol_info.name) || ++ strcmp(req->name, vol_info.name) != 0) { ++ __err_msg(test, func, line, ++ "bad name: requested \"%s\", got \"%s\"", ++ req->name, vol_info.name); ++ return -1; ++ } ++ if (vol_info.corrupted) { ++ __err_msg(test, func, line, "corrupted new volume"); ++ return -1; ++ } ++ ++ leb_size = dev_info->leb_size - (dev_info->leb_size % req->alignment); ++ if (leb_size != vol_info.leb_size) { ++ __err_msg(test, func, line, ++ "bad usable LEB size %d, should be %d", ++ vol_info.leb_size, leb_size); ++ return -1; ++ } ++ ++ rsvd_bytes = req->bytes; ++ if (rsvd_bytes % leb_size) ++ rsvd_bytes += leb_size - (rsvd_bytes % leb_size); ++ ++ if (rsvd_bytes != vol_info.rsvd_bytes) { ++ __err_msg(test, func, line, ++ "bad reserved bytes %lld, should be %lld", ++ vol_info.rsvd_bytes, rsvd_bytes); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/** ++ * __check_vol_patt - check that volume contains certain data ++ * ++ * @libubi libubi descriptor ++ * @dev_info UBI device description ++ * @test test name ++ * @func function name ++ * @line line number ++ * @node volume character device node ++ * @byte data pattern to check ++ * ++ * This function returns %0 if the volume contains only @byte bytes, and %-1 if ++ * not. ++ */ ++int __check_vol_patt(libubi_t libubi, struct ubi_dev_info *dev_info, ++ const char *test, const char *func, int line, ++ const char *node, uint8_t byte) ++{ ++ int ret, fd; ++ long long bytes = 0; ++ struct ubi_vol_info vol_info; ++ unsigned char buf[512]; ++ ++ fd = open(node, O_RDONLY); ++ if (fd == -1) { ++ __failed(test, func, line, "open"); ++ __err_msg(test, func, line, "cannot open \"%s\"\n", node); ++ return -1; ++ } ++ ++ ret = ubi_get_vol_info(libubi, node, &vol_info); ++ if (ret) { ++ __failed(test, func, line, "ubi_get_vol_info"); ++ goto close; ++ } ++ ++ while (bytes < vol_info.data_bytes) { ++ int i; ++ ++ memset(buf, ~byte, 512); ++ ret = read(fd, buf, 512); ++ if (ret == -1) { ++ __failed(test, func, line, "read"); ++ __err_msg(test, func, line, "bytes = %lld, ret = %d", ++ bytes, ret); ++ goto close; ++ } ++ ++ if (ret == 0 && bytes + ret < vol_info.data_bytes) { ++ __err_msg(test, func, line, ++ "EOF, but read only %lld bytes of %lld", ++ bytes + ret, vol_info.data_bytes); ++ goto close; ++ } ++ ++ for (i = 0; i < ret; i++) ++ if (buf[i] != byte) { ++ __err_msg(test, func, line, ++ "byte at %lld is not %#x but %#x", ++ bytes + i, byte, (int)buf[i]); ++ goto close; ++ } ++ ++ bytes += ret; ++ } ++ ++ close(fd); ++ return 0; ++ ++close: ++ close(fd); ++ return -1; ++} ++ ++/** ++ * __update_vol_patt - update volume using a certain byte pattern ++ * ++ * @libubi libubi descriptor ++ * @dev_info UBI device description ++ * @test test name ++ * @func function name ++ * @line line number ++ * @node volume character device node ++ * @byte data pattern to check ++ * ++ * This function returns %0 in case of success, and %-1 if in case of failure. ++ */ ++int __update_vol_patt(libubi_t libubi, const char *test, const char *func, ++ int line, const char *node, long long bytes, uint8_t byte) ++{ ++ int ret, fd; ++ long long written = 0; ++ unsigned char buf[512]; ++ ++ fd = open(node, O_RDWR); ++ if (fd == -1) { ++ __failed(test, func, line, "open"); ++ __err_msg(test, func, line, "cannot open \"%s\"\n", node); ++ return -1; ++ } ++ ++ if (ubi_update_start(libubi, fd, bytes)) { ++ __failed(test, func, line, "ubi_update_start"); ++ __err_msg(test, func, line, "bytes = %lld", bytes); ++ goto close; ++ } ++ ++ memset(buf, byte, 512); ++ ++ while (written != bytes) { ++ ret = write(fd, buf, 512); ++ if (ret == -1) { ++ __failed(test, func, line, "write"); ++ __err_msg(test, func, line, "written = %lld, ret = %d", ++ written, ret); ++ goto close; ++ } ++ written += ret; ++ ++ if (written > bytes) { ++ __err_msg(test, func, line, "update length %lld bytes, " ++ "but %lld bytes are already written", ++ bytes, written); ++ goto close; ++ } ++ } ++ ++ close(fd); ++ return 0; ++ ++close: ++ close(fd); ++ return -1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/common.h linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/common.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/common.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/common.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,103 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem B. Bityutskiy ++ * ++ * The stuff which is common for many tests. ++ */ ++ ++#ifndef __COMMON_H__ ++#define __COMMON_H__ ++ ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define UBI_VOLUME_PATTERN "/dev/ubi%d_%d" ++#define MIN_AVAIL_EBS 5 ++#define PAGE_SIZE 4096 ++ ++#define min(a, b) ((a) < (b) ? (a) : (b)) ++ ++#define err_msg(fmt, ...) \ ++ __err_msg(TESTNAME, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) ++ ++#define failed(name) \ ++ __failed(TESTNAME, __FUNCTION__, __LINE__, name) ++ ++#define initial_check(argc, argv) \ ++ __initial_check(TESTNAME, argc, argv) ++ ++#define check_volume(vol_id, req) \ ++ __check_volume(libubi, &dev_info, TESTNAME, __FUNCTION__, \ ++ __LINE__, vol_id, req) ++ ++#define check_vol_patt(node, byte) \ ++ __check_vol_patt(libubi, &dev_info, TESTNAME, __FUNCTION__, __LINE__, \ ++ node, byte) ++ ++#define update_vol_patt(node, bytes, byte) \ ++ __update_vol_patt(libubi, TESTNAME, __FUNCTION__, __LINE__, \ ++ node, bytes, byte) ++ ++#define check_failed(ret, error, func, fmt, ...) ({ \ ++ int __ret; \ ++ \ ++ if (!ret) { \ ++ err_msg("%s() returned success but should have failed", func); \ ++ err_msg(fmt, ##__VA_ARGS__); \ ++ __ret = -1; \ ++ } \ ++ if (errno != (error)) { \ ++ err_msg("%s failed with error %d (%s), expected %d (%s)", \ ++ func, errno, strerror(errno), error, strerror(error)); \ ++ err_msg(fmt, ##__VA_ARGS__); \ ++ __ret = -1; \ ++ } \ ++ __ret = 0; \ ++}) ++ ++/* Alignments to test, @s is eraseblock size */ ++#define ALIGNMENTS(s) \ ++ {3, 5, 27, 666, 512, 1024, 2048, (s)/2-3, (s)/2-2, (s)/2-1, (s)/2+1, \ ++ (s)/2+2, (s)/2+3, (s)/3-3, (s)/3-2, (s)/3-1, (s)/3+1, (s)/3+2, \ ++ (s)/3+3, (s)/4-3, (s)/4-2, (s)/4-1, (s)/4+1, (s)/4+2, (s)/4+3, \ ++ (s)/5-3, (s)/5-2, (s)/5-1, (s)/5+1, (s)/5+2, (s)/5+3, (s)-17, (s)-9, \ ++ (s)-8, (s)-6, (s)-4, (s)-1, (s)}; ++ ++extern void __err_msg(const char *test, const char *func, int line, ++ const char *fmt, ...); ++void __failed(const char *test, const char *func, int line, ++ const char *failed); ++int __initial_check(const char *test, int argc, char * const argv[]); ++int __check_volume(libubi_t libubi, struct ubi_dev_info *dev_info, ++ const char *test, const char *func, int line, int vol_id, ++ const struct ubi_mkvol_request *req); ++int __check_vol_patt(libubi_t libubi, struct ubi_dev_info *dev_info, ++ const char *test, const char *func, int line, ++ const char *node, uint8_t byte); ++int __update_vol_patt(libubi_t libubi, const char *test, const char *func, ++ int line, const char *node, long long bytes, ++ uint8_t byte); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* !__COMMON_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/integ.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/integ.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/integ.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/integ.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,783 @@ ++#define _LARGEFILE64_SOURCE ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "libubi.h" ++ ++struct erase_block_info; ++struct volume_info; ++struct ubi_device_info; ++ ++struct write_info ++{ ++ struct write_info *next; ++ struct erase_block_info *erase_block; ++ int offset_within_block; /* Offset within erase block */ ++ off64_t offset; /* Offset within volume */ ++ int size; ++ int random_seed; ++}; ++ ++struct erase_block_info ++{ ++ struct volume_info *volume; ++ int block_number; ++ off64_t offset; /* Offset within volume */ ++ off64_t top_of_data; ++ int touched; /* Have we done anything at all with this erase block */ ++ int erased; /* This erased block is currently erased */ ++ struct write_info *writes; ++}; ++ ++struct volume_fd ++{ ++ struct volume_fd *next; ++ struct volume_info *volume; ++ int fd; ++}; ++ ++struct volume_info ++{ ++ struct volume_info *next; ++ struct ubi_device_info *ubi_device; ++ struct volume_fd *fds; ++ struct erase_block_info *erase_blocks; ++ const char *device_file_name; ++ struct ubi_vol_info info; ++}; ++ ++struct ubi_device_info ++{ ++ struct volume_info *volumes; ++ const char *device_file_name; ++ struct ubi_dev_info info; ++}; ++ ++struct open_volume_fd ++{ ++ struct open_volume_fd *next; ++ struct volume_fd *vol_fd; ++}; ++ ++#define MAX_UBI_DEVICES 64 ++ ++static libubi_t libubi; ++ ++static struct ubi_info info; ++static struct ubi_device_info ubi_array[MAX_UBI_DEVICES]; ++ ++static uint64_t total_written = 0; ++static uint64_t total_space = 0; ++ ++static struct open_volume_fd *open_volumes; ++static size_t open_volume_count = 0; ++ ++static const char *ubi_module_load_string; ++ ++static unsigned char *write_buffer = NULL; ++static unsigned char *read_buffer = NULL; ++ ++static long long max_ebs_per_vol = 0; /* max number of ebs per vol (zero => no max) */ ++ ++static unsigned long next_seed = 1; ++ ++static unsigned get_next_seed() ++{ ++ next_seed = next_seed * 1103515245 + 12345; ++ return ((unsigned) (next_seed / 65536) % 32768); ++} ++ ++static void error_exit(const char *msg) ++{ ++ int eno = errno; ++ fprintf(stderr,"UBI Integrity Test Error: %s\n",msg); ++ if (eno) { ++ fprintf(stderr, "errno = %d\n", eno); ++ fprintf(stderr, "strerror = %s\n", strerror(eno)); ++ } ++ exit(1); ++} ++ ++static void *allocate(size_t n) ++{ ++ void *p = malloc(n); ++ if (!p) ++ error_exit("Memory allocation failure"); ++ memset(p, 0, n); ++ return p; ++} ++ ++static unsigned get_random_number(unsigned n) ++{ ++ uint64_t r, b; ++ ++ if (n < 1) ++ return 0; ++ r = rand(); ++ r *= n; ++ b = RAND_MAX; ++ b += 1; ++ r /= b; ++ return r; ++} ++ ++static struct volume_fd *open_volume(struct volume_info *vol) ++{ ++ struct volume_fd *s; ++ struct open_volume_fd *ofd; ++ int fd; ++ ++ if (vol->fds) { ++ /* If already open dup it */ ++ fd = dup(vol->fds->fd); ++ if (fd == -1) ++ error_exit("Failed to dup volume device file des"); ++ } else { ++ fd = open(vol->device_file_name, O_RDWR | O_LARGEFILE); ++ if (fd == -1) ++ error_exit("Failed to open volume device file"); ++ } ++ s = allocate(sizeof(*s)); ++ s->fd = fd; ++ s->volume = vol; ++ s->next = vol->fds; ++ vol->fds = s; ++ /* Add to open volumes list */ ++ ofd = allocate(sizeof(*ofd)); ++ ofd->vol_fd = s; ++ ofd->next = open_volumes; ++ open_volumes = ofd; ++ open_volume_count += 1; ++ return 0; ++} ++ ++static void close_volume(struct volume_fd *vol_fd) ++{ ++ struct volume_fd *vfd, *vfd_last; ++ struct open_volume_fd *ofd, *ofd_last; ++ int fd = vol_fd->fd; ++ ++ /* Remove from open volumes list */ ++ ofd_last = NULL; ++ ofd = open_volumes; ++ while (ofd) { ++ if (ofd->vol_fd == vol_fd) { ++ if (ofd_last) ++ ofd_last->next = ofd->next; ++ else ++ open_volumes = ofd->next; ++ free(ofd); ++ open_volume_count -= 1; ++ break; ++ } ++ ofd_last = ofd; ++ ofd = ofd->next; ++ } ++ /* Remove from volume fd list */ ++ vfd_last = NULL; ++ vfd = vol_fd->volume->fds; ++ while (vfd) { ++ if (vfd == vol_fd) { ++ if (vfd_last) ++ vfd_last->next = vfd->next; ++ else ++ vol_fd->volume->fds = vfd->next; ++ free(vfd); ++ break; ++ } ++ vfd_last = vfd; ++ vfd = vfd->next; ++ } ++ /* Close volume device file */ ++ if (close(fd) == -1) ++ error_exit("Failed to close volume file descriptor"); ++} ++ ++static void set_random_data(unsigned seed, unsigned char *buf, int size) ++{ ++ int i; ++ unsigned r; ++ ++ r = rand(); ++ srand(seed); ++ for (i = 0; i < size; ++i) ++ buf[i] = rand(); ++ srand(r); ++} ++ ++#if 0 ++static void print_write_info(struct write_info *w) ++{ ++ printf("Offset: %lld Size:%d Seed:%u\n", w->offset, w->size, w->random_seed); ++ fflush(stdout); ++} ++#endif ++ ++static void check_erase_block(struct erase_block_info *erase_block, int fd) ++{ ++ struct write_info *w; ++ off64_t gap_end; ++ int leb_size = erase_block->volume->info.leb_size; ++ ssize_t bytes_read; ++ ++ w = erase_block->writes; ++ gap_end = erase_block->offset + leb_size; ++ while (w) { ++ if (w->offset + w->size < gap_end) { ++ /* There is a gap. Check all 0xff */ ++ off64_t gap_start = w->offset + w->size; ++ size_t size = gap_end - gap_start; ++ if (lseek64(fd, gap_start, SEEK_SET) != gap_start) ++ error_exit("lseek64 failed"); ++ memset(read_buffer, 0 , size); ++ errno = 0; ++ bytes_read = read(fd, read_buffer, size); ++ if (bytes_read != size) ++ error_exit("read failed in gap"); ++ while (size) ++ if (read_buffer[--size] != 0xff) { ++ fprintf(stderr, "block no. = %d\n" , erase_block->block_number); ++ fprintf(stderr, "offset = %lld\n" , (long long) gap_start); ++ fprintf(stderr, "size = %ld\n" , (long) bytes_read); ++ error_exit("verify 0xff failed"); ++ } ++ } ++ if (lseek64(fd, w->offset, SEEK_SET) != w->offset) ++ error_exit("lseek64 failed"); ++ memset(read_buffer, 0 , w->size); ++ errno = 0; ++ bytes_read = read(fd, read_buffer, w->size); ++ if (bytes_read != w->size) { ++ fprintf(stderr, "offset = %lld\n" , (long long) w->offset); ++ fprintf(stderr, "size = %ld\n" , (long) w->size); ++ fprintf(stderr, "bytes_read = %ld\n" , (long) bytes_read); ++ error_exit("read failed"); ++ } ++ set_random_data(w->random_seed, write_buffer, w->size); ++ if (memcmp(read_buffer, write_buffer, w->size)) ++ error_exit("verify failed"); ++ gap_end = w->offset; ++ w = w->next; ++ } ++ if (gap_end > erase_block->offset) { ++ /* Check all 0xff */ ++ off64_t gap_start = erase_block->offset; ++ size_t size = gap_end - gap_start; ++ if (lseek64(fd, gap_start, SEEK_SET) != gap_start) ++ error_exit("lseek64 failed"); ++ memset(read_buffer, 0 , size); ++ errno = 0; ++ bytes_read = read(fd, read_buffer, size); ++ if (bytes_read != size) ++ error_exit("read failed in gap"); ++ while (size) ++ if (read_buffer[--size] != 0xff) { ++ fprintf(stderr, "block no. = %d\n" , erase_block->block_number); ++ fprintf(stderr, "offset = %lld\n" , (long long) gap_start); ++ fprintf(stderr, "size = %ld\n" , (long) bytes_read); ++ error_exit("verify 0xff failed!"); ++ } ++ } ++} ++ ++static int write_to_erase_block(struct erase_block_info *erase_block, int fd) ++{ ++ int page_size = erase_block->volume->ubi_device->info.min_io_size; ++ int leb_size = erase_block->volume->info.leb_size; ++ int next_offset = 0; ++ int space, size; ++ off64_t offset; ++ unsigned seed; ++ struct write_info *w; ++ ++ if (erase_block->writes) ++ next_offset = erase_block->writes->offset_within_block + erase_block->writes->size; ++ space = leb_size - next_offset; ++ if (space <= 0) ++ return 0; /* No space */ ++ if (!get_random_number(10)) { ++ /* 1 time in 10 leave a gap */ ++ next_offset += get_random_number(space); ++ next_offset = (next_offset / page_size) * page_size; ++ space = leb_size - next_offset; ++ } ++ if (get_random_number(2)) ++ size = 1 * page_size; ++ else if (get_random_number(2)) ++ size = 2 * page_size; ++ else if (get_random_number(2)) ++ size = 3 * page_size; ++ else if (get_random_number(2)) ++ size = 4 * page_size; ++ else { ++ if (get_random_number(4)) ++ size = get_random_number(space); ++ else ++ size = space; ++ size = (size / page_size) * page_size; ++ } ++ if (size == 0 || size > space) ++ size = page_size; ++ if (next_offset + size > leb_size) ++ error_exit("internal error"); ++ offset = erase_block->offset + next_offset; ++ if (offset < erase_block->top_of_data) ++ error_exit("internal error!"); ++ if (lseek64(fd, offset, SEEK_SET) != offset) ++ error_exit("lseek64 failed"); ++ /* Do write */ ++ seed = get_next_seed(); ++ if (!seed) ++ seed = 1; ++ set_random_data(seed, write_buffer, size); ++ if (write(fd, write_buffer, size) != size) ++ error_exit("write failed"); ++ erase_block->top_of_data = offset + size; ++ /* Make write info and add to eb */ ++ w = allocate(sizeof(*w)); ++ w->offset_within_block = next_offset; ++ w->offset = offset; ++ w->size = size; ++ w->random_seed = seed; ++ w->next = erase_block->writes; ++ erase_block->writes = w; ++ erase_block->touched = 1; ++ erase_block->erased = 0; ++ total_written += size; ++ return 1; ++} ++ ++static void erase_erase_block(struct erase_block_info *erase_block, int fd) ++{ ++ struct write_info *w; ++ uint32_t eb_no; ++ int res; ++ ++ eb_no = erase_block->block_number; ++ res = ioctl(fd, UBI_IOCEBER, &eb_no); ++ if (res) ++ error_exit("Failed to erase an erase block"); ++ /* Remove writes from this eb */ ++ while (erase_block->writes) { ++ w = erase_block->writes; ++ erase_block->writes = erase_block->writes->next; ++ free(w); ++ } ++ erase_block->erased = 1; ++ erase_block->touched = 1; ++ erase_block->top_of_data = erase_block->offset; ++} ++ ++static void operate_on_erase_block(struct erase_block_info *erase_block, int fd) ++{ ++ /* ++ Possible operations: ++ read from it and verify ++ write to it ++ erase it ++ */ ++ int work_done = 1; ++ static int no_work_done_count = 0; ++ ++ if (!get_random_number(10) && no_work_done_count <= 5) { ++ check_erase_block(erase_block, fd); ++ work_done = 0; ++ } else if (get_random_number(100)) { ++ if (!write_to_erase_block(erase_block, fd)) { ++ /* The erase block was full */ ++ if (get_random_number(2) || no_work_done_count > 5) ++ erase_erase_block(erase_block, fd); ++ else ++ work_done = 0; ++ } ++ } else ++ erase_erase_block(erase_block, fd); ++ if (work_done) ++ no_work_done_count = 0; ++ else ++ no_work_done_count += 1; ++} ++ ++static void operate_on_open_volume(struct volume_fd *vol_fd) ++{ ++ /* ++ Possible operations: ++ operate on an erase block ++ close volume ++ */ ++ if (get_random_number(100) == 0) ++ close_volume(vol_fd); ++ else { ++ /* Pick an erase block at random */ ++ int eb_no = get_random_number(vol_fd->volume->info.rsvd_lebs); ++ operate_on_erase_block(&vol_fd->volume->erase_blocks[eb_no], vol_fd->fd); ++ } ++} ++ ++static void operate_on_volume(struct volume_info *vol) ++{ ++ /* ++ Possible operations: ++ open it ++ resize it (must close fd's first) <- TODO ++ delete it (must close fd's first) <- TODO ++ */ ++ open_volume(vol); ++} ++ ++static int ubi_major(const char *device_file_name) ++{ ++ struct stat buf; ++ static int maj = 0; ++ ++ if (maj) ++ return maj; ++ if (stat(device_file_name, &buf) == -1) ++ error_exit("Failed to stat ubi device file"); ++ maj = major(buf.st_rdev); ++ return maj; ++} ++ ++static void operate_on_ubi_device(struct ubi_device_info *ubi_device) ++{ ++ /* ++ TODO: ++ Possible operations: ++ create a new volume ++ operate on existing volume ++ */ ++ /* ++ Simplified operation (i.e. only have 1 volume): ++ If there are no volumes create 1 volumne ++ Then operate on the volume ++ */ ++ if (ubi_device->info.vol_count == 0) { ++ /* Create the one-and-only volume we will use */ ++ char dev_name[1024]; ++ int i, n, maj, fd; ++ struct volume_info *s; ++ struct ubi_mkvol_request req; ++ ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ req.alignment = 1; /* TODO: What is this? */ ++ req.bytes = ubi_device->info.leb_size * max_ebs_per_vol; ++ if (req.bytes == 0 || req.bytes > ubi_device->info.avail_bytes) ++ req.bytes = ubi_device->info.avail_bytes; ++ req.vol_type = UBI_DYNAMIC_VOLUME; ++ req.name = "integ-test-vol"; ++ if (ubi_mkvol(libubi, ubi_device->device_file_name, &req)) ++ error_exit("ubi_mkvol failed"); ++ s = allocate(sizeof(*s)); ++ s->ubi_device = ubi_device; ++ if (ubi_get_vol_info1(libubi, ubi_device->info.dev_num, req.vol_id, &s->info)) ++ error_exit("ubi_get_vol_info failed"); ++ n = s->info.rsvd_lebs; ++ s->erase_blocks = allocate(sizeof(struct erase_block_info) * n); ++ for (i = 0; i < n; ++i) { ++ s->erase_blocks[i].volume = s; ++ s->erase_blocks[i].block_number = i; ++ s->erase_blocks[i].offset = i * (off64_t) s->info.leb_size; ++ s->erase_blocks[i].top_of_data = s->erase_blocks[i].offset; ++ } ++ /* FIXME: Correctly get device file name */ ++ sprintf(dev_name, "%s_%d", ubi_device->device_file_name, req.vol_id); ++ s->device_file_name = strdup(dev_name); ++ ubi_device->volumes = s; ++ ubi_device->info.vol_count += 1; ++ sleep(1); ++ fd = open(s->device_file_name, O_RDONLY); ++ if (fd == -1) { ++ /* FIXME: Correctly make node */ ++ maj = ubi_major(ubi_device->device_file_name); ++ sprintf(dev_name, "mknod %s c %d %d", s->device_file_name, maj, req.vol_id + 1); ++ system(dev_name); ++ } else if (close(fd) == -1) ++ error_exit("Failed to close volume device file"); ++ } ++ operate_on_volume(ubi_device->volumes); ++} ++ ++static void do_an_operation(void) ++{ ++ int too_few = (open_volume_count < info.dev_count * 3); ++ int too_many = (open_volume_count > info.dev_count * 5); ++ ++ if (too_many || (!too_few && get_random_number(1000) > 0)) { ++ /* Operate on an open volume */ ++ size_t pos; ++ struct open_volume_fd *ofd; ++ pos = get_random_number(open_volume_count); ++ for (ofd = open_volumes; pos && ofd && ofd->next; --pos) ++ ofd = ofd->next; ++ operate_on_open_volume(ofd->vol_fd); ++ } else if (info.dev_count > 0) { ++ /* Operate on a ubi device */ ++ size_t ubi_pos = 0; ++ if (info.dev_count > 1) ++ ubi_pos = get_random_number(info.dev_count - 1); ++ operate_on_ubi_device(&ubi_array[ubi_pos]); ++ } else ++ error_exit("Internal error"); ++} ++ ++static void get_ubi_devices_info(void) ++{ ++ int i, ubi_pos = 0; ++ char dev_name[1024]; ++ size_t buf_size = 1024 * 128; ++ ++ if (ubi_get_info(libubi, &info)) ++ error_exit("ubi_get_info failed"); ++ if (info.dev_count > MAX_UBI_DEVICES) ++ error_exit("Too many ubi devices"); ++ for (i = info.lowest_dev_num; i <= info.highest_dev_num; ++i) { ++ struct ubi_device_info *s; ++ s = &ubi_array[ubi_pos++]; ++ if (ubi_get_dev_info1(libubi, i, &s->info)) ++ error_exit("ubi_get_dev_info1 failed"); ++ if (s->info.vol_count) ++ error_exit("There are existing volumes"); ++ /* FIXME: Correctly get device file name */ ++ sprintf(dev_name, "/dev/ubi%d", i); ++ s->device_file_name = strdup(dev_name); ++ if (buf_size < s->info.leb_size) ++ buf_size = s->info.leb_size; ++ if (max_ebs_per_vol && s->info.leb_size * max_ebs_per_vol < s->info.avail_bytes) ++ total_space += s->info.leb_size * max_ebs_per_vol; ++ else ++ total_space += s->info.avail_bytes; ++ } ++ write_buffer = allocate(buf_size); ++ read_buffer = allocate(buf_size); ++} ++ ++static void load_ubi(void) ++{ ++ system("rmmod ubi"); ++ if (system(ubi_module_load_string) != 0) ++ error_exit("Failed to load UBI module"); ++ sleep(1); ++} ++ ++static void do_some_operations(void) ++{ ++ unsigned i = 0; ++ total_written = 0; ++ printf("Total space: %llu\n", (unsigned long long) total_space); ++ while (total_written < total_space * 3) { ++ do_an_operation(); ++ if (i++ % 10000 == 0) ++ printf("Total written: %llu\n", (unsigned long long) total_written); ++ } ++ printf("Total written: %llu\n", (unsigned long long) total_written); ++} ++ ++static void reload_ubi(void) ++{ ++ /* Remove module */ ++ if (system("rmmod ubi") != 0) ++ error_exit("Failed to remove UBI module"); ++ /* Install module */ ++ if (system(ubi_module_load_string) != 0) ++ error_exit("Failed to load UBI module"); ++ sleep(1); ++} ++ ++static void check_volume(struct volume_info *vol) ++{ ++ struct erase_block_info *eb = vol->erase_blocks; ++ int pos; ++ int fd; ++ ++ fd = open(vol->device_file_name, O_RDWR | O_LARGEFILE); ++ if (fd == -1) ++ error_exit("Failed to open volume device file"); ++ for (pos = 0; pos < vol->info.rsvd_lebs; ++pos) ++ check_erase_block(eb++, fd); ++ if (close(fd) == -1) ++ error_exit("Failed to close volume device file"); ++} ++ ++static void check_ubi_device(struct ubi_device_info *ubi_device) ++{ ++ struct volume_info *vol; ++ ++ vol = ubi_device->volumes; ++ while (vol) { ++ check_volume(vol); ++ vol = vol->next; ++ } ++} ++ ++static void check_ubi(void) ++{ ++ int i; ++ ++ for (i = 0; i < info.dev_count; ++i) ++ check_ubi_device(&ubi_array[i]); ++} ++ ++static int is_all_digits(const char *s) ++{ ++ const char *digits = "0123456789"; ++ if (!s || !*s) ++ return 0; ++ for (;*s;++s) ++ if (!strchr(digits,*s)) ++ return 0; ++ return 1; ++} ++ ++static int get_short_arg(int *pos,const char *name,long long *result,int argc,char *argv[]) ++{ ++ const char *p = NULL; ++ int i = *pos; ++ size_t n = strlen(name); ++ ++ if (strlen(argv[i]) > n) ++ p = argv[i] + n; ++ else if (++i < argc) ++ p = argv[i]; ++ if (!is_all_digits(p)) ++ return 1; ++ *result = atoll(p); ++ *pos = i; ++ return 0; ++} ++ ++static int get_long_arg(int *pos,const char *name,long long *result,int argc,char *argv[]) ++{ ++ const char *p = NULL; ++ int i = *pos; ++ size_t n = strlen(name); ++ ++ if (strlen(argv[i]) > n) ++ p = argv[i] + n; ++ else if (++i < argc) ++ p = argv[i]; ++ if (p && *p == '=') { ++ p += 1; ++ if (!*p && ++i < argc) ++ p = argv[i]; ++ } ++ if (!is_all_digits(p)) ++ return 1; ++ *result = atoll(p); ++ *pos = i; ++ return 0; ++} ++ ++static int remove_all_volumes(void) ++{ ++ int i; ++ ++ for (i = 0; i < info.dev_count; ++i) { ++ struct ubi_device_info *ubi_device = &ubi_array[i]; ++ struct volume_info *vol; ++ vol = ubi_device->volumes; ++ while (vol) { ++ int res = ubi_rmvol(libubi, ++ ubi_device->device_file_name, ++ vol->info.vol_id); ++ if (res) ++ return res; ++ vol = vol->next; ++ } ++ } ++ return 0; ++} ++ ++int main(int argc,char *argv[]) ++{ ++ int i; ++ long long r, repeat = 1; ++ int initial_seed = 1, args_ok = 1; ++ ++ printf("UBI Integrity Test\n"); ++ ++ /* Get arguments */ ++ ubi_module_load_string = 0; ++ for (i = 1; i < argc; ++i) { ++ if (strncmp(argv[i], "-h", 2) == 0) ++ args_ok = 0; ++ else if (strncmp(argv[i], "--help", 6) == 0) ++ args_ok = 0; ++ else if (strncmp(argv[i], "-n", 2) == 0) { ++ if (get_short_arg(&i, "-n", &repeat, argc, argv)) ++ args_ok = 0; ++ } else if (strncmp(argv[i], "--repeat", 8) == 0) { ++ if (get_long_arg(&i, "--repeat", &repeat, argc, argv)) ++ args_ok = 0; ++ } else if (strncmp(argv[i], "-m", 2) == 0) { ++ if (get_short_arg(&i,"-m", &max_ebs_per_vol, argc, argv)) ++ args_ok = 0; ++ } else if (strncmp(argv[i], "--maxebs", 8) == 0) { ++ if (get_long_arg(&i, "--maxebs", &max_ebs_per_vol, argc, argv)) ++ args_ok = 0; ++ } else if (!ubi_module_load_string) ++ ubi_module_load_string = argv[i]; ++ else ++ args_ok = 0; ++ } ++ if (!args_ok || !ubi_module_load_string) { ++ fprintf(stderr, "Usage is: ubi_integ [] \n"); ++ fprintf(stderr, " Options: \n"); ++ fprintf(stderr, " -h, --help Help\n"); ++ fprintf(stderr, " -n arg, --repeat=arg Repeat test arg times\n"); ++ fprintf(stderr, " -m arg, --maxebs=arg Max no. of erase blocks\n"); ++ return 1; ++ } ++ ++ initial_seed = getpid(); ++ printf("Initial seed = %u\n", (unsigned) initial_seed); ++ next_seed = initial_seed; ++ srand(initial_seed); ++ load_ubi(); ++ ++ libubi = libubi_open(); ++ if (!libubi) ++ error_exit("Failed to open libubi"); ++ ++ get_ubi_devices_info(); ++ ++ r = 0; ++ while (repeat == 0 || r++ < repeat) { ++ printf("Cycle %lld\n", r); ++ do_some_operations(); ++ ++ /* Close all volumes */ ++ while (open_volumes) ++ close_volume(open_volumes->vol_fd); ++ ++ check_ubi(); ++ ++ libubi_close(libubi); ++ ++ reload_ubi(); ++ ++ libubi = libubi_open(); ++ if (!libubi) ++ error_exit("Failed to open libubi"); ++ ++ check_ubi(); ++ } ++ ++ if (remove_all_volumes()) ++ error_exit("Failed to remove all volumes"); ++ ++ libubi_close(libubi); ++ ++ printf("UBI Integrity Test completed ok\n"); ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/io_basic.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/io_basic.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/io_basic.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/io_basic.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,179 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem B. Bityutskiy ++ * ++ * Test basic UBI volume I/O capabilities. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "libubi.h" ++#define TESTNAME "io_basic" ++#include "common.h" ++ ++static libubi_t libubi; ++static struct ubi_dev_info dev_info; ++const char *node; ++ ++/** ++ * test_basic - check basic volume read and update capabilities. ++ * ++ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) ++ * ++ * Thus function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int test_basic(int type) ++{ ++ struct ubi_mkvol_request req; ++ const char *name = TESTNAME ":test_basic()"; ++ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; ++ ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ req.alignment = 1; ++ req.bytes = dev_info.avail_bytes; ++ req.vol_type = type; ++ req.name = name; ++ ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ return -1; ++ } ++ ++ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); ++ ++ /* Make sure newly created volume contains only 0xFF bytes */ ++ if (check_vol_patt(vol_node, 0xFF)) ++ goto remove; ++ ++ /* Write 0xA5 bytes to the volume */ ++ if (update_vol_patt(vol_node, dev_info.avail_bytes, 0xA5)) ++ goto remove; ++ if (check_vol_patt(vol_node, 0xA5)) ++ goto remove; ++ ++ if (ubi_rmvol(libubi, node, req.vol_id)) { ++ failed("ubi_rmvol"); ++ return -1; ++ } ++ ++ return 0; ++ ++remove: ++ ubi_rmvol(libubi, node, req.vol_id); ++ return -1; ++} ++ ++/** ++ * test_aligned - test volume alignment feature. ++ * ++ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) ++ * ++ * Thus function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int test_aligned(int type) ++{ ++ int i, ebsz; ++ struct ubi_mkvol_request req; ++ const char *name = TESTNAME ":test_aligned()"; ++ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; ++ int alignments[] = ALIGNMENTS(dev_info.leb_size); ++ ++ req.vol_type = type; ++ req.name = name; ++ ++ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ ++ req.alignment = alignments[i]; ++ req.alignment -= req.alignment % dev_info.min_io_size; ++ if (req.alignment == 0) ++ req.alignment = dev_info.min_io_size; ++ ++ ebsz = dev_info.leb_size - dev_info.leb_size % req.alignment; ++ req.bytes = MIN_AVAIL_EBS * ebsz; ++ ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ return -1; ++ } ++ ++ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); ++ ++ /* Make sure newly created volume contains only 0xFF bytes */ ++ if (check_vol_patt(vol_node, 0xFF)) ++ goto remove; ++ ++ /* Write 0xA5 bytes to the volume */ ++ if (update_vol_patt(vol_node, req.bytes, 0xA5)) ++ goto remove; ++ if (check_vol_patt(vol_node, 0xA5)) ++ goto remove; ++ ++ if (ubi_rmvol(libubi, node, req.vol_id)) { ++ failed("ubi_rmvol"); ++ return -1; ++ } ++ } ++ ++ return 0; ++ ++remove: ++ ubi_rmvol(libubi, node, req.vol_id); ++ return -1; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ if (initial_check(argc, argv)) ++ return 1; ++ ++ node = argv[1]; ++ ++ libubi = libubi_open(); ++ if (libubi == NULL) { ++ failed("libubi_open"); ++ return 1; ++ } ++ ++ if (ubi_get_dev_info(libubi, node, &dev_info)) { ++ failed("ubi_get_dev_info"); ++ goto close; ++ } ++ ++ if (test_basic(UBI_DYNAMIC_VOLUME)) ++ goto close; ++ if (test_basic(UBI_STATIC_VOLUME)) ++ goto close; ++ if (test_aligned(UBI_DYNAMIC_VOLUME)) ++ goto close; ++ if (test_aligned(UBI_STATIC_VOLUME)) ++ goto close; ++ ++ libubi_close(libubi); ++ return 0; ++ ++close: ++ libubi_close(libubi); ++ return 1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/io_paral.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/io_paral.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/io_paral.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/io_paral.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,249 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem B. Bityutskiy ++ * ++ * This test does a lot of I/O to volumes in parallel. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "libubi.h" ++#define TESTNAME "io_paral" ++#include "common.h" ++ ++#define THREADS_NUM 3 ++#define ITERATIONS 10 ++ ++static libubi_t libubi; ++static struct ubi_dev_info dev_info; ++const char *node; ++static int iterations = ITERATIONS; ++int total_bytes; ++ ++static long long memory_limit(void) ++{ ++ long long result = 0; ++ FILE *f; ++ ++ f = fopen("/proc/meminfo", "r"); ++ if (!f) ++ return 0; ++ fscanf(f, "%*s %lld", &result); ++ fclose(f); ++ return result * 1024 / 4; ++} ++ ++/** ++ * the_thread - the testing thread. ++ * ++ * @ptr thread number ++ */ ++static void * the_thread(void *ptr) ++{ ++ int fd, iter = iterations, vol_id = (int)ptr; ++ unsigned char *wbuf, *rbuf; ++ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; ++ ++ wbuf = malloc(total_bytes); ++ rbuf = malloc(total_bytes); ++ if (!wbuf || !rbuf) { ++ failed("malloc"); ++ goto free; ++ } ++ ++ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, vol_id); ++ ++ while (iter--) { ++ int i, ret, written = 0, rd = 0; ++ int bytes = (random() % (total_bytes - 1)) + 1; ++ ++ fd = open(vol_node, O_RDWR); ++ if (fd == -1) { ++ failed("open"); ++ err_msg("cannot open \"%s\"\n", node); ++ goto free; ++ } ++ ++ for (i = 0; i < bytes; i++) ++ wbuf[i] = random() % 255; ++ memset(rbuf, '\0', bytes); ++ ++ do { ++ ret = ubi_update_start(libubi, fd, bytes); ++ if (ret && errno != EBUSY) { ++ failed("ubi_update_start"); ++ err_msg("vol_id %d", vol_id); ++ goto close; ++ } ++ } while (ret); ++ ++ while (written < bytes) { ++ int to_write = random() % (bytes - written); ++ ++ if (to_write == 0) ++ to_write = 1; ++ ++ ret = write(fd, wbuf, to_write); ++ if (ret != to_write) { ++ failed("write"); ++ err_msg("failed to write %d bytes at offset %d " ++ "of volume %d", to_write, written, ++ vol_id); ++ err_msg("update: %d bytes", bytes); ++ goto close; ++ } ++ ++ written += to_write; ++ } ++ ++ close(fd); ++ ++ fd = open(vol_node, O_RDONLY); ++ if (fd == -1) { ++ failed("open"); ++ err_msg("cannot open \"%s\"\n", node); ++ goto free; ++ } ++ ++ /* read data back and check */ ++ while (rd < bytes) { ++ int to_read = random() % (bytes - rd); ++ ++ if (to_read == 0) ++ to_read = 1; ++ ++ ret = read(fd, rbuf, to_read); ++ if (ret != to_read) { ++ failed("read"); ++ err_msg("failed to read %d bytes at offset %d " ++ "of volume %d", to_read, rd, vol_id); ++ goto close; ++ } ++ ++ rd += to_read; ++ } ++ ++ close(fd); ++ ++ } ++ ++ free(wbuf); ++ free(rbuf); ++ return NULL; ++ ++close: ++ close(fd); ++free: ++ free(wbuf); ++ free(rbuf); ++ return NULL; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ int i, ret; ++ pthread_t threads[THREADS_NUM]; ++ struct ubi_mkvol_request req; ++ long long mem_limit; ++ ++ if (initial_check(argc, argv)) ++ return 1; ++ ++ node = argv[1]; ++ ++ libubi = libubi_open(); ++ if (libubi == NULL) { ++ failed("libubi_open"); ++ return 1; ++ } ++ ++ if (ubi_get_dev_info(libubi, node, &dev_info)) { ++ failed("ubi_get_dev_info"); ++ goto close; ++ } ++ ++ req.alignment = 1; ++ mem_limit = memory_limit(); ++ if (mem_limit && mem_limit < dev_info.avail_bytes) ++ total_bytes = req.bytes = ++ (mem_limit / dev_info.leb_size / THREADS_NUM) ++ * dev_info.leb_size; ++ else ++ total_bytes = req.bytes = ++ ((dev_info.avail_lebs - 3) / THREADS_NUM) ++ * dev_info.leb_size; ++ for (i = 0; i < THREADS_NUM; i++) { ++ char name[100]; ++ ++ req.vol_id = i; ++ sprintf(name, TESTNAME":%d", i); ++ req.name = name; ++ req.vol_type = (i & 1) ? UBI_STATIC_VOLUME : UBI_DYNAMIC_VOLUME; ++ ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ goto remove; ++ } ++ } ++ ++ /* Create one volume with static data to make WL work more */ ++ req.vol_id = THREADS_NUM; ++ req.name = TESTNAME ":static"; ++ req.vol_type = UBI_DYNAMIC_VOLUME; ++ req.bytes = 3*dev_info.leb_size; ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ goto remove; ++ } ++ ++ for (i = 0; i < THREADS_NUM; i++) { ++ ret = pthread_create(&threads[i], NULL, &the_thread, (void*)i); ++ if (ret) { ++ failed("pthread_create"); ++ goto remove; ++ } ++ } ++ ++ for (i = 0; i < THREADS_NUM; i++) ++ pthread_join(threads[i], NULL); ++ ++ for (i = 0; i <= THREADS_NUM; i++) { ++ if (ubi_rmvol(libubi, node, i)) { ++ failed("ubi_rmvol"); ++ goto remove; ++ } ++ } ++ ++ libubi_close(libubi); ++ return 0; ++ ++remove: ++ for (i = 0; i <= THREADS_NUM; i++) ++ ubi_rmvol(libubi, node, i); ++ ++close: ++ libubi_close(libubi); ++ return 1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/io_read.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/io_read.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/io_read.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/io_read.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,388 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem B. Bityutskiy ++ * ++ * Test UBI volume read. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "libubi.h" ++#define TESTNAME "io_basic" ++#include "common.h" ++ ++static libubi_t libubi; ++static struct ubi_dev_info dev_info; ++const char *node; ++static int fd; ++ ++/* Data lengthes to test, @io - minimal I/O unit size, @s - eraseblock size */ ++#define LENGTHES(io, s) \ ++ {1, (io), (io)+1, 2*(io), 3*(io)-1, 3*(io), \ ++ PAGE_SIZE-1, PAGE_SIZE-(io), 2*PAGE_SIZE, 2*PAGE_SIZE-(io), \ ++ (s)/2-1, (s)/2, (s)/2+1, (s)-1, (s), (s)+1, 2*(s)-(io), 2*(s), \ ++ 2*(s)+(io), 3*(s), 3*(s)+(io)}; ++ ++/* ++ * Offsets to test, @io - minimal I/O unit size, @s - eraseblock size, @sz - ++ * volume size. ++ */ ++#define OFFSETS(io, s, sz) \ ++ {0, (io)-1, (io), (io)+1, 2*(io)-1, 2*(io), 3*(io)-1, 3*(io), \ ++ PAGE_SIZE-1, PAGE_SIZE-(io), 2*PAGE_SIZE, 2*PAGE_SIZE-(io), \ ++ (s)/2-1, (s)/2, (s)/2+1, (s)-1, (s), (s)+1, 2*(s)-(io), 2*(s), \ ++ 2*(s)+(io), 3*(s), (sz)-(s)-1, (sz)-(io)-1, (sz)-PAGE_SIZE-1}; ++ ++/** ++ * test_static - test static volume-specific features. ++ * ++ * Thus function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int test_static(void) ++{ ++ struct ubi_mkvol_request req; ++ const char *name = TESTNAME ":io_basic()"; ++ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; ++ struct ubi_vol_info vol_info; ++ int fd, ret; ++ char buf[20]; ++ ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ req.alignment = 1; ++ req.bytes = dev_info.avail_bytes; ++ req.vol_type = UBI_STATIC_VOLUME; ++ req.name = name; ++ ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ return -1; ++ } ++ ++ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); ++ ++ fd = open(vol_node, O_RDWR); ++ if (fd == -1) { ++ failed("open"); ++ err_msg("cannot open \"%s\"\n", node); ++ goto remove; ++ } ++ ++ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { ++ failed("ubi_get_vol_info"); ++ goto close; ++ } ++ ++ /* Make sure new static volume contains no data */ ++ if (vol_info.data_bytes != 0) { ++ err_msg("data_bytes = %lld, not zero", vol_info.data_bytes); ++ goto close; ++ } ++ ++ /* Ensure read returns EOF */ ++ ret = read(fd, buf, 1); ++ if (ret < 0) { ++ failed("read"); ++ goto close; ++ } ++ if (ret != 0) { ++ err_msg("read data from free static volume"); ++ goto close; ++ } ++ ++ if (ubi_update_start(libubi, fd, 10)) { ++ failed("ubi_update_start"); ++ goto close; ++ } ++ ++ ret = write(fd, buf, 10); ++ if (ret < 0) { ++ failed("write"); ++ goto close; ++ } ++ if (ret != 10) { ++ err_msg("written %d bytes", ret); ++ goto close; ++ } ++ ++ if (lseek(fd, 0, SEEK_SET) != 0) { ++ failed("seek"); ++ goto close; ++ } ++ ret = read(fd, buf, 20); ++ if (ret < 0) { ++ failed("read"); ++ goto close; ++ } ++ if (ret != 10) { ++ err_msg("read %d bytes", ret); ++ goto close; ++ } ++ ++ close(fd); ++ if (ubi_rmvol(libubi, node, req.vol_id)) { ++ failed("ubi_rmvol"); ++ return -1; ++ } ++ ++ return 0; ++ ++close: ++ close(fd); ++remove: ++ ubi_rmvol(libubi, node, req.vol_id); ++ return -1; ++} ++ ++/* ++ * A helper function for test_read2(). ++ */ ++static int test_read3(const struct ubi_vol_info *vol_info, int len, off_t off) ++{ ++ int i, len1; ++ unsigned char ck_buf[len], buf[len]; ++ off_t new_off; ++ ++ if (off + len > vol_info->data_bytes) ++ len1 = vol_info->data_bytes - off; ++ else ++ len1 = len; ++ ++ if (lseek(fd, off, SEEK_SET) != off) { ++ failed("seek"); ++ err_msg("len = %d", len); ++ return -1; ++ } ++ if (read(fd, buf, len) != len1) { ++ failed("read"); ++ err_msg("len = %d", len); ++ return -1; ++ } ++ ++ new_off = lseek(fd, 0, SEEK_CUR); ++ if (new_off != off + len1) { ++ if (new_off == -1) ++ failed("lseek"); ++ else ++ err_msg("read %d bytes from %lld, but resulting " ++ "offset is %lld", len1, (long long) off, (long long) new_off); ++ return -1; ++ } ++ ++ for (i = 0; i < len1; i++) ++ ck_buf[i] = (unsigned char)(off + i); ++ ++ if (memcmp(buf, ck_buf, len1)) { ++ err_msg("incorrect data read from offset %lld", ++ (long long)off); ++ err_msg("len = %d", len); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* ++ * A helper function for test_read1(). ++ */ ++static int test_read2(const struct ubi_vol_info *vol_info, int len) ++{ ++ int i; ++ off_t offsets[] = OFFSETS(dev_info.min_io_size, vol_info->leb_size, ++ vol_info->data_bytes); ++ ++ for (i = 0; i < sizeof(offsets)/sizeof(off_t); i++) { ++ if (test_read3(vol_info, len, offsets[i])) { ++ err_msg("offset = %d", offsets[i]); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * A helper function for test_read(). ++ */ ++static int test_read1(struct ubi_vol_info *vol_info) ++{ ++ int i, written = 0; ++ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; ++ int lengthes[] = LENGTHES(dev_info.min_io_size, vol_info->leb_size); ++ ++ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, ++ vol_info->vol_id); ++ ++ fd = open(vol_node, O_RDWR); ++ if (fd == -1) { ++ failed("open"); ++ err_msg("cannot open \"%s\"\n", node); ++ return -1; ++ } ++ ++ /* Write some pattern to the volume */ ++ if (ubi_update_start(libubi, fd, vol_info->rsvd_bytes)) { ++ failed("ubi_update_start"); ++ err_msg("bytes = %lld", vol_info->rsvd_bytes); ++ goto close; ++ } ++ ++ while (written < vol_info->rsvd_bytes) { ++ int i, ret; ++ unsigned char buf[512]; ++ ++ for (i = 0; i < 512; i++) ++ buf[i] = (unsigned char)(written + i); ++ ++ ret = write(fd, buf, 512); ++ if (ret == -1) { ++ failed("write"); ++ err_msg("written = %d, ret = %d", written, ret); ++ goto close; ++ } ++ written += ret; ++ } ++ ++ close(fd); ++ ++ if (ubi_get_vol_info(libubi, vol_node, vol_info)) { ++ failed("ubi_get_vol_info"); ++ return -1; ++ } ++ ++ fd = open(vol_node, O_RDONLY); ++ if (fd == -1) { ++ failed("open"); ++ err_msg("cannot open \"%s\"\n", node); ++ return -1; ++ } ++ ++ for (i = 0; i < sizeof(lengthes)/sizeof(int); i++) { ++ if (test_read2(vol_info, lengthes[i])) { ++ err_msg("length = %d", lengthes[i]); ++ goto close; ++ } ++ } ++ ++ close(fd); ++ return 0; ++ ++close: ++ close(fd); ++ return -1; ++} ++ ++/** ++ * test_read - test UBI volume reading from different offsets. ++ * ++ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) ++ * ++ * Thus function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int test_read(int type) ++{ ++ const char *name = TESTNAME ":test_read()"; ++ int alignments[] = ALIGNMENTS(dev_info.leb_size); ++ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; ++ struct ubi_mkvol_request req; ++ int i; ++ ++ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { ++ int leb_size; ++ struct ubi_vol_info vol_info; ++ ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ req.vol_type = type; ++ req.name = name; ++ ++ req.alignment = alignments[i]; ++ req.alignment -= req.alignment % dev_info.min_io_size; ++ if (req.alignment == 0) ++ req.alignment = dev_info.min_io_size; ++ ++ leb_size = dev_info.leb_size - dev_info.leb_size % req.alignment; ++ req.bytes = MIN_AVAIL_EBS * leb_size; ++ ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ return -1; ++ } ++ ++ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, ++ req.vol_id); ++ ++ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { ++ failed("ubi_get_vol_info"); ++ goto remove; ++ } ++ ++ if (test_read1(&vol_info)) { ++ err_msg("alignment = %d", req.alignment); ++ goto remove; ++ } ++ ++ if (ubi_rmvol(libubi, node, req.vol_id)) { ++ failed("ubi_rmvol"); ++ return -1; ++ } ++ } ++ ++ return 0; ++ ++remove: ++ ubi_rmvol(libubi, node, req.vol_id); ++ return -1; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ if (initial_check(argc, argv)) ++ return 1; ++ ++ node = argv[1]; ++ ++ libubi = libubi_open(); ++ if (libubi == NULL) { ++ failed("libubi_open"); ++ return 1; ++ } ++ ++ if (ubi_get_dev_info(libubi, node, &dev_info)) { ++ failed("ubi_get_dev_info"); ++ goto close; ++ } ++ ++ if (test_static()) ++ goto close; ++ if (test_read(UBI_DYNAMIC_VOLUME)) ++ goto close; ++ if (test_read(UBI_STATIC_VOLUME)) ++ goto close; ++ ++ libubi_close(libubi); ++ return 0; ++ ++close: ++ libubi_close(libubi); ++ return 1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/io_update.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/io_update.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/io_update.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/io_update.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,298 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem B. Bityutskiy ++ * ++ * Test UBI volume update and atomic LEB change ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#define TESTNAME "io_update" ++#include "common.h" ++ ++static libubi_t libubi; ++static struct ubi_dev_info dev_info; ++const char *node; ++ ++#define SEQUENCES(io, s) { \ ++ {3*(s)-(io)-1, 1}, \ ++ {512}, \ ++ {666}, \ ++ {2048}, \ ++ {(io), (io), PAGE_SIZE}, \ ++ {(io)+1, (io)+1, PAGE_SIZE}, \ ++ {PAGE_SIZE}, \ ++ {PAGE_SIZE-1}, \ ++ {PAGE_SIZE+(io)}, \ ++ {(s)}, \ ++ {(s)-1}, \ ++ {(s)+1}, \ ++ {(io), (s)+1}, \ ++ {(s)+(io), PAGE_SIZE}, \ ++ {2*(s), PAGE_SIZE}, \ ++ {PAGE_SIZE, 2*(s), 1}, \ ++ {PAGE_SIZE, 2*(s)}, \ ++ {2*(s)-1, 2*(s)-1}, \ ++ {3*(s), PAGE_SIZE + 1}, \ ++ {1, PAGE_SIZE}, \ ++ {(io), (s)} \ ++} ++ ++#define SEQ_SZ 21 ++ ++/* ++ * test_update1 - helper function for test_update(). ++ */ ++static int test_update1(struct ubi_vol_info *vol_info, int leb_change) ++{ ++ long long total_len = leb_change ? vol_info->leb_size ++ : vol_info->rsvd_bytes; ++ int sequences[SEQ_SZ][3] = SEQUENCES(dev_info.min_io_size, ++ leb_change ? dev_info.min_io_size * 2 ++ : vol_info->leb_size); ++ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; ++ unsigned char buf[total_len]; ++ int fd, i, j; ++ ++ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, ++ vol_info->vol_id); ++ ++ fd = open(vol_node, O_RDWR); ++ if (fd == -1) { ++ failed("open"); ++ err_msg("cannot open \"%s\"\n", node); ++ return -1; ++ } ++ ++ for (i = 0; i < SEQ_SZ; i++) { ++ int ret, stop = 0, len = 0; ++ off_t off = 0; ++ long long test_len; ++ unsigned char buf1[total_len]; ++ ++ /* ++ * test_len is LEB size (if we test atomic LEB change) or ++ * volume size (if we test update). For better test coverage, ++ * use a little smaller LEB change/update length. ++ */ ++ test_len = total_len - (rand() % (total_len / 10)); ++ ++ if (leb_change) { ++ if (ubi_leb_change_start(libubi, fd, 0, test_len, ++ UBI_SHORTTERM)) { ++ failed("ubi_update_start"); ++ goto close; ++ } ++ } else { ++ if (ubi_update_start(libubi, fd, test_len)) { ++ failed("ubi_update_start"); ++ goto close; ++ } ++ } ++ ++ for (j = 0; off < test_len; j++) { ++ int n, rnd_len, l; ++ ++ if (!stop) { ++ if (sequences[i][j] != 0) ++ l = len = sequences[i][j]; ++ else ++ stop = 1; ++ } ++ ++ /* ++ * Fill some part of the write buffer with random data, ++ * and the other part with 0xFFs to test how UBI ++ * stripes 0xFFs multiple of I/O unit size. ++ */ ++ if (off + l > test_len) ++ l = test_len - off; ++ rnd_len = rand() % (l + 1); ++ for (n = 0; n < rnd_len; n++) ++ buf[off + n] = (unsigned char)rand(); ++ memset(buf + off + rnd_len, 0xFF, l - rnd_len); ++ ++ /* ++ * Deliberately pass len instead of l (len may be ++ * greater then l if this is the last chunk) because ++ * UBI have to read only l bytes anyway. ++ */ ++ ret = write(fd, buf + off, len); ++ if (ret < 0) { ++ failed("write"); ++ err_msg("failed to write %d bytes at offset " ++ "%lld", len, (long long)off); ++ goto close; ++ } ++ len = l; ++ if (ret != len) { ++ err_msg("failed to write %d bytes at offset " ++ "%lld, wrote %d", len, (long long)off, ret); ++ goto close; ++ } ++ off += len; ++ } ++ ++ /* Check data */ ++ if ((ret = lseek(fd, SEEK_SET, 0)) != 0) { ++ failed("lseek"); ++ err_msg("cannot seek to 0"); ++ goto close; ++ } ++ ++ memset(buf1, 0x01, test_len); ++ ++ if (vol_info->type == UBI_STATIC_VOLUME) ++ /* ++ * Static volume must not let use read more then it ++ * contains. ++ */ ++ ret = read(fd, buf1, test_len + 100); ++ else ++ ret = read(fd, buf1, test_len); ++ if (ret < 0) { ++ failed("read"); ++ err_msg("failed to read %d bytes", test_len); ++ goto close; ++ } ++ if (ret != test_len) { ++ err_msg("failed to read %d bytes, read %d", test_len, ret); ++ goto close; ++ } ++ if (memcmp(buf, buf1, test_len)) { ++ err_msg("data corruption"); ++ goto close; ++ } ++ } ++ ++ close(fd); ++ return 0; ++ ++close: ++ close(fd); ++ return -1; ++} ++ ++/** ++ * test_update - check volume update and atomic LEB change capabilities. ++ * ++ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) ++ * ++ * This function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int test_update(int type) ++{ ++ struct ubi_mkvol_request req; ++ const char *name = TESTNAME ":io_update()"; ++ int alignments[] = ALIGNMENTS(dev_info.leb_size); ++ struct ubi_vol_info vol_info; ++ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; ++ int i; ++ ++ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { ++ int leb_size; ++ ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ req.vol_type = type; ++ req.name = name; ++ ++ req.alignment = alignments[i]; ++ req.alignment -= req.alignment % dev_info.min_io_size; ++ if (req.alignment == 0) ++ req.alignment = dev_info.min_io_size; ++ ++ leb_size = dev_info.leb_size - dev_info.leb_size % req.alignment; ++ req.bytes = MIN_AVAIL_EBS * leb_size; ++ ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ return -1; ++ } ++ ++ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, ++ req.vol_id); ++ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { ++ failed("ubi_get_vol_info"); ++ goto remove; ++ } ++ ++ if (test_update1(&vol_info, 0)) { ++ err_msg("alignment = %d", req.alignment); ++ goto remove; ++ } ++ ++ if (vol_info.type != UBI_STATIC_VOLUME) { ++ if (test_update1(&vol_info, 1)) { ++ err_msg("alignment = %d", req.alignment); ++ goto remove; ++ } ++ } ++ ++ if (ubi_rmvol(libubi, node, req.vol_id)) { ++ failed("ubi_rmvol"); ++ return -1; ++ } ++ } ++ ++ return 0; ++ ++remove: ++ ubi_rmvol(libubi, node, req.vol_id); ++ return -1; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ if (initial_check(argc, argv)) ++ return 1; ++ ++ node = argv[1]; ++ ++ libubi = libubi_open(); ++ if (libubi == NULL) { ++ failed("libubi_open"); ++ return 1; ++ } ++ ++ if (ubi_get_dev_info(libubi, node, &dev_info)) { ++ failed("ubi_get_dev_info"); ++ goto close; ++ } ++ ++ if (test_update(UBI_DYNAMIC_VOLUME)) ++ goto close; ++ if (test_update(UBI_STATIC_VOLUME)) ++ goto close; ++ ++ libubi_close(libubi); ++ return 0; ++ ++close: ++ libubi_close(libubi); ++ return 1; ++} ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_bad.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_bad.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_bad.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_bad.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,301 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem B. Bityutskiy ++ * ++ * Test UBI volume creation and deletion ioctl()s with bad input and in case of ++ * incorrect usage. ++ */ ++ ++#include ++#include ++#include ++#include "libubi.h" ++#define TESTNAME "mkvol_bad" ++#include "common.h" ++ ++static libubi_t libubi; ++static struct ubi_dev_info dev_info; ++const char *node; ++ ++/** ++ * test_mkvol - test that UBI mkvol ioctl rejects bad input parameters. ++ * ++ * This function returns %0 if the test passed and %-1 if not. ++ */ ++static int test_mkvol(void) ++{ ++ int ret, i; ++ struct ubi_mkvol_request req; ++ const char *name = TESTNAME ":test_mkvol()"; ++ ++ req.alignment = 1; ++ req.bytes = dev_info.avail_bytes; ++ req.vol_type = UBI_DYNAMIC_VOLUME; ++ req.name = name; ++ ++ /* Bad volume ID */ ++ req.vol_id = -2; ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_id = %d", req.vol_id)) ++ return -1; ++ ++ req.vol_id = dev_info.max_vol_count; ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_id = %d", req.vol_id)) ++ return -1; ++ ++ /* Bad alignment */ ++ req.vol_id = 0; ++ req.alignment = 0; ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", ++ req.alignment)) ++ return -1; ++ ++ req.alignment = -1; ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", ++ req.alignment)) ++ return -1; ++ ++ req.alignment = dev_info.leb_size + 1; ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", ++ req.alignment)) ++ return -1; ++ ++ if (dev_info.min_io_size > 1) { ++ req.alignment = dev_info.min_io_size + 1; ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", ++ req.alignment)) ++ return -1; ++ } ++ ++ /* Bad bytes */ ++ req.alignment = 1; ++ req.bytes = -1; ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, EINVAL, "ubi_mkvol", "bytes = %lld", req.bytes)) ++ return -1; ++ ++ req.bytes = 0; ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, EINVAL, "ubi_mkvol", "bytes = %lld", req.bytes)) ++ return -1; ++ ++ req.bytes = dev_info.avail_bytes + 1; ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, ENOSPC, "ubi_mkvol", "bytes = %lld", req.bytes)) ++ return -1; ++ ++ req.alignment = dev_info.leb_size - dev_info.min_io_size; ++ req.bytes = (dev_info.leb_size - dev_info.leb_size % req.alignment) * ++ dev_info.avail_lebs + 1; ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, ENOSPC, "ubi_mkvol", "bytes = %lld", req.bytes)) ++ return -1; ++ ++ /* Bad vol_type */ ++ req.alignment = 1; ++ req.bytes = dev_info.leb_size; ++ req.vol_type = UBI_DYNAMIC_VOLUME + UBI_STATIC_VOLUME; ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_type = %d", ++ req.vol_type)) ++ return -1; ++ ++ req.vol_type = UBI_DYNAMIC_VOLUME; ++ ++ /* Too long name */ ++ { ++ char name[UBI_VOL_NAME_MAX + 5]; ++ ++ memset(name, 'x', UBI_VOL_NAME_MAX + 1); ++ name[UBI_VOL_NAME_MAX + 1] = '\0'; ++ ++ req.name = name; ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, EINVAL, "ubi_mkvol", "name_len = %d", ++ UBI_VOL_NAME_MAX + 1)) ++ return -1; ++ } ++ ++ /* Try to create 2 volumes with the same ID and name */ ++ req.name = name; ++ req.vol_id = 0; ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ return -1; ++ } ++ ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, EEXIST, "ubi_mkvol", ++ "volume with ID 0 created twice")) ++ return -1; ++ ++ req.vol_id = 1; ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, EEXIST, "ubi_mkvol", ++ "volume with name \"%s\" created twice", name)) ++ return -1; ++ ++ if (ubi_rmvol(libubi, node, 0)) { ++ failed("ubi_rmvol"); ++ return -1; ++ } ++ ++ /* Try to use too much space */ ++ req.vol_id = 0; ++ req.bytes = dev_info.avail_bytes; ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ return -1; ++ } ++ ++ req.bytes = 1; ++ req.vol_id = 1; ++ ret = ubi_mkvol(libubi, node, &req); ++ if (check_failed(ret, EEXIST, "ubi_mkvol", ++ "created volume of maximum size %lld, but still " ++ "can create more volumes", dev_info.avail_bytes)) ++ return -1; ++ ++ if (ubi_rmvol(libubi, node, 0)) { ++ failed("ubi_rmvol"); ++ return -1; ++ } ++ ++ /* Try to create too many volumes */ ++ for (i = 0; i < dev_info.max_vol_count; i++) { ++ char nm[strlen(name) + 50]; ++ ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ req.alignment = 1; ++ req.bytes = 1; ++ req.vol_type = UBI_STATIC_VOLUME; ++ ++ sprintf(nm, "%s:%d", name, i); ++ req.name = nm; ++ ++ if (ubi_mkvol(libubi, node, &req)) { ++ /* ++ * Note, because of gluebi we may be unable to create ++ * dev_info.max_vol_count devices (MTD restrictions). ++ */ ++ if (errno == ENFILE) ++ break; ++ failed("ubi_mkvol"); ++ err_msg("vol_id %d", i); ++ goto remove; ++ } ++ } ++ ++ for (i = 0; i < dev_info.max_vol_count + 1; i++) ++ ubi_rmvol(libubi, node, i); ++ ++ return 0; ++ ++remove: ++ for (i = 0; i < dev_info.max_vol_count + 1; i++) ++ ubi_rmvol(libubi, node, i); ++ return -1; ++} ++ ++/** ++ * test_rmvol - test that UBI rmvol ioctl rejects bad input parameters. ++ * ++ * This function returns %0 if the test passed and %-1 if not. ++ */ ++static int test_rmvol(void) ++{ ++ int ret; ++ struct ubi_mkvol_request req; ++ const char *name = TESTNAME ":test_rmvol()"; ++ ++ /* Bad vol_id */ ++ ret = ubi_rmvol(libubi, node, -1); ++ if (check_failed(ret, EINVAL, "ubi_rmvol", "vol_id = -1")) ++ return -1; ++ ++ ret = ubi_rmvol(libubi, node, dev_info.max_vol_count); ++ if (check_failed(ret, EINVAL, "ubi_rmvol", "vol_id = %d", ++ dev_info.max_vol_count)) ++ return -1; ++ ++ /* Try to remove non-existing volume */ ++ ret = ubi_rmvol(libubi, node, 0); ++ if (check_failed(ret, ENODEV, "ubi_rmvol", ++ "removed non-existing volume 0")) ++ return -1; ++ ++ /* Try to remove volume twice */ ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ req.alignment = 1; ++ req.bytes = dev_info.avail_bytes; ++ req.vol_type = UBI_DYNAMIC_VOLUME; ++ req.name = name; ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ return -1; ++ } ++ ++ if (ubi_rmvol(libubi, node, req.vol_id)) { ++ failed("ubi_rmvol"); ++ return -1; ++ } ++ ++ ret = ubi_rmvol(libubi, node, req.vol_id); ++ if (check_failed(ret, ENODEV, "ubi_rmvol", "volume %d removed twice", ++ req.vol_id)) ++ return -1; ++ ++ return 0; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ if (initial_check(argc, argv)) ++ return 1; ++ ++ node = argv[1]; ++ ++ libubi = libubi_open(); ++ if (libubi == NULL) { ++ failed("libubi_open"); ++ return 1; ++ } ++ ++ if (ubi_get_dev_info(libubi, node, &dev_info)) { ++ failed("ubi_get_dev_info"); ++ goto close; ++ } ++ ++ if (test_mkvol()) ++ goto close; ++ ++ if (test_rmvol()) ++ goto close; ++ ++ libubi_close(libubi); ++ return 0; ++ ++close: ++ libubi_close(libubi); ++ return 1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_basic.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_basic.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_basic.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_basic.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,250 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem B. Bityutskiy ++ * ++ * Test test checks basic volume creation and deletion capabilities. ++ */ ++ ++#include ++#include ++#include ++#include "libubi.h" ++#define TESTNAME "mkvol_basic" ++#include "common.h" ++ ++static libubi_t libubi; ++static struct ubi_dev_info dev_info; ++const char *node; ++ ++/** ++ * mkvol_alignment - create volumes with different alignments. ++ * ++ * Thus function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int mkvol_alignment(void) ++{ ++ struct ubi_mkvol_request req; ++ int i, vol_id, ebsz; ++ const char *name = TESTNAME ":mkvol_alignment()"; ++ int alignments[] = ALIGNMENTS(dev_info.leb_size); ++ ++ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ ++ /* Alignment should actually be multiple of min. I/O size */ ++ req.alignment = alignments[i]; ++ req.alignment -= req.alignment % dev_info.min_io_size; ++ if (req.alignment == 0) ++ req.alignment = dev_info.min_io_size; ++ ++ /* Bear in mind alignment reduces EB size */ ++ ebsz = dev_info.leb_size - dev_info.leb_size % req.alignment; ++ req.bytes = dev_info.avail_lebs * ebsz; ++ ++ req.vol_type = UBI_DYNAMIC_VOLUME; ++ req.name = name; ++ ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ err_msg("alignment %d", req.alignment); ++ return -1; ++ } ++ ++ vol_id = req.vol_id; ++ if (check_volume(vol_id, &req)) ++ goto remove; ++ ++ if (ubi_rmvol(libubi, node, vol_id)) { ++ failed("ubi_rmvol"); ++ return -1; ++ } ++ } ++ ++ return 0; ++ ++remove: ++ ubi_rmvol(libubi, node, vol_id); ++ return -1; ++} ++ ++/** ++ * mkvol_basic - simple test that checks basic volume creation capability. ++ * ++ * Thus function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int mkvol_basic(void) ++{ ++ struct ubi_mkvol_request req; ++ struct ubi_vol_info vol_info; ++ int vol_id, ret; ++ const char *name = TESTNAME ":mkvol_basic()"; ++ ++ /* Create dynamic volume of maximum size */ ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ req.alignment = 1; ++ req.bytes = dev_info.avail_bytes; ++ req.vol_type = UBI_DYNAMIC_VOLUME; ++ req.name = name; ++ ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ return -1; ++ } ++ ++ vol_id = req.vol_id; ++ if (check_volume(vol_id, &req)) ++ goto remove; ++ ++ if (ubi_rmvol(libubi, node, vol_id)) { ++ failed("ubi_rmvol"); ++ return -1; ++ } ++ ++ /* Create static volume of maximum size */ ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ req.alignment = 1; ++ req.bytes = dev_info.avail_bytes; ++ req.vol_type = UBI_STATIC_VOLUME; ++ req.name = name; ++ ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ return -1; ++ } ++ ++ vol_id = req.vol_id; ++ if (check_volume(vol_id, &req)) ++ goto remove; ++ ++ if (ubi_rmvol(libubi, node, vol_id)) { ++ failed("ubi_rmvol"); ++ return -1; ++ } ++ ++ /* Make sure volume does not exist */ ++ ret = ubi_get_vol_info1(libubi, dev_info.dev_num, vol_id, &vol_info); ++ if (ret == 0) { ++ err_msg("removed volume %d exists", vol_id); ++ goto remove; ++ } ++ ++ return 0; ++ ++remove: ++ ubi_rmvol(libubi, node, vol_id); ++ return -1; ++} ++ ++/** ++ * mkvol_multiple - test multiple volumes creation ++ * ++ * Thus function returns %0 if the test passed and %-1 if not. ++ */ ++static int mkvol_multiple(void) ++{ ++ struct ubi_mkvol_request req; ++ int i, ret, max = dev_info.max_vol_count; ++ const char *name = TESTNAME ":mkvol_multiple()"; ++ ++ /* Create maximum number of volumes */ ++ for (i = 0; i < max; i++) { ++ char nm[strlen(name) + 50]; ++ ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ req.alignment = 1; ++ req.bytes = 1; ++ req.vol_type = UBI_STATIC_VOLUME; ++ ++ sprintf(nm, "%s:%d", name, i); ++ req.name = nm; ++ ++ if (ubi_mkvol(libubi, node, &req)) { ++ if (errno == ENFILE) { ++ max = i; ++ break; ++ } ++ failed("ubi_mkvol"); ++ err_msg("vol_id %d", i); ++ goto remove; ++ } ++ ++ if (check_volume(req.vol_id, &req)) { ++ err_msg("vol_id %d", i); ++ goto remove; ++ } ++ } ++ ++ for (i = 0; i < max; i++) { ++ struct ubi_vol_info vol_info; ++ ++ if (ubi_rmvol(libubi, node, i)) { ++ failed("ubi_rmvol"); ++ return -1; ++ } ++ ++ /* Make sure volume does not exist */ ++ ret = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); ++ if (ret == 0) { ++ err_msg("removed volume %d exists", i); ++ goto remove; ++ } ++ } ++ ++ return 0; ++ ++remove: ++ for (i = 0; i < dev_info.max_vol_count + 1; i++) ++ ubi_rmvol(libubi, node, i); ++ return -1; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ if (initial_check(argc, argv)) ++ return 1; ++ ++ node = argv[1]; ++ ++ libubi = libubi_open(); ++ if (libubi == NULL) { ++ failed("libubi_open"); ++ return 1; ++ } ++ ++ if (ubi_get_dev_info(libubi, node, &dev_info)) { ++ failed("ubi_get_dev_info"); ++ goto close; ++ } ++ ++ if (mkvol_basic()) ++ goto close; ++ ++ if (mkvol_alignment()) ++ goto close; ++ ++ if (mkvol_multiple()) ++ goto close; ++ ++ libubi_close(libubi); ++ return 0; ++ ++close: ++ libubi_close(libubi); ++ return 1; ++} ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_paral.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_paral.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_paral.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/mkvol_paral.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,110 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem B. Bityutskiy ++ * ++ * This test creates and deletes volumes in parallel. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "libubi.h" ++#define TESTNAME "mkvol_paral" ++#include "common.h" ++ ++#define THREADS_NUM 4 ++#define ITERATIONS 500 ++ ++static libubi_t libubi; ++static struct ubi_dev_info dev_info; ++const char *node; ++static int iterations = ITERATIONS; ++ ++/** ++ * the_thread - the testing thread. ++ * ++ * @ptr thread number ++ */ ++static void * the_thread(void *ptr) ++{ ++ int n = (int)ptr, iter = iterations; ++ struct ubi_mkvol_request req; ++ const char *name = TESTNAME ":the_thread()"; ++ char nm[strlen(name) + 50]; ++ ++ req.alignment = 1; ++ req.bytes = dev_info.avail_bytes/ITERATIONS; ++ req.vol_type = UBI_DYNAMIC_VOLUME; ++ sprintf(nm, "%s:%d", name, n); ++ req.name = nm; ++ ++ while (iter--) { ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ return NULL; ++ } ++ if (ubi_rmvol(libubi, node, req.vol_id)) { ++ failed("ubi_rmvol"); ++ return NULL; ++ } ++ } ++ ++ return NULL; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ int i, ret; ++ pthread_t threads[THREADS_NUM]; ++ ++ if (initial_check(argc, argv)) ++ return 1; ++ ++ node = argv[1]; ++ ++ libubi = libubi_open(); ++ if (libubi == NULL) { ++ failed("libubi_open"); ++ return 1; ++ } ++ ++ if (ubi_get_dev_info(libubi, node, &dev_info)) { ++ failed("ubi_get_dev_info"); ++ goto close; ++ } ++ ++ for (i = 0; i < THREADS_NUM; i++) { ++ ret = pthread_create(&threads[i], NULL, &the_thread, (void*)i); ++ if (ret) { ++ failed("pthread_create"); ++ goto close; ++ } ++ } ++ ++ for (i = 0; i < THREADS_NUM; i++) ++ pthread_join(threads[i], NULL); ++ ++ libubi_close(libubi); ++ return 0; ++ ++close: ++ libubi_close(libubi); ++ return 1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/rsvol.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/rsvol.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/rsvol.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/rsvol.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,305 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem B. Bityutskiy ++ * ++ * Tes UBI volume re-size. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "libubi.h" ++#define TESTNAME "rsvol" ++#include "common.h" ++ ++static libubi_t libubi; ++static struct ubi_dev_info dev_info; ++const char *node; ++ ++/** ++ * test_basic - check volume re-size capability. ++ * ++ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) ++ * ++ * Thus function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int test_basic(int type) ++{ ++ struct ubi_mkvol_request req; ++ const char *name = TESTNAME ":test_basic()"; ++ ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ req.alignment = 1; ++ req.bytes = MIN_AVAIL_EBS * dev_info.leb_size; ++ req.vol_type = type; ++ req.name = name; ++ ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ return -1; ++ } ++ ++ req.bytes = dev_info.leb_size; ++ if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) { ++ failed("ubi_rsvol"); ++ goto remove; ++ } ++ ++ if (check_volume(req.vol_id, &req)) ++ goto remove; ++ ++ req.bytes = (MIN_AVAIL_EBS + 1) * dev_info.leb_size; ++ if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) { ++ failed("ubi_rsvol"); ++ goto remove; ++ } ++ ++ if (check_volume(req.vol_id, &req)) ++ goto remove; ++ ++ req.bytes -= 1; ++ if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) { ++ failed("ubi_rsvol"); ++ goto remove; ++ } ++ ++ if (check_volume(req.vol_id, &req)) ++ goto remove; ++ ++ if (ubi_rmvol(libubi, node, req.vol_id)) { ++ failed("ubi_rmvol"); ++ return -1; ++ } ++ ++ return 0; ++ ++remove: ++ ubi_rmvol(libubi, node, req.vol_id); ++ return -1; ++} ++ ++/* ++ * Helper function for test_rsvol(). ++ */ ++static int test_rsvol1(struct ubi_vol_info *vol_info) ++{ ++ long long bytes; ++ struct ubi_vol_info vol_info1; ++ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; ++ unsigned char buf[vol_info->rsvd_bytes]; ++ int fd, i, ret; ++ ++ /* Make the volume smaller and check basic volume I/O */ ++ bytes = vol_info->rsvd_bytes - vol_info->leb_size; ++ if (ubi_rsvol(libubi, node, vol_info->vol_id, bytes - 1)) { ++ failed("ubi_rsvol"); ++ return -1; ++ } ++ ++ if (ubi_get_vol_info1(libubi, vol_info->dev_num, vol_info->vol_id, ++ &vol_info1)) { ++ failed("ubi_get_vol_info"); ++ return -1; ++ } ++ ++ if (vol_info1.rsvd_bytes != bytes) { ++ err_msg("rsvd_bytes %lld, must be %lld", ++ vol_info1.rsvd_bytes, bytes); ++ return -1; ++ } ++ ++ if (vol_info1.rsvd_lebs != vol_info->rsvd_lebs - 1) { ++ err_msg("rsvd_lebs %d, must be %d", ++ vol_info1.rsvd_lebs, vol_info->rsvd_lebs - 1); ++ return -1; ++ } ++ ++ /* Write data to the volume */ ++ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, ++ vol_info->vol_id); ++ ++ fd = open(vol_node, O_RDWR); ++ if (fd == -1) { ++ failed("open"); ++ err_msg("cannot open \"%s\"\n", vol_node); ++ return -1; ++ } ++ ++ bytes = vol_info->rsvd_bytes - vol_info->leb_size - 1; ++ if (ubi_update_start(libubi, fd, bytes)) { ++ failed("ubi_update_start"); ++ goto close; ++ } ++ ++ for (i = 0; i < bytes; i++) ++ buf[i] = (unsigned char)i; ++ ++ ret = write(fd, buf, bytes); ++ if (ret != bytes) { ++ failed("write"); ++ goto close; ++ } ++ ++ close(fd); ++ ++ if (ubi_rsvol(libubi, node, vol_info->vol_id, bytes)) { ++ failed("ubi_rsvol"); ++ return -1; ++ } ++ ++ if (ubi_rsvol(libubi, node, vol_info->vol_id, ++ vol_info->leb_size * dev_info.avail_lebs)) { ++ failed("ubi_rsvol"); ++ return -1; ++ } ++ ++ fd = open(vol_node, O_RDWR); ++ if (fd == -1) { ++ failed("open"); ++ err_msg("cannot open \"%s\"\n", vol_node); ++ return -1; ++ } ++ ++ /* Read data back */ ++ if (lseek(fd, 0, SEEK_SET) != 0) { ++ failed("seek"); ++ goto close; ++ } ++ memset(buf, 0, bytes); ++ ret = read(fd, buf, bytes); ++ if (ret != bytes) { ++ failed("read"); ++ goto close; ++ } ++ ++ for (i = 0; i < bytes; i++) { ++ if (buf[i] != (unsigned char)i) { ++ err_msg("bad data"); ++ goto close; ++ } ++ } ++ ++ close(fd); ++ return 0; ++ ++close: ++ close(fd); ++ return -1; ++} ++ ++/** ++ * test_rsvol - test UBI volume re-size. ++ * ++ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) ++ * ++ * Thus function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int test_rsvol(int type) ++{ ++ const char *name = TESTNAME "test_rsvol:()"; ++ int alignments[] = ALIGNMENTS(dev_info.leb_size); ++ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; ++ struct ubi_mkvol_request req; ++ int i; ++ ++ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { ++ int leb_size; ++ struct ubi_vol_info vol_info; ++ ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ req.vol_type = type; ++ req.name = name; ++ ++ req.alignment = alignments[i]; ++ req.alignment -= req.alignment % dev_info.min_io_size; ++ if (req.alignment == 0) ++ req.alignment = dev_info.min_io_size; ++ ++ leb_size = dev_info.leb_size - dev_info.leb_size % req.alignment; ++ req.bytes = MIN_AVAIL_EBS * leb_size; ++ ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ return -1; ++ } ++ ++ sprintf(vol_node, UBI_VOLUME_PATTERN, dev_info.dev_num, ++ req.vol_id); ++ ++ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { ++ failed("ubi_get_vol_info"); ++ goto remove; ++ } ++ ++ if (test_rsvol1(&vol_info)) { ++ err_msg("alignment = %d", req.alignment); ++ goto remove; ++ } ++ ++ if (ubi_rmvol(libubi, node, req.vol_id)) { ++ failed("ubi_rmvol"); ++ return -1; ++ } ++ } ++ ++ return 0; ++ ++remove: ++ ubi_rmvol(libubi, node, req.vol_id); ++ return -1; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ if (initial_check(argc, argv)) ++ return 1; ++ ++ node = argv[1]; ++ ++ libubi = libubi_open(); ++ if (libubi == NULL) { ++ failed("libubi_open"); ++ return 1; ++ } ++ ++ if (ubi_get_dev_info(libubi, node, &dev_info)) { ++ failed("ubi_get_dev_info"); ++ goto close; ++ } ++ ++ if (test_basic(UBI_DYNAMIC_VOLUME)) ++ goto close; ++ if (test_basic(UBI_STATIC_VOLUME)) ++ goto close; ++ if (test_rsvol(UBI_DYNAMIC_VOLUME)) ++ goto close; ++ if (test_rsvol(UBI_STATIC_VOLUME)) ++ goto close; ++ ++ libubi_close(libubi); ++ return 0; ++ ++close: ++ libubi_close(libubi); ++ return 1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/runtests.sh linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/runtests.sh +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/runtests.sh 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/runtests.sh 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,39 @@ ++#!/bin/sh ++ ++ubidev="$1" ++tests="mkvol_basic mkvol_bad mkvol_paral rsvol io_basic io_read io_update ++io_paral volrefcnt" ++ ++if test -z "$ubidev"; ++then ++ echo "Usage:" ++ echo "$0 " ++ exit 1 ++fi ++ ++ubiname=`echo $ubidev | cut -d/ -f3` ++ ++major=`cat /sys/class/ubi/$ubiname/dev | cut -d: -f1` ++ ++for minor in `seq 0 4`; do ++ if test ! -e ${ubidev}_${minor} ; ++ then ++ mknod ${ubidev}_${minor} c $major $(($minor + 1)) ++ fi ++done ++ ++if ! test -c "$ubidev"; ++then ++ echo "Error: $ubidev is not character device" ++ exit 1 ++fi ++ ++for t in `echo $tests`; ++do ++ echo "Running $t $ubidev" ++ "./$t" "$ubidev" || exit 1 ++done ++ ++echo SUCCESS ++ ++exit 0 +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/volrefcnt.c linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/volrefcnt.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/tests/ubi-tests/volrefcnt.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/tests/ubi-tests/volrefcnt.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,122 @@ ++/* ++ * Copyright (c) Nokia Corporation, 2007 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem B. Bityutskiy ++ * ++ * Test volume reference counting - create a volume, open a sysfs file ++ * belonging to the volume, delete the volume but do not close the file, make ++ * sure the file cannot be read, close the file, make sure the volume ++ * disappeard, make sure its sysfs subtree disappeared. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "libubi.h" ++#define TESTNAME "rmvol" ++#include "common.h" ++ ++#define SYSFS_FILE "/sys/class/ubi/ubi%d_%d/usable_eb_size" ++ ++int main(int argc, char * const argv[]) ++{ ++ int ret, fd; ++ char fname[sizeof(SYSFS_FILE) + 20]; ++ const char *node; ++ libubi_t libubi; ++ struct ubi_dev_info dev_info; ++ struct ubi_mkvol_request req; ++ char tmp[100]; ++ ++ if (initial_check(argc, argv)) ++ return 1; ++ ++ node = argv[1]; ++ ++ libubi = libubi_open(); ++ if (libubi == NULL) { ++ failed("libubi_open"); ++ return 1; ++ } ++ ++ if (ubi_get_dev_info(libubi, node, &dev_info)) { ++ failed("ubi_get_dev_info"); ++ goto out_libubi; ++ } ++ ++ /* Create a small dynamic volume */ ++ req.vol_id = UBI_VOL_NUM_AUTO; ++ req.alignment = dev_info.min_io_size; ++ req.bytes = dev_info.leb_size; ++ req.vol_type = UBI_DYNAMIC_VOLUME; ++ req.name = "rmvol"; ++ ++ if (ubi_mkvol(libubi, node, &req)) { ++ failed("ubi_mkvol"); ++ goto out_libubi; ++ } ++ ++ /* Open volume-related sysfs file */ ++ sprintf(fname, SYSFS_FILE, dev_info.dev_num, req.vol_id); ++ fd = open(fname, O_RDONLY); ++ if (fd == -1) { ++ err_msg("cannot open %s", fname); ++ failed("open"); ++ goto out_rmvol; ++ } ++ ++ /* Remove the volume, but do not close the file */ ++ if (ubi_rmvol(libubi, node, req.vol_id)) { ++ failed("ubi_rmvol"); ++ perror("ubi_rmvol"); ++ goto out_close; ++ } ++ ++ /* Try to read from the file, this should fail */ ++ ret = read(fd, tmp, 100); ++ if (ret != -1) { ++ err_msg("read returned %d, expected -1", ret); ++ failed("read"); ++ goto out_close; ++ } ++ ++ /* Close the file and try to open it again, should fail */ ++ close(fd); ++ fd = open(fname, O_RDONLY); ++ if (fd != -1) { ++ err_msg("opened %s again, open returned %d, expected -1", ++ fname, fd); ++ failed("open"); ++ goto out_libubi; ++ } ++ ++ libubi_close(libubi); ++ return 0; ++ ++out_rmvol: ++ ubi_rmvol(libubi, node, req.vol_id); ++out_libubi: ++ libubi_close(libubi); ++ return 1; ++ ++out_close: ++ close(fd); ++ libubi_close(libubi); ++ return 1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/Makefile linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/Makefile +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,85 @@ ++# ++# Makefile for ubi-utils ++# ++ ++OPTFLAGS := -O2 -g -Wall ++KERNELHDR := ../include ++DESTDIR := /usr/local ++SBINDIR=/usr/sbin ++MANDIR=/usr/share/man ++INCLUDEDIR=/usr/include ++ ++CROSS=mipsel-linux- ++CC := $(CROSS)gcc ++CFLAGS := -I./inc -I./src -I$(KERNELHDR) $(OPTFLAGS) -Werror \ ++ -Wwrite-strings -W -std=gnu99 -DPACKAGE_VERSION=\"1.0\" ++ ++PERLPROGS = mkpfi ubicrc32.pl ++ ++NTARGETS = ubiattach ubicrc32 ubidetach ubimkvol ubinfo ubinize \ ++ ubirmvol ubiupdatevol ++TARGETS = pfiflash pddcustomize ubimirror bin2nand nand2bin ubigen \ ++ mkbootenv unubi pfi2bin $(NTARGETS) ++ ++vpath %.c ./src ++ ++%: %.o ++ $(CC) $(LDFLAGS) -g -o $@ $^ ++ ++%.o: %.c ++ $(CC) $(CFLAGS) -g -c -o $@ $< -g -Wp,-MD,.$(shell basename $<).dep ++ ++all: $(TARGETS) ++ make -C new-utils ++ ++IGNORE=${wildcard .*.c.dep} ++-include ${IGNORE} ++ ++$(NTARGETS): ++ make -C new-utils $@ ++ mv new-utils/$@ $@ ++ ++clean: ++ rm -rf *.o $(TARGETS) .*.c.dep ++ make -C new-utils clean ++ ++pddcustomize: pddcustomize.o error.o libubimirror.o bootenv.o hashmap.o \ ++ libubi.o crc32.o ++ $(CC) $(LDFLAGS) -o $@ $^ ++ ++pfiflash: pfiflash.o libpfiflash.o list.o reader.o error.o libubimirror.o \ ++ bootenv.o hashmap.o pfi.o libubi.o crc32.o ++ $(CC) $(LDFLAGS) -o $@ $^ ++ ++ubimirror: ubimirror.o error.o libubimirror.o bootenv.o hashmap.o \ ++ libubi.o crc32.o ++ $(CC) $(LDFLAGS) -o $@ $^ ++ ++nand2bin: nand2bin.o nandecc.o nandcorr.o ++ $(CC) $(LDFLAGS) -o $@ $^ ++ ++bin2nand: bin2nand.o error.o nandecc.o ++ $(CC) $(LDFLAGS) -o $@ $^ ++ ++ubigen: ubigen.o libubigen.o crc32.o ++ $(CC) $(LDFLAGS) -o $@ $^ ++ ++mkbootenv: mkbootenv.o bootenv.o hashmap.o error.o crc32.o ++ $(CC) $(LDFLAGS) -o $@ $^ ++ ++unubi: unubi.o crc32.o unubi_analyze.o eb_chain.o ++ $(CC) $(LDFLAGS) -o $@ $^ ++ ++pfi2bin: pfi2bin.o peb.o error.o list.o crc32.o libubigen.o bootenv.o \ ++ hashmap.o reader.o pfi.o ++ $(CC) $(LDFLAGS) -o $@ $^ ++ ++install: ${TARGETS} ++ mkdir -p ${DESTDIR}/${SBINDIR} ++ install -m0755 ${TARGETS} ${DESTDIR}/${SBINDIR}/ ++ (cd perl && install ${PERLPROGS} ${DESTDIR}/${SBINDIR}/) ++ ++uninstall: ++ for file in ${TARGETS} ${PERLPROGS}; do \ ++ $(RM) ${DESTDIR}/${SBINDIR}/$$file; \ ++ done +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/README linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/README +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/README 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/README 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,236 @@ ++README ++====== ++ ++The programs and libraries in this directory provide a tool-chain to ++generate binary data for embedded systems which can be flashed either ++by a hardware flash programmer, e.g. JTAG debugger, or on the target ++system directly using pfiflash, or ubimkvol, ubirmvol, ubiwritevol. ++ ++The latter is the case when there is already Linux running which has ++build in UBI support. ++ ++Authors: Oliver Lohmann ++ Frank Haverkamp ++ Andreas Arnez ++ ++mkpfi - tool for flash content generation in PFI ++ format ++pfi2bin - conversion tool to transfer a PFI file into a ++ binary image ++pfiflash - tool to update the embedded systems flash using ++ pfi files created by mkpfi ++libbootenv - library for boot-parameter processing ++libpfi - library for partial flash image (PFI) creation ++ and handling ++ubigen - tool to create binary UBI images e.g. for a ++ jtag flashing tool ++nandimg - tool to add OOB data to binary images intended ++ for NAND flash systems ++ubilib - UBI library ++ ++!!! NOTICE !!! ++If you execute ./configure in the top_level directory the helper Makefile ++gets overwritten. Thats actually no problem, but be aware of that. ++ ++1. Build Process ++ ++1.1 Build, install and forget ++ o Build all and everything ++ $make all (takes a while, builds ppc and x86 binaries/libs) ++ o Installation: ++ $make install ++ o Uninstallation: ++ $make uninstall ++ ++ o x86 only would be: ++ $make x86 && make install_x86 ++ ++1.2 Usage for a developer ++ ++ 1.2.1 The build process in detail ++ ++ o If you've checked out the sources from the CVS repository you'll find a ++ directory setup like this: ++ ++ flashutils/ ++ -rw-r--r-- 1 olli olli 1.3K Mar 14 11:53 Makefile ++ -rw-r--r-- 1 olli olli 1.9K Mar 14 10:50 Makefile.am ++ -rwxr-xr-x 1 olli olli 265 Mar 9 00:47 bootstrap ++ -rw-r--r-- 1 olli olli 1.1K Mar 9 16:55 configure.ac ++ drwxr-xr-x 2 olli olli 4.0K Mar 9 00:28 doc ++ drwxr-xr-x 2 olli olli 4.0K Mar 14 11:56 inc ++ drwxr-xr-x 2 olli olli 4.0K Mar 14 11:56 lib ++ drwxr-xr-x 17 olli olli 4.0K Mar 13 16:50 src ++ ++ o To generate the initial build templates you have to call the bootstrap ++ script: ++ $ ./bootstrap ++ o Create a directory for the target platform ++ $ mkdir build_x86 ++ o Descend into the directory and call the top-level configure script ++ with the desired options. ++ $ cd build_x86 ++ $ ../configure --prefix=/usr/local [...] ++ o Now you'll find a directory structure like this: ++ ++ flashutils/build_x86/ ++ -rw-r--r-- 1 olli olli 47K Mar 14 13:33 Makefile ++ -rw-r--r-- 1 olli olli 33K Mar 14 13:33 config.log ++ -rwxr-xr-x 1 olli olli 38K Mar 14 13:33 config.status ++ drwxr-xr-x 2 olli olli 4.0K Mar 14 13:33 inc ++ drwxr-xr-x 3 olli olli 4.0K Mar 14 13:33 lib ++ -rwxr-xr-x 1 olli olli 202K Mar 14 13:33 libtool ++ ++ o The config.guess script can be used to update the Makefiles in the ++ target directory after a change of the top-level template files ++ (i.e. the Makefile.in files). ++ $ ./config.guess ++ o To compile everything for this platform just invoke make in ++ flashutils/build_x86: ++ $ make ++ or from toplevel: ++ $ make -C ./build_x86 ++ o The build process creates a new directory "bin": ++ flashutils/build_x86/ ++ [...] ++ drwxr-xr-x 3 olli olli 4.0K Mar 14 13:41 bin ++ [...] ++ ++ This directory contains all binary files which will be installed ++ by make install, e.g.: ++ ++ flashutils/build_x86/bin/ ++ -rwxr-xr-x 1 olli olli 7.2K Mar 14 13:41 bin2nand ++ -rwxr-xr-x 1 olli olli 15K Mar 14 13:41 mkbootenv ++ -rwxr-xr-x 1 olli olli 16K Mar 14 13:41 pddcustomize ++ -rwxr-xr-x 1 olli olli 36K Mar 14 13:41 pfi2bin ++ -rwxr-xr-x 1 olli olli 6.8K Mar 14 13:41 pfiflash ++ -rwxr-xr-x 1 olli olli 5.0K Mar 14 13:41 ubicrc32 ++ -rwxr-xr-x 1 olli olli 13K Mar 14 13:41 ubigen ++ -rwxr-xr-x 1 olli olli 6.3K Mar 14 13:41 ubimirror ++ ++ ++ 1.2.2 Modifying and Adding Sources ++ ++ o There is a dedicated directory which contains all source code ++ of the flashutils package, e.g.: ++ ++ flashutils/src/ ++ drwxr-xr-x 2 olli olli 4.0K Mar 13 11:42 libbootenv ++ drwxr-xr-x 2 olli olli 4.0K Mar 13 11:42 liberror ++ drwxr-xr-x 2 olli olli 4.0K Mar 13 16:48 mkpfi ++ drwxr-xr-x 2 olli olli 4.0K Mar 13 16:12 pddcustomize ++ ++ ++ ++ The prefix "lib" is used to mark directories as part of a convenience ++ library. Binaries have no special prefix. ++ ++ o How to add sources? ++ ++ Just create a new directory at flashutils/src/, e.g.: ++ ++ For a binary: ++ $ mkdir rider ++ $ cd rider ++ $ vi rider.c ++ /* do sth with that file... */ ++ ++ For a convenience library (as well as for "normal libs") ++ $ mkdir libworld ++ $ cd libworld ++ $ vi world.c ++ /* do sth with that file... */ ++ ++ o How to register sources in the build process (for binaries)? ++ ++ You have to register your sources at the top-level automake Makefile: ++ ++ In directory flashutils/ ++ $ vi Makefile.am ++ ++ Binaries have to be registered at "bin_PROGRAMS", e.g.: ++ bin_PROGRAMS = bin/pddcustomize \ ++ bin/rider ++ ++ Add the rule how the binary is assembled, e.g.: ++ bin_pddcustomize_SOURCES = \ ++ $(top_srcdir)/src/pddcustomize/pddcustomize.c ++ bin_pddcustomize_LDADD = \ ++ $(top_builddir)/lib/libbootenv.la \ ++ $(top_builddir)/lib/liberror.la ++ ++ bin_rider_SOURCES = \ ++ $(top_srcdir)/src/rider/rider.c ++ ++ This example reflects a simple build process for "rider". "rider" ++ is built without any other dependencies or convenience libraries. ++ The example for pddcustomize is a bit more complicated. ++ "_LDADD" adds some convenience libraris into the link process of ++ "pddcustomize". Imagine, that your "rider" has common code ++ with "dragon_bin" which is held in a library called "libworld". ++ The build rules would like like the following: ++ ++ bin_rider_SOURCES = \ ++ $(top_srcdir)/src/rider/rider.c ++ bin_rider_LDADD = \ ++ $(top_builddir)/lib/libworld.la ++ ++ bin_dragon_SOURCES = \ ++ $(top_srcdir)/src/dragon_bin/dragon_bin.c ++ bin_dragon_LDADD = \ ++ $(top_builddir)/lib/libworld.la ++ ++ Don't forget to add "dragon" to "bin_PROGRAMS"! ++ Don't forget to set the build rule for the "libworld" itself! ++ This is documented in the next section. ++ ++ ++ o How to register sources in the build process (for libraries)? ++ ++ Until now we didn't care about the build process of "libworld". ++ Libraries are handled special in this build process because ++ they are handled as "modules", i.e. they are able to be built ++ without building the binaries in the same step. Additionally, ++ it is possible to assemble complex libraries out of simple ones. ++ That especially makes sense if you want to export (install) a ++ library on a system which uses some common code and makes ++ some adoptions for usability and presents a comfortable interface to ++ the user (see libpfiflash in the sources for an example). ++ ++ o Registering "libworld" as convenience library. ++ ++ Instead of editing the "Makefile.am" in "flashtools/", we have to ++ edit now the "Makefile.am" in "flashtools/lib/": ++ ++ noinst_LTLIBRARIES = libworld.la ++ ++ libworld_la_SOURCES = $(top_srcdir)/src/libworld/world.c ++ ++ o Registering "libworld" as library which gets installed. ++ ++ lib_LTLIBRARIES = libworld.la ++ libworld_la_SOURCES = $(top_srcdir)/src/libworld/world.c ++ libworld_la_LDFLAGS = -no-undefined -version-info 0:0:0 ++ ++ o Header files ++ ++ All header files are stored at "flashutils/inc", regardless ++ if convenience library or not. ++ ++ If you want to export headers you have to specify this in the Makefile.am ++ located at "flashutils/inc", e.g. (this should not be done ++ for convenience libraries): ++ ++ nobase_include_HEADERS = world.h ++ ++ ++ ++Appendix ++ ++A.1. FAQ ++ ++ Q How to call configure to setup a cross-platform build? ++ A $ ./configure --build=i686-pc-linux-gnu --host=ppc-linux \ ++ --prefix=/opt/.../ppcnf/crossroot/ \ ++ --exec-prefix=/opt/..../ppcnf/crossroot/usr +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/UBI.TXT linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/UBI.TXT +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/UBI.TXT 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/UBI.TXT 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,108 @@ ++UBI - Unsorted Block Images ++ ++UBI (Latin: "where?") manages multiple logical volumes on a single ++flash device, specifically supporting NAND flash devices. UBI provides ++a flexible partitioning concept which still allows for wear-levelling ++across the whole flash device. ++ ++In a sense, UBI may be compared to the Logical Volume Manager ++(LVM). Whereas LVM maps logical sector numbers to physical HDD sector ++numbers, UBI maps logical eraseblocks to physical eraseblocks. ++ ++More information may be found in the UBI design documentation: ++ubidesign.pdf. Which can be found here: ++http://www.linux-mtd.infradead.org/doc/ubi.html ++ ++Partitioning/Re-partitioning ++ ++ An UBI volume occupies a certain number of erase blocks. This is ++ limited by a configured maximum volume size, which could also be ++ viewed as the partition size. Each individual UBI volume's size can ++ be changed independently of the other UBI volumes, provided that the ++ sum of all volume sizes doesn't exceed a certain limit. ++ ++ UBI supports dynamic volumes and static volumes. Static volumes are ++ read-only and their contents are protected by CRC check sums. ++ ++Bad eraseblocks handling ++ ++ UBI transparently handles bad eraseblocks. When a physical ++ eraseblock becomes bad, it is substituted by a good physical ++ eraseblock, and the user does not even notice this. ++ ++Scrubbing ++ ++ On a NAND flash bit flips can occur on any write operation, ++ sometimes also on read. If bit flips persist on the device, at first ++ they can still be corrected by ECC, but once they accumulate, ++ correction will become impossible. Thus it is best to actively scrub ++ the affected eraseblock, by first copying it to a free eraseblock ++ and then erasing the original. The UBI layer performs this type of ++ scrubbing under the covers, transparently to the UBI volume users. ++ ++Erase Counts ++ ++ UBI maintains an erase count header per eraseblock. This frees ++ higher-level layers (like file systems) from doing this and allows ++ for centralized erase count management instead. The erase counts are ++ used by the wear-levelling algorithm in the UBI layer. The algorithm ++ itself is exchangeable. ++ ++Booting from NAND ++ ++ For booting directly from NAND flash the hardware must at least be ++ capable of fetching and executing a small portion of the NAND ++ flash. Some NAND flash controllers have this kind of support. They ++ usually limit the window to a few kilobytes in erase block 0. This ++ "initial program loader" (IPL) must then contain sufficient logic to ++ load and execute the next boot phase. ++ ++ Due to bad eraseblocks, which may be randomly scattered over the ++ flash device, it is problematic to store the "secondary program ++ loader" (SPL) statically. Also, due to bit-flips it may become ++ corrupted over time. UBI allows to solve this problem gracefully by ++ storing the SPL in a small static UBI volume. ++ ++UBI volumes vs. static partitions ++ ++ UBI volumes are still very similar to static MTD partitions: ++ ++ * both consist of eraseblocks (logical eraseblocks in case of UBI ++ volumes, and physical eraseblocks in case of static partitions; ++ * both support three basic operations - read, write, erase. ++ ++ But UBI volumes have the following advantages over traditional ++ static MTD partitions: ++ ++ * there are no eraseblock wear-leveling constraints in case of UBI ++ volumes, so the user should not care about this; ++ * there are no bit-flips and bad eraseblocks in case of UBI volumes. ++ ++ So, UBI volumes may be considered as flash devices with relaxed ++ restrictions. ++ ++Where can it be found? ++ ++ Documentation, kernel code and applications can be found in the MTD ++ gits. ++ ++What are the applications for? ++ ++ The applications help to create binary flash images for two ++ purposes: pfi files (partial flash images) for in-system update of ++ UBI volumes, and plain binary images, with or without OOB data in ++ case of NAND, for a manufacturing step. Furthermore some tools ++ are/and will be created that allow flash content analysis after a ++ system has crashed. ++ ++Who did UBI? ++ ++ The original ideas, where UBI is based on, were developed by Andreas ++ Arnez, Frank Haverkamp and Thomas Gleixner. Josh W. Boyer and ++ some others were involved too. The implementation of the kernel ++ layer was done by Artem B. Bityutskiy. The user-space applications ++ and tools were written by Oliver Lohmann with contributions from ++ Frank Haverkamp, Andreas Arnez, and Artem. Joern Engel contributed a ++ patch which modifies JFFS2 so that it can be run on a UBI ++ volume. Thomas Gleixner did modifications to the NAND layer and also ++ some to JFFS2 to make it work. +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/doc/unubi.roff linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/doc/unubi.roff +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/doc/unubi.roff 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/doc/unubi.roff 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,123 @@ ++.TH UNUBI 1 "NOVEMBER 2006" FSP "FSP Flashutils" ++.SH NAME ++unubi \- extract volumes/eraseblocks from a raw\-UBI image ++.SH SYNOPSIS ++\fBunubi [\-aevEV] [\-d \fIout\-dir\fB] [\-r \fIvolume\-id\fB] ++[\-b \fIblock\-size\fB] \fIimage\-file ++.SH DESCRIPTION ++.PP ++\fBunubi\fR reads an image file containing blocks of UBI headers and data ++(such as produced from \fBnand2bin\fR) and rebuilds the volumes within. ++The default operation (when no flags are given) is to rebuild all valid ++volumes found in the image. \fBunubi\fR can also read straight from the ++onboard MTD device (ex. /dev/mtdblock/NAND). ++.SH OPTIONS ++.IP "\-a, \-\-analyze" ++When flagged, analysis files are generated within the output directory. These ++may include tables and or graphs detailing statistics gathered from the ++eraseblock data. Files are prefixed `analysis_'. ++ ++See \fBANALYSIS\fR. ++.IP "\-b, \-\-blocksize \fIblock\-size\fR" ++Specify in bytes the \fIimage\-file\fR eraseblock size. Sizes may be ++postfixed with `KiB' or `MiB' to indicate mebibytes or kibibytes ++respectively. Default is 128KiB. ++.IP "\-d, \-\-dir \fIoutput\-dir\fR" ++Specify the output directory. If no directory is specified, the default ++is `unubi_\fIimage\-file\fR' within the curent working directory. If the ++attempt to create the output directory fails, ++.B unubi ++will try to create it in /tmp before aborting. ++.IP "\-e, \-\-eb\-split" ++When flagged, images are created for each eraseblock in \fIimage\-file\fR ++regardless of its validity. Each image is the complete eraseblock, including ++headers and any space to the end of the eraseblock after where the data may ++end. ++ ++Invalid images are named `ebEEEE', where EEEE is the physical index of the ++eraseblock in the image. Valid images are named `ebEEEE_VVV_NNN_RRR' where ++VVV is the known volume ID, NNN is the logical number and RRR is the version ++of the eraseblock data. Note that the version number is in hexadecimal. ++ ++Invalid images may also contain this postfix, if the data in the header ++could be valid (ie. the header contains a resonable volume ID, but the ++header and/or data CRCs are not valid). If this is the case, images are named ++`ebEEEE_VVV_NNN_RRR.reason', so as to distinguish known values from ++non\-definite ones. ++ ++See \fBREASON SUFFIXES\fR. ++.IP "\-r, \-\-rebuild \fIvolume\-id\fR" ++Specify a volume to rebuild. Can be used successively to specify ++several volumes to be rebuilt. ++ ++Images are named `volumeVVV' where VVV is the volume ID. For each missing ++eraseblock, an error message will be printed. ++.IP "\-v, \-\-vol\-split" ++When flagged, images are created for each valid eraseblock in ++\fIimage\-file\fR. Since a vaild eraseblock will have a defined data start and ++data length, only this range will make up the image. ++ ++Images are named `volVVV_NNN_RRR_EEEE', where, for the data in the eraseblock, ++VVV is the volume ID, NNN is the logical number, RRR is the version and EEEE ++is the phyisical index of the eraseblock in the image. ++.IP "\-V, \-\-vol\-split!" ++Same as above, only all images are the complete eraseblock (including headers, ++and raw data, even past the point where the data is supposed to end). ++Overrides \-v when both \-v and \-V are flagged. ++.SH ANALYSIS ++The following files will be generated during the analysis: ++.IP "analysis_ec_hdr.data" ++A space delimited table with these two columns for each eraseblock: the ++eraseblock's index or physical position in the image, and the eraseblock's ++erase count. The third column contains the erase count data sorted. ++.IP "analysis_vid_hdr.data" ++A space delimited table with these four colums for each eraseblock: the ++volume ID, the volume logical number, the leb version, and the data size. ++In addition there are a normalized column representing the volume ID and ++volume logical number, a normalized column representing the leb version, and ++a normalized column representing the data_size. These normalized columns are ++used to better draw the the gnuplot image. ++.IP "analysis_ec_hdr.plot" ++A gnuplot script for quickly viewing a sample output from the respective .data ++file. ++.IP "analysis_vid_hdr.plot" ++A gnuplot script for quickly viewing a sample output from the respective .data ++file. ++.SH REASONS SUFFIXES ++When \-\-eb\-split produces possibly invalid, though usable, eraseblocks, the ++known reason suffixes are: ++.IP ".ec_magic" ++The erase counter header did not contain a valid magic field. ++.IP ".ec_hdr_crc" ++The erase counter header did not contain a vaild header CRC field. ++.IP ".vid_magic" ++The volume ID header did not contain a valid magic field. ++.IP ".vid_hdr_crc" ++The volume ID header did not contain a valid header CRC field. ++.IP ".data_crc" ++The volume ID header did not contain a valid data CRC field. ++.SH EXAMPLES ++To extract and rebuild all valid volumes from demo.img (note the output ++directory will be /home/user/unubi_demo.img): ++.sp 1 ++.RS ++.B /home/user# unubi demo.img ++.sp 1 ++.RE ++To analyze demo.img as well as extract and rebuild volume 7: ++.sp 1 ++.RS ++.B /home/user# unubi \-a \-r 7 demo.img ++.sp 1 ++.RE ++To split demo.img into raw images for each eraseblock into the folder ++/var/eraseblocks: ++.sp 1 ++.RS ++.B /home/user# unubi \-e \-d /var/eraseblocks demo.img ++.SH AUTHORS ++Frank Haverkamp ++.sp 0 ++Drake Dowsett ++.SH CONTACT ++Andreas Arnez +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/inc/libubi.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/inc/libubi.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/inc/libubi.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/inc/libubi.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,268 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem B. Bityutskiy ++ * ++ * UBI (Unsorted Block Images) library. ++ */ ++ ++#ifndef __LIBUBI_H__ ++#define __LIBUBI_H__ ++ ++#include ++#include ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* UBI version libubi is made for */ ++#define LIBUBI_UBI_VERSION 1 ++ ++/* UBI library descriptor */ ++typedef void * libubi_t; ++ ++/** ++ * struct ubi_mkvol_request - volume creation request. ++ * */ ++struct ubi_mkvol_request ++{ ++ int vol_id; ++ int alignment; ++ long long bytes; ++ int vol_type; ++ const char *name; ++}; ++ ++/** ++ * struct ubi_info - general UBI information. ++ * ++ * @dev_count count of UBI devices in system ++ * @lowest_dev_num lowest UBI device number ++ * @highest_dev_num highest UBI device number ++ * @version UBI version ++ */ ++struct ubi_info ++{ ++ int dev_count; ++ int lowest_dev_num; ++ int highest_dev_num; ++ int version; ++}; ++ ++/** ++ * struct ubi_dev_info - UBI device information. ++ * ++ * @vol_count count of volumes on this UBI device ++ * @lowest_vol_num lowest volume number ++ * @highest_vol_num highest volume number ++ * @total_ebs total number of eraseblocks on this UBI device ++ * @avail_ebs how many eraseblocks are not used and available for new ++ * volumes ++ * @total_bytes @total_ebs * @eb_size ++ * @avail_bytes @avail_ebs * @eb_size ++ * @bad_count count of bad eraseblocks ++ * @eb_size size of UBI eraseblock ++ * @max_ec current highest erase counter value ++ * @bad_rsvd how many physical eraseblocks of the underlying flash ++ * device are reserved for bad eraseblocks handling ++ * @max_vol_count maximum count of volumes on this UBI device ++ * @min_io_size minimum input/output size of the UBI device ++ */ ++struct ubi_dev_info ++{ ++ int dev_num; ++ int vol_count; ++ int lowest_vol_num; ++ int highest_vol_num; ++ int total_ebs; ++ int avail_ebs; ++ long long total_bytes; ++ long long avail_bytes; ++ int bad_count; ++ int eb_size; ++ long long max_ec; ++ int bad_rsvd; ++ int max_vol_count; ++ int min_io_size; ++}; ++ ++/** ++ * struct ubi_vol_info - UBI volume information. ++ * ++ * @dev_num UBI device number the volume resides on ++ * @vol_id ID of this volume ++ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) ++ * @alignment alignemnt of this volume ++ * @data_bytes how many data bytes are stored on this volume (equivalent to ++ * @rsvd_bytes for dynamic volumes) ++ * @rsvd_bytes how many bytes are reserved for this volume ++ * @rsvd_ebs how many eraseblocks are reserved for this volume ++ * @eb_size logical eraseblock size of this volume (may be less then ++ * device's logical eraseblock size due to alignment) ++ * @corrupted the volume is corrupted if this flag is not zero ++ * @name volume name (null-terminated) ++ */ ++struct ubi_vol_info ++{ ++ int dev_num; ++ int vol_id; ++ int type; ++ int alignment; ++ long long data_bytes; ++ long long rsvd_bytes; ++ int rsvd_ebs; ++ int eb_size; ++ int corrupted; ++ char name[UBI_VOL_NAME_MAX + 1]; ++}; ++ ++/** ++ * libubi_open - open UBI library. ++ * ++ * This function initializes and opens the UBI library and returns UBI library ++ * descriptor in case of success and %NULL in case of failure. ++ */ ++libubi_t libubi_open(void); ++ ++/** ++ * libubi_close - close UBI library ++ * ++ * @desc UBI library descriptor ++ */ ++void libubi_close(libubi_t desc); ++ ++/** ++ * ubi_get_info - get general UBI information. ++ * ++ * @info pointer to the &struct ubi_info object to fill ++ * @desc UBI library descriptor ++ * ++ * This function fills the passed @info object with general UBI information and ++ * returns %0 in case of success and %-1 in case of failure. ++ */ ++int ubi_get_info(libubi_t desc, struct ubi_info *info); ++ ++/** ++ * ubi_mkvol - create an UBI volume. ++ * ++ * @desc UBI library descriptor ++ * @node name of the UBI character device to create a volume at ++ * @req UBI volume creation request (defined at ) ++ * ++ * This function creates a UBI volume as described at @req and returns %0 in ++ * case of success and %-1 in case of failure. The assigned volume ID is ++ * returned in @req->vol_id. ++ */ ++int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req); ++ ++/** ++ * ubi_rmvol - remove a UBI volume. ++ * ++ * @desc UBI library descriptor ++ * @node name of the UBI character device to remove a volume from ++ * @vol_id ID of the volume to remove ++ * ++ * This function removes volume @vol_id from UBI device @node and returns %0 in ++ * case of success and %-1 in case of failure. ++ */ ++int ubi_rmvol(libubi_t desc, const char *node, int vol_id); ++ ++/** ++ * ubi_rsvol - re-size UBI volume. ++ * ++ * @desc UBI library descriptor ++ * @node name of the UBI character device owning the volume which should be ++ * re-sized ++ * @vol_id volume ID to re-size ++ * @bytes new volume size in bytes ++ * ++ * This function returns %0 in case of success and %-1 in case of error. ++ */ ++int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes); ++ ++/** ++ * ubi_get_dev_info - get UBI device information. ++ * ++ * @desc UBI library descriptor ++ * @node name of the UBI character device to fetch information about ++ * @info pointer to the &struct ubi_dev_info object to fill ++ * ++ * This function fills the passed @info object with UBI device information and ++ * returns %0 in case of success and %-1 in case of failure. ++ */ ++int ubi_get_dev_info(libubi_t desc, const char *node, ++ struct ubi_dev_info *info); ++ ++/** ++ * ubi_get_dev_info1 - get UBI device information. ++ * ++ * @desc UBI library descriptor ++ * @dev_num UBI device number to fetch information about ++ * @info pointer to the &struct ubi_dev_info object to fill ++ * ++ * This function is identical to 'ubi_get_dev_info()' except that it accepts UBI ++ * device number, not UBI character device. ++ */ ++int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info); ++ ++/** ++ * ubi_get_vol_info - get UBI volume information. ++ * ++ * @desc UBI library descriptor ++ * @node name of the UBI volume character device to fetch information about ++ * @info pointer to the &struct ubi_vol_info object to fill ++ * ++ * This function fills the passed @info object with UBI volume information and ++ * returns %0 in case of success and %-1 in case of failure. ++ */ ++int ubi_get_vol_info(libubi_t desc, const char *node, ++ struct ubi_vol_info *info); ++ ++/** ++ * ubi_get_vol_info1 - get UBI volume information. ++ * ++ * @desc UBI library descriptor ++ * @dev_num UBI device number ++ * @vol_id ID of the UBI volume to fetch information about ++ * @info pointer to the &struct ubi_vol_info object to fill ++ * ++ * This function is identical to 'ubi_get_vol_info()' except that it accepts UBI ++ * volume number, not UBI volume character device. ++ */ ++int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, ++ struct ubi_vol_info *info); ++ ++/** ++ * ubi_update_start - start UBI volume update. ++ * ++ * @desc UBI library descriptor ++ * @fd volume character devie file descriptor ++ * @bytes how many bytes will be written to the volume ++ * ++ * This function initiates UBI volume update and returns %0 in case of success ++ * and %-1 in case of error. ++ */ ++int ubi_update_start(libubi_t desc, int fd, long long bytes); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* !__LIBUBI_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/lib/Makefile.am linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/lib/Makefile.am +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/lib/Makefile.am 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/lib/Makefile.am 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,58 @@ ++AUTOMAKE_OPTIONS = foreign ++INCLUDES=-I$(top_srcdir)/inc -I$(top_srcdir)/../../kernel/include ++ ++# ----------------------------------------------------------------------------- ++# all export libs which shall be generated ++lib_LTLIBRARIES = libubi.la \ ++ libpfiflash.la ++ ++# ----------------------------------------------------------------------------- ++# all convinence libs which shall be generated ++noinst_LTLIBRARIES = libcrc32.la \ ++ libubigen.la \ ++ liberror.la \ ++ liblist.la \ ++ libbootenv.la \ ++ libpfi.la \ ++ libpeb.la \ ++ libreader.la \ ++ libubimirror.la ++ ++# ----------------------------------------------------------------------------- ++# exported libs ++libpfiflash_la_SOURCES = $(top_srcdir)/src/libpfiflash/pfiflash.c ++libpfiflash_la_LDFLAGS = -no-undefined -version-info 1:0:0 ++libpfiflash_la_LIBADD = libreader.la \ ++ libubimirror.la \ ++ libubi.la ++ ++libubi_la_SOURCES = $(top_srcdir)/src/libubi/libubi.c \ ++ $(top_srcdir)/src/libubi/libubi_sysfs.c ++libubi_la_LDFLAGS = -no-undefined -version-info 1:0:0 ++ ++# ----------------------------------------------------------------------------- ++# complex convinence libs, beware for double includes. ++libreader_la_SOURCES = $(top_srcdir)/src/libreader/reader.c ++libreader_la_LIBADD = libpfi.la \ ++ liblist.la \ ++ libpeb.la \ ++ libbootenv.la ++ ++libubigen_la_SOURCES = $(top_srcdir)/src/libubigen/ubigen.c ++libubigen_la_LIBADD = libcrc32.la ++ ++libbootenv_la_SOURCES = $(top_srcdir)/src/libbootenv/bootenv.c \ ++ $(top_srcdir)/src/libbootenv/hashmap.c ++libbootenv_la_LIBADD = libcrc32.la ++ ++libubimirror_la_SOURCES = $(top_srcdir)/src/libubimirror/ubimirror.c ++libubimirror_la_LIBADD = libubi.la ++ ++ ++# ----------------------------------------------------------------------------- ++# simple convinence libs ++libcrc32_la_SOURCES = $(top_srcdir)/src/libcrc32/crc32.c ++liberror_la_SOURCES = $(top_srcdir)/src/liberror/error.c ++liblist_la_SOURCES = $(top_srcdir)/src/liblist/list.c ++libpeb_la_SOURCES = $(top_srcdir)/src/libpeb/peb.c ++libpfi_la_SOURCES = $(top_srcdir)/src/libpfi/pfi.c +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/LICENSE.libiniparser linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/LICENSE.libiniparser +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/LICENSE.libiniparser 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/LICENSE.libiniparser 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,21 @@ ++Copyright (c) 2000-2007 by Nicolas Devillard. ++MIT License ++ ++Permission is hereby granted, free of charge, to any person obtaining a ++copy of this software and associated documentation files (the "Software"), ++to deal in the Software without restriction, including without limitation ++the rights to use, copy, modify, merge, publish, distribute, sublicense, ++and/or sell copies of the Software, and to permit persons to whom the ++Software is furnished to do so, subject to the following conditions: ++ ++The above copyright notice and this permission notice shall be included in ++all copies or substantial portions of the Software. ++ ++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++DEALINGS IN THE SOFTWARE. ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/Makefile linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/Makefile +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,81 @@ ++# ++# Makefile for ubi-utils ++# ++ ++OPTFLAGS := -O2 -Wall ++KERNELHDR := ../../include ++#DESTDIR := /usr/local ++DESTDIR := /nfsroot/user/yrtan ++SBINDIR=/usr/sbin ++MANDIR=/usr/man ++INCLUDEDIR=/usr/include ++CROSS=mipsel-linux- ++CC := $(CROSS)gcc ++CFLAGS := -Iinclude -Isrc -I$(KERNELHDR) $(OPTFLAGS) -Werror -Wall ++ ++LIBS = libubi libmtd libubigen libiniparser libscan ++UTILS = ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \ ++ ubidetach ubiformat ubidumpvol ubicrcvol \ ++ ubinize ubicrcsf ubirefimg ++vpath %.c src ++ ++all: $(UTILS) ++ ++# The below cancels existing implicite rule to make programs from .c files, ++# in order to force make using our rule defined below ++%: %.c ++ ++# The below is the rule to get an .o file from a .c file ++%.o: %.c ++ $(CC) $(CFLAGS) $< -c -o $@ ++ ++# And the below is the rule to get final executable from its .o and common.o ++%: libubi.a %.o common.o ++ $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lubi -o $@ ++ ++ubicrcsf: ubicrcsf.o crc32.o ++ $(CC) $(CFLAGS) -o $@ $^ ++ ++ubicrcvol: ubicrcvol.o common.o crc32.o libubi.a ++ $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lubi -o $@ ++ ++ubicrc32: ubicrc32.o crc32.o ++ $(CC) $(CFLAGS) -o $@ $^ ++ ++ubinize: ubinize.o common.o crc32.o libiniparser.a libubigen.a ++ $(CC) $(CFLAGS) $(filter %.o, $^) -L. -liniparser -lubigen -o $@ ++ ++ubiformat: ubiformat.o common.o crc32.o libmtd.a libscan.a libubi.a libubigen.a ++ $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lmtd -lscan -lubi -lubigen -o $@ ++ ++libubi.a: libubi.o ++ $(AR) crv $@ $^ ++ ranlib $@ ++ ++libmtd.a: libmtd.o ++ $(AR) crv $@ $^ ++ ranlib $@ ++ ++libubigen.a: libubigen.o ++ $(AR) crv $@ $^ ++ ranlib $@ ++ ++libiniparser.a: libiniparser.o dictionary.o ++ $(AR) crv $@ $^ ++ ranlib $@ ++ ++libscan.a: libscan.o crc32.o ++ $(AR) crv $@ $^ ++ ranlib $@ ++ ++clean: ++ rm -rf *.o $(addsuffix .a, $(LIBS)) $(UTILS) .*.c.dep ++ ++install: ${UTILS} ++ mkdir -p ${DESTDIR}/${SBINDIR} ++ install -m0755 ${UTILS} ${DESTDIR}/${SBINDIR}/ ++ ++uninstall: ++ for file in ${UTILS}; do \ ++ $(RM) ${DESTDIR}/${SBINDIR}/$$file; \ ++ done +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/README linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/README +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/README 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/README 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,55 @@ ++This directory contains a new UBI toolchain which is intended to replace ++the old one. All utilities support "-h" option which prints sufficient ++usage information. See the MTD web-site for more information. ++ ++Motivation for new tool-chain. ++ ++I was doing very active UBI development and had to add new features like ++dynamic UBI devices and auto-resize feature. Because of the mess in the ++the old tools I basically could not figure out how to upgrade them. In ++my humble oppinion, they are unmaintainable. The original authors did not ++show enthusiasm when I mailed them and asked to clean-up the tool-chain ++[1]. Thus, I re-implemented them, but I did borrow things from the old ++tool-chain and preserved copyrights and author names. ++ ++I really did try to clean-up the old tool chain, but gave up (see git ++history for confirmation). So, ++ ++1. I found the source codes very difficult to navigate and read, especially ++ those related to pdd, pfi, and bootenv. Try to do this yourself - they ++ are a puzzle. ++2. I foud the concept of PFI needlesly complecated - PFI file is nothing ++ else but the initial configuration .ini file + the contents of the ++ UBI volumes packed into one file, but with some changes of the .ini file's ++ format. The PFI file format is not very nice and it is difficult to parse, ++ especially because the PFI headers do not tell you data star and end for ++ each data chunk, and you have to do additional parsing. ++ ++ So basically, you have .ini file + images, then you transfer this to pfi, ++ which does not add any other information. For .ini you have libraries ++ which may parse them, for pfi - not. Then you have to parse this pfi ++ which adds unneeded and complex code. This all needs lists, hashmaps, ++ and so on - for no reason. ++3. I found the command line options of the utilities to be inconsistent. ++ This is OK when you script your single task and do not touch it anymore. ++ But when you have to use the utilities while developing and testing, ++ It is difficult to remember their inconsistent options. ++4. I found it wrong to add development options to user utilities like ++ "broken update". End users should not see them. ++5. I did not find any consistent style and convention inside which ++ irritated me. ++6. I found it weird to introduce needless "logging infrastructure" instead ++ of just re-directing stdout and stderr to another file. ++7. I found the tool to be rather IBM-setup oriented. For example, the VID ++ header position was hard-coded. Some utilities were just weird, like ++ mkbootenv, which changed some ethernet addresses mentioned in a ++ configuration file. ++8. Finally, it was very difficult to realize what is pfi and pdd, what for, ++ why I need this transiant pfi file when I just want to create an UBI ++ image. There was zero documentation. ++ ++And so on. ++ ++Feb 19, 2008, Artem Bityutskiy. ++ ++[1]. http://lists.infradead.org/pipermail/linux-mtd/2007-December/020134.html +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libiniparser.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libiniparser.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libiniparser.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libiniparser.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,280 @@ ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @file iniparser.h ++ @author N. Devillard ++ @date Sep 2007 ++ @version 3.0 ++ @brief Parser for ini files. ++*/ ++/*--------------------------------------------------------------------------*/ ++ ++/* ++ $Id: libiniparser.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ $Revision: 1.1.1.1 $ ++*/ ++ ++#ifndef _INIPARSER_H_ ++#define _INIPARSER_H_ ++ ++/*--------------------------------------------------------------------------- ++ Includes ++ ---------------------------------------------------------------------------*/ ++ ++#include ++#include ++#include ++ ++/* ++ * The following #include is necessary on many Unixes but not Linux. ++ * It is not needed for Windows platforms. ++ * Uncomment it if needed. ++ */ ++/* #include */ ++ ++#include "dictionary.h" ++ ++/*--------------------------------------------------------------------------- ++ Macros ++ ---------------------------------------------------------------------------*/ ++/** For backwards compatibility only */ ++#define iniparser_getstr(d, k) iniparser_getstring(d, k, NULL) ++#define iniparser_setstr iniparser_setstring ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Get number of sections in a dictionary ++ @param d Dictionary to examine ++ @return int Number of sections found in dictionary ++ ++ This function returns the number of sections found in a dictionary. ++ The test to recognize sections is done on the string stored in the ++ dictionary: a section name is given as "section" whereas a key is ++ stored as "section:key", thus the test looks for entries that do not ++ contain a colon. ++ ++ This clearly fails in the case a section name contains a colon, but ++ this should simply be avoided. ++ ++ This function returns -1 in case of error. ++ */ ++/*--------------------------------------------------------------------------*/ ++ ++int iniparser_getnsec(dictionary * d); ++ ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Get name for section n in a dictionary. ++ @param d Dictionary to examine ++ @param n Section number (from 0 to nsec-1). ++ @return Pointer to char string ++ ++ This function locates the n-th section in a dictionary and returns ++ its name as a pointer to a string statically allocated inside the ++ dictionary. Do not free or modify the returned string! ++ ++ This function returns NULL in case of error. ++ */ ++/*--------------------------------------------------------------------------*/ ++ ++char * iniparser_getsecname(dictionary * d, int n); ++ ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Save a dictionary to a loadable ini file ++ @param d Dictionary to dump ++ @param f Opened file pointer to dump to ++ @return void ++ ++ This function dumps a given dictionary into a loadable ini file. ++ It is Ok to specify @c stderr or @c stdout as output files. ++ */ ++/*--------------------------------------------------------------------------*/ ++ ++void iniparser_dump_ini(dictionary * d, FILE * f); ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Dump a dictionary to an opened file pointer. ++ @param d Dictionary to dump. ++ @param f Opened file pointer to dump to. ++ @return void ++ ++ This function prints out the contents of a dictionary, one element by ++ line, onto the provided file pointer. It is OK to specify @c stderr ++ or @c stdout as output files. This function is meant for debugging ++ purposes mostly. ++ */ ++/*--------------------------------------------------------------------------*/ ++void iniparser_dump(dictionary * d, FILE * f); ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Get the string associated to a key ++ @param d Dictionary to search ++ @param key Key string to look for ++ @param def Default value to return if key not found. ++ @return pointer to statically allocated character string ++ ++ This function queries a dictionary for a key. A key as read from an ++ ini file is given as "section:key". If the key cannot be found, ++ the pointer passed as 'def' is returned. ++ The returned char pointer is pointing to a string allocated in ++ the dictionary, do not free or modify it. ++ */ ++/*--------------------------------------------------------------------------*/ ++char * iniparser_getstring(dictionary * d, const char * key, char * def); ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Get the string associated to a key, convert to an int ++ @param d Dictionary to search ++ @param key Key string to look for ++ @param notfound Value to return in case of error ++ @return integer ++ ++ This function queries a dictionary for a key. A key as read from an ++ ini file is given as "section:key". If the key cannot be found, ++ the notfound value is returned. ++ ++ Supported values for integers include the usual C notation ++ so decimal, octal (starting with 0) and hexadecimal (starting with 0x) ++ are supported. Examples: ++ ++ - "42" -> 42 ++ - "042" -> 34 (octal -> decimal) ++ - "0x42" -> 66 (hexa -> decimal) ++ ++ Warning: the conversion may overflow in various ways. Conversion is ++ totally outsourced to strtol(), see the associated man page for overflow ++ handling. ++ ++ Credits: Thanks to A. Becker for suggesting strtol() ++ */ ++/*--------------------------------------------------------------------------*/ ++int iniparser_getint(dictionary * d, const char * key, int notfound); ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Get the string associated to a key, convert to a double ++ @param d Dictionary to search ++ @param key Key string to look for ++ @param notfound Value to return in case of error ++ @return double ++ ++ This function queries a dictionary for a key. A key as read from an ++ ini file is given as "section:key". If the key cannot be found, ++ the notfound value is returned. ++ */ ++/*--------------------------------------------------------------------------*/ ++double iniparser_getdouble(dictionary * d, char * key, double notfound); ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Get the string associated to a key, convert to a boolean ++ @param d Dictionary to search ++ @param key Key string to look for ++ @param notfound Value to return in case of error ++ @return integer ++ ++ This function queries a dictionary for a key. A key as read from an ++ ini file is given as "section:key". If the key cannot be found, ++ the notfound value is returned. ++ ++ A true boolean is found if one of the following is matched: ++ ++ - A string starting with 'y' ++ - A string starting with 'Y' ++ - A string starting with 't' ++ - A string starting with 'T' ++ - A string starting with '1' ++ ++ A false boolean is found if one of the following is matched: ++ ++ - A string starting with 'n' ++ - A string starting with 'N' ++ - A string starting with 'f' ++ - A string starting with 'F' ++ - A string starting with '0' ++ ++ The notfound value returned if no boolean is identified, does not ++ necessarily have to be 0 or 1. ++ */ ++/*--------------------------------------------------------------------------*/ ++int iniparser_getboolean(dictionary * d, const char * key, int notfound); ++ ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Set an entry in a dictionary. ++ @param ini Dictionary to modify. ++ @param entry Entry to modify (entry name) ++ @param val New value to associate to the entry. ++ @return int 0 if Ok, -1 otherwise. ++ ++ If the given entry can be found in the dictionary, it is modified to ++ contain the provided value. If it cannot be found, -1 is returned. ++ It is Ok to set val to NULL. ++ */ ++/*--------------------------------------------------------------------------*/ ++int iniparser_setstring(dictionary * ini, char * entry, char * val); ++ ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Delete an entry in a dictionary ++ @param ini Dictionary to modify ++ @param entry Entry to delete (entry name) ++ @return void ++ ++ If the given entry can be found, it is deleted from the dictionary. ++ */ ++/*--------------------------------------------------------------------------*/ ++void iniparser_unset(dictionary * ini, char * entry); ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Finds out if a given entry exists in a dictionary ++ @param ini Dictionary to search ++ @param entry Name of the entry to look for ++ @return integer 1 if entry exists, 0 otherwise ++ ++ Finds out if a given entry exists in the dictionary. Since sections ++ are stored as keys with NULL associated values, this is the only way ++ of querying for the presence of sections in a dictionary. ++ */ ++/*--------------------------------------------------------------------------*/ ++int iniparser_find_entry(dictionary * ini, char * entry) ; ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Parse an ini file and return an allocated dictionary object ++ @param ininame Name of the ini file to read. ++ @return Pointer to newly allocated dictionary ++ ++ This is the parser for ini files. This function is called, providing ++ the name of the file to be read. It returns a dictionary object that ++ should not be accessed directly, but through accessor functions ++ instead. ++ ++ The returned dictionary must be freed using iniparser_freedict(). ++ */ ++/*--------------------------------------------------------------------------*/ ++dictionary * iniparser_load(const char * ininame); ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Free all memory associated to an ini dictionary ++ @param d Dictionary to free ++ @return void ++ ++ Free all memory associated to an ini dictionary. ++ It is mandatory to call this function before the dictionary object ++ gets out of the current context. ++ */ ++/*--------------------------------------------------------------------------*/ ++void iniparser_freedict(dictionary * d); ++ ++#endif +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libmtd.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libmtd.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libmtd.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libmtd.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,73 @@ ++/* ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem Bityutskiy ++ * ++ * MTD library. ++ */ ++ ++#ifndef __LIBMTD_H__ ++#define __LIBMTD_H__ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * struct mtd_info - information about an MTD device. ++ * @num: MTD device number ++ * @major: major number of corresponding character device ++ * @minor: minor number of corresponding character device ++ * @type: flash type (constants like %MTD_NANDFLASH defined in mtd-abi.h) ++ * @type_str: static R/O flash type string ++ * @size: device size in bytes ++ * @eb_cnt: count of eraseblocks ++ * @eb_size: eraseblock size ++ * @min_io_size: minimum input/output unit size ++ * @subpage_size: sub-page size (not set by 'mtd_get_info()'!!!) ++ * @rdonly: non-zero if the device is read-only ++ * @allows_bb: non-zero if the MTD device may have bad eraseblocks ++ * @fd: descriptor of the opened MTD character device node ++ */ ++struct mtd_info ++{ ++ int num; ++ int major; ++ int minor; ++ int type; ++ const char *type_str; ++ long long size; ++ int eb_cnt; ++ int eb_size; ++ int min_io_size; ++ int subpage_size; ++ unsigned int rdonly:1; ++ unsigned int allows_bb:1; ++ int fd; ++}; ++ ++int mtd_get_info(const char *node, struct mtd_info *mtd); ++int mtd_erase(const struct mtd_info *mtd, int eb); ++int mtd_is_bad(const struct mtd_info *mtd, int eb); ++int mtd_read(const struct mtd_info *mtd, int eb, int offs, void *buf, int len); ++int mtd_write(const struct mtd_info *mtd, int eb, int offs, void *buf, int len); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* __LIBMTD_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libscan.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libscan.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libscan.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libscan.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,112 @@ ++/* ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem Bityutskiy ++ * ++ * UBI scanning library. ++ */ ++ ++#ifndef __LIBSCAN_H__ ++#define __LIBSCAN_H__ ++ ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* ++ * If an eraseblock does not contain an erase counter, this value is used ++ * instead of the erase counter. ++ */ ++#define NO_EC 0xFFFFFFFF ++ ++/* ++ * If an eraseblock contains a corrupted erase counter, this value is used ++ * instead of the erase counter. ++ */ ++#define CORRUPT_EC 0xFFFFFFFE ++ ++/* ++ * If an eraseblock does not contain an erase counter, one of these values is ++ * used. ++ * ++ * @EB_EMPTY: the eraseblock appeared to be empty ++ * @EB_CORRUPTED: the eraseblock contains corrupted erase counter header ++ * @EB_ALIEN: the eraseblock contains some non-UBI data ++ * @EC_MAX: maximum allowed erase counter value ++ */ ++enum ++{ ++ EB_EMPTY = 0xFFFFFFFF, ++ EB_CORRUPTED = 0xFFFFFFFE, ++ EB_ALIEN = 0xFFFFFFFD, ++ EB_BAD = 0xFFFFFFFC, ++ EC_MAX = UBI_MAX_ERASECOUNTER, ++}; ++ ++/** ++ * struct ubi_scan_info - UBI scanning information. ++ * @ec: erase counters or eraseblock status for all eraseblocks ++ * @mean_ec: mean erase counter ++ * @ok_cnt: count of eraseblock with correct erase counter header ++ * @empty_cnt: count of supposedly eraseblocks ++ * @corrupted_cnt: count of eraseblocks with corrupted erase counter header ++ * @alien_cnt: count of eraseblock containing non-ubi data ++ * @bad_cnt: count of bad eraseblocks ++ * @bad_cnt: count of non-bad eraseblocks ++ * @vid_hdr_offs: volume ID header offset from the found EC headers (%-1 means ++ * undefined) ++ * @data_offs: data offset from the found EC headers (%-1 means undefined) ++ */ ++struct ubi_scan_info ++{ ++ uint32_t *ec; ++ long long mean_ec; ++ int ok_cnt; ++ int empty_cnt; ++ int corrupted_cnt; ++ int alien_cnt; ++ int bad_cnt; ++ int good_cnt; ++ int vid_hdr_offs; ++ int data_offs; ++}; ++ ++struct mtd_info; ++ ++/** ++ * ubi_scan - scan an MTD device. ++ * @mtd: information about the MTD device to scan ++ * @info: the result of the scanning is returned here ++ * @verbose: verbose mode: %0 - be silent, %1 - output progress information, ++ * 2 - debugging output mode ++ */ ++int ubi_scan(struct mtd_info *mtd, struct ubi_scan_info **info, int verbose); ++ ++/** ++ * ubi_scan_free - free scanning information. ++ * @si: scanning information to free ++ */ ++void ubi_scan_free(struct ubi_scan_info *si); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* __LIBSCAN_H__ */ ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libubi.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libubi.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libubi.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libubi.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,382 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem Bityutskiy ++ * ++ * UBI (Unsorted Block Images) library. ++ */ ++ ++#ifndef __LIBUBI_H__ ++#define __LIBUBI_H__ ++ ++#include ++#include ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* UBI version libubi is made for */ ++#define LIBUBI_UBI_VERSION 1 ++ ++/* UBI library descriptor */ ++typedef void * libubi_t; ++ ++/** ++ * struct ubi_attach_request - MTD device attachement request. ++ * @dev_num: number to assigne to the newly created UBI device ++ * (%UBI_DEV_NUM_AUTO should be used to automatically assign the ++ * number) ++ * @mtd_num: MTD device number to attach ++ * @vid_hdr_offset: VID header offset (%0 means default offset and this is what ++ * most of the users want) ++ */ ++struct ubi_attach_request ++{ ++ int dev_num; ++ int mtd_num; ++ int vid_hdr_offset; ++}; ++ ++/** ++ * struct ubi_mkvol_request - volume creation request. ++ * @vol_id: ID to assign to the new volume (%UBI_VOL_NUM_AUTO should be used to ++ * automatically assign ID) ++ * @alignment: volume alignment ++ * @bytes: volume size in bytes ++ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) ++ * @name: volume name ++ */ ++struct ubi_mkvol_request ++{ ++ int vol_id; ++ int alignment; ++ long long bytes; ++ int vol_type; ++ const char *name; ++}; ++ ++/** ++ * struct ubi_info - general UBI information. ++ * @dev_count: count of UBI devices in system ++ * @lowest_dev_num: lowest UBI device number ++ * @highest_dev_num: highest UBI device number ++ * @version: UBI version ++ * @ctrl_major: major number of the UBI control device ++ * @ctrl_minor: minor number of the UBI control device ++ */ ++struct ubi_info ++{ ++ int dev_count; ++ int lowest_dev_num; ++ int highest_dev_num; ++ int version; ++ int ctrl_major; ++ int ctrl_minor; ++}; ++ ++/** ++ * struct ubi_dev_info - UBI device information. ++ * @vol_count: count of volumes on this UBI device ++ * @lowest_vol_num: lowest volume number ++ * @highest_vol_num: highest volume number ++ * @major: major number of corresponding character device ++ * @minor: minor number of corresponding character device ++ * @total_lebs: total number of logical eraseblocks on this UBI device ++ * @avail_lebs: how many logical eraseblocks are not used and available for new ++ * volumes ++ * @total_bytes: @total_lebs * @leb_size ++ * @avail_bytes: @avail_lebs * @leb_size ++ * @bad_count: count of bad physical eraseblocks ++ * @leb_size: logical eraseblock size ++ * @max_ec: current highest erase counter value ++ * @bad_rsvd: how many physical eraseblocks of the underlying flash device are ++ * reserved for bad eraseblocks handling ++ * @max_vol_count: maximum possible number of volumes on this UBI device ++ * @min_io_size: minimum input/output unit size of the UBI device ++ */ ++struct ubi_dev_info ++{ ++ int dev_num; ++ int vol_count; ++ int lowest_vol_num; ++ int highest_vol_num; ++ int major; ++ int minor; ++ int total_lebs; ++ int avail_lebs; ++ long long total_bytes; ++ long long avail_bytes; ++ int bad_count; ++ int leb_size; ++ long long max_ec; ++ int bad_rsvd; ++ int max_vol_count; ++ int min_io_size; ++}; ++ ++/** ++ * struct ubi_vol_info - UBI volume information. ++ * @dev_num: UBI device number the volume resides on ++ * @vol_id: ID of this volume ++ * @major: major number of corresponding volume character device ++ * @minor: minor number of corresponding volume character device ++ * @dev_major: major number of corresponding UBI device character device ++ * @dev_minor: minor number of corresponding UBI device character device ++ * @type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) ++ * @alignment: alignemnt of this volume ++ * @data_bytes: how many data bytes are stored on this volume (equivalent to ++ * @rsvd_bytes for dynamic volumes) ++ * @rsvd_bytes: how many bytes are reserved for this volume ++ * @rsvd_lebs: how many logical eraseblocks are reserved for this volume ++ * @leb_size: logical eraseblock size of this volume (may be less then ++ * device's logical eraseblock size due to alignment) ++ * @corrupted: non-zero if the volume is corrupted ++ * @name: volume name (null-terminated) ++ */ ++struct ubi_vol_info ++{ ++ int dev_num; ++ int vol_id; ++ int major; ++ int minor; ++ int dev_major; ++ int dev_minor; ++ int type; ++ int alignment; ++ long long data_bytes; ++ long long rsvd_bytes; ++ int rsvd_lebs; ++ int leb_size; ++ int corrupted; ++ char name[UBI_VOL_NAME_MAX + 1]; ++}; ++ ++/** ++ * libubi_open - open UBI library. ++ * @required: if non-zero, libubi will print an error messages if this UBI is ++ * not present in the system ++ * ++ * This function initializes and opens the UBI library and returns UBI library ++ * descriptor in case of success and %NULL in case of failure. ++ */ ++libubi_t libubi_open(int required); ++ ++/** ++ * libubi_close - close UBI library. ++ * @desc UBI library descriptor ++ */ ++void libubi_close(libubi_t desc); ++ ++/** ++ * ubi_get_info - get general UBI information. ++ * @desc: UBI library descriptor ++ * @info: pointer to the &struct ubi_info object to fill ++ * ++ * This function fills the passed @info object with general UBI information and ++ * returns %0 in case of success and %-1 in case of failure. ++ */ ++int ubi_get_info(libubi_t desc, struct ubi_info *info); ++ ++/** ++ * mtd_num2ubi_dev - find UBI device by attached MTD device. ++ * @@desc: UBI library descriptor ++ * @mtd_num: MTD device number ++ * @dev_num: UBI device number is returned here ++ * ++ * This function finds UBI device to which MTD device @mtd_num is attached. ++ * Returns %0 if the UBI device was found and %-1 if not. ++ */ ++int mtd_num2ubi_dev(libubi_t desc, int mtd_num, int *dev_num); ++ ++/** ++ * ubi_attach_mtd - attach MTD device to UBI. ++ * @desc: UBI library descriptor ++ * @node: name of the UBI control character device node ++ * @req: MTD attach request. ++ * ++ * This function creates a new UBI device by attaching an MTD device as ++ * described by @req. Returns %0 in case of success and %-1 in case of failure. ++ * The newly created UBI device number is returned in @req->dev_num. ++ */ ++int ubi_attach_mtd(libubi_t desc, const char *node, ++ struct ubi_attach_request *req); ++ ++/** ++ * ubi_detach_mtd - detach an MTD device. ++ * @desc: UBI library descriptor ++ * @node: name of the UBI control character device node ++ * @mtd_num: MTD device number to detach ++ * ++ * This function detaches MTD device number @mtd_num from UBI, which means the ++ * corresponding UBI device is removed. Returns zero in case of success and %-1 ++ * in case of failure. ++ */ ++int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num); ++ ++/** ++ * ubi_remove_dev - remove an UBI device. ++ * @desc: UBI library descriptor ++ * @node: name of the UBI control character device node ++ * @ubi_dev: UBI device number to remove ++ * ++ * This function removes UBI device number @ubi_dev and returns zero in case of ++ * success and %-1 in case of failure. ++ */ ++int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev); ++ ++/** ++ * ubi_mkvol - create an UBI volume. ++ * @desc: UBI library descriptor ++ * @node: name of the UBI character device to create a volume at ++ * @req: UBI volume creation request ++ * ++ * This function creates a UBI volume as described at @req and returns %0 in ++ * case of success and %-1 in case of failure. The assigned volume ID is ++ * returned in @req->vol_id. ++ */ ++int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req); ++ ++/** ++ * ubi_rmvol - remove a UBI volume. ++ * @desc: UBI library descriptor ++ * @node: name of the UBI character device to remove a volume from ++ * @vol_id: ID of the volume to remove ++ * ++ * This function removes volume @vol_id from UBI device @node and returns %0 in ++ * case of success and %-1 in case of failure. ++ */ ++int ubi_rmvol(libubi_t desc, const char *node, int vol_id); ++ ++/** ++ * ubi_rsvol - re-size UBI volume. ++ * @desc: UBI library descriptor ++ * @node: name of the UBI character device owning the volume which should be ++ * re-sized ++ * @vol_id: volume ID to re-size ++ * @bytes: new volume size in bytes ++ * ++ * This function returns %0 in case of success and %-1 in case of error. ++ */ ++int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes); ++ ++/** ++ * ubi_node_type - test UBI node type. ++ * @desc: UBI library descriptor ++ * @node: the node to test ++ * ++ * This function tests whether @node is a UBI device or volume node and returns ++ * %1 if this is an UBI device node, %2 if this is a volume node, and %-1 if ++ * this is not an UBI node or if an error occurred (the latter is indicated by ++ * a non-zero errno). ++ */ ++int ubi_node_type(libubi_t desc, const char *node); ++ ++/** ++ * ubi_get_dev_info - get UBI device information. ++ * @desc: UBI library descriptor ++ * @node: name of the UBI character device to fetch information about ++ * @info: pointer to the &struct ubi_dev_info object to fill ++ * ++ * This function fills the passed @info object with UBI device information and ++ * returns %0 in case of success and %-1 in case of failure. ++ */ ++int ubi_get_dev_info(libubi_t desc, const char *node, ++ struct ubi_dev_info *info); ++ ++/** ++ * ubi_get_dev_info1 - get UBI device information. ++ * @desc: UBI library descriptor ++ * @dev_num: UBI device number to fetch information about ++ * @info: pointer to the &struct ubi_dev_info object to fill ++ * ++ * This function is identical to 'ubi_get_dev_info()' except that it accepts UBI ++ * device number, not UBI character device. ++ */ ++int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info); ++ ++/** ++ * ubi_get_vol_info - get UBI volume information. ++ * @desc: UBI library descriptor ++ * @node: name of the UBI volume character device to fetch information about ++ * @info: pointer to the &struct ubi_vol_info object to fill ++ * ++ * This function fills the passed @info object with UBI volume information and ++ * returns %0 in case of success and %-1 in case of failure. ++ */ ++int ubi_get_vol_info(libubi_t desc, const char *node, ++ struct ubi_vol_info *info); ++ ++/** ++ * ubi_get_vol_info1 - get UBI volume information. ++ * @desc: UBI library descriptor ++ * @dev_num: UBI device number ++ * @vol_id: ID of the UBI volume to fetch information about ++ * @info: pointer to the &struct ubi_vol_info object to fill ++ * ++ * This function is identical to 'ubi_get_vol_info()' except that it accepts UBI ++ * volume number, not UBI volume character device. ++ */ ++int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, ++ struct ubi_vol_info *info); ++ ++/** ++ * ubi_update_start - start UBI volume update. ++ * @desc: UBI library descriptor ++ * @fd: volume character devie file descriptor ++ * @bytes: how many bytes will be written to the volume ++ * ++ * This function initiates UBI volume update and returns %0 in case of success ++ * and %-1 in case of error. The caller is assumed to write @bytes data to the ++ * volume @fd afterwards. ++ */ ++int ubi_update_start(libubi_t desc, int fd, long long bytes); ++ ++ ++/** ++ * ubi_leb_read_start ++ * @fd: volume character devie file descriptor ++ * @leb: structure pointer for volume dump request ++ * ++ * This function call "UBI_IOCLEBREAD" ioctl. ++ * return %1 means the LEB is not mapped, no need to dump ++ * return %0 LEB read done successful ++ * return any others error ++ */ ++int ubi_leb_read_start(int fd, struct ubi_leb *leb); ++ ++/** ++ * ubi_leb_change_start - start atomic LEB change. ++ * @desc: UBI library descriptor ++ * @fd: volume character devie file descriptor ++ * @lnum: LEB number to change ++ * @bytes: how many bytes of new data will be written to the LEB ++ * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN) ++ * ++ * This function initiates atomic LEB change operation and returns %0 in case ++ * of success and %-1 in case of error. he caller is assumed to write @bytes ++ * data to the volume @fd afterwards. ++ */ ++int ubi_leb_change_start(libubi_t desc, int fd, int lnum, int bytes, int dtype); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* !__LIBUBI_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libubigen.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libubigen.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libubigen.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/include/libubigen.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,110 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * Authors: Frank Haverkamp ++ * Artem Bityutskiy ++ */ ++ ++#ifndef __LIBUBIGEN_H__ ++#define __LIBUBIGEN_H__ ++ ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * struct ubigen_info - libubigen information. ++ * @leb_size: logical eraseblock size ++ * @peb_size: size of the physical eraseblock ++ * @min_io_size: minimum input/output unit size ++ * @vid_hdr_offs: offset of the VID header ++ * @data_offs: data offset ++ * @ubi_ver: UBI version ++ * @vtbl_size: volume table size ++ * @max_volumes: maximum amount of volumes ++ */ ++struct ubigen_info ++{ ++ int leb_size; ++ int peb_size; ++ int min_io_size; ++ int vid_hdr_offs; ++ int data_offs; ++ int ubi_ver; ++ int vtbl_size; ++ int max_volumes; ++}; ++ ++/** ++ * struct ubigen_vol_info - information about a volume. ++ * @id: volume id ++ * @type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) ++ * @alignment: volume alignment ++ * @data_pad: how many bytes are unused at the end of the each physical ++ * eraseblock to satisfy the requested alignment ++ * @usable_leb_size: LEB size accessible for volume users ++ * @name: volume name ++ * @name_len: volume name length ++ * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, ++ * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) ++ * @used_ebs: total number of used logical eraseblocks in this volume (relevant ++ * for static volumes only) ++ * @bytes: size of the volume contents in bytes (relevant for static volumes ++ * only) ++ * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG) ++ */ ++struct ubigen_vol_info ++{ ++ int id; ++ int type; ++ int alignment; ++ int data_pad; ++ int usable_leb_size; ++ const char *name; ++ int name_len; ++ int compat; ++ int used_ebs; ++ long long bytes; ++ uint8_t flags; ++}; ++ ++void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size, ++ int subpage_size, int vid_hdr_offs, int ubi_ver); ++struct ubi_vtbl_record *ubigen_create_empty_vtbl(const struct ubigen_info *ui); ++void ubigen_init_ec_hdr(const struct ubigen_info *ui, ++ struct ubi_ec_hdr *hdr, long long ec); ++int ubigen_get_vtbl_size(const struct ubigen_info *ui); ++int ubigen_add_volume(const struct ubigen_info *ui, ++ const struct ubigen_vol_info *vi, ++ struct ubi_vtbl_record *vtbl); ++int ubigen_write_volume(const struct ubigen_info *ui, ++ const struct ubigen_vol_info *vi, long long ec, ++ long long bytes, int in, int out); ++int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2, ++ long long ec1, long long ec2, ++ struct ubi_vtbl_record *vtbl, int fd); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* !__LIBUBIGEN_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/common.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/common.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/common.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/common.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,194 @@ ++/* ++ * Copyright (C) 2007, 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * This file contains various common stuff used by UBI utilities. ++ * ++ * Authors: Artem Bityutskiy ++ * Adrian Hunter ++ */ ++ ++#include ++#include ++#include ++#include ++ ++/** ++ * get_multiplier - convert size specifier to an integer multiplier. ++ * @str: the size specifier string ++ * ++ * This function parses the @str size specifier, which may be one of ++ * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive ++ * size multiplier in case of success and %-1 in case of failure. ++ */ ++static int get_multiplier(const char *str) ++{ ++ if (!str) ++ return 1; ++ ++ /* Remove spaces before the specifier */ ++ while (*str == ' ' || *str == '\t') ++ str += 1; ++ ++ if (!strcmp(str, "KiB")) ++ return 1024; ++ if (!strcmp(str, "MiB")) ++ return 1024 * 1024; ++ if (!strcmp(str, "GiB")) ++ return 1024 * 1024 * 1024; ++ ++ /* Handle deprecated stuff */ ++ if (!strcmp(str, "KB") || !strcmp(str, "Kib") || !strcmp(str, "kib") || ++ !strcmp(str, "kiB")) { ++ fprintf(stderr, "Warning: use \"KiB\" instead of \"%s\" to " ++ "specify Kilobytes - support will be removed\n", str); ++ return 1024; ++ } ++ if (!strcmp(str, "MB") || !strcmp(str, "Mib") || !strcmp(str, "mb")) { ++ fprintf(stderr, "Warning: use \"MiB\" instead of \"%s\", " ++ "this support will be removed\n", str); ++ return 1024*1024; ++ } ++ if (!strcmp(str, "GB") || !strcmp(str, "Gib") || !strcmp(str, "gb")) { ++ fprintf(stderr, "Warning: use \"GiB\" instead of \"%s\", " ++ "this support will be removed\n", str); ++ return 1024*1024*1024; ++ } ++ ++ return -1; ++} ++ ++/** ++ * ubiutils_get_bytes - convert a string containing amount of bytes into an ++ * integer ++ * @str: string to convert ++ * ++ * This function parses @str which may have one of 'KiB', 'MiB', or 'GiB' ++ * size specifiers. Returns positive amount of bytes in case of success and %-1 ++ * in case of failure. ++ */ ++long long ubiutils_get_bytes(const char *str) ++{ ++ char *endp; ++ long long bytes = strtoull(str, &endp, 0); ++ ++ if (endp == str || bytes < 0) { ++ fprintf(stderr, "incorrect amount of bytes: \"%s\"\n", str); ++ return -1; ++ } ++ ++ if (*endp != '\0') { ++ int mult = get_multiplier(endp); ++ ++ if (mult == -1) { ++ fprintf(stderr, "bad size specifier: \"%s\" - " ++ "should be 'KiB', 'MiB' or 'GiB'\n", endp); ++ return -1; ++ } ++ bytes *= mult; ++ } ++ ++ return bytes; ++} ++ ++/** ++ * ubiutils_print_bytes - print bytes. ++ * @bytes: variable to print ++ * @bracket: whether brackets have to be put or not ++ * ++ * This is a helper function which prints amount of bytes in a human-readable ++ * form, i.e., it prints the exact amount of bytes following by the approximate ++ * amount of Kilobytes, Megabytes, or Gigabytes, depending on how big @bytes ++ * is. ++ */ ++void ubiutils_print_bytes(long long bytes, int bracket) ++{ ++ const char *p; ++ ++ if (bracket) ++ p = " ("; ++ else ++ p = ", "; ++ ++ printf("%lld bytes", bytes); ++ ++ if (bytes > 1024 * 1024 * 1024) ++ printf("%s%.1f GiB", p, (double)bytes / (1024 * 1024 * 1024)); ++ else if (bytes > 1024 * 1024) ++ printf("%s%.1f MiB", p, (double)bytes / (1024 * 1024)); ++ else if (bytes > 1024 && bytes != 0) ++ printf("%s%.1f KiB", p, (double)bytes / 1024); ++ else ++ return; ++ ++ if (bracket) ++ printf(")"); ++} ++ ++/** ++ * ubiutils_print_text - print text and fold it. ++ * @stream: file stream to print to ++ * @text: text to print ++ * @width: maximum allowed text width ++ * ++ * Print text and fold it so that each line would not have more then @width ++ * characters. ++ */ ++void ubiutils_print_text(FILE *stream, const char *text, int width) ++{ ++ int pos, bpos = 0; ++ const char *p; ++ char line[1024]; ++ ++ if (width > 1023) { ++ fprintf(stream, "%s\n", text); ++ return; ++ } ++ p = text; ++ pos = 0; ++ while (p[pos]) { ++ while (!isspace(p[pos])) { ++ line[pos] = p[pos]; ++ if (!p[pos]) ++ break; ++ ++pos; ++ if (pos == width) { ++ line[pos] = '\0'; ++ fprintf(stream, "%s\n", line); ++ p += pos; ++ pos = 0; ++ } ++ } ++ while (pos < width) { ++ line[pos] = p[pos]; ++ if (!p[pos]) { ++ bpos = pos; ++ break; ++ } ++ if (isspace(p[pos])) ++ bpos = pos; ++ ++pos; ++ } ++ line[bpos] = '\0'; ++ fprintf(stream, "%s\n", line); ++ p += bpos; ++ pos = 0; ++ while (p[pos] && isspace(p[pos])) ++ ++p; ++ } ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/common.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/common.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/common.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/common.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,84 @@ ++/* ++ * Copyright (c) Artem Bityutskiy, 2007, 2008 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __UBI_UTILS_COMMON_H__ ++#define __UBI_UTILS_COMMON_H__ ++ ++#include ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define MIN(a ,b) ((a) < (b) ? (a) : (b)) ++#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) ++ ++/* Verbose messages */ ++#define verbose(verbose, fmt, ...) do { \ ++ if (verbose) \ ++ printf(PROGRAM_NAME ": " fmt "\n", ##__VA_ARGS__); \ ++} while(0) ++ ++/* Normal messages */ ++#define normsg(fmt, ...) do { \ ++ printf(PROGRAM_NAME ": " fmt "\n", ##__VA_ARGS__); \ ++} while(0) ++#define normsg_cont(fmt, ...) do { \ ++ printf(PROGRAM_NAME ": " fmt, ##__VA_ARGS__); \ ++} while(0) ++#define normsg_cont(fmt, ...) do { \ ++ printf(PROGRAM_NAME ": " fmt, ##__VA_ARGS__); \ ++} while(0) ++ ++/* Error messages */ ++#define errmsg(fmt, ...) ({ \ ++ fprintf(stderr, PROGRAM_NAME ": error!: " fmt "\n", ##__VA_ARGS__); \ ++ -1; \ ++}) ++ ++/* System error messages */ ++#define sys_errmsg(fmt, ...) ({ \ ++ int _err = errno, _i; \ ++ fprintf(stderr, PROGRAM_NAME ": error!: " fmt "\n", ##__VA_ARGS__); \ ++ for (_i = 0; _i < sizeof(PROGRAM_NAME) + 1; _i++) \ ++ fprintf(stderr, " "); \ ++ fprintf(stderr, "error %d (%s)\n", _err, strerror(_err)); \ ++ -1; \ ++}) ++ ++/* Warnings */ ++#define warnmsg(fmt, ...) do { \ ++ fprintf(stderr, PROGRAM_NAME ": warning!: " fmt "\n", ##__VA_ARGS__); \ ++} while(0) ++ ++static inline int is_power_of_2(unsigned long long n) ++{ ++ return (n != 0 && ((n & (n - 1)) == 0)); ++} ++ ++long long ubiutils_get_bytes(const char *str); ++void ubiutils_print_bytes(long long bytes, int bracket); ++void ubiutils_print_text(FILE *stream, const char *txt, int len); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* !__UBI_UTILS_COMMON_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/crc32.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/crc32.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/crc32.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/crc32.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,95 @@ ++/* ++ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or ++ * code or tables extracted from it, as desired without restriction. ++ * ++ * First, the polynomial itself and its table of feedback terms. The ++ * polynomial is ++ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 ++ * ++ * Note that we take it "backwards" and put the highest-order term in ++ * the lowest-order bit. The X^32 term is "implied"; the LSB is the ++ * X^31 term, etc. The X^0 term (usually shown as "+1") results in ++ * the MSB being 1 ++ * ++ * Note that the usual hardware shift register implementation, which ++ * is what we're using (we're merely optimizing it by doing eight-bit ++ * chunks at a time) shifts bits into the lowest-order term. In our ++ * implementation, that means shifting towards the right. Why do we ++ * do it this way? Because the calculated CRC must be transmitted in ++ * order from highest-order term to lowest-order term. UARTs transmit ++ * characters in order from LSB to MSB. By storing the CRC this way ++ * we hand it to the UART in the order low-byte to high-byte; the UART ++ * sends each low-bit to hight-bit; and the result is transmission bit ++ * by bit from highest- to lowest-order term without requiring any bit ++ * shuffling on our part. Reception works similarly ++ * ++ * The feedback terms table consists of 256, 32-bit entries. Notes ++ * ++ * The table can be generated at runtime if desired; code to do so ++ * is shown later. It might not be obvious, but the feedback ++ * terms simply represent the results of eight shift/xor opera ++ * tions for all combinations of data and CRC register values ++ * ++ * The values must be right-shifted by eight bits by the "updcrc ++ * logic; the shift must be unsigned (bring in zeroes). On some ++ * hardware you could probably optimize the shift in assembler by ++ * using byte-swap instructions ++ * polynomial $edb88320 ++ */ ++ ++#include ++ ++const uint32_t crc32_table[256] = { ++ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, ++ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, ++ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, ++ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, ++ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, ++ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, ++ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, ++ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, ++ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, ++ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, ++ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, ++ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, ++ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, ++ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, ++ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, ++ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, ++ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, ++ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, ++ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, ++ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, ++ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, ++ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, ++ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, ++ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, ++ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, ++ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, ++ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, ++ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, ++ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, ++ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, ++ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, ++ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, ++ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, ++ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, ++ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, ++ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, ++ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, ++ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, ++ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, ++ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, ++ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, ++ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, ++ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, ++ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, ++ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, ++ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, ++ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, ++ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, ++ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, ++ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, ++ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, ++ 0x2d02ef8dL ++}; +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/crc32.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/crc32.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/crc32.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/crc32.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,19 @@ ++#ifndef CRC32_H ++#define CRC32_H ++ ++#include ++ ++extern const uint32_t crc32_table[256]; ++ ++/* Return a 32-bit CRC of the contents of the buffer. */ ++ ++ static inline uint32_t ++crc32(uint32_t val, const void *ss, int len) ++{ ++ const unsigned char *s = ss; ++ while (--len >= 0) ++ val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8); ++ return val; ++} ++ ++#endif +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/dictionary.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/dictionary.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/dictionary.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/dictionary.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,405 @@ ++/*-------------------------------------------------------------------------*/ ++/** ++ @file dictionary.c ++ @author N. Devillard ++ @date Sep 2007 ++ @version $Revision: 1.1.1.1 $ ++ @brief Implements a dictionary for string variables. ++ ++ This module implements a simple dictionary object, i.e. a list ++ of string/string associations. This object is useful to store e.g. ++ informations retrieved from a configuration file (ini files). ++*/ ++/*--------------------------------------------------------------------------*/ ++ ++/* ++ $Id: dictionary.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ $Revision: 1.1.1.1 $ ++*/ ++/*--------------------------------------------------------------------------- ++ Includes ++ ---------------------------------------------------------------------------*/ ++#include "dictionary.h" ++ ++#include ++#include ++#include ++#include ++ ++/** Maximum value size for integers and doubles. */ ++#define MAXVALSZ 1024 ++ ++/** Minimal allocated number of entries in a dictionary */ ++#define DICTMINSZ 128 ++ ++/** Invalid key token */ ++#define DICT_INVALID_KEY ((char*)-1) ++ ++/*--------------------------------------------------------------------------- ++ Private functions ++ ---------------------------------------------------------------------------*/ ++ ++/* Doubles the allocated size associated to a pointer */ ++/* 'size' is the current allocated size. */ ++static void * mem_double(void * ptr, int size) ++{ ++ void * newptr ; ++ ++ newptr = calloc(2*size, 1); ++ if (newptr==NULL) { ++ return NULL ; ++ } ++ memcpy(newptr, ptr, size); ++ free(ptr); ++ return newptr ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Duplicate a string ++ @param s String to duplicate ++ @return Pointer to a newly allocated string, to be freed with free() ++ ++ This is a replacement for strdup(). This implementation is provided ++ for systems that do not have it. ++ */ ++/*--------------------------------------------------------------------------*/ ++static char * xstrdup(char * s) ++{ ++ char * t ; ++ if (!s) ++ return NULL ; ++ t = malloc(strlen(s)+1) ; ++ if (t) { ++ strcpy(t,s); ++ } ++ return t ; ++} ++ ++/*--------------------------------------------------------------------------- ++ Function codes ++ ---------------------------------------------------------------------------*/ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Compute the hash key for a string. ++ @param key Character string to use for key. ++ @return 1 unsigned int on at least 32 bits. ++ ++ This hash function has been taken from an Article in Dr Dobbs Journal. ++ This is normally a collision-free function, distributing keys evenly. ++ The key is stored anyway in the struct so that collision can be avoided ++ by comparing the key itself in last resort. ++ */ ++/*--------------------------------------------------------------------------*/ ++unsigned dictionary_hash(char * key) ++{ ++ int len ; ++ unsigned hash ; ++ int i ; ++ ++ len = strlen(key); ++ for (hash=0, i=0 ; i>6) ; ++ } ++ hash += (hash <<3); ++ hash ^= (hash >>11); ++ hash += (hash <<15); ++ return hash ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Create a new dictionary object. ++ @param size Optional initial size of the dictionary. ++ @return 1 newly allocated dictionary objet. ++ ++ This function allocates a new dictionary object of given size and returns ++ it. If you do not know in advance (roughly) the number of entries in the ++ dictionary, give size=0. ++ */ ++/*--------------------------------------------------------------------------*/ ++dictionary * dictionary_new(int size) ++{ ++ dictionary * d ; ++ ++ /* If no size was specified, allocate space for DICTMINSZ */ ++ if (sizesize = size ; ++ d->val = (char **)calloc(size, sizeof(char*)); ++ d->key = (char **)calloc(size, sizeof(char*)); ++ d->hash = (unsigned int *)calloc(size, sizeof(unsigned)); ++ return d ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Delete a dictionary object ++ @param d dictionary object to deallocate. ++ @return void ++ ++ Deallocate a dictionary object and all memory associated to it. ++ */ ++/*--------------------------------------------------------------------------*/ ++void dictionary_del(dictionary * d) ++{ ++ int i ; ++ ++ if (d==NULL) return ; ++ for (i=0 ; isize ; i++) { ++ if (d->key[i]!=NULL) ++ free(d->key[i]); ++ if (d->val[i]!=NULL) ++ free(d->val[i]); ++ } ++ free(d->val); ++ free(d->key); ++ free(d->hash); ++ free(d); ++ return ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Get a value from a dictionary. ++ @param d dictionary object to search. ++ @param key Key to look for in the dictionary. ++ @param def Default value to return if key not found. ++ @return 1 pointer to internally allocated character string. ++ ++ This function locates a key in a dictionary and returns a pointer to its ++ value, or the passed 'def' pointer if no such key can be found in ++ dictionary. The returned character pointer points to data internal to the ++ dictionary object, you should not try to free it or modify it. ++ */ ++/*--------------------------------------------------------------------------*/ ++char * dictionary_get(dictionary * d, char * key, char * def) ++{ ++ unsigned hash ; ++ int i ; ++ ++ hash = dictionary_hash(key); ++ for (i=0 ; isize ; i++) { ++ if (d->key[i]==NULL) ++ continue ; ++ /* Compare hash */ ++ if (hash==d->hash[i]) { ++ /* Compare string, to avoid hash collisions */ ++ if (!strcmp(key, d->key[i])) { ++ return d->val[i] ; ++ } ++ } ++ } ++ return def ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Set a value in a dictionary. ++ @param d dictionary object to modify. ++ @param key Key to modify or add. ++ @param val Value to add. ++ @return int 0 if Ok, anything else otherwise ++ ++ If the given key is found in the dictionary, the associated value is ++ replaced by the provided one. If the key cannot be found in the ++ dictionary, it is added to it. ++ ++ It is Ok to provide a NULL value for val, but NULL values for the dictionary ++ or the key are considered as errors: the function will return immediately ++ in such a case. ++ ++ Notice that if you dictionary_set a variable to NULL, a call to ++ dictionary_get will return a NULL value: the variable will be found, and ++ its value (NULL) is returned. In other words, setting the variable ++ content to NULL is equivalent to deleting the variable from the ++ dictionary. It is not possible (in this implementation) to have a key in ++ the dictionary without value. ++ ++ This function returns non-zero in case of failure. ++ */ ++/*--------------------------------------------------------------------------*/ ++int dictionary_set(dictionary * d, char * key, char * val) ++{ ++ int i ; ++ unsigned hash ; ++ ++ if (d==NULL || key==NULL) return -1 ; ++ ++ /* Compute hash for this key */ ++ hash = dictionary_hash(key) ; ++ /* Find if value is already in dictionary */ ++ if (d->n>0) { ++ for (i=0 ; isize ; i++) { ++ if (d->key[i]==NULL) ++ continue ; ++ if (hash==d->hash[i]) { /* Same hash value */ ++ if (!strcmp(key, d->key[i])) { /* Same key */ ++ /* Found a value: modify and return */ ++ if (d->val[i]!=NULL) ++ free(d->val[i]); ++ d->val[i] = val ? xstrdup(val) : NULL ; ++ /* Value has been modified: return */ ++ return 0 ; ++ } ++ } ++ } ++ } ++ /* Add a new value */ ++ /* See if dictionary needs to grow */ ++ if (d->n==d->size) { ++ ++ /* Reached maximum size: reallocate dictionary */ ++ d->val = (char **)mem_double(d->val, d->size * sizeof(char*)) ; ++ d->key = (char **)mem_double(d->key, d->size * sizeof(char*)) ; ++ d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ; ++ if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) { ++ /* Cannot grow dictionary */ ++ return -1 ; ++ } ++ /* Double size */ ++ d->size *= 2 ; ++ } ++ ++ /* Insert key in the first empty slot */ ++ for (i=0 ; isize ; i++) { ++ if (d->key[i]==NULL) { ++ /* Add key here */ ++ break ; ++ } ++ } ++ /* Copy key */ ++ d->key[i] = xstrdup(key); ++ d->val[i] = val ? xstrdup(val) : NULL ; ++ d->hash[i] = hash; ++ d->n ++ ; ++ return 0 ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Delete a key in a dictionary ++ @param d dictionary object to modify. ++ @param key Key to remove. ++ @return void ++ ++ This function deletes a key in a dictionary. Nothing is done if the ++ key cannot be found. ++ */ ++/*--------------------------------------------------------------------------*/ ++void dictionary_unset(dictionary * d, char * key) ++{ ++ unsigned hash ; ++ int i ; ++ ++ if (key == NULL) { ++ return; ++ } ++ ++ hash = dictionary_hash(key); ++ for (i=0 ; isize ; i++) { ++ if (d->key[i]==NULL) ++ continue ; ++ /* Compare hash */ ++ if (hash==d->hash[i]) { ++ /* Compare string, to avoid hash collisions */ ++ if (!strcmp(key, d->key[i])) { ++ /* Found key */ ++ break ; ++ } ++ } ++ } ++ if (i>=d->size) ++ /* Key not found */ ++ return ; ++ ++ free(d->key[i]); ++ d->key[i] = NULL ; ++ if (d->val[i]!=NULL) { ++ free(d->val[i]); ++ d->val[i] = NULL ; ++ } ++ d->hash[i] = 0 ; ++ d->n -- ; ++ return ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Dump a dictionary to an opened file pointer. ++ @param d Dictionary to dump ++ @param f Opened file pointer. ++ @return void ++ ++ Dumps a dictionary onto an opened file pointer. Key pairs are printed out ++ as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as ++ output file pointers. ++ */ ++/*--------------------------------------------------------------------------*/ ++void dictionary_dump(dictionary * d, FILE * out) ++{ ++ int i ; ++ ++ if (d==NULL || out==NULL) return ; ++ if (d->n<1) { ++ fprintf(out, "empty dictionary\n"); ++ return ; ++ } ++ for (i=0 ; isize ; i++) { ++ if (d->key[i]) { ++ fprintf(out, "%20s\t[%s]\n", ++ d->key[i], ++ d->val[i] ? d->val[i] : "UNDEF"); ++ } ++ } ++ return ; ++} ++ ++ ++/* Test code */ ++#ifdef TESTDIC ++#define NVALS 20000 ++int main(int argc, char *argv[]) ++{ ++ dictionary * d ; ++ char * val ; ++ int i ; ++ char cval[90] ; ++ ++ /* Allocate dictionary */ ++ printf("allocating...\n"); ++ d = dictionary_new(0); ++ ++ /* Set values in dictionary */ ++ printf("setting %d values...\n", NVALS); ++ for (i=0 ; in != 0) { ++ printf("error deleting values\n"); ++ } ++ printf("deallocating...\n"); ++ dictionary_del(d); ++ return 0 ; ++} ++#endif ++/* vim: set ts=4 et sw=4 tw=75 */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/dictionary.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/dictionary.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/dictionary.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/dictionary.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,174 @@ ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @file dictionary.h ++ @author N. Devillard ++ @date Sep 2007 ++ @version $Revision: 1.1.1.1 $ ++ @brief Implements a dictionary for string variables. ++ ++ This module implements a simple dictionary object, i.e. a list ++ of string/string associations. This object is useful to store e.g. ++ informations retrieved from a configuration file (ini files). ++*/ ++/*--------------------------------------------------------------------------*/ ++ ++/* ++ $Id: dictionary.h,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ $Author: yrtan $ ++ $Date: 2008-05-13 07:15:32 $ ++ $Revision: 1.1.1.1 $ ++*/ ++ ++#ifndef _DICTIONARY_H_ ++#define _DICTIONARY_H_ ++ ++/*--------------------------------------------------------------------------- ++ Includes ++ ---------------------------------------------------------------------------*/ ++ ++#include ++#include ++#include ++#include ++ ++/*--------------------------------------------------------------------------- ++ New types ++ ---------------------------------------------------------------------------*/ ++ ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Dictionary object ++ ++ This object contains a list of string/string associations. Each ++ association is identified by a unique string key. Looking up values ++ in the dictionary is speeded up by the use of a (hopefully collision-free) ++ hash function. ++ */ ++/*-------------------------------------------------------------------------*/ ++typedef struct _dictionary_ { ++ int n ; /** Number of entries in dictionary */ ++ int size ; /** Storage size */ ++ char ** val ; /** List of string values */ ++ char ** key ; /** List of string keys */ ++ unsigned * hash ; /** List of hash values for keys */ ++} dictionary ; ++ ++ ++/*--------------------------------------------------------------------------- ++ Function prototypes ++ ---------------------------------------------------------------------------*/ ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Compute the hash key for a string. ++ @param key Character string to use for key. ++ @return 1 unsigned int on at least 32 bits. ++ ++ This hash function has been taken from an Article in Dr Dobbs Journal. ++ This is normally a collision-free function, distributing keys evenly. ++ The key is stored anyway in the struct so that collision can be avoided ++ by comparing the key itself in last resort. ++ */ ++/*--------------------------------------------------------------------------*/ ++unsigned dictionary_hash(char * key); ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Create a new dictionary object. ++ @param size Optional initial size of the dictionary. ++ @return 1 newly allocated dictionary objet. ++ ++ This function allocates a new dictionary object of given size and returns ++ it. If you do not know in advance (roughly) the number of entries in the ++ dictionary, give size=0. ++ */ ++/*--------------------------------------------------------------------------*/ ++dictionary * dictionary_new(int size); ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Delete a dictionary object ++ @param d dictionary object to deallocate. ++ @return void ++ ++ Deallocate a dictionary object and all memory associated to it. ++ */ ++/*--------------------------------------------------------------------------*/ ++void dictionary_del(dictionary * vd); ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Get a value from a dictionary. ++ @param d dictionary object to search. ++ @param key Key to look for in the dictionary. ++ @param def Default value to return if key not found. ++ @return 1 pointer to internally allocated character string. ++ ++ This function locates a key in a dictionary and returns a pointer to its ++ value, or the passed 'def' pointer if no such key can be found in ++ dictionary. The returned character pointer points to data internal to the ++ dictionary object, you should not try to free it or modify it. ++ */ ++/*--------------------------------------------------------------------------*/ ++char * dictionary_get(dictionary * d, char * key, char * def); ++ ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Set a value in a dictionary. ++ @param d dictionary object to modify. ++ @param key Key to modify or add. ++ @param val Value to add. ++ @return int 0 if Ok, anything else otherwise ++ ++ If the given key is found in the dictionary, the associated value is ++ replaced by the provided one. If the key cannot be found in the ++ dictionary, it is added to it. ++ ++ It is Ok to provide a NULL value for val, but NULL values for the dictionary ++ or the key are considered as errors: the function will return immediately ++ in such a case. ++ ++ Notice that if you dictionary_set a variable to NULL, a call to ++ dictionary_get will return a NULL value: the variable will be found, and ++ its value (NULL) is returned. In other words, setting the variable ++ content to NULL is equivalent to deleting the variable from the ++ dictionary. It is not possible (in this implementation) to have a key in ++ the dictionary without value. ++ ++ This function returns non-zero in case of failure. ++ */ ++/*--------------------------------------------------------------------------*/ ++int dictionary_set(dictionary * vd, char * key, char * val); ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Delete a key in a dictionary ++ @param d dictionary object to modify. ++ @param key Key to remove. ++ @return void ++ ++ This function deletes a key in a dictionary. Nothing is done if the ++ key cannot be found. ++ */ ++/*--------------------------------------------------------------------------*/ ++void dictionary_unset(dictionary * d, char * key); ++ ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Dump a dictionary to an opened file pointer. ++ @param d Dictionary to dump ++ @param f Opened file pointer. ++ @return void ++ ++ Dumps a dictionary onto an opened file pointer. Key pairs are printed out ++ as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as ++ output file pointers. ++ */ ++/*--------------------------------------------------------------------------*/ ++void dictionary_dump(dictionary * d, FILE * out); ++ ++#endif +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libiniparser.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libiniparser.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libiniparser.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libiniparser.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,646 @@ ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @file iniparser.c ++ @author N. Devillard ++ @date Sep 2007 ++ @version 3.0 ++ @brief Parser for ini files. ++*/ ++/*--------------------------------------------------------------------------*/ ++/* ++ $Id: libiniparser.c,v 1.1.1.1 2008-05-13 07:15:32 yrtan Exp $ ++ $Revision: 1.1.1.1 $ ++ $Date: 2008-05-13 07:15:32 $ ++*/ ++/*---------------------------- Includes ------------------------------------*/ ++#include ++#include ++ ++/*---------------------------- Defines -------------------------------------*/ ++#define ASCIILINESZ (1024) ++#define INI_INVALID_KEY ((char*)-1) ++ ++/*--------------------------------------------------------------------------- ++ Private to this module ++ ---------------------------------------------------------------------------*/ ++/** ++ * This enum stores the status for each parsed line (internal use only). ++ */ ++typedef enum _line_status_ { ++ LINE_UNPROCESSED, ++ LINE_ERROR, ++ LINE_EMPTY, ++ LINE_COMMENT, ++ LINE_SECTION, ++ LINE_VALUE ++} line_status ; ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Convert a string to lowercase. ++ @param s String to convert. ++ @return ptr to statically allocated string. ++ ++ This function returns a pointer to a statically allocated string ++ containing a lowercased version of the input string. Do not free ++ or modify the returned string! Since the returned string is statically ++ allocated, it will be modified at each function call (not re-entrant). ++ */ ++/*--------------------------------------------------------------------------*/ ++static char * strlwc(const char * s) ++{ ++ static char l[ASCIILINESZ+1]; ++ int i ; ++ ++ if (s==NULL) return NULL ; ++ memset(l, 0, ASCIILINESZ+1); ++ i=0 ; ++ while (s[i] && i l) { ++ if (!isspace((int)*(last-1))) ++ break ; ++ last -- ; ++ } ++ *last = (char)0; ++ return (char*)l ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Get number of sections in a dictionary ++ @param d Dictionary to examine ++ @return int Number of sections found in dictionary ++ ++ This function returns the number of sections found in a dictionary. ++ The test to recognize sections is done on the string stored in the ++ dictionary: a section name is given as "section" whereas a key is ++ stored as "section:key", thus the test looks for entries that do not ++ contain a colon. ++ ++ This clearly fails in the case a section name contains a colon, but ++ this should simply be avoided. ++ ++ This function returns -1 in case of error. ++ */ ++/*--------------------------------------------------------------------------*/ ++int iniparser_getnsec(dictionary * d) ++{ ++ int i ; ++ int nsec ; ++ ++ if (d==NULL) return -1 ; ++ nsec=0 ; ++ for (i=0 ; isize ; i++) { ++ if (d->key[i]==NULL) ++ continue ; ++ if (strchr(d->key[i], ':')==NULL) { ++ nsec ++ ; ++ } ++ } ++ return nsec ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Get name for section n in a dictionary. ++ @param d Dictionary to examine ++ @param n Section number (from 0 to nsec-1). ++ @return Pointer to char string ++ ++ This function locates the n-th section in a dictionary and returns ++ its name as a pointer to a string statically allocated inside the ++ dictionary. Do not free or modify the returned string! ++ ++ This function returns NULL in case of error. ++ */ ++/*--------------------------------------------------------------------------*/ ++char * iniparser_getsecname(dictionary * d, int n) ++{ ++ int i ; ++ int foundsec ; ++ ++ if (d==NULL || n<0) return NULL ; ++ foundsec=0 ; ++ for (i=0 ; isize ; i++) { ++ if (d->key[i]==NULL) ++ continue ; ++ if (strchr(d->key[i], ':')==NULL) { ++ foundsec++ ; ++ if (foundsec>n) ++ break ; ++ } ++ } ++ if (foundsec<=n) { ++ return NULL ; ++ } ++ return d->key[i] ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Dump a dictionary to an opened file pointer. ++ @param d Dictionary to dump. ++ @param f Opened file pointer to dump to. ++ @return void ++ ++ This function prints out the contents of a dictionary, one element by ++ line, onto the provided file pointer. It is OK to specify @c stderr ++ or @c stdout as output files. This function is meant for debugging ++ purposes mostly. ++ */ ++/*--------------------------------------------------------------------------*/ ++void iniparser_dump(dictionary * d, FILE * f) ++{ ++ int i ; ++ ++ if (d==NULL || f==NULL) return ; ++ for (i=0 ; isize ; i++) { ++ if (d->key[i]==NULL) ++ continue ; ++ if (d->val[i]!=NULL) { ++ fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); ++ } else { ++ fprintf(f, "[%s]=UNDEF\n", d->key[i]); ++ } ++ } ++ return ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Save a dictionary to a loadable ini file ++ @param d Dictionary to dump ++ @param f Opened file pointer to dump to ++ @return void ++ ++ This function dumps a given dictionary into a loadable ini file. ++ It is Ok to specify @c stderr or @c stdout as output files. ++ */ ++/*--------------------------------------------------------------------------*/ ++void iniparser_dump_ini(dictionary * d, FILE * f) ++{ ++ int i, j ; ++ char keym[ASCIILINESZ+1]; ++ int nsec ; ++ char * secname ; ++ int seclen ; ++ ++ if (d==NULL || f==NULL) return ; ++ ++ nsec = iniparser_getnsec(d); ++ if (nsec<1) { ++ /* No section in file: dump all keys as they are */ ++ for (i=0 ; isize ; i++) { ++ if (d->key[i]==NULL) ++ continue ; ++ fprintf(f, "%s = %s\n", d->key[i], d->val[i]); ++ } ++ return ; ++ } ++ for (i=0 ; isize ; j++) { ++ if (d->key[j]==NULL) ++ continue ; ++ if (!strncmp(d->key[j], keym, seclen+1)) { ++ fprintf(f, ++ "%-30s = %s\n", ++ d->key[j]+seclen+1, ++ d->val[j] ? d->val[j] : ""); ++ } ++ } ++ } ++ fprintf(f, "\n"); ++ return ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Get the string associated to a key ++ @param d Dictionary to search ++ @param key Key string to look for ++ @param def Default value to return if key not found. ++ @return pointer to statically allocated character string ++ ++ This function queries a dictionary for a key. A key as read from an ++ ini file is given as "section:key". If the key cannot be found, ++ the pointer passed as 'def' is returned. ++ The returned char pointer is pointing to a string allocated in ++ the dictionary, do not free or modify it. ++ */ ++/*--------------------------------------------------------------------------*/ ++char * iniparser_getstring(dictionary * d, const char * key, char * def) ++{ ++ char * lc_key ; ++ char * sval ; ++ ++ if (d==NULL || key==NULL) ++ return def ; ++ ++ lc_key = strlwc(key); ++ sval = dictionary_get(d, lc_key, def); ++ return sval ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Get the string associated to a key, convert to an int ++ @param d Dictionary to search ++ @param key Key string to look for ++ @param notfound Value to return in case of error ++ @return integer ++ ++ This function queries a dictionary for a key. A key as read from an ++ ini file is given as "section:key". If the key cannot be found, ++ the notfound value is returned. ++ ++ Supported values for integers include the usual C notation ++ so decimal, octal (starting with 0) and hexadecimal (starting with 0x) ++ are supported. Examples: ++ ++ "42" -> 42 ++ "042" -> 34 (octal -> decimal) ++ "0x42" -> 66 (hexa -> decimal) ++ ++ Warning: the conversion may overflow in various ways. Conversion is ++ totally outsourced to strtol(), see the associated man page for overflow ++ handling. ++ ++ Credits: Thanks to A. Becker for suggesting strtol() ++ */ ++/*--------------------------------------------------------------------------*/ ++int iniparser_getint(dictionary * d, const char * key, int notfound) ++{ ++ char * str ; ++ ++ str = iniparser_getstring(d, key, INI_INVALID_KEY); ++ if (str==INI_INVALID_KEY) return notfound ; ++ return (int)strtol(str, NULL, 0); ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Get the string associated to a key, convert to a double ++ @param d Dictionary to search ++ @param key Key string to look for ++ @param notfound Value to return in case of error ++ @return double ++ ++ This function queries a dictionary for a key. A key as read from an ++ ini file is given as "section:key". If the key cannot be found, ++ the notfound value is returned. ++ */ ++/*--------------------------------------------------------------------------*/ ++double iniparser_getdouble(dictionary * d, char * key, double notfound) ++{ ++ char * str ; ++ ++ str = iniparser_getstring(d, key, INI_INVALID_KEY); ++ if (str==INI_INVALID_KEY) return notfound ; ++ return atof(str); ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Get the string associated to a key, convert to a boolean ++ @param d Dictionary to search ++ @param key Key string to look for ++ @param notfound Value to return in case of error ++ @return integer ++ ++ This function queries a dictionary for a key. A key as read from an ++ ini file is given as "section:key". If the key cannot be found, ++ the notfound value is returned. ++ ++ A true boolean is found if one of the following is matched: ++ ++ - A string starting with 'y' ++ - A string starting with 'Y' ++ - A string starting with 't' ++ - A string starting with 'T' ++ - A string starting with '1' ++ ++ A false boolean is found if one of the following is matched: ++ ++ - A string starting with 'n' ++ - A string starting with 'N' ++ - A string starting with 'f' ++ - A string starting with 'F' ++ - A string starting with '0' ++ ++ The notfound value returned if no boolean is identified, does not ++ necessarily have to be 0 or 1. ++ */ ++/*--------------------------------------------------------------------------*/ ++int iniparser_getboolean(dictionary * d, const char * key, int notfound) ++{ ++ char * c ; ++ int ret ; ++ ++ c = iniparser_getstring(d, key, INI_INVALID_KEY); ++ if (c==INI_INVALID_KEY) return notfound ; ++ if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { ++ ret = 1 ; ++ } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { ++ ret = 0 ; ++ } else { ++ ret = notfound ; ++ } ++ return ret; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Finds out if a given entry exists in a dictionary ++ @param ini Dictionary to search ++ @param entry Name of the entry to look for ++ @return integer 1 if entry exists, 0 otherwise ++ ++ Finds out if a given entry exists in the dictionary. Since sections ++ are stored as keys with NULL associated values, this is the only way ++ of querying for the presence of sections in a dictionary. ++ */ ++/*--------------------------------------------------------------------------*/ ++int iniparser_find_entry( ++ dictionary * ini, ++ char * entry ++) ++{ ++ int found=0 ; ++ if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { ++ found = 1 ; ++ } ++ return found ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Set an entry in a dictionary. ++ @param ini Dictionary to modify. ++ @param entry Entry to modify (entry name) ++ @param val New value to associate to the entry. ++ @return int 0 if Ok, -1 otherwise. ++ ++ If the given entry can be found in the dictionary, it is modified to ++ contain the provided value. If it cannot be found, -1 is returned. ++ It is Ok to set val to NULL. ++ */ ++/*--------------------------------------------------------------------------*/ ++int iniparser_set(dictionary * ini, char * entry, char * val) ++{ ++ return dictionary_set(ini, strlwc(entry), val) ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Delete an entry in a dictionary ++ @param ini Dictionary to modify ++ @param entry Entry to delete (entry name) ++ @return void ++ ++ If the given entry can be found, it is deleted from the dictionary. ++ */ ++/*--------------------------------------------------------------------------*/ ++void iniparser_unset(dictionary * ini, char * entry) ++{ ++ dictionary_unset(ini, strlwc(entry)); ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Load a single line from an INI file ++ @param input_line Input line, may be concatenated multi-line input ++ @param section Output space to store section ++ @param key Output space to store key ++ @param value Output space to store value ++ @return line_status value ++ */ ++/*--------------------------------------------------------------------------*/ ++static line_status iniparser_line( ++ char * input_line, ++ char * section, ++ char * key, ++ char * value) ++{ ++ line_status sta ; ++ char line[ASCIILINESZ+1]; ++ int len ; ++ ++ strcpy(line, strstrip(input_line)); ++ len = (int)strlen(line); ++ ++ sta = LINE_UNPROCESSED ; ++ if (len<1) { ++ /* Empty line */ ++ sta = LINE_EMPTY ; ++ } else if (line[0]=='#') { ++ /* Comment line */ ++ sta = LINE_COMMENT ; ++ } else if (line[0]=='[' && line[len-1]==']') { ++ /* Section name */ ++ sscanf(line, "[%[^]]", section); ++ strcpy(section, strstrip(section)); ++ strcpy(section, strlwc(section)); ++ sta = LINE_SECTION ; ++ } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 ++ || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2 ++ || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) { ++ /* Usual key=value, with or without comments */ ++ strcpy(key, strstrip(key)); ++ strcpy(key, strlwc(key)); ++ strcpy(value, strstrip(value)); ++ /* ++ * sscanf cannot handle '' or "" as empty values ++ * this is done here ++ */ ++ if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { ++ value[0]=0 ; ++ } ++ sta = LINE_VALUE ; ++ } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 ++ || sscanf(line, "%[^=] %[=]", key, value) == 2) { ++ /* ++ * Special cases: ++ * key= ++ * key=; ++ * key=# ++ */ ++ strcpy(key, strstrip(key)); ++ strcpy(key, strlwc(key)); ++ value[0]=0 ; ++ sta = LINE_VALUE ; ++ } else { ++ /* Generate syntax error */ ++ sta = LINE_ERROR ; ++ } ++ return sta ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Parse an ini file and return an allocated dictionary object ++ @param ininame Name of the ini file to read. ++ @return Pointer to newly allocated dictionary ++ ++ This is the parser for ini files. This function is called, providing ++ the name of the file to be read. It returns a dictionary object that ++ should not be accessed directly, but through accessor functions ++ instead. ++ ++ The returned dictionary must be freed using iniparser_freedict(). ++ */ ++/*--------------------------------------------------------------------------*/ ++dictionary * iniparser_load(const char * ininame) ++{ ++ FILE * in ; ++ ++ char line [ASCIILINESZ+1] ; ++ char section [ASCIILINESZ+1] ; ++ char key [ASCIILINESZ+1] ; ++ char tmp [ASCIILINESZ+1] ; ++ char val [ASCIILINESZ+1] ; ++ ++ int last=0 ; ++ int len ; ++ int lineno=0 ; ++ int errs=0; ++ ++ dictionary * dict ; ++ ++ if ((in=fopen(ininame, "r"))==NULL) { ++ fprintf(stderr, "iniparser: cannot open %s\n", ininame); ++ return NULL ; ++ } ++ ++ dict = dictionary_new(0) ; ++ if (!dict) { ++ fclose(in); ++ return NULL ; ++ } ++ ++ memset(line, 0, ASCIILINESZ); ++ memset(section, 0, ASCIILINESZ); ++ memset(key, 0, ASCIILINESZ); ++ memset(val, 0, ASCIILINESZ); ++ last=0 ; ++ ++ while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { ++ lineno++ ; ++ len = (int)strlen(line)-1; ++ /* Safety check against buffer overflows */ ++ if (line[len]!='\n') { ++ fprintf(stderr, ++ "iniparser: input line too long in %s (%d)\n", ++ ininame, ++ lineno); ++ dictionary_del(dict); ++ fclose(in); ++ return NULL ; ++ } ++ /* Get rid of \n and spaces at end of line */ ++ while ((len>=0) && ++ ((line[len]=='\n') || (isspace(line[len])))) { ++ line[len]=0 ; ++ len-- ; ++ } ++ /* Detect multi-line */ ++ if (line[len]=='\\') { ++ /* Multi-line value */ ++ last=len ; ++ continue ; ++ } else { ++ last=0 ; ++ } ++ switch (iniparser_line(line, section, key, val)) { ++ case LINE_EMPTY: ++ case LINE_COMMENT: ++ break ; ++ ++ case LINE_SECTION: ++ errs = dictionary_set(dict, section, NULL); ++ break ; ++ ++ case LINE_VALUE: ++ sprintf(tmp, "%s:%s", section, key); ++ errs = dictionary_set(dict, tmp, val) ; ++ break ; ++ ++ case LINE_ERROR: ++ fprintf(stderr, "iniparser: syntax error in %s (%d):\n", ++ ininame, ++ lineno); ++ fprintf(stderr, "-> %s\n", line); ++ errs++ ; ++ break; ++ ++ default: ++ break ; ++ } ++ memset(line, 0, ASCIILINESZ); ++ last=0; ++ if (errs<0) { ++ fprintf(stderr, "iniparser: memory allocation failure\n"); ++ break ; ++ } ++ } ++ if (errs) { ++ dictionary_del(dict); ++ dict = NULL ; ++ } ++ fclose(in); ++ return dict ; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/** ++ @brief Free all memory associated to an ini dictionary ++ @param d Dictionary to free ++ @return void ++ ++ Free all memory associated to an ini dictionary. ++ It is mandatory to call this function before the dictionary object ++ gets out of the current context. ++ */ ++/*--------------------------------------------------------------------------*/ ++void iniparser_freedict(dictionary * d) ++{ ++ dictionary_del(d); ++} ++ ++/* vim: set ts=4 et sw=4 tw=75 */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libmtd.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libmtd.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libmtd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libmtd.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,314 @@ ++/* ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem Bityutskiy ++ * ++ * MTD library. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include "common.h" ++ ++#define PROGRAM_NAME "libmtd" ++#define MTD_DEV_MAJOR 90 ++ ++/** ++ * mtd_get_info - get information about an MTD device. ++ * @node: name of the MTD device node ++ * @mtd: the MTD device information is returned here ++ * ++ * This function gets information about MTD device defined by the @node device ++ * node file and saves this information in the @mtd object. Returns %0 in case ++ * of success and %-1 in case of failure. ++ */ ++int mtd_get_info(const char *node, struct mtd_info *mtd) ++{ ++ struct stat st; ++ struct mtd_info_user ui; ++ int ret; ++ loff_t offs = 0; ++ ++ if (stat(node, &st)) ++ return sys_errmsg("cannot open \"%s\"", node); ++ ++ if (!S_ISCHR(st.st_mode)) { ++ errno = EINVAL; ++ return errmsg("\"%s\" is not a character device", node); ++ } ++ ++ mtd->major = major(st.st_rdev); ++ mtd->minor = minor(st.st_rdev); ++ ++ if (mtd->major != MTD_DEV_MAJOR) { ++ errno = EINVAL; ++ return errmsg("\"%s\" has major number %d, MTD devices have " ++ "major %d", node, mtd->major, MTD_DEV_MAJOR); ++ } ++ ++ mtd->num = mtd->minor / 2; ++ mtd->rdonly = mtd->minor & 1; ++ ++ mtd->fd = open(node, O_RDWR); ++ if (mtd->fd == -1) ++ return sys_errmsg("cannot open \"%s\"", node); ++ ++ if (ioctl(mtd->fd, MEMGETINFO, &ui)) { ++ sys_errmsg("MEMGETINFO ioctl request failed"); ++ goto out_close; ++ } ++ ++ ret = ioctl(mtd->fd, MEMGETBADBLOCK, &offs); ++ if (ret == -1) { ++ if (errno != EOPNOTSUPP) { ++ sys_errmsg("MEMGETBADBLOCK ioctl failed"); ++ goto out_close; ++ } ++ errno = 0; ++ mtd->allows_bb = 0; ++ } else ++ mtd->allows_bb = 1; ++ ++ mtd->type = ui.type; ++ mtd->size = ui.size; ++ mtd->eb_size = ui.erasesize; ++ mtd->min_io_size = ui.writesize; ++ ++ if (mtd->min_io_size <= 0) { ++ errmsg("mtd%d (%s) has insane min. I/O unit size %d", ++ mtd->num, node, mtd->min_io_size); ++ goto out_close; ++ } ++ if (mtd->eb_size <= 0 || mtd->eb_size < mtd->min_io_size) { ++ errmsg("mtd%d (%s) has insane eraseblock size %d", ++ mtd->num, node, mtd->eb_size); ++ goto out_close; ++ } ++ if (mtd->size <= 0 || mtd->size < mtd->eb_size) { ++ errmsg("mtd%d (%s) has insane size %lld", ++ mtd->num, node, mtd->size); ++ goto out_close; ++ } ++ mtd->eb_cnt = mtd->size / mtd->eb_size; ++ ++ switch(mtd->type) { ++ case MTD_ABSENT: ++ errmsg("mtd%d (%s) is removable and is not present", ++ mtd->num, node); ++ goto out_close; ++ case MTD_RAM: ++ mtd->type_str = "RAM-based"; ++ break; ++ case MTD_ROM: ++ mtd->type_str = "ROM"; ++ break; ++ case MTD_NORFLASH: ++ mtd->type_str = "NOR"; ++ break; ++ case MTD_NANDFLASH: ++ mtd->type_str = "NAND"; ++ break; ++ case MTD_DATAFLASH: ++ mtd->type_str = "DataFlash"; ++ break; ++ case MTD_UBIVOLUME: ++ mtd->type_str = "UBI-emulated MTD"; ++ break; ++ default: ++ mtd->type_str = "Unknown flash type"; ++ break; ++ } ++ ++ if (!(ui.flags & MTD_WRITEABLE)) ++ mtd->rdonly = 1; ++ ++ return 0; ++ ++out_close: ++ close(mtd->fd); ++ return -1; ++} ++ ++/** ++ * mtd_erase - erase an eraseblock. ++ * @mtd: MTD device description object ++ * @eb: eraseblock to erase ++ * ++ * This function erases the eraseblock and returns %0 in case of success and ++ * %-1 in case of failure. ++ */ ++int mtd_erase(const struct mtd_info *mtd, int eb) ++{ ++ struct erase_info_user ei; ++ ++ ei.start = eb * mtd->eb_size;; ++ ei.length = mtd->eb_size; ++ return ioctl(mtd->fd, MEMERASE, &ei); ++} ++ ++/** ++ * mtd_is_bad - check if eraseblock is bad. ++ * @mtd: MTD device description object ++ * @eb: eraseblock to check ++ * ++ * This function checks if eraseblock @eb is bad. Returns %0 if not, %1 if yes, ++ * and %-1 in case of failure. ++ */ ++int mtd_is_bad(const struct mtd_info *mtd, int eb) ++{ ++ int ret; ++ loff_t seek; ++ ++ if (eb < 0 || eb >= mtd->eb_cnt) { ++ errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks", ++ eb, mtd->num, mtd->eb_cnt); ++ errno = EINVAL; ++ return -1; ++ } ++ ++ if (!mtd->allows_bb) ++ return 0; ++ ++ seek = eb * mtd->eb_size; ++ ret = ioctl(mtd->fd, MEMGETBADBLOCK, &seek); ++ if (ret == -1) { ++ sys_errmsg("MEMGETBADBLOCK ioctl failed for " ++ "eraseblock %d (mtd%d)", eb, mtd->num); ++ return -1; ++ } ++ ++ return ret; ++} ++ ++/** ++ * mtd_read - read data from an MTD device. ++ * @mtd: MTD device description object ++ * @eb: eraseblock to read from ++ * @offs: offset withing the eraseblock to read from ++ * @buf: buffer to read data to ++ * @len: how many bytes to read ++ * ++ * This function reads @len bytes of data from eraseblock @eb and offset @offs ++ * of the MTD device defined by @mtd and stores the read data at buffer @buf. ++ * Returns %0 in case of success and %-1 in case of failure. ++ */ ++int mtd_read(const struct mtd_info *mtd, int eb, int offs, void *buf, int len) ++{ ++ int ret, rd = 0; ++ off_t seek; ++ ++ if (eb < 0 || eb >= mtd->eb_cnt) { ++ errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks", ++ eb, mtd->num, mtd->eb_cnt); ++ errno = EINVAL; ++ return -1; ++ } ++ if (offs < 0 || offs + len > mtd->eb_size) { ++ errmsg("bad offset %d or length %d, mtd%d eraseblock size is %d", ++ offs, len, mtd->num, mtd->eb_size); ++ errno = EINVAL; ++ return -1; ++ } ++ ++ /* Seek to the beginning of the eraseblock */ ++ seek = eb * mtd->eb_size + offs; ++ if (lseek(mtd->fd, seek, SEEK_SET) != seek) { ++ sys_errmsg("cannot seek mtd%d to offset %llu", ++ mtd->num, (unsigned long long)seek); ++ return -1; ++ } ++ ++ while (rd < len) { ++ ret = read(mtd->fd, buf, len); ++ if (ret < 0) { ++ sys_errmsg("cannot read %d bytes from mtd%d (eraseblock %d, offset %d)", ++ len, mtd->num, eb, offs); ++ return -1; ++ } ++ rd += ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * mtd_write - write data to an MTD device. ++ * @mtd: MTD device description object ++ * @eb: eraseblock to write to ++ * @offs: offset withing the eraseblock to write to ++ * @buf: buffer to write ++ * @len: how many bytes to write ++ * ++ * This function writes @len bytes of data to eraseblock @eb and offset @offs ++ * of the MTD device defined by @mtd. Returns %0 in case of success and %-1 in ++ * case of failure. ++ */ ++int mtd_write(const struct mtd_info *mtd, int eb, int offs, void *buf, int len) ++{ ++ int ret; ++ off_t seek; ++ ++ if (eb < 0 || eb >= mtd->eb_cnt) { ++ errmsg("bad eraseblock number %d, mtd%d has %d eraseblocks", ++ eb, mtd->num, mtd->eb_cnt); ++ errno = EINVAL; ++ return -1; ++ } ++ if (offs < 0 || offs + len > mtd->eb_size) { ++ errmsg("bad offset %d or length %d, mtd%d eraseblock size is %d", ++ offs, len, mtd->num, mtd->eb_size); ++ errno = EINVAL; ++ return -1; ++ } ++#if 0 ++ if (offs % mtd->subpage_size) { ++ errmsg("write offset %d is not aligned to mtd%d min. I/O size %d", ++ offs, mtd->num, mtd->subpage_size); ++ errno = EINVAL; ++ return -1; ++ } ++ if (len % mtd->subpage_size) { ++ errmsg("write length %d is not aligned to mtd%d min. I/O size %d", ++ len, mtd->num, mtd->subpage_size); ++ errno = EINVAL; ++ return -1; ++ } ++#endif ++ ++ /* Seek to the beginning of the eraseblock */ ++ seek = eb * mtd->eb_size + offs; ++ if (lseek(mtd->fd, seek, SEEK_SET) != seek) { ++ sys_errmsg("cannot seek mtd%d to offset %llu", ++ mtd->num, (unsigned long long)seek); ++ return -1; ++ } ++ ++ ret = write(mtd->fd, buf, len); ++ if (ret != len) { ++ sys_errmsg("cannot write %d bytes to mtd%d (eraseblock %d, offset %d)", ++ len, mtd->num, eb, offs); ++ return -1; ++ } ++ ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libscan.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libscan.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libscan.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libscan.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,225 @@ ++/* ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem Bityutskiy ++ * ++ * UBI scanning library. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include "common.h" ++#include "crc32.h" ++ ++#define PROGRAM_NAME "libscan" ++ ++static int all_ff(const void *buf, int len) ++{ ++ int i; ++ const uint8_t *p = buf; ++ ++ for (i = 0; i < len; i++) ++ if (p[i] != 0xFF) ++ return 0; ++ return 1; ++} ++ ++int ubi_scan(struct mtd_info *mtd, struct ubi_scan_info **info, int verbose) ++{ ++ int eb, v = (verbose == 2), pr = (verbose == 1); ++ struct ubi_scan_info *si; ++ unsigned long long sum = 0; ++ ++ si = calloc(1, sizeof(struct ubi_scan_info)); ++ if (!si) ++ return sys_errmsg("cannot allocate %zd bytes of memory", ++ sizeof(struct ubi_scan_info)); ++ ++ si->ec = calloc(mtd->eb_cnt, sizeof(uint32_t)); ++ if (!si->ec) { ++ sys_errmsg("cannot allocate %zd bytes of memory", ++ sizeof(struct ubi_scan_info)); ++ goto out_si; ++ } ++ ++ si->vid_hdr_offs = si->data_offs = -1; ++ ++ verbose(v, "start scanning eraseblocks 0-%d", mtd->eb_cnt); ++ for (eb = 0; eb < mtd->eb_cnt; eb++) { ++ int ret; ++ uint32_t crc; ++ struct ubi_ec_hdr hdr; ++ unsigned long long ec; ++ ++ if (v) { ++ normsg_cont("scanning eraseblock %d", eb); ++ fflush(stdout); ++ } ++ if (pr) { ++ printf("\r" PROGRAM_NAME ": scanning eraseblock %d -- %2lld %% complete ", ++ eb, (long long)(eb + 1) * 100 / mtd->eb_cnt); ++ fflush(stdout); ++ } ++ ++ ret = mtd_is_bad(mtd, eb); ++ if (ret == -1) ++ goto out_ec; ++ if (ret) { ++ si->bad_cnt += 1; ++ si->ec[eb] = EB_BAD; ++ if (v) ++ printf(": bad\n"); ++ continue; ++ } ++ ++ ret = mtd_read(mtd, eb, 0, &hdr, sizeof(struct ubi_ec_hdr));; ++ if (ret < 0) ++ goto out_ec; ++ ++ /* Check the EC header */ ++ if (be32_to_cpu(hdr.magic) != UBI_EC_HDR_MAGIC) { ++ if (all_ff(&hdr, sizeof(struct ubi_ec_hdr))) { ++ si->empty_cnt += 1; ++ si->ec[eb] = EB_EMPTY; ++ if (v) ++ printf(": empty\n"); ++ } else { ++ si->alien_cnt += 1; ++ si->ec[eb] = EB_ALIEN; ++ if (v) ++ printf(": alien\n"); ++ } ++ continue; ++ } ++ ++ crc = crc32(UBI_CRC32_INIT, &hdr, UBI_EC_HDR_SIZE_CRC); ++ if (be32_to_cpu(hdr.hdr_crc) != crc) { ++ si->corrupted_cnt += 1; ++ si->ec[eb] = EB_CORRUPTED; ++ if (v) ++ printf(": bad CRC %#08x, should be %#08x\n", ++ crc, be32_to_cpu(hdr.hdr_crc)); ++ continue; ++ } ++ ++ ec = be64_to_cpu(hdr.ec); ++ if (ec > EC_MAX) { ++ if (pr) ++ printf("\n"); ++ errmsg("erase counter in EB %d is %llu, while this " ++ "program expects them to be less than %u", ++ eb, ec, EC_MAX); ++ goto out_ec; ++ } ++ ++ if (si->vid_hdr_offs == -1) { ++ si->vid_hdr_offs = be32_to_cpu(hdr.vid_hdr_offset); ++ si->data_offs = be32_to_cpu(hdr.data_offset); ++ if (si->data_offs % mtd->min_io_size) { ++ if (pr) ++ printf("\n"); ++ if (v) ++ printf(": corrupted because of the below\n"); ++ warnmsg("bad data offset %d at eraseblock %d (n" ++ "of multiple of min. I/O unit size %d)", ++ si->data_offs, eb, mtd->min_io_size); ++ warnmsg("treat eraseblock %d as corrupted", eb); ++ si->corrupted_cnt += 1; ++ si->ec[eb] = EB_CORRUPTED; ++ continue; ++ ++ } ++ } else { ++ if (be32_to_cpu(hdr.vid_hdr_offset) != si->vid_hdr_offs) { ++ if (pr) ++ printf("\n"); ++ if (v) ++ printf(": corrupted because of the below\n"); ++ warnmsg("inconsistent VID header offset: was " ++ "%d, but is %d in eraseblock %d", ++ si->vid_hdr_offs, ++ be32_to_cpu(hdr.vid_hdr_offset), eb); ++ warnmsg("treat eraseblock %d as corrupted", eb); ++ si->corrupted_cnt += 1; ++ si->ec[eb] = EB_CORRUPTED; ++ continue; ++ } ++ if (be32_to_cpu(hdr.data_offset) != si->data_offs) { ++ if (pr) ++ printf("\n"); ++ if (v) ++ printf(": corrupted because of the below\n"); ++ warnmsg("inconsistent data offset: was %d, but" ++ " is %d in eraseblock %d", ++ si->data_offs, ++ be32_to_cpu(hdr.data_offset), eb); ++ warnmsg("treat eraseblock %d as corrupted", eb); ++ si->corrupted_cnt += 1; ++ si->ec[eb] = EB_CORRUPTED; ++ continue; ++ } ++ } ++ ++ si->ok_cnt += 1; ++ si->ec[eb] = ec; ++ if (v) ++ printf(": OK, erase counter %u\n", si->ec[eb]); ++ } ++ ++ if (si->ok_cnt != 0) { ++ /* Calculate mean erase counter */ ++ for (eb = 0; eb < mtd->eb_cnt; eb++) { ++ if (si->ec[eb] > EC_MAX) ++ continue; ++ sum += si->ec[eb]; ++ } ++ si->mean_ec = sum / si->ok_cnt; ++ } ++ ++ si->good_cnt = mtd->eb_cnt - si->bad_cnt; ++ verbose(v, "finished, mean EC %lld, %d OK, %d corrupted, %d empty, %d " ++ "alien, bad %d", si->mean_ec, si->ok_cnt, si->corrupted_cnt, ++ si->empty_cnt, si->alien_cnt, si->bad_cnt); ++ ++ *info = si; ++ if (pr) ++ printf("\n"); ++ return 0; ++ ++out_ec: ++ free(si->ec); ++out_si: ++ free(si); ++ *info = NULL; ++ return -1; ++} ++ ++void ubi_scan_free(struct ubi_scan_info *si) ++{ ++ free(si->ec); ++ free(si); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubi.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubi.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubi.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubi.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1154 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem Bityutskiy ++ * ++ * UBI (Unsorted Block Images) library. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "libubi.h" ++#include "libubi_int.h" ++#include "common.h" ++ ++#define PROGRAM_NAME "libubi" ++ ++/** ++ * mkpath - compose full path from 2 given components. ++ * @path: the first component ++ * @name: the second component ++ * ++ * This function returns the resulting path in case of success and %NULL in ++ * case of failure. ++ */ ++static char *mkpath(const char *path, const char *name) ++{ ++ char *n; ++ int len1 = strlen(path); ++ int len2 = strlen(name); ++ ++ n = malloc(len1 + len2 + 2); ++ if (!n) { ++ sys_errmsg("cannot allocate %d bytes", len1 + len2 + 2); ++ return NULL; ++ } ++ ++ memcpy(n, path, len1); ++ if (n[len1 - 1] != '/') ++ n[len1++] = '/'; ++ ++ memcpy(n + len1, name, len2 + 1); ++ return n; ++} ++ ++/** ++ * read_positive_ll - read a positive 'long long' value from a file. ++ * @file: the file to read from ++ * @value: the result is stored here ++ * ++ * This function reads file @file and interprets its contents as a positive ++ * 'long long' integer. If this is not true, it fails with %EINVAL error code. ++ * Returns %0 in case of success and %-1 in case of failure. ++ */ ++static int read_positive_ll(const char *file, long long *value) ++{ ++ int fd, rd; ++ char buf[50]; ++ ++ fd = open(file, O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ rd = read(fd, buf, 50); ++ if (rd == -1) { ++ sys_errmsg("cannot read \"%s\"", file); ++ goto out_error; ++ } ++ if (rd == 50) { ++ errmsg("contents of \"%s\" is too long", file); ++ errno = EINVAL; ++ goto out_error; ++ } ++ ++ if (sscanf(buf, "%lld\n", value) != 1) { ++ /* This must be a UBI bug */ ++ errmsg("cannot read integer from \"%s\"\n", file); ++ errno = EINVAL; ++ goto out_error; ++ } ++ ++ if (*value < 0) { ++ errmsg("negative value %lld in \"%s\"", *value, file); ++ errno = EINVAL; ++ goto out_error; ++ } ++ ++ if (close(fd)) ++ return sys_errmsg("close failed on \"%s\"", file); ++ ++ return 0; ++ ++out_error: ++ close(fd); ++ return -1; ++} ++ ++/** ++ * read_positive_int - read a positive 'int' value from a file. ++ * @file: the file to read from ++ * @value: the result is stored here ++ * ++ * This function is the same as 'read_positive_ll()', but it reads an 'int' ++ * value, not 'long long'. ++ */ ++static int read_positive_int(const char *file, int *value) ++{ ++ long long res; ++ ++ if (read_positive_ll(file, &res)) ++ return -1; ++ ++ /* Make sure the value is not too big */ ++ if (res > INT_MAX) { ++ errmsg("value %lld read from file \"%s\" is out of range", ++ res, file); ++ errno = EINVAL; ++ return -1; ++ } ++ ++ *value = res; ++ return 0; ++} ++ ++/** ++ * read_data - read data from a file. ++ * @file: the file to read from ++ * @buf: the buffer to read to ++ * @buf_len: buffer length ++ * ++ * This function returns number of read bytes in case of success and %-1 in ++ * case of failure. Note, if the file contains more then @buf_len bytes of ++ * date, this function fails with %EINVAL error code. ++ */ ++static int read_data(const char *file, void *buf, int buf_len) ++{ ++ int fd, rd, tmp, tmp1; ++ ++ fd = open(file, O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ rd = read(fd, buf, buf_len); ++ if (rd == -1) { ++ sys_errmsg("cannot read \"%s\"", file); ++ goto out_error; ++ } ++ ++ /* Make sure all data is read */ ++ tmp1 = read(fd, &tmp, 1); ++ if (tmp1 == 1) { ++ sys_errmsg("cannot read \"%s\"", file); ++ goto out_error; ++ } ++ if (tmp1) { ++ errmsg("file \"%s\" contains too much data (> %d bytes)", ++ file, buf_len); ++ errno = EINVAL; ++ goto out_error; ++ } ++ ++ if (close(fd)) { ++ sys_errmsg("close failed on \"%s\"", file); ++ return -1; ++ } ++ ++ return rd; ++ ++out_error: ++ close(fd); ++ return -1; ++} ++ ++/** ++ * read_major - read major and minor numbers from a file. ++ * @file: name of the file to read from ++ * @major: major number is returned here ++ * @minor: minor number is returned here ++ * ++ * This function returns % in case of succes, and %-1 in case of failure. ++ */ ++static int read_major(const char *file, int *major, int *minor) ++{ ++ int ret; ++ char buf[50]; ++ ++ ret = read_data(file, buf, 50); ++ if (ret < 0) ++ return ret; ++ ++ ret = sscanf(buf, "%d:%d\n", major, minor); ++ if (ret != 2) { ++ errno = EINVAL; ++ return errmsg("\"%s\" does not have major:minor format", file); ++ } ++ ++ if (*major < 0 || *minor < 0) { ++ errno = EINVAL; ++ return errmsg("bad major:minor %d:%d in \"%s\"", ++ *major, *minor, file); ++ } ++ ++ return 0; ++} ++ ++/** ++ * dev_read_int - read a positive 'int' value from an UBI device sysfs file. ++ * @patt: file pattern to read from ++ * @dev_num: UBI device number ++ * @value: the result is stored here ++ * ++ * This function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int dev_read_int(const char *patt, int dev_num, int *value) ++{ ++ char file[strlen(patt) + 50]; ++ ++ sprintf(file, patt, dev_num); ++ return read_positive_int(file, value); ++} ++ ++/** ++ * vol_read_int - read a positive 'int' value from an UBI volume sysfs file. ++ * @patt: file pattern to read from ++ * @dev_num: UBI device number ++ * @vol_id: volume ID ++ * @value: the result is stored here ++ * ++ * This function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value) ++{ ++ char file[strlen(patt) + 100]; ++ ++ sprintf(file, patt, dev_num, vol_id); ++ return read_positive_int(file, value); ++} ++ ++/** ++ * dev_read_ll - read a positive 'long long' value from an UBI device sysfs file. ++ * @patt: file pattern to read from ++ * @dev_num: UBI device number ++ * @value: the result is stored here ++ * ++ * This function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int dev_read_ll(const char *patt, int dev_num, long long *value) ++{ ++ char file[strlen(patt) + 50]; ++ ++ sprintf(file, patt, dev_num); ++ return read_positive_ll(file, value); ++} ++ ++/** ++ * vol_read_ll - read a positive 'long long' value from an UBI volume sysfs file. ++ * @patt: file pattern to read from ++ * @dev_num: UBI device number ++ * @vol_id: volume ID ++ * @value: the result is stored here ++ * ++ * This function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int vol_read_ll(const char *patt, int dev_num, int vol_id, ++ long long *value) ++{ ++ char file[strlen(patt) + 100]; ++ ++ sprintf(file, patt, dev_num, vol_id); ++ return read_positive_ll(file, value); ++} ++ ++/** ++ * vol_read_data - read data from an UBI volume's sysfs file. ++ * @patt: file pattern to read from ++ * @dev_num: UBI device number ++ * @vol_id: volume ID ++ * @buf: buffer to read to ++ * @buf_len: buffer length ++ * ++ * This function returns number of read bytes in case of success and %-1 in ++ * case of failure. ++ */ ++static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf, ++ int buf_len) ++{ ++ char file[strlen(patt) + 100]; ++ ++ sprintf(file, patt, dev_num, vol_id); ++ return read_data(file, buf, buf_len); ++} ++ ++/** ++ * dev_get_major - get major and minor numbers of an UBI device. ++ * @lib: libubi descriptor ++ * @dev_num: UBI device number ++ * @major: major number is returned here ++ * @minor: minor number is returned here ++ * ++ * This function returns zero in case of succes and %-1 in case of failure. ++ */ ++static int dev_get_major(struct libubi *lib, int dev_num, int *major, int *minor) ++{ ++ char file[strlen(lib->dev_dev) + 50]; ++ ++ sprintf(file, lib->dev_dev, dev_num); ++ return read_major(file, major, minor); ++} ++ ++/** ++ * vol_get_major - get major and minor numbers of an UBI volume. ++ * @lib: libubi descriptor ++ * @dev_num: UBI device number ++ * @vol_id: volume ID ++ * @major: major number is returned here ++ * @minor: minor number is returned here ++ * ++ * This function returns zero in case of succes and %-1 in case of failure. ++ */ ++static int vol_get_major(struct libubi *lib, int dev_num, int vol_id, ++ int *major, int *minor) ++{ ++ char file[strlen(lib->vol_dev) + 100]; ++ ++ sprintf(file, lib->vol_dev, dev_num, vol_id); ++ return read_major(file, major, minor); ++} ++ ++/** ++ * vol_node2nums - find UBI device number and volume ID by volume device node ++ * file. ++ * @lib: UBI library descriptor ++ * @node: UBI character device node name ++ * @dev_num: UBI device number is returned here ++ * @vol_id: volume ID is returned hers ++ * ++ * This function returns zero in case of succes and %-1 in case of failure. ++ */ ++static int vol_node2nums(struct libubi *lib, const char *node, int *dev_num, ++ int *vol_id) ++{ ++ struct stat st; ++ struct ubi_info info; ++ int i, fd, major, minor; ++ char file[strlen(lib->ubi_vol) + 100]; ++ ++ if (lstat(node, &st)) ++ return -1; ++ ++ if (!S_ISCHR(st.st_mode)) { ++ errno = EINVAL; ++ return errmsg("\"%s\" is not a character device", node); ++ } ++ ++ major = major(st.st_rdev); ++ minor = minor(st.st_rdev); ++ ++ if (minor == 0) { ++ errno = EINVAL; ++ return errmsg("\"%s\" is not a volume character device", node); ++ } ++ ++ if (ubi_get_info((libubi_t *)lib, &info)) ++ return -1; ++ ++ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { ++ int major1, minor1, ret; ++ ++ ret = dev_get_major(lib, i, &major1, &minor1); ++ if (ret) { ++ if (errno == ENOENT) ++ continue; ++ return -1; ++ } ++ ++ if (major1 == major) ++ break; ++ } ++ ++ if (i > info.highest_dev_num) { ++ errno = ENODEV; ++ return -1; ++ } ++ ++ /* Make sure this UBI volume exists */ ++ sprintf(file, lib->ubi_vol, i, minor - 1); ++ fd = open(file, O_RDONLY); ++ if (fd == -1) { ++ errno = ENODEV; ++ return -1; ++ } ++ ++ *dev_num = i; ++ *vol_id = minor - 1; ++ errno = 0; ++ return 0; ++} ++ ++/** ++ * dev_node2num - find UBI device number by its character device node. ++ * @lib: UBI library descriptor ++ * @node: UBI character device node name ++ * ++ * This function returns positive UBI device number in case of success and %-1 ++ * in case of failure. ++ */ ++static int dev_node2num(struct libubi *lib, const char *node, int *dev_num) ++{ ++ struct stat stat; ++ struct ubi_info info; ++ int i, major, minor; ++ ++ if (lstat(node, &stat)) ++ return -1; ++ ++ if (!S_ISCHR(stat.st_mode)) { ++ errno = EINVAL; ++ return errmsg("\"%s\" is not a character device", node); ++ } ++ ++ major = major(stat.st_rdev); ++ minor = minor(stat.st_rdev); ++ ++ if (minor != 0) { ++ errno = EINVAL; ++ return errmsg("\"%s\" is not an UBI character device", node); ++ } ++ ++ if (ubi_get_info((libubi_t *)lib, &info)) ++ return -1; ++ ++ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { ++ int major1, minor1, ret; ++ ++ ret = dev_get_major(lib, i, &major1, &minor1); ++ if (ret) { ++ if (errno == ENOENT) ++ continue; ++ return -1; ++ } ++ ++ if (major1 == major) { ++ if (minor1 != 0) { ++ errmsg("UBI character device minor number is " ++ "%d, but must be 0", minor1); ++ errno = EINVAL; ++ return -1; ++ } ++ errno = 0; ++ *dev_num = i; ++ return 0; ++ } ++ } ++ ++ errno = ENODEV; ++ return -1; ++} ++ ++int mtd_num2ubi_dev(libubi_t desc, int mtd_num, int *dev_num) ++{ ++ struct ubi_info info; ++ int i, ret, mtd_num1; ++ struct libubi *lib = desc; ++ ++ if (ubi_get_info(desc, &info)) ++ return -1; ++ ++ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { ++ ret = dev_read_int(lib->dev_mtd_num, i, &mtd_num1); ++ if (ret) { ++ if (errno == ENOENT) ++ continue; ++ return -1; ++ } ++ ++ if (mtd_num1 == mtd_num) { ++ errno = 0; ++ *dev_num = i; ++ return 0; ++ } ++ } ++ ++ errno = 0; ++ return -1; ++} ++ ++libubi_t libubi_open(int required) ++{ ++ int fd, version; ++ struct libubi *lib; ++ ++ lib = calloc(1, sizeof(struct libubi)); ++ if (!lib) ++ return NULL; ++ ++ /* TODO: this must be discovered instead */ ++ lib->sysfs = strdup("/sys"); ++ if (!lib->sysfs) ++ goto out_error; ++ ++ lib->sysfs_ctrl = mkpath(lib->sysfs, SYSFS_CTRL); ++ if (!lib->sysfs_ctrl) ++ goto out_error; ++ ++ lib->ctrl_dev = mkpath(lib->sysfs_ctrl, CTRL_DEV); ++ if (!lib->ctrl_dev) ++ goto out_error; ++ ++ lib->sysfs_ubi = mkpath(lib->sysfs, SYSFS_UBI); ++ if (!lib->sysfs_ubi) ++ goto out_error; ++ ++ /* Make sure UBI is present */ ++ fd = open(lib->sysfs_ubi, O_RDONLY); ++ if (fd == -1) { ++ if (required) ++ errmsg("cannot open \"%s\", UBI does not seem to " ++ "exist in system", lib->sysfs_ubi); ++ goto out_error; ++ } ++ ++ if (close(fd)) { ++ sys_errmsg("close failed on \"%s\"", lib->sysfs_ubi); ++ goto out_error; ++ } ++ ++ lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT); ++ if (!lib->ubi_dev) ++ goto out_error; ++ ++ lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER); ++ if (!lib->ubi_version) ++ goto out_error; ++ ++ lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV); ++ if (!lib->dev_dev) ++ goto out_error; ++ ++ lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS); ++ if (!lib->dev_avail_ebs) ++ goto out_error; ++ ++ lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS); ++ if (!lib->dev_total_ebs) ++ goto out_error; ++ ++ lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT); ++ if (!lib->dev_bad_count) ++ goto out_error; ++ ++ lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE); ++ if (!lib->dev_eb_size) ++ goto out_error; ++ ++ lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC); ++ if (!lib->dev_max_ec) ++ goto out_error; ++ ++ lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD); ++ if (!lib->dev_bad_rsvd) ++ goto out_error; ++ ++ lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS); ++ if (!lib->dev_max_vols) ++ goto out_error; ++ ++ lib->dev_min_io_size = mkpath(lib->ubi_dev, DEV_MIN_IO_SIZE); ++ if (!lib->dev_min_io_size) ++ goto out_error; ++ ++ lib->dev_mtd_num = mkpath(lib->ubi_dev, DEV_MTD_NUM); ++ if (!lib->dev_mtd_num) ++ goto out_error; ++ ++ lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT); ++ if (!lib->ubi_vol) ++ goto out_error; ++ ++ lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE); ++ if (!lib->vol_type) ++ goto out_error; ++ ++ lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV); ++ if (!lib->vol_dev) ++ goto out_error; ++ ++ lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT); ++ if (!lib->vol_alignment) ++ goto out_error; ++ ++ lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES); ++ if (!lib->vol_data_bytes) ++ goto out_error; ++ ++ lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS); ++ if (!lib->vol_rsvd_ebs) ++ goto out_error; ++ ++ lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE); ++ if (!lib->vol_eb_size) ++ goto out_error; ++ ++ lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED); ++ if (!lib->vol_corrupted) ++ goto out_error; ++ ++ lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME); ++ if (!lib->vol_name) ++ goto out_error; ++ ++ if (read_positive_int(lib->ubi_version, &version)) ++ goto out_error; ++ if (version != LIBUBI_UBI_VERSION) { ++ errmsg("this library was made for UBI version %d, but UBI " ++ "version %d is detected\n", LIBUBI_UBI_VERSION, version); ++ goto out_error; ++ } ++ ++ return lib; ++ ++out_error: ++ libubi_close((libubi_t)lib); ++ return NULL; ++} ++ ++void libubi_close(libubi_t desc) ++{ ++ struct libubi *lib = (struct libubi *)desc; ++ ++ free(lib->vol_name); ++ free(lib->vol_corrupted); ++ free(lib->vol_eb_size); ++ free(lib->vol_rsvd_ebs); ++ free(lib->vol_data_bytes); ++ free(lib->vol_alignment); ++ free(lib->vol_dev); ++ free(lib->vol_type); ++ free(lib->ubi_vol); ++ free(lib->dev_mtd_num); ++ free(lib->dev_min_io_size); ++ free(lib->dev_max_vols); ++ free(lib->dev_bad_rsvd); ++ free(lib->dev_max_ec); ++ free(lib->dev_eb_size); ++ free(lib->dev_bad_count); ++ free(lib->dev_total_ebs); ++ free(lib->dev_avail_ebs); ++ free(lib->dev_dev); ++ free(lib->ubi_version); ++ free(lib->ubi_dev); ++ free(lib->sysfs_ubi); ++ free(lib->ctrl_dev); ++ free(lib->sysfs_ctrl); ++ free(lib->sysfs); ++ free(lib); ++} ++ ++int ubi_attach_mtd(libubi_t desc, const char *node, ++ struct ubi_attach_request *req) ++{ ++ int fd, ret; ++ struct ubi_attach_req r; ++ ++ memset(&r, sizeof(struct ubi_attach_req), '\0'); ++ ++ desc = desc; ++ r.ubi_num = req->dev_num; ++ r.mtd_num = req->mtd_num; ++ r.vid_hdr_offset = req->vid_hdr_offset; ++ ++ fd = open(node, O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ ret = ioctl(fd, UBI_IOCATT, &r); ++ close(fd); ++ if (ret == -1) ++ return -1; ++ ++ req->dev_num = r.ubi_num; ++ ++#ifdef UDEV_SETTLE_HACK ++ if (system("udevsettle") == -1) ++ return -1; ++ if (system("udevsettle") == -1) ++ return -1; ++#endif ++ ++ return ret; ++} ++ ++int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num) ++{ ++ int ret, ubi_dev; ++ ++ ret = mtd_num2ubi_dev(desc, mtd_num, &ubi_dev); ++ if (ret == -1) { ++ errno = ENODEV; ++ return ret; ++ } ++ ++ return ubi_remove_dev(desc, node, ubi_dev); ++} ++ ++int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev) ++{ ++ int fd, ret; ++ ++ desc = desc; ++ ++ fd = open(node, O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ret = ioctl(fd, UBI_IOCDET, &ubi_dev); ++ if (ret == -1) ++ goto out_close; ++ ++#ifdef UDEV_SETTLE_HACK ++ if (system("udevsettle") == -1) ++ return -1; ++#endif ++ ++out_close: ++ close(fd); ++ return ret; ++} ++ ++int ubi_node_type(libubi_t desc, const char *node) ++{ ++ struct stat st; ++ struct ubi_info info; ++ int i, fd, major, minor; ++ struct libubi *lib = (struct libubi *)desc; ++ char file[strlen(lib->ubi_vol) + 100]; ++ ++ if (lstat(node, &st)) ++ return -1; ++ ++ if (!S_ISCHR(st.st_mode)) { ++ errno = EINVAL; ++ return -1; ++ } ++ ++ major = major(st.st_rdev); ++ minor = minor(st.st_rdev); ++ ++ if (ubi_get_info((libubi_t *)lib, &info)) ++ return -1; ++ ++ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { ++ int major1, minor1, ret; ++ ++ ret = dev_get_major(lib, i, &major1, &minor1); ++ if (ret) { ++ if (errno == ENOENT) ++ continue; ++ return -1; ++ } ++ ++ if (major1 == major) ++ break; ++ } ++ ++ if (i > info.highest_dev_num) { ++ /* ++ * The character device node does not correspond to any ++ * existing UBI device or volume, but we do not want to return ++ * any error number in this case, to indicate the fact that it ++ * could be a UBI device/volume, but it doesn't. ++ */ ++ errno = 0; ++ return -1; ++ } ++ ++ if (minor == 0) ++ return 1; ++ ++ /* This is supposdely an UBI volume device node */ ++ sprintf(file, lib->ubi_vol, i, minor - 1); ++ fd = open(file, O_RDONLY); ++ if (fd == -1) { ++ errno = 0; ++ return -1; ++ } ++ ++ return 2; ++} ++ ++int ubi_get_info(libubi_t desc, struct ubi_info *info) ++{ ++ DIR *sysfs_ubi; ++ struct dirent *dirent; ++ struct libubi *lib = (struct libubi *)desc; ++ ++ memset(info, '\0', sizeof(struct ubi_info)); ++ ++ if (read_major(lib->ctrl_dev, &info->ctrl_major, &info->ctrl_minor)) { ++ /* ++ * Older UBI versions did not have control device, so we do not ++ * panic here for compatibility reasons. May be few years later ++ * we could return -1 here, but for now just set major:minor to ++ * -1. ++ */ ++ info->ctrl_major = info->ctrl_minor = -1; ++ } ++ ++ /* ++ * We have to scan the UBI sysfs directory to identify how many UBI ++ * devices are present. ++ */ ++ sysfs_ubi = opendir(lib->sysfs_ubi); ++ if (!sysfs_ubi) ++ return sys_errmsg("cannot open %s", lib->sysfs_ubi); ++ ++ info->lowest_dev_num = INT_MAX; ++ while (1) { ++ int dev_num, ret; ++ char tmp_buf[256]; ++ ++ errno = 0; ++ dirent = readdir(sysfs_ubi); ++ if (!dirent) ++ break; ++ ++ if (strlen(dirent->d_name) > 256) { ++ errmsg("invalid entry in %s: \"%s\"", ++ lib->sysfs_ubi, dirent->d_name); ++ goto out_close; ++ } ++ ++ ret = sscanf(dirent->d_name, UBI_DEV_NAME_PATT"%s", ++ &dev_num, tmp_buf); ++ if (ret == 1) { ++ info->dev_count += 1; ++ if (dev_num > info->highest_dev_num) ++ info->highest_dev_num = dev_num; ++ if (dev_num < info->lowest_dev_num) ++ info->lowest_dev_num = dev_num; ++ } ++ } ++ ++ if (!dirent && errno) { ++ sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); ++ goto out_close; ++ } ++ ++ if (closedir(sysfs_ubi)) ++ return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); ++ ++ if (info->lowest_dev_num == INT_MAX) ++ info->lowest_dev_num = 0; ++ ++ if (read_positive_int(lib->ubi_version, &info->version)) ++ return -1; ++ ++ return 0; ++ ++out_close: ++ closedir(sysfs_ubi); ++ return -1; ++} ++ ++int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req) ++{ ++ int fd, ret; ++ struct ubi_mkvol_req r; ++ size_t n; ++ ++ memset(&r, sizeof(struct ubi_mkvol_req), '\0'); ++ ++ desc = desc; ++ r.vol_id = req->vol_id; ++ r.alignment = req->alignment; ++ r.bytes = req->bytes; ++ r.vol_type = req->vol_type; ++ ++ n = strlen(req->name); ++ if (n > UBI_MAX_VOLUME_NAME) ++ return -1; ++ ++ strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1); ++ r.name_len = n; ++ ++ fd = open(node, O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ ret = ioctl(fd, UBI_IOCMKVOL, &r); ++ if (ret == -1) ++ goto out_close; ++ ++ req->vol_id = r.vol_id; ++ ++#ifdef UDEV_SETTLE_HACK ++ if (system("udevsettle") == -1) ++ return -1; ++#endif ++ ++out_close: ++ close(fd); ++ return ret; ++} ++ ++int ubi_rmvol(libubi_t desc, const char *node, int vol_id) ++{ ++ int fd, ret; ++ ++ desc = desc; ++ fd = open(node, O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ ret = ioctl(fd, UBI_IOCRMVOL, &vol_id); ++ if (ret == -1) ++ goto out_close; ++ ++#ifdef UDEV_SETTLE_HACK ++ if (system("udevsettle") == -1) ++ return -1; ++#endif ++ ++out_close: ++ close(fd); ++ return ret; ++} ++ ++int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes) ++{ ++ int fd, ret; ++ struct ubi_rsvol_req req; ++ ++ desc = desc; ++ fd = open(node, O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ req.bytes = bytes; ++ req.vol_id = vol_id; ++ ++ ret = ioctl(fd, UBI_IOCRSVOL, &req); ++ close(fd); ++ return ret; ++} ++ ++int ubi_update_start(libubi_t desc, int fd, long long bytes) ++{ ++ desc = desc; ++ if (ioctl(fd, UBI_IOCVOLUP, &bytes)) ++ return -1; ++ return 0; ++} ++ ++int ubi_leb_read_start(int fd, struct ubi_leb *leb) ++{ ++ return ioctl(fd, UBI_IOCLEBREAD, leb); ++} ++ ++int ubi_leb_change_start(libubi_t desc, int fd, int lnum, int bytes, int dtype) ++{ ++ struct ubi_leb_change_req req; ++ ++ desc = desc; ++ memset(&req, 0, sizeof(struct ubi_leb_change_req)); ++ req.lnum = lnum; ++ req.bytes = bytes; ++ req.dtype = dtype; ++ ++ if (ioctl(fd, UBI_IOCEBCH, &req)) ++ return -1; ++ return 0; ++} ++ ++int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) ++{ ++ DIR *sysfs_ubi; ++ struct dirent *dirent; ++ struct libubi *lib = (struct libubi *)desc; ++ ++ memset(info, '\0', sizeof(struct ubi_dev_info)); ++ info->dev_num = dev_num; ++ ++ sysfs_ubi = opendir(lib->sysfs_ubi); ++ if (!sysfs_ubi) ++ return -1; ++ ++ info->lowest_vol_num = INT_MAX; ++ ++ while (1) { ++ int vol_id, ret, devno; ++ char tmp_buf[256]; ++ ++ errno = 0; ++ dirent = readdir(sysfs_ubi); ++ if (!dirent) ++ break; ++ ++ if (strlen(dirent->d_name) > 256) { ++ errmsg("invalid entry in %s: \"%s\"", ++ lib->sysfs_ubi, dirent->d_name); ++ goto out_close; ++ } ++ ++ ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT"%s", &devno, &vol_id, tmp_buf); ++ if (ret == 2 && devno == dev_num) { ++ info->vol_count += 1; ++ if (vol_id > info->highest_vol_num) ++ info->highest_vol_num = vol_id; ++ if (vol_id < info->lowest_vol_num) ++ info->lowest_vol_num = vol_id; ++ } ++ } ++ ++ if (!dirent && errno) { ++ sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); ++ goto out_close; ++ } ++ ++ if (closedir(sysfs_ubi)) ++ return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); ++ ++ if (info->lowest_vol_num == INT_MAX) ++ info->lowest_vol_num = 0; ++ ++ if (dev_get_major(lib, dev_num, &info->major, &info->minor)) ++ return -1; ++ ++ if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_lebs)) ++ return -1; ++ if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_lebs)) ++ return -1; ++ if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count)) ++ return -1; ++ if (dev_read_int(lib->dev_eb_size, dev_num, &info->leb_size)) ++ return -1; ++ if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd)) ++ return -1; ++ if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec)) ++ return -1; ++ if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count)) ++ return -1; ++ if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size)) ++ return -1; ++ ++ info->avail_bytes = info->avail_lebs * info->leb_size; ++ info->total_bytes = info->total_lebs * info->leb_size; ++ ++ return 0; ++ ++out_close: ++ closedir(sysfs_ubi); ++ return -1; ++} ++ ++int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info) ++{ ++ int dev_num; ++ struct libubi *lib = (struct libubi *)desc; ++ ++ if (dev_node2num(lib, node, &dev_num)) ++ return -1; ++ ++ return ubi_get_dev_info1(desc, dev_num, info); ++} ++ ++int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, ++ struct ubi_vol_info *info) ++{ ++ int ret; ++ struct libubi *lib = (struct libubi *)desc; ++ char buf[50]; ++ ++ memset(info, '\0', sizeof(struct ubi_vol_info)); ++ info->dev_num = dev_num; ++ info->vol_id = vol_id; ++ ++ if (dev_get_major(lib, dev_num, &info->dev_major, &info->dev_minor)) ++ return -1; ++ if (vol_get_major(lib, dev_num, vol_id, &info->major, &info->minor)) ++ return -1; ++ ++ ret = vol_read_data(lib->vol_type, dev_num, vol_id, buf, 50); ++ if (ret < 0) ++ return -1; ++ ++ if (strncmp(buf, "static\n", ret) == 0) ++ info->type = UBI_STATIC_VOLUME; ++ else if (strncmp(buf, "dynamic\n", ret) == 0) ++ info->type = UBI_DYNAMIC_VOLUME; ++ else { ++ errmsg("bad value at \"%s\"", buf); ++ errno = EINVAL; ++ return -1; ++ } ++ ++ ret = vol_read_int(lib->vol_alignment, dev_num, vol_id, ++ &info->alignment); ++ if (ret) ++ return -1; ++ ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id, ++ &info->data_bytes); ++ if (ret) ++ return -1; ++ ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_lebs); ++ if (ret) ++ return -1; ++ ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->leb_size); ++ if (ret) ++ return -1; ++ ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id, ++ &info->corrupted); ++ if (ret) ++ return -1; ++ info->rsvd_bytes = info->leb_size * info->rsvd_lebs; ++ ++ ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name, ++ UBI_VOL_NAME_MAX + 2); ++ if (ret < 0) ++ return -1; ++ ++ info->name[ret - 1] = '\0'; ++ return 0; ++} ++ ++int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info) ++{ ++ int vol_id, dev_num; ++ struct libubi *lib = (struct libubi *)desc; ++ ++ if (vol_node2nums(lib, node, &dev_num, &vol_id)) ++ return -1; ++ ++ return ubi_get_vol_info1(desc, dev_num, vol_id, info); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubi_int.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubi_int.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubi_int.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubi_int.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,133 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem Bityutskiy ++ * ++ * UBI (Unsorted Block Images) library. ++ */ ++ ++#ifndef __LIBUBI_INT_H__ ++#define __LIBUBI_INT_H__ ++ ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* ++ * The below are pre-define UBI file and directory names. ++ * ++ * Note, older kernels put 'ubiX_Y' directories straight to '/sys/class/ubi/'. ++ * New kernels puts 'ubiX_Y' directories to '/sys/class/ubi/ubiX/', which is ++ * saner. And for compatibility reasons it also puts symlinks to 'ubiX_Y' ++ * directories to '/sys/class/ubi/'. For now libubi assumes old layout. ++ */ ++ ++#define SYSFS_UBI "class/ubi" ++#define SYSFS_CTRL "class/misc/ubi_ctrl/" ++ ++#define CTRL_DEV "dev" ++ ++#define UBI_VER "version" ++#define UBI_DEV_NAME_PATT "ubi%d" ++ ++#define DEV_DEV "dev" ++#define DEV_AVAIL_EBS "avail_eraseblocks" ++#define DEV_TOTAL_EBS "total_eraseblocks" ++#define DEV_BAD_COUNT "bad_peb_count" ++#define DEV_EB_SIZE "eraseblock_size" ++#define DEV_MAX_EC "max_ec" ++#define DEV_MAX_RSVD "reserved_for_bad" ++#define DEV_MAX_VOLS "max_vol_count" ++#define DEV_MIN_IO_SIZE "min_io_size" ++#define DEV_MTD_NUM "mtd_num" ++ ++#define UBI_VOL_NAME_PATT "ubi%d_%d" ++#define VOL_TYPE "type" ++#define VOL_DEV "dev" ++#define VOL_ALIGNMENT "alignment" ++#define VOL_DATA_BYTES "data_bytes" ++#define VOL_RSVD_EBS "reserved_ebs" ++#define VOL_EB_SIZE "usable_eb_size" ++#define VOL_CORRUPTED "corrupted" ++#define VOL_NAME "name" ++ ++/** ++ * libubi - UBI library description data structure. ++ * @sysfs: sysfs file system path ++ * @sysfs_ctrl: UBI control device directory in sysfs ++ * @ctrl_dev: UBI control device major/minor numbers sysfs file ++ * @sysfs_ubi: UBI directory in sysfs ++ * @ubi_dev: UBI device sysfs directory pattern ++ * @ubi_version: UBI version file sysfs path ++ * @dev_dev: UBI device major/minor numbers file pattern ++ * @dev_avail_ebs: count of available eraseblocks sysfs path pattern ++ * @dev_total_ebs: total eraseblocks count sysfs path pattern ++ * @dev_bad_count: count of bad eraseblocks sysfs path pattern ++ * @dev_eb_size: size of UBI device's eraseblocks sysfs path pattern ++ * @dev_max_ec: maximum erase counter sysfs path pattern ++ * @dev_bad_rsvd: count of physical eraseblock reserved for bad eraseblocks ++ * handling ++ * @dev_max_vols: maximum volumes number count sysfs path pattern ++ * @dev_min_io_size: minimum I/O unit size sysfs path pattern ++ * @ubi_vol: UBI volume sysfs directory pattern ++ * @vol_type: volume type sysfs path pattern ++ * @vol_dev: volume major/minor numbers file pattern ++ * @vol_alignment: volume alignment sysfs path pattern ++ * @vol_data_bytes: volume data size sysfs path pattern ++ * @vol_rsvd_ebs: volume reserved size sysfs path pattern ++ * @vol_eb_size: volume eraseblock size sysfs path pattern ++ * @vol_corrupted: volume corruption flag sysfs path pattern ++ * @vol_name: volume name sysfs path pattern ++ */ ++struct libubi ++{ ++ char *sysfs; ++ char *sysfs_ctrl; ++ char *ctrl_dev; ++ char *sysfs_ubi; ++ char *ubi_dev; ++ char *ubi_version; ++ char *dev_dev; ++ char *dev_avail_ebs; ++ char *dev_total_ebs; ++ char *dev_bad_count; ++ char *dev_eb_size; ++ char *dev_max_ec; ++ char *dev_bad_rsvd; ++ char *dev_max_vols; ++ char *dev_min_io_size; ++ char *dev_mtd_num; ++ char *ubi_vol; ++ char *vol_type; ++ char *vol_dev; ++ char *vol_alignment; ++ char *vol_data_bytes; ++ char *vol_rsvd_ebs; ++ char *vol_eb_size; ++ char *vol_corrupted; ++ char *vol_name; ++ char *vol_max_count; ++}; ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* !__LIBUBI_INT_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubigen.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubigen.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubigen.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/libubigen.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,335 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * Generating UBI images. ++ * ++ * Authors: Oliver Lohmann ++ * Artem Bityutskiy ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include "crc32.h" ++#include "common.h" ++ ++#define PROGRAM_NAME "libubigen" ++ ++/** ++ * ubigen_info_init - initialize libubigen. ++ * @ui: libubigen information ++ * @peb_size: flash physical eraseblock size ++ * @min_io_size: flash minimum input/output unit size ++ * @subpage_size: flash sub-page, if present (has to be equivalent to ++ * @min_io_size if does not exist) ++ * @vid_hdr_offs: offset of the VID header ++ * @ubi_ver: UBI version ++ */ ++void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size, ++ int subpage_size, int vid_hdr_offs, int ubi_ver) ++{ ++ if (!vid_hdr_offs) { ++ vid_hdr_offs = UBI_EC_HDR_SIZE + subpage_size - 1; ++ vid_hdr_offs /= subpage_size; ++ vid_hdr_offs *= subpage_size; ++ } ++ ++ ui->peb_size = peb_size; ++ ui->min_io_size = min_io_size; ++ ui->vid_hdr_offs = vid_hdr_offs; ++ ui->data_offs = vid_hdr_offs + UBI_VID_HDR_SIZE + min_io_size - 1; ++ ui->data_offs /= min_io_size; ++ ui->data_offs *= min_io_size; ++ ui->leb_size = peb_size - ui->data_offs; ++ ui->ubi_ver = ubi_ver; ++ ++ ui->max_volumes = ui->leb_size / UBI_VTBL_RECORD_SIZE; ++ if (ui->max_volumes > UBI_MAX_VOLUMES) ++ ui->max_volumes = UBI_MAX_VOLUMES; ++ ui->vtbl_size = ui->max_volumes * UBI_VTBL_RECORD_SIZE; ++} ++ ++/** ++ * ubigen_create_empty_vtbl - creates empty volume table. ++ * ++ * This function creates an empty volume table and returns a pointer to it in ++ * case of success and %NULL in case of failure. The returned object has to be ++ * freed with 'free()' call. ++ */ ++struct ubi_vtbl_record *ubigen_create_empty_vtbl(const struct ubigen_info *ui) ++{ ++ struct ubi_vtbl_record *vtbl; ++ int i; ++ ++ vtbl = calloc(1, ui->vtbl_size); ++ if (!vtbl) { ++ sys_errmsg("cannot allocate %d bytes of memory", ui->vtbl_size); ++ return NULL; ++ } ++ ++ for (i = 0; i < ui->max_volumes; i++) { ++ uint32_t crc = crc32(UBI_CRC32_INIT, &vtbl[i], ++ UBI_VTBL_RECORD_SIZE_CRC); ++ vtbl[i].crc = cpu_to_be32(crc); ++ } ++ ++ return vtbl; ++} ++ ++/** ++ * ubigen_add_volume - add a volume to the volume table. ++ * @ui: libubigen information ++ * @vi: volume information ++ * @vtbl: volume table to add to ++ * ++ * This function adds volume described by input parameters to the volume table ++ * @vtbl. ++ */ ++int ubigen_add_volume(const struct ubigen_info *ui, ++ const struct ubigen_vol_info *vi, ++ struct ubi_vtbl_record *vtbl) ++{ ++ struct ubi_vtbl_record *vtbl_rec = &vtbl[vi->id]; ++ uint32_t tmp; ++ ++ if (vi->id >= ui->max_volumes) ++ return errmsg("too high volume id %d, max. volumes is %d", ++ vi->id, ui->max_volumes); ++ ++ if (vi->alignment >= ui->leb_size) ++ return errmsg("too large alignment %d, max is %d (LEB size)", ++ vi->alignment, ui->leb_size); ++ ++ memset(vtbl_rec, '\0', sizeof(struct ubi_vtbl_record)); ++ tmp = (vi->bytes + ui->leb_size - 1) / ui->leb_size; ++ vtbl_rec->reserved_pebs = cpu_to_be32(tmp); ++ vtbl_rec->alignment = cpu_to_be32(vi->alignment); ++ vtbl_rec->vol_type = vi->type; ++ tmp = ui->leb_size % vi->alignment; ++ vtbl_rec->data_pad = cpu_to_be32(tmp); ++ vtbl_rec->flags = vi->flags; ++ ++ memcpy(vtbl_rec->name, vi->name, vi->name_len); ++ vtbl_rec->name[vi->name_len] = '\0'; ++ vtbl_rec->name_len = cpu_to_be16(vi->name_len); ++ ++ tmp = crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC); ++ vtbl_rec->crc = cpu_to_be32(tmp); ++ return 0; ++} ++ ++/** ++ * ubigen_init_ec_hdr - initialize EC header. ++ * @ui: libubigen information ++ * @hdr: the EC header to initialize ++ * @ec: erase counter value ++ */ ++void ubigen_init_ec_hdr(const struct ubigen_info *ui, ++ struct ubi_ec_hdr *hdr, long long ec) ++{ ++ uint32_t crc; ++ ++ memset(hdr, '\0', sizeof(struct ubi_ec_hdr)); ++ ++ hdr->magic = cpu_to_be32(UBI_EC_HDR_MAGIC); ++ hdr->version = ui->ubi_ver; ++ hdr->ec = cpu_to_be64(ec); ++ hdr->vid_hdr_offset = cpu_to_be32(ui->vid_hdr_offs); ++ ++ hdr->data_offset = cpu_to_be32(ui->data_offs); ++ ++ crc = crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC); ++ hdr->hdr_crc = cpu_to_be32(crc); ++} ++ ++/** ++ * init_vid_hdr - initialize VID header. ++ * @ui: libubigen information ++ * @vi: volume information ++ * @hdr: the VID header to initialize ++ * @lnum: logical eraseblock number ++ * @data: the contents of the LEB (static volumes only) ++ * @data_size: amount of data in this LEB (static volumes only) ++ * ++ * Note, @used_ebs, @data and @data_size are ignored in case of dynamic ++ * volumes. ++ */ ++static void init_vid_hdr(const struct ubigen_info *ui, ++ const struct ubigen_vol_info *vi, ++ struct ubi_vid_hdr *hdr, int lnum, ++ const void *data, int data_size) ++{ ++ uint32_t crc; ++ ++ memset(hdr, '\0', sizeof(struct ubi_vid_hdr)); ++ ++ hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC); ++ hdr->version = ui->ubi_ver; ++ hdr->vol_type = vi->type; ++ hdr->vol_id = cpu_to_be32(vi->id); ++ hdr->lnum = cpu_to_be32(lnum); ++ hdr->data_pad = cpu_to_be32(vi->data_pad); ++ hdr->compat = vi->compat; ++ ++ if (vi->type == UBI_VID_STATIC) { ++ hdr->data_size = cpu_to_be32(data_size); ++ hdr->used_ebs = cpu_to_be32(vi->used_ebs); ++ crc = crc32(UBI_CRC32_INIT, data, data_size); ++ hdr->data_crc = cpu_to_be32(crc); ++ } ++ ++ crc = crc32(UBI_CRC32_INIT, hdr, UBI_VID_HDR_SIZE_CRC); ++ hdr->hdr_crc = cpu_to_be32(crc); ++} ++ ++/** ++ * ubigen_write_volume - write UBI volume. ++ * @ui: libubigen information ++ * @vi: volume information ++ * @ec: erase coutner value to put to EC headers ++ * @bytes: volume size in bytes ++ * @in: input file descriptor (has to be properly seeked) ++ * @out: output file descriptor ++ * ++ * This function reads the contents of the volume from the input file @in and ++ * writes the UBI volume to the output file @out. Returns zero on success and ++ * %-1 on failure. ++ */ ++int ubigen_write_volume(const struct ubigen_info *ui, ++ const struct ubigen_vol_info *vi, long long ec, ++ long long bytes, int in, int out) ++{ ++ int len = vi->usable_leb_size, rd, lnum = 0; ++ char inbuf[ui->leb_size+sizeof(unsigned int)], outbuf[ui->peb_size]; ++ ++ if (vi->id >= ui->max_volumes) ++ return errmsg("too high volume id %d, max. volumes is %d", ++ vi->id, ui->max_volumes); ++ ++ if (vi->alignment >= ui->leb_size) ++ return errmsg("too large alignment %d, max is %d (LEB size)", ++ vi->alignment, ui->leb_size); ++ ++ memset(outbuf, 0xFF, ui->data_offs); ++ ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec); ++ ++ while (bytes) { ++ int l; ++ struct ubi_vid_hdr *vid_hdr; ++ ++ if (bytes < len) ++ len = bytes; ++ bytes -= len; ++ ++ l = len; ++ do { ++ rd = read(in, inbuf + len - l, sizeof(unsigned int)); ++ if (rd != sizeof(unsigned int)) ++ return sys_errmsg("cannot read leb lnum from the input file"); ++ ++ rd = read(in, inbuf + len - l + sizeof(unsigned int), l); ++ if (rd != l) ++ return sys_errmsg("cannot read %d bytes from the input file", l); ++ ++ l -= rd; ++ } while (l); ++ ++ vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]); ++ lnum = ((unsigned int *)inbuf)[0]; ++ init_vid_hdr(ui, vi, vid_hdr, lnum, inbuf+sizeof(unsigned int), len); ++ ++ memcpy(outbuf + ui->data_offs, inbuf+sizeof(unsigned int), len); ++ memset(outbuf + ui->data_offs + len, 0xFF, ++ ui->peb_size - ui->data_offs - len); ++ ++ if (write(out, outbuf, ui->peb_size) != ui->peb_size) ++ return sys_errmsg("cannot write %d bytes to the output file", ui->peb_size); ++ ++// lnum += 1; ++ } ++ ++ return 0; ++} ++ ++/** ++ * ubigen_write_layout_vol - write UBI layout volume ++ * @ui: libubigen information ++ * @peb1: physical eraseblock number to write the first volume table copy ++ * @peb2: physical eraseblock number to write the second volume table copy ++ * @ec1: erase counter value for @peb1 ++ * @ec2: erase counter value for @peb1 ++ * @vtbl: volume table ++ * @fd: output file descriptor seeked to the proper position ++ * ++ * This function creates the UBI layout volume which contains 2 copies of the ++ * volume table. Returns zero in case of success and %-1 in case of failure. ++ */ ++int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2, ++ long long ec1, long long ec2, ++ struct ubi_vtbl_record *vtbl, int fd) ++{ ++ int ret; ++ struct ubigen_vol_info vi; ++ char outbuf[ui->peb_size]; ++ struct ubi_vid_hdr *vid_hdr; ++ off_t seek; ++ ++ vi.bytes = ui->leb_size * UBI_LAYOUT_VOLUME_EBS; ++ vi.id = UBI_LAYOUT_VOLUME_ID; ++ vi.alignment = UBI_LAYOUT_VOLUME_ALIGN; ++ vi.data_pad = ui->leb_size % UBI_LAYOUT_VOLUME_ALIGN; ++ vi.usable_leb_size = ui->leb_size - vi.data_pad; ++ vi.data_pad = ui->leb_size - vi.usable_leb_size; ++ vi.type = UBI_LAYOUT_VOLUME_TYPE; ++ vi.name = UBI_LAYOUT_VOLUME_NAME; ++ vi.name_len = strlen(UBI_LAYOUT_VOLUME_NAME); ++ vi.compat = UBI_LAYOUT_VOLUME_COMPAT; ++ ++ memset(outbuf, 0xFF, ui->data_offs); ++ vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]); ++ memcpy(outbuf + ui->data_offs, vtbl, ui->vtbl_size); ++ memset(outbuf + ui->data_offs + ui->vtbl_size, 0xFF, ++ ui->peb_size - ui->data_offs - ui->vtbl_size); ++ ++ seek = peb1 * ui->peb_size; ++ if (lseek(fd, seek, SEEK_SET) != seek) ++ return sys_errmsg("cannot seek output file"); ++ ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec1); ++ init_vid_hdr(ui, &vi, vid_hdr, 0, NULL, 0); ++ ret = write(fd, outbuf, ui->peb_size); ++ if (ret != ui->peb_size) ++ return sys_errmsg("cannot write %d bytes", ui->peb_size); ++ ++ seek = peb2 * ui->peb_size; ++ if (lseek(fd, seek, SEEK_SET) != seek) ++ return sys_errmsg("cannot seek output file"); ++ ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec2); ++ init_vid_hdr(ui, &vi, vid_hdr, 1, NULL, 0); ++ ret = write(fd, outbuf, ui->peb_size); ++ if (ret != ui->peb_size) ++ return sys_errmsg("cannot write %d bytes", ui->peb_size); ++ ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiattach.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiattach.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiattach.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiattach.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,205 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * An utility to attach MTD devices to UBI. ++ * ++ * Author: Artem Bityutskiy ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "common.h" ++ ++#define PROGRAM_VERSION "1.0" ++#define PROGRAM_NAME "ubiattach" ++ ++/* The variables below are set by command line arguments */ ++struct args { ++ int devn; ++ int mtdn; ++ int vidoffs; ++ const char *node; ++}; ++ ++static struct args args = { ++ .devn = UBI_DEV_NUM_AUTO, ++ .mtdn = -1, ++ .vidoffs = 0, ++ .node = NULL, ++}; ++ ++static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION ++ " - a tool to attach MTD device to UBI."; ++ ++static const char *optionsstr = ++"-d, --devn= the number to assign to the newly created UBI device\n" ++" (the number is assigned automatically if this is not\n" ++" specified\n" ++"-m, --mtdn= MTD device number to attach\n" ++"-O, --vid-hdr-offset VID header offset (do not specify this unless you\n" ++" really know what you do and the optimal defaults will\n" ++" be used)\n" ++"-h, --help print help message\n" ++"-V, --version print program version"; ++ ++static const char *usage = ++"Usage: " PROGRAM_NAME " [-m ] [-d ]\n" ++"\t\t[--mtdn=] [--devn ]\n" ++"Example 1: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 - attach MTD device 0 (mtd0) to UBI\n" ++"Example 2: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 -d 3 - attach MTD device 0 (mtd0) to UBI and\n" ++" and create UBI device number 3 (ubi3)"; ++ ++static const struct option long_options[] = { ++ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, ++ { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' }, ++ { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ { NULL, 0, NULL, 0}, ++}; ++ ++static int parse_opt(int argc, char * const argv[]) ++{ ++ while (1) { ++ int key; ++ char *endp; ++ ++ key = getopt_long(argc, argv, "m:d:O:hV", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 'd': ++ args.devn = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.devn < 0) ++ return errmsg("bad UBI device number: \"%s\"", optarg); ++ ++ break; ++ ++ case 'm': ++ args.mtdn = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.mtdn < 0) ++ return errmsg("bad MTD device number: \"%s\"", optarg); ++ ++ break; ++ ++ case 'O': ++ args.vidoffs = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.vidoffs <= 0) ++ return errmsg("bad VID header offset: \"%s\"", optarg); ++ ++ break; ++ ++ case 'h': ++ fprintf(stderr, "%s\n\n", doc); ++ fprintf(stderr, "%s\n\n", usage); ++ fprintf(stderr, "%s\n", optionsstr); ++ exit(EXIT_SUCCESS); ++ ++ case 'V': ++ fprintf(stderr, "%s\n", PROGRAM_VERSION); ++ exit(EXIT_SUCCESS); ++ ++ case ':': ++ return errmsg("parameter is missing"); ++ ++ default: ++ fprintf(stderr, "Use -h for help\n"); ++ return -1; ++ } ++ } ++ ++ if (optind == argc) ++ return errmsg("UBI control device name was not specified (use -h for help)"); ++ else if (optind != argc - 1) ++ return errmsg("more then one UBI control device specified (use -h for help)"); ++ ++ if (args.mtdn == -1) ++ return errmsg("MTD device number was not specified (use -h for help)"); ++ ++ args.node = argv[optind]; ++ return 0; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ int err; ++ libubi_t libubi; ++ struct ubi_info ubi_info; ++ struct ubi_dev_info dev_info; ++ struct ubi_attach_request req; ++ ++ err = parse_opt(argc, argv); ++ if (err) ++ return -1; ++ ++ libubi = libubi_open(1); ++ if (libubi == NULL) ++ return sys_errmsg("cannot open libubi"); ++ ++ /* ++ * Make sure the kernel is fresh enough and this feature is supported. ++ */ ++ err = ubi_get_info(libubi, &ubi_info); ++ if (err) { ++ sys_errmsg("cannot get UBI information"); ++ goto out_libubi; ++ } ++ ++ if (ubi_info.ctrl_major == -1) { ++ errmsg("MTD attach/detach feature is not supported by your kernel"); ++ goto out_libubi; ++ } ++ ++ req.dev_num = args.devn; ++ req.mtd_num = args.mtdn; ++ req.vid_hdr_offset = args.vidoffs; ++ ++ err = ubi_attach_mtd(libubi, args.node, &req); ++ if (err) { ++ sys_errmsg("cannot attach mtd%d", args.mtdn); ++ goto out_libubi; ++ } ++ ++ /* Print some information about the new UBI device */ ++ err = ubi_get_dev_info1(libubi, req.dev_num, &dev_info); ++ if (err) { ++ sys_errmsg("cannot get information about newly created UBI device"); ++ goto out_libubi; ++ } ++ ++ printf("UBI device number %d, total %d LEBs (", dev_info.dev_num, dev_info.total_lebs); ++ ubiutils_print_bytes(dev_info.total_bytes, 0); ++ printf("), available %d LEBs (", dev_info.avail_lebs); ++ ubiutils_print_bytes(dev_info.avail_bytes, 0); ++ printf("), LEB size "); ++ ubiutils_print_bytes(dev_info.leb_size, 1); ++ printf("\n"); ++ ++ libubi_close(libubi); ++ return 0; ++ ++out_libubi: ++ libubi_close(libubi); ++ return -1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrc32.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrc32.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrc32.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrc32.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,124 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * Calculate CRC32 with UBI start value (0xFFFFFFFF) for a given binary image. ++ * ++ * Author: Oliver Lohmann ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "crc32.h" ++#include "common.h" ++ ++#define BUFSIZE 4096 ++ ++#define PROGRAM_VERSION "1.0" ++#define PROGRAM_NAME "ubicrc32" ++ ++static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION ++ " - a tool to calculate CRC32 with UBI start value (0xFFFFFFFF)"; ++ ++static const char *optionsstr = ++"-h, --help print help message\n" ++"-V, --version print program version"; ++ ++static const char *usage = ++"Usage: " PROGRAM_NAME " [-h] [--help]"; ++ ++static const struct option long_options[] = { ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ { NULL, 0, NULL, 0}, ++}; ++ ++static int parse_opt(int argc, char * const argv[]) ++{ ++ while (1) { ++ int key; ++ ++ key = getopt_long(argc, argv, "hV", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 'h': ++ fprintf(stderr, "%s\n\n", doc); ++ fprintf(stderr, "%s\n\n", usage); ++ fprintf(stderr, "%s\n", optionsstr); ++ exit(EXIT_SUCCESS); ++ ++ case 'V': ++ fprintf(stderr, "%s\n", PROGRAM_VERSION); ++ exit(EXIT_SUCCESS); ++ ++ case ':': ++ return errmsg("parameter is missing"); ++ ++ default: ++ fprintf(stderr, "Use -h for help\n"); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ int err = 0; ++ uint32_t crc = UBI_CRC32_INIT; ++ char buf[BUFSIZE]; ++ FILE *fp; ++ ++ if (argc > 1) { ++ fp = fopen(argv[1], "r"); ++ if (!fp) ++ return sys_errmsg("cannot open \"%s\"", argv[1]); ++ } else ++ fp = stdin; ++ ++ err = parse_opt(argc, argv); ++ if (err) ++ return err; ++ ++ while (!feof(fp)) { ++ size_t read; ++ ++ read = fread(buf, 1, BUFSIZE, fp); ++ if (ferror(fp)) { ++ sys_errmsg("cannot read input file"); ++ err = -1; ++ goto out_close; ++ } ++ crc = crc32(crc, buf, read); ++ } ++ ++ printf("0x%08x\n", crc); ++ ++out_close: ++ if (fp != stdin) ++ fclose(fp); ++ return err; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrcsf.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrcsf.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrcsf.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrcsf.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,94 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * An utility to generate input file CRC ++ * ++ * Authors: Yurong Tan (Nancy) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "common.h" ++#include "crc32.h" ++ ++#define PROGRAM_VERSION "1.1" ++#define PROGRAM_NAME "ubicrcsf" ++#define UBI_LEB_SIZE 258048 ++#define BUF_SIZE (UBI_LEB_SIZE + sizeof(unsigned int)) ++ ++/* ++ * usage: $ubicrcsf ubifs.img ++ */ ++int main(int argc, char * const argv[]) ++{ ++ int err, ifd, tmp, i; ++ int crc_sum = 0; ++ struct stat st; ++ char *buf=NULL; ++ ++ buf = malloc(BUF_SIZE); ++ if(buf==NULL){ ++ printf("no mem\n"); ++ goto out_free; ++ } ++ ++ err = stat(argv[1], &st); ++ if (err < 0) { ++ printf("stat failed on \"%s\"", argv[1]); ++ goto out_free; ++ } ++ ++ ifd = open(argv[1], O_RDONLY); ++ if (ifd == -1) { ++ printf("cannot open \"%s\"", argv[1]); ++ goto out_close; ++ } ++ ++ tmp = st.st_size/BUF_SIZE; ++ ++ for( i=0; i< tmp; i++ ){ ++ err = read(ifd, buf, BUF_SIZE); ++ if (err != BUF_SIZE) { ++ printf("read error\n"); ++ goto out_close; ++ } ++ crc_sum = crc32(crc_sum, &buf[sizeof(unsigned int)], UBI_LEB_SIZE); ++ } ++ ++ printf("CRC sum: %d\n",crc_sum); ++ free(buf); ++ close(ifd); ++ return 0; ++out_close: ++ close(ifd); ++out_free: ++ free(buf); ++ return -1; ++} ++ ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrcvol.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrcvol.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrcvol.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubicrcvol.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,221 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * An utility to update UBI volumes. ++ * ++ * Authors: Frank Haverkamp ++ * Joshua W. Boyer ++ * Artem Bityutskiy ++ * Yurong Tan (Nancy) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "common.h" ++#include "crc32.h" ++ ++#define PROGRAM_VERSION "1.1" ++#define PROGRAM_NAME "ubicrcfatvol" ++ ++struct args { ++ const char *node; ++ long long size; ++ int devn; ++ char dev_name[256]; ++}; ++ ++static struct args args = { ++ .devn = -1, ++}; ++ ++static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION ++ " - a tool to read UBI volumes data and generate CRC."; ++ ++static const char *optionsstr = ++"-h, --help print help message\n" ++"-V, --version print program version\n\n" ++"-s, --read size\n" ++; ++ ++static const char *usage = ++"Usage: " PROGRAM_NAME " [-s] [-h] [-V] [--help]\n" ++"\t\t\t[--version] \n\n" ++ "Example 1: " PROGRAM_NAME " /dev/ubi0_1 \n"; ++ ++struct option long_options[] = { ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ /* Deprecated -d and -B options */ ++ { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' }, ++ { NULL, 0, NULL, 0} ++}; ++ ++static int parse_opt(int argc, char * const argv[]) ++{ ++ while (1) { ++ int key; ++ ++ key = getopt_long(argc, argv, "n:sh?Vd:", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ ++ case 'h': ++ case '?': ++ fprintf(stderr, "%s\n\n", doc); ++ fprintf(stderr, "%s\n\n", usage); ++ fprintf(stderr, "%s\n", optionsstr); ++ exit(EXIT_SUCCESS); ++ ++ case 's': ++ args.size = ubiutils_get_bytes(optarg); ++ if (args.size <= 0) ++ return errmsg("bad read size: \"%s\"", optarg); ++ break; ++ ++ case 'V': ++ fprintf(stderr, "%s\n", PROGRAM_VERSION); ++ exit(EXIT_SUCCESS); ++ ++ case ':': ++ return errmsg("parameter is missing"); ++ ++ default: ++ fprintf(stderr, "Use -h for help\n"); ++ return -1; ++ } ++ } ++ ++ /* Handle deprecated -d option */ ++ if (args.devn != -1) { ++ sprintf(args.dev_name, "/dev/ubi%d", args.devn); ++ args.node = args.dev_name; ++ } else { ++ if (optind == argc) ++ return errmsg("UBI device name was not specified (use -h for help)"); ++ else if (optind != argc - 1) ++ return errmsg("specify UBI device name and image file name as first 2 " ++ "parameters (use -h for help)"); ++ } ++ ++ args.node = argv[optind]; ++ ++ return 0; ++} ++ ++static int crc_volume(libubi_t libubi, struct ubi_vol_info *vol_info) ++{ ++ int fd; ++ struct ubi_leb leb; ++ int i, tmp; ++ int crc_sum = 0; ++ ++ leb.buf = malloc(vol_info->leb_size); ++ if (!leb.buf) ++ return errmsg("cannot allocate %d bytes of memory", vol_info->leb_size); ++ ++ fd = open(args.node, O_RDONLY); ++ if (fd == -1) { ++ sys_errmsg("cannot open UBI volume \"%s\"", args.node); ++ goto out_free; ++ } ++ ++ for(i=0; i < vol_info->rsvd_lebs ; i++){ ++ leb.lnum = i; ++ tmp = ubi_leb_read_start(fd, &leb); ++ if(tmp == 1) ++ continue; ++ else if(tmp == 0){ ++ crc_sum = crc32(crc_sum, leb.buf, vol_info->leb_size); ++ }else{ ++ printf("LEB %d read error\n", i); ++ goto out_close; ++ } ++ } ++ ++ close(fd); ++ free(leb.buf); ++ printf("CRC sum: %d\n",crc_sum); ++ return 0; ++ goto out_close; ++out_close: ++ close(fd); ++out_free: ++ free(leb.buf); ++ return -1; ++} ++ ++ ++int main(int argc, char * const argv[]) ++{ ++ int err; ++ libubi_t libubi; ++ struct ubi_vol_info vol_info; ++ ++ err = parse_opt(argc, argv); ++ if (err) ++ return -1; ++ ++ libubi = libubi_open(1); ++ if (libubi == NULL) { ++ sys_errmsg("cannot open libubi"); ++ goto out_libubi; ++ } ++ ++ err = ubi_node_type(libubi, args.node); ++ if (err == 1) { ++ errmsg("\"%s\" is an UBI device node, not an UBI volume node", ++ args.node); ++ goto out_libubi; ++ } else if (err < 0) { ++ errmsg("\"%s\" is not an UBI volume node", args.node); ++ goto out_libubi; ++ } ++ ++ err = ubi_get_vol_info(libubi, args.node, &vol_info); ++ if (err) { ++ sys_errmsg("cannot get information about UBI volume \"%s\"", ++ args.node); ++ goto out_libubi; ++ } ++ ++ err = crc_volume(libubi, &vol_info); ++ if (err) ++ goto out_libubi; ++ ++ libubi_close(libubi); ++ return 0; ++ ++out_libubi: ++ libubi_close(libubi); ++ return -1; ++ ++} ++ ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubidetach.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubidetach.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubidetach.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubidetach.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,181 @@ ++/* ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * An utility to delete UBI devices (detach MTD devices from UBI). ++ * ++ * Author: Artem Bityutskiy ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "common.h" ++ ++#define PROGRAM_VERSION "1.0" ++#define PROGRAM_NAME "ubidetach" ++ ++/* The variables below are set by command line arguments */ ++struct args { ++ int devn; ++ int mtdn; ++ const char *node; ++}; ++ ++static struct args args = { ++ .devn = UBI_DEV_NUM_AUTO, ++ .mtdn = -1, ++ .node = NULL, ++}; ++ ++static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION ++" - a tool to remove UBI devices (detach MTD devices from UBI)"; ++ ++static const char *optionsstr = ++"-d, --devn= UBI device number to delete\n" ++"-m, --mtdn= or altrnatively, MTD device number to detach -\n" ++" this will delete corresponding UBI device\n" ++"-h, --help print help message\n" ++"-V, --version print program version"; ++ ++static const char *usage = ++"Usage: " PROGRAM_NAME " [-d ] [-m ]\n" ++"\t\t[--devn ] [--mtdn=]\n" ++"Example 1: " PROGRAM_NAME " /dev/ubi_ctrl -d 2 - delete UBI device 2 (ubi2)\n" ++"Example 2: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 - detach MTD device 0 (mtd0)"; ++ ++static const struct option long_options[] = { ++ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, ++ { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ { NULL, 0, NULL, 0}, ++}; ++ ++static int parse_opt(int argc, char * const argv[]) ++{ ++ while (1) { ++ int key; ++ char *endp; ++ ++ key = getopt_long(argc, argv, "m:d:hV", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 'd': ++ args.devn = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.devn < 0) ++ return errmsg("bad UBI device number: \"%s\"", optarg); ++ ++ break; ++ ++ case 'm': ++ args.mtdn = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.mtdn < 0) ++ return errmsg("bad MTD device number: \"%s\"", optarg); ++ ++ break; ++ ++ case 'h': ++ fprintf(stderr, "%s\n\n", doc); ++ fprintf(stderr, "%s\n\n", usage); ++ fprintf(stderr, "%s\n", optionsstr); ++ exit(EXIT_SUCCESS); ++ ++ case 'V': ++ fprintf(stderr, "%s\n", PROGRAM_VERSION); ++ exit(EXIT_SUCCESS); ++ ++ case ':': ++ return errmsg("parameter is missing"); ++ ++ default: ++ fprintf(stderr, "Use -h for help\n"); ++ return -1; ++ } ++ } ++ ++ if (optind == argc) ++ return errmsg("UBI control device name was not specified (use -h for help)"); ++ else if (optind != argc - 1) ++ return errmsg("more then one UBI control device specified (use -h for help)"); ++ ++ if (args.mtdn == -1 && args.devn == -1) ++ return errmsg("neither MTD nor UBI devices were specified (use -h for help)"); ++ ++ if (args.mtdn != -1 && args.devn != -1) ++ return errmsg("specify either MTD or UBI device (use -h for help)"); ++ ++ args.node = argv[optind]; ++ return 0; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ int err; ++ libubi_t libubi; ++ struct ubi_info ubi_info; ++ ++ err = parse_opt(argc, argv); ++ if (err) ++ return -1; ++ ++ libubi = libubi_open(1); ++ if (libubi == NULL) ++ return sys_errmsg("cannot open libubi"); ++ ++ /* ++ * Make sure the kernel is fresh enough and this feature is supported. ++ */ ++ err = ubi_get_info(libubi, &ubi_info); ++ if (err) { ++ sys_errmsg("cannot get UBI information"); ++ goto out_libubi; ++ } ++ ++ if (ubi_info.ctrl_major == -1) { ++ errmsg("MTD detach/detach feature is not supported by your kernel"); ++ goto out_libubi; ++ } ++ ++ if (args.devn != -1) { ++ err = ubi_remove_dev(libubi, args.node, args.devn); ++ if (err) { ++ sys_errmsg("cannot remove ubi%d", args.devn); ++ goto out_libubi; ++ } ++ } else { ++ err = ubi_detach_mtd(libubi, args.node, args.mtdn); ++ if (err) { ++ sys_errmsg("cannot detach mtd%d", args.mtdn); ++ goto out_libubi; ++ } ++ } ++ ++ libubi_close(libubi); ++ return 0; ++ ++out_libubi: ++ libubi_close(libubi); ++ return -1; ++} ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubidumpvol.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubidumpvol.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubidumpvol.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubidumpvol.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,264 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * An utility to update UBI volumes. ++ * ++ * Authors: Frank Haverkamp ++ * Joshua W. Boyer ++ * Artem Bityutskiy ++ * Yurong Tan (Nancy) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "common.h" ++ ++#define PROGRAM_VERSION "1.1" ++#define PROGRAM_NAME "ubidumpvol" ++ ++struct args { ++ int truncate; ++ const char *node; ++ const char *img; ++ /* For deprecated -d and -B options handling */ ++ int devn; ++ char dev_name[256]; ++ int broken_update; ++}; ++ ++static struct args args = { ++ .devn = -1, ++}; ++ ++static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION ++ " - a tool to write data to UBI volumes."; ++ ++static const char *optionsstr = ++"-n, --vol_id= ID of UBI volume to update\n" ++"-t, --truncate truncate volume (wipe it out)\n" ++"-h, --help print help message\n" ++"-V, --version print program version\n\n" ++"The following are compatibility options which are deprecated, do not use them\n" ++"-d, --devn= UBI device number - may be used instead of the UBI\n" ++" device node name in which case the utility assumes\n" ++" that the device node is \"/dev/ubi\"\n" ++"-B, --broken-update broken update, this is for testing"; ++ ++static const char *usage = ++"Usage: " PROGRAM_NAME " [-t] [-h] [-V] [--truncate] [--help]\n" ++"\t\t\t[--version] \n\n" ++ "Example 1: " PROGRAM_NAME " /dev/ubi0_1 fs.img - dump UBI volume /dev/ubi0_1 to file \"fs.img\" \n"; ++ ++struct option long_options[] = { ++ { .name = "truncate", .has_arg = 0, .flag = NULL, .val = 't' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ /* Deprecated -d and -B options */ ++ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, ++ { .name = "broken-update", .has_arg = 1, .flag = NULL, .val = 'B' }, ++ { NULL, 0, NULL, 0} ++}; ++ ++static int parse_opt(int argc, char * const argv[]) ++{ ++ while (1) { ++ int key; ++ ++ key = getopt_long(argc, argv, "n:th?Vd:", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 't': ++ args.truncate = 1; ++ break; ++ ++ case 'h': ++ case '?': ++ fprintf(stderr, "%s\n\n", doc); ++ fprintf(stderr, "%s\n\n", usage); ++ fprintf(stderr, "%s\n", optionsstr); ++ exit(EXIT_SUCCESS); ++ ++ case 'd': ++ { ++ char *endp; ++ ++ /* Handle deprecated -d option */ ++ warnmsg("-d is depricated and will be removed, do not use it"); ++ args.devn = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.devn < 0) ++ return errmsg("bad UBI device number: " "\"%s\"", optarg); ++ break; ++ } ++ ++ case 'B': ++ /* Handle deprecated -B option */ ++ warnmsg("-B is depricated and will be removed, do not use it"); ++ args.broken_update = 1; ++ break; ++ ++ case 'V': ++ fprintf(stderr, "%s\n", PROGRAM_VERSION); ++ exit(EXIT_SUCCESS); ++ ++ case ':': ++ return errmsg("parameter is missing"); ++ ++ default: ++ fprintf(stderr, "Use -h for help\n"); ++ return -1; ++ } ++ } ++ ++ /* Handle deprecated -d option */ ++ if (args.devn != -1) { ++ sprintf(args.dev_name, "/dev/ubi%d", args.devn); ++ args.node = args.dev_name; ++ } else { ++ if (optind == argc) ++ return errmsg("UBI device name was not specified (use -h for help)"); ++ else if (optind != argc - 2) ++ return errmsg("specify UBI device name and image file name as first 2 " ++ "parameters (use -h for help)"); ++ } ++ ++ args.node = argv[optind]; ++ args.img = argv[optind + 1]; ++ ++ return 0; ++} ++ ++static int dump_volume(libubi_t libubi, struct ubi_vol_info *vol_info) ++{ ++ int err, fd, ifd; ++ struct ubi_leb leb; ++ int i, tmp; ++ ++ leb.buf = malloc(vol_info->leb_size); ++ if (!leb.buf) ++ return errmsg("cannot allocate %d bytes of memory", vol_info->leb_size); ++ ++ fd = open(args.node, O_RDONLY); ++ if (fd == -1) { ++ sys_errmsg("cannot open UBI volume \"%s\"", args.node); ++ goto out_free; ++ } ++ ++ ifd = open(args.img, O_WRONLY | O_TRUNC | O_CREAT, 0644); ++ if (ifd == -1) { ++ sys_errmsg("cannot open \"%s\"", args.img); ++ goto out_close1; ++ } ++ ++ for(i=0; i < vol_info->rsvd_lebs; i++){ ++ leb.lnum = i; ++ tmp = ubi_leb_read_start(fd, &leb); ++ if(tmp == 1) ++ continue; ++ else if(tmp == 0){ ++ // write lnum ++ err = write(ifd, (char *)&leb.lnum, sizeof(leb.lnum)); ++ if (err != sizeof(leb.lnum)){ ++ perror("Image file write error\n"); ++ goto out_close; ++ } ++ // write LEB data ++ err = write(ifd, leb.buf, vol_info->leb_size); ++ if (err != vol_info->leb_size){ ++ perror("Image file write error\n"); ++ goto out_close; ++ } ++ }else{ ++ printf("LEB %d read error\n", i); ++ goto out_close; ++ } ++ } ++ ++ close(ifd); ++ close(fd); ++ free(leb.buf); ++ printf("Dump Volume succeed\n"); ++ return 0; ++ goto out_close; ++out_close: ++ close(ifd); ++out_close1: ++ close(fd); ++out_free: ++ free(leb.buf); ++ return -1; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ int err; ++ libubi_t libubi; ++ struct ubi_vol_info vol_info; ++ ++ err = parse_opt(argc, argv); ++ if (err) ++ return -1; ++ ++ if (!args.img && !args.truncate) ++ return errmsg("incorrect arguments, use -h for help"); ++ ++ libubi = libubi_open(1); ++ if (libubi == NULL) { ++ sys_errmsg("cannot open libubi"); ++ goto out_libubi; ++ } ++ ++ err = ubi_node_type(libubi, args.node); ++ if (err == 1) { ++ errmsg("\"%s\" is an UBI device node, not an UBI volume node", ++ args.node); ++ goto out_libubi; ++ } else if (err < 0) { ++ errmsg("\"%s\" is not an UBI volume node", args.node); ++ goto out_libubi; ++ } ++ ++ err = ubi_get_vol_info(libubi, args.node, &vol_info); ++ if (err) { ++ sys_errmsg("cannot get information about UBI volume \"%s\"", ++ args.node); ++ goto out_libubi; ++ } ++ ++ err = dump_volume(libubi, &vol_info); ++ if (err) ++ goto out_libubi; ++ ++ libubi_close(libubi); ++ return 0; ++ ++out_libubi: ++ libubi_close(libubi); ++ return -1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiformat.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiformat.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiformat.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiformat.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,712 @@ ++/* ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * An utility to format MTD devices into UBI and flash UBI images. ++ * ++ * Author: Artem Bityutskiy ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include "crc32.h" ++#include "common.h" ++ ++#define PROGRAM_VERSION "1.0" ++#define PROGRAM_NAME "ubiformat" ++ ++/* The variables below are set by command line arguments */ ++struct args { ++ unsigned int yes:1; ++ unsigned int quiet:1; ++ unsigned int verbose:1; ++ unsigned int override_ec:1; ++ unsigned int novtbl:1; ++ int subpage_size; ++ int vid_hdr_offs; ++ int ubi_ver; ++ long long ec; ++ const char *image; ++ const char *node; ++}; ++ ++static struct args args = ++{ ++ .ubi_ver = 1, ++}; ++ ++static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION ++ " - a tool to format MTD devices and flash UBI images"; ++ ++static const char *optionsstr = ++"-s, --sub-page-size= minimum input/output unit used for UBI\n" ++" headers, e.g. sub-page size in case of NAND\n" ++" flash (equivalent to the minimum input/output\n" ++" unit size by default)\n" ++"-O, --vid-hdr-offset= offset if the VID header from start of the\n" ++" physical eraseblock (default is the next\n" ++" minimum I/O unit or sub-page after the EC\n" ++" header)\n" ++"-n, --no-volume-table only erase all eraseblock and preserve erase\n" ++" counters, do not write empty volume table\n" ++"-f, --flash-image= flash image file\n" ++"-e, --erase-counter= use as the erase counter value for all\n" ++" eraseblocks\n" ++"-y, --yes assume the answer is \"yes\" for all question\n" ++" this program would otherwise ask\n" ++"-q, --quiet suppress progress percentage information\n" ++"-v, --verbose be verbose\n" ++"-x, --ubi-ver= UBI version number to put to EC headers\n" ++" (default is 1)\n" ++"-h, -?, --help print help message\n" ++"-V, --version print program version\n"; ++ ++static const char *usage = ++"Usage: " PROGRAM_NAME " [-h] [-V] [-y] [-q] [-v]\n" ++"\t\t\t[-x ] [-E ] [-s ] [-O ] [-n]\n" ++"\t\t\t[--help] [--version] [--yes] [--verbose] [--quiet]\n" ++"\t\t\t[--ec=] [--vid-hdr-offset=]\n" ++"\t\t\t[--ubi-ver=] [--no-volume-table]\n\n" ++ ++"Example 1: " PROGRAM_NAME " /dev/mtd0 -y - format MTD device number 0 and do\n" ++" not ask questions.\n" ++"Example 2: " PROGRAM_NAME " /dev/mtd0 -q -e 0 - format MTD device number 0,\n" ++" be quiet and force erase counter value 0."; ++ ++static const struct option long_options[] = { ++ { .name = "sub-page-size", .has_arg = 1, .flag = NULL, .val = 's' }, ++ { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' }, ++ { .name = "no-volume-table", .has_arg = 0, .flag = NULL, .val = 'n' }, ++ { .name = "flash-image", .has_arg = 0, .flag = NULL, .val = 'f' }, ++ { .name = "yes", .has_arg = 0, .flag = NULL, .val = 'y' }, ++ { .name = "erase-counter", .has_arg = 1, .flag = NULL, .val = 'e' }, ++ { .name = "quiet", .has_arg = 0, .flag = NULL, .val = 'q' }, ++ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, ++ { .name = "ubi-ver", .has_arg = 1, .flag = NULL, .val = 'x' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ { NULL, 0, NULL, 0}, ++}; ++ ++static int parse_opt(int argc, char * const argv[]) ++{ ++ while (1) { ++ int key; ++ char *endp; ++ ++ key = getopt_long(argc, argv, "nh?Vyqve:x:s:O:f:", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 's': ++ args.subpage_size = ubiutils_get_bytes(optarg); ++ if (args.subpage_size <= 0) ++ return errmsg("bad sub-page size: \"%s\"", optarg); ++ if (!is_power_of_2(args.subpage_size)) ++ return errmsg("sub-page size should be power of 2"); ++ break; ++ ++ case 'O': ++ args.vid_hdr_offs = strtoul(optarg, &endp, 0); ++ if (args.vid_hdr_offs <= 0 || *endp != '\0' || endp == optarg) ++ return errmsg("bad VID header offset: \"%s\"", optarg); ++ break; ++ ++ case 'e': ++ args.ec = strtoull(optarg, &endp, 0); ++ if (args.ec <= 0 || *endp != '\0' || endp == optarg) ++ return errmsg("bad erase counter value: \"%s\"", optarg); ++ if (args.ec >= EC_MAX) ++ return errmsg("too high erase %llu, counter, max is %u", args.ec, EC_MAX); ++ args.override_ec = 1; ++ break; ++ ++ case 'f': ++ args.image = optarg; ++ break; ++ ++ case 'n': ++ args.novtbl = 1; ++ break; ++ ++ case 'y': ++ args.yes = 1; ++ break; ++ ++ case 'q': ++ args.quiet = 1; ++ break; ++ ++ case 'x': ++ args.ubi_ver = strtoul(optarg, &endp, 0); ++ if (args.ubi_ver < 0 || *endp != '\0' || endp == optarg) ++ return errmsg("bad UBI version: \"%s\"", optarg); ++ break; ++ ++ case 'v': ++ args.verbose = 1; ++ break; ++ ++ case 'V': ++ fprintf(stderr, "%s\n", PROGRAM_VERSION); ++ exit(EXIT_SUCCESS); ++ ++ case 'h': ++ case '?': ++ fprintf(stderr, "%s\n\n", doc); ++ fprintf(stderr, "%s\n\n", usage); ++ fprintf(stderr, "%s\n", optionsstr); ++ exit(EXIT_SUCCESS); ++ ++ case ':': ++ return errmsg("parameter is missing"); ++ ++ default: ++ fprintf(stderr, "Use -h for help\n"); ++ return -1; ++ } ++ } ++ ++ if (args.quiet && args.verbose) ++ return errmsg("using \"-q\" and \"-v\" at the same time does not make sense"); ++ ++ if (optind == argc) ++ return errmsg("MTD device name was not specified (use -h for help)"); ++ else if (optind != argc - 1) ++ return errmsg("more then one MTD device specified (use -h for help)"); ++ ++ if (args.image && args.novtbl) ++ return errmsg("-n cannot be used together with -f"); ++ ++ args.node = argv[optind]; ++ return 0; ++} ++ ++static int want_exit(void) ++{ ++ char buf[4]; ++ ++ while (1) { ++ normsg_cont("continue? (yes/no) "); ++ scanf("%3s", buf); ++ if (!strncmp(buf, "yes", 3) || !strncmp(buf, "y", 1)) ++ return 0; ++ if (!strncmp(buf, "no", 2) || !strncmp(buf, "n", 1)) ++ return 1; ++ } ++} ++ ++static int answer_is_yes(void) ++{ ++ char buf[4]; ++ ++ while (1) { ++ scanf("%3s", buf); ++ if (!strncmp(buf, "yes", 3) || !strncmp(buf, "y", 1)) ++ return 1; ++ if (!strncmp(buf, "no", 2) || !strncmp(buf, "n", 1)) ++ return 0; ++ } ++} ++ ++static void print_bad_eraseblocks(const struct mtd_info *mtd, ++ const struct ubi_scan_info *si) ++{ ++ int first = 1, eb; ++ ++ if (si->bad_cnt == 0) ++ return; ++ ++ normsg_cont("bad eraseblocks: "); ++ for (eb = 0; eb < mtd->eb_cnt; eb++) { ++ if (si->ec[eb] != EB_BAD) ++ continue; ++ if (first) { ++ printf("%d", eb); ++ first = 0; ++ } else ++ printf(", %d", eb); ++ } ++ printf("\n"); ++} ++ ++static int change_ec(struct ubi_ec_hdr *hdr, long long ec) ++{ ++ uint32_t crc; ++ ++ /* Check the EC header */ ++ if (be32_to_cpu(hdr->magic) != UBI_EC_HDR_MAGIC) ++ return errmsg("mad UBI magic %#08x, should be %#08x", ++ be32_to_cpu(hdr->magic), UBI_EC_HDR_MAGIC); ++ ++ crc = crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC); ++ if (be32_to_cpu(hdr->hdr_crc) != crc) ++ return errmsg("bad CRC %#08x, should be %#08x\n", ++ crc, be32_to_cpu(hdr->hdr_crc)); ++ ++ hdr->ec = cpu_to_be64(ec); ++ crc = crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC); ++ hdr->hdr_crc = cpu_to_be32(crc); ++ ++ return 0; ++} ++ ++static int drop_ffs(const struct mtd_info *mtd, const void *buf, int len) ++{ ++ int i; ++ ++ for (i = len - 1; i >= 0; i--) ++ if (((const uint8_t *)buf)[i] != 0xFF) ++ break; ++ ++ /* The resulting length must be aligned to the minimum flash I/O size */ ++ len = i + 1; ++ len = (len + mtd->min_io_size - 1) / mtd->min_io_size; ++ len *= mtd->min_io_size; ++ return len; ++} ++ ++static int flash_image(const struct mtd_info *mtd, const struct ubigen_info *ui, ++ struct ubi_scan_info *si) ++{ ++ int fd, img_ebs, eb, written_ebs = 0, divisor; ++ struct stat st; ++ ++ if (stat(args.image, &st)) ++ return sys_errmsg("cannot open \"%s\"", args.image); ++ ++ img_ebs = st.st_size / mtd->eb_size; ++ if (img_ebs > si->good_cnt) ++ return sys_errmsg("file \"%s\" is too large (%lld bytes)", ++ args.image, (long long)st.st_size); ++ ++ if (st.st_size % mtd->eb_size) ++ return sys_errmsg("file \"%s\" (size %lld bytes) is not multiple of eraseblock size (%d bytes)", ++ args.image, (long long)st.st_size, mtd->eb_size); ++ ++ fd = open(args.image, O_RDONLY); ++ if (fd == -1) ++ return sys_errmsg("cannot open \"%s\"", args.image); ++ ++ verbose(args.verbose, "will write %d eraseblocks", img_ebs); ++ divisor = img_ebs; ++ for (eb = 0; eb < mtd->eb_cnt; eb++) { ++ int err, new_len; ++ char buf[mtd->eb_size]; ++ long long ec; ++ ++ if (!args.quiet && !args.verbose) { ++ printf("\r" PROGRAM_NAME ": flashing eraseblock %d -- %2lld %% complete ", ++ eb, (long long)(eb + 1) * 100 / divisor); ++ fflush(stdout); ++ } ++ ++ if (si->ec[eb] == EB_BAD) { ++ divisor += 1; ++ continue; ++ } ++ ++ if (args.verbose) { ++ normsg_cont("eraseblock %d: erase", eb); ++ fflush(stdout); ++ } ++ ++ err = mtd_erase(mtd, eb); ++ if (err) { ++ sys_errmsg("failed to erase eraseblock %d", eb); ++ goto out_close; ++ } ++ ++ if (read(fd, buf, mtd->eb_size) != mtd->eb_size) { ++ sys_errmsg("failed to read eraseblock %d from \"%s\"", ++ written_ebs, args.image); ++ goto out_close; ++ } ++ ++ ++ if (si->ec[eb] <= EC_MAX) ++ ec = si->ec[eb] + 1; ++ else if (!args.override_ec) ++ ec = si->mean_ec; ++ else ++ ec = args.ec; ++ ++ if (args.verbose) { ++ printf(", change EC to %lld", ec); ++ fflush(stdout); ++ } ++ ++ err = change_ec((struct ubi_ec_hdr *)buf, ec); ++ if (err) { ++ errmsg("bad EC header at eraseblock %d of \"%s\"", ++ written_ebs, args.image); ++ goto out_close; ++ } ++ ++ if (args.verbose) { ++ printf(", write data\n"); ++ fflush(stdout); ++ } ++ ++ new_len = drop_ffs(mtd, buf, mtd->eb_size); ++ ++ err = mtd_write(mtd, eb, 0, buf, new_len); ++ if (err) { ++ sys_errmsg("cannot write eraseblock %d", eb); ++ goto out_close; ++ } ++ if (++written_ebs >= img_ebs) ++ break; ++ } ++ ++ if (!args.quiet && !args.verbose) ++ printf("\n"); ++ close(fd); ++ return eb + 1; ++ ++out_close: ++ close(fd); ++ return -1; ++} ++ ++static int format(const struct mtd_info *mtd, const struct ubigen_info *ui, ++ const struct ubi_scan_info *si, int start_eb, int novtbl) ++{ ++ int eb, err, write_size; ++ struct ubi_ec_hdr *hdr; ++ struct ubi_vtbl_record *vtbl; ++ int eb1 = -1, eb2 = -1; ++ long long ec1 = -1, ec2 = -1; ++ ++ write_size = UBI_EC_HDR_SIZE + mtd->subpage_size - 1; ++ write_size /= mtd->subpage_size; ++ write_size *= mtd->subpage_size; ++ hdr = malloc(write_size); ++ if (!hdr) ++ return sys_errmsg("cannot allocate %d bytes of memory", write_size); ++ ++ memset(hdr, 0xFF, write_size); ++ ++ for (eb = start_eb; eb < mtd->eb_cnt; eb++) { ++ long long ec; ++ ++ if (!args.quiet && !args.verbose) { ++ printf("\r" PROGRAM_NAME ": formatting eraseblock %d -- %2lld %% complete ", ++ eb, (long long)(eb + 1 - start_eb) * 100 / (mtd->eb_cnt - start_eb)); ++ fflush(stdout); ++ } ++ ++ if (si->ec[eb] == EB_BAD) ++ continue; ++ ++ if (si->ec[eb] <= EC_MAX) ++ ec = si->ec[eb] + 1; ++ else if (!args.override_ec) ++ ec = si->mean_ec; ++ else ++ ec = args.ec; ++ ubigen_init_ec_hdr(ui, hdr, ec); ++ ++ if (args.verbose) { ++ normsg_cont("eraseblock %d: erase", eb); ++ fflush(stdout); ++ } ++ ++ err = mtd_erase(mtd, eb); ++ if (err) { ++ sys_errmsg("failed to erase eraseblock %d", eb); ++ goto out_free; ++ } ++ ++ if ((eb1 == -1 || eb2 == -1) && !novtbl) { ++ if (eb1 == -1) { ++ eb1 = eb; ++ ec1 = ec; ++ } else if (eb2 == -1) { ++ eb2 = eb; ++ ec2 = ec; ++ } ++ if (args.verbose) ++ printf(", do not write EC, leave for vtbl\n"); ++ continue; ++ } ++ ++ if (args.verbose) { ++ printf(", write EC %lld\n", ec); ++ fflush(stdout); ++ } ++ ++ err = mtd_write(mtd, eb, 0, hdr, write_size); ++ if (err) { ++ sys_errmsg("cannot write EC header (%d bytes buffer) to eraseblock %d", ++ write_size, eb); ++ if (args.subpage_size != mtd->min_io_size) ++ normsg("may be %d is incorrect?", args.subpage_size); ++ goto out_free; ++ } ++ } ++ ++ if (!args.quiet && !args.verbose) ++ printf("\n"); ++ ++ if (!novtbl) { ++ if (eb1 == -1 || eb2 == -1) { ++ errmsg("no eraseblocks for volume table"); ++ goto out_free; ++ } ++ ++ verbose(args.verbose, "write volume table to eraseblocks %d and %d", eb1, eb2); ++ vtbl = ubigen_create_empty_vtbl(ui); ++ if (!vtbl) ++ goto out_free; ++ ++ err = ubigen_write_layout_vol(ui, eb1, eb2, ec1, ec2, vtbl, mtd->fd); ++ free(vtbl); ++ if (err) { ++ errmsg("cannot write layout volume"); ++ goto out_free; ++ } ++ } ++ ++ free(hdr); ++ return 0; ++ ++out_free: ++ free(hdr); ++ return -1; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ int err, verbose; ++ struct mtd_info mtd; ++ libubi_t libubi; ++ struct ubigen_info ui; ++ struct ubi_scan_info *si; ++ ++ err = parse_opt(argc, argv); ++ if (err) ++ return -1; ++ ++ err = mtd_get_info(args.node, &mtd); ++ if (err) ++ return errmsg("cannot get information about \"%s\"", args.node); ++ ++ if (args.subpage_size == 0) ++ args.subpage_size = mtd.min_io_size; ++ else { ++ if (args.subpage_size > mtd.min_io_size) { ++ errmsg("sub-page cannot be larger than min. I/O unit"); ++ goto out_close; ++ } ++ ++ if (mtd.min_io_size % args.subpage_size) { ++ errmsg("min. I/O unit size should be multiple of sub-page size"); ++ goto out_close; ++ } ++ } ++ ++ /* Validate VID header offset if it was specified */ ++ if (args.vid_hdr_offs != 0) { ++ if (args.vid_hdr_offs % 8) { ++ errmsg("VID header offset has to be multiple of min. I/O unit size"); ++ goto out_close; ++ } ++ if (args.vid_hdr_offs + UBI_VID_HDR_SIZE > mtd.eb_size) { ++ errmsg("bad VID header offset"); ++ goto out_close; ++ } ++ } ++ ++ /* ++ * Because of MTD interface limitations 'mtd_get_info()' cannot get ++ * sub-page so we force the user to pass it via the command line. Let's ++ * hope the user passed us something sane. ++ */ ++ mtd.subpage_size = args.subpage_size; ++ ++ if (mtd.rdonly) { ++ errmsg("mtd%d (%s) is a read-only device", mtd.num, args.node); ++ goto out_close; ++ } ++ ++ /* Make sure this MTD device is not attached to UBI */ ++ libubi = libubi_open(0); ++ if (libubi) { ++ int ubi_dev_num; ++ ++ err = mtd_num2ubi_dev(libubi, mtd.num, &ubi_dev_num); ++ libubi_close(libubi); ++ if (!err) { ++ errmsg("please, first detach mtd%d (%s) from ubi%d", ++ mtd.num, args.node, ubi_dev_num); ++ goto out_close; ++ } ++ } ++ ++ if (!args.quiet) { ++ normsg_cont("mtd%d (%s), size ", mtd.num, mtd.type_str); ++ ubiutils_print_bytes(mtd.size, 1); ++ printf(", %d eraseblocks of ", mtd.eb_size); ++ ubiutils_print_bytes(mtd.eb_size, 1); ++ printf(", min. I/O size %d bytes\n", mtd.min_io_size); ++ } ++ ++ if (args.quiet) ++ verbose = 0; ++ else if (args.verbose) ++ verbose = 2; ++ else ++ verbose = 1; ++ err = ubi_scan(&mtd, &si, verbose); ++ if (err) { ++ errmsg("failed to scan mtd%d (%s)", mtd.num, args.node); ++ goto out_close; ++ } ++ ++ if (si->good_cnt == 0) { ++ errmsg("all %d eraseblocks are bad", si->bad_cnt); ++ goto out_free; ++ } ++ ++ if (si->good_cnt < 2 && (!args.novtbl || args.image)) { ++ errmsg("too few non-bad eraseblocks (%d) on mtd%d", si->good_cnt, mtd.num); ++ goto out_free; ++ } ++ ++ if (!args.quiet) { ++ if (si->ok_cnt) ++ normsg("%d eraseblocks have valid erase counter, mean value is %lld", ++ si->ok_cnt, si->mean_ec); ++ if (si->empty_cnt) ++ normsg("%d eraseblocks are supposedly empty", si->empty_cnt); ++ if (si->corrupted_cnt) ++ normsg("%d corrupted erase counters", si->corrupted_cnt); ++ print_bad_eraseblocks(&mtd, si); ++ } ++ ++ if (si->alien_cnt) { ++ if (!args.yes || !args.quiet) ++ warnmsg("%d of %d eraseblocks contain non-ubifs data", ++ si->alien_cnt, si->good_cnt); ++ if (!args.yes && want_exit()) { ++ if (args.yes && !args.quiet) ++ printf("yes\n"); ++ goto out_free; ++ } ++ } ++ ++ if (!args.override_ec && si->empty_cnt < si->good_cnt) { ++ int percent = ((double)si->ok_cnt)/si->good_cnt * 100; ++ ++ /* ++ * Make sure the majority of eraseblocks have valid ++ * erase counters. ++ */ ++ if (percent < 50) { ++ if (!args.yes || !args.quiet) ++ warnmsg("only %d of %d eraseblocks have valid erase counter", ++ si->ok_cnt, si->good_cnt); ++ normsg("erase counter 0 will be used for all eraseblocks"); ++ normsg("note, arbitrary erase counter value may be specified using -e option"); ++ if (!args.yes && want_exit()) { ++ if (args.yes && !args.quiet) ++ printf("yes\n"); ++ goto out_free; ++ } ++ args.ec = 0; ++ args.override_ec = 1; ++ } else if (percent < 95) { ++ if (!args.yes || !args.quiet) ++ warnmsg("only %d of %d eraseblocks have valid erase counter", ++ si->ok_cnt, si->good_cnt); ++ normsg("mean erase counter %lld will be used for the rest of eraseblock", ++ si->mean_ec); ++ if (!args.yes && want_exit()) { ++ if (args.yes && !args.quiet) ++ printf("yes\n"); ++ goto out_free; ++ } ++ args.ec = si->mean_ec; ++ args.override_ec = 1; ++ } ++ } ++ ++ if (!args.quiet && args.override_ec) ++ normsg("use erase counter %lld for all eraseblocks", args.ec); ++ ++ ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, args.subpage_size, ++ args.vid_hdr_offs, args.ubi_ver); ++ ++ if (si->vid_hdr_offs != -1 && ui.vid_hdr_offs != si->vid_hdr_offs) { ++ /* ++ * Hmm, what we read from flash and what we calculated using ++ * min. I/O unit size and sub-page size differs. ++ */ ++ if (!args.yes || !args.quiet) { ++ warnmsg("VID header and data offsets on flash are %d and %d, " ++ "which is different to calculated offsets %d and %d", ++ si->vid_hdr_offs, si->data_offs, ui.vid_hdr_offs, ++ ui.data_offs); ++ normsg_cont("use old offsets %d and %d? (yes/no) ", ++ si->vid_hdr_offs, si->data_offs); ++ } ++ if (args.yes || answer_is_yes()) { ++ if (args.yes && !args.quiet) ++ printf("yes\n"); ++ ui.vid_hdr_offs = si->vid_hdr_offs; ++ ui.data_offs = si->data_offs; ++ } ++ } ++ ++ if (args.image) { ++ err = flash_image(&mtd, &ui, si); ++ if (err < 0) ++ goto out_free; ++ ++ err = format(&mtd, &ui, si, err, 1); ++ if (err) ++ goto out_free; ++ } else { ++ err = format(&mtd, &ui, si, 0, args.novtbl); ++ if (err) ++ goto out_free; ++ } ++ ++ ubi_scan_free(si); ++ close(mtd.fd); ++ return 0; ++ ++out_free: ++ ubi_scan_free(si); ++out_close: ++ close(mtd.fd); ++ return -1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubimkvol.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubimkvol.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubimkvol.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubimkvol.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,310 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * An utility to create UBI volumes. ++ * ++ * Authors: Artem Bityutskiy ++ * Frank Haverkamp ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "common.h" ++ ++#define PROGRAM_VERSION "1.0" ++#define PROGRAM_NAME "ubimkvol" ++ ++/* The variables below are set by command line arguments */ ++struct args { ++ int vol_id; ++ int vol_type; ++ long long bytes; ++ int lebs; ++ int alignment; ++ const char *name; ++ int nlen; ++ const char *node; ++ int maxavs; ++ /* For deprecated -d option handling */ ++ int devn; ++ char dev_name[256]; ++}; ++ ++static struct args args = { ++ .vol_type = UBI_DYNAMIC_VOLUME, ++ .bytes = -1, ++ .lebs = -1, ++ .alignment = 1, ++ .vol_id = UBI_VOL_NUM_AUTO, ++ .devn = -1, ++}; ++ ++static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION ++ " - a tool to create UBI volumes."; ++ ++static const char *optionsstr = ++"-a, --alignment= volume alignment (default is 1)\n" ++"-n, --vol_id= UBI volume ID, if not specified, the volume ID\n" ++" will be assigned automatically\n" ++"-N, --name= volume name\n" ++"-s, --size= volume size volume size in bytes, kilobytes (KiB)\n" ++" or megabytes (MiB)\n" ++"-S, --lebs= alternative way to give volume size in logical\n" ++" eraseblocks\n" ++"-m, --maxavsize set volume size to maximum available size\n" ++"-t, --type= volume type (dynamic, static), default is dynamic\n" ++"-h, -?, --help print help message\n" ++"-V, --version print program version\n\n" ++"The following is a compatibility option which is deprecated, do not use it\n" ++"-d, --devn= UBI device number - may be used instead of the UBI\n" ++" device node name in which case the utility assumes\n" ++" that the device node is \"/dev/ubi\""; ++ ++ ++static const char *usage = ++"Usage: " PROGRAM_NAME " [-h] [-a ] [-n ] [-N ]\n" ++"\t\t\t[-s ] [-S ] [-t ] [-V] [-m]\n" ++"\t\t\t[--alignment=][--vol_id=] [--name=]\n" ++"\t\t\t[--size=] [--lebs=] [--type=] [--help]\n" ++"\t\t\t[--version] [--maxavsize]\n\n" ++"Example: " PROGRAM_NAME "/dev/ubi0 -s 20MiB -N config_data - create a 20 Megabytes volume\n" ++" named \"config_data\" on UBI device /dev/ubi0."; ++ ++static const struct option long_options[] = { ++ { .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'a' }, ++ { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, ++ { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' }, ++ { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' }, ++ { .name = "lebs", .has_arg = 1, .flag = NULL, .val = 'S' }, ++ { .name = "type", .has_arg = 1, .flag = NULL, .val = 't' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ { .name = "maxavsize", .has_arg = 0, .flag = NULL, .val = 'm' }, ++ /* Deprecated -d option */ ++ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, ++ { NULL, 0, NULL, 0}, ++}; ++ ++static int param_sanity_check(void) ++{ ++ int len; ++ ++ if (args.bytes == -1 && !args.maxavs && args.lebs == -1) ++ return errmsg("volume size was not specified (use -h for help)"); ++ ++ if ((args.bytes != -1 && (args.maxavs || args.lebs != -1)) || ++ (args.lebs != -1 && (args.maxavs || args.bytes != -1)) || ++ (args.maxavs && (args.bytes != -1 || args.lebs != -1))) ++ return errmsg("size specified with more then one option"); ++ ++ if (args.name == NULL) ++ return errmsg("volume name was not specified (use -h for help)"); ++ ++ len = strlen(args.name); ++ if (len > UBI_MAX_VOLUME_NAME) ++ return errmsg("too long name (%d symbols), max is %d", len, UBI_MAX_VOLUME_NAME); ++ ++ return 0; ++} ++ ++static int parse_opt(int argc, char * const argv[]) ++{ ++ while (1) { ++ int key; ++ char *endp; ++ ++ key = getopt_long(argc, argv, "a:n:N:s:S:t:h?Vmd:", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 't': ++ if (!strcmp(optarg, "dynamic")) ++ args.vol_type = UBI_DYNAMIC_VOLUME; ++ else if (!strcmp(optarg, "static")) ++ args.vol_type = UBI_STATIC_VOLUME; ++ else ++ return errmsg("bad volume type: \"%s\"", optarg); ++ break; ++ ++ case 's': ++ args.bytes = ubiutils_get_bytes(optarg); ++ if (args.bytes <= 0) ++ return errmsg("bad volume size: \"%s\"", optarg); ++ break; ++ ++ case 'S': ++ args.lebs = strtoull(optarg, &endp, 0); ++ if (endp == optarg || args.lebs <= 0 || *endp != '\0') ++ return errmsg("bad LEB count: \"%s\"", optarg); ++ break; ++ ++ case 'a': ++ args.alignment = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.alignment <= 0) ++ return errmsg("bad volume alignment: \"%s\"", optarg); ++ break; ++ ++ case 'n': ++ args.vol_id = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.vol_id < 0) ++ return errmsg("bad volume ID: " "\"%s\"", optarg); ++ break; ++ ++ case 'd': ++ /* Handle deprecated -d option */ ++ warnmsg("-d is depricated and will be removed, do not use it"); ++ args.devn = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.devn < 0) ++ return errmsg("bad UBI device number: " "\"%s\"", optarg); ++ break; ++ ++ case 'N': ++ args.name = optarg; ++ args.nlen = strlen(args.name); ++ break; ++ ++ case 'h': ++ case '?': ++ fprintf(stderr, "%s\n\n", doc); ++ fprintf(stderr, "%s\n\n", usage); ++ fprintf(stderr, "%s\n", optionsstr); ++ exit(EXIT_SUCCESS); ++ ++ case 'V': ++ fprintf(stderr, "%s\n", PROGRAM_VERSION); ++ exit(EXIT_SUCCESS); ++ ++ case 'm': ++ args.maxavs = 1; ++ break; ++ ++ case ':': ++ return errmsg("parameter is missing"); ++ ++ default: ++ fprintf(stderr, "Use -h for help\n"); ++ return -1; ++ } ++ } ++ ++ /* Handle deprecated -d option */ ++ if (args.devn != -1) { ++ sprintf(args.dev_name, "/dev/ubi%d", args.devn); ++ args.node = args.dev_name; ++ } else { ++ if (optind == argc) ++ return errmsg("UBI device name was not specified (use -h for help)"); ++ else if (optind != argc - 1) ++ return errmsg("more then one UBI device specified (use -h for help)"); ++ ++ args.node = argv[optind]; ++ } ++ ++ if (param_sanity_check()) ++ return -1; ++ ++ return 0; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ int err; ++ libubi_t libubi; ++ struct ubi_dev_info dev_info; ++ struct ubi_vol_info vol_info; ++ struct ubi_mkvol_request req; ++ ++ err = parse_opt(argc, argv); ++ if (err) ++ return err; ++ ++ libubi = libubi_open(1); ++ if (!libubi) ++ return sys_errmsg("cannot open libubi"); ++ ++ err = ubi_node_type(libubi, args.node); ++ if (err == 2) { ++ errmsg("\"%s\" is an UBI volume node, not an UBI device node", ++ args.node); ++ goto out_libubi; ++ } else if (err < 0) { ++ errmsg("\"%s\" is not an UBI device node", args.node); ++ goto out_libubi; ++ } ++ ++ err = ubi_get_dev_info(libubi, args.node, &dev_info); ++ if (err) { ++ sys_errmsg("cannot get information about UBI device \"%s\"", ++ args.node); ++ goto out_libubi; ++ } ++ ++ if (args.maxavs) { ++ args.bytes = dev_info.avail_bytes; ++ printf("Set volume size to %lld\n", args.bytes); ++ } ++ ++ if (args.lebs != -1) { ++ args.bytes = dev_info.leb_size; ++ args.bytes -= dev_info.leb_size % args.alignment; ++ args.bytes *= args.lebs; ++ } ++ ++ req.vol_id = args.vol_id; ++ req.alignment = args.alignment; ++ req.bytes = args.bytes; ++ req.vol_type = args.vol_type; ++ req.name = args.name; ++ ++ err = ubi_mkvol(libubi, args.node, &req); ++ if (err < 0) { ++ sys_errmsg("cannot UBI create volume"); ++ goto out_libubi; ++ } ++ ++ args.vol_id = req.vol_id; ++ ++ /* Print information about the created device */ ++ err = ubi_get_vol_info1(libubi, dev_info.dev_num, args.vol_id, &vol_info); ++ if (err) { ++ sys_errmsg("cannot get information about newly created UBI volume"); ++ goto out_libubi; ++ } ++ ++ printf("Volume ID %d, size %d LEBs (", vol_info.vol_id, vol_info.rsvd_lebs); ++ ubiutils_print_bytes(vol_info.rsvd_bytes, 0); ++ printf("), LEB size "); ++ ubiutils_print_bytes(vol_info.leb_size, 1); ++ printf(", %s, name \"%s\", alignment %d\n", ++ req.vol_type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static", ++ vol_info.name, vol_info.alignment); ++ ++ libubi_close(libubi); ++ return 0; ++ ++out_libubi: ++ libubi_close(libubi); ++ return -1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubinfo.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubinfo.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubinfo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubinfo.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,409 @@ ++/* ++ * Copyright (C) 2007, 2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * An utility to get UBI information. ++ * ++ * Author: Artem Bityutskiy ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "common.h" ++ ++#define PROGRAM_VERSION "1.0" ++#define PROGRAM_NAME "ubinfo" ++ ++/* The variables below are set by command line arguments */ ++struct args { ++ int devn; ++ int vol_id; ++ int all; ++ const char *node; ++}; ++ ++static struct args args = { ++ .vol_id = -1, ++ .devn = -1, ++ .all = 0, ++ .node = NULL, ++}; ++ ++static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION ++ " - a tool to print UBI information."; ++ ++static const char *optionsstr = ++"-d, --devn= UBI device number to get information about\n" ++"-n, --vol_id= ID of UBI volume to print information about\n" ++"-a, --all print information about all devices and volumes,\n" ++" or about all volumes if the UBI device was\n" ++" specified\n" ++"-h, --help print help message\n" ++"-V, --version print program version"; ++ ++static const char *usage = ++"Usage 1: " PROGRAM_NAME " [-d ] [-n ] [-a] [-h] [-V] [--vol_id=]\n" ++"\t\t[--devn ] [--all] [--help] [--version]\n" ++"Usage 2: " PROGRAM_NAME " [-a] [-h] [-V] [--all] [--help] [--version]\n" ++"Usage 3: " PROGRAM_NAME " [-h] [-V] [--help] [--version]\n\n" ++"Example 1: " PROGRAM_NAME " - (no arguments) print general UBI information\n" ++"Example 2: " PROGRAM_NAME " -d 1 - print information about UBI device number 1\n" ++"Example 3: " PROGRAM_NAME " /dev/ubi0 -a - print information about all volumes of UBI\n" ++" device /dev/ubi0\n" ++"Example 4: " PROGRAM_NAME " /dev/ubi1_0 - print information about UBI volume /dev/ubi1_0\n" ++"Example 5: " PROGRAM_NAME " -a - print all information\n"; ++ ++static const struct option long_options[] = { ++ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, ++ { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, ++ { .name = "all", .has_arg = 0, .flag = NULL, .val = 'a' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ { NULL, 0, NULL, 0}, ++}; ++ ++static int parse_opt(int argc, char * const argv[]) ++{ ++ while (1) { ++ int key; ++ char *endp; ++ ++ key = getopt_long(argc, argv, "an:d:hV", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 'a': ++ args.all = 1; ++ break; ++ ++ case 'n': ++ args.vol_id = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.vol_id < 0) ++ return errmsg("bad volume ID: " "\"%s\"", optarg); ++ break; ++ ++ case 'd': ++ args.devn = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.devn < 0) ++ return errmsg("bad UBI device number: \"%s\"", optarg); ++ ++ break; ++ ++ case 'h': ++ fprintf(stderr, "%s\n\n", doc); ++ fprintf(stderr, "%s\n\n", usage); ++ fprintf(stderr, "%s\n", optionsstr); ++ exit(EXIT_SUCCESS); ++ ++ case 'V': ++ fprintf(stderr, "%s\n", PROGRAM_VERSION); ++ exit(EXIT_SUCCESS); ++ ++ case ':': ++ return errmsg("parameter is missing"); ++ ++ default: ++ fprintf(stderr, "Use -h for help\n"); ++ return -1; ++ } ++ } ++ ++ if (optind == argc - 1) ++ args.node = argv[optind]; ++ else if (optind < argc) ++ return errmsg("more then one UBI devices specified (use -h for help)"); ++ ++ return 0; ++} ++ ++static int translate_dev(libubi_t libubi, const char *node) ++{ ++ int err; ++ ++ err = ubi_node_type(libubi, node); ++ if (err == -1) { ++ if (errno) ++ return errmsg("unrecognized device node \"%s\"", node); ++ return errmsg("\"%s\" does not correspond to any UBI device or volume", node); ++ } ++ ++ if (err == 1) { ++ struct ubi_dev_info dev_info; ++ ++ err = ubi_get_dev_info(libubi, node, &dev_info); ++ if (err) ++ return sys_errmsg("cannot get information about UBI device \"%s\"", node); ++ ++ args.devn = dev_info.dev_num; ++ } else { ++ struct ubi_vol_info vol_info; ++ ++ err = ubi_get_vol_info(libubi, node, &vol_info); ++ if (err) ++ return sys_errmsg("cannot get information about UBI volume \"%s\"", node); ++ ++ if (args.vol_id != -1) ++ return errmsg("both volume character device node (\"%s\") and " ++ "volume ID (%d) are specify, use only one of them" ++ "(use -h for help)", node, args.vol_id); ++ ++ args.devn = vol_info.dev_num; ++ args.vol_id = vol_info.vol_id; ++ } ++ ++ return 0; ++} ++ ++static int print_vol_info(libubi_t libubi, int dev_num, int vol_id) ++{ ++ int err; ++ struct ubi_vol_info vol_info; ++ ++ err = ubi_get_vol_info1(libubi, dev_num, vol_id, &vol_info); ++ if (err) ++ return sys_errmsg("cannot get information about UBI volume %d on ubi%d", ++ vol_id, dev_num); ++ ++ printf("Volume ID: %d (on ubi%d)\n", vol_info.vol_id, vol_info.dev_num); ++ printf("Type: %s\n", ++ vol_info.type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static"); ++ printf("Alignment: %d\n", vol_info.alignment); ++ ++ printf("Size: %d LEBs (", vol_info.rsvd_lebs); ++ ubiutils_print_bytes(vol_info.rsvd_bytes, 0); ++ printf(")\n"); ++ ++ if (vol_info.type == UBI_STATIC_VOLUME) { ++ printf("Data bytes: "); ++ ubiutils_print_bytes(vol_info.data_bytes, 1); ++ } ++ printf("State: %s\n", vol_info.corrupted ? "corrupted" : "OK"); ++ printf("Name: %s\n", vol_info.name); ++ printf("Character device major/minor: %d:%d\n", ++ vol_info.major, vol_info.minor); ++ ++ return 0; ++} ++ ++static int print_dev_info(libubi_t libubi, int dev_num, int all) ++{ ++ int i, err, first = 1; ++ struct ubi_dev_info dev_info; ++ struct ubi_vol_info vol_info; ++ ++ err = ubi_get_dev_info1(libubi, dev_num, &dev_info); ++ if (err) ++ return sys_errmsg("cannot get information about UBI device %d", dev_num); ++ ++ printf("ubi%d:\n", dev_info.dev_num); ++ printf("Volumes count: %d\n", dev_info.vol_count); ++ printf("Logical eraseblock size: %d\n", dev_info.leb_size); ++ ++ printf("Total amount of logical eraseblocks: %d (", dev_info.total_lebs); ++ ubiutils_print_bytes(dev_info.total_bytes, 0); ++ printf(")\n"); ++ ++ printf("Amount of available logical eraseblocks: %d (", dev_info.avail_lebs); ++ ubiutils_print_bytes(dev_info.avail_bytes, 0); ++ printf(")\n"); ++ ++ printf("Maximum count of volumes %d\n", dev_info.max_vol_count); ++ printf("Count of bad physical eraseblocks: %d\n", dev_info.bad_count); ++ printf("Count of reserved physical eraseblocks: %d\n", dev_info.bad_rsvd); ++ printf("Current maximum erase counter value: %lld\n", dev_info.max_ec); ++ printf("Minimum input/output unit size: %d bytes\n", dev_info.min_io_size); ++ printf("Character device major/minor: %d:%d\n", ++ dev_info.major, dev_info.minor); ++ ++ if (dev_info.vol_count == 0) ++ return 0; ++ ++ printf("Present volumes: "); ++ for (i = dev_info.lowest_vol_num; ++ i <= dev_info.highest_vol_num; i++) { ++ err = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); ++ if (err == -1) { ++ if (errno == ENOENT) ++ continue; ++ ++ return sys_errmsg("libubi failed to probe volume %d on ubi%d", ++ i, dev_info.dev_num); ++ } ++ ++ if (!first) ++ printf(", %d", i); ++ else { ++ printf("%d", i); ++ first = 0; ++ } ++ } ++ printf("\n"); ++ ++ if (!all) ++ return 0; ++ ++ first = 1; ++ printf("\n"); ++ ++ for (i = dev_info.lowest_vol_num; ++ i <= dev_info.highest_vol_num; i++) { ++ if(!first) ++ printf("-----------------------------------\n"); ++ err = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); ++ if (err == -1) { ++ if (errno == ENOENT) ++ continue; ++ ++ return sys_errmsg("libubi failed to probe volume %d on ubi%d", ++ i, dev_info.dev_num); ++ } ++ first = 0; ++ ++ err = print_vol_info(libubi, dev_info.dev_num, i); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int print_general_info(libubi_t libubi, int all) ++{ ++ int i, err, first = 1; ++ struct ubi_info ubi_info; ++ struct ubi_dev_info dev_info; ++ ++ err = ubi_get_info(libubi, &ubi_info); ++ if (err) ++ return sys_errmsg("cannot get UBI information"); ++ ++ printf("UBI version: %d\n", ubi_info.version); ++ printf("Count of UBI devices: %d\n", ubi_info.dev_count); ++ if (ubi_info.ctrl_major != -1) ++ printf("UBI control device major/minor: %d:%d\n", ++ ubi_info.ctrl_major, ubi_info.ctrl_minor); ++ else ++ printf("UBI control device is not supported by this kernel\n"); ++ ++ if (ubi_info.dev_count == 0) ++ return 0; ++ ++ printf("Present UBI devices: "); ++ for (i = ubi_info.lowest_dev_num; ++ i <= ubi_info.highest_dev_num; i++) { ++ err = ubi_get_dev_info1(libubi, i, &dev_info); ++ if (err == -1) { ++ if (errno == ENOENT) ++ continue; ++ ++ return sys_errmsg("libubi failed to probe UBI device %d", i); ++ } ++ ++ if (!first) ++ printf(", ubi%d", i); ++ else { ++ printf("ubi%d", i); ++ first = 0; ++ } ++ } ++ printf("\n"); ++ ++ if (!all) ++ return 0; ++ ++ first = 1; ++ printf("\n"); ++ ++ for (i = ubi_info.lowest_dev_num; ++ i <= ubi_info.highest_dev_num; i++) { ++ if(!first) ++ printf("\n===================================\n\n"); ++ err = ubi_get_dev_info1(libubi, i, &dev_info); ++ if (err == -1) { ++ if (errno == ENOENT) ++ continue; ++ ++ return sys_errmsg("libubi failed to probe UBI device %d", i); ++ } ++ first = 0; ++ ++ err = print_dev_info(libubi, i, all); ++ if (err) ++ return err; ++ } ++ return 0; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ int err; ++ libubi_t libubi; ++ ++ err = parse_opt(argc, argv); ++ if (err) ++ return -1; ++ ++ if (!args.node && args.devn != -1) ++ return errmsg("specify either device number or node file (use -h for help)"); ++ ++ libubi = libubi_open(1); ++ if (libubi == NULL) ++ return sys_errmsg("cannot open libubi"); ++ ++ if (args.node) { ++ /* ++ * A character device was specified, translate this into UBI ++ * device number and volume ID. ++ */ ++ err = translate_dev(libubi, args.node); ++ if (err) ++ goto out_libubi; ++ } ++ ++ if (args.vol_id != -1 && args.devn == -1) { ++ errmsg("volume ID is specified, but UBI device number is not " ++ "(use -h for help)\n"); ++ goto out_libubi; ++ } ++ ++ if (args.devn != -1 && args.vol_id != -1) { ++ print_vol_info(libubi, args.devn, args.vol_id); ++ goto out; ++ } ++ ++ if (args.devn == -1 && args.vol_id == -1) ++ err = print_general_info(libubi, args.all); ++ else if (args.devn != -1 && args.vol_id == -1) ++ err = print_dev_info(libubi, args.devn, args.all); ++ ++ if (err) ++ goto out_libubi; ++ ++out: ++ libubi_close(libubi); ++ return 0; ++ ++out_libubi: ++ libubi_close(libubi); ++ return -1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubinize.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubinize.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubinize.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubinize.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,582 @@ ++/* ++ * Copyright (C) 2008 Nokia Corporation ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * Generate UBI images. ++ * ++ * Authors: Artem Bityutskiy ++ * Oliver Lohmann ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include "common.h" ++ ++#define PROGRAM_VERSION "1.0" ++#define PROGRAM_NAME "ubinize" ++ ++static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION ++" - a tool to generate UBI images. An UBI image may contain one or more UBI " ++"volumes which have to be defined in the input configuration ini-file. The " ++"ini file defines all the UBI volumes - their characteristics and the and the " ++"contents, but it does not define the characteristics of the flash the UBI " ++"image is generated for. Instead, the flash characteristics are defined via " ++"the command-line options. Note, if not sure about some of the command-line " ++"parameters, do not specify them and let the utility to use default values."; ++ ++static const char *optionsstr = ++"-o, --output= output file name\n" ++"-p, --peb-size= size of the physical eraseblock of the flash\n" ++" this UBI image is created for in bytes,\n" ++" kilobytes (KiB), or megabytes (MiB)\n" ++" (mandatory parameter)\n" ++"-m, --min-io-size= minimum input/output unit size of the flash\n" ++" in bytes\n" ++"-s, --sub-page-size= minimum input/output unit used for UBI\n" ++" headers, e.g. sub-page size in case of NAND\n" ++" flash (equivalent to the minimum input/output\n" ++" unit size by default)\n" ++"-O, --vid-hdr-offset= offset if the VID header from start of the\n" ++" physical eraseblock (default is the next\n" ++" minimum I/O unit or sub-page after the EC\n" ++" header)\n" ++"-e, --erase-counter= the erase counter value to put to EC headers\n" ++" (default is 0)\n" ++"-x, --ubi-ver= UBI version number to put to EC headers\n" ++" (default is 1)\n" ++"-v, --verbose be verbose\n" ++"-h, --help print help message\n" ++"-V, --version print program version"; ++ ++static const char *usage = ++"Usage: " PROGRAM_NAME " [-o filename] [-h] [-V] [--output=] [--help]\n" ++"\t\t[--version] ini-file\n" ++"Example: " PROGRAM_NAME " -o ubi.img cfg.ini - create UBI image 'ubi.img' as\n" ++" described by configuration file 'cfg.ini'"; ++ ++static const char *ini_doc = "INI-file format.\n" ++"The input configuration ini-file describes all the volumes which have to\n" ++"be included to the output UBI image. Each volume is described in its own\n" ++"section which may be named arbitrarily. The section consists on\n" ++"\"key=value\" pairs, for example:\n\n" ++"[jffs2-volume]\n" ++"mode=ubi\n" ++"image=../jffs2.img\n" ++"vol_id=1\n" ++"vol_size=30MiB\n" ++"vol_type=dynamic\n" ++"vol_name=jffs2_volume\n" ++"vol_flags=autoresize\n" ++"vol_alignment=1\n\n" ++"This example configuration file tells the utility to create an UBI image\n" ++"with one volume with ID 1, volume size 30MiB, the volume is dynamic, has\n" ++"name \"jffs2_volume\", \"autoresize\" volume flag, and alignment 1. The\n" ++"\"image=../jffs2.img\" line tells the utility to take the contents of the\n" ++"volume from the \"../jffs2.img\" file. The size of the image file has to be\n" ++"less or equivalent to the volume size (30MiB). The \"mode=ubi\" line is\n" ++"mandatory and just tells that the section describes an UBI volume - other\n" ++"section modes may be added in the future.\n" ++"Notes:\n" ++" * size in vol_size might be specified kilobytes (KiB), megabytes (MiB),\n" ++" gigabytes (GiB) or bytes (no modifier);\n" ++" * if \"vol_size\" key is absent, the volume size is assumed to be\n" ++" equivalent to the size of the image file (defined by \"image\" key);\n" ++" * if the \"image\" is absent, the volume is assumed to be empty;\n" ++" * volume alignment must not be greater than the logical eraseblock size;\n" ++" * one ini file may contain arbitrary number of sections, the utility will\n" ++" put all the volumes which are described by these section to the output\n" ++" UBI image file."; ++ ++struct option long_options[] = { ++ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, ++ { .name = "peb-size", .has_arg = 1, .flag = NULL, .val = 'p' }, ++ { .name = "min-io-size", .has_arg = 1, .flag = NULL, .val = 'm' }, ++ { .name = "sub-page-size", .has_arg = 1, .flag = NULL, .val = 's' }, ++ { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' }, ++ { .name = "erase-counter", .has_arg = 1, .flag = NULL, .val = 'e' }, ++ { .name = "ubi-ver", .has_arg = 1, .flag = NULL, .val = 'x' }, ++ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ { NULL, 0, NULL, 0} ++}; ++ ++struct args { ++ const char *f_in; ++ const char *f_out; ++ int out_fd; ++ int peb_size; ++ int min_io_size; ++ int subpage_size; ++ int vid_hdr_offs; ++ int ec; ++ int ubi_ver; ++ int verbose; ++ dictionary *dict; ++}; ++ ++static struct args args = { ++ .peb_size = -1, ++ .min_io_size = -1, ++ .subpage_size = -1, ++ .ubi_ver = 1, ++}; ++ ++static int parse_opt(int argc, char * const argv[]) ++{ ++ while (1) { ++ int key; ++ char *endp; ++ ++ key = getopt_long(argc, argv, "o:p:m:s:O:e:x:vhV", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 'o': ++ args.out_fd = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, ++ S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH); ++ if (args.out_fd == -1) ++ return sys_errmsg("cannot open file \"%s\"", optarg); ++ args.f_out = optarg; ++ break; ++ ++ case 'p': ++ args.peb_size = ubiutils_get_bytes(optarg); ++ if (args.peb_size <= 0) ++ return errmsg("bad physical eraseblock size: \"%s\"", optarg); ++ break; ++ ++ case 'm': ++ args.min_io_size = ubiutils_get_bytes(optarg); ++ if (args.min_io_size <= 0) ++ return errmsg("bad min. I/O unit size: \"%s\"", optarg); ++ if (!is_power_of_2(args.min_io_size)) ++ return errmsg("min. I/O unit size should be power of 2"); ++ break; ++ ++ case 's': ++ args.subpage_size = ubiutils_get_bytes(optarg); ++ if (args.subpage_size <= 0) ++ return errmsg("bad sub-page size: \"%s\"", optarg); ++ if (!is_power_of_2(args.subpage_size)) ++ return errmsg("sub-page size should be power of 2"); ++ break; ++ ++ case 'O': ++ args.vid_hdr_offs = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.vid_hdr_offs < 0) ++ return errmsg("bad VID header offset: \"%s\"", optarg); ++ break; ++ ++ case 'e': ++ args.ec = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.ec < 0) ++ return errmsg("bad erase counter value: \"%s\"", optarg); ++ break; ++ ++ case 'x': ++ args.ubi_ver = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.ubi_ver < 0) ++ return errmsg("bad UBI version: \"%s\"", optarg); ++ break; ++ ++ case 'v': ++ args.verbose = 1; ++ break; ++ ++ case 'h': ++ ubiutils_print_text(stderr, doc, 80); ++ fprintf(stderr, "\n%s\n\n", ini_doc); ++ fprintf(stderr, "%s\n", usage); ++ fprintf(stderr, "%s\n", optionsstr); ++ exit(EXIT_SUCCESS); ++ ++ case 'V': ++ fprintf(stderr, "%s\n", PROGRAM_VERSION); ++ exit(EXIT_SUCCESS); ++ ++ default: ++ fprintf(stderr, "Use -h for help\n"); ++ return -1; ++ } ++ } ++ ++ if (optind == argc) ++ return errmsg("input configuration file was not specified (use -h for help)"); ++ ++ if (optind != argc - 1) ++ return errmsg("more then one configuration file was specified (use -h for help)"); ++ ++ args.f_in = argv[optind]; ++ ++ if (args.peb_size < 0) ++ return errmsg("physical eraseblock size was not specified (use -h for help)"); ++ ++ if (args.peb_size > 1024*1024) ++ return errmsg("too high physical eraseblock size %d", args.peb_size); ++ ++ if (args.min_io_size < 0) ++ return errmsg("min. I/O unit size was not specified (use -h for help)"); ++ ++ if (args.subpage_size < 0) ++ args.subpage_size = args.min_io_size; ++ ++ if (args.subpage_size > args.min_io_size) ++ return errmsg("sub-page cannot be larger then min. I/O unit"); ++ ++ if (args.peb_size % args.min_io_size) ++ return errmsg("physical eraseblock should be multiple of min. I/O units"); ++ ++ if (args.min_io_size % args.subpage_size) ++ return errmsg("min. I/O unit size should be multiple of sub-page size"); ++ ++ if (!args.f_out) ++ return errmsg("output file was not specified (use -h for help)"); ++ ++ if (args.vid_hdr_offs) { ++ if (args.vid_hdr_offs + UBI_VID_HDR_SIZE >= args.peb_size) ++ return errmsg("bad VID header position"); ++ if (args.vid_hdr_offs % 8) ++ return errmsg("VID header offset has to be multiple of min. I/O unit size"); ++ } ++ ++ return 0; ++} ++ ++static int read_section(const char *sname, struct ubigen_vol_info *vi, ++ const char **img) ++{ ++ char buf[256]; ++ const char *p; ++ ++ *img = NULL; ++ ++ if (strlen(sname) > 128) ++ return errmsg("too long section name \"%s\"", sname); ++ ++ /* Make sure mode is UBI, otherwise ignore this section */ ++ sprintf(buf, "%s:mode", sname); ++ p = iniparser_getstring(args.dict, buf, NULL); ++ if (!p) { ++ errmsg("\"mode\" key not found in section \"%s\"", sname); ++ errmsg("the \"mode\" key is mandatory and has to be " ++ "\"mode=ubi\" if the section describes an UBI volume"); ++ return -1; ++ } ++ ++ /* If mode is not UBI, skip this section */ ++ if (strcmp(p, "ubi")) { ++ verbose(args.verbose, "skip non-ubi section \"%s\"", sname); ++ return 1; ++ } ++ ++ verbose(args.verbose, "mode=ubi, keep parsing"); ++ ++ /* Fetch the name of the volume image file */ ++ sprintf(buf, "%s:image", sname); ++ p = iniparser_getstring(args.dict, buf, NULL); ++ if (p) ++ *img = p; ++ ++ /* Fetch volume id */ ++ sprintf(buf, "%s:vol_id", sname); ++ vi->id = iniparser_getint(args.dict, buf, -1); ++ if (vi->id == -1) ++ return errmsg("\"vol_id\" key not found in section \"%s\"", sname); ++ ++ if (vi->id < 0) ++ return errmsg("negative volume ID %d", vi->id); ++ ++ if (vi->id >= UBI_MAX_VOLUMES) ++ return errmsg("too high volume ID %d, max. is %d", vi->id, UBI_MAX_VOLUMES); ++ ++ verbose(args.verbose, "volume ID: %d", vi->id); ++ ++ /* Fetch volume size */ ++ sprintf(buf, "%s:vol_size", sname); ++ p = iniparser_getstring(args.dict, buf, NULL); ++ if (p) { ++ vi->bytes = ubiutils_get_bytes(p); ++ if (vi->bytes <= 0) ++ return errmsg("bad \"vol_size\" key: \"%s\"", p); ++ ++ verbose(args.verbose, "volume size: %lld bytes", vi->bytes); ++ } else { ++ struct stat st; ++ ++ if (!*img) ++ return errmsg("neither image file (\"image=\") nor volume size (\"vol_size=\") specified"); ++ ++ if (stat(*img, &st)) ++ return sys_errmsg("cannot stat \"%s\"", *img); ++ ++ vi->bytes = st.st_size; ++ ++ if (vi->bytes == 0) ++ return errmsg("file \"%s\" referred from section \"%s\" is empty", *img, sname); ++ ++ normsg_cont("volume size was not specified in section \"%s\", assume ", sname); ++ ubiutils_print_bytes(vi->bytes, 1); ++ printf("\n"); ++ } ++ ++ /* Fetch volume type */ ++ sprintf(buf, "%s:vol_type", sname); ++ p = iniparser_getstring(args.dict, buf, NULL); ++ if (!p) { ++ normsg("volume type was not specified in " ++ "section \"%s\", assume \"dynamic\"\n", sname); ++ vi->type = UBI_VID_DYNAMIC; ++ } else { ++ if (!strcmp(p, "static")) ++ vi->type = UBI_VID_STATIC; ++ else if (!strcmp(p, "dynamic")) ++ vi->type = UBI_VID_DYNAMIC; ++ else ++ return errmsg("invalid volume type \"%s\"", p); ++ } ++ ++ verbose(args.verbose, "volume type: %s", ++ vi->type == UBI_VID_DYNAMIC ? "dynamic" : "static"); ++ ++ /* Fetch volume name */ ++ sprintf(buf, "%s:vol_name", sname); ++ p = iniparser_getstring(args.dict, buf, NULL); ++ if (!p) ++ return errmsg("\"vol_name\" key not found in section \"%s\"", sname); ++ ++ vi->name = p; ++ vi->name_len = strlen(p); ++ if (vi->name_len > UBI_VOL_NAME_MAX) ++ return errmsg("too long volume name in section \"%s\", max. is %d characters", ++ vi->name, UBI_VOL_NAME_MAX); ++ ++ verbose(args.verbose, "volume name: %s", p); ++ ++ /* Fetch volume alignment */ ++ sprintf(buf, "%s:vol_alignment", sname); ++ vi->alignment = iniparser_getint(args.dict, buf, -1); ++ if (vi->alignment == -1) { ++ normsg("volume alignment was not specified in section " ++ "\"%s\", assume 1", sname); ++ vi->alignment = 1; ++ } else if (vi->id < 0) ++ return errmsg("negative volume alignement %d", vi->alignment); ++ ++ verbose(args.verbose, "volume alignment: %d", vi->alignment); ++ ++ /* Fetch volume flags */ ++ sprintf(buf, "%s:vol_flags", sname); ++ p = iniparser_getstring(args.dict, buf, NULL); ++ if (p) { ++ if (!strcmp(p, "autoresize")) { ++ verbose(args.verbose, "autoresize flags found"); ++ vi->flags |= UBI_VTBL_AUTORESIZE_FLG; ++ } else { ++ return errmsg("unknown flags \"%s\" in section \"%s\"", p, sname); ++ } ++ } ++ ++ return 0; ++} ++ ++static void init_vol_info(const struct ubigen_info *ui, ++ struct ubigen_vol_info *vi) ++{ ++ vi->data_pad = ui->leb_size % vi->alignment; ++ vi->usable_leb_size = ui->leb_size - vi->data_pad; ++ vi->used_ebs = (vi->bytes + vi->usable_leb_size - 1) / vi->usable_leb_size; ++ vi->compat = 0; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ int err = -1, sects, i, volumes, autoresize_was_already = 0; ++ struct ubigen_info ui; ++ struct ubi_vtbl_record *vtbl; ++ off_t seek; ++ int tmp; ++ ++ err = parse_opt(argc, argv); ++ if (err) ++ return -1; ++ ++ ubigen_info_init(&ui, args.peb_size, args.min_io_size, ++ args.subpage_size, args.vid_hdr_offs, ++ args.ubi_ver); ++ ++ verbose(args.verbose, "LEB size: %d", ui.leb_size); ++ verbose(args.verbose, "PEB size: %d", ui.peb_size); ++ verbose(args.verbose, "min. I/O size: %d", ui.min_io_size); ++ verbose(args.verbose, "sub-page size: %d", ui.min_io_size); ++ verbose(args.verbose, "VID offset: %d", ui.vid_hdr_offs); ++ verbose(args.verbose, "data offset: %d", ui.data_offs); ++ ++ vtbl = ubigen_create_empty_vtbl(&ui); ++ if (!vtbl) ++ goto out; ++ ++ args.dict = iniparser_load(args.f_in); ++ if (!args.dict) { ++ errmsg("cannot load the input ini file \"%s\"", args.f_in); ++ goto out_vtbl; ++ } ++ ++ verbose(args.verbose, "loaded the ini-file \"%s\"", args.f_in); ++ ++ /* Each section describes one volume */ ++ sects = iniparser_getnsec(args.dict); ++ if (sects == -1) { ++ errmsg("ini-file parsing error (iniparser_getnsec)"); ++ goto out_dict; ++ } ++ ++ verbose(args.verbose, "count of sections: %d", sects); ++ if (sects == 0) { ++ errmsg("no sections found the ini-file \"%s\"", args.f_in); ++ goto out_dict; ++ } ++ ++ /* ++ * Skip 2 PEBs at the beginning of the file for the volume table which ++ * will be written later. ++ */ ++ seek = ui.peb_size * 2; ++ if (lseek(args.out_fd, seek, SEEK_SET) != seek) { ++ sys_errmsg("cannot seek file \"%s\"", args.f_out); ++ goto out_dict; ++ } ++ ++ for (i = 0; i < sects; i++) { ++ const char *sname = iniparser_getsecname(args.dict, i); ++ struct ubigen_vol_info vi; ++ const char *img = NULL; ++ struct stat st; ++ int fd; ++ ++ if (!sname) { ++ errmsg("ini-file parsing error (iniparser_getsecname)"); ++ goto out_dict; ++ } ++ ++ if (args.verbose) ++ printf("\n"); ++ verbose(args.verbose, "parsing section \"%s\"", sname); ++ ++ err = read_section(sname, &vi, &img); ++ if (err == -1) ++ goto out_dict; ++ if (!err) ++ volumes += 1; ++ init_vol_info(&ui, &vi); ++ ++ if (vi.id >= ui.max_volumes) ++ return errmsg("too high volume ID %d, max. is %d", ++ vi.id, ui.max_volumes); ++ ++ verbose(args.verbose, "adding volume %d", vi.id); ++ ++ /* Make sure only one volume has auto-resize flag */ ++ if (vi.flags & UBI_VTBL_AUTORESIZE_FLG) { ++ if (autoresize_was_already) ++ return errmsg("only one volume is allowed " ++ "to have auto-resize flag"); ++ autoresize_was_already = 1; ++ } ++ ++ err = ubigen_add_volume(&ui, &vi, vtbl); ++ if (err) { ++ errmsg("cannot add volume for section \"%s\"", sname); ++ goto out_dict; ++ } ++ ++ if (!img) ++ continue; ++ ++ if (stat(img, &st)) { ++ sys_errmsg("cannot stat \"%s\"", img); ++ goto out_dict; ++ } ++ ++ /* ++ * Make sure the image size is not larger then the volume size. ++ */ ++ tmp = (st.st_size / (ui.leb_size + sizeof(unsigned int))) * sizeof(unsigned int); ++ if (st.st_size - tmp> vi.bytes) { ++ errmsg("error in section \"%s\": size of the image file \"%s\" " ++ "is %lld, which is larger then the volume size %lld", ++ sname, img, (long long)st.st_size, vi.bytes); ++ goto out_dict; ++ } ++ ++ fd = open(img, O_RDONLY); ++ if (fd == -1) { ++ sys_errmsg("cannot open \"%s\"", img); ++ goto out_dict; ++ } ++ ++ verbose(args.verbose, "writing volume %d", vi.id); ++ verbose(args.verbose, "image file: %s", img); ++ ++ err = ubigen_write_volume(&ui, &vi, args.ec, st.st_size-tmp, fd, args.out_fd); ++ close(fd); ++ if (err) { ++ errmsg("cannot write volume for section \"%s\"", sname); ++ goto out_dict; ++ } ++ ++ if (args.verbose) ++ printf("\n"); ++ } ++ ++ verbose(args.verbose, "writing layout volume"); ++ ++ err = ubigen_write_layout_vol(&ui, 0, 1, args.ec, args.ec, vtbl, args.out_fd); ++ if (err) { ++ errmsg("cannot write layout volume"); ++ goto out_dict; ++ } ++ ++ verbose(args.verbose, "done"); ++ ++ iniparser_freedict(args.dict); ++ free(vtbl); ++ close(args.out_fd); ++ return 0; ++ ++out_dict: ++ iniparser_freedict(args.dict); ++out_vtbl: ++ free(vtbl); ++out: ++ close(args.out_fd); ++ remove(args.f_out); ++ return err; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubirefimg.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubirefimg.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubirefimg.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubirefimg.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,99 @@ ++/* ++ * An utility to reformat the image file generated by mkfs.ubifs ++ * ++ * Authors: Yurong Tan (Nancy) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define PROGRAM_VERSION "1.1" ++#define PROGRAM_NAME "ubirefimg" ++#define UBI_LEB_SIZE 258048 ++ ++/* ++ * sourcefile usually generated by mkfs.ubifs. ++ * usage: #ubirefimg sourcefile outputfile ++ */ ++int main(int argc, char * const argv[]) ++{ ++ int err, ifd, ofd, i, j, tmp; ++ struct stat st; ++ unsigned char *buf=NULL; ++ ++ buf = malloc(UBI_LEB_SIZE); ++ if(buf==NULL){ ++ printf("no mem\n"); ++ goto out_free; ++ } ++ ++ err = stat(argv[1], &st); ++ if (err < 0) { ++ printf("stat failed on \"%s\"", argv[1]); ++ goto out_free; ++ } ++ ++ ifd = open(argv[1], O_RDONLY); ++ if (ifd == -1) { ++ printf("cannot open \"%s\"", argv[1]); ++ goto out_close; ++ } ++ ++ ofd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0644); ++ if (ofd == -1) { ++ printf("cannot create \"%s\"", argv[2]); ++ goto out_close; ++ } ++ ++ tmp = st.st_size/UBI_LEB_SIZE; ++ ++ for( i=0; i< tmp; i++ ){ ++ err = read(ifd, buf, UBI_LEB_SIZE); ++ if (err != UBI_LEB_SIZE) { ++ printf("read error\n"); ++ goto out_close1; ++ } ++ for(j=0; j ++ * Frank Haverkamp ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "common.h" ++ ++#define PROGRAM_VERSION "1.0" ++#define PROGRAM_NAME "ubirmvol" ++ ++/* The variables below are set by command line arguments */ ++struct args { ++ int vol_id; ++ const char *node; ++ /* For deprecated -d option handling */ ++ int devn; ++ char dev_name[256]; ++}; ++ ++static struct args args = { ++ .vol_id = -1, ++ .devn = -1, ++}; ++ ++static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION ++ " - a tool to remove UBI volumes."; ++ ++static const char *optionsstr = ++"-n, --vol_id= volume ID to remove\n" ++"-h, -?, --help print help message\n" ++"-V, --version print program version\n\n" ++"The following is a compatibility option which is deprecated, do not use it\n" ++"-d, --devn= UBI device number - may be used instead of the UBI\n" ++" device node name in which case the utility assumes\n" ++" that the device node is \"/dev/ubi\""; ++ ++static const char *usage = ++"Usage: " PROGRAM_NAME " [-n ] [--vol_id=] [-h] [--help]\n\n" ++"Example: " PROGRAM_NAME "/dev/ubi0 -n 1 - remove UBI volume 1 from UBI device corresponding\n" ++" to the node file /dev/ubi0."; ++ ++static const struct option long_options[] = { ++ { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ /* Deprecated -d option */ ++ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, ++ { NULL, 0, NULL, 0}, ++}; ++ ++static int param_sanity_check(void) ++{ ++ if (args.vol_id == -1) { ++ errmsg("volume ID is was not specified"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int parse_opt(int argc, char * const argv[]) ++{ ++ while (1) { ++ int key; ++ char *endp; ++ ++ key = getopt_long(argc, argv, "n:h?Vd:", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ ++ case 'n': ++ args.vol_id = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.vol_id < 0) { ++ errmsg("bad volume ID: " "\"%s\"", optarg); ++ return -1; ++ } ++ break; ++ ++ case 'h': ++ case '?': ++ fprintf(stderr, "%s\n\n", doc); ++ fprintf(stderr, "%s\n\n", usage); ++ fprintf(stderr, "%s\n", optionsstr); ++ exit(EXIT_SUCCESS); ++ ++ case 'd': ++ /* Handle deprecated -d option */ ++ warnmsg("-d is depricated and will be removed, do not use it"); ++ args.devn = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.devn < 0) ++ return errmsg("bad UBI device number: " "\"%s\"", optarg); ++ break; ++ ++ case 'V': ++ fprintf(stderr, "%s\n", PROGRAM_VERSION); ++ exit(EXIT_SUCCESS); ++ ++ case ':': ++ errmsg("parameter is missing"); ++ return -1; ++ ++ default: ++ fprintf(stderr, "Use -h for help\n"); ++ return -1; ++ } ++ } ++ ++ /* Handle deprecated -d option */ ++ if (args.devn != -1) { ++ sprintf(args.dev_name, "/dev/ubi%d", args.devn); ++ args.node = args.dev_name; ++ } else { ++ if (optind == argc) { ++ errmsg("UBI device name was not specified (use -h for help)"); ++ return -1; ++ } else if (optind != argc - 1) { ++ errmsg("more then one UBI device specified (use -h for help)"); ++ return -1; ++ } ++ ++ args.node = argv[optind]; ++ } ++ ++ ++ if (param_sanity_check()) ++ return -1; ++ ++ return 0; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ int err; ++ libubi_t libubi; ++ ++ err = parse_opt(argc, argv); ++ if (err) ++ return -1; ++ ++ libubi = libubi_open(1); ++ if (libubi == NULL) ++ return sys_errmsg("cannot open libubi"); ++ ++ err = ubi_node_type(libubi, args.node); ++ if (err == 2) { ++ errmsg("\"%s\" is an UBI volume node, not an UBI device node", ++ args.node); ++ goto out_libubi; ++ } else if (err < 0) { ++ errmsg("\"%s\" is not an UBI device node", args.node); ++ goto out_libubi; ++ } ++ ++ err = ubi_rmvol(libubi, args.node, args.vol_id); ++ if (err) { ++ sys_errmsg("cannot UBI remove volume"); ++ goto out_libubi; ++ } ++ ++ libubi_close(libubi); ++ return 0; ++ ++out_libubi: ++ libubi_close(libubi); ++ return -1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiupdatevol.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiupdatevol.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiupdatevol.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/new-utils/src/ubiupdatevol.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,335 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * An utility to update UBI volumes. ++ * ++ * Authors: Frank Haverkamp ++ * Joshua W. Boyer ++ * Artem Bityutskiy ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "common.h" ++ ++#define PROGRAM_VERSION "1.1" ++#define PROGRAM_NAME "ubiupdatevol" ++ ++struct args { ++ int truncate; ++ const char *node; ++ const char *img; ++ /* For deprecated -d and -B options handling */ ++ int devn; ++ char dev_name[256]; ++ int broken_update; ++}; ++ ++static struct args args = { ++ .devn = -1, ++}; ++ ++static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION ++ " - a tool to write data to UBI volumes."; ++ ++static const char *optionsstr = ++"-n, --vol_id= ID of UBI volume to update\n" ++"-t, --truncate truncate volume (wipe it out)\n" ++"-h, --help print help message\n" ++"-V, --version print program version\n\n" ++"The following are compatibility options which are deprecated, do not use them\n" ++"-d, --devn= UBI device number - may be used instead of the UBI\n" ++" device node name in which case the utility assumes\n" ++" that the device node is \"/dev/ubi\"\n" ++"-B, --broken-update broken update, this is for testing"; ++ ++static const char *usage = ++"Usage: " PROGRAM_NAME " [-t] [-h] [-V] [--truncate] [--help]\n" ++"\t\t\t[--version] \n\n" ++"Example 1: " PROGRAM_NAME " /dev/ubi0_1 fs.img - write file \"fs.img\" to UBI volume /dev/ubi0_1\n" ++"Example 2: " PROGRAM_NAME " /dev/ubi0_1 -t - wipe out UBI volume /dev/ubi0_1"; ++ ++struct option long_options[] = { ++ { .name = "truncate", .has_arg = 0, .flag = NULL, .val = 't' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ /* Deprecated -d and -B options */ ++ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, ++ { .name = "broken-update", .has_arg = 1, .flag = NULL, .val = 'B' }, ++ { NULL, 0, NULL, 0} ++}; ++ ++static int parse_opt(int argc, char * const argv[]) ++{ ++ while (1) { ++ int key; ++ ++ key = getopt_long(argc, argv, "n:th?Vd:", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 't': ++ args.truncate = 1; ++ break; ++ ++ case 'h': ++ case '?': ++ fprintf(stderr, "%s\n\n", doc); ++ fprintf(stderr, "%s\n\n", usage); ++ fprintf(stderr, "%s\n", optionsstr); ++ exit(EXIT_SUCCESS); ++ ++ case 'd': ++ { ++ char *endp; ++ ++ /* Handle deprecated -d option */ ++ warnmsg("-d is depricated and will be removed, do not use it"); ++ args.devn = strtoul(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || args.devn < 0) ++ return errmsg("bad UBI device number: " "\"%s\"", optarg); ++ break; ++ } ++ ++ case 'B': ++ /* Handle deprecated -B option */ ++ warnmsg("-B is depricated and will be removed, do not use it"); ++ args.broken_update = 1; ++ break; ++ ++ case 'V': ++ fprintf(stderr, "%s\n", PROGRAM_VERSION); ++ exit(EXIT_SUCCESS); ++ ++ case ':': ++ return errmsg("parameter is missing"); ++ ++ default: ++ fprintf(stderr, "Use -h for help\n"); ++ return -1; ++ } ++ } ++ ++ /* Handle deprecated -d option */ ++ if (args.devn != -1) { ++ sprintf(args.dev_name, "/dev/ubi%d", args.devn); ++ args.node = args.dev_name; ++ } else { ++ if (optind == argc) ++ return errmsg("UBI device name was not specified (use -h for help)"); ++ else if (optind != argc - 2 && !args.truncate) ++ return errmsg("specify UBI device name and image file name as first 2 " ++ "parameters (use -h for help)"); ++ } ++ ++ args.node = argv[optind]; ++ args.img = argv[optind + 1]; ++ ++ return 0; ++} ++ ++static int truncate_volume(libubi_t libubi) ++{ ++ int err, fd; ++ ++ fd = open(args.node, O_RDWR); ++ if (fd == -1) ++ return sys_errmsg("cannot open \"%s\"", args.node); ++ ++ err = ubi_update_start(libubi, fd, 0); ++ if (err) { ++ sys_errmsg("cannot truncate volume \"%s\"", args.node); ++ close(fd); ++ return -1; ++ } ++ ++ close(fd); ++ return 0; ++} ++ ++static int ubi_write(int fd, const void *buf, int len) ++{ ++ int ret; ++ ++ while (len) { ++ ret = write(fd, buf, len); ++ if (ret < 0) { ++ if (errno == EINTR) { ++ warnmsg("do not interrupt me!"); ++ continue; ++ } ++ return sys_errmsg("cannot write %d bytes to volume \"%s\"", ++ len, args.node); ++ } ++ ++ if (ret == 0) ++ return errmsg("cannot write %d bytes to volume \"%s\"", len, args.node); ++ ++ len -= ret; ++ buf += ret; ++ } ++ ++ return 0; ++} ++ ++static int update_volume(libubi_t libubi, struct ubi_vol_info *vol_info) ++{ ++ int err, fd, ifd; ++ long long bytes, tmp; ++ struct stat st; ++ char *buf; ++ ++ buf = malloc(vol_info->leb_size+sizeof(unsigned int)); ++ if (!buf) ++ return errmsg("cannot allocate %d bytes of memory", vol_info->leb_size); ++ ++ err = stat(args.img, &st); ++ if (err < 0) { ++ errmsg("stat failed on \"%s\"", args.img); ++ goto out_free; ++ } ++ ++ bytes = st.st_size; ++ tmp = bytes / (vol_info->leb_size + sizeof(unsigned int)) * sizeof(unsigned int); ++ bytes -= tmp; ++ if (bytes > vol_info->rsvd_bytes ) { ++ errmsg("\"%s\" (size %lld) will not fit volume \"%s\" (size %lld)", ++ args.img, bytes, args.node, vol_info->rsvd_bytes); ++ goto out_free; ++ } ++ ++ /* A hack to handle deprecated -B option */ ++ if (args.broken_update) ++ bytes = 1; ++ ++ fd = open(args.node, O_RDWR); ++ if (fd == -1) { ++ sys_errmsg("cannot open UBI volume \"%s\"", args.node); ++ goto out_free; ++ } ++ ++ ifd = open(args.img, O_RDONLY); ++ if (ifd == -1) { ++ sys_errmsg("cannot open \"%s\"", args.img); ++ goto out_close1; ++ } ++ ++ err = ubi_update_start(libubi, fd, bytes); ++ if (err) { ++ sys_errmsg("cannot start volume \"%s\" update", args.node); ++ goto out_close; ++ } ++ ++ bytes += tmp; ++ while (bytes) { ++ int tocopy = vol_info->leb_size + sizeof(unsigned int) ; ++ ++ if (tocopy > bytes) ++ tocopy = bytes; ++ ++ err = read(ifd, buf, tocopy); ++ if (err != tocopy) { ++ if (errno == EINTR) { ++ warnmsg("do not interrupt me!"); ++ continue; ++ } else { ++ sys_errmsg("cannot read %d bytes from \"%s\"", ++ tocopy, args.img); ++ goto out_close; ++ } ++ } ++ ++ err = ubi_write(fd, buf, tocopy-sizeof(unsigned int)); ++ if (err) ++ goto out_close; ++ ++ bytes -= tocopy; ++ } ++ ++ close(ifd); ++ close(fd); ++ free(buf); ++ return 0; ++ ++out_close: ++ close(ifd); ++out_close1: ++ close(fd); ++out_free: ++ free(buf); ++ return -1; ++} ++ ++int main(int argc, char * const argv[]) ++{ ++ int err; ++ libubi_t libubi; ++ struct ubi_vol_info vol_info; ++ ++ err = parse_opt(argc, argv); ++ if (err) ++ return -1; ++ ++ libubi = libubi_open(1); ++ if (libubi == NULL) { ++ sys_errmsg("cannot open libubi"); ++ goto out_libubi; ++ } ++ ++ err = ubi_node_type(libubi, args.node); ++ if (err == 1) { ++ errmsg("\"%s\" is an UBI device node, not an UBI volume node", ++ args.node); ++ goto out_libubi; ++ } else if (err < 0) { ++ errmsg("\"%s\" is not an UBI volume node", args.node); ++ goto out_libubi; ++ } ++ ++ err = ubi_get_vol_info(libubi, args.node, &vol_info); ++ if (err) { ++ sys_errmsg("cannot get information about UBI volume \"%s\"", ++ args.node); ++ goto out_libubi; ++ } ++ ++ if (args.truncate) ++ err = truncate_volume(libubi); ++ else ++ err = update_volume(libubi, &vol_info); ++ if (err) ++ goto out_libubi; ++ ++ libubi_close(libubi); ++ return 0; ++ ++out_libubi: ++ libubi_close(libubi); ++ return -1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/perl/f128_nand_sample.cfg linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/perl/f128_nand_sample.cfg +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/perl/f128_nand_sample.cfg 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/perl/f128_nand_sample.cfg 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,38 @@ ++[targets] ++complete=ipl,spl,bootenv,kernel,rootfs ++bootcode=spl,bootenv ++ ++# Build sections ++[ipl] ++image=ipl.bin ++raw_starts=0x00000000 ++raw_total_size=128kiB ++ ++[spl] ++image=u-boot.bin ++ubi_ids=2,3 ++ubi_size=2MiB ++ubi_type=static ++ubi_names=spl_0,spl_1 ++ ++[bootenv] ++bootenv_file=bootenv_complete.txt ++ubi_ids=4,5 ++ubi_size=128kiB ++ubi_type=static ++ubi_names=bootenv_0,bootenv_1 ++ ++[kernel] ++image=vmlinux.bin ++ubi_ids=6,7 ++ubi_size=6MiB ++ubi_type=static ++ubi_names=kernel_0,kernel_1 ++ ++[rootfs] ++image=rootfs.bin ++ubi_ids=8,9 ++ubi_alignment=2kiB ++ubi_size=16MiB ++ubi_type=dynamic ++ubi_names=rootfs_0,rootfs_1 +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/perl/f64_nor_sample.cfg linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/perl/f64_nor_sample.cfg +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/perl/f64_nor_sample.cfg 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/perl/f64_nor_sample.cfg 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,39 @@ ++[targets] ++complete=ipl,spl,bootenv,kernel,rootfs ++bootcode=spl,bootenv ++rootfs=rootfs ++ ++# Build sections ++[ipl] ++image=ipl.bin ++raw_starts=0x02FE0000, 0x03FE0000 ++raw_total_size=128kiB ++ ++[spl] ++image=u-boot.bin ++ubi_ids=2,3 ++ubi_size=2MiB ++ubi_type=static ++ubi_names=spl_0,spl_1 ++ ++[bootenv] ++bootenv_file=bootenv_complete.txt ++ubi_ids=4,5 ++ubi_size=128kiB ++ubi_type=static ++ubi_names=bootenv_0,bootenv_1 ++ ++[kernel] ++image=vmlinux.bin ++ubi_ids=6,7 ++ubi_size=6MiB ++ubi_type=static ++ubi_names=kernel_0,kernel_1 ++ ++[rootfs] ++image=rootfs.bin ++ubi_ids=8,9 ++ubi_alignment=2kiB ++ubi_size=16128kiB ++ubi_type=dynamic ++ubi_names=rootfs_0,rootfs_1 +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/perl/mkpfi linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/perl/mkpfi +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/perl/mkpfi 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/perl/mkpfi 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,723 @@ ++#!/usr/bin/perl ++# ++# Copyright (c) International Business Machines Corp., 2006 ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 2 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++# the GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++# ++ ++# ++# mkpfi ++# ++# This perl program is assembles PFI files from a config file. ++# ++# Author: Oliver Lohmann (oliloh@de.ibm.com) ++# ++use warnings; ++use strict; ++use lib "/usr/lib/perl5"; # Please change this path as you need it, or ++ # make a proposal how this could be done ++ # nicer. ++use Getopt::Long; ++use Pod::Usage; ++use Config::IniFiles; ++use File::Temp; ++ ++# ---------------------------------------------------------------------------- ++# Versions ++our $version : unique = "0.1"; ++our $pfi_version : unique = "0x1"; ++ ++# ---------------------------------------------------------------------------- ++# Globals ++my $verbose = 0; ++my $cfg; ++ ++my %opts = (); ++my %files = (config => ""); ++my @tmp_files; ++ ++my %tools = (ubicrc32 => "ubicrc32"); ++ ++# ---------------------------------------------------------------------------- ++# Processing the input sections ++# ++# The idea is to combine each section entry with a function ++# in order to allow some kind of preprocessing for the values ++# before they are written into the PFI file. ++# This is especially useful to be more verbose and ++# user-friendly in the layout file. ++# ++# All key-function hashes are applied after the general ++# validation of the configuration file. ++# If any mandatory key is missing in a section the user ++# will be informed and the PFI creation process is aborted. ++# ++# Default keys will be checked for their presence inside the config ++# file. If they are missing, they will be generated with appr. values. ++ ++# Mandatory keys for UBI volumes. ++my %ubi_keys = ("ubi_ids" => \&check_id_list, ++ "ubi_size" => \&replace_num, ++ "ubi_type" => \&replace_type, ++ "ubi_names" => \&remove_spaces, ++ "ubi_alignment" => \&replace_num); ++ ++# Mandatory keys for RAW sections. ++my %raw_keys = ("raw_starts" => \&expand_starts, ++ "raw_total_size" => \&replace_num); ++ ++# Common default keys for documentation and control purposes. ++my %common_keys = ("flags" => \&replace_num, ++ "label" => \&do_nothing); ++ ++# Define any defaults here. Values which maintained in this default ++# region need not to be specified by the user explicitly. ++my %def_ubi_keys = ("ubi_alignment" => [\&set_default, "0x1"]); ++my %def_raw_keys = (); ++my %def_common_keys = ("flags" => [\&set_default, "0x0"], ++ "label" => [\&generate_label, ""]); ++ ++# ---------------------------------------------------------------------------- ++# Input keys, actually the path to the input data. ++ ++my %input_keys = ("image" => \&do_nothing); ++ ++# Placeholder keys allow the replacement via a special ++# purpose function. E.g. the bootenv_file key will be used ++# to generate bootenv binary data from an text file and ++# replace the bootenv_file key with an image key to handle it ++# in the same way in the further creation process. ++my %input_placeholder_keys = ("bootenv_file" => \&create_bootenv_image); ++ ++# ---------------------------------------------------------------------------- ++# Helper ++ ++# @brief Get current time string. ++sub get_date { ++ my $tmp = scalar localtime; ++ $tmp =~ s/ /_/g; ++ return $tmp; ++} ++ ++# @brief Print an info message to stdout. ++sub INFO($) { ++ my $str = shift; ++ ++ if (!$verbose) { ++ return; ++ } ++ ++ print STDOUT $str; ++} ++ ++# @brief Print an error message to stderr. ++sub ERR($) { ++ my $str = shift; ++ print STDERR $str; ++} ++ ++# @brief Print a warning message to stderr. ++sub WARN($) { ++ my $str = shift; ++ print STDERR $str; ++} ++ ++sub parse_command_line($) { ++ my $opt = shift; ++ my $result = GetOptions( "help" => \$$opt{'help'}, ++ "man" => \$$opt{'man'}, ++ "config=s" => \$$opt{'config'}, ++ "verbose" => \$$opt{'verbose'}, ++ ) or pod2usage(2); ++ pod2usage(1) if defined ($$opt{help}); ++ pod2usage(-verbose => 2) if defined ($$opt{man}); ++ ++ $verbose = $$opt{verbose} if defined $$opt{verbose}; ++ ++ if (!defined $$opt{config}) { ++ ERR("[ ERROR: No config file specified. Aborting...\n"); ++ exit 1; ++ } ++ ++} ++ ++# @brief Check if all needed tools are in PATH. ++sub check_tools { ++ my $err = 0; ++ my $key; ++ ++ foreach $key (keys %tools) { ++ if (`which $tools{$key}` eq "") { ++ ERR("\n") if ($err == 0); ++ ERR("! Please add the tool \'$tools{$key}\' " . ++ "to your path!\n"); ++ $err = 1; ++ } ++ } ++ die "[ ERROR: Did not find all needed tools!\n" if $err; ++} ++ ++sub open_cfg_file($) { ++ my $fname = shift; ++ my $res = new Config::IniFiles( -file => $fname ); ++ ++ die "[ ERROR: Cannot load your config file!\n" if (!defined $res); ++ return $res; ++} ++ ++sub set_default($$$$) { ++ my ($cfg, $section, $parameter, $def_value) = @_; ++ $cfg->newval($section, $parameter, $def_value); ++ return; ++} ++ ++sub generate_label($$$$) { ++ my ($cfg, $section, $parameter, $def_value) = @_; ++ my $new_label = $def_value . $section; ++ $new_label .= "_" . get_date; ++ $cfg->newval($section, $parameter, $new_label); ++ return; ++} ++ ++# @brief Converts any num to a unified hex string, i.e the resulting value ++# always starts with "0x" and is aligned to 8 hexdigits. ++# @return Returns 0 on success, otherwise an error occured. ++# ++sub any_num_to_hex($$) { ++ my $val = shift; ++ my $res = shift; ++ ++ # M(iB) ++ if ($val =~ m/([0-9]+)[Mm][i]?[Bb]?/g) { ++ $$res = sprintf("0x%08x", $1 * 1024 * 1024); ++ } ++ # k(iB) ++ elsif ($val =~ m/([0-9]+)[kK][i]?[Bb]?/g) { ++ $$res = sprintf("0x%08x", $1 * 1024); ++ } ++ # hex ++ elsif ($val =~ m/0x?([0-9a-fA-F]+)/g) { ++ $$res = sprintf("0x%08x", hex $1); ++ } ++ # decimal ++ elsif ($val =~ m/^([0-9]+)$/g) { ++ $$res = sprintf("0x%08x", $1); ++ } ++ else { ++ $$res = ""; ++ return -1; ++ } ++ ++ return 0; ++} ++ ++sub remove_spaces($$$) { ++ my ($cfg, $section, $parameter) = @_; ++ my ($start, @starts, @new_starts); ++ my $val = $cfg->val($section, $parameter); ++ my $res; ++ ++ $val =~ s/ //g; # spaces ++ $cfg->newval($section, $parameter, $val); ++} ++ ++sub expand_starts($$$) { ++ my ($cfg, $section, $parameter) = @_; ++ my ($start, @starts, @new_starts); ++ my $val = $cfg->val($section, $parameter); ++ my $res; ++ ++ $val =~ s/ //g; # spaces ++ @starts = split(/,/, $val); ++ ++ foreach $start (@starts) { ++ if (any_num_to_hex($start, \$res) != 0) { ++ ERR("[ ERROR: [$section]\n"); ++ ERR("[ Expecting a list of numeric " . ++ "values for parameter: $parameter\n"); ++ exit 1; ++ } ++ push (@new_starts, $res); ++ } ++ $res = join(',', @starts); ++ ++ $cfg->newval($section, $parameter, $res); ++} ++ ++sub check_id_list($$$) { ++ my ($cfg, $section, $parameter) = @_; ++ my $val = $cfg->val($section, $parameter); ++ my $res; ++ ++ if (!($val =~ m/^[0-9]+[,0-9]*/)) { ++ ERR("[ ERROR: Syntax error in 'ubi_ids' in " . ++ "section '$section': $val\n"); ++ ERR("[ Aborting... "); ++ exit 1; ++ } ++} ++ ++sub replace_type($$$) { ++ my ($cfg, $section, $parameter) = @_; ++ my $val = $cfg->val($section, $parameter); ++ my $res; ++ ++ $res = lc($val); ++ grep {$res eq $_} ('static', 'dynamic') ++ or die "[ ERROR: Unknown UBI Volume Type in " . ++ "section '$section': $val\n"; ++ ++ $cfg->newval($section, $parameter, $res); ++} ++ ++ ++sub replace_num($$$) { ++ my ($cfg, $section, $parameter) = @_; ++ my $val = $cfg->val($section, $parameter); ++ my $res = ""; ++ ++ if (any_num_to_hex($val, \$res) != 0) { ++ ERR("[ ERROR: [$section]\n"); ++ ERR("[ Expecting a numeric value " . ++ "for parameter: $parameter\n"); ++ exit 1; ++ } ++ $cfg->newval($section, $parameter, $res); ++} ++ ++sub do_nothing($$$) { ++ my ($cfg, $section, $parameter) = @_; ++ return; ++} ++ ++sub bootenv_sanity_check($) { ++ my $env = shift; # hash array containing bootenv ++ my %pdd = (); ++ ++ defined($$env{'pdd'}) or return "'pdd' not defined"; ++ foreach (split /,/, $$env{'pdd'}) { ++ defined($$env{$_}) or return "undefined '$_' in pdd"; ++ $pdd{$_} = 1; ++ } ++ ++ defined $$env{'pdd_preserve'} or ++ return ""; ++ foreach (split /,/, $$env{'pdd_preserve'}) { ++ defined($pdd{$_}) ++ or return "pdd_preserve field '$_' not in pdd"; ++ } ++ return ""; ++} ++ ++sub create_bootenv_image($$$) { ++ my ($cfg, $section, $parameter) = @_; ++ my $txt_fn = $cfg->val($section, "bootenv_file"); ++ my $in; ++ ++ my %value = (); ++ my @key = (); ++ ++ open $in, "<", $txt_fn ++ or die "[ ERROR: can't open bootenv file '$txt_fn'.\n"; ++ while (<$in>) { ++ next if (/^\s*(\#.*)?$/); # Skip comments/whitespace. ++ ++ if (/^(\S+?)\+\=(.*)$/) { ++ defined($value{$1}) or ++ die "$txt_fn:$.: error: appending to" . ++ " non-existent '$1'\n"; ++ $value{$1} .= $2; ++ } elsif (/^(\S+?)\=(.*)$/) { ++ not defined($value{$1}) or ++ die "$txt_fn:$.: error: trying to" . ++ " redefine '$1'\n"; ++ push @key, $1; ++ $value{$1} = $2; ++ } else { ++ die "$txt_fn:$.: error: unrecognized syntax\n"; ++ } ++ } ++ close $in; ++ ++ $_ = &bootenv_sanity_check(\%value) ++ and die "$txt_fn: error: $_\n"; ++ ++ my $tmp_file = new File::Temp(); ++ push (@tmp_files, $tmp_file); ++ ++ foreach (@key) { ++ print $tmp_file "$_=", $value{$_}, "\0"; ++ } ++ close $tmp_file; ++ ++ $cfg->newval($section, "image", $tmp_file-> filename); ++} ++ ++sub process_keys($$$) { ++ my ($cfg, $section, $keys) = @_; ++ my @parameters = $cfg->Parameters($section); ++ my $i; ++ ++ for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { ++ if (defined($$keys{$parameters[$i]})) { ++ $$keys{$parameters[$i]}->($cfg, $section, ++ $parameters[$i]); ++ } ++ } ++ ++} ++ ++sub is_in_keylist($$) { ++ my ($key, $keys) = @_; ++ my $i; ++ ++ for ($i = 0; $i < scalar(@$keys); $i++) { ++ if ($$keys[$i] eq $key) { ++ return 1; ++ } ++ } ++ ++ return 0; ++} ++ ++sub check_default_keys($$$) { ++ my ($cfg, $section, $keys) = @_; ++ my @parameters = $cfg->Parameters($section); ++ my $key; ++ ++ foreach $key (keys %$keys) { ++ if (!is_in_keylist($key, \@parameters)) { ++ $$keys{$key}[0]-> ++ ($cfg, $section, $key, $$keys{$key}[1]); ++ } ++ } ++ ++} ++ ++ ++ ++sub check_keys($$$) { ++ my ($cfg, $section, $keys) = @_; ++ my @parameters = $cfg->Parameters($section); ++ my ($i, $key, $err); ++ ++ $err = 0; ++ for ($i = 0 ; $i < scalar(@$keys) ; $i++ ) { ++ if (!is_in_keylist($$keys[$i], \@parameters)) { ++ ERR("[ ERROR: [$section]\n") if $err == 0; ++ $err = 1; ++ ERR("[ Missing key '$$keys[$i]'\n"); ++ } ++ } ++ ++ if ($err) { ++ ERR("[ Aborting...\n"); ++ exit 1; ++ } ++} ++ ++sub push_pfi_data($$$$$) { ++ my ($cfg, $section, $pfi_infos, $keys, $mode) = @_; ++ my ($tmp, $i, $hdr); ++ ++ my %pfi_info = (); ++ $pfi_info{'mode'} = $mode; ++ $pfi_info{'image'} = $cfg->val($section, "image"); ++ ++ # Build the PFI header ++ $hdr = sprintf("PFI!\n"); ++ $hdr .= sprintf("version=0x%08x\n", hex $pfi_version); ++ $hdr .= sprintf("mode=$mode\n"); ++ ++ # calculate the size of the binary data part ++ $tmp = -s $cfg->val($section, "image"); ++ if (!defined $tmp) { ++ ERR("[ ERROR: [$section]\n"); ++ ERR("[ Missing input image: " ++ . $cfg->val($section, "image") . "\n"); ++ exit 1; ++ } ++ # Check for the image to fit into the given space ++ my $quota; ++ if ($mode eq 'raw') { ++ $quota = oct $cfg->val($section, "raw_total_size"); ++ } elsif ($mode eq 'ubi') { ++ $quota = oct $cfg->val($section, "ubi_size"); ++ } ++ $tmp <= $quota ++ or die "[ERROR: image file too big: " . ++ $cfg->val($section, "image") . "\n"; ++ $pfi_info{'size'} = $tmp; ++ ++ $hdr .= sprintf("size=0x%08x\n", $tmp); ++ ++ my $img_file = $cfg->val($section, "image"); ++ my $crc32 = `$tools{'ubicrc32'} $img_file 2>&1`; ++ if (any_num_to_hex($crc32, \$tmp) != 0) { ++ die "[ ERROR: $tools{'ubicrc32'} returned with errors"; ++ } ++ $hdr .= sprintf("crc=$tmp\n"); ++ ++ ++ # Process all remaining keys ++ for ($i = 0; $i < scalar (@$keys); $i++) { ++ if ($$keys[$i] eq "image") { # special case image input file ++ if (! -e ($tmp = $cfg->val($section, "image"))) { ++ ERR("[ ERROR: [$section]\n"); ++ ERR("[ Cannot find input file $tmp\n"); ++ exit 1; ++ } ++ next; ++ } ++ $hdr .= sprintf("%s=%s\n", $$keys[$i], ++ $cfg->val($section, $$keys[$i])); ++ } ++ ++ $hdr .= sprintf("\n"); # end marker for PFI-header ++ ++ $pfi_info{'header'} = $hdr; ++ ++ # store in the header list ++ push @$pfi_infos, \%pfi_info; ++} ++ ++sub process_section($$$$$$) { ++ my ($cfg, $section, $pfi_infos, $custom_keys, ++ $def_custom_keys, $mode) = @_; ++ my @keys = (keys %common_keys, keys %$custom_keys); ++ my @complete_keys = (@keys, keys %input_keys); ++ ++ # set defaults if necessary ++ check_default_keys($cfg, $section, $def_custom_keys); ++ check_default_keys($cfg, $section, \%def_common_keys); ++ ++ # check for placeholders... ++ process_keys($cfg, $section, \%input_placeholder_keys); ++ ++ # VALIDATE layout.cfg entries ++ check_keys($cfg, $section, \@complete_keys); ++ ++ # execute linked functions (if any) ++ process_keys($cfg, $section, \%common_keys); ++ process_keys($cfg, $section, $custom_keys); ++ ++ push_pfi_data($cfg, $section, $pfi_infos, \@keys, $mode); ++} ++ ++sub get_section_info($$) { ++ my ($cfg, $section) = @_; ++ my @parameters = $cfg->Parameters($section); ++ my ($ubi, $raw, $i, @res); ++ ++ $ubi = $raw = 0; ++ for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { ++ if ($parameters[$i] =~ m/ubi_/gi) { ++ $ubi = 1; ++ @res = (\%ubi_keys, \%def_ubi_keys, "ubi"); ++ } ++ if ($parameters[$i] =~ m/raw_/gi) { ++ $raw = 1; ++ @res = (\%raw_keys, \%def_raw_keys, "raw"); ++ } ++ } ++ ++ if (($ubi + $raw) != 1) { # double definition in section ++ ERR("[ ERROR: Layout error in section '$section'\n"); ++ exit 1; ++ } ++ ++ return @res; ++} ++ ++sub mk_target_list($$) { ++ my $val = shift; ++ my $tmp = shift; ++ my $complete = 0; ++ ++ if ($val =~ m/\((.*)\)/g) { ++ $val = $1; ++ $complete = 1; ++ } ++ $val =~ s/ //g; # spaces ++ ++ @$tmp = split(/,/, $val); ++ ++ return $complete; ++} ++ ++sub copy_bytes($$$) { ++ my ($in, $out, $to_copy) = @_; ++ ++ while ($to_copy) { ++ my $buf; ++ my $bufsize = 1024*1024; ++ ++ $bufsize < $to_copy or $bufsize = $to_copy; ++ read($in, $buf, $bufsize) == $bufsize ++ or die "[ ERROR: Image file shrunk during operation\n"; ++ print $out $buf; ++ $to_copy -= $bufsize; ++ } ++} ++ ++sub write_target($$) { ++ my ($pfi_infos, $target) = @_; ++ my ($pfi_info); ++ ++ INFO("[ Writting target pfi file: '$target.pfi'...\n"); ++ if (-e "$target.pfi") { ++ WARN("! Replaced old pfi...\n"); ++ `rm -f $target.pfi`; ++ } ++ open(FILE, ">", "$target.pfi") ++ or die "[ ERROR: Cannot create output file: $target.pfi\n"; ++ binmode(FILE); ++ ++ # @FIXME sort by mode (first raw, then ubi) ++ # Currently this ordering is based on a string comparism. :-) ++ @$pfi_infos = sort {(lc $$a{'mode'}) cmp (lc $$b{'mode'})} @$pfi_infos; ++ ++ # Print all headers first ++ foreach $pfi_info (@$pfi_infos) { ++ print FILE $$pfi_info{'header'}; ++ ++ } ++ # Print the linked data sections ++ print FILE "DATA\n"; ++ foreach $pfi_info (@$pfi_infos) { ++ open(IMAGE, "<", $$pfi_info{'image'}) ++ or die "[ ERROR: Cannot open input image: " . ++ "$$pfi_info{'image'}" . "\n"; ++ binmode(IMAGE); ++ ©_bytes(\*IMAGE, \*FILE, $$pfi_info{'size'}); ++ close(IMAGE) or die "[ ERROR: Cannot close input image: " . ++ "$$pfi_info{'image'}" . "\n"; ++ } ++ close(FILE) or die "[ ERROR: Cannot close output file: $target.pfi\n"; ++} ++ ++sub process_config($) { ++ my $cfg = shift; ++ my @sections = $cfg->Sections; ++ my ($i, $j, $keylist, $def_keylist, $mode, $tmp, ++ @tlist, $complete,@pfi_infos); ++ ++ my @parameters = $cfg->Parameters("targets") or ++ die "[ ERROR: Config file has no 'targets' section!\n"; ++ ++ for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { ++ INFO("[ Processing target '$parameters[$i]'...\n"); ++ @pfi_infos = (); ++ ++ # get a list of subtargets ++ $complete = mk_target_list($cfg->val("targets", ++ $parameters[$i]), \@tlist); ++ # build all subtargets ++ for ($j = 0 ; $j < scalar(@tlist) ; $j++ ) { ++ ($keylist, $def_keylist, $mode) ++ = get_section_info($cfg, $tlist[$j]); ++ process_section($cfg, $tlist[$j], ++ \@pfi_infos, ++ $keylist, $def_keylist, $mode); ++ } ++ ++ write_target(\@pfi_infos, $parameters[$i]); ++ } ++ ++ INFO("[ Success.\n"); ++ ++ ++} ++ ++sub clear_files() { ++ # @FIXME: ++ # Works implicitly and Fedora seems to have removed ++ # the cleanup call. Thus for now, inactive. ++ # File::Temp::cleanup(); ++} ++ ++require 5.008_000; # Tested with version 5.8.0. ++select STDOUT; $| = 1; # make STDOUT output unbuffered ++select STDERR; $| = 1; # make STDERR output unbuffered ++ ++parse_command_line(\%opts); ++check_tools; ++$cfg = open_cfg_file($opts{config}); ++process_config($cfg); ++clear_files; ++ ++__END__ ++ ++ ++=head1 NAME ++ ++mkpfi - Using GetOpt::Long, Pod::Usage, Config::IniFiles ++ ++ ++=head1 SYNOPSIS ++ ++mkpfi [OPTIONS ...] ++ ++ ++ OPTION ++ ++ [--config] [--help] [--man] ++ ++ ++=head1 ABSTRACT ++ ++Perl script for generating pdd pfi files from given config files. ++ ++=head1 OPTIONS ++ ++=over ++ ++=item B<--help> ++ ++Print out brief help message. ++ ++=item B<--usage> ++ ++Print usage. ++ ++=item B<--config> ++ ++Config input file. ++ ++=item B<--man> ++ ++Print manual page, same as 'perldoc mkpfi'. ++ ++=item B<--verbose> ++ ++Be verbose! ++ ++=back ++ ++=head1 BUGS ++ ++Report via MTD mailing list ++ ++ ++=head1 SEE ALSO ++ ++http://www.linux-mtd.infradead.org/ ++ ++ ++=head1 AUTHOR ++ ++Oliver Lohmann (oliloh@de.ibm.com) ++ ++=cut +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/perl/ubicrc32.pl linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/perl/ubicrc32.pl +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/perl/ubicrc32.pl 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/perl/ubicrc32.pl 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,74 @@ ++#!/usr/bin/perl -w ++ ++# Subroutine crc32(): Calculates the CRC on a given string. ++ ++{ ++ my @table = (); ++ ++ # @brief Calculate CRC32 for a given string. ++ sub crc32 ++ { ++ unless (@table) { ++ # Initialize the CRC table ++ my $poly = 0xEDB88320; ++ @table = (); ++ ++ for my $i (0..255) { ++ my $c = $i; ++ ++ for my $j (0..7) { ++ $c = ($c & 1) ? (($c >> 1) ^ $poly) : ($c >> 1); ++ } ++ $table[$i] = $c; ++ } ++ } ++ my $s = shift; # string to calculate the CRC for ++ my $crc = shift; # CRC start value ++ ++ defined($crc) ++ or $crc = 0xffffffff; # Default CRC start value ++ ++ for (my $i = 0; $i < length($s); $i++) { ++ $crc = $table[($crc ^ ord(substr($s, $i, 1))) & 0xff] ++ ^ ($crc >> 8); ++ } ++ return $crc; ++ } ++} ++ ++sub crc32_on_file ++{ ++ my $file = shift; ++ ++ my $crc32 = crc32(''); ++ my $buf = ''; ++ my $ret = 0; ++ ++ while ($ret = read($file, $buf, 8192)) { ++ $crc32 = crc32($buf, $crc32); ++ } ++ defined($ret) ++ or return undef; ++ printf("0x%x\n", $crc32); ++} ++ ++ ++# Main routine: Calculate the CRCs on the given files and print the ++# results. ++ ++{ ++ if (@ARGV) { ++ while (my $path = shift) { ++ my $file; ++ open $file, "<", $path ++ or die "Error opening '$path'.\n"; ++ ++ &crc32_on_file($file) ++ or die "Error reading from '$path'.\n"; ++ close $file; ++ } ++ } else { ++ &crc32_on_file(\*STDIN) ++ or die "Error reading from stdin.\n"; ++ } ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/Makefile linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/Makefile +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,75 @@ ++# ++# Makefile ++# ++# Testcase for UBI pfi update. ++# ++# Author: Frank Haverkamp ++# ++ ++card = test ++mkpfi_cfg = test.cfg ++ ++# ++# Some default values you might want to overwrite. Try it if you need ++# it and add more if needed. Note that no real sanity checking is done ++# on those values. If you do it wrong your card has no valid PDD data. ++# ++ ++PATH := $(PATH):/opt/ppc/usr/bin:../perl:.. ++ ++dd = dd ++sed = sed ++bin2nand = bin2nand ++ubigen = ubigen ++mkpfi = mkpfi -v ++pfi2bin = pfi2bin -v ++ ++vmlinux_bin ?= test_vmlinux.bin ++rootfs_bin ?= test_rootfs.bin ++spl_bin ?= test_u-boot.bin ++pdd_txt ?= pdd.txt ++ ++flashtype ?= nand ++pagesize ?= 2048 ++ ++compl ?= $(card)_complete ++compl_pfi ?= $(compl).pfi ++compl_img ?= $(compl).img ++ ++compl_nand2048_mif=$(compl).$(flashtype)$(pagesize).mif ++compl_nand2048_img=$(compl).$(flashtype)$(pagesize).img ++ ++all: $(compl_pfi) $(compl_nand2048_mif) ++ ++$(compl_pfi): $(vmlinux_bin) $(rootfs_bin) $(spl_bin) ++ $(mkpfi) -c $(mkpfi_cfg) ++ ++# Binary data and out of band data (OOB) ++# ++$(compl_nand2048_mif): $(compl_img) ++ $(bin2nand) -p $(pagesize) -o $(compl_nand2048_mif) $< ++ ++# Binary data only ++# ++$(compl_img): $(compl_pfi) ++ $(pfi2bin) -j $(pdd_txt) -o $@ $< ++ ++# ++# Default data ++# ++# If the binary data is not available in the current working directory ++# we try to create symlinks to our test data. ++# ++$(vmlinux_bin) $(rootfs_bin) $(spl_bin): ++ @echo ++ @echo "No $@ found, will use defaults !" ++ @echo ++ @echo "OR press CTRL-C to provide your own $@" && \ ++ sleep 1 && \ ++ $(dd) if=/dev/urandom of=$@ bs=1M count=1 ++ ++clean: ++ $(RM) *.pfi *~ ++ ++distclean: clean ++ $(RM) *.bin *.mif *.oob *.img +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/README linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/README +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/README 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/README 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,11 @@ ++README ++====== ++ ++This procedure creates a test pfi which should be flashed to our ++system with pfiflash. The testcase should read the data back and ++compare with the original. ++ ++We should try not forget to run these tests before we release ++a new version of UBI. ++ ++Frank +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/TODO linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/TODO +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/TODO 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/TODO 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,5 @@ ++TODO ++==== ++ ++ * Range checking is broken, reserving 2M and offering 3M binary data ++ ... works!? No! +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/bin2nand2bin_test.sh linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/bin2nand2bin_test.sh +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/bin2nand2bin_test.sh 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/bin2nand2bin_test.sh 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,184 @@ ++#!/bin/sh ++# ++# Testcase for nand2bin and bin2nand. Generate testdata and inject ++# biterrors. Convert data back and compare with original data. ++# ++# Conversion: ++# bin -> bin2nand -> mif -> nand2bin -> img ++# ++ ++inject_biterror=./scripts/inject_biterror.pl ++ ++pagesize=2048 ++oobsize=64 ++ ++# Create test data ++dd if=/dev/urandom of=testblock.bin bs=131072 count=1 ++ ++echo "Test conversion without bitflips ..." ++ ++echo -n "Convert bin to mif ... " ++bin2nand --pagesize=${pagesize} -o testblock.mif testblock.bin ++if [ $? -ne "0" ]; then ++ echo "failed!" ++ exit 1 ++else ++ echo "ok" ++fi ++ ++echo -n "Convert mif to bin ... " ++nand2bin --pagesize=${pagesize} -o testblock.img testblock.mif ++if [ $? -ne "0" ]; then ++ echo "failed!" ++ exit 1 ++else ++ echo "ok" ++fi ++ ++echo -n "Comparing data ... " ++diff testblock.bin testblock.img ++if [ $? -ne "0" ]; then ++ echo "failed!" ++ exit 1 ++else ++ echo "ok" ++fi ++ ++echo "Test conversion with uncorrectable ECC erors ..." ++echo -n "Inject biterror at offset $ioffs ... " ++${inject_biterror} --offset=0 --bitmask=0x81 \ ++ --input=testblock.mif \ ++ --output=testblock_bitflip.mif ++if [ $? -ne "0" ]; then ++ echo "failed!" ++ exit 1 ++else ++ echo "ok" ++fi ++ ++echo "Convert mif to bin ... " ++rm testblock.img ++nand2bin --correct-ecc --pagesize=${pagesize} -o testblock.img \ ++ testblock_bitflip.mif ++if [ $? -ne "0" ]; then ++ echo "failed!" ++ exit 1 ++else ++ echo "ok" ++fi ++ ++echo -n "Comparing data, must fail due to uncorrectable ECC ... " ++diff testblock.bin testblock.img ++if [ $? -ne "0" ]; then ++ echo "ok" # Must fail! ++else ++ echo "failed!" ++ exit 1 ++fi ++ ++echo "Test bitflips in data ... " ++for offs in `seq 0 255` ; do ++ ++ cp testblock.mif testblock_bitflip.mif ++ ++ for xoffs in 0 256 512 768 ; do ++ let ioffs=$offs+$xoffs ++ ++ cp testblock_bitflip.mif testblock_bitflip_tmp.mif ++ echo -n "Inject biterror at offset $ioffs ... " ++ ${inject_biterror} --offset=${ioffs} --bitmask=0x01 \ ++ --input=testblock_bitflip_tmp.mif \ ++ --output=testblock_bitflip.mif ++ if [ $? -ne "0" ]; then ++ echo "failed!" ++ exit 1 ++ else ++ echo "ok" ++ fi ++ done ++ ++ echo "Convert mif to bin ... " ++ rm testblock.img ++ nand2bin --correct-ecc --pagesize=${pagesize} -o testblock.img \ ++ testblock_bitflip.mif ++ if [ $? -ne "0" ]; then ++ echo "failed!" ++ exit 1 ++ else ++ echo "ok" ++ fi ++ ++ echo -n "Comparing data ... " ++ diff testblock.bin testblock.img ++ if [ $? -ne "0" ]; then ++ hexdump testblock.bin > testblock.bin.txt ++ hexdump testblock.img > testblock.img.txt ++ echo "Use tkdiff testblock.bin.txt testblock.img.txt to compare" ++ echo "failed!" ++ exit 1 ++ else ++ echo "ok" ++ fi ++ ++ # Without correction ++ echo "Convert mif to bin ... " ++ rm testblock.img ++ nand2bin --pagesize=${pagesize} -o testblock.img \ ++ testblock_bitflip.mif ++ if [ $? -ne "0" ]; then ++ echo "failed!" ++ exit 1 ++ else ++ echo "ok" ++ fi ++ ++ echo -n "Comparing data must differ, correction is disabled ... " ++ diff testblock.bin testblock.img ++ if [ $? -ne "0" ]; then ++ echo "ok" # must fail ++ else ++ echo "failed!" ++ exit 1 ++ fi ++done ++ ++echo "Test bitflips in OOB data ... " ++for offs in `seq 0 $oobsize` ; do ++ ++ let ioffs=$pagesize+$offs ++ ++ echo -n "Inject biterror at offset $ioffs ... " ++ ${inject_biterror} --offset=${ioffs} --bitmask=0x01 \ ++ --input=testblock.mif \ ++ --output=testblock_bitflip.mif ++ if [ $? -ne "0" ]; then ++ echo "failed!" ++ exit 1 ++ else ++ echo "ok" ++ fi ++ ++ echo "Convert mif to bin ... " ++ rm testblock.img ++ nand2bin --correct-ecc --pagesize=${pagesize} -o testblock.img \ ++ testblock_bitflip.mif ++ if [ $? -ne "0" ]; then ++ echo "failed!" ++ exit 1 ++ else ++ echo "ok" ++ fi ++ ++ echo -n "Comparing data ... " ++ diff testblock.bin testblock.img ++ if [ $? -ne "0" ]; then ++ hexdump testblock.bin > testblock.bin.txt ++ hexdump testblock.img > testblock.img.txt ++ echo "Use tkdiff testblock.bin.txt testblock.img.txt to compare" ++ echo "failed!" ++ exit 1 ++ else ++ echo "ok" ++ fi ++done ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/inject_biterror.pl linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/inject_biterror.pl +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/inject_biterror.pl 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/inject_biterror.pl 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,94 @@ ++#!/usr/bin/perl -w ++# ++# 2007 Frank Haverkamp ++# ++# Program for bit-error injection. I am sure that perl experts do it ++# in 1 line. Please let me know how it is done right ;-). ++# ++ ++use strict; ++use warnings; ++use Getopt::Long; ++use Pod::Usage; ++ ++my $i; ++my $help; ++my $result; ++my $offset = 0; ++my $bitmask = 0x01; ++my $in = "input.mif"; ++my $out = "output.mif"; ++ ++$result = GetOptions ("offset=i" => \$offset, # numeric ++ "bitmask=o" => \$bitmask, # numeric ++ "input=s" => \$in, # string ++ "output=s" => \$out, # string ++ "help|?" => \$help) or pod2usage(2); ++ ++pod2usage(1) if $help; ++ ++my $buf; ++ ++open(my $in_fh, "<", $in) ++ or die "Cannot open file $in: $!"; ++binmode $in_fh; ++ ++open(my $out_fh, ">", $out) or ++ die "Cannot open file $out: $!"; ++binmode $out_fh; ++ ++$i = 0; ++while (sysread($in_fh, $buf, 1)) { ++ ++ $buf = pack('C', unpack('C', $buf) ^ $bitmask) if ($i == $offset); ++ syswrite($out_fh, $buf, 1) or ++ die "Cannot write to offset $offset: $!"; ++ $i++; ++} ++ ++close $in_fh; ++close $out_fh; ++ ++__END__ ++ ++=head1 NAME ++ ++inject_biterrors.pl ++ ++=head1 SYNOPSIS ++ ++inject_biterror.pl [options] ++ ++=head1 OPTIONS ++ ++=over 8 ++ ++=item B<--help> ++ ++Print a brief help message and exits. ++ ++=item B<--offset>=I ++ ++Byte-offset where bit-error should be injected. ++ ++=item B<--bitmask>=I ++ ++Bit-mask where to inject errors in the byte. ++ ++=item B<--input>=I ++ ++Input file. ++ ++=item B<--output>=I ++ ++Output file. ++ ++=back ++ ++=head1 DESCRIPTION ++ ++B will read the given input file and inject ++biterrors at the I specified. The location of the biterrors ++are defined by the I parameter. ++ ++=cut +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/jffs2_test.sh linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/jffs2_test.sh +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/jffs2_test.sh 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/jffs2_test.sh 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,91 @@ ++#!/bin/sh ++# ++# Testcase for JFFS2 verification. We do not want to see any ++# kernel errors occuring when this is executed. ++# ++# ++# To have a standardized output I define the following function to be ++# used when a test was ok or when it failed. ++# ++failed () ++{ ++ echo "FAILED" ++} ++ ++passed () ++{ ++ echo "PASSED" ++} ++ ++# ++# Print sucess message. Consider to exit with zero as return code. ++# ++exit_success () ++{ ++ echo "SUCCESS" ++ exit 0 ++} ++ ++# ++# Print failure message. Consider to exit with non zero return code. ++# ++exit_failure () ++{ ++ echo "FAILED" ++ exit 1 ++} ++ ++echo "***********************************************************************" ++echo "* jffs2 testing ... *" ++echo "***********************************************************************" ++ ++ulimit -c unlimited ++ ++for i in `seq 5000`; do ++ echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... " ++ dd if=/dev/urandom of=test.bin bs=$i count=1; ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ echo "Copy to different file ... " ++ dd if=test.bin of=new.bin bs=$i count=1; ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ echo "Comparing files ... " ++ cmp test.bin new.bin ++ dd if=test.bin of=new.bin bs=$i count=1; ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++done ++ ++for i in `seq 5000`; do ++ echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... " ++ dd if=/dev/urandom of=foo bs=$i count=1; ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++done ++ ++for i in `seq 5000`; do ++ echo "Testing $i byte (dd if=/dev/zero of=foo bs=$i count=1) ... " ++ dd if=/dev/zero of=foo bs=$i count=1; ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++done ++ ++echo "***********************************************************************" ++echo "* Congratulations, no errors found! *" ++echo "* Have fun with your cool JFFS2 using system! *" ++echo "***********************************************************************" ++ ++exit_success +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/mkdevs.pl linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/mkdevs.pl +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/mkdevs.pl 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/mkdevs.pl 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,32 @@ ++#!/usr/bin/perl -w ++ ++# ++# Author: Artem B. Bityutskiy ++# ++# A small scrip which creates UBI device nodes in /dev. UBI allocates ++# major number dynamically, so the script looks at /proc/devices to find ++# out UBI's major number. ++# ++ ++ ++my $proc = '/proc/devices'; ++my $regexp = '(\d+) (ubi\d+)$'; ++ ++ ++open FILE, "<", $proc or die "Cannot open $proc file: $!\n"; ++my @file = ; ++close FILE; ++ ++foreach (@file) { ++ next if not m/$regexp/g; ++ print "found $2\n"; ++ ++ system("rm -rf /dev/$2"); ++ system("mknod /dev/$2 c $1 0"); ++ ++ for (my $i = 0; $i < 128; $i += 1) { ++ system("rm -rf /dev/$2_$i"); ++ my $j = $i + 1; ++ system("mknod /dev/$2_$i c $1 $j"); ++ } ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/pdd.txt linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/pdd.txt +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/pdd.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/pdd.txt 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,16 @@ ++pdd=flash_type,flash_size,flash_eraseblock_size,flash_page_size,card_serialnumber,card_type,ethaddr,eth1addr,eth0,eth1,total,card_hardwarelevel ++pdd_preserve=ethaddr,eth1addr,card_serialnumber ++# To be personalized ++ethaddr=00:04:34:56:78:9A ++eth1addr=00:04:34:56:78:9B ++card_serialnumber=SN0 ++# Static for this card type ++total=102M ++card_type=nand_driven_testcard ++card_hardwarelevel=0 ++eth0=bcm5222,eth0,0 ++eth1=bcm5222,eth0,1 ++flash_type=NAND ++flash_size=0x08000000 ++flash_eraseblock_size=0x00020000 ++flash_page_size=0x00000800 +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/run_all.sh linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/run_all.sh +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/run_all.sh 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/run_all.sh 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,101 @@ ++#!/bin/sh ++ ++exit_success () ++{ ++ echo "UBI Utils Test Scripts - SUCCESS!" ++ exit 0 ++} ++ ++exit_failure () ++{ ++ echo $1 ++ echo "UBI Utils Test Scripts - FAILED!" ++ exit 1 ++} ++ ++echo UBI Utils Test Scripts ++ ++devno=$1 ++logfile=temp-test-log.txt ++ ++if test -z "$devno"; ++then ++ echo "Usage is $0 " ++ exit 1 ++fi ++ ++cwd=`pwd` || exit_failure "pwd failed" ++ ++log="${cwd}/${logfile}" ++ ++PATH=$PATH:$cwd:.. ++ ++cat /dev/null > $log || exit_failure "Failed to create $log" ++ ++echo "Setting up for jffs2_test.sh" | tee -a $log ++ ++avail=`cat /sys/class/ubi/ubi${devno}/avail_eraseblocks` ++size=`cat /sys/class/ubi/ubi${devno}/eraseblock_size` ++ ++bytes=`expr $avail \* $size` ++ ++ubimkvol -d$devno -s$bytes -n0 -Njtstvol || exit_failure "ubimkvol failed" ++ ++mkdir -p /mnt/test_file_system || exit_failure "mkdir failed" ++ ++mtd=`cat /proc/mtd | grep jtstvol | cut -d: -f1` ++ ++if test -z "$mtd"; ++then ++ exit_failure "mtd device not found" ++fi ++ ++mount -t jffs2 $mtd /mnt/test_file_system || exit_failure "mount failed" ++ ++cd /mnt/test_file_system || exit_failure "cd failed" ++ ++echo Running jffs2_test.sh | tee -a $log ++ ++jffs2_test.sh >> $log 2>&1 || exit_failure "jffs2_test.sh failed" ++ ++rm -f * ++ ++cd $cwd || exit_failure "cd failed" ++ ++umount /mnt/test_file_system || exit_failure "umount failed" ++ ++ubirmvol -d$devno -n0 || exit_failure "ubirmvol failed" ++ ++major=`cat /sys/class/ubi/ubi${devno}/dev | cut -d: -f1` ++ ++for minor in `seq 0 32`; do ++ if test ! -e /dev/ubi${devno}_$minor ; ++ then ++ mknod /dev/ubi${devno}_$minor c $major $(($minor + 1)) ++ fi ++done ++ ++rm -f testdata.bin readdata.bin ++ ++echo Running ubi_jffs2_test.sh | tee -a $log ++ ++ubi_jffs2_test.sh >> $log 2>&1 || exit_failure "ubi_jffs2_test.sh failed" ++ ++echo Running ubi_test.sh | tee -a $log ++ ++ubi_test.sh >> $log 2>&1 || exit_failure "ubi_test.sh failed" ++ ++for minor in `seq 0 32`; do ++ if test -e /sys/class/ubi/ubi${devno}/$minor; ++ then ++ ubirmvol -d$devno -n$minor || exit_failure "ubirmvol failed" ++ fi ++done ++ ++echo Running ubi_tools_test.sh | tee -a $log ++ ++ubi_tools_test.sh >> $log 2>&1 || exit_failure "ubi_tools_test failed" ++ ++rm -f $log ++ ++exit_success +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/test.cfg linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/test.cfg +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/test.cfg 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/test.cfg 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,23 @@ ++[targets] ++test_complete=spl,kernel,rootfs ++ ++[spl] ++image=test_u-boot.bin ++ubi_ids=10,11 ++ubi_size=1MiB ++ubi_type=static ++ubi_names=test_spl_0,test_spl_1 ++ ++[kernel] ++image=test_vmlinux.bin ++ubi_ids=12,13 ++ubi_size=2MiB ++ubi_type=static ++ubi_names=test_kernel_0,test_kernel_1 ++ ++[rootfs] ++image=test_rootfs.bin ++ubi_ids=14,15 ++ubi_size=2MiB ++ubi_type=dynamic ++ubi_names=test_rootfs_0,test_rootfs_1 +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_jffs2_test.sh linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_jffs2_test.sh +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_jffs2_test.sh 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_jffs2_test.sh 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,411 @@ ++#!/bin/sh ++# ++# UBI Volume creation/deletion/write/read and JFFS2 on top of UBI ++# testcases. ++# ++# Written in shell language to reduce dependencies to more sophisticated ++# interpreters, which may not be available on some stupid platforms. ++# ++# Author: Frank Haverkamp ++# ++# 1.0 Initial version ++# 1.1 Added fixup for delayed device node creation by udev ++# This points to a problem in the tools, mabe in the desing ++# Tue Oct 31 14:14:54 CET 2006 ++# ++ ++VERSION="1.1" ++ ++export PATH=$PATH:/bin:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/ ++ ++ITERATIONS=250 ++ALIGNMENT=2048 ++ ++UBIMKVOL="ubimkvol -a $ALIGNMENT" ++UBIRMVOL=ubirmvol ++UBIUPDATEVOL=ubiupdatevol ++ ++SIZE_512K=524288 ++SIZE_1M=1310720 ++ ++MINVOL=10 ++MAXVOL=12 ++ ++TLOG=/dev/null ++ ++# ++# To have a standardized output I define the following function to be ++# used when a test was ok or when it failed. ++# ++failed () ++{ ++ echo "FAILED" ++} ++ ++passed () ++{ ++ echo "PASSED" ++} ++ ++# ++# Print sucess message. Consider to exit with zero as return code. ++# ++exit_success () ++{ ++ echo "SUCCESS" ++ exit 0 ++} ++ ++# ++# Print failure message. Consider to exit with non zero return code. ++# ++exit_failure () ++{ ++ echo "FAILED" ++ exit 1 ++} ++ ++############################################################################### ++# ++# START ++# ++############################################################################### ++ ++fix_sysfs_issue () ++{ ++ echo "*** Fixing the sysfs issue with the /dev nodes ... " ++ ++ minor=0 ++ major=`grep ubi0 /proc/devices | sed -e 's/\(.*\) ubi0/\1/'` ++ ++ rm -rf /dev/ubi0 ++ mknod /dev/ubi0 c $major 0 ++ ++ for minor in `seq $MINVOL $MAXVOL`; do ++ echo " -> mknod /dev/ubi0_$minor c $major $(($minor + 1))" ++ rm -rf /dev/ubi0_$minor ++ mknod /dev/ubi0_$minor c $major $(($minor + 1)) ++ done ++ passed ++} ++ ++# ++# FIXME Udev needs some time until the device nodes are created. ++# This will cause trouble if after ubimkvol an update attempt ++# is started immediately, since the device node is not yet ++# available. We should either fix the tools with inotify or ++# other ideas or figure out a different way to solve the problem ++# e.g. to use ubi0 and make the volume device nodes obsolete... ++# ++udev_wait () ++{ ++ echo -n "FIXME Waiting for udev to create/delete device node " ++ grep 2\.6\.5 /proc/version > /dev/null ++ if [ $? -eq "0" ]; then ++ for i in `seq 0 5`; do ++ sleep 1; echo -n "."; ++ done ++ echo " ok" ++ fi ++} ++ ++# delete_volume - Delete a volume. If it does not exist, do not try ++# to delete it. ++# @id: volume id ++# ++delete_volume () ++{ ++ volume=$1 ++ ++ ### FIXME broken sysfs!!!! ++ if [ -e /sys/class/ubi/$volume -o \ ++ -e /sys/class/ubi/ubi0/$volume -o \ ++ -e /sys/class/ubi/ubi0_$volume ]; then ++ ++ echo "*** Truncate volume if it exists ... " ++ echo " $UBIUPDATEVOL -d0 -n$volume -t" ++ $UBIUPDATEVOL -d0 -n$volume -t ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ echo -n "*** Delete volume if it exists ... " ++ $UBIRMVOL -d0 -n$volume ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ # udev_wait ++ fi ++} ++ ++# writevol_test - Tests volume creation and writing data to it. ++# ++# @volume: Volume number ++# @size: Size of random data to write ++# @type: Volume type static or dynamic ++# ++writevol_test () ++{ ++ volume=$1 ++ size=$2 ++ type=$3 ++ ++ echo "*** Write volume test with size $size" ++ ++### Make sure that volume exist, delete existing volume, create new ++ ++ delete_volume $volume ++ ++ echo "*** Try to create volume" ++ echo " $UBIMKVOL -d0 -n$volume -t$type -NNEW$volume -s $size ... " ++ $UBIMKVOL -d0 -n$volume -t$type -N"NEW$volume" -s $size ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ udev_wait ++ ++### Try to create same volume again ++ echo -n "*** Try to create some volume again, this must fail ... " ++ $UBIMKVOL -d0 -n$volume -t$type -N"NEW$volume" -s $size ++ if [ $? -eq "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++### Now create test data, write it, read it, compare it ++ echo -n "*** Create test data ... " ++ dd if=/dev/urandom of=testdata.bin bs=$size count=1 ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ echo "*** Now writing data to volume ... " ++ echo " $UBIUPDATEVOL -d0 -n$volume testdata.bin" ++ ls -l testdata.bin ++ $UBIUPDATEVOL -d0 -n$volume testdata.bin ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ echo "*** Download data with dd bs=1 ... " ++ dd if=/dev/ubi0_$volume of=readdata.bin bs=$size count=1 ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ echo -n "*** Comparing data ... " ++ cmp readdata.bin testdata.bin ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ echo -n "*** Now truncate volume ... " ++ $UBIUPDATEVOL -d0 -n$volume -t ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++} ++ ++jffs2_torture () ++{ ++ cat /dev/null > TLOG ++ ++ echo "*** Torture test ... " ++ ++ for i in `seq $iterations`; do ++ dd if=/dev/urandom of=test.bin bs=$i count=1 2>> $TLOG ++ if [ $? -ne "0" ] ; then ++ echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... " ++ exit_failure ++ fi ++ #passed ++ ++ dd if=test.bin of=new.bin bs=$i count=1 2>> $TLOG ++ if [ $? -ne "0" ] ; then ++ echo "dd if=test.bin of=new.bin bs=$i count=1 2>> $TLOG" ++ exit_failure ++ fi ++ #passed ++ ++ #echo "Comparing files ... " ++ cmp test.bin new.bin ++ dd if=test.bin of=new.bin bs=$i count=1 2>> $TLOG ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ #passed ++ #echo -n "." ++ done ++ ++ echo -n "step0:ok " ++ ++ for i in `seq $iterations`; do ++ dd if=/dev/urandom of=foo bs=$i count=1 2>> $TLOG ++ if [ $? -ne "0" ] ; then ++ echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... " ++ exit_failure ++ fi ++ #passed ++ done ++ ++ echo -n "step1:ok " ++ ++ for i in `seq $iterations`; do ++ dd if=/dev/zero of=foo bs=1 count=$i 2>> $TLOG ++ if [ $? -ne "0" ] ; then ++ echo "Testing $i byte (dd if=/dev/zero of=foo bs=1 count=$i) ... " ++ exit_failure ++ fi ++ #passed ++ done ++ ++ echo -n "step2:ok " ++ ++ for i in `seq $iterations`; do ++ dd if=/dev/zero of=foo bs=$i count=16 2>> $TLOG ++ if [ $? -ne "0" ] ; then ++ echo "Testing $i byte (dd if=/dev/zero of=foo bs=$i count=1024) ... " ++ exit_failure ++ fi ++ #passed ++ done ++ ++ echo -n "step3:ok " ++ ++ passed ++} ++ ++# writevol_test - Tests volume creation and writing data to it. ++# ++# @volume: Volume number ++# @size: Size of random data to write ++# @type: Volume type static or dynamic ++# ++jffs2_test () ++{ ++ name=$1 ++ iterations=$2 ++ directory=`pwd` ++ ++ ### Setup ++ ulimit -c unlimited ++ ++ echo -n "*** Create directory /mnt/$name ... " ++ mkdir -p /mnt/$name ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ echo -n "*** mount -t jffs2 mtd:$name /mnt/$name ... " ++ mount -t jffs2 mtd:$name /mnt/$name ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ echo -n "*** change directory ... " ++ cd /mnt/$name ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ ls ++ echo "*** list directory ... " ++ ls -la ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ ### Torture ++ echo -n "*** touch I_WAS_HERE ... " ++ touch I_WAS_HERE ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ jffs2_torture ++ ++ echo "*** list directory ... " ++ ls -la ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ ### Cleanup ++ echo -n "*** go back ... " ++ cd $directory ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ ### Still mounted, ubiupdatevol must fail! ++ ++ echo -n "*** $UBIUPDATEVOL -d0 -n$volume -t must fail! ..." ++ $UBIUPDATEVOL -d0 -n$volume -t ++ if [ $? -eq "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ echo -n "*** umount /mnt/$name ... " ++ umount /mnt/$name ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ return ++} ++ ++echo "***********************************************************************" ++echo "* UBI JFFS2 Testing starts now ... *" ++echo "* Good luck! *" ++echo "***********************************************************************" ++echo "VERSION: $VERSION" ++ ++# Set to zero if not running on example hardware ++grep ubi /proc/devices > /dev/null ++if [ $? -ne "0" ]; then ++ echo "No UBI found in /proc/devices! I am broken!" ++ exit_failure ++fi ++ ++# Set to zero if not running on example hardware ++grep 1142 /proc/cpuinfo > /dev/null ++if [ $? -eq "0" ]; then ++ echo "Running on example hardware" ++ mount -o remount,rw / / ++ sleep 1 ++ fix_sysfs_issue ++else ++ echo "Running on Artems hardware" ++fi ++ ++for volume in `seq $MINVOL $MAXVOL`; do ++ echo -n "************ VOLUME $volume NEW$volume " ++ echo "******************************************" ++ writevol_test $volume $SIZE_1M dynamic ++ jffs2_test NEW$volume $ITERATIONS ++ delete_volume $volume ++done ++ ++echo "***********************************************************************" ++echo "* Congratulations, no errors found! *" ++echo "* Have fun with your cool UBI system! *" ++echo "***********************************************************************" ++ ++exit_success +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_test.sh linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_test.sh +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_test.sh 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_test.sh 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,328 @@ ++#!/bin/sh ++# ++# UBI Volume creation/deletion/write/read test script ++# ++# Written in shell language to reduce dependencies to more sophisticated ++# interpreters, which may not be available on some stupid platforms. ++# ++# Author: Frank Haverkamp ++# ++# 1.0 Initial version ++# 1.1 Use ubiupdatevol instead of ubiwritevol ++# ++ ++VERSION="1.1" ++ ++export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/ ++ ++UBIMKVOL=ubimkvol ++UBIRMVOL=ubirmvol ++UBIUPDATEVOL=ubiupdatevol ++ ++# 128 KiB 131072 ++# 256 KiB 262144 ++# 512 KiB 524288 ++ ++SIZE_512K=524288 ++SIZE_1M=1310720 ++ ++SELF=$0 ++MINVOL=10 ++MAXVOL=12 ++ ++# ++# To have a standardized output I define the following function to be ++# used when a test was ok or when it failed. ++# ++failed () ++{ ++ echo "FAILED" ++} ++ ++passed () ++{ ++ echo "PASSED" ++} ++ ++# ++# Print sucess message. Consider to exit with zero as return code. ++# ++exit_success () ++{ ++ echo "SUCCESS" ++ exit 0 ++} ++ ++# ++# Print failure message. Consider to exit with non zero return code. ++# ++exit_failure () ++{ ++ echo "FAILED" ++ exit 1 ++} ++ ++############################################################################### ++# ++# START ++# ++############################################################################### ++ ++fix_sysfs_issue () ++{ ++ echo -n "*** Fixing the sysfs issue with the /dev nodes ... " ++ ++ minor=0 ++ major=`grep ubi0 /proc/devices | sed -e 's/\(.*\) ubi0/\1/'` ++ ++ rm -rf /dev/ubi0 ++ mknod /dev/ubi0 c $major 0 ++ ++ for minor in `seq 0 $MAXVOL`; do ++ ### echo " mknod /dev/ubi0_$minor c $major $(($minor + 1))" ++ rm -rf /dev/ubi0_$minor ++ mknod /dev/ubi0_$minor c $major $(($minor + 1)) ++ done ++ passed ++} ++ ++# delete_volume - Delete a volume. If it does not exist, do not try ++# to delete it. ++# @id: volume id ++# ++delete_volume () ++{ ++ volume=$1 ++ ++ ### FIXME broken sysfs!!!! ++ if [ -e /sys/class/ubi/$volume -o -e /sys/class/ubi/ubi0/$volume -o -e /sys/class/ubi/ubi0_$volume ]; then ++ ++ echo -n "*** Truncate volume if it exists ... " ++ $UBIUPDATEVOL -d0 -n$volume -t ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ echo -n "*** Delete volume if it exists ... " ++ $UBIRMVOL -d0 -n$volume ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ fi ++} ++ ++mkvol_rmvol_test () ++{ ++ type=$1 ++ ++### Test if volume delete on non-existing volumes fails nicely ++ ++ for i in `seq $MINVOL $MAXVOL`; do ++ echo "*** Delete if exist or not $i ... " ++ ++ delete_volume $i ++ passed ++ done ++ ++### Now deleting volumes must fail ++ ++ for i in `seq $MINVOL $MAXVOL`; do ++ echo "*** Trying to delete non existing UBI Volume $i ... " ++ ++ $UBIRMVOL -d0 -n$i ++ if [ $? -eq "0" ] ; then ++ exit_failure ++ fi ++ passed ++ done ++ ++### Test if volume creation works ok ++ ++ for i in `seq $MINVOL $MAXVOL`; do ++ echo "*** Creating UBI Volume $i ... " ++ echo " $UBIMKVOL -d0 -n$i -t$type -NNEW$i -s $SIZE_512K" ++ ++ $UBIMKVOL -d0 -n$i -t$type -N"NEW$i" -s $SIZE_512K ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ done ++ ++### Now deleting volumes must be ok ++ ++ for i in `seq $MINVOL $MAXVOL`; do ++ echo "*** Trying to delete UBI Volume $i ... " ++ ++ $UBIRMVOL -d0 -n$i ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ done ++ ++### Now allocate too large volume ++ ++ echo -n "*** Try to create too large volume" ++ $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s 800000000 ++ if [ $? -eq "0" ] ; then ++ exit_failure ++ fi ++ passed ++} ++ ++# writevol_test - Tests volume creation and writing data to it. ++# ++# @size: Size of random data to write ++# @type: Volume type static or dynamic ++# ++writevol_test () ++{ ++ size=$1 ++ type=$2 ++ ++ echo "*** Write volume test with size $size" ++ ++### Make sure that volume exist, delete existing volume, create new ++ ++ delete_volume $MINVOL ++ ++ echo -n "*** Try to create volume ... " ++ $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s $SIZE_1M ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++### Try to create same volume again ++ echo -n "*** Try to create some volume again, this must fail ... " ++ $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s $SIZE_1M ++ if [ $? -eq "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++### Now create test data, write it, read it, compare it ++ echo -n "*** Create test data ... " ++ dd if=/dev/urandom of=testdata.bin bs=$size count=1 ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ echo "*** Now writing data to volume ... " ++ # sleep 5 ++ ls -l testdata.bin ++ echo " $UBIUPDATEVOL -d0 -n$MINVOL testdata.bin" ++ $UBIUPDATEVOL -d0 -n$MINVOL testdata.bin ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ if [ $type = "static" ] ; then ++ echo "*** Download data with cat ... " ++ cat /dev/ubi0_$MINVOL > readdata.bin ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ else ++ echo "*** Download data with dd bs=1 ... " ++ dd if=/dev/ubi0_$MINVOL of=readdata.bin bs=$size count=1 ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ # Size 1 does not work with this test ... ++ # ++ #echo "*** Download data with dd bs=$size ... " ++ #dd if=/dev/ubi0_$MINVOL of=readdata2.bin bs=$size count=1 ++ #if [ $? -ne "0" ] ; then ++ # exit_failure ++ #fi ++ #passed ++ ++ #echo -n "*** Comparing data (1) ... " ++ #cmp readdata.bin readdata2.bin ++ #if [ $? -ne "0" ] ; then ++ # exit_failure ++ #fi ++ #passed ++ fi ++ ++ echo -n "*** Comparing data ... " ++ cmp readdata.bin testdata.bin ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++} ++ ++echo "***********************************************************************" ++echo "* UBI Testing starts now ... *" ++echo "* Good luck! *" ++echo "***********************************************************************" ++ ++# Set to zero if not running on example hardware ++grep ubi /proc/devices > /dev/null ++if [ $? -ne "0" ]; then ++ echo "No UBI found in /proc/devices! I am broken!" ++ exit_failure ++fi ++ ++# Set to zero if not running on example hardware ++grep 1142 /proc/cpuinfo > /dev/null ++if [ $? -eq "0" ]; then ++ echo "Running on example hardware" ++ mount -o remount,rw / / ++ sleep 1 ++ fix_sysfs_issue ++else ++ echo "Running on Artems hardware" ++fi ++ ++echo "***********************************************************************" ++echo "* mkvol/rmvol testing for static volumes ... *" ++echo "***********************************************************************" ++ ++mkvol_rmvol_test static ++ ++echo "***********************************************************************" ++echo "* mkvol/rmvol testing for dynamic volumes ... *" ++echo "***********************************************************************" ++ ++mkvol_rmvol_test dynamic ++ ++echo "***********************************************************************" ++echo "* write to static volumes ... *" ++echo "***********************************************************************" ++ ++# 10 Erase blocks = (128 KiB - 64 * 2) * 10 ++# = 1309440 bytes ++# 128 KiB 131072 ++# 256 KiB 262144 ++# 512 KiB 524288 ++ ++for size in 262144 131073 131072 2048 1 4096 12800 31313 ; do ++ writevol_test $size static ++done ++ ++echo "***********************************************************************" ++echo "* write to dynamic volumes ... *" ++echo "***********************************************************************" ++echo "VERSION: $VERSION" ++ ++for size in 131073 131072 2048 1 4096 12800 31313 262144 ; do ++ writevol_test $size dynamic ++done ++ ++echo "***********************************************************************" ++echo "* Congratulations, no errors found! *" ++echo "* Have fun with your cool UBI system! *" ++echo "***********************************************************************" ++ ++exit_success +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_tools_test.sh linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_tools_test.sh +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_tools_test.sh 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/ubi_tools_test.sh 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,252 @@ ++#!/bin/sh ++# ++# UBI Volume creation/deletion/write/read test script. ++# Uses our flash update tools and the associated toolchain for flash ++# image creation. ++# ++# Written in shell language to reduce dependencies to more sophisticated ++# interpreters, which may not be available on some stupid platforms. ++# ++# Author: Frank Haverkamp ++# ++# 1.0 Initial version ++# ++ ++VERSION="1.0" ++ ++export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/ ++ ++UBIMKVOL=ubimkvol ++UBIRMVOL=ubirmvol ++UBIWRITEVOL=ubiupdatevol ++PFIFLASH=pfiflash ++CMP=cmp ++ ++MAXVOL=32 ++ ++test_pfi=test_complete.pfi ++real_pfi=example_complete.pfi ++ ++# 128 KiB 131072 ++# 256 KiB 262144 ++# 512 KiB 524288 ++ ++# ++# To have a standardized output I define the following function to be ++# used when a test was ok or when it failed. ++# ++failed () ++{ ++ echo "FAILED" ++} ++ ++passed () ++{ ++ echo "PASSED" ++} ++ ++# ++# Print sucess message. Consider to exit with zero as return code. ++# ++exit_success () ++{ ++ echo "SUCCESS" ++ exit 0 ++} ++ ++# ++# Print failure message. Consider to exit with non zero return code. ++# ++exit_failure () ++{ ++ echo "FAILED" ++ exit 1 ++} ++ ++############################################################################### ++# ++# START ++# ++############################################################################### ++ ++fix_sysfs_issue () ++{ ++ echo -n "*** Fixing the sysfs issue with the /dev nodes ... " ++ ++ minor=0 ++ major=`grep ubi0 /proc/devices | sed -e 's/\(.*\) ubi0/\1/'` ++ ++ rm -rf /dev/ubi0 ++ mknod /dev/ubi0 c $major 0 ++ ++ for minor in `seq 0 $MAXVOL`; do ++ ### echo " mknod /dev/ubi0_$minor c $major $(($minor + 1))" ++ rm -rf /dev/ubi0_$minor ++ mknod /dev/ubi0_$minor c $major $(($minor + 1)) ++ done ++ passed ++} ++ ++# delete_volume - Delete a volume. If it does not exist, do not try ++# to delete it. ++# @id: volume id ++# ++delete_volume () ++{ ++ volume=$1 ++ ++ ### FIXME broken sysfs!!!! ++ if [ -e /sys/class/ubi/$volume -o -e /sys/class/ubi/ubi0/$volume -o -e /sys/class/ubi/ubi0_$volume ]; then ++ ++ echo -n "*** Truncate volume if it exists ... " ++ $UBIWRITEVOL -d0 -n$volume -t ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ ++ echo -n "*** Delete volume if it exists ... " ++ $UBIRMVOL -d0 -n$volume ++ if [ $? -ne "0" ] ; then ++ exit_failure ++ fi ++ passed ++ fi ++} ++ ++echo "***********************************************************************" ++echo "* UBI Tools Testing starts now ... *" ++echo "* Good luck! *" ++echo "***********************************************************************" ++ ++# Set to zero if not running on example hardware ++grep ubi /proc/devices > /dev/null ++if [ $? -ne "0" ]; then ++ echo "No UBI found in /proc/devices! I am broken!" ++ exit_failure ++fi ++ ++# Set to zero if not running on example hardware ++grep 1142 /proc/cpuinfo > /dev/null ++if [ $? -eq "0" ]; then ++ echo "Running on example hardware" ++ mount -o remount,rw / / ++ sleep 1 ++ fix_sysfs_issue ++else ++ echo "Running on other hardware" ++fi ++ ++### Test basic stuff ++pfiflash_basic () ++{ ++ echo "Calling pfiflash with test-data ... " ++ echo " $PFIFLASH $test_pfi" ++ $PFIFLASH $test_pfi ++ if [ $? -ne "0" ]; then ++ echo "Uhhh something went wrong!" ++ exit_failure ++ fi ++ passed ++ ++ echo "Testing if data is correct 10 and 11 ... " ++ $CMP /dev/ubi0_10 /dev/ubi0_11 ++ if [ $? -ne "0" ]; then ++ echo "Mirrored volumes not equal!" ++ exit_failure ++ fi ++ passed ++ ++ echo "Comparing against original data ... " ++ $CMP /dev/ubi0_10 test_u-boot.bin ++ if [ $? -ne "0" ]; then ++ echo "Compared volume not equal!" ++ exit_failure ++ fi ++ passed ++ ++ echo "Testing if data is correct 12 and 13 ... " ++ $CMP /dev/ubi0_12 /dev/ubi0_13 ++ if [ $? -ne "0" ]; then ++ echo "Mirrored volumes not equal!" ++ exit_failure ++ fi ++ passed ++ ++ echo "Comparing against original data ... " ++ $CMP /dev/ubi0_12 test_vmlinux.bin ++ if [ $? -ne "0" ]; then ++ echo "Compared volume not equal!" ++ exit_failure ++ fi ++ passed ++ ++ echo "Testing if data is correct 14 and 15 ... " ++ $CMP /dev/ubi0_14 /dev/ubi0_15 ++ if [ $? -ne "0" ]; then ++ echo "Mirrored volumes not equal!" ++ exit_failure ++ fi ++ passed ++} ++ ++### Test each and everything ++pfiflash_advanced () ++{ ++ if [ -e example_complete.pfi ]; then ++ echo "Calling pfiflash with real data ... " ++ $PFIFLASH -p overwrite --complete example_complete.pfi ++ if [ $? -ne "0" ]; then ++ echo "Uhhh something went wrong!" ++ exit_failure ++ fi ++ passed ++ ++ echo "Testing if data is correct 2 and 3 ... " ++ $CMP /dev/ubi0_2 /dev/ubi0_3 ++ if [ $? -ne "0" ]; then ++ echo "Mirrored volumes not equal!" ++ exit_failure ++ fi ++ passed ++ ++ echo "Comparing against original data ... " ++ $CMP /dev/ubi0_2 u-boot.bin ++ if [ $? -ne "0" ]; then ++ echo "Compared volume not equal!" ++ exit_failure ++ fi ++ passed ++ ++ echo "Testing if data is correct 6 and 7 ... " ++ $CMP /dev/ubi0_6 /dev/ubi0_7 ++ if [ $? -ne "0" ]; then ++ echo "Mirrored volumes not equal!" ++ exit_failure ++ fi ++ passed ++ ++ echo "Comparing against original data ... " ++ $CMP /dev/ubi0_6 vmlinux.bin ++ if [ $? -ne "0" ]; then ++ echo "Compared volume not equal!" ++ exit_failure ++ fi ++ passed ++ fi ++} ++ ++echo "***********************************************************************" ++echo "* Testing pfiflash ... *" ++echo "***********************************************************************" ++echo "VERSION: $VERSION" ++ ++pfiflash_basic ++pfiflash_advanced ++ ++echo "***********************************************************************" ++echo "* Congratulations, no errors found! *" ++echo "* Have fun with your cool UBI system! *" ++echo "***********************************************************************" ++ ++exit_success +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/unubi_test.sh linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/unubi_test.sh +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/scripts/unubi_test.sh 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/scripts/unubi_test.sh 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,105 @@ ++#!/bin/sh ++# ++# Use raw NAND data, extract UBI image and apply tool to it. ++# Test basic functionality. ++# ++# 2007 Frank Haverkamp ++# ++ ++version=1.1 ++ ++image=data.mif ++oob=oob.bin ++data=data.bin ++pagesize=2048 ++volmax=31 ++datadir=unubi_data ++ ++# general arguments e.g. debug enablement ++# unubi_args="-D" ++ ++echo "------------------------------------------------------------------------" ++echo "Testcase: ${0} Version: ${version}" ++echo "------------------------------------------------------------------------" ++echo "Testing nand2bin ..." ++echo " Input: ${image}" ++echo " Data: ${data}" ++echo " OOB: ${oob}" ++echo " Pagesize: ${pagesize}" ++nand2bin --pagesize ${pagesize} -o ${data} -O ${oob} ${image} ++echo ++ ++echo "------------------------------------------------------------------------" ++echo "Testing unubi ..." ++echo "------------------------------------------------------------------------" ++unubi --version ++echo ++ ++echo "------------------------------------------------------------------------" ++echo "Trying to extract first ${volmax} volumes ..." ++echo "------------------------------------------------------------------------" ++mkdir -p ${datadir}/volumes ++for v in `seq 0 ${volmax}` ; do ++ unubi ${unubi_args} -r${v} -d${datadir}/volumes ${data} ++ echo -n "." ++done ++echo "ok" ++ls -l ${datadir}/volumes ++echo ++ ++echo "------------------------------------------------------------------------" ++echo "Extracting graphics ..." ++echo "------------------------------------------------------------------------" ++unubi -a -d${datadir} ${data} ++echo "Use gnuplot to display:" ++ls ${datadir}/*.plot ++ls ${datadir}/*.data ++echo ++ ++echo "------------------------------------------------------------------------" ++echo "eb-split" ++echo "------------------------------------------------------------------------" ++unubi -e -d${datadir}/eb-split ${data} ++ls -l ${datadir}/eb-split ++echo ++ ++echo "------------------------------------------------------------------------" ++echo "vol-split" ++echo "------------------------------------------------------------------------" ++unubi -v -d${datadir}/vol-split ${data} ++ls -l ${datadir}/vol-split ++echo ++echo "The generated images contain only the data (126KiB in our " ++echo "case) not including the UBI erase count and volume info " ++echo "header. For dynamic volumes the data should be the full " ++echo "126KiB. Unubi cannot know how much of the data is valid. " ++echo ++ ++echo "------------------------------------------------------------------------" ++echo "!vol-split" ++echo "------------------------------------------------------------------------" ++unubi -V -d${datadir}/vol-split! ${data} ++ls -l ${datadir}/vol-split\! ++echo ++echo "The generated images contain the full block data of 128KiB " ++echo "including the UBI erase count and volume information header." ++echo ++ ++echo "------------------------------------------------------------------------" ++echo "Extracting volume info table ..." ++echo "------------------------------------------------------------------------" ++unubi -i -d${datadir} ${data} ++echo "I strongly hope that empty ubi blocks are filled with 0xff! " ++echo ++ ++echo "------------------------------------------------------------------------" ++echo "Table 0" ++echo "------------------------------------------------------------------------" ++cat ${datadir}/vol_info_table0 ++echo ++ ++echo "------------------------------------------------------------------------" ++echo "Table 1" ++echo "------------------------------------------------------------------------" ++cat ${datadir}/vol_info_table1 ++echo +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/bin2nand.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/bin2nand.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/bin2nand.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/bin2nand.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,344 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2007 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ */ ++ ++/* ++ * Create a flashable NAND image from a binary image ++ * ++ * History: ++ * 1.0 Initial release (tglx) ++ * 1.1 Understands hex and dec input parameters (tglx) ++ * 1.2 Generates separated OOB data, if needed. (oloh) ++ * 1.3 Padds data/oob to a given size. (oloh) ++ * 1.4 Removed argp because we want to use uClibc. ++ * 1.5 Minor cleanup ++ * 1.6 written variable not initialized (-j did not work) (haver) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "error.h" ++#include "config.h" ++#include "nandecc.h" ++ ++#define PROGRAM_VERSION "1.6" ++ ++#define CHECK_ENDP(option, endp) do { \ ++ if (*endp) { \ ++ fprintf(stderr, \ ++ "Parse error option \'%s\'. " \ ++ "No correct numeric value.\n" \ ++ , option); \ ++ exit(EXIT_FAILURE); \ ++ } \ ++} while(0) ++ ++typedef enum action_t { ++ ACT_NORMAL = 0x00000001, ++} action_t; ++ ++#define PAGESIZE 2048 ++#define PADDING 0 /* 0 means, do not adjust anything */ ++#define BUFSIZE 4096 ++ ++static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" ++ "bin2nand - a tool for adding OOB information to a " ++ "binary input file.\n"; ++ ++static const char *optionsstr = ++" -c, --copyright Print copyright informatoin.\n" ++" -j, --padding= Padding in Byte/Mi/ki. Default = no padding\n" ++" -p, --pagesize= Pagesize in Byte/Mi/ki. Default = 2048\n" ++" -o, --output= Output filename. Interleaved Data/OOB if\n" ++" output-oob not specified.\n" ++" -q, --output-oob= Write OOB data in separate file.\n" ++" -?, --help Give this help list\n" ++" --usage Give a short usage message\n" ++" -V, --version Print program version\n"; ++ ++static const char *usage = ++"Usage: bin2nand [-c?V] [-j ] [-p ] [-o ] [-q ]\n" ++" [--copyright] [--padding=] [--pagesize=]\n" ++" [--output=] [--output-oob=] [--help] [--usage]\n" ++" [--version]\n"; ++ ++struct option long_options[] = { ++ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, ++ { .name = "padding", .has_arg = 1, .flag = NULL, .val = 'j' }, ++ { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' }, ++ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, ++ { .name = "output-oob", .has_arg = 1, .flag = NULL, .val = 'q' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, ++ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ { NULL, 0, NULL, 0} ++}; ++ ++static const char copyright [] __attribute__((unused)) = ++ "Copyright IBM Corp. 2006"; ++ ++typedef struct myargs { ++ action_t action; ++ ++ size_t pagesize; ++ size_t padding; ++ ++ FILE* fp_in; ++ char *file_out_data; /* Either: Data and OOB interleaved ++ or plain data */ ++ char *file_out_oob; /* OOB Data only. */ ++ ++ /* special stuff needed to get additional arguments */ ++ char *arg1; ++ char **options; /* [STRING...] */ ++} myargs; ++ ++ ++static int ustrtoull(const char *cp, char **endp, unsigned int base) ++{ ++ unsigned long long res = strtoull(cp, endp, base); ++ ++ switch (**endp) { ++ case 'G': ++ res *= 1024; ++ case 'M': ++ res *= 1024; ++ case 'k': ++ case 'K': ++ res *= 1024; ++ /* "Ki", "ki", "Mi" or "Gi" are to be used. */ ++ if ((*endp)[1] == 'i') ++ (*endp) += 2; ++ } ++ return res; ++} ++ ++static int ++parse_opt(int argc, char **argv, myargs *args) ++{ ++ char* endp; ++ ++ while (1) { ++ int key; ++ ++ key = getopt_long(argc, argv, "cj:p:o:q:?V", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 'p': /* pagesize */ ++ args->pagesize = (size_t) ++ ustrtoull(optarg, &endp, 0); ++ CHECK_ENDP("p", endp); ++ break; ++ case 'j': /* padding */ ++ args->padding = (size_t) ++ ustrtoull(optarg, &endp, 0); ++ CHECK_ENDP("j", endp); ++ break; ++ case 'o': /* output */ ++ args->file_out_data = optarg; ++ break; ++ case 'q': /* output oob */ ++ args->file_out_oob = optarg; ++ break; ++ case '?': /* help */ ++ printf("%s", doc); ++ printf("%s", optionsstr); ++ exit(0); ++ break; ++ case 'V': ++ printf("%s\n", PROGRAM_VERSION); ++ exit(0); ++ break; ++ case 'c': ++ printf("%s\n", copyright); ++ exit(0); ++ default: ++ printf("%s", usage); ++ exit(-1); ++ } ++ } ++ ++ if (optind < argc) { ++ args->fp_in = fopen(argv[optind++], "rb"); ++ if ((args->fp_in) == NULL) { ++ err_quit("Cannot open file %s for input\n", ++ argv[optind++]); ++ } ++ } ++ ++ return 0; ++} ++ ++static int ++process_page(uint8_t* buf, size_t pagesize, ++ FILE *fp_data, FILE* fp_oob, size_t* written) ++{ ++ int eccpoi, oobsize; ++ size_t i; ++ uint8_t oobbuf[64]; ++ ++ memset(oobbuf, 0xff, sizeof(oobbuf)); ++ ++ switch(pagesize) { ++ case 2048: oobsize = 64; eccpoi = 64 / 2; break; ++ case 512: oobsize = 16; eccpoi = 16 / 2; break; ++ default: ++ err_msg("Unsupported page size: %d\n", pagesize); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < pagesize; i += 256, eccpoi += 3) { ++ oobbuf[eccpoi++] = 0x0; ++ /* Calculate ECC */ ++ nand_calculate_ecc(&buf[i], &oobbuf[eccpoi]); ++ } ++ ++ /* write data */ ++ *written += fwrite(buf, 1, pagesize, fp_data); ++ ++ /* either separate oob or interleave with data */ ++ if (fp_oob) { ++ i = fwrite(oobbuf, 1, oobsize, fp_oob); ++ if (ferror(fp_oob)) { ++ err_msg("IO error\n"); ++ return -EIO; ++ } ++ } ++ else { ++ i = fwrite(oobbuf, 1, oobsize, fp_data); ++ if (ferror(fp_data)) { ++ err_msg("IO error\n"); ++ return -EIO; ++ } ++ } ++ ++ return 0; ++} ++ ++int main (int argc, char** argv) ++{ ++ int rc = -1; ++ int res = 0; ++ size_t written = 0, read; ++ myargs args = { ++ .action = ACT_NORMAL, ++ .pagesize = PAGESIZE, ++ .padding = PADDING, ++ .fp_in = NULL, ++ .file_out_data = NULL, ++ .file_out_oob = NULL, ++ }; ++ ++ FILE* fp_out_data = stdout; ++ FILE* fp_out_oob = NULL; ++ ++ parse_opt(argc, argv, &args); ++ ++ uint8_t* buf = calloc(1, BUFSIZE); ++ if (!buf) { ++ err_quit("Cannot allocate page buffer.\n"); ++ } ++ ++ if (!args.fp_in) { ++ err_msg("No input image specified!\n"); ++ goto err; ++ } ++ ++ if (args.file_out_data) { ++ fp_out_data = fopen(args.file_out_data, "wb"); ++ if (fp_out_data == NULL) { ++ err_sys("Cannot open file %s for output\n", ++ args.file_out_data); ++ goto err; ++ } ++ } ++ ++ if (args.file_out_oob) { ++ fp_out_oob = fopen(args.file_out_oob, "wb"); ++ if (fp_out_oob == NULL) { ++ err_sys("Cannot open file %s for output\n", ++ args.file_out_oob); ++ goto err; ++ } ++ } ++ ++ ++ while(1) { ++ read = fread(buf, 1, args.pagesize, args.fp_in); ++ if (feof(args.fp_in) && read == 0) ++ break; ++ ++ if (read < args.pagesize) { ++ err_msg("Image not page aligned\n"); ++ goto err; ++ } ++ ++ if (ferror(args.fp_in)) { ++ err_msg("Read error\n"); ++ goto err; ++ } ++ ++ res = process_page(buf, args.pagesize, fp_out_data, ++ fp_out_oob, &written); ++ if (res != 0) ++ goto err; ++ } ++ ++ while (written < args.padding) { ++ memset(buf, 0xff, args.pagesize); ++ res = process_page(buf, args.pagesize, fp_out_data, ++ fp_out_oob, &written); ++ if (res != 0) ++ goto err; ++ } ++ ++ rc = 0; ++err: ++ free(buf); ++ ++ if (args.fp_in) ++ fclose(args.fp_in); ++ ++ if (fp_out_oob) ++ fclose(fp_out_oob); ++ ++ if (fp_out_data && fp_out_data != stdout) ++ fclose(fp_out_data); ++ ++ if (rc != 0) { ++ err_msg("Error during conversion. rc: %d\n", rc); ++ if (args.file_out_data) ++ remove(args.file_out_data); ++ if (args.file_out_oob) ++ remove(args.file_out_oob); ++ } ++ return rc; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/bootenv.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/bootenv.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/bootenv.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/bootenv.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1032 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2008 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "hashmap.h" ++#include "error.h" ++ ++#include ++#include "crc32.h" ++ ++#define ubi_unused __attribute__((unused)) ++ ++#define BOOTENV_MAXLINE 512 /* max line size of a bootenv.txt file */ ++ ++/* Structures */ ++struct bootenv { ++ hashmap_t map; ///< Pointer to hashmap which holds data structure. ++}; ++ ++struct bootenv_list { ++ hashmap_t head; ///< Pointer to list which holds the data structure. ++}; ++ ++/** ++ * @brief Remove the '\n' from a given line. ++ * @param line Input/Output line. ++ * @param size Size of the line. ++ * @param fp File Pointer. ++ * @return 0 ++ * @return or error ++ */ ++static int ++remove_lf(char *line, size_t size, FILE* fp) ++{ ++ size_t i; ++ ++ for (i = 0; i < size; i++) { ++ if (line[i] == '\n') { ++ line[i] = '\0'; ++ return 0; ++ } ++ } ++ ++ if (!feof(fp)) { ++ return BOOTENV_EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * @brief Determine if a line contains only WS. ++ * @param line The line to process. ++ * @param size Size of input line. ++ * @return 1 Yes, only WS. ++ * @return 0 No, contains data. ++ */ ++static int ++is_ws(const char *line, size_t size) ++{ ++ size_t i = 0; ++ ++ while (i < size) { ++ switch (line[i]) { ++ case '\n': ++ return 1; ++ case '#': ++ return 1; ++ case ' ': ++ i++; ++ continue; ++ case '\t': ++ i++; ++ continue; ++ default: /* any other char -> no cmnt */ ++ return 0; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++/* ------------------------------------------------------------------------- */ ++ ++/** ++ * @brief Build a list from a comma seperated value string. ++ * @param list Pointer to hashmap structure which shall store ++ * the list. ++ * @param value Comma seperated value string. ++ * @return 0 ++ * @return or error. ++ */ ++static int ++build_list_definition(hashmap_t list, const char *value) ++{ ++ int rc = 0; ++ char *str = NULL; ++ char *ptr = NULL; ++ size_t len, i, j; ++ ++ /* str: val1,val2 , val4,...,valN */ ++ len = strlen(value); ++ str = (char*) malloc((len+1) * sizeof(char)); ++ ++ /* 1. reformat string: remove spaces */ ++ for (i = 0, j = 0; i < len; i++) { ++ if (value[i] == ' ') ++ continue; ++ ++ str[j] = value[i]; ++ j++; ++ } ++ str[j] = '\0'; ++ ++ /* str: val1,val2,val4,...,valN\0*/ ++ /* 2. replace ',' seperator with '\0' */ ++ len = strlen(str); ++ for (i = 0; i < len; i++) { ++ if (str[i] == ',') { ++ str[i] = '\0'; ++ } ++ } ++ ++ /* str: val1\0val2\0val4\0...\0valN\0*/ ++ /* 3. insert definitions into a hash map, using it like a list */ ++ i = j = 0; ++ ptr = str; ++ while (((i = strlen(ptr)) > 0) && (j < len)) { ++ rc = hashmap_add(list, ptr, ""); ++ if (rc != 0) { ++ free(str); ++ return rc; ++ } ++ j += i+1; ++ if (j < len) ++ ptr += i+1; ++ } ++ ++ free(str); ++ return rc; ++} ++ ++/** ++ * @brief Extract a key value pair and add it to a hashmap ++ * @param str Input string which contains a key value pair. ++ * @param env The updated handle which contains the new pair. ++ * @return 0 ++ * @return or error ++ * @note The input string format is: "key=value" ++ */ ++static int ++extract_pair(const char *str, bootenv_t env) ++{ ++ int rc = 0; ++ char *key = NULL; ++ char *val = NULL; ++ ++ key = strdup(str); ++ if (key == NULL) ++ return -ENOMEM; ++ ++ val = strstr(key, "="); ++ if (val == NULL) { ++ rc = BOOTENV_EBADENTRY; ++ goto err; ++ } ++ ++ *val = '\0'; /* split strings */ ++ val++; ++ ++ rc = bootenv_set(env, key, val); ++ ++ err: ++ free(key); ++ return rc; ++} ++ ++int ++bootenv_destroy(bootenv_t* env) ++{ ++ int rc = 0; ++ ++ if (env == NULL || *env == NULL) ++ return -EINVAL; ++ ++ bootenv_t tmp = *env; ++ ++ rc = hashmap_free(tmp->map); ++ if (rc != 0) ++ return rc; ++ ++ free(tmp); ++ return rc; ++} ++ ++int ++bootenv_create(bootenv_t* env) ++{ ++ bootenv_t res; ++ res = (bootenv_t) calloc(1, sizeof(struct bootenv)); ++ ++ if (res == NULL) ++ return -ENOMEM; ++ ++ res->map = hashmap_new(); ++ ++ if (res->map == NULL) { ++ free(res); ++ return -ENOMEM; ++ } ++ ++ *env = res; ++ ++ return 0; ++} ++ ++ ++/** ++ * @brief Read a formatted buffer and scan it for valid bootenv ++ * key/value pairs. Add those pairs into a hashmap. ++ * @param env Hashmap which shall be used to hold the data. ++ * @param buf Formatted buffer. ++ * @param size Size of the buffer. ++ * @return 0 ++ * @return or error ++ */ ++static int ++rd_buffer(bootenv_t env, const char *buf, size_t size) ++{ ++ const char *curr = buf; /* ptr to current key/value pair */ ++ uint32_t i, j; /* current length, chars processed */ ++ ++ if (buf[size - 1] != '\0') /* must end in '\0' */ ++ return BOOTENV_EFMT; ++ ++ for (j = 0; j < size; j += i, curr += i) { ++ /* strlen returns the size of the string upto ++ but not including the null terminator; ++ adding 1 to account for '\0' */ ++ i = strlen(curr) + 1; ++ ++ if (i == 1) ++ return 0; /* no string found */ ++ ++ if (extract_pair(curr, env) != 0) ++ return BOOTENV_EINVAL; ++ } ++ ++ return 0; ++} ++ ++ ++int ++bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t* ret_crc) ++{ ++ int rc; ++ char *buf = NULL; ++ size_t i = 0; ++ uint32_t crc32_table[256]; ++ ++ if ((fp == NULL) || (env == NULL)) ++ return -EINVAL; ++ ++ /* allocate temp buffer */ ++ buf = (char*) calloc(1, size * sizeof(char)); ++ if (buf == NULL) ++ return -ENOMEM; ++ ++ /* FIXME Andreas, please review this I removed size-1 and ++ * replaced it by just size, I saw the kernel image starting ++ * with a 0x0060.... and not with the 0x60.... what it should ++ * be. Is this a tools problem or is it a problem here where ++ * fp is moved not to the right place due to the former size-1 ++ * here. ++ */ ++ while((i < size) && (!feof(fp))) { ++ int c = fgetc(fp); ++ if (c == EOF) { ++ /* FIXME isn't this dangerous, to update ++ the boot envs with incomplete data? */ ++ buf[i++] = '\0'; ++ break; /* we have enough */ ++ } ++ if (ferror(fp)) { ++ rc = -EIO; ++ goto err; ++ } ++ ++ buf[i++] = (char)c; ++ } ++ ++ /* calculate crc to return */ ++ if (ret_crc != NULL) { ++ init_crc32_table(crc32_table); ++ *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size); ++ } ++ ++ /* transfer to hashmap */ ++ rc = rd_buffer(env, buf, size); ++ ++err: ++ free(buf); ++ return rc; ++} ++ ++ ++/** ++ * If we have a single file containing the boot-parameter size should ++ * be specified either as the size of the file or as BOOTENV_MAXSIZE. ++ * If the bootparameter are in the middle of a file we need the exact ++ * length of the data. ++ */ ++int ++bootenv_read(FILE* fp, bootenv_t env, size_t size) ++{ ++ return bootenv_read_crc(fp, env, size, NULL); ++} ++ ++ ++int ++bootenv_read_txt(FILE* fp, bootenv_t env) ++{ ++ int rc = 0; ++ char *buf = NULL; ++ char *line = NULL; ++ char *lstart = NULL; ++ char *curr = NULL; ++ size_t len; ++ size_t size; ++ ++ if ((fp == NULL) || (env == NULL)) ++ return -EINVAL; ++ ++ size = BOOTENV_MAXSIZE; ++ ++ /* allocate temp buffers */ ++ buf = (char*) calloc(1, size * sizeof(char)); ++ lstart = line = (char*) calloc(1, size * sizeof(char)); ++ if ((buf == NULL) || (line == NULL)) { ++ rc = -ENOMEM; ++ goto err; ++ } ++ ++ curr = buf; ++ while ((line = fgets(line, size, fp)) != NULL) { ++ if (is_ws(line, size)) { ++ continue; ++ } ++ rc = remove_lf(line, BOOTENV_MAXSIZE, fp); ++ if (rc != 0) { ++ goto err; ++ } ++ ++ /* copy new line to binary buffer */ ++ len = strlen(line); ++ if (len > size) { ++ rc = -EFBIG; ++ goto err; ++ } ++ size -= len; /* track remaining space */ ++ ++ memcpy(curr, line, len); ++ curr += len + 1; /* for \0 seperator */ ++ } ++ ++ rc = rd_buffer(env, buf, BOOTENV_MAXSIZE); ++err: ++ if (buf != NULL) ++ free(buf); ++ if (lstart != NULL) ++ free(lstart); ++ return rc; ++} ++ ++static int ++fill_output_buffer(bootenv_t env, char *buf, size_t buf_size_max ubi_unused, ++ size_t *written) ++{ ++ int rc = 0; ++ size_t keys_size, i; ++ size_t wr = 0; ++ const char **keys = NULL; ++ const char *val = NULL; ++ ++ rc = bootenv_get_key_vector(env, &keys_size, 1, &keys); ++ if (rc != 0) ++ goto err; ++ ++ for (i = 0; i < keys_size; i++) { ++ if (wr > BOOTENV_MAXSIZE) { ++ rc = -ENOSPC; ++ goto err; ++ } ++ ++ rc = bootenv_get(env, keys[i], &val); ++ if (rc != 0) ++ goto err; ++ ++ wr += snprintf(buf + wr, BOOTENV_MAXSIZE - wr, ++ "%s=%s", keys[i], val); ++ wr++; /* for \0 */ ++ } ++ ++ *written = wr; ++ ++err: ++ if (keys != NULL) ++ free(keys); ++ ++ return rc; ++} ++ ++int ++bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc) ++{ ++ int rc = 0; ++ size_t size = 0; ++ char *buf = NULL; ++ uint32_t crc32_table[256]; ++ ++ if ((fp == NULL) || (env == NULL)) ++ return -EINVAL; ++ ++ buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char)); ++ if (buf == NULL) ++ return -ENOMEM; ++ ++ ++ rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, &size); ++ if (rc != 0) ++ goto err; ++ ++ /* calculate crc to return */ ++ if (ret_crc != NULL) { ++ init_crc32_table(crc32_table); ++ *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size); ++ } ++ ++ if (fwrite(buf, size, 1, fp) != 1) { ++ rc = -EIO; ++ goto err; ++ } ++ ++err: ++ if (buf != NULL) ++ free(buf); ++ return rc; ++} ++ ++int ++bootenv_write(FILE* fp, bootenv_t env) ++{ ++ return bootenv_write_crc(fp, env, NULL); ++} ++ ++int ++bootenv_compare(bootenv_t first, bootenv_t second) ++{ ++ int rc; ++ size_t written_first, written_second; ++ char *buf_first, *buf_second; ++ ++ if (first == NULL || second == NULL) ++ return -EINVAL; ++ ++ buf_first = malloc(BOOTENV_MAXSIZE); ++ if (!buf_first) ++ return -ENOMEM; ++ buf_second = malloc(BOOTENV_MAXSIZE); ++ if (!buf_second) { ++ rc = -ENOMEM; ++ goto err; ++ } ++ ++ rc = fill_output_buffer(first, buf_first, BOOTENV_MAXSIZE, ++ &written_first); ++ if (rc < 0) ++ goto err; ++ rc = fill_output_buffer(second, buf_second, BOOTENV_MAXSIZE, ++ &written_second); ++ if (rc < 0) ++ goto err; ++ ++ if (written_first != written_second) { ++ rc = 1; ++ goto err; ++ } ++ ++ rc = memcmp(buf_first, buf_second, written_first); ++ if (rc != 0) { ++ rc = 2; ++ goto err; ++ } ++ ++err: ++ if (buf_first) ++ free(buf_first); ++ if (buf_second) ++ free(buf_second); ++ ++ return rc; ++} ++ ++int ++bootenv_size(bootenv_t env, size_t *size) ++{ ++ int rc = 0; ++ char *buf = NULL; ++ ++ if (env == NULL) ++ return -EINVAL; ++ ++ buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char)); ++ if (buf == NULL) ++ return -ENOMEM; ++ ++ rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, size); ++ if (rc != 0) ++ goto err; ++ ++err: ++ if (buf != NULL) ++ free(buf); ++ return rc; ++} ++ ++int ++bootenv_write_txt(FILE* fp, bootenv_t env) ++{ ++ int rc = 0; ++ size_t size, wr, i; ++ const char **keys = NULL; ++ const char *key = NULL; ++ const char *val = NULL; ++ ++ if ((fp == NULL) || (env == NULL)) ++ return -EINVAL; ++ ++ rc = bootenv_get_key_vector(env, &size, 1, &keys); ++ if (rc != 0) ++ goto err; ++ ++ for (i = 0; i < size; i++) { ++ key = keys[i]; ++ rc = bootenv_get(env, key, &val); ++ if (rc != 0) ++ goto err; ++ ++ wr = fprintf(fp, "%s=%s\n", key, val); ++ if (wr != strlen(key) + strlen(val) + 2) { ++ rc = -EIO; ++ goto err; ++ } ++ } ++ ++err: ++ if (keys != NULL) ++ free(keys); ++ return rc; ++} ++ ++int ++bootenv_valid(bootenv_t env ubi_unused) ++{ ++ /* @FIXME No sanity check implemented. */ ++ return 0; ++} ++ ++int ++bootenv_copy_bootenv(bootenv_t in, bootenv_t *out) ++{ ++ int rc = 0; ++ const char *tmp = NULL; ++ const char **keys = NULL; ++ size_t vec_size, i; ++ ++ if ((in == NULL) || (out == NULL)) ++ return -EINVAL; ++ ++ /* purge output var for sure... */ ++ rc = bootenv_destroy(out); ++ if (rc != 0) ++ return rc; ++ ++ /* create the new map */ ++ rc = bootenv_create(out); ++ if (rc != 0) ++ goto err; ++ ++ /* get the key list from the input map */ ++ rc = bootenv_get_key_vector(in, &vec_size, 0, &keys); ++ if (rc != 0) ++ goto err; ++ ++ if (vec_size != hashmap_size(in->map)) { ++ rc = BOOTENV_ECOPY; ++ goto err; ++ } ++ ++ /* make a deep copy of the hashmap */ ++ for (i = 0; i < vec_size; i++) { ++ rc = bootenv_get(in, keys[i], &tmp); ++ if (rc != 0) ++ goto err; ++ ++ rc = bootenv_set(*out, keys[i], tmp); ++ if (rc != 0) ++ goto err; ++ } ++ ++err: ++ if (keys != NULL) ++ free(keys); ++ ++ return rc; ++} ++ ++/* ------------------------------------------------------------------------- */ ++ ++ ++int ++bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res, ++ int *warnings, char *err_buf ubi_unused, ++ size_t err_buf_size ubi_unused) ++{ ++ bootenv_list_t l_old = NULL; ++ bootenv_list_t l_new = NULL; ++ const char *pdd_old = NULL; ++ const char *pdd_new = NULL; ++ const char *tmp = NULL; ++ const char **vec_old = NULL; ++ const char **vec_new = NULL; ++ const char **pdd_up_vec = NULL; ++ size_t vec_old_size, vec_new_size, pdd_up_vec_size, i; ++ int rc = 0; ++ ++ if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) ++ return -EINVAL; ++ ++ /* get the pdd strings, e.g.: ++ * pdd_old=a,b,c ++ * pdd_new=a,c,d,e */ ++ rc = bootenv_get(env_old, "pdd", &pdd_old); ++ if (rc != 0) ++ goto err; ++ rc = bootenv_get(env_new, "pdd", &pdd_new); ++ if (rc != 0) ++ goto err; ++ ++ /* put it into a list and then convert it to an vector */ ++ rc = bootenv_list_create(&l_old); ++ if (rc != 0) ++ goto err; ++ rc = bootenv_list_create(&l_new); ++ if (rc != 0) ++ goto err; ++ ++ rc = bootenv_list_import(l_old, pdd_old); ++ if (rc != 0) ++ goto err; ++ ++ rc = bootenv_list_import(l_new, pdd_new); ++ if (rc != 0) ++ goto err; ++ ++ rc = bootenv_list_to_vector(l_old, &vec_old_size, &vec_old); ++ if (rc != 0) ++ goto err; ++ ++ rc = bootenv_list_to_vector(l_new, &vec_new_size, &vec_new); ++ if (rc != 0) ++ goto err; ++ ++ rc = bootenv_copy_bootenv(env_new, env_res); ++ if (rc != 0) ++ goto err; ++ ++ /* calculate the update vector between the old and new pdd */ ++ pdd_up_vec = hashmap_get_update_key_vector(vec_old, vec_old_size, ++ vec_new, vec_new_size, &pdd_up_vec_size); ++ ++ if (pdd_up_vec == NULL) { ++ rc = -ENOMEM; ++ goto err; ++ } ++ ++ if (pdd_up_vec_size != 0) { ++ /* need to warn the user about the unset of ++ * some pdd/bootenv values */ ++ *warnings = BOOTENV_WPDD_STRING_DIFFERS; ++ ++ /* remove all entries in the new bootenv load */ ++ for (i = 0; i < pdd_up_vec_size; i++) { ++ bootenv_unset(*env_res, pdd_up_vec[i]); ++ } ++ } ++ ++ /* generate the keep array and copy old pdd values to new bootenv */ ++ for (i = 0; i < vec_old_size; i++) { ++ rc = bootenv_get(env_old, vec_old[i], &tmp); ++ if (rc != 0) { ++ rc = BOOTENV_EPDDINVAL; ++ goto err; ++ } ++ rc = bootenv_set(*env_res, vec_old[i], tmp); ++ if (rc != 0) { ++ goto err; ++ } ++ } ++ /* put the old pdd string into the result map */ ++ rc = bootenv_set(*env_res, "pdd", pdd_old); ++ if (rc != 0) { ++ goto err; ++ } ++ ++ ++err: ++ if (vec_old != NULL) ++ free(vec_old); ++ if (vec_new != NULL) ++ free(vec_new); ++ if (pdd_up_vec != NULL) ++ free(pdd_up_vec); ++ ++ bootenv_list_destroy(&l_old); ++ bootenv_list_destroy(&l_new); ++ return rc; ++} ++ ++ ++int ++bootenv_pdd_overwrite(bootenv_t env_old, bootenv_t env_new, ++ bootenv_t *env_res, int *warnings ubi_unused, ++ char *err_buf ubi_unused, size_t err_buf_size ubi_unused) ++{ ++ if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) ++ return -EINVAL; ++ ++ return bootenv_copy_bootenv(env_new, env_res); ++} ++ ++int ++bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res, ++ int *warnings ubi_unused, char *err_buf, size_t err_buf_size) ++{ ++ if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) ++ return -EINVAL; ++ ++ snprintf(err_buf, err_buf_size, "The PDD merge operation is not " ++ "implemented. Contact: "); ++ ++ return BOOTENV_ENOTIMPL; ++} ++ ++/* ------------------------------------------------------------------------- */ ++ ++int ++bootenv_get(bootenv_t env, const char *key, const char **value) ++{ ++ if (env == NULL) ++ return -EINVAL; ++ ++ *value = hashmap_lookup(env->map, key); ++ if (*value == NULL) ++ return BOOTENV_ENOTFOUND; ++ ++ return 0; ++} ++ ++int ++bootenv_get_num(bootenv_t env, const char *key, uint32_t *value) ++{ ++ char *endptr = NULL; ++ const char *str; ++ ++ if (env == NULL) ++ return 0; ++ ++ str = hashmap_lookup(env->map, key); ++ if (!str) ++ return -EINVAL; ++ ++ *value = strtoul(str, &endptr, 0); ++ ++ if (*endptr == '\0') { ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++int ++bootenv_set(bootenv_t env, const char *key, const char *value) ++{ ++ if (env == NULL) ++ return -EINVAL; ++ ++ return hashmap_add(env->map, key, value); ++} ++ ++int ++bootenv_unset(bootenv_t env, const char *key) ++{ ++ if (env == NULL) ++ return -EINVAL; ++ ++ return hashmap_remove(env->map, key); ++} ++ ++int ++bootenv_get_key_vector(bootenv_t env, size_t* size, int sort, ++ const char ***vector) ++{ ++ if ((env == NULL) || (size == NULL)) ++ return -EINVAL; ++ ++ *vector = hashmap_get_key_vector(env->map, size, sort); ++ ++ if (*vector == NULL) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++int ++bootenv_dump(bootenv_t env) ++{ ++ if (env == NULL) ++ return -EINVAL; ++ ++ return hashmap_dump(env->map); ++} ++ ++int ++bootenv_list_create(bootenv_list_t *list) ++{ ++ bootenv_list_t res; ++ res = (bootenv_list_t) calloc(1, sizeof(struct bootenv_list)); ++ ++ if (res == NULL) ++ return -ENOMEM; ++ ++ res->head = hashmap_new(); ++ ++ if (res->head == NULL) { ++ free(res); ++ return -ENOMEM; ++ } ++ ++ *list = res; ++ return 0; ++} ++ ++int ++bootenv_list_destroy(bootenv_list_t *list) ++{ ++ int rc = 0; ++ ++ if (list == NULL) ++ return -EINVAL; ++ ++ bootenv_list_t tmp = *list; ++ if (tmp == 0) ++ return 0; ++ ++ rc = hashmap_free(tmp->head); ++ if (rc != 0) ++ return rc; ++ ++ free(tmp); ++ *list = NULL; ++ return 0; ++} ++ ++int ++bootenv_list_import(bootenv_list_t list, const char *str) ++{ ++ if (list == NULL) ++ return -EINVAL; ++ ++ return build_list_definition(list->head, str); ++} ++ ++int ++bootenv_list_export(bootenv_list_t list, char **string) ++{ ++ size_t size, i, j, bufsize, tmp, rc = 0; ++ const char **items; ++ ++ if (list == NULL) ++ return -EINVAL; ++ ++ bufsize = BOOTENV_MAXLINE; ++ char *res = (char*) malloc(bufsize * sizeof(char)); ++ if (res == NULL) ++ return -ENOMEM; ++ ++ rc = bootenv_list_to_vector(list, &size, &items); ++ if (rc != 0) { ++ goto err; ++ } ++ ++ j = 0; ++ for (i = 0; i < size; i++) { ++ tmp = strlen(items[i]); ++ if (j >= bufsize) { ++ bufsize += BOOTENV_MAXLINE; ++ res = (char*) realloc(res, bufsize * sizeof(char)); ++ if (res == NULL) { ++ rc = -ENOMEM; ++ goto err; ++ } ++ } ++ memcpy(res + j, items[i], tmp); ++ j += tmp; ++ if (i < (size - 1)) { ++ res[j] = ','; ++ j++; ++ } ++ } ++ j++; ++ res[j] = '\0'; ++ free(items); ++ *string = res; ++ return 0; ++err: ++ free(items); ++ return rc; ++} ++ ++int ++bootenv_list_add(bootenv_list_t list, const char *item) ++{ ++ if ((list == NULL) || (item == NULL)) ++ return -EINVAL; ++ ++ return hashmap_add(list->head, item, ""); ++} ++ ++int ++bootenv_list_remove(bootenv_list_t list, const char *item) ++{ ++ if ((list == NULL) || (item == NULL)) ++ return -EINVAL; ++ ++ return hashmap_remove(list->head, item); ++} ++ ++int ++bootenv_list_is_in(bootenv_list_t list, const char *item) ++{ ++ if ((list == NULL) || (item == NULL)) ++ return -EINVAL; ++ ++ return hashmap_lookup(list->head, item) != NULL ? 1 : 0; ++} ++ ++int ++bootenv_list_to_vector(bootenv_list_t list, size_t *size, const char ***vector) ++{ ++ if ((list == NULL) || (size == NULL)) ++ return -EINVAL; ++ ++ *vector = hashmap_get_key_vector(list->head, size, 1); ++ if (*vector == NULL) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++int ++bootenv_list_to_num_vector(bootenv_list_t list, size_t *size, ++ uint32_t **vector) ++{ ++ int rc = 0; ++ size_t i; ++ uint32_t* res = NULL; ++ char *endptr = NULL; ++ const char **a = NULL; ++ ++ rc = bootenv_list_to_vector(list, size, &a); ++ if (rc != 0) ++ goto err; ++ ++ res = (uint32_t*) malloc (*size * sizeof(uint32_t)); ++ if (!res) ++ goto err; ++ ++ for (i = 0; i < *size; i++) { ++ res[i] = strtoul(a[i], &endptr, 0); ++ if (*endptr != '\0') ++ goto err; ++ } ++ ++ if (a) ++ free(a); ++ *vector = res; ++ return 0; ++ ++err: ++ if (a) ++ free(a); ++ if (res) ++ free(res); ++ return rc; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/bootenv.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/bootenv.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/bootenv.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/bootenv.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,434 @@ ++#ifndef __BOOTENV_H__ ++#define __BOOTENV_H__ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include /* FILE */ ++#include ++#include ++ ++/* DOXYGEN DOCUMENTATION */ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * @file bootenv.h ++ * @author oliloh@de.ibm.com ++ * @version 1.3 ++ * ++ * 1.3 Some renaming ++ */ ++ ++/** ++ * @mainpage Usage ++ * ++ * @section intro Introduction ++ * This library provides all functionality to handle with the so-called ++ * platform description data (PDD) and the bootparameters defined in ++ * U-Boot. It is able to apply the defined PDD operations in PDD update ++ * scenarios. For more information about the PDD and bootparameter ++ * environment "bootenv" confer the PDD documentation. ++ * ++ * @section ret Return codes ++ * This library defines some return codes which will be delivered classified ++ * as warnings or errors. See the "Defines" section for details and numeric ++ * values. ++ * ++ * @section benv Bootenv format description ++ * There are two different input formats: ++ * - text files ++ * - binary files ++ * ++ * @subsection txt Text Files ++ * Text files have to be specified like: ++ * @verbatim key1=value1,value2,value7\n key2=value55,value1\n key4=value1\n@endverbatim ++ * ++ * @subsection bin Binary files ++ * Binary files have to be specified like: ++ * @verbatimkey1=value1,value2,value7\0key2=value55,value1\0... @endverbatim ++ * You can confer the U-Boot documentation for more details. ++ * ++ * @section benvlists Bootenv lists format description. ++ * Values referenced in the preceeding subsection can be ++ * defined like lists: ++ * @verbatim value1,value2,value3 @endverbatim ++ * There are some situation where a conversion of a comma ++ * seperated list can be useful, e.g. to get a list ++ * of defined PDD entries. ++ */ ++ ++#define BOOTENV_MAXSIZE (1024 * 100) /* max 100kiB space for bootenv */ ++ ++/** ++ * @def BOOTENV_ECRC ++ * @brief Given binary file is to large. ++ * @def BOOTENV_EFMT ++ * @brief Given bootenv section has an invalid format ++ * @def BOOTENV_EBADENTRY ++ * @brief Bad entry in the bootenv section. ++ * @def BOOTENV_EINVAL ++ * @brief Invalid bootenv defintion. ++ * @def BOOTENV_ENOPDD ++ * @brief Given bootenv sectoin has no PDD defintion string (pdd=...). ++ * @def BOOTENV_EPDDINVAL ++ * @brief Given bootenv section has an invalid PDD defintion. ++ * @def BOOTENV_ENOTIMPL ++ * @brief Functionality not implemented. ++ * @def BOOTENV_ECOPY ++ * @brief Bootenv memory copy error ++ * @def BOOTENV_ENOTFOUND ++ * @brief Given key has has no value. ++ * @def BOOTENV_EMAX ++ * @brief Highest error value. ++ */ ++#define BOOTENV_ETOOBIG 1 ++#define BOOTENV_EFMT 2 ++#define BOOTENV_EBADENTRY 3 ++#define BOOTENV_EINVAL 4 ++#define BOOTENV_ENOPDD 5 ++#define BOOTENV_EPDDINVAL 6 ++#define BOOTENV_ENOTIMPL 7 ++#define BOOTENV_ECOPY 8 ++#define BOOTENV_ENOTFOUND 9 ++#define BOOTENV_EMAX 10 ++ ++/** ++ * @def BOOTENV_W ++ * @brief A warning which is handled internally as an error ++ * but can be recovered by manual effort. ++ * @def BOOTENV_WPDD_STRING_DIFFERS ++ * @brief The PDD strings of old and new PDD differ and ++ * can cause update problems, because new PDD values ++ * are removed from the bootenv section completely. ++ */ ++#define BOOTENV_W 20 ++#define BOOTENV_WPDD_STRING_DIFFERS 21 ++#define BOOTENV_WMAX 22 /* highest warning value */ ++ ++ ++typedef struct bootenv *bootenv_t; ++ /**< A bootenv library handle. */ ++ ++typedef struct bootenv_list *bootenv_list_t; ++ /**< A handle for a value list. */ ++ ++typedef int(*pdd_func_t)(bootenv_t, bootenv_t, bootenv_t*, ++ int*, char*, size_t); ++ ++ ++/** ++ * @brief Get a new handle. ++ * @return 0 ++ * @return or error ++ * */ ++int bootenv_create(bootenv_t *env); ++ ++/** ++ * @brief Cleanup structure. ++ * @param env Bootenv structure which shall be destroyed. ++ * @return 0 ++ * @return or error ++ */ ++int bootenv_destroy(bootenv_t *env); ++ ++/** ++ * @brief Copy a bootenv handle. ++ * @param in The input bootenv. ++ * @param out The copied output bootenv. Discards old data. ++ * @return 0 ++ * @return or error ++ */ ++int bootenv_copy_bootenv(bootenv_t in, bootenv_t *out); ++ ++/** ++ * @brief Looks for a value inside the bootenv data. ++ * @param env Handle to a bootenv structure. ++ * @param key The key. ++ * @return NULL key not found ++ * @return !NULL ptr to value ++ */ ++int bootenv_get(bootenv_t env, const char *key, const char **value); ++ ++ ++/** ++ * @brief Looks for a value inside the bootenv data and converts it to num. ++ * @param env Handle to a bootenv structure. ++ * @param key The key. ++ * @param value A pointer to the resulting numerical value ++ * @return NULL key not found ++ * @return !NULL ptr to value ++ */ ++int bootenv_get_num(bootenv_t env, const char *key, uint32_t *value); ++ ++/** ++ * @brief Set a bootenv value by key. ++ * @param env Handle to a bootenv structure. ++ * @param key Key. ++ * @param value Value to set. ++ * @return 0 ++ * @return or error ++ */ ++int bootenv_set(bootenv_t env, const char *key, const char *value); ++ ++/** ++ * @brief Remove the given key (and its value) from a bootenv structure. ++ * @param env Handle to a bootenv structure. ++ * @param key Key. ++ * @return 0 ++ * @return or error ++ */ ++int bootenv_unset(bootenv_t env, const char *key); ++ ++ ++/** ++ * @brief Get a vector of all keys which are currently set ++ * within a bootenv handle. ++ * @param env Handle to a bootenv structure. ++ * @param size The size of the allocated array structure. ++ * @param sort Flag, if set the vector is sorted ascending. ++ * @return NULL on error. ++ * @return !NULL a pointer to the first element the allocated vector. ++ * @warning Free the allocate memory yourself! ++ */ ++int bootenv_get_key_vector(bootenv_t env, size_t *size, int sort, ++ const char ***vector); ++ ++/** ++ * @brief Calculate the size in bytes which are necessary to write the ++ * current bootenv section in a *binary file. ++ * @param env bootenv handle. ++ * @param size The size in bytes of the bootenv handle. ++ * @return 0 ++ * @return or ERROR. ++ */ ++int bootenv_size(bootenv_t env, size_t *size); ++ ++/** ++ * @brief Read a binary bootenv file. ++ * @param fp File pointer to input stream. ++ * @param env bootenv handle. ++ * @param size maximum data size. ++ * @return 0 ++ * @return or ERROR. ++ */ ++int bootenv_read(FILE* fp, bootenv_t env, size_t size); ++ ++/** ++ * @param ret_crc return value of crc of read data ++ */ ++int bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t *ret_crc); ++ ++/** ++ * @brief Read bootenv data from an text/ascii file. ++ * @param fp File pointer to ascii PDD file. ++ * @param env bootenv handle ++ * @return 0 ++ * @return or ERROR. ++ */ ++int bootenv_read_txt(FILE* fp, bootenv_t env); ++ ++/** ++ * @brief Write a bootenv structure to the given location (binary). ++ * @param fp Filepointer to binary file. ++ * @param env Bootenv structure which shall be written. ++ * @return 0 ++ * @return or error ++ */ ++int bootenv_write(FILE* fp, bootenv_t env); ++ ++/** ++ * @param ret_crc return value of crc of read data ++ */ ++int bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc); ++ ++/** ++ * @brief Write a bootenv structure to the given location (text). ++ * @param fp Filepointer to text file. ++ * @param env Bootenv structure which shall be written. ++ * @return 0 ++ * @return or error ++ */ ++int bootenv_write_txt(FILE* fp, bootenv_t env); ++ ++/** ++ * @brief Compare bootenvs using memcmp(). ++ * @param first First bootenv. ++ * @param second Second bootenv. ++ * @return 0 if bootenvs are equal ++ * @return < 0 if error ++ * @return > 0 if unequal ++ */ ++int bootenv_compare(bootenv_t first, bootenv_t second); ++ ++/** ++ * @brief Prototype for a PDD handling funtion ++ */ ++ ++/** ++ * @brief The PDD keep operation. ++ * @param env_old The old bootenv structure. ++ * @param env_new The new bootenv structure. ++ * @param env_res The result of PDD keep. ++ * @param warnings A flag which marks any warnings. ++ * @return 0 ++ * @return or error ++ * @note For a complete documentation about the algorithm confer the ++ * PDD documentation. ++ */ ++int bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new, ++ bootenv_t *env_res, int *warnings, ++ char *err_buf, size_t err_buf_size); ++ ++ ++/** ++ * @brief The PDD merge operation. ++ * @param env_old The old bootenv structure. ++ * @param env_new The new bootenv structure. ++ * @param env_res The result of merge-pdd. ++ * @param warnings A flag which marks any warnings. ++ * @return 0 ++ * @return or error ++ * @note For a complete documentation about the algorithm confer the ++ * PDD documentation. ++ */ ++int bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new, ++ bootenv_t *env_res, int *warnings, ++ char *err_buf, size_t err_buf_size); ++ ++/** ++ * @brief The PDD overwrite operation. ++ * @param env_old The old bootenv structure. ++ * @param env_new The new bootenv structure. ++ * @param env_res The result of overwrite-pdd. ++ * @param warnings A flag which marks any warnings. ++ * @return 0 ++ * @return or error ++ * @note For a complete documentation about the algorithm confer the ++ * PDD documentation. ++ */ ++int bootenv_pdd_overwrite(bootenv_t env_new, ++ bootenv_t env_old, bootenv_t *env_res, int *warnings, ++ char *err_buf, size_t err_buf_size); ++ ++/** ++ * @brief Dump a bootenv structure to stdout. (Debug) ++ * @param env Handle to a bootenv structure. ++ * @return 0 ++ * @return or error ++ */ ++int bootenv_dump(bootenv_t env); ++ ++/** ++ * @brief Validate a bootenv structure. ++ * @param env Handle to a bootenv structure. ++ * @return 0 ++ * @return or error ++ */ ++int bootenv_valid(bootenv_t env); ++ ++/** ++ * @brief Create a new bootenv list structure. ++ * @return NULL on error ++ * @return or a new list handle. ++ * @note This structure is used to store values in a list. ++ * A useful addition when handling PDD strings. ++ */ ++int bootenv_list_create(bootenv_list_t *list); ++ ++/** ++ * @brief Destroy a bootenv list structure ++ * @param list Handle to a bootenv list structure. ++ * @return 0 ++ * @return or error ++ */ ++int bootenv_list_destroy(bootenv_list_t *list); ++ ++/** ++ * @brief Import a list from a comma seperated string ++ * @param list Handle to a bootenv list structure. ++ * @param str Comma seperated string list. ++ * @return 0 ++ * @return or error ++ */ ++int bootenv_list_import(bootenv_list_t list, const char *str); ++ ++/** ++ * @brief Export a list to a string of comma seperated values. ++ * @param list Handle to a bootenv list structure. ++ * @return NULL one error ++ * @return or pointer to a newly allocated string. ++ * @warning Free the allocated memory by yourself! ++ */ ++int bootenv_list_export(bootenv_list_t list, char **string); ++ ++/** ++ * @brief Add an item to the list. ++ * @param list A handle of a list structure. ++ * @param item An item. ++ * @return 0 ++ * @return or error ++ */ ++int bootenv_list_add(bootenv_list_t list, const char *item); ++ ++/** ++ * @brief Remove an item from the list. ++ * @param list A handle of a list structure. ++ * @param item An item. ++ * @return 0 ++ * @return or error ++ */ ++int bootenv_list_remove(bootenv_list_t list, const char *item); ++ ++/** ++ * @brief Check if a given item is in a given list. ++ * @param list A handle of a list structure. ++ * @param item An item. ++ * @return 1 Item is in list. ++ * @return 0 Item is not in list. ++ */ ++int bootenv_list_is_in(bootenv_list_t list, const char *item); ++ ++ ++/** ++ * @brief Convert a list into a vector of all values inside the list. ++ * @param list Handle to a bootenv structure. ++ * @param size The size of the allocated vector structure. ++ * @return 0 ++ * @return or error ++ * @warning Free the allocate memory yourself! ++ */ ++int bootenv_list_to_vector(bootenv_list_t list, size_t *size, ++ const char ***vector); ++ ++/** ++ * @brief Convert a list into a vector of all values inside the list. ++ * @param list Handle to a bootenv structure. ++ * @param size The size of the allocated vector structure. ++ * @return 0 ++ * @return or error ++ * @warning Free the allocate memory yourself! ++ */ ++int bootenv_list_to_num_vector(bootenv_list_t list, size_t *size, ++ uint32_t **vector); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif /*__BOOTENV_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/config.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/config.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/config.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/config.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,28 @@ ++#ifndef __CONFIG_H__ ++#define __CONFIG_H__ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Frank Haverkamp ++ */ ++ ++#define PACKAGE_BUGREPORT \ ++ "haver@vnet.ibm.com, dedekind@linutronix.de, or tglx@linutronix.de" ++ ++#define ubi_unused __attribute__((unused)) ++ ++#endif +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/crc32.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/crc32.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/crc32.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/crc32.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,83 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Thomas Gleixner ++ */ ++ ++/* ++ * CRC32 functions ++ * ++ * Can be compiled as seperate object, but is included into the ipl source ++ * so gcc can inline the functions. We optimize for size so the omission of ++ * the function frame is helpful. ++ * ++ */ ++ ++#include ++#include ++ ++/* CRC polynomial */ ++#define CRC_POLY 0xEDB88320 ++ ++/** ++ * init_crc32_table - Initialize crc table ++ * ++ * @table: pointer to the CRC table which must be initialized ++ * ++ * Create CRC32 table for given polynomial. The table is created with ++ * the lowest order term in the highest order bit. So the x^32 term ++ * has to implied in the crc calculation function. ++ */ ++void init_crc32_table(uint32_t *table) ++{ ++ uint32_t crc; ++ int i, j; ++ ++ for (i = 0; i < 256; i++) { ++ crc = i; ++ for (j = 8; j > 0; j--) { ++ if (crc & 1) ++ crc = (crc >> 1) ^ CRC_POLY; ++ else ++ crc >>= 1; ++ } ++ table[i] = crc; ++ } ++} ++ ++/** ++ * clc_crc32 - Calculate CRC32 over a buffer ++ * ++ * @table: pointer to the CRC table ++ * @crc: initial crc value ++ * @buf: pointer to the buffer ++ * @len: number of bytes to calc ++ * ++ * Returns the updated crc value. ++ * ++ * The algorithm resembles a hardware shift register, but calculates 8 ++ * bit at once. ++ */ ++uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf, ++ int len) ++{ ++ const unsigned char *p = buf; ++ ++ while(--len >= 0) ++ crc = table[(crc ^ *p++) & 0xff] ^ (crc >> 8); ++ return crc; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/crc32.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/crc32.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/crc32.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/crc32.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,36 @@ ++#ifndef __CRC32_H__ ++#define __CRC32_H__ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * Author: Thomas Gleixner ++ * ++ * CRC32 functions ++ * ++ * Can be compiled as seperate object, but is included into the ipl source ++ * so gcc can inline the functions. We optimize for size so the omission of ++ * the function frame is helpful. ++ * ++ */ ++#include ++ ++void init_crc32_table(uint32_t *table); ++uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf, int len); ++ ++#endif /* __CRC32_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/eb_chain.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/eb_chain.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/eb_chain.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/eb_chain.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,281 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006, 2007 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * Author: Drake Dowsett, dowsett@de.ibm.com ++ * Contact: Andreas Arnez, arnez@de.ibm.com ++ */ ++ ++/* see eb_chain.h */ ++ ++#include ++#include ++#include ++#include ++#include "unubi_analyze.h" ++#include "crc32.h" ++ ++#define COPY(dst, src) \ ++ do { \ ++ dst = malloc(sizeof(*dst)); \ ++ if (dst == NULL) \ ++ return -ENOMEM; \ ++ memcpy(dst, src, sizeof(*dst)); \ ++ } while (0) ++ ++ ++/** ++ * inserts an eb_info into the chain starting at head, then searching ++ * linearly for the correct position; ++ * new should contain valid vid and ec headers and the data_crc should ++ * already have been checked before insertion, otherwise the chain ++ * could be have un an undesired manner; ++ * returns -ENOMEM if alloc fails, otherwise SHOULD always return 0, ++ * if not, the code reached the last line and returned -EAGAIN, ++ * meaning there is a bug or a case not being handled here; ++ **/ ++int ++eb_chain_insert(struct eb_info **head, struct eb_info *new) ++{ ++ uint32_t vol, num, ver; ++ uint32_t new_vol, new_num, new_ver; ++ struct eb_info *prev, *cur, *hist, *ins; ++ struct eb_info **prev_ptr; ++ ++ if ((head == NULL) || (new == NULL)) ++ return 0; ++ ++ if (*head == NULL) { ++ COPY(*head, new); ++ (*head)->next = NULL; ++ return 0; ++ } ++ ++ new_vol = be32_to_cpu(new->vid.vol_id); ++ new_num = be32_to_cpu(new->vid.lnum); ++ new_ver = be32_to_cpu(new->vid.leb_ver); ++ ++ /** TRAVERSE HORIZONTALY **/ ++ ++ cur = *head; ++ prev = NULL; ++ ++ /* traverse until vol_id/lnum align */ ++ vol = be32_to_cpu(cur->vid.vol_id); ++ num = be32_to_cpu(cur->vid.lnum); ++ while ((new_vol > vol) || ((new_vol == vol) && (new_num > num))) { ++ /* insert new at end of chain */ ++ if (cur->next == NULL) { ++ COPY(ins, new); ++ ins->next = NULL; ++ cur->next = ins; ++ return 0; ++ } ++ ++ prev = cur; ++ cur = cur->next; ++ vol = be32_to_cpu(cur->vid.vol_id); ++ num = be32_to_cpu(cur->vid.lnum); ++ } ++ ++ if (prev == NULL) ++ prev_ptr = head; ++ else ++ prev_ptr = &(prev->next); ++ ++ /* insert new into the middle of chain */ ++ if ((new_vol != vol) || (new_num != num)) { ++ COPY(ins, new); ++ ins->next = cur; ++ *prev_ptr = ins; ++ return 0; ++ } ++ ++ /** TRAVERSE VERTICALY **/ ++ ++ hist = cur; ++ prev = NULL; ++ ++ /* traverse until versions align */ ++ ver = be32_to_cpu(cur->vid.leb_ver); ++ while (new_ver < ver) { ++ /* insert new at bottom of history */ ++ if (hist->older == NULL) { ++ COPY(ins, new); ++ ins->next = NULL; ++ ins->older = NULL; ++ hist->older = ins; ++ return 0; ++ } ++ ++ prev = hist; ++ hist = hist->older; ++ ver = be32_to_cpu(hist->vid.leb_ver); ++ } ++ ++ if (prev == NULL) { ++ /* replace active version */ ++ COPY(ins, new); ++ ins->next = hist->next; ++ *prev_ptr = ins; ++ ++ /* place cur in vertical histroy */ ++ ins->older = hist; ++ hist->next = NULL; ++ return 0; ++ } ++ ++ /* insert between versions, beneath active version */ ++ COPY(ins, new); ++ ins->next = NULL; ++ ins->older = prev->older; ++ prev->older = ins; ++ return 0; ++} ++ ++ ++/** ++ * sets the pointer at pos to the position of the first entry in the chain ++ * with of vol_id and, if given, with the same lnum as *lnum; ++ * if there is no entry in the chain, then *pos is NULL on return; ++ * always returns 0; ++ **/ ++int ++eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum, ++ struct eb_info **pos) ++{ ++ uint32_t vol, num; ++ struct eb_info *cur; ++ ++ if ((head == NULL) || (*head == NULL) || (pos == NULL)) ++ return 0; ++ ++ *pos = NULL; ++ ++ cur = *head; ++ while (cur != NULL) { ++ vol = be32_to_cpu(cur->vid.vol_id); ++ num = be32_to_cpu(cur->vid.lnum); ++ ++ if ((vol_id == vol) && ((lnum == NULL) || (*lnum == num))) { ++ *pos = cur; ++ return 0; ++ } ++ ++ cur = cur->next; ++ } ++ ++ return 0; ++} ++ ++ ++/** ++ * prints to stream, the vol_id, lnum and leb_ver for each entry in the ++ * chain, starting at head; ++ * this is intended for debuging purposes; ++ * always returns 0; ++ * ++ * FIXME I do not like the double list traversion ... ++ **/ ++int ++eb_chain_print(FILE* stream, struct eb_info *head) ++{ ++ struct eb_info *cur; ++ ++ if (stream == NULL) ++ stream = stdout; ++ ++ if (head == NULL) { ++ fprintf(stream, "EMPTY\n"); ++ return 0; ++ } ++ /* 012345678012345678012345678012301230123 0123 01234567 0123457 01234567*/ ++ fprintf(stream, "VOL_ID LNUM LEB_VER EC VID DAT PBLK PADDR DSIZE EC\n"); ++ cur = head; ++ while (cur != NULL) { ++ struct eb_info *hist; ++ ++ fprintf(stream, "%08x %-8u %08x %-4s%-4s", ++ be32_to_cpu(cur->vid.vol_id), ++ be32_to_cpu(cur->vid.lnum), ++ be32_to_cpu(cur->vid.leb_ver), ++ cur->ec_crc_ok ? "ok":"bad", ++ cur->vid_crc_ok ? "ok":"bad"); ++ if (cur->vid.vol_type == UBI_VID_STATIC) ++ fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"bad"); ++ else fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"ign"); ++ fprintf(stream, " %-4d %08x %-8u %-8llu\n", cur->phys_block, ++ cur->phys_addr, be32_to_cpu(cur->vid.data_size), ++ (unsigned long long)be64_to_cpu(cur->ec.ec)); ++ ++ hist = cur->older; ++ while (hist != NULL) { ++ fprintf(stream, "%08x %-8u %08x %-4s%-4s", ++ be32_to_cpu(hist->vid.vol_id), ++ be32_to_cpu(hist->vid.lnum), ++ be32_to_cpu(hist->vid.leb_ver), ++ hist->ec_crc_ok ? "ok":"bad", ++ hist->vid_crc_ok ? "ok":"bad"); ++ if (hist->vid.vol_type == UBI_VID_STATIC) ++ fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"bad"); ++ else fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"ign"); ++ fprintf(stream, " %-4d %08x %-8u %-8llu (*)\n", ++ hist->phys_block, hist->phys_addr, ++ be32_to_cpu(hist->vid.data_size), ++ (unsigned long long)be64_to_cpu(hist->ec.ec)); ++ ++ hist = hist->older; ++ } ++ cur = cur->next; ++ } ++ ++ return 0; ++} ++ ++ ++/** ++ * frees the memory of the entire chain, starting at head; ++ * head will be NULL on return; ++ * always returns 0; ++ **/ ++int ++eb_chain_destroy(struct eb_info **head) ++{ ++ if (head == NULL) ++ return 0; ++ ++ while (*head != NULL) { ++ struct eb_info *cur; ++ struct eb_info *hist; ++ ++ cur = *head; ++ *head = (*head)->next; ++ ++ hist = cur->older; ++ while (hist != NULL) { ++ struct eb_info *temp; ++ ++ temp = hist; ++ hist = hist->older; ++ free(temp); ++ } ++ free(cur); ++ } ++ return 0; ++} ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/error.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/error.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/error.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/error.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,240 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "error.h" ++ ++#define MAXLINE 4096 ++#define MAXWIDTH 80 ++ ++static FILE *logfp = NULL; ++ ++static void err_doit(int, int, const char *, va_list); ++ ++int ++read_procfile(FILE *fp_out, const char *procfile) ++{ ++ FILE *fp; ++ ++ if (!fp_out) ++ return -ENXIO; ++ ++ fp = fopen(procfile, "r"); ++ if (!fp) ++ return -ENOENT; ++ ++ while(!feof(fp)) { ++ int c = fgetc(fp); ++ ++ if (c == EOF) ++ return 0; ++ ++ if (putc(c, fp_out) == EOF) ++ return -EIO; ++ ++ if (ferror(fp)) ++ return -EIO; ++ } ++ return fclose(fp); ++} ++ ++void ++error_initlog(const char *logfile) ++{ ++ if (!logfile) ++ return; ++ ++ logfp = fopen(logfile, "a+"); ++ read_procfile(logfp, "/proc/cpuinfo"); ++} ++ ++void ++info_msg(const char *fmt, ...) ++{ ++ FILE* fpout; ++ char buf[MAXLINE + 1]; ++ va_list ap; ++ int n; ++ ++ fpout = stdout; ++ ++ va_start(ap, fmt); ++ vsnprintf(buf, MAXLINE, fmt, ap); ++ n = strlen(buf); ++ strcat(buf, "\n"); ++ ++ fputs(buf, fpout); ++ fflush(fpout); ++ if (fpout != stdout) ++ fclose(fpout); ++ ++ va_end(ap); ++ return; ++} ++ ++void ++__err_ret(const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ err_doit(1, LOG_INFO, fmt, ap); ++ va_end(ap); ++ return; ++} ++ ++void ++__err_sys(const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ err_doit(1, LOG_ERR, fmt, ap); ++ va_end(ap); ++ exit(EXIT_FAILURE); ++} ++ ++ ++void ++__err_msg(const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ err_doit(0, LOG_INFO, fmt, ap); ++ va_end(ap); ++ ++ return; ++} ++ ++void ++__err_quit(const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ err_doit(0, LOG_ERR, fmt, ap); ++ va_end(ap); ++ exit(EXIT_FAILURE); ++} ++ ++void ++__err_dump(const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ err_doit(1, LOG_ERR, fmt, ap); ++ va_end(ap); ++ abort(); /* dump core and terminate */ ++ exit(EXIT_FAILURE); /* shouldn't get here */ ++} ++ ++/** ++ * If a logfile is used we must not print on stderr and stdout ++ * anymore. Since pfilfash might be used in a server context, it is ++ * even dangerous to write to those descriptors. ++ */ ++static void ++err_doit(int errnoflag, int level __attribute__((unused)), ++ const char *fmt, va_list ap) ++{ ++ FILE* fpout; ++ int errno_save, n; ++ char buf[MAXLINE + 1]; ++ fpout = stderr; ++ ++ errno_save = errno; /* value caller might want printed */ ++ ++ vsnprintf(buf, MAXLINE, fmt, ap); /* safe */ ++ ++ n = strlen(buf); ++ ++ if (errnoflag) ++ snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save)); ++ strcat(buf, "\n"); ++ ++ if (logfp) { ++ fputs(buf, logfp); ++ fflush(logfp); ++ return; /* exit when logging completes */ ++ } ++ ++ if (fpout == stderr) { ++ /* perform line wrap when outputting to stderr */ ++ int word_len, post_len, chars; ++ char *buf_ptr; ++ const char *frmt = "%*s%n %n"; ++ ++ chars = 0; ++ buf_ptr = buf; ++ while (sscanf(buf_ptr, frmt, &word_len, &post_len) != EOF) { ++ int i; ++ char word[word_len + 1]; ++ char post[post_len + 1]; ++ ++ strncpy(word, buf_ptr, word_len); ++ word[word_len] = '\0'; ++ buf_ptr += word_len; ++ post_len -= word_len; ++ ++ if (chars + word_len > MAXWIDTH) { ++ fputc('\n', fpout); ++ chars = 0; ++ } ++ fputs(word, fpout); ++ chars += word_len; ++ ++ if (post_len > 0) { ++ strncpy(post, buf_ptr, post_len); ++ post[post_len] = '\0'; ++ buf_ptr += post_len; ++ } ++ for (i = 0; i < post_len; i++) { ++ int inc = 1, chars_new; ++ ++ if (post[i] == '\t') ++ inc = 8; ++ if (post[i] == '\n') { ++ inc = 0; ++ chars_new = 0; ++ } else ++ chars_new = chars + inc; ++ ++ if (chars_new > MAXWIDTH) { ++ fputc('\n', fpout); ++ chars_new = inc; ++ } ++ fputc(post[i], fpout); ++ chars = chars_new; ++ } ++ } ++ } ++ else ++ fputs(buf, fpout); ++ fflush(fpout); ++ if (fpout != stderr) ++ fclose(fpout); ++ ++ return; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/error.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/error.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/error.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/error.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,84 @@ ++#ifndef __ERROR_H__ ++#define __ERROR_H__ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++ ++void error_initlog(const char *logfile); ++int read_procfile(FILE *fp_out, const char *procfile); ++ ++void __err_ret(const char *fmt, ...); ++void __err_sys(const char *fmt, ...); ++void __err_msg(const char *fmt, ...); ++void __err_quit(const char *fmt, ...); ++void __err_dump(const char *fmt, ...); ++ ++void info_msg(const char *fmt, ...); ++ ++#ifdef DEBUG ++#define __loc_msg(str) do { \ ++ __err_msg("[%s. FILE: %s FUNC: %s LINE: %d]\n", \ ++ str, __FILE__, __FUNCTION__, __LINE__); \ ++} while (0) ++#else ++#define __loc_msg(str) ++#endif ++ ++ ++#define err_dump(fmt, ...) do { \ ++ __loc_msg("ErrDump"); \ ++ __err_dump(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define err_quit(fmt, ...) do { \ ++ __loc_msg("ErrQuit"); \ ++ __err_quit(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++ ++#define err_ret(fmt, ...) do { \ ++ __loc_msg("ErrRet"); \ ++ __err_ret(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define err_sys(fmt, ...) do { \ ++ __loc_msg("ErrSys"); \ ++ __err_sys(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define err_msg(fmt, ...) do { \ ++ __loc_msg("ErrMsg"); \ ++ __err_msg(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define log_msg(fmt, ...) do { \ ++ /* __loc_msg("LogMsg"); */ \ ++ __err_msg(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#ifdef DEBUG ++#define dbg_msg(fmt, ...) do { \ ++ __loc_msg("DbgMsg"); \ ++ __err_msg(fmt, ##__VA_ARGS__); \ ++} while (0) ++#else ++#define dbg_msg(fmt, ...) do {} while (0) ++#endif ++ ++#endif /* __ERROR_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/example_ubi.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/example_ubi.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/example_ubi.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/example_ubi.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,28 @@ ++#ifndef __EXAMPLE_UBI_H__ ++#define __EXAMPLE_UBI_H__ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/** ++ * Defaults for our cards. ++ */ ++#define EXAMPLE_UBI_DEVICE 0 ++#define EXAMPLE_BOOTENV_VOL_ID_1 4 ++#define EXAMPLE_BOOTENV_VOL_ID_2 5 ++ ++#endif /* __EXAMPLE_UBI_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/hashmap.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/hashmap.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/hashmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/hashmap.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,412 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ */ ++ ++#include ++#include ++#include ++#include ++#include "error.h" ++#include "hashmap.h" ++#define DEFAULT_BUCKETS 4096 ++ ++#if 0 ++#define INFO_MSG(fmt...) do { \ ++ info_msg(fmt); \ ++} while (0) ++#else ++#define INFO_MSG(fmt...) ++#endif ++ ++struct hashentry { ++ char* key; /* key '0' term. str */ ++ char* value; /* payload '0' term. str */ ++ ++ hashentry_t next; ++}; ++ ++struct hashmap { ++ size_t entries; /* current #entries */ ++ size_t maxsize; /* no. of hash buckets */ ++ hashentry_t* data; /* array of buckets */ ++}; ++ ++static int ++is_empty(hashentry_t l) ++{ ++ return l == NULL ? 1 : 0; ++} ++ ++hashmap_t ++hashmap_new(void) ++{ ++ hashmap_t res; ++ res = (hashmap_t) calloc(1, sizeof(struct hashmap)); ++ ++ if (res == NULL) ++ return NULL; ++ ++ res->maxsize = DEFAULT_BUCKETS; ++ res->entries = 0; ++ ++ res->data = (hashentry_t*) ++ calloc(1, res->maxsize * sizeof(struct hashentry)); ++ ++ if (res->data == NULL) ++ return NULL; ++ ++ return res; ++} ++ ++static hashentry_t ++new_entry(const char* key, const char* value) ++{ ++ hashentry_t res; ++ ++ res = (hashentry_t) calloc(1, sizeof(struct hashentry)); ++ ++ if (res == NULL) ++ return NULL; ++ ++ /* allocate key and value and copy them */ ++ res->key = strdup(key); ++ if (res->key == NULL) { ++ free(res); ++ return NULL; ++ } ++ ++ res->value = strdup(value); ++ if (res->value == NULL) { ++ free(res->key); ++ free(res); ++ return NULL; ++ } ++ ++ res->next = NULL; ++ ++ return res; ++} ++ ++static hashentry_t ++free_entry(hashentry_t e) ++{ ++ if (!is_empty(e)) { ++ if(e->key != NULL) { ++ free(e->key); ++ } ++ if(e->value != NULL) ++ free(e->value); ++ free(e); ++ } ++ ++ return NULL; ++} ++ ++static hashentry_t ++remove_entry(hashentry_t l, const char* key, size_t* entries) ++{ ++ hashentry_t lnext; ++ if (is_empty(l)) ++ return NULL; ++ ++ if(strcmp(l->key,key) == 0) { ++ lnext = l->next; ++ l = free_entry(l); ++ (*entries)--; ++ return lnext; ++ } ++ ++ l->next = remove_entry(l->next, key, entries); ++ ++ return l; ++} ++ ++static hashentry_t ++insert_entry(hashentry_t l, hashentry_t e, size_t* entries) ++{ ++ if (is_empty(l)) { ++ (*entries)++; ++ return e; ++ } ++ ++ /* check for update */ ++ if (strcmp(l->key, e->key) == 0) { ++ e->next = l->next; ++ l = free_entry(l); ++ return e; ++ } ++ ++ l->next = insert_entry(l->next, e, entries); ++ return l; ++} ++ ++static hashentry_t ++remove_all(hashentry_t l, size_t* entries) ++{ ++ hashentry_t lnext; ++ if (is_empty(l)) ++ return NULL; ++ ++ lnext = l->next; ++ free_entry(l); ++ (*entries)--; ++ ++ return remove_all(lnext, entries); ++} ++ ++static const char* ++value_lookup(hashentry_t l, const char* key) ++{ ++ if (is_empty(l)) ++ return NULL; ++ ++ if (strcmp(l->key, key) == 0) ++ return l->value; ++ ++ return value_lookup(l->next, key); ++} ++ ++static void ++print_all(hashentry_t l) ++{ ++ if (is_empty(l)) { ++ printf("\n"); ++ return; ++ } ++ ++ printf("%s=%s", l->key, l->value); ++ if (!is_empty(l->next)) { ++ printf(","); ++ } ++ ++ print_all(l->next); ++} ++ ++static void ++keys_to_array(hashentry_t l, const char** a, size_t* i) ++{ ++ if (is_empty(l)) ++ return; ++ ++ a[*i] = l->key; ++ (*i)++; ++ ++ keys_to_array(l->next, a, i); ++} ++ ++uint32_t ++hash_str(const char* str, uint32_t mapsize) ++{ ++ uint32_t hash = 0; ++ uint32_t x = 0; ++ uint32_t i = 0; ++ size_t len = strlen(str); ++ ++ for(i = 0; i < len; str++, i++) { ++ hash = (hash << 4) + (*str); ++ if((x = hash & 0xF0000000L) != 0) { ++ hash ^= (x >> 24); ++ hash &= ~x; ++ } ++ } ++ ++ return (hash & 0x7FFFFFFF) % mapsize; ++} ++ ++ ++int ++hashmap_is_empty(hashmap_t map) ++{ ++ if (map == NULL) ++ return -EINVAL; ++ ++ return map->entries > 0 ? 1 : 0; ++} ++ ++const char* ++hashmap_lookup(hashmap_t map, const char* key) ++{ ++ uint32_t i; ++ ++ if ((map == NULL) || (key == NULL)) ++ return NULL; ++ ++ i = hash_str(key, map->maxsize); ++ ++ return value_lookup(map->data[i], key); ++} ++ ++int ++hashmap_add(hashmap_t map, const char* key, const char* value) ++{ ++ uint32_t i; ++ hashentry_t entry; ++ ++ if ((map == NULL) || (key == NULL) || (value == NULL)) ++ return -EINVAL; ++ ++ i = hash_str(key, map->maxsize); ++ entry = new_entry(key, value); ++ if (entry == NULL) ++ return -ENOMEM; ++ ++ map->data[i] = insert_entry(map->data[i], ++ entry, &map->entries); ++ ++ INFO_MSG("HASH_ADD: chain[%d] key:%s val:%s",i, key, value); ++ return 0; ++} ++ ++int ++hashmap_remove(hashmap_t map, const char* key) ++{ ++ uint32_t i; ++ ++ if ((map == NULL) || (key == NULL)) ++ return -EINVAL; ++ ++ i = hash_str(key, map->maxsize); ++ map->data[i] = remove_entry(map->data[i], key, &map->entries); ++ ++ return 0; ++} ++ ++size_t ++hashmap_size(hashmap_t map) ++{ ++ if (map != NULL) ++ return map->entries; ++ else ++ return 0; ++} ++ ++int ++hashmap_free(hashmap_t map) ++{ ++ size_t i; ++ ++ if (map == NULL) ++ return -EINVAL; ++ ++ /* "children" first */ ++ for(i = 0; i < map->maxsize; i++) { ++ map->data[i] = remove_all(map->data[i], &map->entries); ++ } ++ free(map->data); ++ free(map); ++ ++ return 0; ++} ++ ++int ++hashmap_dump(hashmap_t map) ++{ ++ size_t i; ++ if (map == NULL) ++ return -EINVAL; ++ ++ for(i = 0; i < map->maxsize; i++) { ++ if (map->data[i] != NULL) { ++ printf("[%zd]: ", i); ++ print_all(map->data[i]); ++ } ++ } ++ ++ return 0; ++} ++ ++static const char** ++sort_key_vector(const char** a, size_t size) ++{ ++ /* uses bubblesort */ ++ size_t i, j; ++ const char* tmp; ++ ++ if (size <= 0) ++ return a; ++ ++ for (i = size - 1; i > 0; i--) { ++ for (j = 0; j < i; j++) { ++ if (strcmp(a[j], a[j+1]) > 0) { ++ tmp = a[j]; ++ a[j] = a[j+1]; ++ a[j+1] = tmp; ++ } ++ } ++ } ++ return a; ++} ++ ++const char** ++hashmap_get_key_vector(hashmap_t map, size_t* size, int sort) ++{ ++ const char** res; ++ size_t i, j; ++ *size = map->entries; ++ ++ res = (const char**) malloc(*size * sizeof(char*)); ++ if (res == NULL) ++ return NULL; ++ ++ j = 0; ++ for(i=0; i < map->maxsize; i++) { ++ keys_to_array(map->data[i], res, &j); ++ } ++ ++ if (sort) ++ res = sort_key_vector(res, *size); ++ ++ return res; ++} ++ ++int ++hashmap_key_is_in_vector(const char** vec, size_t size, const char* key) ++{ ++ size_t i; ++ for (i = 0; i < size; i++) { ++ if (strcmp(vec[i], key) == 0) /* found */ ++ return 1; ++ } ++ ++ return 0; ++} ++ ++const char** ++hashmap_get_update_key_vector(const char** vec1, size_t vec1_size, ++ const char** vec2, size_t vec2_size, size_t* res_size) ++{ ++ const char** res; ++ size_t i, j; ++ ++ *res_size = vec2_size; ++ ++ res = (const char**) malloc(*res_size * sizeof(char*)); ++ if (res == NULL) ++ return NULL; ++ ++ /* get all keys from vec2 which are not set in vec1 */ ++ j = 0; ++ for (i = 0; i < vec2_size; i++) { ++ if (!hashmap_key_is_in_vector(vec1, vec1_size, vec2[i])) ++ res[j++] = vec2[i]; ++ } ++ ++ *res_size = j; ++ return res; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/hashmap.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/hashmap.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/hashmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/hashmap.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,49 @@ ++#ifndef __HASHMAP_H__ ++#define __HASHMAP_H__ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ */ ++ ++#include ++#include ++ ++typedef struct hashentry *hashentry_t; ++typedef struct hashmap *hashmap_t; ++ ++hashmap_t hashmap_new(void); ++int hashmap_free(hashmap_t map); ++ ++int hashmap_add(hashmap_t map, const char* key, const char* value); ++int hashmap_update(hashmap_t map, const char* key, const char* value); ++int hashmap_remove(hashmap_t map, const char* key); ++const char* hashmap_lookup(hashmap_t map, const char* key); ++ ++const char** hashmap_get_key_vector(hashmap_t map, size_t* size, int sort); ++int hashmap_key_is_in_vector(const char** vec, size_t size, const char* key); ++const char** hashmap_get_update_key_vector(const char** vec1, size_t vec1_size, ++ const char** vec2, size_t vec2_size, size_t* res_size); ++ ++int hashmap_dump(hashmap_t map); ++ ++int hashmap_is_empty(hashmap_t map); ++size_t hashmap_size(hashmap_t map); ++ ++uint32_t hash_str(const char* str, uint32_t mapsize); ++ ++#endif /* __HASHMAP_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libpfiflash.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libpfiflash.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libpfiflash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libpfiflash.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1325 @@ ++/* ++ * Copyright International Business Machines Corp., 2006, 2007 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * Authors: Oliver Lohmann ++ * Drake Dowsett ++ * Contact: Andreas Arnez ++ */ ++ ++/* TODO Compare data before writing it. This implies that the volume ++ * parameters are compared first: size, alignment, name, type, ..., ++ * this is the same, compare the data. Volume deletion is deffered ++ * until the difference has been found out. ++ */ ++ ++#include ++#include ++#include ++#include ++#define __USE_GNU ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include /* FIXME Is this ok here? */ ++#include ++ ++#include "pfiflash_error.h" ++#include "ubimirror.h" ++#include "error.h" ++#include "reader.h" ++#include "example_ubi.h" ++#include "bootenv.h" ++ ++/* ubi-header.h and crc32.h needed for CRC checking */ ++#include /* FIXME Is this ok here? */ ++#include "crc32.h" ++ ++#define ubi_unused __attribute__((unused)) ++ ++#define COMPARE_BUFFER_SIZE 2048 ++ ++#define DEFAULT_DEV_PATTERN "/dev/ubi%d" ++#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d" ++ ++static const char copyright [] ubi_unused = ++ "Copyright International Business Machines Corp., 2006, 2007"; ++ ++/* simply clear buffer, then write into front of it */ ++#define EBUF(fmt...) \ ++ snprintf(err_buf, err_buf_size, fmt); ++ ++/* make a history of buffer and then prepend something in front */ ++#define EBUF_PREPEND(fmt) \ ++ do { \ ++ int EBUF_HISTORY_LENGTH = strlen(err_buf); \ ++ char EBUF_HISTORY[EBUF_HISTORY_LENGTH + 1]; \ ++ strncpy(EBUF_HISTORY, err_buf, EBUF_HISTORY_LENGTH + 1);\ ++ EBUF(fmt ": %s", EBUF_HISTORY); \ ++ } while (0) ++ ++/* An array of PDD function pointers indexed by the algorithm. */ ++static pdd_func_t pdd_funcs[PDD_HANDLING_NUM] = ++ { ++ &bootenv_pdd_keep, ++ &bootenv_pdd_merge, ++ &bootenv_pdd_overwrite ++ }; ++ ++typedef enum ubi_update_process_t { ++ UBI_REMOVE = 0, ++ UBI_WRITE, ++ UBI_COMPARE, ++} ubi_update_process_t; ++ ++ ++/** ++ * skip_raw_volumes - reads data from pfi to advance fp past raw block ++ * @pfi: fp to pfi data ++ * @pfi_raws: header information ++ * ++ * Error handling): ++ * when early EOF in pfi data ++ * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err ++ * when file I/O error ++ * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err ++ **/ ++static int ++skip_raw_volumes(FILE* pfi, list_t pfi_raws, ++ char* err_buf, size_t err_buf_size) ++{ ++ int rc; ++ void *i; ++ list_t ptr; ++ ++ if (is_empty(pfi_raws)) ++ return 0; ++ ++ rc = 0; ++ foreach(i, ptr, pfi_raws) { ++ size_t j; ++ pfi_raw_t raw; ++ ++ raw = (pfi_raw_t)i; ++ for(j = 0; j < raw->data_size; j++) { ++ int c; ++ ++ c = fgetc(pfi); ++ if (c == EOF) ++ rc = -PFIFLASH_ERR_EOF; ++ else if (ferror(pfi)) ++ rc = -PFIFLASH_ERR_FIO; ++ ++ if (rc != 0) ++ goto err; ++ } ++ } ++ ++ err: ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ return rc; ++} ++ ++ ++/** ++ * my_ubi_mkvol - wraps the ubi_mkvol functions and impl. bootenv update hook ++ * @devno: UBI device number. ++ * @s: Current seqnum. ++ * @u: Information about the UBI volume from the PFI. ++ * ++ * Error handling: ++ * when UBI system couldn't be opened ++ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err ++ * when UBI system couldn't create a volume ++ * - returns -PFIFLASH_ERR_UBI_MKVOL, err_buf matches text to err ++ **/ ++static int ++my_ubi_mkvol(int devno, int s, pfi_ubi_t u, ++ char *err_buf, size_t err_buf_size) ++{ ++ int rc, type; ++ char path[PATH_MAX]; ++ libubi_t ulib; ++ struct ubi_mkvol_request req; ++ ++ rc = 0; ++ ulib = NULL; ++ ++ log_msg("[ ubimkvol id=%d, size=%d, data_size=%d, type=%d, " ++ "alig=%d, nlen=%d, name=%s", ++ u->ids[s], u->size, u->data_size, u->type, u->alignment, ++ strnlen(u->names[s], PFI_UBI_VOL_NAME_LEN), u->names[s]); ++ ++ ulib = libubi_open(); ++ if (ulib == NULL) { ++ rc = -PFIFLASH_ERR_UBI_OPEN; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ ++ switch (u->type) { ++ case pfi_ubi_static: ++ type = UBI_STATIC_VOLUME; break; ++ case pfi_ubi_dynamic: ++ default: ++ type = UBI_DYNAMIC_VOLUME; ++ } ++ ++ snprintf(path, PATH_MAX, DEFAULT_DEV_PATTERN, devno); ++ ++ req.vol_id = u->ids[s]; ++ req.alignment = u->alignment; ++ req.bytes = u->size; ++ req.vol_type = type; ++ req.name = u->names[s]; ++ ++ rc = ubi_mkvol(ulib, path, &req); ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_UBI_MKVOL; ++ EBUF(PFIFLASH_ERRSTR[-rc], u->ids[s]); ++ goto err; ++ } ++ ++ err: ++ if (ulib != NULL) ++ libubi_close(ulib); ++ ++ return rc; ++} ++ ++ ++/** ++ * my_ubi_rmvol - a wrapper around the UBI library function ubi_rmvol ++ * @devno UBI device number ++ * @id UBI volume id to remove ++ * ++ * If the volume does not exist, the function will return success. ++ * ++ * Error handling: ++ * when UBI system couldn't be opened ++ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err ++ * when UBI system couldn't update (truncate) a volume ++ * - returns -PFIFLASH_ERR_UBI_VOL_UPDATE, err_buf matches text to err ++ * when UBI system couldn't remove a volume ++ * - returns -PFIFLASH_ERR_UBI_RMVOL, err_buf matches text to err ++ **/ ++static int ++my_ubi_rmvol(int devno, uint32_t id, ++ char *err_buf, size_t err_buf_size) ++{ ++ int rc, fd; ++ char path[PATH_MAX]; ++ libubi_t ulib; ++ ++ rc = 0; ++ ulib = NULL; ++ ++ log_msg("[ ubirmvol id=%d", id); ++ ++ ulib = libubi_open(); ++ if (ulib == NULL) { ++ rc = -PFIFLASH_ERR_UBI_OPEN; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ ++ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); ++ ++ /* truncate whether it exist or not */ ++ fd = open(path, O_RDWR); ++ if (fd < 0) { ++ libubi_close(ulib); ++ return 0; /* not existent, return 0 */ ++ } ++ ++ rc = ubi_update_start(ulib, fd, 0); ++ close(fd); ++ if (rc < 0) { ++ rc = -PFIFLASH_ERR_UBI_VOL_UPDATE; ++ EBUF(PFIFLASH_ERRSTR[-rc], id); ++ goto err; /* if EBUSY than empty device, continue */ ++ } ++ ++ snprintf(path, PATH_MAX, DEFAULT_DEV_PATTERN, devno); ++ ++ rc = ubi_rmvol(ulib, path, id); ++ if (rc != 0) { ++#ifdef DEBUG ++ int rc_old = rc; ++ dbg_msg("Remove UBI volume %d returned with error: %d " ++ "errno=%d", id, rc_old, errno); ++#endif ++ ++ rc = -PFIFLASH_ERR_UBI_RMVOL; ++ EBUF(PFIFLASH_ERRSTR[-rc], id); ++ ++ /* TODO Define a ubi_rmvol return value which says ++ * sth like EUBI_NOSUCHDEV. In this case, a failed ++ * operation is acceptable. Everything else has to be ++ * classified as real error. But talk to Andreas Arnez ++ * before defining something odd... ++ */ ++ /* if ((errno == EINVAL) || (errno == ENODEV)) ++ return 0; */ /* currently it is EINVAL or ENODEV */ ++ ++ goto err; ++ } ++ ++ err: ++ if (ulib != NULL) ++ libubi_close(ulib); ++ ++ return rc; ++} ++ ++ ++/** ++ * read_bootenv_volume - reads the current bootenv data from id into be_old ++ * @devno UBI device number ++ * @id UBI volume id to remove ++ * @bootenv_old to hold old boot_env data ++ * ++ * Error handling: ++ * when UBI system couldn't be opened ++ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err ++ * when UBI system couldn't open a volume to read ++ * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err ++ * when couldn't read bootenv data ++ * - returns -PFIFLASH_ERR_BOOTENV_READ, err_buf matches text to err ++ **/ ++static int ++read_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, ++ char *err_buf, size_t err_buf_size) ++{ ++ int rc; ++ FILE* fp_in; ++ char path[PATH_MAX]; ++ libubi_t ulib; ++ ++ rc = 0; ++ fp_in = NULL; ++ ulib = NULL; ++ ++ ulib = libubi_open(); ++ if (ulib == NULL) { ++ rc = -PFIFLASH_ERR_UBI_OPEN; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ ++ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); ++ ++ fp_in = fopen(path, "r"); ++ if (!fp_in) { ++ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; ++ EBUF(PFIFLASH_ERRSTR[-rc], id); ++ goto err; ++ } ++ ++ log_msg("[ reading old bootenvs ..."); ++ ++ /* Save old bootenvs for reference */ ++ rc = bootenv_read(fp_in, bootenv_old, BOOTENV_MAXSIZE); ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_BOOTENV_READ; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ ++ err: ++ if (fp_in) ++ fclose(fp_in); ++ if (ulib) ++ libubi_close(ulib); ++ ++ return rc; ++} ++ ++ ++/** ++ * write_bootenv_volume - writes data from PFI file int to bootenv UBI volume ++ * @devno UBI device number ++ * @id UBI volume id ++ * @bootend_old old PDD data from machine ++ * @pdd_f function to handle PDD with ++ * @fp_in new pdd data contained in PFI ++ * @fp_in_size data size of new pdd data in PFI ++ * @pfi_crc crc value from PFI header ++ * ++ * Error handling: ++ * when UBI system couldn't be opened ++ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err ++ * when bootenv can't be created ++ * - returns -PFIFLASH_ERR_BOOTENV_CREATE, err_buf matches text to err ++ * when bootenv can't be read ++ * - returns -PFIFLASH_ERR_BOOTENV_READ, err_buf matches text to err ++ * when PDD handling function returns and error ++ * - passes rc and err_buf data ++ * when CRC check fails ++ * - returns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err ++ * when bootenv can't be resized ++ * - returns -PFIFLASH_ERR_BOOTENV_SIZE, err_buf matches text to err ++ * when UBI system couldn't open a volume ++ * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err ++ * when couldn't write bootenv data ++ * - returns -PFIFLASH_ERR_BOOTENV_WRITE, err_buf matches text to err ++ **/ ++static int ++write_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, ++ pdd_func_t pdd_f, FILE* fp_in, size_t fp_in_size, ++ uint32_t pfi_crc, ++ char *err_buf, size_t err_buf_size) ++{ ++ int rc, warnings, fd_out; ++ uint32_t crc; ++ char path[PATH_MAX]; ++ size_t update_size; ++ FILE *fp_out; ++ bootenv_t bootenv_new, bootenv_res; ++ libubi_t ulib; ++ ++ rc = 0; ++ warnings = 0; ++ crc = 0; ++ update_size = 0; ++ fp_out = NULL; ++ bootenv_new = NULL; ++ bootenv_res = NULL; ++ ulib = NULL; ++ ++ log_msg("[ ubiupdatevol bootenv id=%d, fp_in=%p", id, fp_in); ++ ++ /* Workflow: ++ * 1. Apply PDD operation and get the size of the returning ++ * bootenv_res section. Without the correct size it wouldn't ++ * be possible to call UBI update vol. ++ * 2. Call UBI update vol ++ * 3. Get FILE* to vol dev ++ * 4. Write to FILE* ++ */ ++ ++ ulib = libubi_open(); ++ if (ulib == NULL) { ++ rc = -PFIFLASH_ERR_UBI_OPEN; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ ++ rc = bootenv_create(&bootenv_new); ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_BOOTENV_CREATE; ++ EBUF(PFIFLASH_ERRSTR[-rc], " 'new'"); ++ goto err; ++ } ++ ++ rc = bootenv_create(&bootenv_res); ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_BOOTENV_CREATE; ++ EBUF(PFIFLASH_ERRSTR[-rc], " 'res'"); ++ goto err; ++ } ++ ++ rc = bootenv_read_crc(fp_in, bootenv_new, fp_in_size, &crc); ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_BOOTENV_READ; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } else if (crc != pfi_crc) { ++ rc = -PFIFLASH_ERR_CRC_CHECK; ++ EBUF(PFIFLASH_ERRSTR[-rc], pfi_crc, crc); ++ goto err; ++ } ++ ++ rc = pdd_f(bootenv_old, bootenv_new, &bootenv_res, &warnings, ++ err_buf, err_buf_size); ++ if (rc != 0) { ++ EBUF_PREPEND("handling PDD"); ++ goto err; ++ } ++ else if (warnings) ++ /* TODO do something with warnings */ ++ dbg_msg("A warning in the PDD operation occured: %d", ++ warnings); ++ ++ rc = bootenv_size(bootenv_res, &update_size); ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_BOOTENV_SIZE; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ ++ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); ++ ++ fd_out = open(path, O_RDWR); ++ if (fd_out < 0) { ++ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; ++ EBUF(PFIFLASH_ERRSTR[-rc], id); ++ goto err; ++ } ++ fp_out = fdopen(fd_out, "r+"); ++ if (!fp_out) { ++ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; ++ EBUF(PFIFLASH_ERRSTR[-rc], id); ++ goto err; ++ } ++ rc = ubi_update_start(ulib, fd_out, update_size); ++ if (rc < 0) { ++ rc = -PFIFLASH_ERR_UBI_VOL_UPDATE; ++ EBUF(PFIFLASH_ERRSTR[-rc], id); ++ goto err; ++ } ++ ++ rc = bootenv_write(fp_out, bootenv_res); ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_BOOTENV_WRITE; ++ EBUF(PFIFLASH_ERRSTR[-rc], devno, id); ++ goto err; ++ } ++ ++ err: ++ if (ulib != NULL) ++ libubi_close(ulib); ++ if (bootenv_new != NULL) ++ bootenv_destroy(&bootenv_new); ++ if (bootenv_res != NULL) ++ bootenv_destroy(&bootenv_res); ++ if (fp_out) ++ fclose(fp_out); ++ ++ return rc; ++} ++ ++ ++/** ++ * write_normal_volume - writes data from PFI file int to regular UBI volume ++ * @devno UBI device number ++ * @id UBI volume id ++ * @update_size size of data stream ++ * @fp_in PFI data file pointer ++ * @pfi_crc CRC data from PFI header ++ * ++ * Error handling: ++ * when UBI system couldn't be opened ++ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err ++ * when UBI system couldn't open a volume ++ * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err ++ * when unexpected EOF is encountered ++ * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err ++ * when file I/O error ++ * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err ++ * when CRC check fails ++ * - retruns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err ++ **/ ++static int ++write_normal_volume(int devno, uint32_t id, size_t update_size, FILE* fp_in, ++ uint32_t pfi_crc, ++ char *err_buf, size_t err_buf_size) ++{ ++ int rc, fd_out; ++ uint32_t crc, crc32_table[256]; ++ char path[PATH_MAX]; ++ size_t bytes_left; ++ FILE* fp_out; ++ libubi_t ulib; ++ ++ rc = 0; ++ crc = UBI_CRC32_INIT; ++ bytes_left = update_size; ++ fp_out = NULL; ++ ulib = NULL; ++ ++ log_msg("[ ubiupdatevol id=%d, update_size=%d fp_in=%p", ++ id, update_size, fp_in); ++ ++ ulib = libubi_open(); ++ if (ulib == NULL) { ++ rc = -PFIFLASH_ERR_UBI_OPEN; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ ++ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); ++ ++ fd_out = open(path, O_RDWR); ++ if (fd_out < 0) { ++ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; ++ EBUF(PFIFLASH_ERRSTR[-rc], id); ++ goto err; ++ } ++ fp_out = fdopen(fd_out, "r+"); ++ if (!fp_out) { ++ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; ++ EBUF(PFIFLASH_ERRSTR[-rc], id); ++ goto err; ++ } ++ rc = ubi_update_start(ulib, fd_out, update_size); ++ if (rc < 0) { ++ rc = -PFIFLASH_ERR_UBI_VOL_UPDATE; ++ EBUF(PFIFLASH_ERRSTR[-rc], id); ++ goto err; ++ } ++ ++ init_crc32_table(crc32_table); ++ while (bytes_left) { ++ char buf[1024]; ++ size_t to_rw = sizeof buf > bytes_left ? ++ bytes_left : sizeof buf; ++ if (fread(buf, 1, to_rw, fp_in) != to_rw) { ++ rc = -PFIFLASH_ERR_EOF; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ crc = clc_crc32(crc32_table, crc, buf, to_rw); ++ if (fwrite(buf, 1, to_rw, fp_out) != to_rw) { ++ rc = -PFIFLASH_ERR_FIO; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ bytes_left -= to_rw; ++ } ++ ++ if (crc != pfi_crc) { ++ rc = -PFIFLASH_ERR_CRC_CHECK; ++ EBUF(PFIFLASH_ERRSTR[-rc], pfi_crc, crc); ++ goto err; ++ } ++ ++ err: ++ if (fp_out) ++ fclose(fp_out); ++ if (ulib) ++ libubi_close(ulib); ++ ++ return rc; ++} ++ ++static int compare_bootenv(FILE *fp_pfi, FILE **fp_flash, uint32_t ids_size, ++ uint32_t data_size, pdd_func_t pdd_f, char *err_buf, ++ size_t err_buf_size) ++{ ++ int rc, warnings = 0; ++ unsigned int i; ++ bootenv_t bootenv_pfi, bootenv_res = NULL, bootenv_flash = NULL; ++ ++ rc = bootenv_create(&bootenv_pfi); ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_BOOTENV_CREATE; ++ goto err; ++ } ++ ++ rc = bootenv_create(&bootenv_res); ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_BOOTENV_CREATE; ++ goto err; ++ } ++ ++ rc = bootenv_read(fp_pfi, bootenv_pfi, data_size); ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_BOOTENV_READ; ++ goto err; ++ } ++ ++ for (i = 0; i < ids_size; i++) { ++ rc = bootenv_create(&bootenv_flash); ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_BOOTENV_CREATE; ++ goto err; ++ } ++ ++ rc = bootenv_read(fp_flash[i], bootenv_flash, BOOTENV_MAXSIZE); ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_BOOTENV_READ; ++ goto err; ++ } ++ ++ rc = pdd_f(bootenv_flash, bootenv_pfi, &bootenv_res, ++ &warnings, err_buf, err_buf_size); ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_PDD_UNKNOWN; ++ goto err; ++ } ++ ++ rc = bootenv_compare(bootenv_flash, bootenv_res); ++ if (rc > 0) { ++ rc = -PFIFLASH_CMP_DIFF; ++ goto err; ++ } else if (rc < 0) { ++ rc = -PFIFLASH_ERR_COMPARE; ++ goto err; ++ } ++ ++ bootenv_destroy(&bootenv_flash); ++ bootenv_flash = NULL; ++ } ++ ++err: ++ if (bootenv_pfi) ++ bootenv_destroy(&bootenv_pfi); ++ if (bootenv_res) ++ bootenv_destroy(&bootenv_res); ++ if (bootenv_flash) ++ bootenv_destroy(&bootenv_flash); ++ ++ return rc; ++} ++ ++static int compare_data(FILE *fp_pfi, FILE **fp_flash, uint32_t ids_size, ++ uint32_t bytes_left) ++{ ++ unsigned int i; ++ size_t read_bytes, rc = 0; ++ char buf_pfi[COMPARE_BUFFER_SIZE]; ++ char *buf_flash[ids_size]; ++ ++ for (i = 0; i < ids_size; i++) { ++ buf_flash[i] = malloc(COMPARE_BUFFER_SIZE); ++ if (!buf_flash[i]) ++ return -PFIFLASH_ERR_COMPARE; ++ } ++ ++ while (bytes_left) { ++ if (bytes_left > COMPARE_BUFFER_SIZE) ++ read_bytes = COMPARE_BUFFER_SIZE; ++ else ++ read_bytes = bytes_left; ++ ++ rc = fread(buf_pfi, 1, read_bytes, fp_pfi); ++ if (rc != read_bytes) { ++ rc = -PFIFLASH_ERR_COMPARE; ++ goto err; ++ } ++ ++ for (i = 0; i < ids_size; i++) { ++ rc = fread(buf_flash[i], 1, read_bytes, fp_flash[i]); ++ if (rc != read_bytes) { ++ rc = -PFIFLASH_CMP_DIFF; ++ goto err; ++ } ++ ++ rc = memcmp(buf_pfi, buf_flash[i], read_bytes); ++ if (rc != 0) { ++ rc = -PFIFLASH_CMP_DIFF; ++ goto err; ++ } ++ } ++ ++ bytes_left -= read_bytes; ++ } ++ ++err: ++ for (i = 0; i < ids_size; i++) ++ free(buf_flash[i]); ++ ++ return rc; ++} ++ ++static int compare_volumes(int devno, pfi_ubi_t u, FILE *fp_pfi, ++ pdd_func_t pdd_f, char *err_buf, size_t err_buf_size) ++{ ++ int rc, is_bootenv = 0; ++ unsigned int i; ++ char path[PATH_MAX]; ++ libubi_t ulib = NULL; ++ FILE *fp_flash[u->ids_size]; ++ ++ ulib = libubi_open(); ++ if (ulib == NULL) { ++ rc = -PFIFLASH_ERR_UBI_OPEN; ++ goto err; ++ } ++ ++ for (i = 0; i < u->ids_size; i++) { ++ if (u->ids[i] == EXAMPLE_BOOTENV_VOL_ID_1 || ++ u->ids[i] == EXAMPLE_BOOTENV_VOL_ID_2) ++ is_bootenv = 1; ++ ++ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, u->ids[i]); ++ ++ fp_flash[i] = fopen(path, "r"); ++ if (fp_flash[i] == NULL) { ++ rc = -PFIFLASH_ERR_UBI_OPEN; ++ goto err; ++ } ++ } ++ ++ if (is_bootenv) ++ rc = compare_bootenv(fp_pfi, fp_flash, u->ids_size, ++ u->data_size, pdd_f, err_buf, err_buf_size); ++ else ++ rc = compare_data(fp_pfi, fp_flash, u->ids_size, u->data_size); ++ ++err: ++ if (rc < 0) ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ ++ for (i = 0; i < u->ids_size; i++) ++ fclose(fp_flash[i]); ++ if (ulib) ++ libubi_close(ulib); ++ ++ return rc; ++} ++ ++static int ++erase_mtd_region(FILE* file_p, int start, int length) ++{ ++ int rc, fd; ++ erase_info_t erase; ++ mtd_info_t mtdinfo; ++ loff_t offset = start; ++ loff_t end = offset + length; ++ ++ fd = fileno(file_p); ++ if (fd < 0) ++ return -PFIFLASH_ERR_MTD_ERASE; ++ ++ rc = ioctl(fd, MEMGETINFO, &mtdinfo); ++ if (rc) ++ return -PFIFLASH_ERR_MTD_ERASE; ++ ++ /* check for bad blocks in case of NAND flash */ ++ if (mtdinfo.type == MTD_NANDFLASH) { ++ while (offset < end) { ++ rc = ioctl(fd, MEMGETBADBLOCK, &offset); ++ if (rc > 0) { ++ return -PFIFLASH_ERR_MTD_ERASE; ++ } ++ ++ offset += mtdinfo.erasesize; ++ } ++ } ++ ++ erase.start = start; ++ erase.length = length; ++ ++ rc = ioctl(fd, MEMERASE, &erase); ++ if (rc) { ++ return -PFIFLASH_ERR_MTD_ERASE; ++ } ++ ++ return rc; ++} ++ ++/** ++ * process_raw_volumes - writes the raw sections of the PFI data ++ * @pfi PFI data file pointer ++ * @pfi_raws list of PFI raw headers ++ * @rawdev device to use to write raw data ++ * ++ * Error handling: ++ * when early EOF in PFI data ++ * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err ++ * when file I/O error ++ * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err ++ * when CRC check fails ++ * - returns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err ++ * when opening MTD device fails ++ * - reutrns -PFIFLASH_ERR_MTD_OPEN, err_buf matches text to err ++ * when closing MTD device fails ++ * - returns -PFIFLASH_ERR_MTD_CLOSE, err_buf matches text to err ++ **/ ++static int ++process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev, ++ char* err_buf, size_t err_buf_size) ++{ ++ int rc; ++ char *pfi_data; ++ void *i; ++ uint32_t crc, crc32_table[256]; ++ size_t j, k; ++ FILE* mtd = NULL; ++ list_t ptr; ++ ++ if (is_empty(pfi_raws)) ++ return 0; ++ ++ if (rawdev == NULL) ++ return 0; ++ ++ rc = 0; ++ ++ pfi_data = NULL; ++ ++ log_msg("[ rawupdate dev=%s", rawdev); ++ ++ crc = UBI_CRC32_INIT; ++ init_crc32_table(crc32_table); ++ ++ /* most likely only one element in list, but just in case */ ++ foreach(i, ptr, pfi_raws) { ++ pfi_raw_t r = (pfi_raw_t)i; ++ ++ /* read in pfi data */ ++ if (pfi_data != NULL) ++ free(pfi_data); ++ pfi_data = malloc(r->data_size * sizeof(char)); ++ for (j = 0; j < r->data_size; j++) { ++ int c = fgetc(pfi); ++ if (c == EOF) { ++ rc = -PFIFLASH_ERR_EOF; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } else if (ferror(pfi)) { ++ rc = -PFIFLASH_ERR_FIO; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ pfi_data[j] = (char)c; ++ } ++ crc = clc_crc32(crc32_table, crc, pfi_data, r->data_size); ++ ++ /* check crc */ ++ if (crc != r->crc) { ++ rc = -PFIFLASH_ERR_CRC_CHECK; ++ EBUF(PFIFLASH_ERRSTR[-rc], r->crc, crc); ++ goto err; ++ } ++ ++ /* open device */ ++ mtd = fopen(rawdev, "r+"); ++ if (mtd == NULL) { ++ rc = -PFIFLASH_ERR_MTD_OPEN; ++ EBUF(PFIFLASH_ERRSTR[-rc], rawdev); ++ goto err; ++ } ++ ++ for (j = 0; j < r->starts_size; j++) { ++ rc = erase_mtd_region(mtd, r->starts[j], r->data_size); ++ if (rc) { ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ ++ fseek(mtd, r->starts[j], SEEK_SET); ++ for (k = 0; k < r->data_size; k++) { ++ int c = fputc((int)pfi_data[k], mtd); ++ if (c == EOF) { ++ fclose(mtd); ++ rc = -PFIFLASH_ERR_EOF; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ if ((char)c != pfi_data[k]) { ++ fclose(mtd); ++ rc = -1; ++ goto err; ++ } ++ } ++ } ++ rc = fclose(mtd); ++ mtd = NULL; ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_MTD_CLOSE; ++ EBUF(PFIFLASH_ERRSTR[-rc], rawdev); ++ goto err; ++ } ++ } ++ ++ err: ++ if (mtd != NULL) ++ fclose(mtd); ++ if (pfi_data != NULL) ++ free(pfi_data); ++ return rc; ++} ++ ++ ++/** ++ * erase_unmapped_ubi_volumes - skip volumes provided by PFI file, clear rest ++ * @devno UBI device number ++ * @pfi_ubis list of UBI header data ++ * ++ * Error handling: ++ * when UBI id is out of bounds ++ * - returns -PFIFLASH_ERR_UBI_VID_OOB, err_buf matches text to err ++ * when UBI volume can't be removed ++ * - passes rc, prepends err_buf with contextual aid ++ **/ ++static int ++erase_unmapped_ubi_volumes(int devno, list_t pfi_ubis, ++ char *err_buf, size_t err_buf_size) ++{ ++ int rc; ++ uint8_t ubi_volumes[PFI_UBI_MAX_VOLUMES]; ++ size_t i; ++ list_t ptr; ++ pfi_ubi_t u; ++ ++ rc = 0; ++ ++ for (i = 0; i < PFI_UBI_MAX_VOLUMES; i++) ++ ubi_volumes[i] = 1; ++ ++ foreach(u, ptr, pfi_ubis) { ++ /* iterate over each vol_id */ ++ for(i = 0; i < u->ids_size; i++) { ++ if (u->ids[i] >= PFI_UBI_MAX_VOLUMES) { ++ rc = -PFIFLASH_ERR_UBI_VID_OOB; ++ EBUF(PFIFLASH_ERRSTR[-rc], u->ids[i]); ++ goto err; ++ } ++ /* remove from removal list */ ++ ubi_volumes[u->ids[i]] = 0; ++ } ++ } ++ ++ for (i = 0; i < PFI_UBI_MAX_VOLUMES; i++) { ++ if (ubi_volumes[i]) { ++ rc = my_ubi_rmvol(devno, i, err_buf, err_buf_size); ++ if (rc != 0) { ++ EBUF_PREPEND("remove volume failed"); ++ goto err; ++ } ++ } ++ } ++ ++ err: ++ return rc; ++} ++ ++ ++/** ++ * process_ubi_volumes - delegate tasks regarding UBI volumes ++ * @pfi PFI data file pointer ++ * @seqnum sequence number ++ * @pfi_ubis list of UBI header data ++ * @bootenv_old storage for current system PDD ++ * @pdd_f function to handle PDD ++ * @ubi_update_process whether reading or writing ++ * ++ * Error handling: ++ * when and unknown ubi_update_process is given ++ * - returns -PFIFLASH_ERR_UBI_UNKNOWN, err_buf matches text to err ++ * otherwise ++ * - passes rc and err_buf ++ **/ ++static int ++process_ubi_volumes(FILE* pfi, int seqnum, list_t pfi_ubis, ++ bootenv_t bootenv_old, pdd_func_t pdd_f, ++ ubi_update_process_t ubi_update_process, ++ char *err_buf, size_t err_buf_size) ++{ ++ int rc; ++ pfi_ubi_t u; ++ list_t ptr; ++ ++ rc = 0; ++ ++ foreach(u, ptr, pfi_ubis) { ++ int s = seqnum; ++ ++ if (s > ((int)u->ids_size - 1)) ++ s = 0; /* per default use the first */ ++ u->curr_seqnum = s; ++ ++ switch (ubi_update_process) { ++ case UBI_REMOVE: ++ /* TODO are all these "EXAMPLE" vars okay? */ ++ if ((u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_1) || ++ (u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_2)) { ++ rc = read_bootenv_volume(EXAMPLE_UBI_DEVICE, ++ u->ids[s], bootenv_old, ++ err_buf, err_buf_size); ++ /* it's okay if there is no bootenv ++ * we're going to write one */ ++ if ((rc == -PFIFLASH_ERR_UBI_VOL_FOPEN) || ++ (rc == -PFIFLASH_ERR_BOOTENV_READ)) ++ rc = 0; ++ if (rc != 0) ++ goto err; ++ } ++ ++ rc = my_ubi_rmvol(EXAMPLE_UBI_DEVICE, u->ids[s], ++ err_buf, err_buf_size); ++ if (rc != 0) ++ goto err; ++ ++ break; ++ case UBI_WRITE: ++ rc = my_ubi_mkvol(EXAMPLE_UBI_DEVICE, s, u, ++ err_buf, err_buf_size); ++ if (rc != 0) { ++ EBUF_PREPEND("creating volume"); ++ goto err; ++ } ++ ++ if ((u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_1) || ++ (u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_2)) { ++ rc = write_bootenv_volume(EXAMPLE_UBI_DEVICE, ++ u->ids[s], ++ bootenv_old, pdd_f, ++ pfi, ++ u->data_size, ++ u->crc, ++ err_buf, ++ err_buf_size); ++ if (rc != 0) ++ EBUF_PREPEND("bootenv volume"); ++ } else { ++ rc = write_normal_volume(EXAMPLE_UBI_DEVICE, ++ u->ids[s], ++ u->data_size, pfi, ++ u->crc, ++ err_buf, ++ err_buf_size); ++ if (rc != 0) ++ EBUF_PREPEND("normal volume"); ++ } ++ if (rc != 0) ++ goto err; ++ ++ break; ++ case UBI_COMPARE: ++ rc = compare_volumes(EXAMPLE_UBI_DEVICE, u, pfi, pdd_f, ++ err_buf, err_buf_size); ++ if (rc != 0) { ++ EBUF_PREPEND("compare volume"); ++ goto err; ++ } ++ ++ break; ++ default: ++ rc = -PFIFLASH_ERR_UBI_UNKNOWN; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ } ++ ++ err: ++ return rc; ++} ++ ++ ++/** ++ * mirror_ubi_volumes - mirror redundant pairs of volumes ++ * @devno UBI device number ++ * @pfi_ubis list of PFI header data ++ * ++ * Error handling: ++ * when UBI system couldn't be opened ++ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err ++ **/ ++static int ++mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis, ++ char *err_buf, size_t err_buf_size) ++{ ++ int rc; ++ uint32_t j; ++ list_t ptr; ++ pfi_ubi_t i; ++ libubi_t ulib; ++ ++ rc = 0; ++ ulib = NULL; ++ ++ log_msg("[ mirror ..."); ++ ++ ulib = libubi_open(); ++ if (ulib == NULL) { ++ rc = -PFIFLASH_ERR_UBI_OPEN; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ ++ /** ++ * Execute all mirror operations on redundant groups. ++ * Create a volume within a redundant group if it does ++ * not exist already (this is a precondition of ++ * ubimirror). ++ */ ++ foreach(i, ptr, pfi_ubis) { ++ for (j = 0; j < i->ids_size; j++) { ++ /* skip self-match */ ++ if (i->ids[j] == i->ids[i->curr_seqnum]) ++ continue; ++ ++ rc = my_ubi_rmvol(devno, i->ids[j], ++ err_buf, err_buf_size); ++ if (rc != 0) ++ goto err; ++ ++ rc = my_ubi_mkvol(devno, j, i, ++ err_buf, err_buf_size); ++ if (rc != 0) ++ goto err; ++ } ++ } ++ ++ foreach(i, ptr, pfi_ubis) { ++ rc = ubimirror(devno, i->curr_seqnum, i->ids, i->ids_size, ++ err_buf, err_buf_size); ++ if (rc != 0) ++ goto err; ++ } ++ ++ ++ err: ++ if (ulib != NULL) ++ libubi_close(ulib); ++ ++ return rc; ++} ++ ++ ++/** ++ * pfiflash_with_options - exposed func to flash memory with a PFI file ++ * @pfi PFI data file pointer ++ * @complete flag to erase unmapped volumes ++ * @seqnum sequence number ++ * @compare flag to compare ++ * @pdd_handling method to handle pdd (keep, merge, overwrite...) ++ * ++ * Error handling: ++ * when bootenv can't be created ++ * - returns -PFIFLASH_ERR_BOOTENV_CREATE, err_buf matches text to err ++ * when PFI headers can't be read, or ++ * when fail to skip raw sections, or ++ * when error occurs while processing raw volumes, or ++ * when fail to erase unmapped UBI vols, or ++ * when error occurs while processing UBI volumes, or ++ * when error occurs while mirroring UBI volumes ++ * - passes rc, prepends err_buf with contextual aid ++ **/ ++int ++pfiflash_with_options(FILE* pfi, int complete, int seqnum, int compare, ++ pdd_handling_t pdd_handling, const char* rawdev, ++ char *err_buf, size_t err_buf_size) ++{ ++ int rc; ++ bootenv_t bootenv; ++ pdd_func_t pdd_f; ++ ++ if (pfi == NULL) ++ return -EINVAL; ++ ++ rc = 0; ++ pdd_f = NULL; ++ ++ /* If the user didnt specify a seqnum we start per default ++ * with the index 0 */ ++ int curr_seqnum = seqnum < 0 ? 0 : seqnum; ++ ++ list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */ ++ list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */ ++ ++ rc = bootenv_create(&bootenv); ++ if (rc != 0) { ++ rc = -PFIFLASH_ERR_BOOTENV_CREATE; ++ EBUF(PFIFLASH_ERRSTR[-rc], ""); ++ goto err; ++ } ++ ++ rc = read_pfi_headers(&pfi_raws, &pfi_ubis, pfi, err_buf, err_buf_size); ++ if (rc != 0) { ++ EBUF_PREPEND("reading PFI header"); ++ goto err; ++ } ++ ++ if (rawdev == NULL || compare) ++ rc = skip_raw_volumes(pfi, pfi_raws, err_buf, err_buf_size); ++ else ++ rc = process_raw_volumes(pfi, pfi_raws, rawdev, err_buf, ++ err_buf_size); ++ if (rc != 0) { ++ EBUF_PREPEND("handling raw section"); ++ goto err; ++ } ++ ++ if (complete && !compare) { ++ rc = erase_unmapped_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis, ++ err_buf, err_buf_size); ++ if (rc != 0) { ++ EBUF_PREPEND("deleting unmapped UBI volumes"); ++ goto err; ++ } ++ } ++ ++ if (((int)pdd_handling >= 0) && ++ (pdd_handling < PDD_HANDLING_NUM)) ++ pdd_f = pdd_funcs[pdd_handling]; ++ else { ++ rc = -PFIFLASH_ERR_PDD_UNKNOWN; ++ EBUF(PFIFLASH_ERRSTR[-rc]); ++ goto err; ++ } ++ ++ if (!compare) { ++ rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, ++ pdd_f, UBI_REMOVE, err_buf, err_buf_size); ++ if (rc != 0) { ++ EBUF_PREPEND("removing UBI volumes"); ++ goto err; ++ } ++ ++ rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, ++ pdd_f, UBI_WRITE, err_buf, err_buf_size); ++ if (rc != 0) { ++ EBUF_PREPEND("writing UBI volumes"); ++ goto err; ++ } ++ ++ if (seqnum < 0) { /* mirror redundant pairs */ ++ rc = mirror_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis, ++ err_buf, err_buf_size); ++ if (rc != 0) { ++ EBUF_PREPEND("mirroring UBI volumes"); ++ goto err; ++ } ++ } ++ } else { ++ /* only compare volumes, don't alter the content */ ++ rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, ++ pdd_f, UBI_COMPARE, err_buf, err_buf_size); ++ ++ if (rc == -PFIFLASH_CMP_DIFF) ++ /* update is necessary, return positive value */ ++ rc = 1; ++ ++ if (rc < 0) { ++ EBUF_PREPEND("comparing UBI volumes"); ++ goto err; ++ } ++ } ++ ++ err: ++ pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws); ++ pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis); ++ bootenv_destroy(&bootenv); ++ return rc; ++} ++ ++ ++/** ++ * pfiflash - passes to pfiflash_with_options ++ * @pfi PFI data file pointer ++ * @complete flag to erase unmapped volumes ++ * @seqnum sequence number ++ * @pdd_handling method to handle pdd (keep, merge, overwrite...) ++ **/ ++int ++pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling, ++ char *err_buf, size_t err_buf_size) ++{ ++ return pfiflash_with_options(pfi, complete, seqnum, 0, pdd_handling, ++ NULL, err_buf, err_buf_size); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libubi.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libubi.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libubi.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libubi.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,915 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem B. Bityutskiy ++ * ++ * UBI (Unsorted Block Images) library. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "libubi.h" ++#include "libubi_int.h" ++ ++libubi_t libubi_open(void) ++{ ++ int fd, version; ++ struct libubi *lib; ++ ++ lib = calloc(1, sizeof(struct libubi)); ++ if (!lib) ++ return NULL; ++ ++ /* TODO: this must be discovered instead */ ++ lib->sysfs = strdup("/sys"); ++ if (!lib->sysfs) ++ goto error; ++ ++ lib->sysfs_ubi = mkpath(lib->sysfs, SYSFS_UBI); ++ if (!lib->sysfs_ubi) ++ goto error; ++ ++ /* Make sure UBI is present */ ++ fd = open(lib->sysfs_ubi, O_RDONLY); ++ if (fd == -1) ++ goto error; ++ close(fd); ++ ++ lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT); ++ if (!lib->ubi_dev) ++ goto error; ++ ++ lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER); ++ if (!lib->ubi_version) ++ goto error; ++ ++ lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV); ++ if (!lib->dev_dev) ++ goto error; ++ ++ lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS); ++ if (!lib->dev_avail_ebs) ++ goto error; ++ ++ lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS); ++ if (!lib->dev_total_ebs) ++ goto error; ++ ++ lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT); ++ if (!lib->dev_bad_count) ++ goto error; ++ ++ lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE); ++ if (!lib->dev_eb_size) ++ goto error; ++ ++ lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC); ++ if (!lib->dev_max_ec) ++ goto error; ++ ++ lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD); ++ if (!lib->dev_bad_rsvd) ++ goto error; ++ ++ lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS); ++ if (!lib->dev_max_vols) ++ goto error; ++ ++ lib->dev_min_io_size = mkpath(lib->ubi_dev, DEV_MIN_IO_SIZE); ++ if (!lib->dev_min_io_size) ++ goto error; ++ ++ lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT); ++ if (!lib->ubi_vol) ++ goto error; ++ ++ lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE); ++ if (!lib->vol_type) ++ goto error; ++ ++ lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV); ++ if (!lib->vol_dev) ++ goto error; ++ ++ lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT); ++ if (!lib->vol_alignment) ++ goto error; ++ ++ lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES); ++ if (!lib->vol_data_bytes) ++ goto error; ++ ++ lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS); ++ if (!lib->vol_rsvd_ebs) ++ goto error; ++ ++ lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE); ++ if (!lib->vol_eb_size) ++ goto error; ++ ++ lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED); ++ if (!lib->vol_corrupted) ++ goto error; ++ ++ lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME); ++ if (!lib->vol_name) ++ goto error; ++ ++ if (read_int(lib->ubi_version, &version)) ++ goto error; ++ if (version != LIBUBI_UBI_VERSION) { ++ fprintf(stderr, "LIBUBI: this library was made for UBI version " ++ "%d, but UBI version %d is detected\n", ++ LIBUBI_UBI_VERSION, version); ++ goto error; ++ } ++ ++ return lib; ++ ++error: ++ free(lib->vol_corrupted); ++ free(lib->vol_eb_size); ++ free(lib->vol_rsvd_ebs); ++ free(lib->vol_data_bytes); ++ free(lib->vol_alignment); ++ free(lib->vol_dev); ++ free(lib->vol_type); ++ free(lib->ubi_vol); ++ free(lib->dev_min_io_size); ++ free(lib->dev_max_vols); ++ free(lib->dev_bad_rsvd); ++ free(lib->dev_max_ec); ++ free(lib->dev_eb_size); ++ free(lib->dev_bad_count); ++ free(lib->dev_total_ebs); ++ free(lib->dev_avail_ebs); ++ free(lib->dev_dev); ++ free(lib->ubi_version); ++ free(lib->ubi_dev); ++ free(lib->sysfs_ubi); ++ free(lib->sysfs); ++ free(lib); ++ return NULL; ++} ++ ++void libubi_close(libubi_t desc) ++{ ++ struct libubi *lib = (struct libubi *)desc; ++ ++ free(lib->vol_name); ++ free(lib->vol_corrupted); ++ free(lib->vol_eb_size); ++ free(lib->vol_rsvd_ebs); ++ free(lib->vol_data_bytes); ++ free(lib->vol_alignment); ++ free(lib->vol_dev); ++ free(lib->vol_type); ++ free(lib->ubi_vol); ++ free(lib->dev_min_io_size); ++ free(lib->dev_max_vols); ++ free(lib->dev_bad_rsvd); ++ free(lib->dev_max_ec); ++ free(lib->dev_eb_size); ++ free(lib->dev_bad_count); ++ free(lib->dev_total_ebs); ++ free(lib->dev_avail_ebs); ++ free(lib->dev_dev); ++ free(lib->ubi_version); ++ free(lib->ubi_dev); ++ free(lib->sysfs_ubi); ++ free(lib->sysfs); ++ free(lib); ++} ++ ++int ubi_get_info(libubi_t desc, struct ubi_info *info) ++{ ++ DIR *sysfs_ubi; ++ struct dirent *dirent; ++ struct libubi *lib = (struct libubi *)desc; ++ ++ memset(info, '\0', sizeof(struct ubi_info)); ++ ++ /* ++ * We have to scan the UBI sysfs directory to identify how many UBI ++ * devices are present. ++ */ ++ sysfs_ubi = opendir(lib->sysfs_ubi); ++ if (!sysfs_ubi) ++ return -1; ++ ++ info->lowest_dev_num = INT_MAX; ++ while ((dirent = readdir(sysfs_ubi))) { ++ char *name = &dirent->d_name[0]; ++ int dev_num, ret; ++ ++ ret = sscanf(name, UBI_DEV_NAME_PATT, &dev_num); ++ if (ret == 1) { ++ info->dev_count += 1; ++ if (dev_num > info->highest_dev_num) ++ info->highest_dev_num = dev_num; ++ if (dev_num < info->lowest_dev_num) ++ info->lowest_dev_num = dev_num; ++ } ++ } ++ ++ if (info->lowest_dev_num == INT_MAX) ++ info->lowest_dev_num = 0; ++ ++ if (read_int(lib->ubi_version, &info->version)) ++ goto close; ++ ++ return closedir(sysfs_ubi); ++ ++close: ++ closedir(sysfs_ubi); ++ return -1; ++} ++ ++int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req) ++{ ++ int fd, ret; ++ struct ubi_mkvol_req r; ++ size_t n; ++ ++ desc = desc; ++ r.vol_id = req->vol_id; ++ r.alignment = req->alignment; ++ r.bytes = req->bytes; ++ r.vol_type = req->vol_type; ++ ++ n = strlen(req->name); ++ if (n > UBI_MAX_VOLUME_NAME) ++ return -1; ++ ++ strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1); ++ r.name_len = n; ++ ++ fd = open(node, O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ ret = ioctl(fd, UBI_IOCMKVOL, &r); ++ if (!ret) ++ req->vol_id = r.vol_id; ++ ++ close(fd); ++ return ret; ++} ++ ++int ubi_rmvol(libubi_t desc, const char *node, int vol_id) ++{ ++ int fd, ret; ++ ++ desc = desc; ++ fd = open(node, O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ ret = ioctl(fd, UBI_IOCRMVOL, &vol_id); ++ close(fd); ++ return ret; ++} ++ ++int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes) ++{ ++ int fd, ret; ++ struct ubi_rsvol_req req; ++ ++ desc = desc; ++ fd = open(node, O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ req.bytes = bytes; ++ req.vol_id = vol_id; ++ ++ ret = ioctl(fd, UBI_IOCRSVOL, &req); ++ close(fd); ++ return ret; ++} ++ ++int ubi_update_start(libubi_t desc, int fd, long long bytes) ++{ ++ desc = desc; ++ if (ioctl(fd, UBI_IOCVOLUP, &bytes)) ++ return -1; ++ return 0; ++} ++ ++int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info) ++{ ++ int dev_num; ++ struct libubi *lib = (struct libubi *)desc; ++ ++ dev_num = find_dev_num(lib, node); ++ if (dev_num == -1) ++ return -1; ++ ++ return ubi_get_dev_info1(desc, dev_num, info); ++} ++ ++int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) ++{ ++ DIR *sysfs_ubi; ++ struct dirent *dirent; ++ struct libubi *lib = (struct libubi *)desc; ++ ++ memset(info, '\0', sizeof(struct ubi_dev_info)); ++ info->dev_num = dev_num; ++ ++ sysfs_ubi = opendir(lib->sysfs_ubi); ++ if (!sysfs_ubi) ++ return -1; ++ ++ info->lowest_vol_num = INT_MAX; ++ while ((dirent = readdir(sysfs_ubi))) { ++ char *name = &dirent->d_name[0]; ++ int vol_id, ret, devno; ++ ++ ret = sscanf(name, UBI_VOL_NAME_PATT, &devno, &vol_id); ++ if (ret == 2 && devno == dev_num) { ++ info->vol_count += 1; ++ if (vol_id > info->highest_vol_num) ++ info->highest_vol_num = vol_id; ++ if (vol_id < info->lowest_vol_num) ++ info->lowest_vol_num = vol_id; ++ } ++ } ++ ++ closedir(sysfs_ubi); ++ ++ if (info->lowest_vol_num == INT_MAX) ++ info->lowest_vol_num = 0; ++ ++ if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_ebs)) ++ return -1; ++ if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_ebs)) ++ return -1; ++ if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count)) ++ return -1; ++ if (dev_read_int(lib->dev_eb_size, dev_num, &info->eb_size)) ++ return -1; ++ if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd)) ++ return -1; ++ if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec)) ++ return -1; ++ if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count)) ++ return -1; ++ if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size)) ++ return -1; ++ ++ info->avail_bytes = info->avail_ebs * info->eb_size; ++ info->total_bytes = info->total_ebs * info->eb_size; ++ ++ return 0; ++} ++ ++int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info) ++{ ++ int vol_id, dev_num; ++ struct libubi *lib = (struct libubi *)desc; ++ ++ dev_num = find_dev_num_vol(lib, node); ++ if (dev_num == -1) ++ return -1; ++ ++ vol_id = find_vol_num(lib, dev_num, node); ++ if (vol_id == -1) ++ return -1; ++ ++ return ubi_get_vol_info1(desc, dev_num, vol_id, info); ++} ++ ++int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, ++ struct ubi_vol_info *info) ++{ ++ int ret; ++ struct libubi *lib = (struct libubi *)desc; ++ char buf[50]; ++ ++ memset(info, '\0', sizeof(struct ubi_vol_info)); ++ info->dev_num = dev_num; ++ info->vol_id = vol_id; ++ ++ ret = vol_read_data(lib->vol_type, dev_num, vol_id, &buf[0], 50); ++ if (ret < 0) ++ return -1; ++ ++ if (strncmp(&buf[0], "static\n", ret) == 0) ++ info->type = UBI_STATIC_VOLUME; ++ else if (strncmp(&buf[0], "dynamic\n", ret) == 0) ++ info->type = UBI_DYNAMIC_VOLUME; ++ else { ++ fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); ++ errno = EINVAL; ++ return -1; ++ } ++ ++ ret = vol_read_int(lib->vol_alignment, dev_num, vol_id, ++ &info->alignment); ++ if (ret) ++ return -1; ++ ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id, ++ &info->data_bytes); ++ if (ret) ++ return -1; ++ ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_ebs); ++ if (ret) ++ return -1; ++ ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->eb_size); ++ if (ret) ++ return -1; ++ ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id, ++ &info->corrupted); ++ if (ret) ++ return -1; ++ info->rsvd_bytes = info->eb_size * info->rsvd_ebs; ++ ++ ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name, ++ UBI_VOL_NAME_MAX + 2); ++ if (ret < 0) ++ return -1; ++ ++ info->name[ret - 1] = '\0'; ++ return 0; ++} ++ ++/** ++ * read_int - read an 'int' value from a file. ++ * ++ * @file the file to read from ++ * @value the result is stored here ++ * ++ * This function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int read_int(const char *file, int *value) ++{ ++ int fd, rd; ++ char buf[50]; ++ ++ fd = open(file, O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ rd = read(fd, &buf[0], 50); ++ if (rd == -1) ++ goto error; ++ ++ if (sscanf(&buf[0], "%d\n", value) != 1) { ++ /* This must be a UBI bug */ ++ fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); ++ errno = EINVAL; ++ goto error; ++ } ++ ++ close(fd); ++ return 0; ++ ++error: ++ close(fd); ++ return -1; ++} ++ ++/** ++ * dev_read_int - read an 'int' value from an UBI device's sysfs file. ++ * ++ * @patt the file pattern to read from ++ * @dev_num UBI device number ++ * @value the result is stored here ++ * ++ * This function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int dev_read_int(const char *patt, int dev_num, int *value) ++{ ++ int fd, rd; ++ char buf[50]; ++ char file[strlen(patt) + 50]; ++ ++ sprintf(&file[0], patt, dev_num); ++ fd = open(&file[0], O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ rd = read(fd, &buf[0], 50); ++ if (rd == -1) ++ goto error; ++ ++ if (sscanf(&buf[0], "%d\n", value) != 1) { ++ /* This must be a UBI bug */ ++ fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); ++ errno = EINVAL; ++ goto error; ++ } ++ ++ close(fd); ++ return 0; ++ ++error: ++ close(fd); ++ return -1; ++} ++ ++/** ++ * dev_read_ll - read a 'long long' value from an UBI device's sysfs file. ++ * ++ * @patt the file pattern to read from ++ * @dev_num UBI device number ++ * @value the result is stored here ++ * ++ * This function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int dev_read_ll(const char *patt, int dev_num, long long *value) ++{ ++ int fd, rd; ++ char buf[50]; ++ char file[strlen(patt) + 50]; ++ ++ sprintf(&file[0], patt, dev_num); ++ fd = open(&file[0], O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ rd = read(fd, &buf[0], 50); ++ if (rd == -1) ++ goto error; ++ ++ if (sscanf(&buf[0], "%lld\n", value) != 1) { ++ /* This must be a UBI bug */ ++ fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); ++ errno = EINVAL; ++ goto error; ++ } ++ ++ close(fd); ++ return 0; ++ ++error: ++ close(fd); ++ return -1; ++} ++ ++/** ++ * dev_read_data - read data from an UBI device's sysfs file. ++ * ++ * @patt the file pattern to read from ++ * @dev_num UBI device number ++ * @buf buffer to read data to ++ * @buf_len buffer length ++ * ++ * This function returns number of read bytes in case of success and %-1 in ++ * case of failure. ++ */ ++static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len) ++{ ++ int fd, rd; ++ char file[strlen(patt) + 50]; ++ ++ sprintf(&file[0], patt, dev_num); ++ fd = open(&file[0], O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ rd = read(fd, buf, buf_len); ++ if (rd == -1) { ++ close(fd); ++ return -1; ++ } ++ ++ close(fd); ++ return rd; ++} ++ ++/** ++ * vol_read_int - read an 'int' value from an UBI volume's sysfs file. ++ * ++ * @patt the file pattern to read from ++ * @dev_num UBI device number ++ * @vol_id volume identifier ++ * @value the result is stored here ++ * ++ * This function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value) ++{ ++ int fd, rd; ++ char buf[50]; ++ char file[strlen(patt) + 100]; ++ ++ sprintf(&file[0], patt, dev_num, vol_id); ++ fd = open(&file[0], O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ rd = read(fd, &buf[0], 50); ++ if (rd == -1) ++ goto error; ++ ++ if (sscanf(&buf[0], "%d\n", value) != 1) { ++ /* This must be a UBI bug */ ++ fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); ++ errno = EINVAL; ++ goto error; ++ } ++ ++ close(fd); ++ return 0; ++ ++error: ++ close(fd); ++ return -1; ++} ++ ++/** ++ * vol_read_ll - read a 'long long' value from an UBI volume's sysfs file. ++ * ++ * @patt the file pattern to read from ++ * @dev_num UBI device number ++ * @vol_id volume identifier ++ * @value the result is stored here ++ * ++ * This function returns %0 in case of success and %-1 in case of failure. ++ */ ++static int vol_read_ll(const char *patt, int dev_num, int vol_id, ++ long long *value) ++{ ++ int fd, rd; ++ char buf[50]; ++ char file[strlen(patt) + 100]; ++ ++ sprintf(&file[0], patt, dev_num, vol_id); ++ fd = open(&file[0], O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ rd = read(fd, &buf[0], 50); ++ if (rd == -1) ++ goto error; ++ ++ if (sscanf(&buf[0], "%lld\n", value) != 1) { ++ /* This must be a UBI bug */ ++ fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); ++ errno = EINVAL; ++ goto error; ++ } ++ ++ close(fd); ++ return 0; ++ ++error: ++ close(fd); ++ return -1; ++} ++ ++/** ++ * vol_read_data - read data from an UBI volume's sysfs file. ++ * ++ * @patt the file pattern to read from ++ * @dev_num UBI device number ++ * @vol_id volume identifier ++ * @buf buffer to read to ++ * @buf_len buffer length ++ * ++ * This function returns number of read bytes in case of success and %-1 in ++ * case of failure. ++ */ ++static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf, ++ int buf_len) ++{ ++ int fd, rd; ++ char file[strlen(patt) + 100]; ++ ++ sprintf(&file[0], patt, dev_num, vol_id); ++ fd = open(&file[0], O_RDONLY); ++ if (fd == -1) ++ return -1; ++ ++ rd = read(fd, buf, buf_len); ++ if (rd == -1) { ++ close(fd); ++ return -1; ++ } ++ ++ close(fd); ++ return rd; ++} ++ ++/** ++ * mkpath - compose full path from 2 given components. ++ * ++ * @path first component ++ * @name second component ++ * ++ * This function returns the resulting path in case of success and %NULL in ++ * case of failure. ++ */ ++static char *mkpath(const char *path, const char *name) ++{ ++ char *n; ++ int len1 = strlen(path); ++ int len2 = strlen(name); ++ ++ n = malloc(len1 + len2 + 2); ++ if (!n) ++ return NULL; ++ ++ memcpy(n, path, len1); ++ if (n[len1 - 1] != '/') ++ n[len1++] = '/'; ++ ++ memcpy(n + len1, name, len2 + 1); ++ return n; ++} ++ ++/** ++ * find_dev_num - find UBI device number by its character device node. ++ * ++ * @lib UBI library descriptor ++ * @node UBI character device node name ++ * ++ * This function returns positive UBI device number in case of success and %-1 ++ * in case of failure. ++ */ ++static int find_dev_num(struct libubi *lib, const char *node) ++{ ++ struct stat stat; ++ struct ubi_info info; ++ int i, major, minor; ++ ++ if (lstat(node, &stat)) ++ return -1; ++ ++ if (!S_ISCHR(stat.st_mode)) { ++ errno = EINVAL; ++ return -1; ++ } ++ ++ major = major(stat.st_rdev); ++ minor = minor(stat.st_rdev); ++ ++ if (minor != 0) { ++ errno = -EINVAL; ++ return -1; ++ } ++ ++ if (ubi_get_info((libubi_t *)lib, &info)) ++ return -1; ++ ++ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { ++ int major1, minor1, ret; ++ char buf[50]; ++ ++ ret = dev_read_data(lib->dev_dev, i, &buf[0], 50); ++ if (ret < 0) ++ return -1; ++ ++ ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1); ++ if (ret != 2) { ++ fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); ++ errno = EINVAL; ++ return -1; ++ } ++ ++ if (minor1 == minor && major1 == major) ++ return i; ++ } ++ ++ errno = ENOENT; ++ return -1; ++} ++ ++/** ++ * find_dev_num_vol - find UBI device number by volume character device node. ++ * ++ * @lib UBI library descriptor ++ * @node UBI character device node name ++ * ++ * This function returns positive UBI device number in case of success and %-1 ++ * in case of failure. ++ */ ++static int find_dev_num_vol(struct libubi *lib, const char *node) ++{ ++ struct stat stat; ++ struct ubi_info info; ++ int i, major; ++ ++ if (lstat(node, &stat)) ++ return -1; ++ ++ if (!S_ISCHR(stat.st_mode)) { ++ errno = EINVAL; ++ return -1; ++ } ++ ++ major = major(stat.st_rdev); ++ ++ if (minor(stat.st_rdev) == 0) { ++ errno = -EINVAL; ++ return -1; ++ } ++ ++ if (ubi_get_info((libubi_t *)lib, &info)) ++ return -1; ++ ++ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { ++ int major1, minor1, ret; ++ char buf[50]; ++ ++ ret = dev_read_data(lib->dev_dev, i, &buf[0], 50); ++ if (ret < 0) ++ return -1; ++ ++ ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1); ++ if (ret != 2) { ++ fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); ++ errno = EINVAL; ++ return -1; ++ } ++ ++ if (major1 == major) ++ return i; ++ } ++ ++ errno = ENOENT; ++ return -1; ++} ++ ++/** ++ * find_vol_num - find UBI volume number by its character device node. ++ * ++ * @lib UBI library descriptor ++ * @dev_num UBI device number ++ * @node UBI volume character device node name ++ * ++ * This function returns positive UBI volume number in case of success and %-1 ++ * in case of failure. ++ */ ++static int find_vol_num(struct libubi *lib, int dev_num, const char *node) ++{ ++ struct stat stat; ++ struct ubi_dev_info info; ++ int i, major, minor; ++ ++ if (lstat(node, &stat)) ++ return -1; ++ ++ if (!S_ISCHR(stat.st_mode)) { ++ errno = EINVAL; ++ return -1; ++ } ++ ++ major = major(stat.st_rdev); ++ minor = minor(stat.st_rdev); ++ ++ if (minor == 0) { ++ errno = -EINVAL; ++ return -1; ++ } ++ ++ if (ubi_get_dev_info1((libubi_t *)lib, dev_num, &info)) ++ return -1; ++ ++ for (i = info.lowest_vol_num; i <= info.highest_vol_num; i++) { ++ int major1, minor1, ret; ++ char buf[50]; ++ ++ ret = vol_read_data(lib->vol_dev, dev_num, i, &buf[0], 50); ++ if (ret < 0) ++ return -1; ++ ++ ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1); ++ if (ret != 2) { ++ fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); ++ errno = EINVAL; ++ return -1; ++ } ++ ++ if (minor1 == minor && major1 == major) ++ return i; ++ } ++ ++ errno = ENOENT; ++ return -1; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libubi_int.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libubi_int.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libubi_int.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libubi_int.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,129 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Artem B. Bityutskiy ++ * ++ * UBI (Unsorted Block Images) library. ++ */ ++ ++#ifndef __LIBUBI_INT_H__ ++#define __LIBUBI_INT_H__ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* ++ * UBI heavily makes use of the sysfs file system to interact with users-pace. ++ * The below are pre-define UBI file and directory names. ++ */ ++ ++#define SYSFS_UBI "class/ubi" ++#define UBI_DEV_NAME_PATT "ubi%d" ++#define UBI_VER "version" ++#define DEV_DEV "dev" ++#define UBI_VOL_NAME_PATT "ubi%d_%d" ++#define DEV_AVAIL_EBS "avail_eraseblocks" ++#define DEV_TOTAL_EBS "total_eraseblocks" ++#define DEV_BAD_COUNT "bad_peb_count" ++#define DEV_EB_SIZE "eraseblock_size" ++#define DEV_MAX_EC "max_ec" ++#define DEV_MAX_RSVD "reserved_for_bad" ++#define DEV_MAX_VOLS "max_vol_count" ++#define DEV_MIN_IO_SIZE "min_io_size" ++#define VOL_TYPE "type" ++#define VOL_DEV "dev" ++#define VOL_ALIGNMENT "alignment" ++#define VOL_DATA_BYTES "data_bytes" ++#define VOL_RSVD_EBS "reserved_ebs" ++#define VOL_EB_SIZE "usable_eb_size" ++#define VOL_CORRUPTED "corrupted" ++#define VOL_NAME "name" ++ ++/** ++ * libubi - UBI library description data structure. ++ * ++ * @sysfs sysfs file system path ++ * @sysfs_ubi UBI directory in sysfs ++ * @ubi_dev UBI device sysfs directory pattern ++ * @ubi_version UBI version file sysfs path ++ * @dev_dev UBI device's major/minor numbers file pattern ++ * @dev_avail_ebs count of available eraseblocks sysfs path pattern ++ * @dev_total_ebs total eraseblocks count sysfs path pattern ++ * @dev_bad_count count of bad eraseblocks sysfs path pattern ++ * @dev_eb_size size of UBI device's eraseblocks sysfs path pattern ++ * @dev_max_ec maximum erase counter sysfs path pattern ++ * @dev_bad_rsvd count of physical eraseblock reserved for bad eraseblocks ++ * handling ++ * @dev_max_vols maximum volumes number count sysfs path pattern ++ * @dev_min_io_size minimum I/O unit size sysfs path pattern ++ * @ubi_vol UBI volume sysfs directory pattern ++ * @vol_type volume type sysfs path pattern ++ * @vol_dev volume's major/minor numbers file pattern ++ * @vol_alignment volume alignment sysfs path pattern ++ * @vol_data_bytes volume data size sysfs path pattern ++ * @vol_rsvd_ebs volume reserved size sysfs path pattern ++ * @vol_eb_size volume eraseblock size sysfs path pattern ++ * @vol_corrupted volume corruption flag sysfs path pattern ++ * @vol_name volume name sysfs path pattern ++ */ ++struct libubi ++{ ++ char *sysfs; ++ char *sysfs_ubi; ++ char *ubi_dev; ++ char *ubi_version; ++ char *dev_dev; ++ char *dev_avail_ebs; ++ char *dev_total_ebs; ++ char *dev_bad_count; ++ char *dev_eb_size; ++ char *dev_max_ec; ++ char *dev_bad_rsvd; ++ char *dev_max_vols; ++ char *dev_min_io_size; ++ char *ubi_vol; ++ char *vol_type; ++ char *vol_dev; ++ char *vol_alignment; ++ char *vol_data_bytes; ++ char *vol_rsvd_ebs; ++ char *vol_eb_size; ++ char *vol_corrupted; ++ char *vol_name; ++ char *vol_max_count; ++}; ++ ++static int read_int(const char *file, int *value); ++static int dev_read_int(const char *patt, int dev_num, int *value); ++static int dev_read_ll(const char *patt, int dev_num, long long *value); ++static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len); ++static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value); ++static int vol_read_ll(const char *patt, int dev_num, int vol_id, ++ long long *value); ++static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf, ++ int buf_len); ++static char *mkpath(const char *path, const char *name); ++static int find_dev_num(struct libubi *lib, const char *node); ++static int find_dev_num_vol(struct libubi *lib, const char *node); ++static int find_vol_num(struct libubi *lib, int dev_num, const char *node); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* !__LIBUBI_INT_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libubigen.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libubigen.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libubigen.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libubigen.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,487 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ * ++ * Add UBI headers to binary data. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "config.h" ++#include "ubigen.h" ++#include "crc32.h" ++ ++#define UBI_NAME_SIZE 256 ++#define DEFAULT_VID_OFFSET ((DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE)) ++#define MIN(a,b) ((a) < (b) ? (a) : (b)) ++ ++static uint32_t crc32_table[256]; ++ ++struct ubi_info { ++ struct ubi_vid_hdr* v; /* Volume ID header */ ++ struct ubi_ec_hdr* ec; /* Erase count header */ ++ ++ FILE* fp_in; /* Input Stream */ ++ FILE* fp_out; /* Output stream */ ++ ++ size_t eb_size; /* Physical EB size in bytes */ ++ size_t leb_size; /* Size of a logical EB in a physical EB */ ++ size_t leb_total; /* Total input size in logical EB */ ++ size_t alignment; /* Block alignment */ ++ size_t data_pad; /* Size of padding in each physical EB */ ++ ++ size_t bytes_total; /* Total input size in bytes */ ++ size_t bytes_read; /* Nymber of read bytes (total) */ ++ ++ uint32_t blks_written; /* Number of written logical EB */ ++ ++ uint8_t* buf; /* Allocated buffer */ ++ uint8_t* ptr_ec_hdr; /* Pointer to EC hdr in buf */ ++ uint8_t* ptr_vid_hdr; /* Pointer to VID hdr in buf */ ++ uint8_t* ptr_data; /* Pointer to data region in buf */ ++}; ++ ++ ++static uint32_t ++byte_to_blk(uint64_t byte, uint32_t eb_size) ++{ ++ return (byte % eb_size) == 0 ++ ? (byte / eb_size) ++ : (byte / eb_size) + 1; ++} ++ ++static int ++validate_ubi_info(ubi_info_t u) ++{ ++ if ((u->v->vol_type != UBI_VID_DYNAMIC) && ++ (u->v->vol_type != UBI_VID_STATIC)) { ++ return EUBIGEN_INVALID_TYPE; ++ } ++ ++ if (be32_to_cpu(u->ec->vid_hdr_offset) < UBI_VID_HDR_SIZE) { ++ return EUBIGEN_INVALID_HDR_OFFSET; ++ } ++ ++ return 0; ++} ++ ++static int ++skip_blks(ubi_info_t u, uint32_t blks) ++{ ++ uint32_t i; ++ size_t read = 0, to_read = 0; ++ ++ /* Step to a maximum of leb_total - 1 to keep the ++ restrictions. */ ++ for (i = 0; i < MIN(blks, u->leb_total-1); i++) { ++ /* Read in data */ ++ to_read = MIN(u->leb_size, ++ (u->bytes_total - u->bytes_read)); ++ read = fread(u->ptr_data, 1, to_read, u->fp_in); ++ if (read != to_read) { ++ return -EIO; ++ } ++ u->bytes_read += read; ++ u->blks_written++; ++ } ++ ++ return 0; ++} ++ ++static void ++clear_buf(ubi_info_t u) ++{ ++ memset(u->buf, 0xff, u->eb_size); ++} ++ ++static void ++write_ec_hdr(ubi_info_t u) ++{ ++ memcpy(u->ptr_ec_hdr, u->ec, UBI_EC_HDR_SIZE); ++} ++ ++static int ++fill_data_buffer_from_file(ubi_info_t u, size_t* read) ++{ ++ size_t to_read = 0; ++ ++ if (u-> fp_in == NULL) ++ return -EIO; ++ ++ to_read = MIN(u->leb_size, (u->bytes_total - u->bytes_read)); ++ *read = fread(u->ptr_data, 1, to_read, u->fp_in); ++ if (*read != to_read) { ++ return -EIO; ++ } ++ return 0; ++} ++ ++static void ++add_static_info(ubi_info_t u, size_t data_size, ubigen_action_t action) ++{ ++ uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT, ++ u->ptr_data, data_size); ++ ++ u->v->data_size = cpu_to_be32(data_size); ++ u->v->data_crc = cpu_to_be32(crc); ++ ++ if (action & BROKEN_DATA_CRC) { ++ u->v->data_crc = ++ cpu_to_be32(be32_to_cpu(u->v->data_crc) + 1); ++ } ++ if (action & BROKEN_DATA_SIZE) { ++ u->v->data_size = ++ cpu_to_be32(be32_to_cpu(u->v->data_size) + 1); ++ } ++} ++ ++static void ++write_vid_hdr(ubi_info_t u, ubigen_action_t action) ++{ ++ uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT, ++ u->v, UBI_VID_HDR_SIZE_CRC); ++ /* Write VID header */ ++ u->v->hdr_crc = cpu_to_be32(crc); ++ if (action & BROKEN_HDR_CRC) { ++ u->v->hdr_crc = cpu_to_be32(be32_to_cpu(u->v->hdr_crc) + 1); ++ } ++ memcpy(u->ptr_vid_hdr, u->v, UBI_VID_HDR_SIZE); ++} ++ ++static int ++write_to_output_stream(ubi_info_t u) ++{ ++ size_t written; ++ ++ written = fwrite(u->buf, 1, u->eb_size, u->fp_out); ++ if (written != u->eb_size) { ++ return -EIO; ++ } ++ return 0; ++} ++ ++int ++ubigen_write_leb(ubi_info_t u, ubigen_action_t action) ++{ ++ int rc = 0; ++ size_t read = 0; ++ ++ clear_buf(u); ++ write_ec_hdr(u); ++ ++ rc = fill_data_buffer_from_file(u, &read); ++ if (rc != 0) ++ return rc; ++ ++ if (u->v->vol_type == UBI_VID_STATIC) { ++ add_static_info(u, read, action); ++ } ++ ++ u->v->lnum = cpu_to_be32(u->blks_written); ++ ++ if (action & MARK_AS_UPDATE) { ++ u->v->copy_flag = (u->v->copy_flag)++; ++ } ++ ++ write_vid_hdr(u, action); ++ rc = write_to_output_stream(u); ++ if (rc != 0) ++ return rc; ++ ++ /* Update current handle */ ++ u->bytes_read += read; ++ u->blks_written++; ++ return 0; ++} ++ ++int ++ubigen_write_complete(ubi_info_t u) ++{ ++ size_t i; ++ int rc = 0; ++ ++ for (i = 0; i < u->leb_total; i++) { ++ rc = ubigen_write_leb(u, NO_ERROR); ++ if (rc != 0) ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int ++ubigen_write_broken_update(ubi_info_t u, uint32_t blk) ++{ ++ int rc = 0; ++ ++ rc = skip_blks(u, blk); ++ if (rc != 0) ++ return rc; ++ ++ rc = ubigen_write_leb(u, MARK_AS_UPDATE | BROKEN_DATA_CRC); ++ if (rc != 0) ++ return rc; ++ ++ ++ return 0; ++} ++ ++void ++dump_info(ubi_info_t u ubi_unused) ++{ ++#ifdef DEBUG ++ int err = 0; ++ if (!u) { ++ fprintf(stderr, ""); ++ return; ++ } ++ if (!u->ec) { ++ fprintf(stderr, ""); ++ err = 1; ++ } ++ if (!u->v) { ++ fprintf(stderr, ""); ++ err = 1; ++ } ++ if (err) return; ++ ++ fprintf(stderr, "ubi volume\n"); ++ fprintf(stderr, "version : %8d\n", u->v->version); ++ fprintf(stderr, "vol_id : %8d\n", be32_to_cpu(u->v->vol_id)); ++ fprintf(stderr, "vol_type : %8s\n", ++ u->v->vol_type == UBI_VID_STATIC ? ++ "static" : "dynamic"); ++ fprintf(stderr, "used_ebs : %8d\n", ++ be32_to_cpu(u->v->used_ebs)); ++ fprintf(stderr, "eb_size : 0x%08x\n", u->eb_size); ++ fprintf(stderr, "leb_size : 0x%08x\n", u->leb_size); ++ fprintf(stderr, "data_pad : 0x%08x\n", ++ be32_to_cpu(u->v->data_pad)); ++ fprintf(stderr, "leb_total : %8d\n", u->leb_total); ++ fprintf(stderr, "header offs : 0x%08x\n", ++ be32_to_cpu(u->ec->vid_hdr_offset)); ++ fprintf(stderr, "bytes_total : %8d\n", u->bytes_total); ++ fprintf(stderr, " + in MiB : %8.2f M\n", ++ ((float)(u->bytes_total)) / 1024 / 1024); ++ fprintf(stderr, "-------------------------------\n\n"); ++#else ++ return; ++#endif ++} ++ ++int ++ubigen_destroy(ubi_info_t *u) ++{ ++ if (u == NULL) ++ return -EINVAL; ++ ++ ubi_info_t tmp = *u; ++ ++ if (tmp) { ++ if (tmp->v) ++ free(tmp->v); ++ if (tmp->ec) ++ free(tmp->ec); ++ if (tmp->buf) ++ free(tmp->buf); ++ free(tmp); ++ } ++ *u = NULL; ++ return 0; ++} ++ ++void ++ubigen_init(void) ++{ ++ init_crc32_table(crc32_table); ++} ++ ++int ++ubigen_create(ubi_info_t* u, uint32_t vol_id, uint8_t vol_type, ++ uint32_t eb_size, uint64_t ec, uint32_t alignment, ++ uint8_t version, uint32_t vid_hdr_offset, uint8_t compat_flag, ++ size_t data_size, FILE* fp_in, FILE* fp_out) ++{ ++ int rc = 0; ++ ubi_info_t res = NULL; ++ uint32_t crc; ++ uint32_t data_offset; ++ ++ if (alignment == 0) { ++ rc = EUBIGEN_INVALID_ALIGNMENT; ++ goto ubigen_create_err; ++ } ++ if ((fp_in == NULL) || (fp_out == NULL)) { ++ rc = -EINVAL; ++ goto ubigen_create_err; ++ } ++ ++ res = (ubi_info_t) calloc(1, sizeof(struct ubi_info)); ++ if (res == NULL) { ++ rc = -ENOMEM; ++ goto ubigen_create_err; ++ } ++ ++ res->v = (struct ubi_vid_hdr*) calloc(1, sizeof(struct ubi_vid_hdr)); ++ if (res->v == NULL) { ++ rc = -ENOMEM; ++ goto ubigen_create_err; ++ } ++ ++ res->ec = (struct ubi_ec_hdr*) calloc(1, sizeof(struct ubi_ec_hdr)); ++ if (res->ec == NULL) { ++ rc = -ENOMEM; ++ goto ubigen_create_err; ++ } ++ ++ /* data which is needed in the general process */ ++ vid_hdr_offset = vid_hdr_offset ? vid_hdr_offset : DEFAULT_VID_OFFSET; ++ data_offset = vid_hdr_offset + UBI_VID_HDR_SIZE; ++ res->bytes_total = data_size; ++ res->eb_size = eb_size ? eb_size : DEFAULT_BLOCKSIZE; ++ res->data_pad = (res->eb_size - data_offset) % alignment; ++ res->leb_size = res->eb_size - data_offset - res->data_pad; ++ res->leb_total = byte_to_blk(data_size, res->leb_size); ++ res->alignment = alignment; ++ ++ if ((res->eb_size < (vid_hdr_offset + UBI_VID_HDR_SIZE))) { ++ rc = EUBIGEN_TOO_SMALL_EB; ++ goto ubigen_create_err; ++ } ++ res->fp_in = fp_in; ++ res->fp_out = fp_out; ++ ++ /* vid hdr data which doesn't change */ ++ res->v->magic = cpu_to_be32(UBI_VID_HDR_MAGIC); ++ res->v->version = version ? version : UBI_VERSION; ++ res->v->vol_type = vol_type; ++ res->v->vol_id = cpu_to_be32(vol_id); ++ res->v->compat = compat_flag; ++ res->v->data_pad = cpu_to_be32(res->data_pad); ++ ++ /* static only: used_ebs */ ++ if (res->v->vol_type == UBI_VID_STATIC) { ++ res->v->used_ebs = cpu_to_be32(byte_to_blk ++ (res->bytes_total, ++ res->leb_size)); ++ } ++ ++ /* ec hdr (fixed, doesn't change) */ ++ res->ec->magic = cpu_to_be32(UBI_EC_HDR_MAGIC); ++ res->ec->version = version ? version : UBI_VERSION; ++ res->ec->ec = cpu_to_be64(ec); ++ res->ec->vid_hdr_offset = cpu_to_be32(vid_hdr_offset); ++ ++ res->ec->data_offset = cpu_to_be32(data_offset); ++ ++ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, res->ec, ++ UBI_EC_HDR_SIZE_CRC); ++ res->ec->hdr_crc = cpu_to_be32(crc); ++ ++ /* prepare a read buffer */ ++ res->buf = (uint8_t*) malloc (res->eb_size * sizeof(uint8_t)); ++ if (res->buf == NULL) { ++ rc = -ENOMEM; ++ goto ubigen_create_err; ++ } ++ ++ /* point to distinct regions within the buffer */ ++ res->ptr_ec_hdr = res->buf; ++ res->ptr_vid_hdr = res->buf + be32_to_cpu(res->ec->vid_hdr_offset); ++ res->ptr_data = res->buf + be32_to_cpu(res->ec->vid_hdr_offset) ++ + UBI_VID_HDR_SIZE; ++ ++ rc = validate_ubi_info(res); ++ if (rc != 0) { ++ fprintf(stderr, "Volume validation failed: %d\n", rc); ++ goto ubigen_create_err; ++ } ++ ++ dump_info(res); ++ *u = res; ++ return rc; ++ ++ ubigen_create_err: ++ if (res) { ++ if (res->v) ++ free(res->v); ++ if (res->ec) ++ free(res->ec); ++ if (res->buf) ++ free(res->buf); ++ free(res); ++ } ++ *u = NULL; ++ return rc; ++} ++ ++int ++ubigen_get_leb_size(ubi_info_t u, size_t* size) ++{ ++ if (u == NULL) ++ return -EINVAL; ++ ++ *size = u->leb_size; ++ return 0; ++} ++ ++ ++int ++ubigen_get_leb_total(ubi_info_t u, size_t* total) ++{ ++ if (u == NULL) ++ return -EINVAL; ++ ++ *total = u->leb_total; ++ return 0; ++} ++ ++int ++ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes, ++ const char* vol_name, struct ubi_vtbl_record *lvol_rec) ++{ ++ uint32_t crc; ++ ++ if ((u == NULL) || (vol_name == NULL)) ++ return -EINVAL; ++ ++ memset(lvol_rec, 0x0, UBI_VTBL_RECORD_SIZE); ++ ++ lvol_rec->reserved_pebs = ++ cpu_to_be32(byte_to_blk(reserved_bytes, u->leb_size)); ++ lvol_rec->alignment = cpu_to_be32(u->alignment); ++ lvol_rec->data_pad = u->v->data_pad; ++ lvol_rec->vol_type = u->v->vol_type; ++ ++ lvol_rec->name_len = ++ cpu_to_be16((uint16_t)strlen((const char*)vol_name)); ++ ++ memcpy(lvol_rec->name, vol_name, UBI_VOL_NAME_MAX + 1); ++ ++ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, ++ lvol_rec, UBI_VTBL_RECORD_SIZE_CRC); ++ lvol_rec->crc = cpu_to_be32(crc); ++ ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libubimirror.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libubimirror.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/libubimirror.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/libubimirror.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,237 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "ubimirror.h" ++ ++#define COMPARE_BUF_SIZE (128 * 1024) ++ ++#define DEFAULT_DEV_PATTERN "/dev/ubi%d" ++#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d" ++ ++#define EBUF(fmt...) do { \ ++ snprintf(err_buf, err_buf_size, fmt); \ ++} while (0) ++ ++enum { ++ compare_error = -1, ++ seek_error = -2, ++ write_error = -3, ++ read_error = -4, ++ update_error = -5, ++ ubi_error = -6, ++ open_error = -7, ++ close_error = -8, ++ compare_equal = 0, ++ compare_different = 1 ++}; ++ ++/* ++ * Read len number of bytes from fd. ++ * Return 0 on EOF, -1 on error. ++ */ ++static ssize_t fill_buffer(int fd, unsigned char *buf, ssize_t len) ++{ ++ ssize_t got, have = 0; ++ ++ do { ++ got = read(fd, buf + have, len - have); ++ if (got == -1 && errno != EINTR) ++ return -1; ++ have += got; ++ } while (got > 0 && have < len); ++ return have; ++} ++ ++/* ++ * Write len number of bytes to fd. ++ * Return bytes written (>= 0), -1 on error. ++ */ ++static ssize_t flush_buffer(int fd, unsigned char *buf, ssize_t len) ++{ ++ ssize_t done, have = 0; ++ ++ do { ++ done = write(fd, buf + have, len - have); ++ if (done == -1 && errno != EINTR) ++ return -1; ++ have += done; ++ } while (done > 0 && have < len); ++ return have; ++} ++ ++/* ++ * Compare two files. Return 0, 1, or -1, depending on whether the ++ * files are equal, different, or an error occured. ++ * Return compare-different when target volume can not be read. Might be ++ * an interrupted volume update and then the target device returns -EIO but ++ * can be updated. ++ * ++ * fd_a is source ++ * fd_b is destination ++ */ ++static int compare_files(int fd_a, int fd_b) ++{ ++ unsigned char buf_a[COMPARE_BUF_SIZE], buf_b[COMPARE_BUF_SIZE]; ++ ssize_t len_a, len_b; ++ int rc; ++ ++ for (;;) { ++ len_a = fill_buffer(fd_a, buf_a, sizeof(buf_a)); ++ if (len_a == -1) { ++ rc = compare_error; ++ break; ++ } ++ len_b = fill_buffer(fd_b, buf_b, sizeof(buf_b)); ++ if (len_b == -1) { ++ rc = compare_different; ++ break; ++ } ++ if (len_a != len_b) { ++ rc = compare_different; ++ break; ++ } ++ if (len_a == 0) { /* Size on both files equal and EOF */ ++ rc = compare_equal; ++ break; ++ } ++ if (memcmp(buf_a, buf_b, len_a) != 0 ) { ++ rc = compare_different; ++ break; ++ } ++ } ++ /* Position both files at the beginning */ ++ if (lseek(fd_a, 0, SEEK_SET) == -1 || ++ lseek(fd_b, 0, SEEK_SET) == -1) ++ rc = seek_error; ++ return rc; ++} ++ ++int vol_get_used_bytes(int vol_fd, unsigned long long *bytes) ++{ ++ off_t res; ++ ++ res = lseek(vol_fd, 0, SEEK_END); ++ if (res == (off_t)-1) ++ return -1; ++ *bytes = (unsigned long long) res; ++ res = lseek(vol_fd, 0, SEEK_SET); ++ return res == (off_t)-1 ? -1 : 0; ++} ++ ++static int copy_files(libubi_t ulib, int fd_in, int fd_out) ++{ ++ unsigned char buf_a[COMPARE_BUF_SIZE]; ++ ssize_t len_a, len_b; ++ unsigned long long update_size, copied; ++ ++ if (vol_get_used_bytes(fd_in, &update_size) == -1 || ++ ubi_update_start(ulib, fd_out, update_size) == -1) ++ return update_error; ++ for (copied = 0; copied < update_size; copied += len_b ) { ++ len_a = fill_buffer(fd_in, buf_a, sizeof(buf_a)); ++ if (len_a == -1) ++ return read_error; ++ if (len_a == 0) /* Reach EOF */ ++ return 0; ++ len_b = flush_buffer(fd_out, buf_a, len_a); ++ if (len_b != len_a) ++ return write_error; ++ } ++ return 0; ++} ++ ++int ubimirror(uint32_t devno, int seqnum, uint32_t *ids, ssize_t ids_size, ++ char *err_buf, size_t err_buf_size) ++{ ++ int rc = 0; ++ uint32_t src_id; ++ char path[PATH_MAX]; ++ libubi_t ulib; ++ int fd_in = -1, i = 0, fd_out = -1; ++ ++ if (ids_size == 0) ++ return 0; ++ else { ++ if ((seqnum < 0) || (seqnum > (ids_size - 1))) { ++ EBUF("volume id %d out of range", seqnum); ++ return EUBIMIRROR_NO_SRC; ++ } ++ src_id = ids[seqnum]; ++ } ++ ++ ulib = libubi_open(); ++ if (ulib == NULL) ++ return ubi_error; ++ ++ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, src_id); ++ ++ fd_in = open(path, O_RDONLY); ++ if (fd_in == -1) { ++ EBUF("open error source volume %d", ids[i]); ++ rc = open_error; ++ goto err; ++ } ++ ++ for (i = 0; i < ids_size; i++) { ++ if (ids[i] == src_id) /* skip self-mirror */ ++ continue; ++ ++ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, ids[i]); ++ ++ fd_out = open(path, O_RDWR); ++ if (fd_out < 0){ ++ EBUF("open error destination volume %d", ids[i]); ++ rc = open_error; ++ goto err; ++ } ++ rc = compare_files(fd_in, fd_out); ++ if (rc < 0) { ++ EBUF("compare error volume %d and %d", src_id, ids[i]); ++ goto err; ++ } else if (rc == compare_different) { ++ rc = copy_files(ulib, fd_in, fd_out); ++ if (rc != 0) { ++ EBUF("mirror error volume %d to %d", src_id, ++ ids[i]); ++ goto err; ++ } ++ } ++ if ((rc = close(fd_out)) == -1) { ++ EBUF("close error volume %d", ids[i]); ++ rc = close_error; ++ goto err; ++ } else ++ fd_out = -1; ++ } ++err: ++ if (fd_out != -1) ++ close(fd_out); ++ if (fd_in != -1) ++ close(fd_in); ++ if (ulib != NULL) ++ libubi_close(ulib); ++ return rc; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/list.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/list.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/list.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/list.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,149 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ */ ++ ++#include ++#include ++#include ++ ++#include "list.h" ++ ++list_t ++mk_empty(void) ++{ ++ return (list_t) NULL; ++} ++ ++int ++is_empty(list_t l) ++{ ++ return l == NULL; ++} ++ ++info_t ++head(list_t l) ++{ ++ assert(!is_empty(l)); ++ return l->info; ++} ++ ++list_t ++tail(list_t l) ++{ ++ assert(!is_empty(l)); ++ return l->next; ++} ++ ++list_t ++remove_head(list_t l) ++{ ++ list_t res; ++ assert(!is_empty(l)); ++ ++ res = l->next; ++ free(l); ++ return res; ++} ++ ++list_t ++cons(info_t e, list_t l) ++{ ++ list_t res = malloc(sizeof(*l)); ++ if (!res) ++ return NULL; ++ res->info = e; ++ res->next = l; ++ ++ return res; ++} ++ ++list_t ++prepend_elem(info_t e, list_t l) ++{ ++ return cons(e,l); ++} ++ ++list_t ++append_elem(info_t e, list_t l) ++{ ++ if (is_empty(l)) { ++ return cons(e,l); ++ } ++ l->next = append_elem(e, l->next); ++ ++ return l; ++} ++ ++list_t ++insert_sorted(cmp_func_t cmp, info_t e, list_t l) ++{ ++ if (is_empty(l)) ++ return cons(e, l); ++ ++ switch (cmp(e, l->info)) { ++ case -1: ++ case 0: ++ return l; ++ break; ++ case 1: ++ l->next = insert_sorted(cmp, e, l); ++ break; ++ default: ++ break; ++ } ++ ++ /* never reached */ ++ return NULL; ++} ++ ++list_t ++remove_all(free_func_t free_func, list_t l) ++{ ++ if (is_empty(l)) ++ return l; ++ list_t lnext = l->next; ++ ++ if (free_func && l->info) { ++ free_func(&(l->info)); ++ } ++ free(l); ++ ++ return remove_all(free_func, lnext); ++} ++ ++ ++info_t ++is_in(cmp_func_t cmp, info_t e, list_t l) ++{ ++ return ++ (is_empty(l)) ++ ? NULL ++ : (cmp(e, l->info)) == 0 ? l->info : is_in(cmp, e, l->next); ++} ++ ++ ++void ++apply(process_func_t process_func, list_t l) ++{ ++ list_t ptr; ++ void *i; ++ foreach(i, ptr, l) { ++ process_func(i); ++ } ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/list.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/list.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/list.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/list.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,56 @@ ++#ifndef __LIST_H__ ++#define __LIST_H__ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ */ ++ ++#include ++ ++#define foreach(elem, ptr, list) \ ++ for (elem = list != NULL ? (typeof(elem)) head(list) \ ++ : NULL, ptr = list; \ ++ ptr != NULL; \ ++ ptr = tail(ptr), \ ++ elem = (typeof(elem)) ptr ? head(ptr) : NULL) ++ ++typedef struct node* list_t; ++typedef void* info_t; ++typedef int (*free_func_t)(info_t*); ++typedef int (*cmp_func_t)(info_t, info_t); ++typedef void (*process_func_t)(info_t); ++ ++struct node { ++ list_t next; ++ info_t info; ++}; ++ ++list_t mk_empty(void); ++int is_empty(list_t l); ++info_t is_in(cmp_func_t cmp, info_t e, list_t l); ++info_t head(list_t l); ++list_t tail(list_t l); ++list_t remove_head(list_t l); ++list_t cons(info_t e, list_t l); ++list_t prepend_elem(info_t e, list_t); ++list_t append_elem(info_t e, list_t); ++list_t remove_all(free_func_t free_func, list_t l); ++list_t insert_sorted(cmp_func_t cmp_func, info_t e, list_t l); ++void apply(process_func_t process_func, list_t l); ++ ++#endif /* __LIST_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/mkbootenv.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/mkbootenv.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/mkbootenv.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/mkbootenv.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,168 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ * ++ * Create boot-parameter/pdd data from an ASCII-text input file. ++ * ++ * 1.2 Removed argp because we want to use uClibc. ++ * 1.3 Minor cleanup ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "config.h" ++#include "bootenv.h" ++#include "error.h" ++ ++#define PROGRAM_VERSION "1.3" ++ ++static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" ++ "mkbootenv - processes bootenv text files and convertes " ++ "them into a binary format.\n"; ++ ++static const char copyright [] __attribute__((unused)) = ++ "Copyright (c) International Business Machines Corp., 2006"; ++ ++static const char *optionsstr = ++" -c, --copyright Print copyright informatoin.\n" ++" -o, --output= Write the output data to instead of\n" ++" stdout.\n" ++" -?, --help Give this help list\n" ++" --usage Give a short usage message\n" ++" -V, --version Print program version\n"; ++ ++static const char *usage = ++"Usage: mkbootenv [-c?V] [-o ] [--copyright] [--output=]\n" ++" [--help] [--usage] [--version] [bootenv-txt-file]\n"; ++ ++struct option long_options[] = { ++ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, ++ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, ++ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ { NULL, 0, NULL, 0} ++}; ++ ++typedef struct myargs { ++ FILE* fp_in; ++ FILE* fp_out; ++ ++ char *arg1; ++ char **options; /* [STRING...] */ ++} myargs; ++ ++static int ++parse_opt(int argc, char **argv, myargs *args) ++{ ++ while (1) { ++ int key; ++ ++ key = getopt_long(argc, argv, "co:?V", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 'c': ++ fprintf(stderr, "%s\n", copyright); ++ exit(0); ++ break; ++ case 'o': ++ args->fp_out = fopen(optarg, "wb"); ++ if ((args->fp_out) == NULL) { ++ fprintf(stderr, "Cannot open file %s " ++ "for output\n", optarg); ++ exit(1); ++ } ++ break; ++ case '?': /* help */ ++ printf("%s", doc); ++ printf("%s", optionsstr); ++ printf("\nReport bugs to %s\n", ++ PACKAGE_BUGREPORT); ++ exit(0); ++ break; ++ case 'V': ++ printf("%s\n", PROGRAM_VERSION); ++ exit(0); ++ break; ++ default: ++ printf("%s", usage); ++ exit(-1); ++ } ++ } ++ ++ if (optind < argc) { ++ args->fp_in = fopen(argv[optind++], "rb"); ++ if ((args->fp_in) == NULL) { ++ fprintf(stderr, "Cannot open file %s for input\n", ++ argv[optind]); ++ exit(1); ++ } ++ } ++ ++ return 0; ++} ++ ++int ++main(int argc, char **argv) { ++ int rc = 0; ++ bootenv_t env; ++ ++ myargs args = { ++ .fp_in = stdin, ++ .fp_out = stdout, ++ .arg1 = NULL, ++ .options = NULL, ++ }; ++ ++ parse_opt(argc, argv, &args); ++ ++ rc = bootenv_create(&env); ++ if (rc != 0) { ++ err_msg("Cannot create bootenv handle."); ++ goto err; ++ } ++ rc = bootenv_read_txt(args.fp_in, env); ++ if (rc != 0) { ++ err_msg("Cannot read bootenv from input file."); ++ goto err; ++ } ++ rc = bootenv_write(args.fp_out, env); ++ if (rc != 0) { ++ err_msg("Cannot write bootenv to output file."); ++ goto err; ++ } ++ ++ if (args.fp_in != stdin) { ++ fclose(args.fp_in); ++ } ++ if (args.fp_out != stdout) { ++ fclose(args.fp_out); ++ } ++ ++err: ++ bootenv_destroy(&env); ++ return rc; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/nand2bin.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/nand2bin.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/nand2bin.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/nand2bin.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,493 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006, 2007 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Frank Haverkamp ++ * ++ * An utility to decompose NAND images and strip OOB off. Not yet finished ... ++ * ++ * 1.2 Removed argp because we want to use uClibc. ++ * 1.3 Minor cleanup ++ * 1.4 Fixed OOB output file ++ * 1.5 Added verbose output and option to set blocksize. ++ * Added split block mode for more convenient analysis. ++ * 1.6 Fixed ECC error detection and correction. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "config.h" ++#include "nandecc.h" ++ ++#define PROGRAM_VERSION "1.6" ++ ++#define MAXPATH 1024 ++#define MIN(x,y) ((x)<(y)?(x):(y)) ++ ++struct args { ++ const char *oob_file; ++ const char *output_file; ++ size_t pagesize; ++ size_t blocksize; ++ int split_blocks; ++ size_t in_len; /* size of input file */ ++ int correct_ecc; ++ ++ /* special stuff needed to get additional arguments */ ++ char *arg1; ++ char **options; /* [STRING...] */ ++}; ++ ++static struct args myargs = { ++ .output_file = "data.bin", ++ .oob_file = "oob.bin", ++ .pagesize = 2048, ++ .blocksize = 128 * 1024, ++ .in_len = 0, ++ .split_blocks = 0, ++ .correct_ecc = 0, ++ .arg1 = NULL, ++ .options = NULL, ++}; ++ ++static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" ++ "nand2bin - split data and OOB.\n"; ++ ++static const char *optionsstr = ++" -o, --output= Data output file\n" ++" -O, --oob= OOB output file\n" ++" -p, --pagesize= NAND pagesize\n" ++" -b, --blocksize= NAND blocksize\n" ++" -s, --split-blocks generate binaries for each block\n" ++" -e, --correct-ecc Correct data according to ECC info\n" ++" -v, --verbose verbose output\n" ++" -?, --help Give this help list\n" ++" --usage Give a short usage message\n"; ++ ++static const char *usage = ++"Usage: nand2bin [-?] [-o ] [-O ] [-p ]\n" ++" [--output=] [--oob=] [--pagesize=] [--help]\n" ++" [--usage] input.mif\n"; ++ ++static int verbose = 0; ++ ++static struct option long_options[] = { ++ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, ++ { .name = "oob", .has_arg = 1, .flag = NULL, .val = 'O' }, ++ { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' }, ++ { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'b' }, ++ { .name = "split-blocks", .has_arg = 0, .flag = NULL, .val = 's' }, ++ { .name = "correct-ecc", .has_arg = 0, .flag = NULL, .val = 'e' }, ++ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, ++ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, ++ { NULL, 0, NULL, 0} ++}; ++ ++/* ++ * str_to_num - Convert string into number and cope with endings like ++ * k, K, kib, KiB for kilobyte ++ * m, M, mib, MiB for megabyte ++ */ ++static uint32_t str_to_num(char *str) ++{ ++ char *s = str; ++ ulong num = strtoul(s, &s, 0); ++ ++ if (*s != '\0') { ++ if (strcmp(s, "KiB") == 0) ++ num *= 1024; ++ else if (strcmp(s, "MiB") == 0) ++ num *= 1024*1024; ++ else { ++ fprintf(stderr, "WARNING: Wrong number format " ++ "\"%s\", check your paramters!\n", str); ++ } ++ } ++ return num; ++} ++ ++/* ++ * @brief Parse the arguments passed into the test case. ++ * ++ * @param argc The number of arguments ++ * @param argv The argument list ++ * @param args Pointer to program args structure ++ * ++ * @return error ++ * ++ */ ++static int parse_opt(int argc, char **argv, struct args *args) ++{ ++ while (1) { ++ int key; ++ ++ key = getopt_long(argc, argv, "b:eo:O:p:sv?", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 'p': /* --pagesize */ ++ args->pagesize = str_to_num(optarg); ++ break; ++ ++ case 'b': /* --blocksize */ ++ args->blocksize = str_to_num(optarg); ++ break; ++ ++ case 'v': /* --verbose */ ++ verbose++; ++ break; ++ ++ case 's': /* --split-blocks */ ++ args->split_blocks = 1; ++ break; ++ ++ case 'e': /* --correct-ecc */ ++ args->correct_ecc = 1; ++ break; ++ ++ case 'o': /* --output= */ ++ args->output_file = optarg; ++ break; ++ ++ case 'O': /* --oob= */ ++ args->oob_file = optarg; ++ break; ++ ++ case '?': /* help */ ++ printf("Usage: nand2bin [OPTION...] input.mif\n"); ++ printf("%s", doc); ++ printf("%s", optionsstr); ++ printf("\nReport bugs to %s\n", ++ PACKAGE_BUGREPORT); ++ exit(0); ++ break; ++ ++ case 'V': ++ printf("%s\n", PROGRAM_VERSION); ++ exit(0); ++ break; ++ ++ default: ++ printf("%s", usage); ++ exit(-1); ++ } ++ } ++ ++ if (optind < argc) ++ args->arg1 = argv[optind++]; ++ ++ return 0; ++} ++ ++static int calc_oobsize(size_t pagesize) ++{ ++ switch (pagesize) { ++ case 512: return 16; ++ case 2048: return 64; ++ default: ++ exit(EXIT_FAILURE); ++ } ++ return 0; ++} ++ ++static inline void hexdump(FILE *fp, const uint8_t *buf, ssize_t size) ++{ ++ int k; ++ ++ for (k = 0; k < size; k++) { ++ fprintf(fp, "%02x ", buf[k]); ++ if ((k & 15) == 15) ++ fprintf(fp, "\n"); ++ } ++} ++ ++static int process_page(uint8_t* buf, uint8_t *oobbuf, size_t pagesize) ++{ ++ int eccpoi, oobsize; ++ size_t i; ++ ++ switch (pagesize) { ++ case 2048: oobsize = 64; eccpoi = 64 / 2; break; ++ case 512: oobsize = 16; eccpoi = 16 / 2; break; ++ default: ++ fprintf(stderr, "Unsupported page size: %zd\n", pagesize); ++ return -EINVAL; ++ } ++ memset(oobbuf, 0xff, oobsize); ++ ++ for (i = 0; i < pagesize; i += 256, eccpoi += 3) { ++ oobbuf[eccpoi++] = 0x0; ++ /* Calculate ECC */ ++ nand_calculate_ecc(&buf[i], &oobbuf[eccpoi]); ++ } ++ return 0; ++} ++ ++static int bad_marker_offs_in_oob(int pagesize) ++{ ++ switch (pagesize) { ++ case 2048: return 0; ++ case 512: return 5; ++ } ++ return -EINVAL; ++} ++ ++static int decompose_image(struct args *args, FILE *in_fp, ++ FILE *bin_fp, FILE *oob_fp) ++{ ++ int read, rc, page = 0; ++ size_t oobsize = calc_oobsize(args->pagesize); ++ uint8_t *buf = malloc(args->pagesize); ++ uint8_t *oob = malloc(oobsize); ++ uint8_t *calc_oob = malloc(oobsize); ++ uint8_t *calc_buf = malloc(args->pagesize); ++ uint8_t *page_buf; ++ int pages_per_block = args->blocksize / args->pagesize; ++ int eccpoi = 0, eccpoi_start; ++ unsigned int i; ++ int badpos = bad_marker_offs_in_oob(args->pagesize); ++ ++ switch (args->pagesize) { ++ case 2048: eccpoi_start = 64 / 2; break; ++ case 512: eccpoi_start = 16 / 2; break; ++ default: exit(EXIT_FAILURE); ++ } ++ ++ if (!buf) ++ exit(EXIT_FAILURE); ++ if (!oob) ++ exit(EXIT_FAILURE); ++ if (!calc_oob) ++ exit(EXIT_FAILURE); ++ if (!calc_buf) ++ exit(EXIT_FAILURE); ++ ++ while (!feof(in_fp)) { ++ /* read page by page */ ++ read = fread(buf, 1, args->pagesize, in_fp); ++ if (ferror(in_fp)) { ++ fprintf(stderr, "I/O Error."); ++ exit(EXIT_FAILURE); ++ } ++ if (read != (ssize_t)args->pagesize) ++ break; ++ ++ read = fread(oob, 1, oobsize, in_fp); ++ if (ferror(in_fp)) { ++ fprintf(stderr, "I/O Error."); ++ exit(EXIT_FAILURE); ++ } ++ ++ page_buf = buf; /* default is unmodified data */ ++ ++ if ((page == 0 || page == 1) && (oob[badpos] != 0xff)) { ++ if (verbose) ++ printf("Block %d is bad\n", ++ page / pages_per_block); ++ goto write_data; ++ } ++ if (args->correct_ecc) ++ page_buf = calc_buf; ++ ++ process_page(buf, calc_oob, args->pagesize); ++ memcpy(calc_buf, buf, args->pagesize); ++ ++ /* ++ * Our oob format uses only the last 3 bytes out of 4. ++ * The first byte is 0x00 when the ECC is generated by ++ * our toolset and 0xff when generated by Linux. This ++ * is to be fixed when we want nand2bin work for other ++ * ECC layouts too. ++ */ ++ for (i = 0, eccpoi = eccpoi_start; i < args->pagesize; ++ i += 256, eccpoi += 4) ++ oob[eccpoi] = calc_oob[eccpoi] = 0xff; ++ ++ if (verbose && memcmp(oob, calc_oob, oobsize) != 0) { ++ printf("\nECC compare mismatch found at block %d page %d!\n", ++ page / pages_per_block, page % pages_per_block); ++ ++ printf("Read out OOB Data:\n"); ++ hexdump(stdout, oob, oobsize); ++ ++ printf("Calculated OOB Data:\n"); ++ hexdump(stdout, calc_oob, oobsize); ++ } ++ ++ /* Do correction on subpage base */ ++ for (i = 0, eccpoi = eccpoi_start; i < args->pagesize; ++ i += 256, eccpoi += 4) { ++ rc = nand_correct_data(calc_buf + i, &oob[eccpoi + 1], ++ &calc_oob[eccpoi + 1]); ++ ++ if (rc == -1) ++ fprintf(stdout, "Uncorrectable ECC error at " ++ "block %d page %d/%d\n", ++ page / pages_per_block, ++ page % pages_per_block, i / 256); ++ else if (rc > 0) ++ fprintf(stdout, "Correctable ECC error at " ++ "block %d page %d/%d\n", ++ page / pages_per_block, ++ page % pages_per_block, i / 256); ++ } ++ ++ write_data: ++ rc = fwrite(page_buf, 1, args->pagesize, bin_fp); ++ if (ferror(bin_fp)) { ++ fprintf(stderr, "I/O Error."); ++ exit(EXIT_FAILURE); ++ } ++ rc = fwrite(oob, 1, oobsize, oob_fp); ++ if (ferror(bin_fp)) { ++ fprintf(stderr, "I/O Error."); ++ exit(EXIT_FAILURE); ++ } ++ ++ page++; ++ } ++ free(calc_buf); ++ free(calc_oob); ++ free(oob); ++ free(buf); ++ return 0; ++} ++ ++static int split_blocks(struct args *args, FILE *in_fp) ++{ ++ uint8_t *buf; ++ size_t oobsize = calc_oobsize(args->pagesize); ++ int pages_per_block = args->blocksize / args->pagesize; ++ int block_len = pages_per_block * (args->pagesize + oobsize); ++ int blocks = args->in_len / block_len; ++ char bname[256] = { 0, }; ++ int badpos = bad_marker_offs_in_oob(args->pagesize); ++ int bad_blocks = 0, i, bad_block = 0; ++ ssize_t rc; ++ FILE *b; ++ ++ buf = malloc(block_len); ++ if (!buf) { ++ perror("Not enough memory"); ++ exit(EXIT_FAILURE); ++ } ++ ++ for (i = 0; i < blocks; i++) { ++ rc = fread(buf, 1, block_len, in_fp); ++ if (rc != block_len) { ++ fprintf(stderr, "cannot read enough data!\n"); ++ exit(EXIT_FAILURE); ++ } ++ ++ /* do block analysis */ ++ bad_block = 0; ++ if ((buf[args->pagesize + badpos] != 0xff) || ++ (buf[2 * args->pagesize + oobsize + badpos] != 0xff)) { ++ bad_blocks++; ++ bad_block = 1; ++ } ++ if ((verbose && bad_block) || (verbose > 1)) { ++ printf("-- (block %d oob of page 0 and 1)\n", i); ++ hexdump(stdout, buf + args->pagesize, oobsize); ++ printf("--\n"); ++ hexdump(stdout, buf + 2 * args->pagesize + ++ oobsize, oobsize); ++ } ++ ++ /* write complete block out */ ++ snprintf(bname, sizeof(bname) - 1, "%s.%d", args->arg1, i); ++ b = fopen(bname, "w+"); ++ if (!b) { ++ perror("Cannot open file"); ++ exit(EXIT_FAILURE); ++ } ++ rc = fwrite(buf, 1, block_len, b); ++ if (rc != block_len) { ++ fprintf(stderr, "could not write all data!\n"); ++ exit(EXIT_FAILURE); ++ } ++ fclose(b); ++ } ++ ++ free(buf); ++ if (bad_blocks || verbose) ++ fprintf(stderr, "%d blocks, %d bad blocks\n", ++ blocks, bad_blocks); ++ return 0; ++} ++ ++int ++main(int argc, char *argv[]) ++{ ++ FILE *in, *bin = NULL, *oob = NULL; ++ struct stat file_info; ++ ++ parse_opt(argc, argv, &myargs); ++ ++ if (!myargs.arg1) { ++ fprintf(stderr, "Please specify input file!\n"); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (lstat(myargs.arg1, &file_info) != 0) { ++ perror("Cannot fetch file size from input file.\n"); ++ exit(EXIT_FAILURE); ++ } ++ myargs.in_len = file_info.st_size; ++ ++ in = fopen(myargs.arg1, "r"); ++ if (!in) { ++ perror("Cannot open file"); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (myargs.split_blocks) { ++ split_blocks(&myargs, in); ++ goto out; ++ } ++ ++ bin = fopen(myargs.output_file, "w+"); ++ if (!bin) { ++ perror("Cannot open file"); ++ exit(EXIT_FAILURE); ++ } ++ oob = fopen(myargs.oob_file, "w+"); ++ if (!oob) { ++ perror("Cannot open file"); ++ exit(EXIT_FAILURE); ++ } ++ decompose_image(&myargs, in, bin, oob); ++ ++ out: ++ if (in) fclose(in); ++ if (bin) fclose(bin); ++ if (oob) fclose(oob); ++ exit(EXIT_SUCCESS); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/nandcorr.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/nandcorr.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/nandcorr.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/nandcorr.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,95 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * ECC algorithm for NAND FLASH. Detects and corrects 1 bit errors in ++ * a 256 bytes of data. ++ * ++ * Reimplement by Thomas Gleixner after staring long enough at the ++ * mess in drivers/mtd/nand/nandecc.c ++ * ++ */ ++ ++#include "nandecc.h" ++ ++static int countbits(uint32_t byte) ++{ ++ int res = 0; ++ ++ for (;byte; byte >>= 1) ++ res += byte & 0x01; ++ return res; ++} ++ ++/** ++ * @dat: data which should be corrected ++ * @read_ecc: ecc information read from flash ++ * @calc_ecc: calculated ecc information from the data ++ * @return: number of corrected bytes ++ * or -1 when no correction is possible ++ */ ++int nand_correct_data(uint8_t *dat, const uint8_t *read_ecc, ++ const uint8_t *calc_ecc) ++{ ++ uint8_t s0, s1, s2; ++ ++ /* ++ * Do error detection ++ * ++ * Be careful, the index magic is due to a pointer to a ++ * uint32_t. ++ */ ++ s0 = calc_ecc[0] ^ read_ecc[0]; ++ s1 = calc_ecc[1] ^ read_ecc[1]; ++ s2 = calc_ecc[2] ^ read_ecc[2]; ++ ++ if ((s0 | s1 | s2) == 0) ++ return 0; ++ ++ /* Check for a single bit error */ ++ if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 && ++ ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 && ++ ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) { ++ ++ uint32_t byteoffs, bitnum; ++ ++ byteoffs = (s1 << 0) & 0x80; ++ byteoffs |= (s1 << 1) & 0x40; ++ byteoffs |= (s1 << 2) & 0x20; ++ byteoffs |= (s1 << 3) & 0x10; ++ ++ byteoffs |= (s0 >> 4) & 0x08; ++ byteoffs |= (s0 >> 3) & 0x04; ++ byteoffs |= (s0 >> 2) & 0x02; ++ byteoffs |= (s0 >> 1) & 0x01; ++ ++ bitnum = (s2 >> 5) & 0x04; ++ bitnum |= (s2 >> 4) & 0x02; ++ bitnum |= (s2 >> 3) & 0x01; ++ ++ dat[byteoffs] ^= (1 << bitnum); ++ ++ return 1; ++ } ++ ++ if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1) ++ return 1; ++ ++ return -1; ++} ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/nandecc.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/nandecc.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/nandecc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/nandecc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,159 @@ ++/* ++ * This file contains an ECC algorithm from Toshiba that detects and ++ * corrects 1 bit errors in a 256 byte block of data. ++ * ++ * drivers/mtd/nand/nand_ecc.c ++ * ++ * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) ++ * Toshiba America Electronics Components, Inc. ++ * ++ * This file is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 or (at your option) any ++ * later version. ++ * ++ * This file is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this file; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ++ * ++ * As a special exception, if other files instantiate templates or use ++ * macros or inline functions from these files, or you compile these ++ * files and link them with other works to produce a work based on these ++ * files, these files do not by themselves cause the resulting work to be ++ * covered by the GNU General Public License. However the source code for ++ * these files must still be made available in accordance with section (3) ++ * of the GNU General Public License. ++ * ++ * This exception does not invalidate any other reasons why a work based on ++ * this file might be covered by the GNU General Public License. ++ */ ++ ++#include "nandecc.h" ++ ++/* ++ * Pre-calculated 256-way 1 byte column parity ++ */ ++static const uint8_t nand_ecc_precalc_table[] = { ++ 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, ++ 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, ++ 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, ++ 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, ++ 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, ++ 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, ++ 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, ++ 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, ++ 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, ++ 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, ++ 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, ++ 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, ++ 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, ++ 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, ++ 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, ++ 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, ++ 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, ++ 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, ++ 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, ++ 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, ++ 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, ++ 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, ++ 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, ++ 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, ++ 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, ++ 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, ++ 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, ++ 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, ++ 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, ++ 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, ++ 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, ++ 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 ++}; ++ ++/** ++ * nand_trans_result - [GENERIC] create non-inverted ECC ++ * @reg2: line parity reg 2 ++ * @reg3: line parity reg 3 ++ * @ecc_code: ecc ++ * ++ * Creates non-inverted ECC code from line parity ++ */ ++static void nand_trans_result(uint8_t reg2, uint8_t reg3, ++ uint8_t *ecc_code) ++{ ++ uint8_t a, b, i, tmp1, tmp2; ++ ++ /* Initialize variables */ ++ a = b = 0x80; ++ tmp1 = tmp2 = 0; ++ ++ /* Calculate first ECC byte */ ++ for (i = 0; i < 4; i++) { ++ if (reg3 & a) /* LP15,13,11,9 --> ecc_code[0] */ ++ tmp1 |= b; ++ b >>= 1; ++ if (reg2 & a) /* LP14,12,10,8 --> ecc_code[0] */ ++ tmp1 |= b; ++ b >>= 1; ++ a >>= 1; ++ } ++ ++ /* Calculate second ECC byte */ ++ b = 0x80; ++ for (i = 0; i < 4; i++) { ++ if (reg3 & a) /* LP7,5,3,1 --> ecc_code[1] */ ++ tmp2 |= b; ++ b >>= 1; ++ if (reg2 & a) /* LP6,4,2,0 --> ecc_code[1] */ ++ tmp2 |= b; ++ b >>= 1; ++ a >>= 1; ++ } ++ ++ /* Store two of the ECC bytes */ ++ ecc_code[1] = tmp1; ++ ecc_code[0] = tmp2; ++} ++ ++/** ++ * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for ++ * 256 byte block ++ * ++ * @dat: raw data ++ * @ecc_code: buffer for ECC ++ */ ++int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code) ++{ ++ uint8_t idx, reg1, reg2, reg3; ++ int j; ++ ++ /* Initialize variables */ ++ reg1 = reg2 = reg3 = 0; ++ ecc_code[0] = ecc_code[1] = ecc_code[2] = 0; ++ ++ /* Build up column parity */ ++ for(j = 0; j < 256; j++) { ++ ++ /* Get CP0 - CP5 from table */ ++ idx = nand_ecc_precalc_table[dat[j]]; ++ reg1 ^= (idx & 0x3f); ++ ++ /* All bit XOR = 1 ? */ ++ if (idx & 0x40) { ++ reg3 ^= (uint8_t) j; ++ reg2 ^= ~((uint8_t) j); ++ } ++ } ++ ++ /* Create non-inverted ECC code from line parity */ ++ nand_trans_result(reg2, reg3, ecc_code); ++ ++ /* Calculate final ECC code */ ++ ecc_code[0] = ~ecc_code[0]; ++ ecc_code[1] = ~ecc_code[1]; ++ ecc_code[2] = ((~reg1) << 2) | 0x03; ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/nandecc.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/nandecc.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/nandecc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/nandecc.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,29 @@ ++#ifndef _NAND_ECC_H ++#define _NAND_ECC_H ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * NAND ecc functions ++ */ ++ ++#include ++ ++int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code); ++int nand_correct_data(uint8_t *dat, const uint8_t *read_ecc, ++ const uint8_t *calc_ecc); ++ ++#endif +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pddcustomize.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pddcustomize.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pddcustomize.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pddcustomize.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,516 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2008 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ * ++ * PDD (platform description data) contains a set of system specific ++ * boot-parameters. Some of those parameters need to be handled ++ * special on updates, e.g. the MAC addresses. They must also be kept ++ * if the system is updated and one must be able to modify them when ++ * the system has booted the first time. This tool is intended to do ++ * PDD modification. ++ * ++ * 1.3 Removed argp because we want to use uClibc. ++ * 1.4 Minor cleanups ++ * 1.5 Migrated to new libubi ++ * 1.6 Fixed broken volume update ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "config.h" ++#include "bootenv.h" ++#include "error.h" ++#include "example_ubi.h" ++#include "libubi.h" ++#include "ubimirror.h" ++ ++#define PROGRAM_VERSION "1.6" ++ ++#define DEFAULT_DEV_PATTERN "/dev/ubi%d" ++#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d" ++ ++typedef enum action_t { ++ ACT_NORMAL = 0, ++ ACT_LIST, ++ ACT_ARGP_ABORT, ++ ACT_ARGP_ERR, ++} action_t; ++ ++#define ABORT_ARGP do { \ ++ args->action = ACT_ARGP_ABORT; \ ++} while (0) ++ ++#define ERR_ARGP do { \ ++ args->action = ACT_ARGP_ERR; \ ++} while (0) ++ ++static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" ++ "pddcustomize - customize bootenv and pdd values.\n"; ++ ++static const char *optionsstr = ++" -b, --both Mirror updated PDD to redundand copy.\n" ++" -c, --copyright Print copyright information.\n" ++" -i, --input= Binary input file. For debug purposes.\n" ++" -l, --list List card bootenv/pdd values.\n" ++" -o, --output= Binary output file. For debug purposes.\n" ++" -s, --side= The side/seqnum to update.\n" ++" -x, --host use x86 platform for debugging.\n" ++" -?, --help Give this help list\n" ++" --usage Give a short usage message\n" ++" -V, --version Print program version\n"; ++ ++static const char *usage = ++"Usage: pddcustomize [-bclx?V] [-i ] [-o ] [-s ]\n" ++" [--both] [--copyright] [--input=] [--list]\n" ++" [--output=] [--side=] [--host] [--help] [--usage]\n" ++" [--version] [key=value] [...]\n"; ++ ++struct option long_options[] = { ++ { .name = "both", .has_arg = 0, .flag = NULL, .val = 'b' }, ++ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, ++ { .name = "input", .has_arg = 1, .flag = NULL, .val = 'i' }, ++ { .name = "list", .has_arg = 0, .flag = NULL, .val = 'l' }, ++ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, ++ { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' }, ++ { .name = "host", .has_arg = 0, .flag = NULL, .val = 'x' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, ++ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ { NULL, 0, NULL, 0} ++}; ++ ++static const char copyright [] __attribute__((unused)) = ++ "Copyright IBM Corp 2006"; ++ ++typedef struct myargs { ++ action_t action; ++ const char* file_in; ++ const char* file_out; ++ int both; ++ int side; ++ int x86; /* X86 host, use files for testing */ ++ bootenv_t env_in; ++ ++ char *arg1; ++ char **options; /* [STRING...] */ ++} myargs; ++ ++static int ++get_update_side(const char* str) ++{ ++ uint32_t i = strtoul(str, NULL, 0); ++ ++ if ((i != 0) && (i != 1)) { ++ return -1; ++ } ++ ++ return i; ++} ++ ++static int ++extract_pair(bootenv_t env, const char* str) ++{ ++ int rc = 0; ++ char* key; ++ char* val; ++ ++ key = strdup(str); ++ if (key == NULL) ++ return -ENOMEM; ++ ++ val = strstr(key, "="); ++ if (val == NULL) { ++ err_msg("Wrong argument: %s\n" ++ "Expecting key=value pair.\n", str); ++ rc = -1; ++ goto err; ++ } ++ ++ *val = '\0'; /* split strings */ ++ val++; ++ rc = bootenv_set(env, key, val); ++ ++err: ++ free(key); ++ return rc; ++} ++ ++static int ++parse_opt(int argc, char **argv, myargs *args) ++{ ++ int rc = 0; ++ ++ while (1) { ++ int key; ++ ++ key = getopt_long(argc, argv, "clbxs:i:o:?V", ++ long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 'c': ++ err_msg("%s\n", copyright); ++ ABORT_ARGP; ++ break; ++ case 'l': ++ args->action = ACT_LIST; ++ break; ++ case 'b': ++ args->both = 1; ++ break; ++ case 'x': ++ args->x86 = 1; ++ break; ++ case 's': ++ args->side = get_update_side(optarg); ++ if (args->side < 0) { ++ err_msg("Unsupported seqnum: %d.\n" ++ "Supported seqnums are " ++ "'0' and '1'\n", ++ args->side, optarg); ++ ERR_ARGP; ++ } ++ break; ++ case 'i': ++ args->file_in = optarg; ++ break; ++ case 'o': ++ args->file_out = optarg; ++ break; ++ case '?': /* help */ ++ err_msg("Usage: pddcustomize [OPTION...] " ++ "[key=value] [...]"); ++ err_msg("%s", doc); ++ err_msg("%s", optionsstr); ++ err_msg("\nReport bugs to %s", ++ PACKAGE_BUGREPORT); ++ exit(0); ++ break; ++ case 'V': ++ err_msg("%s", PROGRAM_VERSION); ++ exit(0); ++ break; ++ default: ++ err_msg("%s", usage); ++ exit(-1); ++ } ++ } ++ ++ if (optind < argc) { ++ rc = extract_pair(args->env_in, argv[optind++]); ++ if (rc != 0) ++ ERR_ARGP; ++ } ++ ++ return 0; ++} ++ ++static int ++list_bootenv(bootenv_t env) ++{ ++ int rc = 0; ++ rc = bootenv_write_txt(stdout, env); ++ if (rc != 0) { ++ err_msg("Cannot list bootenv/pdd. rc: %d\n", rc); ++ goto err; ++ } ++err: ++ return rc; ++} ++ ++static int ++process_key_value(bootenv_t env_in, bootenv_t env) ++{ ++ int rc = 0; ++ size_t size, i; ++ const char* tmp; ++ const char** key_vec = NULL; ++ ++ rc = bootenv_get_key_vector(env_in, &size, 0, &key_vec); ++ if (rc != 0) ++ goto err; ++ ++ for (i = 0; i < size; i++) { ++ rc = bootenv_get(env_in, key_vec[i], &tmp); ++ if (rc != 0) { ++ err_msg("Cannot read value to input key: %s. rc: %d\n", ++ key_vec[i], rc); ++ goto err; ++ } ++ rc = bootenv_set(env, key_vec[i], tmp); ++ if (rc != 0) { ++ err_msg("Cannot set value key: %s. rc: %d\n", ++ key_vec[i], rc); ++ goto err; ++ } ++ } ++ ++err: ++ if (key_vec != NULL) ++ free(key_vec); ++ return rc; ++} ++ ++static int ++read_bootenv(const char* file, bootenv_t env) ++{ ++ int rc = 0; ++ FILE* fp_in = NULL; ++ ++ fp_in = fopen(file, "rb"); ++ if (fp_in == NULL) { ++ err_msg("Cannot open file: %s\n", file); ++ return -EIO; ++ } ++ ++ rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE); ++ if (rc != 0) { ++ err_msg("Cannot read bootenv from file %s. rc: %d\n", ++ file, rc); ++ goto err; ++ } ++ ++err: ++ fclose(fp_in); ++ return rc; ++} ++ ++/* ++ * Read bootenv from ubi volume ++ */ ++static int ++ubi_read_bootenv(uint32_t devno, uint32_t id, bootenv_t env) ++{ ++ libubi_t ulib; ++ int rc = 0; ++ char path[PATH_MAX]; ++ FILE* fp_in = NULL; ++ ++ ulib = libubi_open(); ++ if (ulib == NULL) { ++ err_msg("Cannot allocate ubi structure\n"); ++ return -1; ++ } ++ ++ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); ++ ++ fp_in = fopen(path, "r"); ++ if (fp_in == NULL) { ++ err_msg("Cannot open volume:%d number:%d\n", devno, id); ++ goto err; ++ } ++ ++ rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE); ++ if (rc != 0) { ++ err_msg("Cannot read volume:%d number:%d\n", devno, id); ++ goto err; ++ } ++ ++err: ++ if (fp_in) ++ fclose(fp_in); ++ libubi_close(ulib); ++ return rc; ++} ++ ++static int ++write_bootenv(const char* file, bootenv_t env) ++{ ++ int rc = 0; ++ FILE* fp_out; ++ ++ fp_out = fopen(file, "wb"); ++ if (fp_out == NULL) { ++ err_msg("Cannot open file: %s\n", file); ++ return -EIO; ++ } ++ ++ rc = bootenv_write(fp_out, env); ++ if (rc != 0) { ++ err_msg("Cannot write bootenv to file %s. rc: %d\n", file, rc); ++ goto err; ++ } ++ ++err: ++ fclose(fp_out); ++ return rc; ++} ++ ++/* ++ * Read bootenv from ubi volume ++ */ ++static int ++ubi_write_bootenv(uint32_t devno, uint32_t id, bootenv_t env) ++{ ++ libubi_t ulib; ++ int rc = 0; ++ char path[PATH_MAX]; ++ FILE* fp_out = NULL; ++ size_t nbytes; ++ ++ rc = bootenv_size(env, &nbytes); ++ if (rc) { ++ err_msg("Cannot determine size of bootenv structure\n"); ++ return rc; ++ } ++ ulib = libubi_open(); ++ if (ulib == NULL) { ++ err_msg("Cannot allocate ubi structure\n"); ++ return rc; ++ } ++ ++ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); ++ ++ fp_out = fopen(path, "r+"); ++ if (fp_out == NULL) { ++ err_msg("Cannot fopen volume:%d number:%d\n", devno, id); ++ rc = -EBADF; ++ goto err; ++ } ++ ++ rc = ubi_update_start(ulib, fileno(fp_out), nbytes); ++ if (rc != 0) { ++ err_msg("Cannot start update for %s\n", path); ++ goto err; ++ } ++ ++ rc = bootenv_write(fp_out, env); ++ if (rc != 0) { ++ err_msg("Cannot write bootenv to volume %d number:%d\n", ++ devno, id); ++ goto err; ++ } ++err: ++ if( fp_out ) ++ fclose(fp_out); ++ libubi_close(ulib); ++ return rc; ++} ++ ++static int ++do_mirror(int volno) ++{ ++ char errbuf[1024]; ++ uint32_t ids[2]; ++ int rc; ++ int src_volno_idx = 0; ++ ++ ids[0] = EXAMPLE_BOOTENV_VOL_ID_1; ++ ids[1] = EXAMPLE_BOOTENV_VOL_ID_2; ++ ++ if (volno == EXAMPLE_BOOTENV_VOL_ID_2) ++ src_volno_idx = 1; ++ ++ rc = ubimirror(EXAMPLE_UBI_DEVICE, src_volno_idx, ids, 2, errbuf, ++ sizeof errbuf); ++ if( rc ) ++ err_msg(errbuf); ++ return rc; ++} ++ ++int ++main(int argc, char **argv) { ++ int rc = 0; ++ bootenv_t env = NULL; ++ uint32_t boot_volno; ++ myargs args = { ++ .action = ACT_NORMAL, ++ .file_in = NULL, ++ .file_out = NULL, ++ .side = -1, ++ .x86 = 0, ++ .both = 0, ++ .env_in = NULL, ++ ++ .arg1 = NULL, ++ .options = NULL, ++ }; ++ ++ rc = bootenv_create(&env); ++ if (rc != 0) { ++ err_msg("Cannot create bootenv handle. rc: %d", rc); ++ goto err; ++ } ++ ++ rc = bootenv_create(&(args.env_in)); ++ if (rc != 0) { ++ err_msg("Cannot create bootenv handle. rc: %d", rc); ++ goto err; ++ } ++ ++ parse_opt(argc, argv, &args); ++ if (args.action == ACT_ARGP_ERR) { ++ rc = -1; ++ goto err; ++ } ++ if (args.action == ACT_ARGP_ABORT) { ++ rc = 0; ++ goto out; ++ } ++ ++ if ((args.side == 0) || (args.side == -1)) ++ boot_volno = EXAMPLE_BOOTENV_VOL_ID_1; ++ else ++ boot_volno = EXAMPLE_BOOTENV_VOL_ID_2; ++ ++ if( args.x86 ) ++ rc = read_bootenv(args.file_in, env); ++ else ++ rc = ubi_read_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env); ++ if (rc != 0) { ++ goto err; ++ } ++ ++ if (args.action == ACT_LIST) { ++ rc = list_bootenv(env); ++ if (rc != 0) { ++ goto err; ++ } ++ goto out; ++ } ++ ++ rc = process_key_value(args.env_in, env); ++ if (rc != 0) { ++ goto err; ++ } ++ ++ if( args.x86 ) ++ rc = write_bootenv(args.file_in, env); ++ else ++ rc = ubi_write_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env); ++ if (rc != 0) ++ goto err; ++ ++ if( args.both ) /* No side specified, update both */ ++ rc = do_mirror(boot_volno); ++ ++ out: ++ err: ++ bootenv_destroy(&env); ++ bootenv_destroy(&(args.env_in)); ++ return rc; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/peb.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/peb.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/peb.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/peb.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,116 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "peb.h" ++ ++int ++peb_cmp(peb_t eb_1, peb_t eb_2) ++{ ++ assert(eb_1); ++ assert(eb_2); ++ ++ return eb_1->num == eb_2->num ? 0 ++ : eb_1->num > eb_2->num ? 1 : -1; ++} ++ ++int ++peb_new(uint32_t eb_num, uint32_t eb_size, peb_t *peb) ++{ ++ int rc = 0; ++ ++ peb_t res = (peb_t) malloc(sizeof(struct peb)); ++ if (!res) { ++ rc = -ENOMEM; ++ goto err; ++ } ++ ++ res->num = eb_num; ++ res->size = eb_size; ++ res->data = (uint8_t*) malloc(res->size * sizeof(uint8_t)); ++ if (!res->data) { ++ rc = -ENOMEM; ++ goto err; ++ } ++ memset(res->data, 0xff, res->size); ++ ++ *peb = res; ++ return 0; ++err: ++ if (res) { ++ if (res->data) ++ free(res->data); ++ free(res); ++ } ++ *peb = NULL; ++ return rc; ++} ++ ++int ++peb_fill(peb_t peb, uint8_t* buf, size_t buf_size) ++{ ++ if (!peb) ++ return -EINVAL; ++ ++ if (buf_size > peb->size) ++ return -EINVAL; ++ ++ memcpy(peb->data, buf, buf_size); ++ return 0; ++} ++ ++int ++peb_write(FILE* fp_out, peb_t peb) ++{ ++ size_t written = 0; ++ ++ if (peb == NULL) ++ return -EINVAL; ++ ++ written = fwrite(peb->data, 1, peb->size, fp_out); ++ ++ if (written != peb->size) ++ return -EIO; ++ ++ return 0; ++} ++ ++int ++peb_free(peb_t* peb) ++{ ++ peb_t tmp = *peb; ++ if (tmp) { ++ if (tmp->data) ++ free(tmp->data); ++ free(tmp); ++ } ++ *peb = NULL; ++ ++ return 0; ++} ++ ++void peb_dump(FILE* fp_out, peb_t peb) ++{ ++ fprintf(fp_out, "num: %08d\tsize: 0x%08x\n", peb->num, peb->size); ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/peb.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/peb.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/peb.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/peb.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,41 @@ ++#ifndef __RAW_BLOCK_H__ ++#define __RAW_BLOCK_H__ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ */ ++ ++#include ++#include ++ ++typedef struct peb *peb_t; ++struct peb { ++ uint32_t num; /* Physical eraseblock number ++ * in the RAW file. */ ++ uint32_t size; /* Data Size (equals physical ++ * erase block size) */ ++ uint8_t* data; /* Data buffer */ ++}; ++ ++int peb_new(uint32_t peb_num, uint32_t peb_size, peb_t* peb); ++int peb_free(peb_t* peb); ++int peb_cmp(peb_t peb_1, peb_t peb_2); ++int peb_write(FILE* fp_out, peb_t peb); ++void peb_dump(FILE* fp_out, peb_t peb); ++ ++#endif /* __RAW_BLOCK_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfi.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfi.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfi.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfi.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,458 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * @file pfi.c ++ * ++ * @author Oliver Lohmann ++ * Andreas Arnez ++ * Joern Engel ++ * Frank Haverkamp ++ * ++ * @brief libpfi holds all code to create and process pfi files. ++ * ++ * Wed Feb 8 11:38:22 CET 2006: Initial creation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "pfi.h" ++ ++#define PFI_MAGIC "PFI!\n" ++#define PFI_DATA "DATA\n" /* The same size as PFI_MAGIC */ ++#define PFI_MAGIC_LEN 5 ++ ++static const char copyright [] __attribute__((unused)) = ++ "Copyright (c) International Business Machines Corp., 2006"; ++ ++enum key_id { ++ /* version 1 */ ++ key_version, /* must be index position 0! */ ++ key_mode, ++ key_size, ++ key_crc, ++ key_label, ++ key_flags, ++ key_ubi_ids, ++ key_ubi_size, ++ key_ubi_type, ++ key_ubi_names, ++ key_ubi_alignment, ++ key_raw_starts, ++ key_raw_total_size, ++ num_keys, ++}; ++ ++struct pfi_header { ++ char defined[num_keys]; /* reserve all possible keys even if ++ version does not require this. */ ++ int mode_no; /* current mode no. -> can only increase */ ++ union { ++ char *str; ++ uint32_t num; ++ } value[num_keys]; ++}; ++ ++ ++#define PFI_MANDATORY 0x0001 ++#define PFI_STRING 0x0002 ++#define PFI_LISTVALUE 0x0004 /* comma seperated list of nums */ ++#define PFI_MANDATORY_UBI 0x0008 ++#define PFI_MANDATORY_RAW 0x0010 ++ ++struct key_descriptor { ++ enum key_id id; ++ const char *name; ++ uint32_t flags; ++}; ++ ++static const struct key_descriptor key_desc_v1[] = { ++ { key_version, "version", PFI_MANDATORY }, ++ { key_mode, "mode", PFI_MANDATORY | PFI_STRING }, ++ { key_size, "size", PFI_MANDATORY }, ++ { key_crc, "crc", PFI_MANDATORY }, ++ { key_label, "label", PFI_MANDATORY | PFI_STRING }, ++ { key_flags, "flags", PFI_MANDATORY }, ++ { key_ubi_ids, "ubi_ids", PFI_MANDATORY_UBI | PFI_STRING }, ++ { key_ubi_size, "ubi_size", PFI_MANDATORY_UBI }, ++ { key_ubi_type, "ubi_type", PFI_MANDATORY_UBI | PFI_STRING }, ++ { key_ubi_names, "ubi_names", PFI_MANDATORY_UBI | PFI_STRING }, ++ { key_ubi_alignment, "ubi_alignment", PFI_MANDATORY_UBI }, ++ { key_raw_starts, "raw_starts", PFI_MANDATORY_RAW | PFI_STRING }, ++ { key_raw_total_size, "raw_total_size", PFI_MANDATORY_RAW }, ++}; ++ ++static const struct key_descriptor *key_descriptors[] = { ++ NULL, ++ key_desc_v1, /* version 1 */ ++}; ++ ++static const int key_descriptors_max[] = { ++ 0, /* version 0 */ ++ sizeof(key_desc_v1)/sizeof(struct key_descriptor), /* version 1 */ ++}; ++ ++#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) ++ ++static const char* modes[] = {"raw", "ubi"}; /* order isn't arbitrary! */ ++ ++/* latest version contains all possible keys */ ++static const struct key_descriptor *key_desc = key_desc_v1; ++ ++#define PFI_IS_UBI(mode) \ ++ (((mode) != NULL) && (strcmp("ubi", (mode)) == 0)) ++ ++#define PFI_IS_RAW(mode) \ ++ (((mode) != NULL) && (strcmp("raw", (mode)) == 0)) ++ ++/** ++ * @return <0 On Error. ++ * >=0 Mode no. ++ */ ++static int ++get_mode_no(const char* mode) ++{ ++ int i; ++ ++ for (i = 0; i < (int)ARRAY_SIZE(modes); i++) ++ if (strcmp(mode, modes[i]) == 0) ++ return i; ++ return -1; ++} ++ ++static int ++find_key_by_name (const char *name) ++{ ++ int i; ++ ++ for (i = 0; i < num_keys; i++) { ++ if (strcmp(name, key_desc[i].name) == 0) ++ return i; ++ } ++ return -1; ++} ++ ++static int ++check_valid (pfi_header head) ++{ ++ int i; ++ int max_keys; ++ uint32_t version; ++ const char *mode; ++ const struct key_descriptor *desc; ++ uint32_t to_check = PFI_MANDATORY; ++ ++ /* ++ * For the validity check the list of possible keys depends on ++ * the version of the PFI file used. ++ */ ++ version = head->value[key_version].num; ++ if (version > PFI_HDRVERSION) ++ return PFI_ENOHEADER; ++ ++ max_keys = key_descriptors_max[version]; ++ desc = key_descriptors[version]; ++ ++ if (!desc) ++ return PFI_ENOVERSION; ++ ++ mode = head->value[key_mode].str; ++ if (PFI_IS_UBI(mode)) { ++ to_check |= PFI_MANDATORY_UBI; ++ } ++ else if (PFI_IS_RAW(mode)) { ++ to_check |= PFI_MANDATORY_RAW; ++ } ++ else { /* neither UBI nor RAW == ERR */ ++ return PFI_EINSUFF; ++ } ++ ++ for (i = 0; i < max_keys; i++) { ++ if ((desc[i].flags & to_check) && !head->defined[i]) { ++ fprintf(stderr, "libpfi: %s missing\n", desc[i].name); ++ return PFI_EINSUFF; ++ } ++ } ++ ++ return 0; ++} ++ ++int pfi_header_init (pfi_header *head) ++{ ++ int i; ++ pfi_header self = (pfi_header) malloc(sizeof(*self)); ++ ++ *head = self; ++ if (self == NULL) ++ return PFI_ENOMEM; ++ ++ /* initialize maximum number of possible keys */ ++ for (i = 0; i < num_keys; i++) { ++ memset(self, 0, sizeof(*self)); ++ self->defined[i] = 0; ++ } ++ ++ return 0; ++} ++ ++int pfi_header_destroy (pfi_header *head) ++{ ++ int i; ++ pfi_header self = *head; ++ ++ for (i = 0; i < num_keys; i++) { ++ if (self->defined[i] && (key_desc[i].flags & PFI_STRING) && ++ self->value[i].str) { ++ free(self->value[i].str); ++ } ++ } ++ free(*head); ++ *head = NULL; ++ return 0; ++} ++ ++int pfi_header_setnumber (pfi_header head, ++ const char *key, uint32_t value) ++{ ++ int key_id = find_key_by_name(key); ++ ++ if (key_id < 0) ++ return PFI_EUNDEF; ++ ++ if (key_desc[key_id].flags & PFI_STRING) ++ return PFI_EBADTYPE; ++ ++ head->value[key_id].num = value; ++ head->defined[key_id] = 1; ++ return 0; ++} ++ ++int pfi_header_setvalue (pfi_header head, ++ const char *key, const char *value) ++{ ++ int key_id = find_key_by_name(key); ++ ++ if (value == NULL) ++ return PFI_EINSUFF; ++ ++ if ((key_id < 0) || (key_id >= num_keys)) ++ return PFI_EUNDEF; ++ ++ if (key_desc[key_id].flags & PFI_STRING) { ++ /* ++ * The value is a string. Copy to a newly allocated ++ * buffer. Delete the old value, if already set. ++ */ ++ size_t len = strlen(value) + 1; ++ char *old_str = NULL; ++ char *str; ++ ++ old_str = head->value[key_id].str; ++ if (old_str != NULL) ++ free(old_str); ++ ++ str = head->value[key_id].str = (char *) malloc(len); ++ if (str == NULL) ++ return PFI_ENOMEM; ++ ++ strcpy(str, value); ++ } else { ++ int len; ++ int ret; ++ /* FIXME: here we assume that the value is always ++ given in hex and starts with '0x'. */ ++ ret = sscanf(value, "0x%x%n", &head->value[key_id].num, &len); ++ if (ret < 1 || value[len] != '\0') ++ return PFI_EBADTYPE; ++ } ++ head->defined[key_id] = 1; ++ return 0; ++} ++ ++int pfi_header_getnumber (pfi_header head, ++ const char *key, uint32_t *value) ++{ ++ int key_id = find_key_by_name(key); ++ ++ if (key_id < 0) ++ return PFI_EUNDEF; ++ ++ if (key_desc[key_id].flags & PFI_STRING) ++ return PFI_EBADTYPE; ++ ++ if (!head->defined[key_id]) ++ return PFI_EUNDEF; ++ ++ *value = head->value[key_id].num; ++ return 0; ++} ++ ++int pfi_header_getstring (pfi_header head, ++ const char *key, char *value, size_t size) ++{ ++ int key_id = find_key_by_name(key); ++ ++ if (key_id < 0) ++ return PFI_EUNDEF; ++ ++ if (!(key_desc[key_id].flags & PFI_STRING)) ++ return PFI_EBADTYPE; ++ ++ if (!head->defined[key_id]) ++ return PFI_EUNDEF; ++ ++ strncpy(value, head->value[key_id].str, size-1); ++ value[size-1] = '\0'; ++ return 0; ++} ++ ++int pfi_header_write (FILE *out, pfi_header head) ++{ ++ int i; ++ int ret; ++ ++ pfi_header_setnumber(head, "version", PFI_HDRVERSION); ++ ++ if ((ret = check_valid(head)) != 0) ++ return ret; ++ ++ /* OK. Now write the header. */ ++ ++ ret = fwrite(PFI_MAGIC, 1, PFI_MAGIC_LEN, out); ++ if (ret < PFI_MAGIC_LEN) ++ return ret; ++ ++ ++ for (i = 0; i < num_keys; i++) { ++ if (!head->defined[i]) ++ continue; ++ ++ ret = fprintf(out, "%s=", key_desc[i].name); ++ if (ret < 0) ++ return PFI_EFILE; ++ ++ if (key_desc[i].flags & PFI_STRING) { ++ ret = fprintf(out, "%s", head->value[i].str); ++ if (ret < 0) ++ return PFI_EFILE; ++ } else { ++ ret = fprintf(out, "0x%8x", head->value[i].num); ++ if (ret < 0) ++ return PFI_EFILE; ++ ++ } ++ ret = fprintf(out, "\n"); ++ if (ret < 0) ++ return PFI_EFILE; ++ } ++ ret = fprintf(out, "\n"); ++ if (ret < 0) ++ return PFI_EFILE; ++ ++ ret = fflush(out); ++ if (ret != 0) ++ return PFI_EFILE; ++ ++ return 0; ++} ++ ++int pfi_header_read (FILE *in, pfi_header head) ++{ ++ char magic[PFI_MAGIC_LEN]; ++ char mode[PFI_KEYWORD_LEN]; ++ char buf[256]; ++ ++ if (PFI_MAGIC_LEN != fread(magic, 1, PFI_MAGIC_LEN, in)) ++ return PFI_EFILE; ++ if (memcmp(magic, PFI_MAGIC, PFI_MAGIC_LEN) != 0) { ++ if (memcmp(magic, PFI_DATA, PFI_MAGIC_LEN) == 0) { ++ return PFI_DATA_START; ++ } ++ return PFI_ENOHEADER; ++ } ++ ++ while (fgets(buf, sizeof(buf), in) != NULL && buf[0] != '\n') { ++ char *value; ++ char *end; ++ value = strchr(buf, '='); ++ if (value == NULL) ++ return PFI_ENOHEADER; ++ ++ *value = '\0'; ++ value++; ++ end = strchr(value, '\n'); ++ if (end) ++ *end = '\0'; ++ ++ if (pfi_header_setvalue(head, buf, value)) ++ return PFI_ENOHEADER; ++ } ++ ++ if (check_valid(head) != 0) ++ return PFI_ENOHEADER; ++ ++ /* set current mode no. in head */ ++ pfi_header_getstring(head, "mode", mode, PFI_KEYWORD_LEN); ++ if (head->mode_no > get_mode_no(mode)) { ++ return PFI_EMODE; ++ } ++ head->mode_no = get_mode_no(mode); ++ return 0; ++} ++ ++int pfi_header_dump (FILE *out, pfi_header head __attribute__((__unused__))) ++{ ++ fprintf(out, "Sorry not implemented yet. Write mail to " ++ "Andreas Arnez and complain!\n"); ++ return 0; ++} ++ ++int pfi_read (FILE *in, pfi_read_func func, void *priv_data) ++{ ++ int rc; ++ pfi_header header; ++ ++ rc = pfi_header_init (&header); ++ if (0 != rc) ++ return rc; ++ if (!func) ++ return PFI_EINVAL; ++ ++ while ((0 == rc) && !feof(in)) { ++ /* ++ * Read header and check consistency of the fields. ++ */ ++ rc = pfi_header_read( in, header ); ++ if (0 != rc) ++ break; ++ if (func) { ++ rc = func(in, header, priv_data); ++ if (rc != 0) ++ break; ++ } ++ } ++ ++ pfi_header_destroy(&header); ++ return rc; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfi.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfi.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfi.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfi.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,244 @@ ++#ifndef __pfi_h ++#define __pfi_h ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/** ++ * @file pfi.h ++ * ++ * @author Oliver Lohmann ++ * Andreas Arnez ++ * Joern Engel ++ * Frank Haverkamp ++ * ++ * @brief libpfi will hold all code to create and process pfi ++ * images. Definitions made in this file are equaly usable for the ++ * development host and the target system. ++ * ++ * @note This header additionally holds the official definitions for ++ * the pfi headers. ++ */ ++ ++#include /* FILE */ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* Definitions. */ ++ ++#define PFI_HDRVERSION 1 /* current header version */ ++ ++#define PFI_ENOVERSION 1 /* unknown version */ ++#define PFI_ENOHEADER 2 /* not a pfi header */ ++#define PFI_EINSUFF 3 /* insufficient information */ ++#define PFI_EUNDEF 4 /* key not defined */ ++#define PFI_ENOMEM 5 /* out of memory */ ++#define PFI_EBADTYPE 6 /* bad data type */ ++#define PFI_EFILE 7 /* file I/O error: see errno */ ++#define PFI_EFILEINVAL 8 /* file format not valid */ ++#define PFI_EINVAL 9 /* invalid parameter */ ++#define PFI_ERANGE 10 /* invalid range */ ++#define PFI_EMODE 11 /* expecting other mode in this header */ ++#define PFI_DATA_START 12 /* data section starts */ ++#define PFI_EMAX 13 /* should be always larger as the largest ++ error code */ ++ ++#define PFI_LABEL_LEN 64 /* This is the maximum length for a ++ PFI header label */ ++#define PFI_KEYWORD_LEN 32 /* This is the maximum length for an ++ entry in the mode and type fields */ ++ ++#define PFI_UBI_MAX_VOLUMES 128 ++#define PFI_UBI_VOL_NAME_LEN 127 ++ ++/** ++ * @brief The pfi header allows to set flags which influence the flashing ++ * behaviour. ++ */ ++#define PFI_FLAG_PROTECTED 0x00000001 ++ ++ ++/** ++ * @brief Handle to pfi header. Used in most of the functions associated ++ * with pfi file handling. ++ */ ++typedef struct pfi_header *pfi_header; ++ ++ ++/** ++ * @brief Initialize a pfi header object. ++ * ++ * @param head Pointer to handle. This function allocates memory ++ * for this data structure. ++ * @return 0 on success, otherwise: ++ * PFI_ENOMEM : no memory available for the handle. ++ */ ++int pfi_header_init (pfi_header *head); ++ ++ ++/** ++ * @brief Destroy a pfi header object. ++ * ++ * @param head handle. head is invalid after calling this function. ++ * @return 0 always. ++ */ ++int pfi_header_destroy (pfi_header *head); ++ ++ ++/** ++ * @brief Add a key/value pair to a pfi header object. ++ * ++ * @param head handle. ++ * @param key pointer to key string. Must be 0 terminated. ++ * @param value pointer to value string. Must be 0 terminated. ++ * @return 0 on success, otherwise: ++ * PFI_EUNDEF : key was not found. ++ * PFI_ENOMEM : no memory available for the handle. ++ * PFI_EBADTYPE : value is not an hex string. This happens ++ * when the key stores an integer and the ++ * new value is not convertable e.g. not in ++ * 0xXXXXXXXX format. ++ */ ++int pfi_header_setvalue (pfi_header head, ++ const char *key, const char *value); ++ ++ ++/** ++ * @brief Add a key/value pair to a pfi header object. Provide the ++ * value as a number. ++ * ++ * @param head handle. ++ * @param key pointer to key string. Must be 0 terminated. ++ * @param value value to set. ++ * @return 0 on success, otherwise: ++ * PFI_EUNDEF : key was not found. ++ * PFI_EBADTYPE : value is not a string. This happens ++ * when the key stores a string. ++ */ ++int pfi_header_setnumber (pfi_header head, ++ const char *key, uint32_t value); ++ ++ ++/** ++ * @brief For a given key, return the numerical value stored in a ++ * pfi header object. ++ * ++ * @param head handle. ++ * @param key pointer to key string. Must be 0 terminated. ++ * @param value pointer to value. ++ * @return 0 on success, otherwise: ++ * PFI_EUNDEF : key was not found. ++ * PFI_EBADTYPE : stored value is not an integer but a string. ++ */ ++int pfi_header_getnumber (pfi_header head, ++ const char *key, uint32_t *value); ++ ++ ++static inline uint32_t ++pfi_getnumber(pfi_header head, const char *key) ++{ ++ uint32_t value; ++ pfi_header_getnumber(head, key, &value); ++ return value; ++} ++ ++/** ++ * @brief For a given key, return the string value stored in a pfi ++ * header object. ++ * ++ * @param head handle. ++ * @param key pointer to key string. Must be 0 terminated. ++ * @param value pointer to value string. Memory must be allocated by the user. ++ * @return 0 on success, otherwise: ++ * PFI_EUNDEF : key was not found. ++ * PFI_EBADTYPE : stored value is not a string but an integer. ++ */ ++int pfi_header_getstring (pfi_header head, ++ const char *key, char *value, size_t size); ++ ++ ++/** ++ * @brief Write a pfi header object into a given file. ++ * ++ * @param out output stream. ++ * @param head handle. ++ * @return 0 on success, error values otherwise: ++ * PFI_EINSUFF : not all mandatory fields are filled. ++ * PFI_ENOHEADER : wrong header version or magic number. ++ * -E* : see . ++ */ ++int pfi_header_write (FILE *out, pfi_header head); ++ ++ ++/** ++ * @brief Read a pfi header object from a given file. ++ * ++ * @param in input stream. ++ * @param head handle. ++ * @return 0 on success, error values otherwise: ++ * PFI_ENOVERSION: unknown header version. ++ * PFI_EFILE : cannot read enough data. ++ * PFI_ENOHEADER : wrong header version or magic number. ++ * -E* : see . ++ * ++ * If the header verification returned success the user can assume that ++ * all mandatory fields for a particular version are accessible. Checking ++ * the return code when calling the get-function for those keys is not ++ * required in those cases. For optional fields the checking must still be ++ * done. ++ */ ++int pfi_header_read (FILE *in, pfi_header head); ++ ++ ++/** ++ * @brief Display a pfi header in human-readable form. ++ * ++ * @param out output stream. ++ * @param head handle. ++ * @return always 0. ++ * ++ * @note Prints out that it is not implemented and whom you should ++ * contact if you need it urgently!. ++ */ ++int pfi_header_dump (FILE *out, pfi_header head); ++ ++ ++/* ++ * @brief Iterates over a stream of pfi files. The iterator function ++ * must advance the file pointer in FILE *in to the next pfi ++ * header. Function exists on feof(in). ++ * ++ * @param in input file descriptor, must be open and valid. ++ * @param func iterator function called when pfi header could be ++ * read and was validated. The function must return 0 on ++ * success. ++ * @return See pfi_header_init and pfi_header_read. ++ * PFI_EINVAL : func is not valid ++ * 0 ok. ++ */ ++typedef int (* pfi_read_func)(FILE *in, pfi_header hdr, void *priv_data); ++ ++int pfi_read (FILE *in, pfi_read_func func, void *priv_data); ++ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* __pfi_h */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfi2bin.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfi2bin.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfi2bin.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfi2bin.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,682 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ * ++ * Convert a PFI file (partial flash image) into a plain binary file. ++ * This tool can be used to prepare the data to be burned into flash ++ * chips in a manufacturing step where the flashes are written before ++ * being soldered onto the hardware. For NAND images another step is ++ * required to add the right OOB data to the binary image. ++ * ++ * 1.3 Removed argp because we want to use uClibc. ++ * 1.4 Minor cleanups ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "config.h" ++#include "list.h" ++#include "error.h" ++#include "reader.h" ++#include "peb.h" ++#include "crc32.h" ++ ++#define PROGRAM_VERSION "1.4" ++ ++#define MAX_FNAME 255 ++#define DEFAULT_ERASE_COUNT 0 /* Hmmm.... Perhaps */ ++#define ERR_BUF_SIZE 1024 ++ ++#define MIN(a,b) ((a) < (b) ? (a) : (b)) ++ ++static uint32_t crc32_table[256]; ++static char err_buf[ERR_BUF_SIZE]; ++ ++/* ++ * Data used to buffer raw blocks which have to be ++ * located at a specific point inside the generated RAW file ++ */ ++ ++typedef enum action_t { ++ ACT_NOTHING = 0x00000000, ++ ACT_RAW = 0x00000001, ++} action_t; ++ ++static const char copyright [] __attribute__((unused)) = ++ "(c) Copyright IBM Corp 2006\n"; ++ ++static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" ++ "pfi2bin - a tool to convert PFI files into binary images.\n"; ++ ++static const char *optionsstr = ++" Common settings:\n" ++" -c, --copyright\n" ++" -v, --verbose Print more information.\n" ++"\n" ++" Input:\n" ++" -j, --platform=pdd-file PDD information which contains the card settings.\n" ++"\n" ++" Output:\n" ++" -o, --output=filename Outputfile, default: stdout.\n" ++"\n" ++" -?, --help Give this help list\n" ++" --usage Give a short usage message\n" ++" -V, --version Print program version\n"; ++ ++static const char *usage = ++"Usage: pfi2bin [-cv?V] [-j pdd-file] [-o filename] [--copyright]\n" ++" [--verbose] [--platform=pdd-file] [--output=filename] [--help]\n" ++" [--usage] [--version] pfifile\n"; ++ ++struct option long_options[] = { ++ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, ++ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, ++ { .name = "platform", .has_arg = 1, .flag = NULL, .val = 'j' }, ++ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, ++ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ { NULL, 0, NULL, 0} ++}; ++ ++typedef struct io { ++ FILE* fp_pdd; /* a FilePointer to the PDD data */ ++ FILE* fp_pfi; /* a FilePointer to the PFI input stream */ ++ FILE* fp_out; /* a FilePointer to the output stream */ ++} *io_t; ++ ++typedef struct myargs { ++ /* common settings */ ++ action_t action; ++ int verbose; ++ const char *f_in_pfi; ++ const char *f_in_pdd; ++ const char *f_out; ++ ++ /* special stuff needed to get additional arguments */ ++ char *arg1; ++ char **options; /* [STRING...] */ ++} myargs; ++ ++static int ++parse_opt(int argc, char **argv, myargs *args) ++{ ++ while (1) { ++ int key; ++ ++ key = getopt_long(argc, argv, "cvj:o:?V", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ /* common settings */ ++ case 'v': /* --verbose= */ ++ args->verbose = 1; ++ break; ++ ++ case 'c': /* --copyright */ ++ fprintf(stderr, "%s\n", copyright); ++ exit(0); ++ break; ++ ++ case 'j': /* --platform */ ++ args->f_in_pdd = optarg; ++ break; ++ ++ case 'o': /* --output */ ++ args->f_out = optarg; ++ break; ++ ++ case '?': /* help */ ++ printf("pfi2bin [OPTION...] pfifile\n"); ++ printf("%s", doc); ++ printf("%s", optionsstr); ++ printf("\nReport bugs to %s\n", ++ PACKAGE_BUGREPORT); ++ exit(0); ++ break; ++ ++ case 'V': ++ printf("%s\n", PROGRAM_VERSION); ++ exit(0); ++ break; ++ ++ default: ++ printf("%s", usage); ++ exit(-1); ++ } ++ } ++ ++ if (optind < argc) ++ args->f_in_pfi = argv[optind++]; ++ ++ return 0; ++} ++ ++ ++static size_t ++byte_to_blk(size_t byte, size_t blk_size) ++{ ++ return (byte % blk_size) == 0 ++ ? byte / blk_size ++ : byte / blk_size + 1; ++} ++ ++ ++ ++ ++/** ++ * @precondition IO: File stream points to first byte of RAW data. ++ * @postcondition IO: File stream points to first byte of next ++ * or EOF. ++ */ ++static int ++memorize_raw_eb(pfi_raw_t pfi_raw, pdd_data_t pdd, list_t *raw_pebs, ++ io_t io) ++{ ++ int rc = 0; ++ uint32_t i; ++ ++ size_t read, to_read, eb_num; ++ size_t bytes_left; ++ list_t pebs = *raw_pebs; ++ peb_t peb = NULL; ++ ++ long old_file_pos = ftell(io->fp_pfi); ++ for (i = 0; i < pfi_raw->starts_size; i++) { ++ bytes_left = pfi_raw->data_size; ++ rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET); ++ if (rc != 0) ++ goto err; ++ ++ eb_num = byte_to_blk(pfi_raw->starts[i], pdd->eb_size); ++ while (bytes_left) { ++ to_read = MIN(bytes_left, pdd->eb_size); ++ rc = peb_new(eb_num++, pdd->eb_size, &peb); ++ if (rc != 0) ++ goto err; ++ read = fread(peb->data, 1, to_read, io->fp_pfi); ++ if (read != to_read) { ++ rc = -EIO; ++ goto err; ++ } ++ pebs = append_elem(peb, pebs); ++ bytes_left -= read; ++ } ++ ++ } ++ *raw_pebs = pebs; ++ return 0; ++err: ++ pebs = remove_all((free_func_t)&peb_free, pebs); ++ return rc; ++} ++ ++static int ++convert_ubi_volume(pfi_ubi_t ubi, pdd_data_t pdd, list_t raw_pebs, ++ struct ubi_vtbl_record *vol_tab, ++ size_t *ebs_written, io_t io) ++{ ++ int rc = 0; ++ uint32_t i, j; ++ peb_t raw_peb; ++ peb_t cmp_peb; ++ ubi_info_t u; ++ size_t leb_total = 0; ++ uint8_t vol_type; ++ ++ switch (ubi->type) { ++ case pfi_ubi_static: ++ vol_type = UBI_VID_STATIC; break; ++ case pfi_ubi_dynamic: ++ vol_type = UBI_VID_DYNAMIC; break; ++ default: ++ vol_type = UBI_VID_DYNAMIC; ++ } ++ ++ rc = peb_new(0, 0, &cmp_peb); ++ if (rc != 0) ++ goto err; ++ ++ long old_file_pos = ftell(io->fp_pfi); ++ for (i = 0; i < ubi->ids_size; i++) { ++ rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET); ++ if (rc != 0) ++ goto err; ++ rc = ubigen_create(&u, ubi->ids[i], vol_type, ++ pdd->eb_size, DEFAULT_ERASE_COUNT, ++ ubi->alignment, UBI_VERSION, ++ pdd->vid_hdr_offset, 0, ubi->data_size, ++ io->fp_pfi, io->fp_out); ++ if (rc != 0) ++ goto err; ++ ++ rc = ubigen_get_leb_total(u, &leb_total); ++ if (rc != 0) ++ goto err; ++ ++ j = 0; ++ while(j < leb_total) { ++ cmp_peb->num = *ebs_written; ++ raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb, ++ raw_pebs); ++ if (raw_peb) { ++ rc = peb_write(io->fp_out, raw_peb); ++ } ++ else { ++ rc = ubigen_write_leb(u, NO_ERROR); ++ j++; ++ } ++ if (rc != 0) ++ goto err; ++ (*ebs_written)++; ++ } ++ /* memorize volume table entry */ ++ rc = ubigen_set_lvol_rec(u, ubi->size, ++ ubi->names[i], ++ (void*) &vol_tab[ubi->ids[i]]); ++ if (rc != 0) ++ goto err; ++ ubigen_destroy(&u); ++ } ++ ++ peb_free(&cmp_peb); ++ return 0; ++ ++err: ++ peb_free(&cmp_peb); ++ ubigen_destroy(&u); ++ return rc; ++} ++ ++ ++static FILE* ++my_fmemopen (void *buf, size_t size, const char *opentype) ++{ ++ FILE* f; ++ size_t ret; ++ ++ assert(strcmp(opentype, "r") == 0); ++ ++ f = tmpfile(); ++ ret = fwrite(buf, 1, size, f); ++ rewind(f); ++ ++ return f; ++} ++ ++/** ++ * @brief Builds a UBI volume table from a volume entry list. ++ * @return 0 On success. ++ * else Error. ++ */ ++static int ++write_ubi_volume_table(pdd_data_t pdd, list_t raw_pebs, ++ struct ubi_vtbl_record *vol_tab, size_t vol_tab_size, ++ size_t *ebs_written, io_t io) ++{ ++ int rc = 0; ++ ubi_info_t u; ++ peb_t raw_peb; ++ peb_t cmp_peb; ++ size_t leb_size, leb_total, j = 0; ++ uint8_t *ptr = NULL; ++ FILE* fp_leb = NULL; ++ int vt_slots; ++ size_t vol_tab_size_limit; ++ ++ rc = peb_new(0, 0, &cmp_peb); ++ if (rc != 0) ++ goto err; ++ ++ /* @FIXME: Artem creates one volume with 2 LEBs. ++ * IMO 2 volumes would be more convenient. In order ++ * to get 2 reserved LEBs from ubigen, I have to ++ * introduce this stupid mechanism. Until no final ++ * decision of the VTAB structure is made... Good enough. ++ */ ++ rc = ubigen_create(&u, UBI_LAYOUT_VOLUME_ID, UBI_VID_DYNAMIC, ++ pdd->eb_size, DEFAULT_ERASE_COUNT, ++ 1, UBI_VERSION, ++ pdd->vid_hdr_offset, UBI_COMPAT_REJECT, ++ vol_tab_size, stdin, io->fp_out); ++ /* @FIXME stdin for fp_in is a hack */ ++ if (rc != 0) ++ goto err; ++ rc = ubigen_get_leb_size(u, &leb_size); ++ if (rc != 0) ++ goto err; ++ ubigen_destroy(&u); ++ ++ /* ++ * The number of supported volumes is restricted by the eraseblock size ++ * and by the UBI_MAX_VOLUMES constant. ++ */ ++ vt_slots = leb_size / UBI_VTBL_RECORD_SIZE; ++ if (vt_slots > UBI_MAX_VOLUMES) ++ vt_slots = UBI_MAX_VOLUMES; ++ vol_tab_size_limit = vt_slots * UBI_VTBL_RECORD_SIZE; ++ ++ ptr = (uint8_t*) malloc(leb_size * sizeof(uint8_t)); ++ if (ptr == NULL) ++ goto err; ++ ++ memset(ptr, 0xff, leb_size); ++ memcpy(ptr, vol_tab, vol_tab_size_limit); ++ fp_leb = my_fmemopen(ptr, leb_size, "r"); ++ ++ rc = ubigen_create(&u, UBI_LAYOUT_VOLUME_ID, UBI_VID_DYNAMIC, ++ pdd->eb_size, DEFAULT_ERASE_COUNT, ++ 1, UBI_VERSION, pdd->vid_hdr_offset, ++ UBI_COMPAT_REJECT, leb_size * UBI_LAYOUT_VOLUME_EBS, ++ fp_leb, io->fp_out); ++ if (rc != 0) ++ goto err; ++ rc = ubigen_get_leb_total(u, &leb_total); ++ if (rc != 0) ++ goto err; ++ ++ long old_file_pos = ftell(fp_leb); ++ while(j < leb_total) { ++ rc = fseek(fp_leb, old_file_pos, SEEK_SET); ++ if (rc != 0) ++ goto err; ++ ++ cmp_peb->num = *ebs_written; ++ raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb, ++ raw_pebs); ++ if (raw_peb) { ++ rc = peb_write(io->fp_out, raw_peb); ++ } ++ else { ++ rc = ubigen_write_leb(u, NO_ERROR); ++ j++; ++ } ++ ++ if (rc != 0) ++ goto err; ++ (*ebs_written)++; ++ } ++ ++err: ++ free(ptr); ++ peb_free(&cmp_peb); ++ ubigen_destroy(&u); ++ fclose(fp_leb); ++ return rc; ++} ++ ++static int ++write_remaining_raw_ebs(pdd_data_t pdd, list_t raw_blocks, size_t *ebs_written, ++ FILE* fp_out) ++{ ++ int rc = 0; ++ uint32_t j, delta; ++ list_t ptr; ++ peb_t empty_eb, peb; ++ ++ /* create an empty 0xff EB (for padding) */ ++ rc = peb_new(0, pdd->eb_size, &empty_eb); ++ ++ foreach(peb, ptr, raw_blocks) { ++ if (peb->num < *ebs_written) { ++ continue; /* omit blocks which ++ are already passed */ ++ } ++ ++ if (peb->num < *ebs_written) { ++ err_msg("eb_num: %d\n", peb->num); ++ err_msg("Bug: This should never happen. %d %s", ++ __LINE__, __FILE__); ++ goto err; ++ } ++ ++ delta = peb->num - *ebs_written; ++ if (((delta + *ebs_written) * pdd->eb_size) > pdd->flash_size) { ++ err_msg("RAW block outside of flash_size."); ++ goto err; ++ } ++ for (j = 0; j < delta; j++) { ++ rc = peb_write(fp_out, empty_eb); ++ if (rc != 0) ++ goto err; ++ (*ebs_written)++; ++ } ++ rc = peb_write(fp_out, peb); ++ if (rc != 0) ++ goto err; ++ (*ebs_written)++; ++ } ++ ++err: ++ peb_free(&empty_eb); ++ return rc; ++} ++ ++static int ++init_vol_tab(struct ubi_vtbl_record **vol_tab, size_t *vol_tab_size) ++{ ++ uint32_t crc; ++ size_t i; ++ struct ubi_vtbl_record* res = NULL; ++ ++ *vol_tab_size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE; ++ ++ res = (struct ubi_vtbl_record*) calloc(1, *vol_tab_size); ++ if (vol_tab == NULL) { ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < UBI_MAX_VOLUMES; i++) { ++ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, ++ &(res[i]), UBI_VTBL_RECORD_SIZE_CRC); ++ res[i].crc = cpu_to_be32(crc); ++ } ++ ++ *vol_tab = res; ++ return 0; ++} ++ ++static int ++create_raw(io_t io) ++{ ++ int rc = 0; ++ size_t ebs_written = 0; /* eraseblocks written already... */ ++ size_t vol_tab_size; ++ list_t ptr; ++ ++ list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */ ++ list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */ ++ list_t raw_pebs = mk_empty(); /* list of raw eraseblocks */ ++ ++ struct ubi_vtbl_record *vol_tab = NULL; ++ pdd_data_t pdd = NULL; ++ ++ rc = init_vol_tab (&vol_tab, &vol_tab_size); ++ if (rc != 0) { ++ err_msg("Cannot initialize volume table."); ++ goto err; ++ } ++ ++ rc = read_pdd_data(io->fp_pdd, &pdd, ++ err_buf, ERR_BUF_SIZE); ++ if (rc != 0) { ++ err_msg("Cannot read necessary pdd_data: %s rc: %d", ++ err_buf, rc); ++ goto err; ++ } ++ ++ rc = read_pfi_headers(&pfi_raws, &pfi_ubis, io->fp_pfi, ++ err_buf, ERR_BUF_SIZE); ++ if (rc != 0) { ++ err_msg("Cannot read pfi header: %s rc: %d", ++ err_buf, rc); ++ goto err; ++ } ++ ++ pfi_raw_t pfi_raw; ++ foreach(pfi_raw, ptr, pfi_raws) { ++ rc = memorize_raw_eb(pfi_raw, pdd, &raw_pebs, ++ io); ++ if (rc != 0) { ++ err_msg("Cannot create raw_block in mem. rc: %d\n", ++ rc); ++ goto err; ++ } ++ } ++ ++ pfi_ubi_t pfi_ubi; ++ foreach(pfi_ubi, ptr, pfi_ubis) { ++ rc = convert_ubi_volume(pfi_ubi, pdd, raw_pebs, ++ vol_tab, &ebs_written, io); ++ if (rc != 0) { ++ err_msg("Cannot convert UBI volume. rc: %d\n", rc); ++ goto err; ++ } ++ } ++ ++ rc = write_ubi_volume_table(pdd, raw_pebs, vol_tab, vol_tab_size, ++ &ebs_written, io); ++ if (rc != 0) { ++ err_msg("Cannot write UBI volume table. rc: %d\n", rc); ++ goto err; ++ } ++ ++ rc = write_remaining_raw_ebs(pdd, raw_pebs, &ebs_written, io->fp_out); ++ if (rc != 0) ++ goto err; ++ ++ if (io->fp_out != stdout) ++ info_msg("Physical eraseblocks written: %8d\n", ebs_written); ++err: ++ free(vol_tab); ++ pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws); ++ pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis); ++ raw_pebs = remove_all((free_func_t)&peb_free, raw_pebs); ++ free_pdd_data(&pdd); ++ return rc; ++} ++ ++ ++/* ------------------------------------------------------------------------- */ ++static void ++open_io_handle(myargs *args, io_t io) ++{ ++ /* set PDD input */ ++ io->fp_pdd = fopen(args->f_in_pdd, "r"); ++ if (io->fp_pdd == NULL) { ++ err_sys("Cannot open: %s", args->f_in_pdd); ++ } ++ ++ /* set PFI input */ ++ io->fp_pfi = fopen(args->f_in_pfi, "r"); ++ if (io->fp_pfi == NULL) { ++ err_sys("Cannot open PFI input file: %s", args->f_in_pfi); ++ } ++ ++ /* set output prefix */ ++ if (strcmp(args->f_out,"") == 0) ++ io->fp_out = stdout; ++ else { ++ io->fp_out = fopen(args->f_out, "wb"); ++ if (io->fp_out == NULL) { ++ err_sys("Cannot open output file: %s", args->f_out); ++ } ++ } ++} ++ ++static void ++close_io_handle(io_t io) ++{ ++ if (fclose(io->fp_pdd) != 0) { ++ err_sys("Cannot close PDD file."); ++ } ++ if (fclose(io->fp_pfi) != 0) { ++ err_sys("Cannot close PFI file."); ++ } ++ if (io->fp_out != stdout) { ++ if (fclose(io->fp_out) != 0) { ++ err_sys("Cannot close output file."); ++ } ++ } ++ ++ io->fp_pdd = NULL; ++ io->fp_pfi = NULL; ++ io->fp_out = NULL; ++} ++ ++int ++main(int argc, char *argv[]) ++{ ++ int rc = 0; ++ ++ ubigen_init(); ++ init_crc32_table(crc32_table); ++ ++ struct io io = {NULL, NULL, NULL}; ++ myargs args = { ++ .action = ACT_RAW, ++ .verbose = 0, ++ ++ .f_in_pfi = "", ++ .f_in_pdd = "", ++ .f_out = "", ++ ++ /* arguments */ ++ .arg1 = NULL, ++ .options = NULL, ++ }; ++ ++ /* parse arguments */ ++ parse_opt(argc, argv, &args); ++ ++ if (strcmp(args.f_in_pfi, "") == 0) { ++ err_quit("No PFI input file specified!"); ++ } ++ ++ if (strcmp(args.f_in_pdd, "") == 0) { ++ err_quit("No PDD input file specified!"); ++ } ++ ++ open_io_handle(&args, &io); ++ ++ info_msg("[ Creating RAW..."); ++ rc = create_raw(&io); ++ if (rc != 0) { ++ err_msg("Creating RAW failed."); ++ goto err; ++ } ++ ++err: ++ close_io_handle(&io); ++ if (rc != 0) { ++ remove(args.f_out); ++ } ++ ++ return rc; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,264 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ * Frank Haverkamp ++ * ++ * Process a PFI (partial flash image) and write the data to the ++ * specified UBI volumes. This tool is intended to be used for system ++ * update using PFI files. ++ * ++ * 1.1 fixed output to stderr and stdout in logfile mode. ++ * 1.2 updated. ++ * 1.3 removed argp parsing to be able to use uClib. ++ * 1.4 Minor cleanups. ++ * 1.5 Forgot to delete raw block before updating it. ++ * 1.6 Migrated to new libubi. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#undef DEBUG ++#include "error.h" ++#include "config.h" ++ ++#define PROGRAM_VERSION "1.6" ++ ++static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" ++ "pfiflash - a tool for updating a controller with PFI files.\n"; ++ ++static const char *optionsstr = ++" Standard options:\n" ++" -c, --copyright Print copyright information.\n" ++" -l, --logfile= Write a logfile to .\n" ++" -v, --verbose Be verbose during program execution.\n" ++"\n" ++" Process options:\n" ++" -C, --complete Execute a complete system update. Updates both\n" ++" sides.\n" ++" -p, --pdd-update= Specify the pdd-update algorithm. is either\n" ++" 'keep', 'merge' or 'overwrite'.\n" ++" -r, --raw-flash= Flash the raw data. Use the specified mtd device.\n" ++" -s, --side= Select the side which shall be updated.\n" ++" -x, --compare Only compare on-flash and pfi data, print info if\n" ++" an update is neccessary and return appropriate\n" ++" error code.\n" ++"\n" ++" -?, --help Give this help list\n" ++" --usage Give a short usage message\n" ++" -V, --version Print program version\n"; ++ ++static const char *usage = ++"Usage: pfiflash [-cvC?V] [-l ] [-p ] [-r ] [-s ]\n" ++" [--copyright] [--logfile=] [--verbose] [--complete]\n" ++" [--pdd-update=] [--raw-flash=] [--side=]\n" ++" [--compare] [--help] [--usage] [--version] [pfifile]\n"; ++ ++static const char copyright [] __attribute__((unused)) = ++ "Copyright IBM Corp 2006"; ++ ++struct option long_options[] = { ++ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, ++ { .name = "logfile", .has_arg = 1, .flag = NULL, .val = 'l' }, ++ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, ++ { .name = "complete", .has_arg = 0, .flag = NULL, .val = 'C' }, ++ { .name = "pdd-update", .has_arg = 1, .flag = NULL, .val = 'p' }, ++ { .name = "raw-flash", .has_arg = 1, .flag = NULL, .val = 'r' }, ++ { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' }, ++ { .name = "compare", .has_arg = 0, .flag = NULL, .val = 'x' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, ++ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ { NULL, 0, NULL, 0} ++}; ++ ++typedef struct myargs { ++ int verbose; ++ const char *logfile; ++ const char *raw_dev; ++ ++ pdd_handling_t pdd_handling; ++ int seqnum; ++ int compare; ++ int complete; ++ ++ FILE* fp_in; ++ ++ /* special stuff needed to get additional arguments */ ++ char *arg1; ++ char **options; /* [STRING...] */ ++} myargs; ++ ++static pdd_handling_t ++get_pdd_handling(const char* str) ++{ ++ if (strcmp(str, "keep") == 0) { ++ return PDD_KEEP; ++ } ++ if (strcmp(str, "merge") == 0) { ++ return PDD_MERGE; ++ } ++ if (strcmp(str, "overwrite") == 0) { ++ return PDD_OVERWRITE; ++ } ++ ++ return -1; ++} ++ ++static int ++get_update_seqnum(const char* str) ++{ ++ uint32_t i = strtoul(str, NULL, 0); ++ ++ if ((i != 0) && (i != 1)) { ++ return -1; ++ } ++ ++ return i; ++} ++ ++ ++static int ++parse_opt(int argc, char **argv, myargs *args) ++{ ++ while (1) { ++ int key; ++ ++ key = getopt_long(argc, argv, "cl:vCp:r:s:x?V", ++ long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ /* standard options */ ++ case 'c': ++ err_msg("%s\n", copyright); ++ exit(0); ++ break; ++ case 'v': ++ args->verbose = 1; ++ break; ++ case 'l': ++ args->logfile = optarg; ++ break; ++ /* process options */ ++ case 'C': ++ args->complete = 1; ++ break; ++ case 'p': ++ args->pdd_handling = get_pdd_handling(optarg); ++ if ((int)args->pdd_handling < 0) { ++ err_quit("Unknown PDD handling: %s.\n" ++ "Please use either " ++ "'keep', 'merge' or" ++ "'overwrite'.\n'"); ++ } ++ break; ++ case 's': ++ args->seqnum = get_update_seqnum(optarg); ++ if (args->seqnum < 0) { ++ err_quit("Unsupported side: %s.\n" ++ "Supported sides are '0' " ++ "and '1'\n", optarg); ++ } ++ break; ++ case 'x': ++ args->compare = 1; ++ break; ++ case 'r': ++ args->raw_dev = optarg; ++ break; ++ case '?': /* help */ ++ err_msg("Usage: pfiflash [OPTION...] [pfifile]"); ++ err_msg("%s", doc); ++ err_msg("%s", optionsstr); ++ err_msg("\nReport bugs to %s\n", ++ PACKAGE_BUGREPORT); ++ exit(0); ++ break; ++ case 'V': ++ err_msg("%s", PROGRAM_VERSION); ++ exit(0); ++ break; ++ default: ++ err_msg("%s", usage); ++ exit(-1); ++ ++ } ++ } ++ ++ if (optind < argc) { ++ args->fp_in = fopen(argv[optind++], "r"); ++ if ((args->fp_in) == NULL) { ++ err_sys("Cannot open PFI file %s for input", ++ argv[optind]); ++ } ++ } ++ ++ return 0; ++} ++ ++int main (int argc, char** argv) ++{ ++ int rc = 0; ++ char err_buf[PFIFLASH_MAX_ERR_BUF_SIZE]; ++ memset(err_buf, '\0', PFIFLASH_MAX_ERR_BUF_SIZE); ++ ++ myargs args = { ++ .verbose = 0, ++ .seqnum = -1, ++ .compare = 0, ++ .complete = 0, ++ .logfile = NULL, /* "/tmp/pfiflash.log", */ ++ .pdd_handling = PDD_KEEP, ++ .fp_in = stdin, ++ .raw_dev = NULL, ++ }; ++ ++ parse_opt(argc, argv, &args); ++ error_initlog(args.logfile); ++ ++ if (!args.fp_in) { ++ rc = -1; ++ snprintf(err_buf, PFIFLASH_MAX_ERR_BUF_SIZE, ++ "No PFI input file specified!\n"); ++ goto err; ++ } ++ ++ rc = pfiflash_with_options(args.fp_in, args.complete, args.seqnum, ++ args.compare, args.pdd_handling, args.raw_dev, err_buf, ++ PFIFLASH_MAX_ERR_BUF_SIZE); ++ if (rc < 0) { ++ goto err_fp; ++ } ++ ++ err_fp: ++ if (args.fp_in != stdin) ++ fclose(args.fp_in); ++ err: ++ if (rc != 0) ++ err_msg("pfiflash: %s\nrc: %d\n", err_buf, rc); ++ return rc; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,76 @@ ++#ifndef __PFIFLASH_H__ ++#define __PFIFLASH_H__ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/** ++ * ++ * @file pfi.h ++ * ++ * @author Oliver Lohmann ++ * ++ * @brief The pfiflash library offers an interface for using the ++ * pfiflash * utility. ++ */ ++ ++#include /* FILE */ ++ ++#define PFIFLASH_MAX_ERR_BUF_SIZE 1024 ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++typedef enum pdd_handling_t ++{ ++ PDD_KEEP = 0, ++ PDD_MERGE, ++ PDD_OVERWRITE, ++ PDD_HANDLING_NUM, /* always the last item */ ++} pdd_handling_t; /**< Possible PDD handle algorithms. */ ++ ++/** ++ * @brief Flashes a PFI file to UBI Device 0. ++ * @param complete [0|1] Do a complete system update. ++ * @param seqnum Index in a redundant group. ++ * @param compare [0|1] Compare contents. ++ * @param pdd_handling The PDD handling algorithm. ++ * @param rawdev Device to use for raw flashing ++ * @param err_buf An error buffer. ++ * @param err_buf_size Size of the error buffer. ++ */ ++int pfiflash_with_options(FILE* pfi, int complete, int seqnum, int compare, ++ pdd_handling_t pdd_handling, const char* rawdev, ++ char *err_buf, size_t err_buf_size); ++ ++/** ++ * @brief Flashes a PFI file to UBI Device 0. ++ * @param complete [0|1] Do a complete system update. ++ * @param seqnum Index in a redundant group. ++ * @param pdd_handling The PDD handling algorithm. ++ * @param err_buf An error buffer. ++ * @param err_buf_size Size of the error buffer. ++ */ ++int pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling, ++ char *err_buf, size_t err_buf_size); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* __PFIFLASH_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash_error.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash_error.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash_error.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/pfiflash_error.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,75 @@ ++#ifndef __PFIFLASH_ERROR_H__ ++#define __PFIFLASH_ERROR_H__ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * Author: Drake Dowsett ++ * Contact: Andreas Arnez ++ */ ++ ++enum pfiflash_err { ++ PFIFLASH_ERR_EOF = 1, ++ PFIFLASH_ERR_FIO, ++ PFIFLASH_ERR_UBI_OPEN, ++ PFIFLASH_ERR_UBI_CLOSE, ++ PFIFLASH_ERR_UBI_MKVOL, ++ PFIFLASH_ERR_UBI_RMVOL, ++ PFIFLASH_ERR_UBI_VOL_UPDATE, ++ PFIFLASH_ERR_UBI_VOL_FOPEN, ++ PFIFLASH_ERR_UBI_UNKNOWN, ++ PFIFLASH_ERR_UBI_VID_OOB, ++ PFIFLASH_ERR_BOOTENV_CREATE, ++ PFIFLASH_ERR_BOOTENV_READ, ++ PFIFLASH_ERR_BOOTENV_SIZE, ++ PFIFLASH_ERR_BOOTENV_WRITE, ++ PFIFLASH_ERR_PDD_UNKNOWN, ++ PFIFLASH_ERR_MTD_OPEN, ++ PFIFLASH_ERR_MTD_CLOSE, ++ PFIFLASH_ERR_CRC_CHECK, ++ PFIFLASH_ERR_MTD_ERASE, ++ PFIFLASH_ERR_COMPARE, ++ PFIFLASH_CMP_DIFF ++}; ++ ++const char *const PFIFLASH_ERRSTR[] = { ++ "", ++ "unexpected EOF", ++ "file I/O error", ++ "couldn't open UBI", ++ "couldn't close UBI", ++ "couldn't make UBI volume %d", ++ "couldn't remove UBI volume %d", ++ "couldn't update UBI volume %d", ++ "couldn't open UBI volume %d", ++ "unknown UBI operation", ++ "PFI data contains out of bounds UBI id %d", ++ "couldn't create bootenv%s", ++ "couldn't read bootenv", ++ "couldn't resize bootenv", ++ "couldn't write bootenv on ubi%d_%d", ++ "unknown PDD handling algorithm", ++ "couldn't open MTD device %s", ++ "couldn't close MTD device %s", ++ "CRC check failed: given=0x%08x, calculated=0x%08x", ++ "couldn't erase raw mtd region", ++ "couldn't compare volumes", ++ "on-flash data differ from pfi data, update is neccessary" ++}; ++ ++#endif /* __PFIFLASH_ERROR_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/reader.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/reader.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/reader.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/reader.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,482 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ * ++ * Read in PFI (partial flash image) data and store it into internal ++ * data structures for further processing. Take also care about ++ * special handling if the data contains PDD (platform description ++ * data/boot-parameters). ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "bootenv.h" ++#include "reader.h" ++ ++#define __unused __attribute__((unused)) ++ ++/* @FIXME hard coded offsets right now - get them from Artem? */ ++#define NAND2048_DEFAULT_VID_HDR_OFF 1984 ++#define NAND512_DEFAULT_VID_HDR_OFF 448 ++#define NOR_DEFAULT_VID_HDR_OFF 64 ++ ++#define EBUF_PFI(fmt...) \ ++ do { int i = snprintf(err_buf, err_buf_size, "%s\n", label); \ ++ snprintf(err_buf + i, err_buf_size - i, fmt); \ ++ } while (0) ++ ++#define EBUF(fmt...) \ ++ do { snprintf(err_buf, err_buf_size, fmt); } while (0) ++ ++ ++int ++read_pdd_data(FILE* fp_pdd, pdd_data_t* pdd_data, ++ char* err_buf, size_t err_buf_size) ++{ ++ int rc = 0; ++ bootenv_t pdd = NULL; ++ pdd_data_t res = NULL; ++ const char* value; ++ ++ res = (pdd_data_t) malloc(sizeof(struct pdd_data)); ++ if (!res) { ++ rc = -ENOMEM; ++ goto err; ++ } ++ rc = bootenv_create(&pdd); ++ if (rc != 0) { ++ goto err; ++ } ++ rc = bootenv_read_txt(fp_pdd, pdd); ++ if (rc != 0) { ++ goto err; ++ } ++ rc = bootenv_get(pdd, "flash_type", &value); ++ if (rc != 0) { ++ goto err; ++ } ++ ++ if (strcmp(value, "NAND") == 0) { ++ ++ rc = bootenv_get_num(pdd, "flash_page_size", ++ &(res->flash_page_size)); ++ if (rc != 0) { ++ EBUF("Cannot read 'flash_page_size' from pdd."); ++ goto err; ++ } ++ res->flash_type = NAND_FLASH; ++ ++ switch (res->flash_page_size) { ++ case 512: ++ res->vid_hdr_offset = NAND512_DEFAULT_VID_HDR_OFF; ++ break; ++ case 2048: ++ res->vid_hdr_offset = NAND2048_DEFAULT_VID_HDR_OFF; ++ break; ++ default: ++ EBUF("Unsupported 'flash_page_size' %d.", ++ res->flash_page_size); ++ goto err; ++ } ++ } ++ else if (strcmp(value, "NOR") == 0){ ++ res->flash_type = NOR_FLASH; ++ res->vid_hdr_offset = NOR_DEFAULT_VID_HDR_OFF; ++ } ++ else { ++ snprintf(err_buf, err_buf_size, ++ "Unkown flash type: %s", value); ++ goto err; ++ } ++ ++ rc = bootenv_get_num(pdd, "flash_eraseblock_size", ++ &(res->eb_size)); ++ if (rc != 0) { ++ EBUF("Cannot read 'flash_eraseblock_size' from pdd."); ++ goto err; ++ } ++ ++ rc = bootenv_get_num(pdd, "flash_size", ++ &(res->flash_size)); ++ if (rc != 0) { ++ EBUF("Cannot read 'flash_size' from pdd."); ++ goto err; ++ } ++ ++ goto out; ++ err: ++ if (res) { ++ free(res); ++ res = NULL; ++ } ++ out: ++ bootenv_destroy(&pdd); ++ *pdd_data = res; ++ return rc; ++} ++ ++int ++read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_raw_t* pfi_raw, ++ const char* label, char* err_buf, size_t err_buf_size) ++{ ++ int rc = 0; ++ char tmp_str[PFI_KEYWORD_LEN]; ++ bootenv_list_t raw_start_list = NULL; ++ pfi_raw_t res; ++ size_t size; ++ ++ res = (pfi_raw_t) malloc(sizeof(struct pfi_raw)); ++ if (!res) ++ return -ENOMEM; ++ ++ rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size)); ++ if (rc != 0) { ++ EBUF_PFI("Cannot read 'size' from PFI."); ++ goto err; ++ } ++ ++ rc = pfi_header_getnumber(pfi_hd, "crc", &(res->crc)); ++ if (rc != 0) { ++ EBUF_PFI("Cannot read 'crc' from PFI."); ++ goto err; ++ } ++ ++ rc = pfi_header_getstring(pfi_hd, "raw_starts", ++ tmp_str, PFI_KEYWORD_LEN); ++ if (rc != 0) { ++ EBUF_PFI("Cannot read 'raw_starts' from PFI."); ++ goto err; ++ } ++ ++ rc = bootenv_list_create(&raw_start_list); ++ if (rc != 0) { ++ goto err; ++ } ++ ++ rc = bootenv_list_import(raw_start_list, tmp_str); ++ if (rc != 0) { ++ EBUF_PFI("Cannot translate PFI value: %s", tmp_str); ++ goto err; ++ } ++ ++ rc = bootenv_list_to_num_vector(raw_start_list, ++ &size, &(res->starts)); ++ res->starts_size = size; ++ ++ if (rc != 0) { ++ EBUF_PFI("Cannot create numeric value array: %s", tmp_str); ++ goto err; ++ } ++ ++ goto out; ++ ++ err: ++ if (res) { ++ free(res); ++ res = NULL; ++ } ++ out: ++ bootenv_list_destroy(&raw_start_list); ++ *pfi_raw = res; ++ return rc; ++} ++ ++int ++read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_ubi_t* pfi_ubi, ++ const char *label, char* err_buf, size_t err_buf_size) ++{ ++ int rc = 0; ++ const char** tmp_names = NULL; ++ char tmp_str[PFI_KEYWORD_LEN]; ++ bootenv_list_t ubi_id_list = NULL; ++ bootenv_list_t ubi_name_list = NULL; ++ pfi_ubi_t res; ++ uint32_t i; ++ size_t size; ++ ++ res = (pfi_ubi_t) calloc(1, sizeof(struct pfi_ubi)); ++ if (!res) ++ return -ENOMEM; ++ ++ rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size)); ++ if (rc != 0) { ++ EBUF_PFI("Cannot read 'size' from PFI."); ++ goto err; ++ } ++ ++ rc = pfi_header_getnumber(pfi_hd, "crc", &(res->crc)); ++ if (rc != 0) { ++ EBUF_PFI("Cannot read 'crc' from PFI."); ++ goto err; ++ } ++ ++ rc = pfi_header_getstring(pfi_hd, "ubi_ids", tmp_str, PFI_KEYWORD_LEN); ++ if (rc != 0) { ++ EBUF_PFI("Cannot read 'ubi_ids' from PFI."); ++ goto err; ++ } ++ ++ rc = bootenv_list_create(&ubi_id_list); ++ if (rc != 0) { ++ goto err; ++ } ++ rc = bootenv_list_create(&ubi_name_list); ++ if (rc != 0) { ++ goto err; ++ } ++ ++ rc = bootenv_list_import(ubi_id_list, tmp_str); ++ if (rc != 0) { ++ EBUF_PFI("Cannot translate PFI value: %s", tmp_str); ++ goto err; ++ } ++ ++ rc = bootenv_list_to_num_vector(ubi_id_list, &size, ++ &(res->ids)); ++ res->ids_size = size; ++ if (rc != 0) { ++ EBUF_PFI("Cannot create numeric value array: %s", tmp_str); ++ goto err; ++ } ++ ++ if (res->ids_size == 0) { ++ rc = -1; ++ EBUF_PFI("Sanity check failed: No ubi_ids specified."); ++ goto err; ++ } ++ ++ rc = pfi_header_getstring(pfi_hd, "ubi_type", ++ tmp_str, PFI_KEYWORD_LEN); ++ if (rc != 0) { ++ EBUF_PFI("Cannot read 'ubi_type' from PFI."); ++ goto err; ++ } ++ if (strcmp(tmp_str, "static") == 0) ++ res->type = pfi_ubi_static; ++ else if (strcmp(tmp_str, "dynamic") == 0) ++ res->type = pfi_ubi_dynamic; ++ else { ++ EBUF_PFI("Unknown ubi_type in PFI."); ++ goto err; ++ } ++ ++ rc = pfi_header_getnumber(pfi_hd, "ubi_alignment", &(res->alignment)); ++ if (rc != 0) { ++ EBUF_PFI("Cannot read 'ubi_alignment' from PFI."); ++ goto err; ++ } ++ ++ rc = pfi_header_getnumber(pfi_hd, "ubi_size", &(res->size)); ++ if (rc != 0) { ++ EBUF_PFI("Cannot read 'ubi_size' from PFI."); ++ goto err; ++ } ++ ++ rc = pfi_header_getstring(pfi_hd, "ubi_names", ++ tmp_str, PFI_KEYWORD_LEN); ++ if (rc != 0) { ++ EBUF_PFI("Cannot read 'ubi_names' from PFI."); ++ goto err; ++ } ++ ++ rc = bootenv_list_import(ubi_name_list, tmp_str); ++ if (rc != 0) { ++ EBUF_PFI("Cannot translate PFI value: %s", tmp_str); ++ goto err; ++ } ++ rc = bootenv_list_to_vector(ubi_name_list, &size, ++ &(tmp_names)); ++ res->names_size = size; ++ if (rc != 0) { ++ EBUF_PFI("Cannot create string array: %s", tmp_str); ++ goto err; ++ } ++ ++ if (res->names_size != res->ids_size) { ++ EBUF_PFI("Sanity check failed: ubi_ids list does not match " ++ "sizeof ubi_names list."); ++ rc = -1; ++ } ++ ++ /* copy tmp_names to own structure */ ++ res->names = (char**) calloc(1, res->names_size * sizeof (char*)); ++ if (res->names == NULL) ++ goto err; ++ ++ for (i = 0; i < res->names_size; i++) { ++ res->names[i] = calloc(PFI_UBI_VOL_NAME_LEN + 1, sizeof(char)); ++ if (res->names[i] == NULL) ++ goto err; ++ strncpy(res->names[i], tmp_names[i], PFI_UBI_VOL_NAME_LEN + 1); ++ } ++ ++ goto out; ++ ++ err: ++ if (res) { ++ if (res->names) { ++ for (i = 0; i < res->names_size; i++) { ++ if (res->names[i]) { ++ free(res->names[i]); ++ } ++ } ++ free(res->names); ++ } ++ if (res->ids) { ++ free(res->ids); ++ } ++ free(res); ++ res = NULL; ++ } ++ ++ out: ++ bootenv_list_destroy(&ubi_id_list); ++ bootenv_list_destroy(&ubi_name_list); ++ if (tmp_names != NULL) ++ free(tmp_names); ++ *pfi_ubi = res; ++ return rc; ++} ++ ++ ++int ++free_pdd_data(pdd_data_t* pdd_data) ++{ ++ if (*pdd_data) { ++ free(*pdd_data); ++ } ++ *pdd_data = NULL; ++ ++ return 0; ++} ++ ++int ++free_pfi_raw(pfi_raw_t* pfi_raw) ++{ ++ pfi_raw_t tmp = *pfi_raw; ++ if (tmp) { ++ if (tmp->starts) ++ free(tmp->starts); ++ free(tmp); ++ } ++ *pfi_raw = NULL; ++ ++ return 0; ++} ++ ++int ++free_pfi_ubi(pfi_ubi_t* pfi_ubi) ++{ ++ size_t i; ++ pfi_ubi_t tmp = *pfi_ubi; ++ if (tmp) { ++ if (tmp->ids) ++ free(tmp->ids); ++ if (tmp->names) { ++ for (i = 0; i < tmp->names_size; i++) { ++ if (tmp->names[i]) { ++ free(tmp->names[i]); ++ } ++ } ++ free(tmp->names); ++ } ++ free(tmp); ++ } ++ *pfi_ubi = NULL; ++ ++ return 0; ++} ++ ++ ++int ++read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi, ++ char* err_buf, size_t err_buf_size) ++{ ++ int rc = 0; ++ char mode[PFI_KEYWORD_LEN]; ++ char label[PFI_LABEL_LEN]; ++ ++ *pfi_raws = mk_empty(); pfi_raw_t raw = NULL; ++ *pfi_ubis = mk_empty(); pfi_ubi_t ubi = NULL; ++ pfi_header pfi_header = NULL; ++ ++ /* read all headers from PFI and store them in lists */ ++ rc = pfi_header_init(&pfi_header); ++ if (rc != 0) { ++ EBUF("Cannot initialize pfi header."); ++ goto err; ++ } ++ while ((rc == 0) && !feof(fp_pfi)) { ++ rc = pfi_header_read(fp_pfi, pfi_header); ++ if (rc != 0) { ++ if (rc == PFI_DATA_START) { ++ rc = 0; ++ break; /* data section starts, ++ all headers read */ ++ } ++ else { ++ goto err; ++ } ++ } ++ rc = pfi_header_getstring(pfi_header, "label", label, ++ PFI_LABEL_LEN); ++ if (rc != 0) { ++ EBUF("Cannot read 'label' from PFI."); ++ goto err; ++ } ++ rc = pfi_header_getstring(pfi_header, "mode", mode, ++ PFI_KEYWORD_LEN); ++ if (rc != 0) { ++ EBUF("Cannot read 'mode' from PFI."); ++ goto err; ++ } ++ if (strcmp(mode, "ubi") == 0) { ++ rc = read_pfi_ubi(pfi_header, fp_pfi, &ubi, label, ++ err_buf, err_buf_size); ++ if (rc != 0) { ++ goto err; ++ } ++ *pfi_ubis = append_elem(ubi, *pfi_ubis); ++ } ++ else if (strcmp(mode, "raw") == 0) { ++ rc = read_pfi_raw(pfi_header, fp_pfi, &raw, label, ++ err_buf, err_buf_size); ++ if (rc != 0) { ++ goto err; ++ } ++ *pfi_raws = append_elem(raw, *pfi_raws); ++ } ++ else { ++ EBUF("Recvieved unknown mode from PFI: %s", mode); ++ goto err; ++ } ++ } ++ goto out; ++ ++ err: ++ *pfi_raws = remove_all((free_func_t)&free_pfi_raw, *pfi_raws); ++ *pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, *pfi_ubis); ++ out: ++ pfi_header_destroy(&pfi_header); ++ return rc; ++ ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/reader.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/reader.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/reader.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/reader.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,87 @@ ++#ifndef __READER_H__ ++#define __READER_H__ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ * ++ * Read Platform Description Data (PDD). ++ */ ++ ++#include ++#include ++ ++#include "pfi.h" ++#include "bootenv.h" ++#include "list.h" ++ ++typedef enum flash_type_t { ++ NAND_FLASH = 0, ++ NOR_FLASH, ++} flash_type_t; ++ ++typedef struct pdd_data *pdd_data_t; ++typedef struct pfi_raw *pfi_raw_t; ++typedef struct pfi_ubi *pfi_ubi_t; ++ ++struct pdd_data { ++ uint32_t flash_size; ++ uint32_t flash_page_size; ++ uint32_t eb_size; ++ uint32_t vid_hdr_offset; ++ flash_type_t flash_type; ++}; ++ ++struct pfi_raw { ++ uint32_t data_size; ++ uint32_t *starts; ++ uint32_t starts_size; ++ uint32_t crc; ++}; ++ ++struct pfi_ubi { ++ uint32_t data_size; ++ uint32_t alignment; ++ uint32_t *ids; ++ uint32_t ids_size; ++ char **names; ++ uint32_t names_size; ++ uint32_t size; ++ enum { pfi_ubi_dynamic, pfi_ubi_static } type; ++ int curr_seqnum; /* specifies the seqnum taken in an update, ++ default: 0 (used by pfiflash, ubimirror) */ ++ uint32_t crc; ++}; ++ ++int read_pdd_data(FILE* fp_pdd, pdd_data_t *pdd_data, ++ char *err_buf, size_t err_buf_size); ++int read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi, pfi_raw_t *pfi_raw, ++ const char *label, char *err_buf, size_t err_buf_size); ++int read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi, pfi_ubi_t *pfi_ubi, ++ const char *label, char *err_buf, size_t err_buf_size); ++ ++/** ++ * @brief Reads all pfi headers into list structures, separated by ++ * RAW and UBI sections. ++ */ ++int read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi, ++ char* err_buf, size_t err_buf_size); ++int free_pdd_data(pdd_data_t *pdd_data); ++int free_pfi_raw(pfi_raw_t *raw_pfi); ++int free_pfi_ubi(pfi_ubi_t *pfi_ubi); ++ ++#endif /* __READER_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/ubigen.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/ubigen.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/ubigen.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/ubigen.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,359 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ * ++ * Tool to add UBI headers to binary images. ++ * ++ * 1.0 Initial version ++ * 1.1 Different CRC32 start value ++ * 1.2 Removed argp because we want to use uClibc. ++ * 1.3 Minor cleanups ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ubigen.h" ++#include "config.h" ++ ++#define PROGRAM_VERSION "1.3" ++ ++typedef enum action_t { ++ ACT_NORMAL = 0x00000001, ++ ACT_BROKEN_UPDATE = 0x00000002, ++} action_t; ++ ++static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" ++ "ubigen - a tool for adding UBI information to a binary input file.\n"; ++ ++static const char *optionsstr = ++" Common settings:\n" ++" -c, --copyright Print copyright information.\n" ++" -d, --debug\n" ++" -v, --verbose Print more progress information.\n" ++"\n" ++" UBI Settings:\n" ++" -A, --alignment= Set the alignment size to (default 1).\n" ++" Values can be specified as bytes, 'ki' or 'Mi'.\n" ++" -B, --blocksize= Set the eraseblock size to (default 128\n" ++" KiB).\n" ++" Values can be specified as bytes, 'ki' or 'Mi'.\n" ++" -E, --erasecount= Set the erase count to (default 0)\n" ++" -I, --id= The UBI volume id.\n" ++" -O, --offset= Offset from start of an erase block to the UBI\n" ++" volume header.\n" ++" -T, --type= The UBI volume type:\n" ++" 1 = dynamic, 2 = static\n" ++" -X, --setver= Set UBI version number to (default 1)\n" ++"\n" ++" Input/Output:\n" ++" -i, --infile= Read input from file.\n" ++" -o, --outfile= Write output to file (default is stdout).\n" ++"\n" ++" Special options:\n" ++" -U, --broken-update= Create an ubi image which simulates a broken\n" ++" update.\n" ++" specifies the logical eraseblock number to\n" ++" update.\n" ++"\n" ++" -?, --help Give this help list\n" ++" --usage Give a short usage message\n" ++" -V, --version Print program version\n"; ++ ++static const char *usage = ++"Usage: ubigen [-cdv?V] [-A ] [-B ] [-E ] [-I ]\n" ++" [-O ] [-T ] [-X ] [-i ] [-o ]\n" ++" [-U ] [--copyright] [--debug] [--verbose] [--alignment=]\n" ++" [--blocksize=] [--erasecount=] [--id=]\n" ++" [--offset=] [--type=] [--setver=]\n" ++" [--infile=] [--outfile=]\n" ++" [--broken-update=] [--help] [--usage] [--version]\n"; ++ ++struct option long_options[] = { ++ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, ++ { .name = "debug", .has_arg = 0, .flag = NULL, .val = 'd' }, ++ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, ++ { .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'A' }, ++ { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'B' }, ++ { .name = "erasecount", .has_arg = 1, .flag = NULL, .val = 'E' }, ++ { .name = "id", .has_arg = 1, .flag = NULL, .val = 'I' }, ++ { .name = "offset", .has_arg = 1, .flag = NULL, .val = 'O' }, ++ { .name = "type", .has_arg = 1, .flag = NULL, .val = 'T' }, ++ { .name = "setver", .has_arg = 1, .flag = NULL, .val = 'X' }, ++ { .name = "infile", .has_arg = 1, .flag = NULL, .val = 'i' }, ++ { .name = "outfile", .has_arg = 1, .flag = NULL, .val = 'o' }, ++ { .name = "broken-update", .has_arg = 1, .flag = NULL, .val = 'U' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, ++ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ { NULL, 0, NULL, 0} ++}; ++ ++static const char copyright [] __attribute__((unused)) = ++ "Copyright IBM Corp 2006"; ++ ++#define CHECK_ENDP(option, endp) do { \ ++ if (*endp) { \ ++ fprintf(stderr, \ ++ "Parse error option \'%s\'. " \ ++ "No correct numeric value.\n" \ ++ , option); \ ++ exit(EXIT_FAILURE); \ ++ } \ ++} while(0) ++ ++typedef struct myargs { ++ /* common settings */ ++ action_t action; ++ int verbose; ++ ++ int32_t id; ++ uint8_t type; ++ uint32_t eb_size; ++ uint64_t ec; ++ uint8_t version; ++ uint32_t hdr_offset; ++ uint32_t update_block; ++ uint32_t alignment; ++ ++ FILE* fp_in; ++ FILE* fp_out; ++ ++ /* special stuff needed to get additional arguments */ ++ char *arg1; ++ char **options; /* [STRING...] */ ++} myargs; ++ ++ ++static int ustrtoul(const char *cp, char **endp, unsigned int base) ++{ ++ unsigned long result = strtoul(cp, endp, base); ++ ++ switch (**endp) { ++ case 'G': ++ result *= 1024; ++ case 'M': ++ result *= 1024; ++ case 'k': ++ case 'K': ++ result *= 1024; ++ /* "Ki", "ki", "Mi" or "Gi" are to be used. */ ++ if ((*endp)[1] == 'i') ++ (*endp) += 2; ++ } ++ return result; ++} ++ ++static int ++parse_opt(int argc, char **argv, myargs *args) ++{ ++ int err = 0; ++ char* endp; ++ ++ while (1) { ++ int key; ++ ++ key = getopt_long(argc, argv, "cdvA:B:E:I:O:T:X:i:o:U:?V", ++ long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 'c': ++ fprintf(stderr, "%s\n", copyright); ++ exit(0); ++ break; ++ case 'o': /* output */ ++ args->fp_out = fopen(optarg, "wb"); ++ if ((args->fp_out) == NULL) { ++ fprintf(stderr, "Cannot open file %s " ++ "for output\n", optarg); ++ exit(1); ++ } ++ break; ++ case 'i': /* input */ ++ args->fp_in = fopen(optarg, "rb"); ++ if ((args->fp_in) == NULL) { ++ fprintf(stderr, "Cannot open file %s " ++ "for input\n", optarg); ++ exit(1); ++ } ++ break; ++ case 'v': /* verbose */ ++ args->verbose = 1; ++ break; ++ ++ case 'B': /* eb_size */ ++ args->eb_size = ++ (uint32_t)ustrtoul(optarg, &endp, 0); ++ CHECK_ENDP("B", endp); ++ break; ++ case 'E': /* erasecount */ ++ args->ec = (uint64_t)strtoul(optarg, &endp, 0); ++ CHECK_ENDP("E", endp); ++ break; ++ case 'I': /* id */ ++ args->id = (uint16_t)strtoul(optarg, &endp, 0); ++ CHECK_ENDP("I", endp); ++ break; ++ case 'T': /* type */ ++ args->type = ++ (uint16_t)strtoul(optarg, &endp, 0); ++ CHECK_ENDP("T", endp); ++ break; ++ case 'X': /* versionnr */ ++ args->version = ++ (uint8_t)strtoul(optarg, &endp, 0); ++ CHECK_ENDP("X", endp); ++ break; ++ case 'O': /* offset for volume hdr */ ++ args->hdr_offset = ++ (uint32_t) strtoul(optarg, &endp, 0); ++ CHECK_ENDP("O", endp); ++ break; ++ ++ case 'U': /* broken update */ ++ args->action = ACT_BROKEN_UPDATE; ++ args->update_block = ++ (uint32_t) strtoul(optarg, &endp, 0); ++ CHECK_ENDP("U", endp); ++ break; ++ ++ case '?': /* help */ ++ fprintf(stderr, "Usage: ubigen [OPTION...]\n"); ++ fprintf(stderr, "%s", doc); ++ fprintf(stderr, "%s", optionsstr); ++ fprintf(stderr, "\nReport bugs to %s\n", ++ PACKAGE_BUGREPORT); ++ exit(0); ++ break; ++ ++ case 'V': ++ fprintf(stderr, "%s\n", PROGRAM_VERSION); ++ exit(0); ++ break; ++ ++ default: ++ fprintf(stderr, "%s", usage); ++ exit(-1); ++ } ++ } ++ ++ if (optind < argc) { ++ if (!args->fp_in) { ++ args->fp_in = fopen(argv[optind++], "rb"); ++ if ((args->fp_in) == NULL) { ++ fprintf(stderr, "Cannot open file %s for " ++ "input\n", argv[optind]); ++ exit(1); ++ } ++ } ++ } ++ if (args->id < 0) { ++ err = 1; ++ fprintf(stderr, ++ "Please specify an UBI Volume ID.\n"); ++ } ++ if (args->type == 0) { ++ err = 1; ++ fprintf(stderr, ++ "Please specify an UBI Volume type.\n"); ++ } ++ if (err) { ++ fprintf(stderr, "%s", usage); ++ exit(1); ++ } ++ ++ return 0; ++} ++ ++ ++int ++main(int argc, char **argv) ++{ ++ int rc = 0; ++ ubi_info_t u; ++ struct stat file_info; ++ off_t input_len = 0; /* only used in static volumes */ ++ ++ myargs args = { ++ .action = ACT_NORMAL, ++ .verbose = 0, ++ ++ .id = -1, ++ .type = 0, ++ .eb_size = 0, ++ .update_block = 0, ++ .ec = 0, ++ .version = 0, ++ .hdr_offset = (DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE), ++ .alignment = 1, ++ ++ .fp_in = NULL, ++ .fp_out = stdout, ++ /* arguments */ ++ .arg1 = NULL, ++ .options = NULL, ++ }; ++ ++ ubigen_init(); /* Init CRC32 table in ubigen */ ++ ++ /* parse arguments */ ++ parse_opt(argc, argv, &args); ++ ++ if (fstat(fileno(args.fp_in), &file_info) != 0) { ++ fprintf(stderr, "Cannot fetch file size " ++ "from input file.\n"); ++ } ++ input_len = file_info.st_size; ++ ++ rc = ubigen_create(&u, (uint32_t)args.id, args.type, ++ args.eb_size, args.ec, args.alignment, ++ args.version, args.hdr_offset, 0 ,input_len, ++ args.fp_in, args.fp_out); ++ ++ if (rc != 0) { ++ fprintf(stderr, "Cannot create UBI info handler rc: %d\n", rc); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (!args.fp_in || !args.fp_out) { ++ fprintf(stderr, "Input/Output error.\n"); ++ exit(EXIT_FAILURE); ++ ++ } ++ ++ if (args.action & ACT_NORMAL) { ++ rc = ubigen_write_complete(u); ++ } ++ else if (args.action & ACT_BROKEN_UPDATE) { ++ rc = ubigen_write_broken_update(u, args.update_block); ++ } ++ if (rc != 0) { ++ fprintf(stderr, "Error converting input data.\n"); ++ exit(EXIT_FAILURE); ++ } ++ ++ rc = ubigen_destroy(&u); ++ return rc; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/ubigen.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/ubigen.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/ubigen.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/ubigen.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,149 @@ ++#ifndef __UBIGEN_H__ ++#define __UBIGEN_H__ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Frank Haverkamp ++ * ++ * An utility to update UBI volumes. ++ */ ++ ++#include /* FILE */ ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define DEFAULT_BLOCKSIZE (128 * 1024) ++#define DEFAULT_PAGESIZE (2*1024) ++ ++#define EUBIGEN_INVALID_TYPE 1 ++#define EUBIGEN_INVALID_HDR_OFFSET 2 ++#define EUBIGEN_INVALID_ALIGNMENT 3 ++#define EUBIGEN_TOO_SMALL_EB 4 ++#define EUBIGEN_MAX_ERROR 5 ++ ++ ++typedef enum action { ++ NO_ERROR = 0x00000000, ++ BROKEN_HDR_CRC = 0x00000001, ++ BROKEN_DATA_CRC = 0x00000002, ++ BROKEN_DATA_SIZE = 0x00000004, ++ BROKEN_OMIT_BLK = 0x00000008, ++ MARK_AS_UPDATE = 0x00000010, ++} ubigen_action_t; ++ ++typedef struct ubi_info *ubi_info_t; ++ ++/** ++ * @brief Initialize the internal CRC32 table. ++ * @note Necessary because of the used crc32 function in UBI. ++ * A usage of CRC32, from e.g. zlib will fail. ++ */ ++void ubigen_init(void); ++ ++/** ++ * @brief Create an ubigen handle. ++ * @param ... ++ * @return 0 On sucess. ++ * else Error. ++ * @note This parameterlist is ugly. But we have to use ++ * two big structs and meta information internally, ++ * filling them would be even uglier. ++ */ ++int ubigen_create(ubi_info_t *u, uint32_t vol_id, uint8_t vol_type, ++ uint32_t eb_size, uint64_t ec, uint32_t alignment, ++ uint8_t version, uint32_t vid_hdr_offset, ++ uint8_t compat_flag, size_t data_size, ++ FILE* fp_in, FILE* fp_out); ++ ++/** ++ * @brief Destroy an ubigen handle. ++ * @param u Handle to free. ++ * @return 0 On success. ++ * else Error. ++ */ ++int ubigen_destroy(ubi_info_t *u); ++ ++/** ++ * @brief Get number of total logical EBs, necessary for the ++ * complete storage of data in the handle. ++ * @param u The handle. ++ * @return 0 On success. ++ * else Error. ++ */ ++int ubigen_get_leb_total(ubi_info_t u, size_t* total); ++ ++/** ++ * @brief Get the size in bytes of one logical EB in the handle. ++ * @param u The handle. ++ * @return 0 On success. ++ * else Error. ++ */ ++int ubigen_get_leb_size(ubi_info_t u, size_t* size); ++ ++ ++/** ++ * @brief Write a logical EB (fits exactly into 1 physical EB). ++ * @param u Handle which holds all necessary data. ++ * @param action Additional operations which shall be applied on this ++ * logical eraseblock. Mostly injecting artifical errors. ++ * @return 0 On success. ++ * else Error. ++ */ ++int ubigen_write_leb(ubi_info_t u, ubigen_action_t action); ++ ++/** ++ * @brief Write a complete array of logical eraseblocks at once. ++ * @param u Handle which holds all necessary data. ++ * @return 0 On success. ++ * else Error. ++ */ ++int ubigen_write_complete(ubi_info_t u); ++ ++/** ++ * @brief Write a single block which is extracted from the ++ * binary input data. ++ * @param u Handle which holds all necessary data. ++ * @param blk Logical eraseblock which shall hold a inc. copy entry ++ * and a bad data crc. ++ * @return 0 On success. ++ * else Error. ++ */ ++int ubigen_write_broken_update(ubi_info_t u, uint32_t blk); ++ ++/** ++ * @brief Use the current ubi_info data and some additional data ++ * to set an UBI volume table entry from it. ++ * @param u Handle which holds some of the necessary data. ++ * @param res_bytes Number of reserved bytes which is stored in the volume ++ * table entry. ++ * @param name A string which shall be used as a volume label. ++ * @param lvol_r A pointer to a volume table entry. ++ * @return 0 On success. ++ * else Error. ++ */ ++int ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes, ++ const char* name, struct ubi_vtbl_record *lvol_rec); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* __UBIGEN_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/ubimirror.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/ubimirror.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/ubimirror.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/ubimirror.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,213 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ * ++ * 1.2 Removed argp because we want to use uClibc. ++ * 1.3 Minor cleanups ++ * 1.4 Migrated to new libubi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "config.h" ++#include "error.h" ++#include "example_ubi.h" ++#include "ubimirror.h" ++ ++#define PROGRAM_VERSION "1.4" ++ ++typedef enum action_t { ++ ACT_NORMAL = 0, ++ ACT_ARGP_ABORT, ++ ACT_ARGP_ERR, ++} action_t; ++ ++#define ABORT_ARGP do { \ ++ args->action = ACT_ARGP_ABORT; \ ++} while (0) ++ ++#define ERR_ARGP do { \ ++ args->action = ACT_ARGP_ERR; \ ++} while (0) ++ ++#define VOL_ARGS_MAX 2 ++ ++static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" ++ "ubimirror - mirrors ubi volumes.\n"; ++ ++static const char *optionsstr = ++" -c, --copyright Print copyright information.\n" ++" -s, --side= Use the side as source.\n" ++" -?, --help Give this help list\n" ++" --usage Give a short usage message\n" ++" -V, --version Print program version\n"; ++ ++static const char *usage = ++"Usage: ubimirror [-c?V] [-s ] [--copyright] [--side=]\n" ++" [--help] [--usage] [--version] \n"; ++ ++static const char copyright [] __attribute__((unused)) = ++ "(C) IBM Coorporation 2007"; ++ ++struct option long_options[] = { ++ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, ++ { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, ++ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, ++ { NULL, 0, NULL, 0} ++}; ++ ++typedef struct myargs { ++ action_t action; ++ int side; ++ int vol_no; /* index of current volume */ ++ /* @FIXME replace by bootenv_list, makes live easier */ ++ /* @FIXME remove the constraint of two entries in the array */ ++ const char* vol[VOL_ARGS_MAX]; /* comma separated list of src/dst ++ volumes */ ++ char *arg1; ++ char **options; /* [STRING...] */ ++} myargs; ++ ++static int ++get_update_side(const char* str) ++{ ++ uint32_t i = strtoul(str, NULL, 0); ++ ++ if ((i != 0) && (i != 1)) { ++ return -1; ++ } ++ return i; ++} ++ ++ ++static int ++parse_opt(int argc, char **argv, myargs *args) ++{ ++ while (1) { ++ int key; ++ ++ key = getopt_long(argc, argv, "cs:?V", long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 'c': ++ err_msg("%s", copyright); ++ ABORT_ARGP; ++ break; ++ case 's': ++ args->side = get_update_side(optarg); ++ if (args->side < 0) { ++ err_msg("Unsupported seqnum: %s.\n" ++ "Supported seqnums are '0' " ++ "and '1'\n", optarg); ++ ERR_ARGP; ++ } ++ break; ++ case '?': /* help */ ++ err_msg("Usage: ubimirror [OPTION...] " ++ " \n"); ++ err_msg("%s", doc); ++ err_msg("%s", optionsstr); ++ err_msg("\nReport bugs to %s\n", ++ PACKAGE_BUGREPORT); ++ exit(0); ++ break; ++ case 'V': ++ err_msg("%s", PROGRAM_VERSION); ++ exit(0); ++ break; ++ default: ++ err_msg("%s", usage); ++ exit(-1); ++ } ++ } ++ ++ while (optind < argc) { ++ /* only two entries allowed */ ++ if (args->vol_no >= VOL_ARGS_MAX) { ++ err_msg("%s", usage); ++ ERR_ARGP; ++ } ++ args->vol[(args->vol_no)++] = argv[optind++]; ++ } ++ ++ return 0; ++} ++ ++ ++int ++main(int argc, char **argv) { ++ int rc = 0; ++ unsigned int ids[VOL_ARGS_MAX]; ++ char err_buf[1024]; ++ ++ myargs args = { ++ .action = ACT_NORMAL, ++ .side = -1, ++ .vol_no = 0, ++ .vol = {"", ""}, ++ .options = NULL, ++ }; ++ ++ parse_opt(argc, argv, &args); ++ if (args.action == ACT_ARGP_ERR) { ++ rc = 127; ++ goto err; ++ } ++ if (args.action == ACT_ARGP_ABORT) { ++ rc = 126; ++ goto out; ++ } ++ if (args.vol_no < VOL_ARGS_MAX) { ++ fprintf(stderr, "missing volume number for %s\n", ++ args.vol_no == 0 ? "source and target" : "target"); ++ rc = 125; ++ goto out; ++ } ++ for( rc = 0; rc < args.vol_no; ++rc){ ++ char *endp; ++ ids[rc] = strtoul(args.vol[rc], &endp, 0); ++ if( *endp != '\0' ){ ++ fprintf(stderr, "invalid volume number %s\n", ++ args.vol[rc]); ++ rc = 125; ++ goto out; ++ } ++ } ++ rc = ubimirror(EXAMPLE_UBI_DEVICE, args.side, ids, args.vol_no, ++ err_buf, sizeof(err_buf)); ++ if( rc ){ ++ err_buf[sizeof err_buf - 1] = '\0'; ++ fprintf(stderr, err_buf); ++ if( rc < 0 ) ++ rc = -rc; ++ } ++ out: ++ err: ++ return rc; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/ubimirror.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/ubimirror.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/ubimirror.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/ubimirror.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,66 @@ ++#ifndef __UBIMIRROR_H__ ++#define __UBIMIRROR_H__ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Oliver Lohmann ++ * ++ * An utility to mirror UBI volumes. ++ */ ++ ++#include ++ ++/** ++ * @def EUBIMIRROR_SRC_EQ_DST ++ * @brief Given source volume is also in the set of destination volumes. ++ */ ++#define EUBIMIRROR_SRC_EQ_DST 20 ++ ++/** ++ * @def EUBIMIRROR_NO_SRC ++ * @brief The given source volume does not exist. ++ */ ++#define EUBIMIRROR_NO_SRC 21 ++ ++/** ++ * @def EUBIMIRROR_NO_DST ++ * @brief One of the given destination volumes does not exist. ++ */ ++#define EUBIMIRROR_NO_DST 22 ++ ++/** ++ * @brief Mirrors UBI devices from a source device (specified by seqnum) ++ * to n target devices. ++ * @param devno Device number used by the UBI operations. ++ * @param seqnum An index into ids (defines the src_id). ++ * @param ids An array of ids. ++ * @param ids_size The number of entries in the ids array. ++ * @param err_buf A buffer to store verbose error messages. ++ * @param err_buf_size The size of the error buffer. ++ * ++ * @note A seqnum of value < 0 defaults to a seqnum of 0. ++ * @note A seqnum exceeding the range of ids_size defaults to 0. ++ * @note An empty ids list results in a empty stmt. ++ * @pre The UBI volume which shall be used as source volume exists. ++ * @pre The UBI volumes which are defined as destination volumes exist. ++ * @post The content of the UBI volume which was defined as source volume ++ * equals the content of the volumes which were defined as destination. ++ */ ++int ubimirror(uint32_t devno, int seqnum, uint32_t* ids, ssize_t ids_size, ++ char *err_buf, size_t err_buf_size); ++ ++#endif /* __UBIMIRROR_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/unubi.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/unubi.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/unubi.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/unubi.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1024 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006, 2007 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * Authors: Drake Dowsett, dowsett@de.ibm.com ++ * Frank Haverkamp, haver@vnet.ibm.com ++ * ++ * 1.2 Removed argp because we want to use uClibc. ++ * 1.3 Minor cleanups. ++ * 1.4 Meanwhile Drake had done a lot of changes, syncing those. ++ * 1.5 Bugfixes, simplifications ++ */ ++ ++/* ++ * unubi reads an image file containing blocks of UBI headers and data ++ * (such as produced from nand2bin) and rebuilds the volumes within. The ++ * default operation (when no flags are given) is to rebuild all valid ++ * volumes found in the image. unubi can also read straight from the ++ * onboard MTD device (ex. /dev/mtdblock/NAND). ++ */ ++ ++/* TODO: consideration for dynamic vs. static volumes */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "crc32.h" ++#include "unubi_analyze.h" ++ ++#define EXEC "unubi" ++#define CONTACT "haver@vnet.ibm.com" ++#define VERSION "1.5" ++ ++static char doc[] = "\nVersion: " VERSION "\n"; ++static int debug = 0; ++ ++static const char *optionsstr = ++"Extract volumes and/or analysis information from an UBI data file.\n" ++"When no parameters are flagged or given, the default operation is\n" ++"to rebuild all valid complete UBI volumes found within the image.\n" ++"\n" ++" OPERATIONS\n" ++" -a, --analyze Analyze image and create gnuplot graphs\n" ++" -i, --info-table Extract volume information tables\n" ++" -r, --rebuild= Extract and rebuild volume\n" ++"\n" ++" OPTIONS\n" ++" -b, --blocksize= Specify size of eraseblocks in image in bytes\n" ++" (default 128KiB)\n" ++" -d, --dir= Specify output directory\n" ++" -D, --debug Enable debug output\n" ++" -s, --headersize= Specify size reserved for metadata in eraseblock\n" ++ " in bytes (default 2048 Byte)\n" ++ /* the -s option might be insufficient when using different vid ++ offset than what we used when writing this tool ... Better would ++ probably be --vid-hdr-offset or alike */ ++"\n" ++" ADVANCED\n" ++" -e, --eb-split Generate individual eraseblock images (all\n" ++" eraseblocks)\n" ++" -v, --vol-split Generate individual eraseblock images (valid\n" ++" eraseblocks only)\n" ++" -V, --vol-split! Raw split by eraseblock (valid eraseblocks only)\n" ++"\n" ++" -?, --help Give this help list\n" ++" --usage Give a short usage message\n" ++" --version Print program version\n" ++"\n"; ++ ++static const char *usage = ++"Usage: unubi [-aievV?] [-r ] [-b ] [-d ]\n" ++" [-s ] [--analyze] [--info-table]\n" ++" [--rebuild=] [--blocksize=]\n" ++" [--dir=] [--headersize=] [--eb-split]\n" ++" [--vol-split] [--vol-split!] [--help] [--usage] [--version]\n" ++" image-file\n"; ++ ++#define ERR_MSG(fmt...) \ ++ fprintf(stderr, EXEC ": " fmt) ++ ++#define SPLIT_DATA 1 ++#define SPLIT_RAW 2 ++ ++#define DIR_FMT "unubi_%s" ++#define KIB 1024 ++#define MIB (KIB * KIB) ++#define MAXPATH KIB ++ ++/* filenames */ ++#define FN_INVAL "%s/eb%04u%s" /* invalid eraseblock */ ++#define FN_NSURE "%s/eb%04u_%03u_%03u_%03x%s" /* unsure eraseblock */ ++#define FN_VALID "%s/eb%04u_%03u_%03u_%03x%s" /* valid eraseblock */ ++#define FN_VOLSP "%s/vol%03u_%03u_%03u_%04zu" /* split volume */ ++#define FN_VOLWH "%s/volume%03u" /* whole volume */ ++#define FN_VITBL "%s/vol_info_table%zu" /* vol info table */ ++ ++static uint32_t crc32_table[256]; ++ ++/* struct args: ++ * bsize int, blocksize of image blocks ++ * hsize int, eraseblock header size ++ * analyze flag, when non-zero produce analysis ++ * eb_split flag, when non-zero output eb#### ++ * note: SPLIT_DATA vs. SPLIT_RAW ++ * vol_split flag, when non-zero output vol###_#### ++ * note: SPLIT_DATA vs. SPLIT_RAW ++ * odir_path string, directory to place volumes in ++ * img_path string, file to read as ubi image ++ * vols int array of size UBI_MAX_VOLUMES, where a 1 can be ++ * written for each --rebuild flag in the index specified ++ * then the array can be counted and collapsed using ++ * count_set() and collapse() ++ */ ++struct args { ++ int analyze; ++ int itable; ++ uint32_t *vols; ++ ++ size_t vid_hdr_offset; ++ size_t data_offset; ++ size_t bsize; /* FIXME replace by vid_hdr/data offs? */ ++ size_t hsize; ++ ++ char *odir_path; ++ int eb_split; ++ int vol_split; ++ char *img_path; ++ ++ char **options; ++}; ++ ++struct option long_options[] = { ++ { .name = "rebuild", .has_arg = 1, .flag = NULL, .val = 'r' }, ++ { .name = "dir", .has_arg = 1, .flag = NULL, .val = 'd' }, ++ { .name = "analyze", .has_arg = 0, .flag = NULL, .val = 'a' }, ++ { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'b' }, ++ { .name = "eb-split", .has_arg = 0, .flag = NULL, .val = 'e' }, ++ { .name = "vol-split", .has_arg = 0, .flag = NULL, .val = 'v' }, ++ { .name = "vol-split!", .has_arg = 0, .flag = NULL, .val = 'e' }, ++ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, ++ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, ++ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'J' }, ++ { NULL, 0, NULL, 0} ++}; ++ ++/** ++ * parses out a numerical value from a string of numbers followed by: ++ * k, K, kib, KiB for kibibyte ++ * m, M, mib, MiB for mebibyte ++ **/ ++static uint32_t ++str_to_num(char *str) ++{ ++ char *s; ++ ulong num; ++ ++ s = str; ++ num = strtoul(s, &s, 0); ++ ++ if (*s != '\0') { ++ if ((strcmp(s, "KiB") == 0) || (strcmp(s, "K") == 0) || ++ (strcmp(s, "kib") == 0) || (strcmp(s, "k") == 0)) ++ num *= KIB; ++ else if ((strcmp(s, "MiB") == 0) || (strcmp(s, "M") == 0) || ++ (strcmp(s, "mib") == 0) || (strcmp(s, "m") == 0)) ++ num *= MIB; ++ else ++ ERR_MSG("couldn't parse '%s', assuming %lu\n", ++ s, num); ++ } ++ return num; ++} ++ ++static int ++parse_opt(int argc, char **argv, struct args *args) ++{ ++ uint32_t i; ++ ++ while (1) { ++ int key; ++ ++ key = getopt_long(argc, argv, "ab:s:d:Deir:vV?J", ++ long_options, NULL); ++ if (key == -1) ++ break; ++ ++ switch (key) { ++ case 'a': /* --analyze */ ++ args->analyze = 1; ++ break; ++ case 'b': /* --block-size= */ ++ args->bsize = str_to_num(optarg); ++ break; ++ case 's': /* --header-size= */ ++ args->hsize = str_to_num(optarg); ++ break; ++ case 'd': /* --dir= */ ++ args->odir_path = optarg; ++ break; ++ case 'D': /* --debug */ ++ /* I wanted to use -v but that was already ++ used ... */ ++ debug = 1; ++ break; ++ case 'e': /* --eb-split */ ++ args->eb_split = SPLIT_RAW; ++ break; ++ case 'i': /* --info-table */ ++ args->itable = 1; ++ break; ++ case 'r': /* --rebuild= */ ++ i = str_to_num(optarg); ++ if (i < UBI_MAX_VOLUMES) ++ args->vols[str_to_num(optarg)] = 1; ++ else { ++ ERR_MSG("volume-id out of bounds\n"); ++ return -1; ++ } ++ break; ++ case 'v': /* --vol-split */ ++ if (args->vol_split != SPLIT_RAW) ++ args->vol_split = SPLIT_DATA; ++ break; ++ case 'V': /* --vol-split! */ ++ args->vol_split = SPLIT_RAW; ++ break; ++ case '?': /* help */ ++ fprintf(stderr, "Usage: unubi [OPTION...] " ++ "image-file\n%s%s\nReport bugs to %s\n", ++ doc, optionsstr, CONTACT); ++ exit(0); ++ break; ++ case 'J': ++ fprintf(stderr, "%s\n", VERSION); ++ exit(0); ++ break; ++ default: ++ fprintf(stderr, "%s", usage); ++ exit(-1); ++ } ++ } ++ ++ /* FIXME I suppose hsize should be replaced! */ ++ args->vid_hdr_offset = args->hsize - UBI_VID_HDR_SIZE; ++ args->data_offset = args->hsize; ++ ++ if (optind < argc) ++ args->img_path = argv[optind++]; ++ return 0; ++} ++ ++ ++/** ++ * counts the number of indicies which are flagged in full_array; ++ * full_array is an array of flags (1/0); ++ **/ ++static size_t ++count_set(uint32_t *full_array, size_t full_len) ++{ ++ size_t count, i; ++ ++ if (full_array == NULL) ++ return 0; ++ ++ for (i = 0, count = 0; i < full_len; i++) ++ if (full_array[i] != 0) ++ count++; ++ ++ return count; ++} ++ ++ ++/** ++ * generates coll_array from full_array; ++ * full_array is an array of flags (1/0); ++ * coll_array is an array of the indicies in full_array which are flagged (1); ++ **/ ++static size_t ++collapse(uint32_t *full_array, size_t full_len, ++ uint32_t *coll_array, size_t coll_len) ++{ ++ size_t i, j; ++ ++ if ((full_array == NULL) || (coll_array == NULL)) ++ return 0; ++ ++ for (i = 0, j = 0; (i < full_len) && (j < coll_len); i++) ++ if (full_array[i] != 0) { ++ coll_array[j] = i; ++ j++; ++ } ++ ++ return j; ++} ++ ++/** ++ * data_crc: save the FILE* position, calculate the crc over a span, ++ * reset the position ++ * returns non-zero when EOF encountered ++ **/ ++static int ++data_crc(FILE* fpin, size_t length, uint32_t *ret_crc) ++{ ++ int rc; ++ size_t i; ++ char buf[length]; ++ uint32_t crc; ++ fpos_t start; ++ ++ rc = fgetpos(fpin, &start); ++ if (rc < 0) ++ return -1; ++ ++ for (i = 0; i < length; i++) { ++ int c = fgetc(fpin); ++ if (c == EOF) { ++ ERR_MSG("unexpected EOF\n"); ++ return -1; ++ } ++ buf[i] = (char)c; ++ } ++ ++ rc = fsetpos(fpin, &start); ++ if (rc < 0) ++ return -1; ++ ++ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, length); ++ *ret_crc = crc; ++ return 0; ++} ++ ++ ++/** ++ * reads data of size len from fpin and writes it to path ++ **/ ++static int ++extract_data(FILE* fpin, size_t len, const char *path) ++{ ++ int rc; ++ size_t i; ++ FILE* fpout; ++ ++ rc = 0; ++ fpout = NULL; ++ ++ fpout = fopen(path, "wb"); ++ if (fpout == NULL) { ++ ERR_MSG("couldn't open file for writing: %s\n", path); ++ rc = -1; ++ goto err; ++ } ++ ++ for (i = 0; i < len; i++) { ++ int c = fgetc(fpin); ++ if (c == EOF) { ++ ERR_MSG("unexpected EOF while writing: %s\n", path); ++ rc = -2; ++ goto err; ++ } ++ c = fputc(c, fpout); ++ if (c == EOF) { ++ ERR_MSG("couldn't write: %s\n", path); ++ rc = -3; ++ goto err; ++ } ++ } ++ ++ err: ++ if (fpout != NULL) ++ fclose(fpout); ++ return rc; ++} ++ ++ ++/** ++ * extract volume information table from block. saves and reloads fpin ++ * position ++ * returns -1 when a fpos set or get fails, otherwise <= -2 on other ++ * failure and 0 on success ++ **/ ++static int ++extract_itable(FILE *fpin, struct eb_info *cur, size_t bsize, size_t num, ++ const char *path) ++{ ++ char filename[MAXPATH + 1]; ++ int rc; ++ size_t i, max; ++ fpos_t temp; ++ FILE* fpout = NULL; ++ struct ubi_vtbl_record rec; ++ ++ if (fpin == NULL || cur == NULL || path == NULL) ++ return -2; ++ ++ /* remember position */ ++ rc = fgetpos(fpin, &temp); ++ if (rc < 0) ++ return -1; ++ ++ /* jump to top of eraseblock, skip to data section */ ++ fsetpos(fpin, &cur->eb_top); ++ if (rc < 0) ++ return -1; ++ fseek(fpin, be32_to_cpu(cur->ec.data_offset), SEEK_CUR); ++ ++ /* prepare output file */ ++ if (be32_to_cpu(cur->vid.vol_id) != UBI_LAYOUT_VOLUME_ID) ++ return -2; ++ memset(filename, 0, MAXPATH + 1); ++ snprintf(filename, MAXPATH, FN_VITBL, path, num); ++ fpout = fopen(filename, "w"); ++ if (fpout == NULL) ++ return -2; ++ ++ /* loop through entries */ ++ fprintf(fpout, ++ "index\trpebs\talign\ttype\tcrc\t\tname\n"); ++ max = bsize - be32_to_cpu(cur->ec.data_offset); ++ for (i = 0; i < (max / sizeof(rec)); i++) { ++ int blank = 1; ++ char *ptr, *base; ++ char name[UBI_VOL_NAME_MAX + 1]; ++ const char *type = "unknown\0"; ++ uint32_t crc; ++ ++ /* read record */ ++ rc = fread(&rec, 1, sizeof(rec), fpin); ++ if (rc == 0) ++ break; ++ if (rc != sizeof(rec)) { ++ ERR_MSG("reading volume information " ++ "table record failed\n"); ++ rc = -3; ++ goto exit; ++ } ++ ++ /* check crc */ ++ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &rec, ++ UBI_VTBL_RECORD_SIZE_CRC); ++ if (crc != be32_to_cpu(rec.crc)) ++ continue; ++ ++ /* check for empty */ ++ base = (char *)&rec; ++ ptr = base; ++ while (blank && ++ ((unsigned)(ptr - base) < UBI_VTBL_RECORD_SIZE_CRC)) { ++ if (*ptr != 0) ++ blank = 0; ++ ptr++; ++ } ++ ++ if (blank) ++ continue; ++ ++ /* prep type string */ ++ if (rec.vol_type == UBI_VID_DYNAMIC) ++ type = "dynamic\0"; ++ else if (rec.vol_type == UBI_VID_STATIC) ++ type = "static\0"; ++ ++ /* prep name string */ ++ rec.name[be16_to_cpu(rec.name_len)] = '\0'; ++ sprintf(name, "%s", rec.name); ++ ++ /* print record line to fpout */ ++ fprintf(fpout, "%zu\t%u\t%u\t%s\t0x%08x\t%s\n", ++ i, ++ be32_to_cpu(rec.reserved_pebs), ++ be32_to_cpu(rec.alignment), ++ type, ++ be32_to_cpu(rec.crc), ++ name); ++ } ++ ++ exit: ++ /* reset position */ ++ if (fsetpos(fpin, &temp) < 0) ++ rc = -1; ++ ++ if (fpout != NULL) ++ fclose(fpout); ++ ++ return rc; ++} ++ ++ ++/** ++ * using eb chain, tries to rebuild the data of volume at vol_id, or for all ++ * the known volumes, if vol_id is NULL; ++ **/ ++static int ++rebuild_volume(FILE * fpin, uint32_t *vol_id, struct eb_info **head, ++ const char *path, size_t block_size, size_t header_size) ++{ ++ char filename[MAXPATH]; ++ int rc; ++ uint32_t vol, num, data_size; ++ FILE* fpout; ++ struct eb_info *cur; ++ ++ rc = 0; ++ ++ if ((fpin == NULL) || (head == NULL) || (*head == NULL)) ++ return 0; ++ ++ /* when vol_id is null, then do all */ ++ if (vol_id == NULL) { ++ cur = *head; ++ vol = be32_to_cpu(cur->vid.vol_id); ++ } else { ++ vol = *vol_id; ++ eb_chain_position(head, vol, NULL, &cur); ++ if (cur == NULL) { ++ if (debug) ++ ERR_MSG("no valid volume %d was found\n", vol); ++ return -1; ++ } ++ } ++ ++ num = 0; ++ snprintf(filename, MAXPATH, FN_VOLWH, path, vol); ++ fpout = fopen(filename, "wb"); ++ if (fpout == NULL) { ++ ERR_MSG("couldn't open file for writing: %s\n", filename); ++ return -1; ++ } ++ ++ while (cur != NULL) { ++ size_t i; ++ ++ if (be32_to_cpu(cur->vid.vol_id) != vol) { ++ /* close out file */ ++ fclose(fpout); ++ ++ /* only stay around if that was the only volume */ ++ if (vol_id != NULL) ++ goto out; ++ ++ /* begin with next */ ++ vol = be32_to_cpu(cur->vid.vol_id); ++ num = 0; ++ snprintf(filename, MAXPATH, FN_VOLWH, path, vol); ++ fpout = fopen(filename, "wb"); ++ if (fpout == NULL) { ++ ERR_MSG("couldn't open file for writing: %s\n", ++ filename); ++ return -1; ++ } ++ } ++ ++ while (num < be32_to_cpu(cur->vid.lnum)) { ++ /* FIXME haver: I hope an empty block is ++ written out so that the binary has no holes ++ ... */ ++ if (debug) ++ ERR_MSG("missing valid block %d for volume %d\n", ++ num, vol); ++ num++; ++ } ++ ++ rc = fsetpos(fpin, &(cur->eb_top)); ++ if (rc < 0) ++ goto out; ++ fseek(fpin, be32_to_cpu(cur->ec.data_offset), SEEK_CUR); ++ ++ if (cur->vid.vol_type == UBI_VID_DYNAMIC) ++ /* FIXME It might be that alignment has influence */ ++ data_size = block_size - header_size; ++ else ++ data_size = be32_to_cpu(cur->vid.data_size); ++ ++ for (i = 0; i < data_size; i++) { ++ int c = fgetc(fpin); ++ if (c == EOF) { ++ ERR_MSG("unexpected EOF while writing: %s\n", ++ filename); ++ rc = -2; ++ goto out; ++ } ++ c = fputc(c, fpout); ++ if (c == EOF) { ++ ERR_MSG("couldn't write: %s\n", filename); ++ rc = -3; ++ goto out; ++ } ++ } ++ ++ cur = cur->next; ++ num++; ++ } ++ ++ out: ++ if (vol_id == NULL) ++ fclose(fpout); ++ return rc; ++} ++ ++ ++/** ++ * traverses FILE* trying to load complete, valid and accurate header data ++ * into the eb chain; ++ **/ ++static int ++unubi_volumes(FILE* fpin, uint32_t *vols, size_t vc, struct args *a) ++{ ++ char filename[MAXPATH + 1]; ++ char reason[MAXPATH + 1]; ++ int rc; ++ size_t i, count, itable_num; ++ /* relations: ++ * cur ~ head ++ * next ~ first */ ++ struct eb_info *head, *cur, *first, *next; ++ struct eb_info **next_ptr; ++ ++ rc = 0; ++ count = 0; ++ itable_num = 0; ++ head = NULL; ++ first = NULL; ++ next = NULL; ++ cur = malloc(sizeof(*cur)); ++ if (cur == NULL) { ++ ERR_MSG("out of memory\n"); ++ rc = -ENOMEM; ++ goto err; ++ } ++ memset(cur, 0, sizeof(*cur)); ++ ++ fgetpos(fpin, &(cur->eb_top)); ++ while (1) { ++ const char *raw_path; ++ uint32_t crc; ++ ++ cur->phys_addr = ftell(fpin); ++ cur->phys_block = cur->phys_addr / a->bsize; ++ cur->data_crc_ok = 0; ++ cur->ec_crc_ok = 0; ++ cur->vid_crc_ok = 0; ++ ++ memset(filename, 0, MAXPATH + 1); ++ memset(reason, 0, MAXPATH + 1); ++ ++ /* in case of an incomplete ec header */ ++ raw_path = FN_INVAL; ++ ++ /* read erasecounter header */ ++ rc = fread(&cur->ec, 1, sizeof(cur->ec), fpin); ++ if (rc == 0) ++ goto out; /* EOF */ ++ if (rc != sizeof(cur->ec)) { ++ ERR_MSG("reading ec-hdr failed\n"); ++ rc = -1; ++ goto err; ++ } ++ ++ /* check erasecounter header magic */ ++ if (be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) { ++ snprintf(reason, MAXPATH, ".invalid.ec_magic"); ++ goto invalid; ++ } ++ ++ /* check erasecounter header crc */ ++ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &(cur->ec), ++ UBI_EC_HDR_SIZE_CRC); ++ if (be32_to_cpu(cur->ec.hdr_crc) != crc) { ++ snprintf(reason, MAXPATH, ".invalid.ec_hdr_crc"); ++ goto invalid; ++ } ++ ++ /* read volume id header */ ++ rc = fsetpos(fpin, &(cur->eb_top)); ++ if (rc != 0) ++ goto err; ++ fseek(fpin, be32_to_cpu(cur->ec.vid_hdr_offset), SEEK_CUR); ++ rc = fread(&cur->vid, 1, sizeof(cur->vid), fpin); ++ if (rc == 0) ++ goto out; /* EOF */ ++ if (rc != sizeof(cur->vid)) { ++ ERR_MSG("reading vid-hdr failed\n"); ++ rc = -1; ++ goto err; ++ } ++ ++ /* if the magic number is 0xFFFFFFFF, then it's very likely ++ * that the volume is empty */ ++ if (be32_to_cpu(cur->vid.magic) == 0xffffffff) { ++ snprintf(reason, MAXPATH, ".empty"); ++ goto invalid; ++ } ++ ++ /* vol_id should be in bounds */ ++ if ((be32_to_cpu(cur->vid.vol_id) >= UBI_MAX_VOLUMES) && ++ (be32_to_cpu(cur->vid.vol_id) < ++ UBI_INTERNAL_VOL_START)) { ++ snprintf(reason, MAXPATH, ".invalid"); ++ goto invalid; ++ } else ++ raw_path = FN_NSURE; ++ ++ /* check volume id header magic */ ++ if (be32_to_cpu(cur->vid.magic) != UBI_VID_HDR_MAGIC) { ++ snprintf(reason, MAXPATH, ".invalid.vid_magic"); ++ goto invalid; ++ } ++ cur->ec_crc_ok = 1; ++ ++ /* check volume id header crc */ ++ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &(cur->vid), ++ UBI_VID_HDR_SIZE_CRC); ++ if (be32_to_cpu(cur->vid.hdr_crc) != crc) { ++ snprintf(reason, MAXPATH, ".invalid.vid_hdr_crc"); ++ goto invalid; ++ } ++ cur->vid_crc_ok = 1; ++ ++ /* check data crc, but only for a static volume */ ++ if (cur->vid.vol_type == UBI_VID_STATIC) { ++ rc = data_crc(fpin, be32_to_cpu(cur->vid.data_size), ++ &crc); ++ if (rc < 0) ++ goto err; ++ if (be32_to_cpu(cur->vid.data_crc) != crc) { ++ snprintf(reason, MAXPATH, ".invalid.data_crc"); ++ goto invalid; ++ } ++ cur->data_crc_ok = 1; ++ } ++ ++ /* enlist this vol, it's valid */ ++ raw_path = FN_VALID; ++ cur->linear = count; ++ rc = eb_chain_insert(&head, cur); ++ if (rc < 0) { ++ if (rc == -ENOMEM) { ++ ERR_MSG("out of memory\n"); ++ goto err; ++ } ++ ERR_MSG("unknown and unexpected error, please contact " ++ CONTACT "\n"); ++ goto err; ++ } ++ ++ /* extract info-table */ ++ if (a->itable && ++ (be32_to_cpu(cur->vid.vol_id) == UBI_LAYOUT_VOLUME_ID)) { ++ extract_itable(fpin, cur, a->bsize, ++ itable_num, a->odir_path); ++ itable_num++; ++ } ++ ++ /* split volumes */ ++ if (a->vol_split) { ++ size_t size = 0; ++ ++ rc = fsetpos(fpin, &(cur->eb_top)); ++ if (rc != 0) ++ goto err; ++ ++ /* ++ * FIXME For dynamic UBI volumes we must write ++ * the maximum available data. The ++ * vid.data_size field is not used in this ++ * case. The dynamic volume user is ++ * responsible for the content. ++ */ ++ if (a->vol_split == SPLIT_DATA) { ++ /* Write only data section */ ++ if (cur->vid.vol_type == UBI_VID_DYNAMIC) { ++ /* FIXME Formular is not ++ always right ... */ ++ size = a->bsize - a->hsize; ++ } else ++ size = be32_to_cpu(cur->vid.data_size); ++ ++ fseek(fpin, ++ be32_to_cpu(cur->ec.data_offset), ++ SEEK_CUR); ++ } ++ else if (a->vol_split == SPLIT_RAW) ++ /* write entire eraseblock */ ++ size = a->bsize; ++ ++ snprintf(filename, MAXPATH, FN_VOLSP, ++ a->odir_path, ++ be32_to_cpu(cur->vid.vol_id), ++ be32_to_cpu(cur->vid.lnum), ++ be32_to_cpu(cur->vid.leb_ver), count); ++ rc = extract_data(fpin, size, filename); ++ if (rc < 0) ++ goto err; ++ } ++ ++ invalid: ++ /* split eraseblocks */ ++ if (a->eb_split) { ++ /* jump to top of block */ ++ rc = fsetpos(fpin, &(cur->eb_top)); ++ if (rc != 0) ++ goto err; ++ ++ if (strcmp(raw_path, FN_INVAL) == 0) ++ snprintf(filename, MAXPATH, raw_path, ++ a->odir_path, count, reason); ++ else ++ snprintf(filename, MAXPATH, raw_path, ++ a->odir_path, ++ count, ++ be32_to_cpu(cur->vid.vol_id), ++ be32_to_cpu(cur->vid.lnum), ++ be32_to_cpu(cur->vid.leb_ver), ++ reason); ++ ++ rc = extract_data(fpin, a->bsize, filename); ++ if (rc < 0) ++ goto err; ++ } ++ ++ /* append to simple linked list */ ++ if (first == NULL) ++ next_ptr = &first; ++ else ++ next_ptr = &next->next; ++ ++ *next_ptr = malloc(sizeof(**next_ptr)); ++ if (*next_ptr == NULL) { ++ ERR_MSG("out of memory\n"); ++ rc = -ENOMEM; ++ goto err; ++ } ++ memset(*next_ptr, 0, sizeof(**next_ptr)); ++ ++ next = *next_ptr; ++ memcpy(next, cur, sizeof(*next)); ++ next->next = NULL; ++ ++ count++; ++ rc = fsetpos(fpin, &(cur->eb_top)); ++ if (rc != 0) ++ goto err; ++ fseek(fpin, a->bsize, SEEK_CUR); ++ memset(cur, 0, sizeof(*cur)); ++ ++ fgetpos(fpin, &(cur->eb_top)); ++ } ++ ++ out: ++ for (i = 0; i < vc; i++) { ++ rc = rebuild_volume(fpin, &vols[i], &head, a->odir_path, ++ a->bsize, a->hsize); ++ if (rc < 0) ++ goto err; ++ } ++ ++ /* if there were no volumes specified, rebuild them all, ++ * UNLESS eb_ or vol_ split or analyze was specified */ ++ if ((vc == 0) && (!a->eb_split) && (!a->vol_split) && ++ (!a->analyze) && (!a->itable)) { ++ rc = rebuild_volume(fpin, NULL, &head, a->odir_path, a->bsize, ++ a->hsize); ++ if (rc < 0) ++ goto err; ++ } ++ ++ err: ++ free(cur); ++ ++ if (a->analyze) { ++ char fname[PATH_MAX]; ++ FILE *fp; ++ ++ unubi_analyze(&head, first, a->odir_path); ++ ++ /* prepare output files */ ++ memset(fname, 0, PATH_MAX + 1); ++ snprintf(fname, PATH_MAX, "%s/%s", a->odir_path, FN_EH_STAT); ++ fp = fopen(fname, "w"); ++ if (fp != NULL) { ++ eb_chain_print(fp, head); ++ fclose(fp); ++ } ++ } ++ eb_chain_destroy(&head); ++ eb_chain_destroy(&first); ++ ++ return rc; ++} ++ ++ ++/** ++ * handles command line arguments, then calls unubi_volumes ++ **/ ++int ++main(int argc, char *argv[]) ++{ ++ int rc, free_a_odir; ++ size_t vols_len; ++ uint32_t *vols; ++ FILE* fpin; ++ struct args a; ++ ++ rc = 0; ++ free_a_odir = 0; ++ vols_len = 0; ++ vols = NULL; ++ fpin = NULL; ++ init_crc32_table(crc32_table); ++ ++ /* setup struct args a */ ++ memset(&a, 0, sizeof(a)); ++ a.bsize = 128 * KIB; ++ a.hsize = 2 * KIB; ++ a.vols = malloc(sizeof(*a.vols) * UBI_MAX_VOLUMES); ++ if (a.vols == NULL) { ++ ERR_MSG("out of memory\n"); ++ rc = ENOMEM; ++ goto err; ++ } ++ memset(a.vols, 0, sizeof(*a.vols) * UBI_MAX_VOLUMES); ++ ++ /* parse args and check for validity */ ++ parse_opt(argc, argv, &a); ++ if (a.img_path == NULL) { ++ ERR_MSG("no image file specified\n"); ++ rc = EINVAL; ++ goto err; ++ } ++ else if (a.odir_path == NULL) { ++ char *ptr; ++ int len; ++ ++ ptr = strrchr(a.img_path, '/'); ++ if (ptr == NULL) ++ ptr = a.img_path; ++ else ++ ptr++; ++ ++ len = strlen(DIR_FMT) + strlen(ptr); ++ free_a_odir = 1; ++ a.odir_path = malloc(sizeof(*a.odir_path) * len); ++ if (a.odir_path == NULL) { ++ ERR_MSG("out of memory\n"); ++ rc = ENOMEM; ++ goto err; ++ } ++ snprintf(a.odir_path, len, DIR_FMT, ptr); ++ } ++ ++ fpin = fopen(a.img_path, "rb"); ++ if (fpin == NULL) { ++ ERR_MSG("couldn't open file for reading: " ++ "%s\n", a.img_path); ++ rc = EINVAL; ++ goto err; ++ } ++ ++ rc = mkdir(a.odir_path, 0777); ++ if ((rc < 0) && (errno != EEXIST)) { ++ ERR_MSG("couldn't create ouput directory: " ++ "%s\n", a.odir_path); ++ rc = -rc; ++ goto err; ++ } ++ ++ /* fill in vols array */ ++ vols_len = count_set(a.vols, UBI_MAX_VOLUMES); ++ if (vols_len > 0) { ++ vols = malloc(sizeof(*vols) * vols_len); ++ if (vols == NULL) { ++ ERR_MSG("out of memory\n"); ++ rc = ENOMEM; ++ goto err; ++ } ++ collapse(a.vols, UBI_MAX_VOLUMES, vols, vols_len); ++ } ++ ++ /* unubi volumes */ ++ rc = unubi_volumes(fpin, vols, vols_len, &a); ++ if (rc < 0) { ++ /* ERR_MSG("error encountered while working on image file: " ++ "%s\n", a.img_path); */ ++ rc = -rc; ++ goto err; ++ } ++ ++ err: ++ free(a.vols); ++ if (free_a_odir != 0) ++ free(a.odir_path); ++ if (fpin != NULL) ++ fclose(fpin); ++ if (vols_len > 0) ++ free(vols); ++ return rc; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/unubi_analyze.c linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/unubi_analyze.c +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/unubi_analyze.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/unubi_analyze.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,463 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006, 2007 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * Authors: Drake Dowsett, dowsett@de.ibm.com ++ * Contact: Andreas Arnez, arnez@de.ibm.com ++ * ++ * unubi uses the following functions to generate analysis output based on ++ * the header information in a raw-UBI image ++ */ ++ ++/* ++ * TODO: use OOB data to check for eraseblock validity in NAND images ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "unubi_analyze.h" ++#include "crc32.h" ++ ++#define EC_X_INT 50 ++ ++/** ++ * intcmp - function needed by qsort to order integers ++ **/ ++int intcmp(const void *a, const void *b) ++{ ++ int A = *(int *)a; ++ int B = *(int *)b; ++ return A - B; ++} ++ ++int longcmp(const void *a, const void *b) ++{ ++ long long A = *(long long *)a; ++ long long B = *(long long *)b; ++ return A - B; ++} ++ ++ ++/** ++ * unubi_analyze_group_index - finds the normalized index in an array ++ * item: look for this item in the array ++ * array: array to search through ++ * size: length of the array ++ * array should be sorted for this algorithm to perform properly; ++ * if the item is not found returns -1, otherwise return value is the ++ * index in the array (note this contricts the array size to 2^32-1); ++ **/ ++int ++norm_index(uint32_t item, uint32_t *array, size_t length) ++{ ++ size_t i, index; ++ ++ for (index = 0, i = 0; i < length; i++) { ++ if ((i != 0) && (array[i] != array[i - 1])) ++ index++; ++ ++ if (item == array[i]) ++ return index; ++ } ++ ++ return -1; ++} ++ ++ ++/** ++ * unubi_analyze_ec_hdr - generate data table and plot script ++ * first: head of simple linked list ++ * path: folder to write into ++ * generates a data file containing the eraseblock index in the image ++ * and the erase counter found in its ec header; ++ * if the crc check fails, the line is commented out in the data file; ++ * also generates a simple gnuplot sript for quickly viewing one ++ * display of the data file; ++ **/ ++int ++unubi_analyze_ec_hdr(struct eb_info *first, const char *path) ++{ ++ char filename[PATH_MAX + 1]; ++ size_t count, eraseblocks; ++ uint32_t crc, crc32_table[256]; ++ uint64_t *erase_counts; ++ FILE* fpdata; ++ FILE* fpplot; ++ struct eb_info *cur; ++ ++ if (first == NULL) ++ return -1; ++ ++ /* crc check still needed for `first' linked list */ ++ init_crc32_table(crc32_table); ++ ++ /* prepare output files */ ++ memset(filename, 0, PATH_MAX + 1); ++ snprintf(filename, PATH_MAX, "%s/%s", path, FN_EH_DATA); ++ fpdata = fopen(filename, "w"); ++ if (fpdata == NULL) ++ return -1; ++ ++ memset(filename, 0, PATH_MAX + 1); ++ snprintf(filename, PATH_MAX, "%s/%s", path, FN_EH_PLOT); ++ fpplot = fopen(filename, "w"); ++ if (fpplot == NULL) { ++ fclose(fpdata); ++ return -1; ++ } ++ ++ /* make executable */ ++ chmod(filename, 0755); ++ ++ /* first run: count elements */ ++ count = 0; ++ cur = first; ++ while (cur != NULL) { ++ cur = cur->next; ++ count++; ++ } ++ eraseblocks = count; ++ ++ erase_counts = malloc(eraseblocks * sizeof(*erase_counts)); ++ if (!erase_counts) { ++ perror("out of memory"); ++ exit(EXIT_FAILURE); ++ } ++ ++ memset(erase_counts, 0, eraseblocks * sizeof(*erase_counts)); ++ ++ /* second run: populate array to sort */ ++ count = 0; ++ cur = first; ++ while (cur != NULL) { ++ erase_counts[count] = be64_to_cpu(cur->ec.ec); ++ cur = cur->next; ++ count++; ++ } ++ qsort(erase_counts, eraseblocks, sizeof(*erase_counts), ++ (void *)longcmp); ++ ++ /* third run: generate data file */ ++ count = 0; ++ cur = first; ++ fprintf(fpdata, "# eraseblock_no actual_erase_count " ++ "sorted_erase_count\n"); ++ while (cur != NULL) { ++ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &cur->ec, ++ UBI_EC_HDR_SIZE_CRC); ++ ++ if ((be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) || ++ (crc != be32_to_cpu(cur->ec.hdr_crc))) ++ fprintf(fpdata, "# "); ++ ++ fprintf(fpdata, "%zu %llu %llu", count, ++ (unsigned long long)be64_to_cpu(cur->ec.ec), ++ (unsigned long long)erase_counts[count]); ++ ++ if (be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) ++ fprintf(fpdata, " ## bad magic: %08x", ++ be32_to_cpu(cur->ec.magic)); ++ ++ if (crc != be32_to_cpu(cur->ec.hdr_crc)) ++ fprintf(fpdata, " ## CRC mismatch: given=%08x, " ++ "calc=%08x", be32_to_cpu(cur->ec.hdr_crc), ++ crc); ++ ++ fprintf(fpdata, "\n"); ++ ++ cur = cur->next; ++ count++; ++ } ++ fclose(fpdata); ++ ++ fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n"); ++ fprintf(fpplot, "set xlabel \"eraseblock\"\n"); ++ ++ /* fourth run: generate plot file xtics */ ++ count = 0; ++ cur = first; ++ fprintf(fpplot, "set xtics ("); ++ while (cur != NULL) { ++ if ((count % EC_X_INT) == 0) { ++ if (count > 0) ++ fprintf(fpplot, ", "); ++ fprintf(fpplot, "%zd", count); ++ } ++ ++ cur = cur->next; ++ count++; ++ } ++ fprintf(fpplot, ")\n"); ++ ++ fprintf(fpplot, "set ylabel \"erase count\"\n"); ++ fprintf(fpplot, "set xrange [-1:%zu]\n", eraseblocks + 1); ++ fprintf(fpplot, "# set yrange [-1:%llu]\n", ++ (unsigned long long)erase_counts[eraseblocks - 1] + 1); ++ fprintf(fpplot, "plot \"%s\" u 1:2 t \"unsorted: %s\" with boxes\n", ++ FN_EH_DATA, FN_EH_DATA); ++ fprintf(fpplot, "# replot \"%s\" u 1:3 t \"sorted: %s\" with lines\n", ++ FN_EH_DATA, FN_EH_DATA); ++ fprintf(fpplot, "pause -1 \"press ENTER\"\n"); ++ ++ fclose(fpplot); ++ ++ return 0; ++} ++ ++ ++/** ++ * unubi_analyze_vid_hdr - generate data table and plot script ++ * head: head of complex linked list (eb_chain) ++ * path: folder to write into ++ * generates a data file containing the volume id, logical number, leb version, ++ * and data size from the vid header; ++ * all eraseblocks listed in the eb_chain are valid (checked in unubi); ++ * also generates a simple gnuplot sript for quickly viewing one ++ * display of the data file; ++ **/ ++int ++unubi_analyze_vid_hdr(struct eb_info **head, const char *path) ++{ ++ char filename[PATH_MAX + 1]; ++ int rc, y1, y2; ++ size_t count, step, breadth; ++ uint32_t *leb_versions, *data_sizes; ++ FILE* fpdata; ++ FILE* fpplot; ++ struct eb_info *cur; ++ ++ if (head == NULL || *head == NULL) ++ return -1; ++ ++ rc = 0; ++ fpdata = NULL; ++ fpplot = NULL; ++ data_sizes = NULL; ++ leb_versions = NULL; ++ ++ /* prepare output files */ ++ memset(filename, 0, PATH_MAX + 1); ++ snprintf(filename, PATH_MAX, "%s/%s", path, FN_VH_DATA); ++ fpdata = fopen(filename, "w"); ++ if (fpdata == NULL) { ++ rc = -1; ++ goto exit; ++ } ++ ++ memset(filename, 0, PATH_MAX + 1); ++ snprintf(filename, PATH_MAX, "%s/%s", path, FN_VH_PLOT); ++ fpplot = fopen(filename, "w"); ++ if (fpplot == NULL) { ++ rc = -1; ++ goto exit; ++ } ++ ++ /* make executable */ ++ chmod(filename, 0755); ++ ++ /* first run: count elements */ ++ count = 0; ++ cur = *head; ++ while (cur != NULL) { ++ cur = cur->next; ++ count++; ++ } ++ breadth = count; ++ ++ leb_versions = malloc(breadth * sizeof(uint32_t)); ++ if (leb_versions == NULL) { ++ rc = -1; ++ goto exit; ++ } ++ memset(leb_versions, 0, breadth * sizeof(uint32_t)); ++ ++ data_sizes = malloc(breadth * sizeof(uint32_t)); ++ if (data_sizes == NULL) { ++ rc = -1; ++ goto exit; ++ } ++ memset(data_sizes, 0, breadth * sizeof(*data_sizes)); ++ ++ /* second run: populate arrays to sort */ ++ count = 0; ++ cur = *head; ++ while (cur != NULL) { ++ leb_versions[count] = be32_to_cpu(cur->vid.leb_ver); ++ data_sizes[count] = be32_to_cpu(cur->vid.data_size); ++ cur = cur->next; ++ count++; ++ } ++ qsort(leb_versions, breadth, sizeof(*leb_versions), (void *)intcmp); ++ qsort(data_sizes, breadth, sizeof(*data_sizes), (void *)intcmp); ++ ++ /* third run: generate data file */ ++ count = 0; ++ cur = *head; ++ fprintf(fpdata, "# x_axis vol_id lnum y1_axis leb_ver " ++ "y2_axis data_size\n"); ++ while (cur != NULL) { ++ y1 = norm_index(be32_to_cpu(cur->vid.leb_ver), leb_versions, ++ breadth); ++ y2 = norm_index(be32_to_cpu(cur->vid.data_size), data_sizes, ++ breadth); ++ ++ if ((y1 == -1) || (y2 == -1)) { ++ rc = -1; ++ goto exit; ++ } ++ ++ fprintf(fpdata, "%zu %u %u %u %u %u %u\n", ++ count, ++ be32_to_cpu(cur->vid.vol_id), ++ be32_to_cpu(cur->vid.lnum), ++ y1, ++ be32_to_cpu(cur->vid.leb_ver), ++ y2, ++ be32_to_cpu(cur->vid.data_size)); ++ cur = cur->next; ++ count++; ++ } ++ ++ fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n"); ++ fprintf(fpplot, "set xlabel \"volume\"\n"); ++ ++ /* fourth run: generate plot file xtics */ ++ count = 0; ++ step = 0; ++ cur = *head; ++ fprintf(fpplot, "set xtics ("); ++ while (cur != NULL) { ++ if (count > 0) ++ fprintf(fpplot, ", "); ++ if (step != be32_to_cpu(cur->vid.vol_id)) { ++ step = be32_to_cpu(cur->vid.vol_id); ++ fprintf(fpplot, "\"%zd\" %zd 0", step, count); ++ } ++ else ++ fprintf(fpplot, "\"%d\" %zd 1", ++ be32_to_cpu(cur->vid.lnum), count); ++ cur = cur->next; ++ count++; ++ } ++ fprintf(fpplot, ")\n"); ++ fprintf(fpplot, "set nox2tics\n"); ++ ++ /* fifth run: generate plot file ytics */ ++ count = 0; ++ cur = *head; ++ fprintf(fpplot, "set ylabel \"leb version\"\n"); ++ fprintf(fpplot, "set ytics ("); ++ while (cur->next != NULL) { ++ y1 = norm_index(be32_to_cpu(cur->vid.leb_ver), leb_versions, ++ breadth); ++ ++ if (y1 == -1) { ++ rc = -1; ++ goto exit; ++ } ++ ++ if (count > 0) ++ fprintf(fpplot, ", "); ++ ++ fprintf(fpplot, "\"%u\" %u", be32_to_cpu(cur->vid.leb_ver), ++ y1); ++ ++ cur = cur->next; ++ count++; ++ } ++ fprintf(fpplot, ")\n"); ++ ++ /* sixth run: generate plot file y2tics */ ++ count = 0; ++ cur = *head; ++ fprintf(fpplot, "set y2label \"data size\"\n"); ++ fprintf(fpplot, "set y2tics ("); ++ while (cur != NULL) { ++ y2 = norm_index(be32_to_cpu(cur->vid.data_size), ++ data_sizes, breadth); ++ ++ if (y2 == -1) { ++ rc = -1; ++ goto exit; ++ } ++ ++ if (count > 0) ++ fprintf(fpplot, ", "); ++ ++ fprintf(fpplot, "\"%u\" %u", be32_to_cpu(cur->vid.data_size), ++ y2); ++ ++ cur = cur->next; ++ count++; ++ } ++ fprintf(fpplot, ")\n"); ++ ++ y1 = norm_index(leb_versions[breadth - 1], leb_versions, breadth); ++ y2 = norm_index(data_sizes[breadth - 1], data_sizes, breadth); ++ fprintf(fpplot, "set xrange [-1:%zu]\n", count + 1); ++ fprintf(fpplot, "set yrange [-1:%u]\n", y1 + 1); ++ fprintf(fpplot, "set y2range [-1:%u]\n", y2 + 1); ++ fprintf(fpplot, "plot \"%s\" u 1:4 t \"leb version: %s\" " ++ "axes x1y1 with lp\n", FN_VH_DATA, FN_VH_DATA); ++ fprintf(fpplot, "replot \"%s\" u 1:6 t \"data size: %s\" " ++ "axes x1y2 with lp\n", FN_VH_DATA, FN_VH_DATA); ++ fprintf(fpplot, "pause -1 \"press ENTER\"\n"); ++ ++ exit: ++ if (fpdata != NULL) ++ fclose(fpdata); ++ if (fpplot != NULL) ++ fclose(fpplot); ++ if (data_sizes != NULL) ++ free(data_sizes); ++ if (leb_versions != NULL) ++ free(leb_versions); ++ ++ return rc; ++} ++ ++ ++/** ++ * unubi_analyze - run all analyses ++ * head: eb_chain head ++ * first: simple linked list of eraseblock headers (use .next) ++ * path: directory (without trailing slash) to output to ++ * returns 0 upon successful completion, or -1 otherwise ++ **/ ++int ++unubi_analyze(struct eb_info **head, struct eb_info *first, const char *path) ++{ ++ int ec_rc, vid_rc; ++ ++ if (path == NULL) ++ return -1; ++ ++ ec_rc = unubi_analyze_ec_hdr(first, path); ++ vid_rc = unubi_analyze_vid_hdr(head, path); ++ if (ec_rc < 0 || vid_rc < 0) ++ return -1; ++ ++ return 0; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/unubi_analyze.h linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/unubi_analyze.h +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/src/unubi_analyze.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/src/unubi_analyze.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,87 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006, 2007 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __UNUBI_ANALYZE_H__ ++#define __UNUBI_ANALYZE_H__ ++ ++/* ++ * Author: Drake Dowsett ++ * Contact: Andreas Arnez (arnez@de.ibm.com) ++ * ++ * Eraseblock Chain ++ * ++ * A linked list structure to order eraseblocks by volume and logical number ++ * and to update by version number. Doesn't contain actual eraseblock data ++ * but rather the erasecounter and volume id headers as well as a position ++ * indicator. ++ * ++ * Diagram Example: ++ * ++ * [V1.0v0]->[V1.1v2]->[V1.2v1]->[V2.0v2]->[V2.1v0]->[V2.2v1]->NULL ++ * | | | | | | ++ * NULL [V1.1v1] [V1.2v0] [V2.0v1] NULL [V2.2v0] ++ * | | | | ++ * [V1.1v0] NULL [V2.0v0] NULL ++ * | | ++ * NULL NULL ++ * ++ * [VA.BvC] represents the eb_info for the eraseblock with the vol_id A, ++ * lnum B and leb_ver C ++ * -> represents the `next' pointer ++ * | represents the `older' pointer ++ */ ++ ++#include ++#include ++#include ++ ++#define FN_EH_STAT "analysis_blocks.txt" ++#define FN_EH_DATA "analysis_ec_hdr.data" ++#define FN_EH_PLOT "analysis_ec_hdr.plot" ++#define FN_VH_DATA "analysis_vid_hdr.data" ++#define FN_VH_PLOT "analysis_vid_hdr.plot" ++ ++struct eb_info { ++ struct ubi_ec_hdr ec; ++ struct ubi_vid_hdr vid; ++ ++ fpos_t eb_top; ++ uint32_t linear; ++ int ec_crc_ok; ++ int vid_crc_ok; ++ int data_crc_ok; ++ uint32_t phys_addr; ++ int phys_block; ++ ++ struct eb_info *next; ++ struct eb_info *older; ++}; ++ ++int eb_chain_insert(struct eb_info **head, struct eb_info *item); ++ ++int eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum, ++ struct eb_info **pos); ++ ++int eb_chain_print(FILE *stream, struct eb_info *head); ++ ++int eb_chain_destroy(struct eb_info **head); ++ ++int unubi_analyze(struct eb_info **head, struct eb_info *first, ++ const char *path); ++ ++#endif /* __UNUBI_ANALYZE_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/testcases.txt linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/testcases.txt +--- linux-2.6.24.7.old/drivers/mtd/mtd-utils/ubi-utils/testcases.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtd-utils/ubi-utils/testcases.txt 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,9 @@ ++1. Start some large update, but write there byte-by-byte ++ ++2. start update for N bytes, write N-x bytes, then write y bytes, y>x. ++ You have to see that at the last write only x bytes were written, ++ but y-x bytes were not. we may vary x a bit. good number would be ++ 1, 128, 128Ki-128... ++ ++3. Try to start update for x bytes, write x bytes, then try to write more. ++ Check that it is impossible to write more. +diff -urN linux-2.6.24.7.old/drivers/mtd/mtd_blkdevs.c linux-2.6.24.7/drivers/mtd/mtd_blkdevs.c +--- linux-2.6.24.7.old/drivers/mtd/mtd_blkdevs.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/mtd_blkdevs.c 2009-04-12 18:13:57.000000000 +0200 +@@ -278,7 +278,7 @@ + + /* 2.5 has capacity in units of 512 bytes while still + having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ +- set_capacity(gd, (new->size * tr->blksize) >> 9); ++ set_capacity(gd, ((u_int64_t)new->size * tr->blksize) >> 9); + + gd->private_data = new; + new->blkcore_priv = gd; +diff -urN linux-2.6.24.7.old/drivers/mtd/mtdblock-jz.uu linux-2.6.24.7/drivers/mtd/mtdblock-jz.uu +--- linux-2.6.24.7.old/drivers/mtd/mtdblock-jz.uu 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtdblock-jz.uu 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,535 @@ ++begin 644 mtdblock-jz.o ++M?T5,1@$!`0````````````$`"``!```````````````T-0```1``4#0````` ++M`"@`(P`@`````````````````#P`@XR`*`4`'`!BC.K_!"0A**(```"CC/]_ ++M`CS__T(T`P!A!"008@```,*L(2````@`X`,A$(````"#C```HHP``(*L"`#@ ++M`P``HZS8_[TG(3B@`"``M*\AH(``)`"(CAP`LZ\AF,``0#`'`,`0!P"`(`<` ++M*AAF`B0`OZ\8`+*O%`"QKQ``L*\@`(F.`0#*)"$02`!$`&`4(2"(````@XP` ++M`$*,`"D#```A`@"`&`,`@!`"`",@@@`C**,`(2")`"$HJ0```(*,``"CC"L0 ++M8@`U`$`0(8C``(`0!P"`(`H`(9`"`8`8$0`J$&H"(2"(``\`0!0A&&@```"" ++MC```8XP`(0(``"D#`(`0`@"`&`,`(R""`",HHP`A((D`(2BI````@HP``*., ++M*Q!B``N(0@$?`"<2@(`1`"&`$`$``$*.```#C@`A`@``*0,`@!`"`(`8`P`C ++M(((`(RBC`"$H)0$A("0!"`"$)`X```P(`*4D(2!``@X```PA*``"(3@@`D`P ++M!P`D`(B.P!`'`(`@!P`J&&8"(`")C@$`RB0A$$@`OO]@$"$@B``V```((8C@ ++M`"0`OX\@`+2/'`"SCQ@`LH\4`+&/$`"PCP@`X`,H`+TGR/^])RP`MZ\T`+^O ++M,`"^KR@`MJ\D`+6O(`"TKQP`LZ\8`+*O%`"QKQ``L*\\`)6,`!$%`"``M(Z` ++M*`4`(Q!%`"$05``(`%*,``"GCB0`OHXJ$$<"*0!`$"&X0`(`$1(`@"@2`",P ++M10`A&(8"#`!DC```8HPK$((`(`!`$`P`TR2=```(([#R`"``HHXA$&("#`!$ ++MC```0XPK&(,`%P!@$`P`/*`"VCR0`M8\@`+2/'`"SCQ@` ++MLH\4`+&/$`"PCP@`X`,X`+TG```#/`@`X`/8`&*,```#/`@`X`/<`&*,Z/^] ++M)Q``L*\4`+^O````#"&`@``A(``"%`"_CQ``L(\````(&`"])^#_O2<0`+"O ++M(8"@`!0`L:\A*```(8B```@`!B08`+^O````#"$@``($``,D$``")`$``J(` ++M``.B(``CCB$0``""&0,``@`#IA@`OX\4`+&/$`"PCP@`X`,@`+TGX/^])QP` ++MOZ\4`+&O&`"RKQ``L*\``(*,(8B``*@`0XP4`%*,4`!DC%0`<(P!``@D(X`$ ++M`@2`"`+__Q`R)``DCD*2$@`(`":N!``EKB$P``(,`">N+``HKO__4C(````, ++M(2@```(P$G(H`"2.'`"_CQ@`LH\4`+&/$`"PCR$H```````((`"])XC_O2=P ++M`+^O;`"WKV@`MJ]D`+6O8`"TKUP`LZ]8`+*O5`"QKU``L*\\`(.,``"3C``` ++M:(RH`'*.`0`")0(`0B@@`&>,5`!&CE``1(X9`$`4)`!CC",0Q``!`!0D!!!4 ++M`/__0C`A,&``__]5)"$@```8`+8G```7/`0`T(P!`(0D@!`0```9$``C&&(` ++M(8CC``0`(I(!`$,P`@!",`,`0!0$`,8D$`!@%`````#R_X@4`````/3_`R1P ++M`+^/;`"WCV@`MH]D`+6/8`"TCUP`LX]8`+*/5`"QCU``L(\A$&``"`#@`W@` ++MO2<``+"L(2#``B$H```````,.``&),P`Y28,``(D(1@```(`!"0P`**O-`"C ++MKT@`I:\8`+2O0`"DKU0`2([#'Q``)S`(`$(X$`!0`$2.!CC'``08`P$@``8Q ++M!!`0`248XP`+&$8`!""5``L0!@`A,$0`PR\$`!``MJ\K0,(`(3AE`$@`8HXA ++M.`O>`"VKW0`M:]P`+2O;`"SKV@`LJ]D`+&O8`"PKP```CP,`)&,)`!"C!P` ++MD(Q7`$`0J``SCAT``A(```(\*`!")(8"``@!``0D&``#$@```````$.,`0"$ ++M)/O_8!0$`$(D```#/```8HP?`$`0```2/```8B0$`$*,#`!`$``````7``(2 ++M```"/`@`0B29`@`(`0`$)!0``Q(``!(\``!#C`$`A"3[_V`4!`!")"$0``"$ ++M`+^/@`"^CWP`MX]X`+:/=`"UCW``M(]L`+./:`"RCV0`L8]@`+"/"`#@`X@` ++MO2.)R`&`$`8"0`$&(,`!B#'`"4@9``@`,@P!BC)``L@J``A$(("```$K@`` ++M0XPC$(,`"``#K@\`0!P$``*N```$/`````P``(0D```%C@@`!HX```0\```` ++M#```A"0```0\````#```A"0```2.__^")`0``JX$``*.#``$KM"`!32`(`(` ++M````#!```JX```2.'``"K@`1!`"`(`0`(R!$``````S0``4D```$CB```JZ` ++M(`0`!`"$)`````S0``4D(1A``!P``HY;_T`0)``#KB```HX5_T`0]/\")!/_ ++M8!`AD```4`"@KR&8```\`+"N```1CB&P0`(J$%$"5`!`$%@`LJ]"*!(`(8@` ++M`%P`I:]4`**/7`"ECU0`1(P8`*,G$`"CKR<8!``&&&4`!#B3`"``A3`E.&<` ++M!#"2`$@`XHX+.,4`(2#@`@GX0``+,`4````"/,P`1"3,`$.,!`""C`8`8A`! ++M`#$F````#``````J$"("Y_]`%%0`HH\@``*.4`"ECP$``R0A$*(`!`!#H``` ++M`CS,`$(D"`!#C/__`B1F`&(04`"DCR```HXA$((```!#K"0``XZ`$!(`(1!# ++M``0`5JQ0`*6/(``#C@$`PB8A&*,`"`!BK%0`HH]<`*6/5`!$C(``XHXG&`0` ++M!AAE``0XDP`@`(4P!#"2`"4X9P`+.,4````#/`LP!0`A(.`""?A``,P`<8Q# ++M`$`06`"FCU@`I8\K`@`,(2"@`@$`0B8K(%(`(1B3`%``I(\AD$``#`"$)"&8 ++M8`"/`P`(4`"DK\(7$0`\`+*.(1!1`$.``@`@`%:."```&B0`4XXA*``"(2!` ++M`O__$"83```,(3`@`OO_`!8A*``"`@`B*AL`0!2`$!$`(8!3``$`%R0$`',F ++M```#C@``8HX`*0,``"$"`(`8`P"`$`(`(R""`",HHP`A(,0"(2C%`@@`A"0. ++M```,"`"E)/__,28A*``"#@``#"$@8`(A($`"`0`%)!,```PA,"`"ZO\W%OS_ ++M$";__P,D3`#")P0`HZXA$(("```#/```5:S8`'6L``"DC@```CR=`@`(W`!$ ++MK"$H(`+N`0`,(2"@`M\#``@!`$(F(``"CB$0H@#``P`(``!`K'C_O2=\`+.O ++M>`"RKW``L*\<`*"O(`"@KX``OZ]T`+&O&`"@KR0`H*\H`*"O``"#CP```CP0 ++M`+,G``!")"&`@``<`*.O(`"BKR$@8`(AD*``````#*@`$8X```(\*!9")&`` ++MHJ\P`+"O5``GCL,?$@`G*`<`0B`2``8@I``$,.,`)3#$`#P`IJ]4`"2.$``% ++MC@00D@`@`.`"RCW0`L8]P`+"/"`#@`X@`O2<````,```` ++M`"$@8`(````,(2@@`B$0``*``+^/?`"SCW@`LH]T`+&/<`"PCP@`X`.(`+TG ++MX/^])QP`OZ\8`+*O%`"QKQ``L*\D`*6O``"0C`0`A8PAD(``OP$`#*@`$8XD ++M`*6/*P(`#"$@0`(D`*6/)@0`#"$@``(```4\```$/```I20F`$`4``"$)%0` ++M)(XD`**/(`"%,"$P0`##/P(`0A@"`"<0!``&&$,`!#B'`"4X9P`$,(8`A``" ++MC@LXQ0`A(``""?A```LP!0`A($`""`$`#"0`I2<```0\```%/```A"0#`$`0 ++M``"E)`````P`````!`!%CB0`IH_N`0`,(2!``B0`HH\<`+^/&`"RCQ0`L8\0 ++M`+"/"`#@`R``O2<````,)`"FCYD$``A4`"2.@/^])W``MJ]\`+^O>`"^KW0` ++MMZ]L`+6O:`"TKV0`LZ]@`+*O7`"QKU@`L*^``*2O``"3C#P`@HRH`'6.$`!D ++MCH0`I:\@`$*,T``%)%``HJ]4`+&.````#%``L(ZD`$`0(;!``!@`I"/<`"VCVP`M8]H`+2/9`"SCV``LH]<`+&/6`"PCP@` ++MX`.``+TG9`4`"```A"1\`+^/>`"^CW0`MX]P`+:/;`"UCV@`M(]D`+./8`"R ++MCUP`L8]8`+"/]/\")`@`X`.``+TG-`"$C`,`!20!``8D````""$X```@_[TG ++MT`"VK[P`L:_<`+^OV`"^K]0`MZ_,`+6OR`"TK\0`LZ_``+*ON`"PKP``@HP! ++M`!$DK`"BKZ@`0XP\`(6,J`"CKU0`8HQ0`&.,(`"EC",00P`$$%$`__]","&P ++M@``L`(2,I`"EKZ``HJ\X`,6.#@"`%)P`I:_<`+^/V`"^C]0`MX_0`+:/S`"U ++MC\@`M(_$`+./P`"RC[P`L8^X`+"/(1````@`X`/@`+TG&`"D)R$H```````, ++M.``&)*P`J(\8`+&O%``$C0P``B0``-6.,`"BKP```CP@`*2O(1@```(`!"3, ++M`$(D-`"CKT``I*\D`*"O2`"BKZ@`HXX4`**.E`"CKS@`Q(Z4`*6/F`"DKT(2 ++M`@#__UC@(0T'/__Q`FC`"BK[``L*^(`*"O ++MB`"CCR0`PH[__W,P(1!3````0Y"B`&`0E`"DCQ$`X!("$'=RD`"BKY``HX\H ++M`,*.(2!#````@Y`,`6`0E`"EC_\%``@AD`````!"D`@!0!"4`*6/`0!")O__ ++M4C#Z__(6(1"2`+``HX^(`*2/__]B,`$`A"0!`$(DX?]$%(@`I*\(`->.(8@` ++M`(`@%P``&1<`I`"ECR.09``A@+(`!``"D@0`0C!D`$`0`````/G_(`:D`*6/ ++M``#4CCP`QXZH`)..`0`1)%``8XY4`&*.4`"D)R,00P`$$%$`(2@``#@`!B0@ ++M`/",````#/__7C!0`+&O```(/!0`AXX```@E#``")```!(UH`**O```"/"$8 ++M``#_``4D6`"GK\P`0B0"``O7`"@KP````R``**O!`## ++MC@``!#S,`(.L!`#"CLP`A20A@%`"!`"BK````XZ9`,`;"`"CK"&`X`+#CQ<` ++M(9```$D&``A"J!<`D@#2$P````!4`&>.4`!HCB_T`4__\1)*P`I(\F!``,(2C@`B&(0``` ++M``*.(2#``@$`0B0```*N=```#"$HX`*4_R$&(2C@`H($``PA(,`"```$/``` ++M!3PA,.`"``"$)`@`PJX``*4D````#"$X0``(`->.@"`7``X&``@`&1<`C`"E ++MCU``AXPA$+,`(1@``$(H`@`G(`<`!BB%``2(XP`@`.8P!(#B`"6(L0`+B`8" ++MIP8`"`N`!@!(`$*.`0!")`4`0R@F`&`02`!"KI0`J(]0``>-(`#"C@08\P`A ++M$$,`?`"BKU``HB<0`**O2`"BCB$P``(A."`""?A``"$@H`(``!0\```$/``` ++MA"0``(4F(3#``Q<`0!`A.&`"```#/$@`8HPAD&``````#!``HJ\,`,.."`#" ++MC@``!#S>_V(4``"$)`````P`````2`!"C@$`0B0%`$,HW/]@%$@`0JX```0\ ++M``"$)```A28````,(3#``P``!#RP`*./2`"`K(@`I(___V(P`0"$)`$`0B03 ++M_T04B`"DKPL&``@(`->.````#`````"!`$`0H`"BCW\`0!@"$%=P(8!``*`` ++MHH_F!@`((9`"`GD`$A(`````J`"CCYP`I8]0`&2,&`"H)Q``J*\G$`0`K`"H ++MCT(8$``A.```1`"EKP880P`$.(<`(`"%,`0PD``E.&<`2``"C0LXQ0`A(``! ++M"?A```LP!0`!`!`FZ/]`$"&(0``A*.`"@@0`#"$@P`(AN$``@"`"```9`@`. ++M!@`("`#"KB&0``",`*B/4`"FC"$0:`(A&```0B@"`"<@!@`&*(4`!(C#`"`` ++MQS`$@,(`)8BQ``N(!P(9!P`("X`'`$0`@HX!`$(D!0!#*"(`8!!$`(*NF`"B ++MCU``HR=\`**O$`"CKT@`HHXA,``"(3@@`@GX0``A(*`"```%/```"#P``*0D ++M(3#``P``!248`$`0(3A@`@```SQ$`&*,(:!@``````P0`**O#`##C@@`PHX` ++M``4\XO]B%```I"0````,`````$0`@HX!`$(D!0!#*.#_8!1$`(*N```$/``` ++M"#P``(0D```%)0````PA,,`#```#/"L05P((`$`41`!@K`0&``BP`*./`0!" ++M)O__4C`K&%<"NOY@$+``HX\H`,*.D`"DCY@`J(\A$((`(1!2````0Y!`.A(` ++M(2@'`?+_8!0``@8DE`"BCU``1(P@`,*.!""3`"$@AP`````,(2""`$<'``@! ++M`$(F#`#0C@@`PHX8``(24`"D)P``T8X\`,*.(2@``#@`!B0@`%*,````#*@` ++M,XX,``(D`0`&)&@`HJ\```(\4`"FKR$8```"``8DS`!")"$@(`(A*``";`"C ++MKW@`IJ\F!``,@`"BKR(`0!````,\``#"C@$`$"2H`$.,%`!1C%``9(Q4`&*, ++M0HH1`",01``$@%``__\0,B0`Q(XA,``"__\Q,BP`P*X````,(2@```(P$7(H ++M`,2.````#"$H``#<`+^/V`"^C]0`MX_0`+:/S`"UC\@`M(_$`+./P`"RC[P` ++ML8^X`+"/(1````@`X`/@`+TG``!C)```9(S_``4D````#`P`!B2`&!```!$0 ++M`",00P`A$$("``!#C```!#P!`&,DS`"$)```0ZP(`(.L5`!DCE``I2O ++M=`"UKW``M*]L`+.O:`"RKV``L*^$`+^O@`"^KW@`MJ]D`+&O``"1C"&@@``4 ++M`"*.J``VCD(2`@#__T0P&P"D`/0!@``!`!OD`"FKQ*H``!)`*,2$(`` ++M`!@`OB>Q"``((`"W)P````P`````&P!R`O0!0`(!``(D(`"BKQ0`(XX8`**/ ++M,`"'CC0`A8XH`*.O(2`@`BP`H*\0,````AA"/>`"VCW0`M8]P`+2/;`"SCV@`LH]D ++M`+&/8`"PCR$0```(`.`#B`"])P(8D'`H`(*.6`"DCR$08@`A$$0```!#D"H` ++M8!`@`+,G0!($`"``@XX$*+``(2BB`"$HHP`````,(2#@`(0`OX^``+Z/?`"W ++MCW@`MH]T`+6/<`"TCVP`LX]H`+*/9`"QCV``L(\A$```"`#@`X@`O2. ++M-`"0KE@`I(^0`*:/0"H$`)0`I(\````,(2BG`(0`OX^``+Z/?`"WCW@`MH]T ++M`+6/<`"TCVP`LX]H`+*/9`"QCV``L(\A$```"`#@`X@`O2"5X("T^ ++M($Y53$P*`&1R:79EF]N95]P ++M='(@:7,@;G5L;`H```!Z;VYE7W!T"`*`````"5S.B!W"`*`````"5S.B!F:6QL ++M7V)L;V-K,2!P:'ES7V)L;V-K.B5D+'!A9V4Z)60L7-?8FQO ++M8VL*````)7,Z(')E861I;F<@97)R;W(@=VAE;B!M;V1I9GEI;F<@<&AY"5X(&5R87-I ++M;F<@9F%I;&5D+"!M87)K960@8F%D+"!A;F0@9FEN9"!N97<@8FQO8VL@,'@E ++M>`H`````)7,Z('!H>7-?8FQO8VL@,'@E>"!P````!T````?````]`4`````#X#X____```````` ++M```H````'0```!\```#\!@``````@/C___\``````````!@````=````'P`` ++M`+@'``````>`_/___P``````````(````!T````?````K`@``````X#X____ ++M```````````@````'0```!\```"X"0````#_P/S___\``````````(@````= ++M````'P```)@0``````^`^/___P``````````B````!T````?````"!(````` ++M!X#\____```````````@````'0```!\````$$P````#_P/S___\````````` ++M`(`````=````'P```"@6`````````````````````````````!T````?```` ++M````````````````````````````````'0```!\```````````#_P/S___\` ++M`````````.`````=````'P```/@>``````.`^/___P``````````(````!T` ++M```?````;!\`````#X#X____```````````H````'0```!\````````````# ++M@/C___\``````````"`````=````'P```'@A`````/_`_/___P`````````` ++MB````!T````?`````````````8#\____```````````8````'0```!\```!4 ++M)0`````````````````````````````=````'P```'@E`````/^`^/___P`` ++M````````0````!T````?`````````````(#X____```````````8````'0`` ++M`!\```"D)P`````````````````````````````=````'P````!'0T,Z("A' ++M3E4I(#0N,2XR```N6UT86)?9W!L ++M`"YR96Q?7VMC"\``"0```````````````0````````` ++MVP````D```````````````Q)```P````(0```!<````$````"````.L````! ++M`````P````````"<+P``&P```````````````0````````#V````"`````,` ++M````````P"\``/```````````````!``````````^P````@````#```0```` ++M`,`O```````````````````$``````````4!```!``````````````#`+P`` ++M0`0`````````````!``````````!`0``"0``````````````/$D``!`!```A ++M````'`````0````(````"@$```$````````````````T```````````````` ++M```!`````````!@!```!````````````````-```$@```````````````0`` ++M```````1`````P``````````````$C0``"$!``````````````$````````` ++M`0````(``````````````$Q*``"0"@``(@```(8````$````$`````D````# ++M``````````````#<5```@`@``````````````0````````!H`0``!`$``'0! ++M```$`0``J`$```0!``!0`@``!`$``*@"```$`0``M`(```0!``#\`@``!1H` ++M``0#```&&@``"`,```4:```0`P``!AH``"`#```$J```-`,```2+``!<`P`` ++M!)D``/0#```$F0``&`0```29```4!0``!)D``!P%```&&@``F`4```8:``#L ++M!0``!`$```P&```%HP``%`8```:C``````0!0```9X``!$%```!)<``+`2```%:```2!0```9H``!P%``` ++M!`$``(`4```%>0``A!0```9Y```\%```!6@``(P4```&:```D!0```27``"H ++M%```!`$``+@4```$`0``Z!0```0!```H%0``!`$``(@5```%>@``C!4```9Z ++M``"4%0``!)<``'P4```%:```F!4```9H``"@%0``!`$``*@5```$`0``L!4` ++M``2+``#L%0``!`$``/04```%>P``\!4```9[```T%@``!(X``/`6```$F0`` ++M$!<```4:```@%P``!AH``'07```$F0``Z!<```0!``",&```!)D``)@8```% ++M?```H!@```9\``"P&```!1H``,`8```&&@``V!@```29``#D&```!1H``.@8 ++M```&&@``E`0```4:``#P&```!AH``!09```$`0``D!D```0!``"8&0``!7T` ++M`*09```&?0``K!0```5H``"H&0``!F@``+`9```$EP``Y!D```0!````&@`` ++M!`$``!`:```$`0``&!H```5^```D&@``!GX``)P9```%:```+!H```9H```P ++M&@``!)<``$`:```$`0``>!H```0!``"`&@``!AH``)`:```&&@``S!H```5_ ++M``#0&@``!G\``!P:```%:```U!H```9H``#D&@``!1H``.@:```&&@``\!H` ++M``27````&P``!8````@;```&@```#!L```27```4&P``!AH``"0;```&&@`` ++M*!L```6!```L&P``!H$``,@:```%:```,!L```9H```T&P``!)<``#P;```% ++M&@``1!L```8:``!@&P``!`$``&@;```$E0``B!L```0!``#X&P``!`$```P< ++M```$`0``2!P```0!``!0'```!AH``&`<```&&@``B!P```6"``"0'```!H(` ++M`(P<```%:```F!P```9H``"D'```!1H``*@<```&&@``L!P```27``#`'``` ++M!8```,@<```&@```S!P```27``#4'```!AH``.0<```&&@``Z!P```6!``#P ++M'```!H$``.P<```%:```]!P```9H``#X'```!)<````=```%&@``#!T```8: ++M```0'0``!`$``&@=```$B@`````$F0``+!X```29``#@'0``!7P``&@>```&?``` ++M=!X```29``"0'@``!1H``)@>```&&@``Z!X```0!``#P'@``!`$```P?```% ++M&@``$!\```8:```D'P``!)8``"P?```$G@``-!\```2=``"$'P``!1H``(@? ++M```&&@``G!\```0!``"8'P``!1H``*`?```&&@``N!\```4:``"\'P``!AH` ++M`,0?```&&@``W!\```0!``#8'P``!1H``.`?```&&@``&"````4:```@(``` ++M!AH``#0@```$E@``/"````2>``!$(```!)T``'0@```$BP``?"````2+``"$ ++M(```!(L``(P@```$BP``E"````2+``"<(```!(L``*0@```$BP``K"````2+ ++M``"T(```!(L``+P@```$BP``Z"````4:``#L(```!AH``/@@```%@P``_"`` ++M``:#````(0``!)<``/0@```%#P``!"$```8/```((0``!`$``"@A```$E@`` ++M,"$```2>```X(0``!)T```PB```$`0``%"(```29``"4(@``!6@``*`B```& ++M:```F"(```6$``"D(@``!H0``)PB```%#P``K"(```8/``"P(@``!)<``+PB ++M```$`0``S"(```0!``#L(@``!)D``%PC```$B@``L",```2*``#T(P``!)D` ++M`(@D```$B@``S"0```0!``#4)```!`$``-PD```$E@``Y"0```2>``#L)``` ++M!)T``/0D```%:````"4```9H``#X)```!80```0E```&A```""4```27``#\ ++M)```!0\```PE```&#P``%"4```0!```<)0``!`$``#@E```$`0``6"4```4: ++M``!@)0``!AH``'`E```$`0``^"4```0!```4)@``!`$``"`F```$`0``/"8` ++M``0!``!,)@``!`$``)0F```$B@``V"8```26``#@)@``!)X``.@F```$G0`` ++M]"8```0!````)P``!`$```PG```%A0``+"8```6%```4)P``!H4``!@G```$ ++MEP``$"<```5H```<)P``!F@``"`G```$`0``+"<```26```T)P``!)X``#PG ++M```$G0``2"<```0!``!@)P``!`$``&@G```%A0``;"<```:%``!P)P``!)<` ++M`%0G```%:```="<```9H``!X)P``!`$``(PG```$`0``J"<```4:``"P)P`` ++M!AH``,`G```$`0``$`````4:```4````!AH``"`````%&@``*`````8:```P ++M````!*```%@````%&@``8`````8:``!H````!*```)0````$FP``D`````41 ++M``"8````!A$```0````$D0````````41```(````!A$````````"&@`````` ++M``*4```$`````A````@````"H0``#`````(0```0`````HD``!0````"$``` ++M&`````*0```<`````A```"`````"B```)`````(0`````````J4```0````" ++MI@``"`````*?```,`````HT``!`````"AP````````((```4`````@$``!@` ++M```"`0``'`````(!```@`````@$``"0````"`0``*`````(!```L`````@$` ++M`#`````"`0````````(%`````````@,````````"&0``!`````(#```,```` ++M`AD``!`````"`P``&`````(9```<`````@,````````"`0``(`````(!``!` ++M`````@$``&`````"`0``@`````*)``"@`````J$``,`````"`P``X`````(# ++M`````0```@,``"`!```"`P``0`$```(!``!@`0```@$``(`!```"`0``H`$` ++M``(!``#``0```@$``.`!```"`0````(```(!```@`@```@$``$`"```"`0`` ++M8`(```(!``"``@```@$``*`"```"`0``P`(```(!``#@`@```@4````#```" ++MG@``(`,```(!``!``P```@$``&`#```"E```@`,```(!``"@`P```H@``,`# ++M```"`0``X`,```(!````!````I```"`$```"`0`````````````````````` ++M``````````````````,``0`````````````````#``(````````````````` ++M`P`#``````````````````,`!``````````````````#``4````````````` ++M`````P`&``````````````````,`!P`````````````````#``@````````` ++M`````````P`)``````````````````,`"@`````````````````#``L````` ++M`````````````P`,``````````````````,`#0`````````````````#``X` ++M`````````````````P`/``````````````````,`$``````````````````# ++M`!$``````````````````P`2``````````````````,`$P`````````````` ++M```#`!0``````````````````P`5``````````````````,`%@`````````` ++M```````#`!<``````````````````P`8``````````````````,`&0`````` ++M```````````#`!H``````````````````P`;``````````````````,`'``` ++M```````````````#`!T``````````````````P`>``````````````````,` ++M'P`````````````````#`"```````````````````P`A```````````````` ++M``,`(@`!```````````````$`/'_#P`````````X`````@`!`"H````X```` ++M%`````(``0`X````3````(0!```"``$`3````-`!```L`0```@`!`&4```#8 ++M````!`````$`&@!R````W`````0````!`!H`?``````````@`````@`#`(P` ++M``!`````!`````$`&@"6````(````#@````"``,`I````"`````@`````0`: ++M`*T```!8````.`````(``P"Z`````````"`````!`!H`P@```)`````,```` ++M`@`#`-``````````3`````$`$0#<````%`,``"@````"``$`\````#P#``!< ++M`````@`!```!``"8`P``B`````(``0`;`0``(`0``-0!```"``$`-`$``,P` ++M```,`````0`:`#T!``#T!0``"`$```(``0!.`0``_`8``+P````"``$`<`$` ++M`+@'``#T`````@`!`)`!```D````(`````$`#P"?`0``K`@```P!```"``$` ++MP0$``+@)``#@!@```@`!`,\!``#@````!`````$`&@#:`0``3````(`````! ++M`!H`X@$``!0````.`````0`/`/$!``````````````$`&P#]`0``F!```'`! ++M```"``$`"0(``"@6```4`````@`!`!@"```($@``_`````(``0`X`@``!!,` ++M`"0#```"``$`5P(````````,`````@`%`&@"``!(````!`````$`&@!Z`@`` ++M1`````0````!`!H`C`(``/@>``!T`````@`!`)L"``!L'P``I`$```(``0"L ++M`@```````!$````!``\`NP(``'@A``"L`P```@`!`,H"``!$````#P````$` ++M#P#9`@``5"4``"0````"``$`ZP(``'@E```(`@```@`!`/L"``"D)P``)``` ++M``(``0`.`P````````0````!`!,`*@,````````$`````0`5`$0#```````` ++M#`````$`%P!<`P````````H````!`!D`>`,```P````,`````0`7`(X#```* ++M````"0````$`&0"H`P``&`````P````!`!<`O0,``!,````(`````0`9`-8# ++M````````"`````$`"P#P`P```````!`````!`!``"@0````````$`````0`- ++M`"0$```(````"`````$`"P`Z!```$`````P````!`!``4`0```0````$```` ++M`0`-`&8$```0````"`````$`"P!_!```'`````\````!`!``F`0```@````$ ++M`````0`-`+$$```8````"`````$`"P#2!```+````!<````!`!``\P0```P` ++M```$`````0`-`!0%```@````"`````$`"P`T!0``1````!8````!`!``5`4` ++M`!`````$`````0`-`'0%````````````````"`!Y!0``+`````````````@` ++M?@4``$@````````````(`(,%``!@````````````"`"(!0``=``````````` ++M``@`C04``)0````````````(`)(%``#4````````````"`"7!0``,`$````` ++M``````@`G`4```P!```````````(`*$%``!(`0``````````"`"F!0``8`$` ++M``````````@`K`4``'@!```````````(`+(%``"D`0``````````"`"X!0`` ++M[`$```````````@`O@4``#`"```````````(`,0%``!,`@``````````"`#* ++M!0``>`(```````````@`T`4``)P"```````````(`-8%``#,`@`````````` ++M"`#BYC`&UT9&)L;V-K7V%D9')E0!M=&1B;&]C:U]L:69E=&EM95]R97-O0!?7V9U;F-?7RXQ-3,T.0!M ++M=&1B;&]C:U]B;&]C:U]I;F9O7VUA<%]B861?8FQO8VL`;71D8FQO8VM?;W!E ++M;@!M=&1B;&M?:61X`&UT9&)L:W,`7U]F=6YC7U\N,38Q.#,`7U]K97DN,38P ++M-3,`97)A&ET8V%L;%]C;&5A;G5P7VUT9&)L;V-K`%]?:6YI=&-A;&Q? ++M:6YI=%]M=&1B;&]C:S8`7U]S971U<%]M=&1B;&ML;V=?6UT86)?=61C7V9L=7-H ++M7V-A8VAE`%]?:W-T6UT86)?=61C7V=E=%]M=&0`7U]K6UT86)? ++M=61C7V=E=%]M=&1B;&L`7U]K6UT86)?=61C7VUT9&)L;V-K7W=R ++M:71E6UT86)?=61C7VUT ++M9&)L;V-K7W)E861S96-T`%]?:W-T%]L;V-K`'!R:6YT:P!?7VUU=&5X7VEN:70` ++M;65M + * (C) 1999-2003 David Woodhouse +@@ -15,7 +15,7 @@ + #include + #include + #include +- ++#include + #include + #include + #include +@@ -361,12 +361,27 @@ + kfree(dev); + } + ++ ++static int mtdblock_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) ++{ ++ struct gendisk *gd = dev->blkcore_priv; ++ memset(geo, 0, sizeof(*geo)); ++ geo->heads = 4; ++ geo->sectors = 16; ++ geo->cylinders = dev->size/(4*16); ++ ++ printk("cylinders: %x \n", geo->cylinders); ++ printk("sects: %x\n", dev->size); ++ return 0; ++} ++ + static struct mtd_blktrans_ops mtdblock_tr = { + .name = "mtdblock", + .major = 31, + .part_bits = 0, + .blksize = 512, + .open = mtdblock_open, ++ .getgeo = mtdblock_getgeo, + .flush = mtdblock_flush, + .release = mtdblock_release, + .readsect = mtdblock_readsect, +diff -urN linux-2.6.24.7.old/drivers/mtd/mtdchar.c linux-2.6.24.7/drivers/mtd/mtdchar.c +--- linux-2.6.24.7.old/drivers/mtd/mtdchar.c 2009-04-12 18:05:07.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/mtdchar.c 2009-04-12 18:13:57.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * $Id: mtdchar.c,v 1.76 2005/11/07 11:14:20 gleixner Exp $ ++ * $Id: mtdchar.c,v 1.1.1.1 2008-03-28 04:29:21 jlwei Exp $ + * + * Character-device access to raw MTD devices. + * +@@ -7,7 +7,6 @@ + + #include + #include +-#include + #include + #include + #include +@@ -83,8 +82,6 @@ + return -EINVAL; + } + +- +- + static int mtd_open(struct inode *inode, struct file *file) + { + int minor = iminor(inode); +@@ -137,8 +134,7 @@ + + DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); + +- /* Only sync if opened RW */ +- if ((file->f_mode & 2) && mtd->sync) ++ if (mtd->sync) + mtd->sync(mtd); + + put_mtd_device(mtd); +@@ -157,7 +153,7 @@ + { + struct mtd_file_info *mfi = file->private_data; + struct mtd_info *mtd = mfi->mtd; +- size_t retlen=0; ++ size_mtd_t retlen=0; + size_t total_retlen=0; + int ret=0; + int len; +@@ -251,7 +247,7 @@ + struct mtd_file_info *mfi = file->private_data; + struct mtd_info *mtd = mfi->mtd; + char *kbuf; +- size_t retlen; ++ size_mtd_t retlen; + size_t total_retlen=0; + int ret=0; + int len; +@@ -536,7 +532,7 @@ + { + struct mtd_oob_buf buf; + struct mtd_oob_ops ops; +- ++ + if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) + return -EFAULT; + +@@ -576,6 +572,73 @@ + break; + } + ++ case MEMWRITEPAGE: ++ { ++ struct mtd_page_buf buf; ++ struct mtd_oob_ops ops; ++ ++ memset(&ops, 0, sizeof(ops)); ++#if 1 ++ if(!(file->f_mode & 2)) ++ return -EPERM; ++#endif ++ ++ if (copy_from_user(&buf, argp, sizeof(struct mtd_page_buf))) ++ return -EFAULT; ++ ++ if (buf.ooblength > mtd->oobsize) ++ return -EINVAL; ++ ++ if (!mtd->write_oob) ++ ret = -EOPNOTSUPP; ++ else ++ ret = access_ok(VERIFY_READ, buf.oobptr, ++ buf.ooblength) ? 0 : EFAULT; ++ ++ if (ret) ++ return ret; ++ ++ ops.len = mtd->writesize; ++ ops.ooblen = buf.ooblength; ++ ops.ooboffs = buf.start & (mtd->oobsize - 1); ++ ops.mode = MTD_OOB_PLACE; ++ ++ if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) ++ return -EINVAL; ++ ++ /* alloc memory and copy oob data from user mode to kernel mode */ ++ ops.oobbuf = kmalloc(buf.ooblength, GFP_KERNEL); ++ if (!ops.oobbuf) ++ return -ENOMEM; ++ ++ if (copy_from_user(ops.oobbuf, buf.oobptr, buf.ooblength)) { ++ kfree(ops.oobbuf); ++ return -EFAULT; ++ } ++ ++ /* alloc memory and copy page data from user mode to kernel mode */ ++ ops.datbuf = kmalloc(mtd->writesize, GFP_KERNEL); ++ if (!ops.datbuf) ++ return -ENOMEM; ++ ++ if (copy_from_user(ops.datbuf, buf.datptr, mtd->writesize)) { ++ kfree(ops.datbuf); ++ return -EFAULT; ++ } ++ ++ buf.start &= ~(mtd->oobsize - 1); ++ ret = mtd->write_oob(mtd, buf.start, &ops); ++ ++ if (copy_to_user(argp + 2*sizeof(uint32_t), &ops.retlen, ++ sizeof(uint32_t))) ++ ret = -EFAULT; ++ ++ kfree(ops.oobbuf); ++ kfree(ops.datbuf); ++ break; ++ } ++ ++ + case MEMLOCK: + { + struct erase_info_user info; +@@ -627,9 +690,9 @@ + + case MEMGETBADBLOCK: + { +- loff_t offs; ++ loff_mtd_t offs; + +- if (copy_from_user(&offs, argp, sizeof(loff_t))) ++ if (copy_from_user(&offs, argp, sizeof(loff_mtd_t))) + return -EFAULT; + if (!mtd->block_isbad) + ret = -EOPNOTSUPP; +@@ -640,9 +703,9 @@ + + case MEMSETBADBLOCK: + { +- loff_t offs; ++ loff_mtd_t offs; + +- if (copy_from_user(&offs, argp, sizeof(loff_t))) ++ if (copy_from_user(&offs, argp, sizeof(loff_mtd_t))) + return -EFAULT; + if (!mtd->block_markbad) + ret = -EOPNOTSUPP; +@@ -764,9 +827,9 @@ + #endif + + default: ++ printk("line : %d\n", __LINE__); + ret = -ENOTTY; + } +- + return ret; + } /* memory_ioctl */ + +diff -urN linux-2.6.24.7.old/drivers/mtd/mtdchar.c.orig linux-2.6.24.7/drivers/mtd/mtdchar.c.orig +--- linux-2.6.24.7.old/drivers/mtd/mtdchar.c.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/mtdchar.c.orig 2009-04-12 18:01:55.000000000 +0200 +@@ -0,0 +1,816 @@ ++/* ++ * $Id: mtdchar.c,v 1.76 2005/11/07 11:14:20 gleixner Exp $ ++ * ++ * Character-device access to raw MTD devices. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++static struct class *mtd_class; ++ ++static void mtd_notify_add(struct mtd_info* mtd) ++{ ++ if (!mtd) ++ return; ++ ++ class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), ++ NULL, "mtd%d", mtd->index); ++ ++ class_device_create(mtd_class, NULL, ++ MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), ++ NULL, "mtd%dro", mtd->index); ++} ++ ++static void mtd_notify_remove(struct mtd_info* mtd) ++{ ++ if (!mtd) ++ return; ++ ++ class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2)); ++ class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1)); ++} ++ ++static struct mtd_notifier notifier = { ++ .add = mtd_notify_add, ++ .remove = mtd_notify_remove, ++}; ++ ++/* ++ * Data structure to hold the pointer to the mtd device as well ++ * as mode information ofr various use cases. ++ */ ++struct mtd_file_info { ++ struct mtd_info *mtd; ++ enum mtd_file_modes mode; ++}; ++ ++static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) ++{ ++ struct mtd_file_info *mfi = file->private_data; ++ struct mtd_info *mtd = mfi->mtd; ++ ++ switch (orig) { ++ case SEEK_SET: ++ break; ++ case SEEK_CUR: ++ offset += file->f_pos; ++ break; ++ case SEEK_END: ++ offset += mtd->size; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (offset >= 0 && offset <= mtd->size) ++ return file->f_pos = offset; ++ ++ return -EINVAL; ++} ++ ++ ++ ++static int mtd_open(struct inode *inode, struct file *file) ++{ ++ int minor = iminor(inode); ++ int devnum = minor >> 1; ++ struct mtd_info *mtd; ++ struct mtd_file_info *mfi; ++ ++ DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); ++ ++ if (devnum >= MAX_MTD_DEVICES) ++ return -ENODEV; ++ ++ /* You can't open the RO devices RW */ ++ if ((file->f_mode & 2) && (minor & 1)) ++ return -EACCES; ++ ++ mtd = get_mtd_device(NULL, devnum); ++ ++ if (IS_ERR(mtd)) ++ return PTR_ERR(mtd); ++ ++ if (MTD_ABSENT == mtd->type) { ++ put_mtd_device(mtd); ++ return -ENODEV; ++ } ++ ++ /* You can't open it RW if it's not a writeable device */ ++ if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) { ++ put_mtd_device(mtd); ++ return -EACCES; ++ } ++ ++ mfi = kzalloc(sizeof(*mfi), GFP_KERNEL); ++ if (!mfi) { ++ put_mtd_device(mtd); ++ return -ENOMEM; ++ } ++ mfi->mtd = mtd; ++ file->private_data = mfi; ++ ++ return 0; ++} /* mtd_open */ ++ ++/*====================================================================*/ ++ ++static int mtd_close(struct inode *inode, struct file *file) ++{ ++ struct mtd_file_info *mfi = file->private_data; ++ struct mtd_info *mtd = mfi->mtd; ++ ++ DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); ++ ++ /* Only sync if opened RW */ ++ if ((file->f_mode & 2) && mtd->sync) ++ mtd->sync(mtd); ++ ++ put_mtd_device(mtd); ++ file->private_data = NULL; ++ kfree(mfi); ++ ++ return 0; ++} /* mtd_close */ ++ ++/* FIXME: This _really_ needs to die. In 2.5, we should lock the ++ userspace buffer down and use it directly with readv/writev. ++*/ ++#define MAX_KMALLOC_SIZE 0x20000 ++ ++static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) ++{ ++ struct mtd_file_info *mfi = file->private_data; ++ struct mtd_info *mtd = mfi->mtd; ++ size_t retlen=0; ++ size_t total_retlen=0; ++ int ret=0; ++ int len; ++ char *kbuf; ++ ++ DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); ++ ++ if (*ppos + count > mtd->size) ++ count = mtd->size - *ppos; ++ ++ if (!count) ++ return 0; ++ ++ /* FIXME: Use kiovec in 2.5 to lock down the user's buffers ++ and pass them directly to the MTD functions */ ++ ++ if (count > MAX_KMALLOC_SIZE) ++ kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL); ++ else ++ kbuf=kmalloc(count, GFP_KERNEL); ++ ++ if (!kbuf) ++ return -ENOMEM; ++ ++ while (count) { ++ ++ if (count > MAX_KMALLOC_SIZE) ++ len = MAX_KMALLOC_SIZE; ++ else ++ len = count; ++ ++ switch (mfi->mode) { ++ case MTD_MODE_OTP_FACTORY: ++ ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); ++ break; ++ case MTD_MODE_OTP_USER: ++ ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); ++ break; ++ case MTD_MODE_RAW: ++ { ++ struct mtd_oob_ops ops; ++ ++ ops.mode = MTD_OOB_RAW; ++ ops.datbuf = kbuf; ++ ops.oobbuf = NULL; ++ ops.len = len; ++ ++ ret = mtd->read_oob(mtd, *ppos, &ops); ++ retlen = ops.retlen; ++ break; ++ } ++ default: ++ ret = mtd->read(mtd, *ppos, len, &retlen, kbuf); ++ } ++ /* Nand returns -EBADMSG on ecc errors, but it returns ++ * the data. For our userspace tools it is important ++ * to dump areas with ecc errors ! ++ * For kernel internal usage it also might return -EUCLEAN ++ * to signal the caller that a bitflip has occured and has ++ * been corrected by the ECC algorithm. ++ * Userspace software which accesses NAND this way ++ * must be aware of the fact that it deals with NAND ++ */ ++ if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) { ++ *ppos += retlen; ++ if (copy_to_user(buf, kbuf, retlen)) { ++ kfree(kbuf); ++ return -EFAULT; ++ } ++ else ++ total_retlen += retlen; ++ ++ count -= retlen; ++ buf += retlen; ++ if (retlen == 0) ++ count = 0; ++ } ++ else { ++ kfree(kbuf); ++ return ret; ++ } ++ ++ } ++ ++ kfree(kbuf); ++ return total_retlen; ++} /* mtd_read */ ++ ++static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) ++{ ++ struct mtd_file_info *mfi = file->private_data; ++ struct mtd_info *mtd = mfi->mtd; ++ char *kbuf; ++ size_t retlen; ++ size_t total_retlen=0; ++ int ret=0; ++ int len; ++ ++ DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); ++ ++ if (*ppos == mtd->size) ++ return -ENOSPC; ++ ++ if (*ppos + count > mtd->size) ++ count = mtd->size - *ppos; ++ ++ if (!count) ++ return 0; ++ ++ if (count > MAX_KMALLOC_SIZE) ++ kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL); ++ else ++ kbuf=kmalloc(count, GFP_KERNEL); ++ ++ if (!kbuf) ++ return -ENOMEM; ++ ++ while (count) { ++ ++ if (count > MAX_KMALLOC_SIZE) ++ len = MAX_KMALLOC_SIZE; ++ else ++ len = count; ++ ++ if (copy_from_user(kbuf, buf, len)) { ++ kfree(kbuf); ++ return -EFAULT; ++ } ++ ++ switch (mfi->mode) { ++ case MTD_MODE_OTP_FACTORY: ++ ret = -EROFS; ++ break; ++ case MTD_MODE_OTP_USER: ++ if (!mtd->write_user_prot_reg) { ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); ++ break; ++ ++ case MTD_MODE_RAW: ++ { ++ struct mtd_oob_ops ops; ++ ++ ops.mode = MTD_OOB_RAW; ++ ops.datbuf = kbuf; ++ ops.oobbuf = NULL; ++ ops.len = len; ++ ++ ret = mtd->write_oob(mtd, *ppos, &ops); ++ retlen = ops.retlen; ++ break; ++ } ++ ++ default: ++ ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); ++ } ++ if (!ret) { ++ *ppos += retlen; ++ total_retlen += retlen; ++ count -= retlen; ++ buf += retlen; ++ } ++ else { ++ kfree(kbuf); ++ return ret; ++ } ++ } ++ ++ kfree(kbuf); ++ return total_retlen; ++} /* mtd_write */ ++ ++/*====================================================================== ++ ++ IOCTL calls for getting device parameters. ++ ++======================================================================*/ ++static void mtdchar_erase_callback (struct erase_info *instr) ++{ ++ wake_up((wait_queue_head_t *)instr->priv); ++} ++ ++#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP) ++static int otp_select_filemode(struct mtd_file_info *mfi, int mode) ++{ ++ struct mtd_info *mtd = mfi->mtd; ++ int ret = 0; ++ ++ switch (mode) { ++ case MTD_OTP_FACTORY: ++ if (!mtd->read_fact_prot_reg) ++ ret = -EOPNOTSUPP; ++ else ++ mfi->mode = MTD_MODE_OTP_FACTORY; ++ break; ++ case MTD_OTP_USER: ++ if (!mtd->read_fact_prot_reg) ++ ret = -EOPNOTSUPP; ++ else ++ mfi->mode = MTD_MODE_OTP_USER; ++ break; ++ default: ++ ret = -EINVAL; ++ case MTD_OTP_OFF: ++ break; ++ } ++ return ret; ++} ++#else ++# define otp_select_filemode(f,m) -EOPNOTSUPP ++#endif ++ ++static int mtd_ioctl(struct inode *inode, struct file *file, ++ u_int cmd, u_long arg) ++{ ++ struct mtd_file_info *mfi = file->private_data; ++ struct mtd_info *mtd = mfi->mtd; ++ void __user *argp = (void __user *)arg; ++ int ret = 0; ++ u_long size; ++ struct mtd_info_user info; ++ ++ DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n"); ++ ++ size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; ++ if (cmd & IOC_IN) { ++ if (!access_ok(VERIFY_READ, argp, size)) ++ return -EFAULT; ++ } ++ if (cmd & IOC_OUT) { ++ if (!access_ok(VERIFY_WRITE, argp, size)) ++ return -EFAULT; ++ } ++ ++ switch (cmd) { ++ case MEMGETREGIONCOUNT: ++ if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int))) ++ return -EFAULT; ++ break; ++ ++ case MEMGETREGIONINFO: ++ { ++ struct region_info_user ur; ++ ++ if (copy_from_user(&ur, argp, sizeof(struct region_info_user))) ++ return -EFAULT; ++ ++ if (ur.regionindex >= mtd->numeraseregions) ++ return -EINVAL; ++ if (copy_to_user(argp, &(mtd->eraseregions[ur.regionindex]), ++ sizeof(struct mtd_erase_region_info))) ++ return -EFAULT; ++ break; ++ } ++ ++ case MEMGETINFO: ++ info.type = mtd->type; ++ info.flags = mtd->flags; ++ info.size = mtd->size; ++ info.erasesize = mtd->erasesize; ++ info.writesize = mtd->writesize; ++ info.oobsize = mtd->oobsize; ++ /* The below fields are obsolete */ ++ info.ecctype = -1; ++ info.eccsize = 0; ++ if (copy_to_user(argp, &info, sizeof(struct mtd_info_user))) ++ return -EFAULT; ++ break; ++ ++ case MEMERASE: ++ { ++ struct erase_info *erase; ++ ++ if(!(file->f_mode & 2)) ++ return -EPERM; ++ ++ erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL); ++ if (!erase) ++ ret = -ENOMEM; ++ else { ++ wait_queue_head_t waitq; ++ DECLARE_WAITQUEUE(wait, current); ++ ++ init_waitqueue_head(&waitq); ++ ++ if (copy_from_user(&erase->addr, argp, ++ sizeof(struct erase_info_user))) { ++ kfree(erase); ++ return -EFAULT; ++ } ++ erase->mtd = mtd; ++ erase->callback = mtdchar_erase_callback; ++ erase->priv = (unsigned long)&waitq; ++ ++ /* ++ FIXME: Allow INTERRUPTIBLE. Which means ++ not having the wait_queue head on the stack. ++ ++ If the wq_head is on the stack, and we ++ leave because we got interrupted, then the ++ wq_head is no longer there when the ++ callback routine tries to wake us up. ++ */ ++ ret = mtd->erase(mtd, erase); ++ if (!ret) { ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&waitq, &wait); ++ if (erase->state != MTD_ERASE_DONE && ++ erase->state != MTD_ERASE_FAILED) ++ schedule(); ++ remove_wait_queue(&waitq, &wait); ++ set_current_state(TASK_RUNNING); ++ ++ ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0; ++ } ++ kfree(erase); ++ } ++ break; ++ } ++ ++ case MEMWRITEOOB: ++ { ++ struct mtd_oob_buf buf; ++ struct mtd_oob_ops ops; ++ ++ if(!(file->f_mode & 2)) ++ return -EPERM; ++ ++ if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) ++ return -EFAULT; ++ ++ if (buf.length > 4096) ++ return -EINVAL; ++ ++ if (!mtd->write_oob) ++ ret = -EOPNOTSUPP; ++ else ++ ret = access_ok(VERIFY_READ, buf.ptr, ++ buf.length) ? 0 : EFAULT; ++ ++ if (ret) ++ return ret; ++ ++ ops.ooblen = buf.length; ++ ops.ooboffs = buf.start & (mtd->oobsize - 1); ++ ops.datbuf = NULL; ++ ops.mode = MTD_OOB_PLACE; ++ ++ if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) ++ return -EINVAL; ++ ++ ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); ++ if (!ops.oobbuf) ++ return -ENOMEM; ++ ++ if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) { ++ kfree(ops.oobbuf); ++ return -EFAULT; ++ } ++ ++ buf.start &= ~(mtd->oobsize - 1); ++ ret = mtd->write_oob(mtd, buf.start, &ops); ++ ++ if (copy_to_user(argp + sizeof(uint32_t), &ops.oobretlen, ++ sizeof(uint32_t))) ++ ret = -EFAULT; ++ ++ kfree(ops.oobbuf); ++ break; ++ ++ } ++ ++ case MEMREADOOB: ++ { ++ struct mtd_oob_buf buf; ++ struct mtd_oob_ops ops; ++ ++ if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) ++ return -EFAULT; ++ ++ if (buf.length > 4096) ++ return -EINVAL; ++ ++ if (!mtd->read_oob) ++ ret = -EOPNOTSUPP; ++ else ++ ret = access_ok(VERIFY_WRITE, buf.ptr, ++ buf.length) ? 0 : -EFAULT; ++ if (ret) ++ return ret; ++ ++ ops.ooblen = buf.length; ++ ops.ooboffs = buf.start & (mtd->oobsize - 1); ++ ops.datbuf = NULL; ++ ops.mode = MTD_OOB_PLACE; ++ ++ if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) ++ return -EINVAL; ++ ++ ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); ++ if (!ops.oobbuf) ++ return -ENOMEM; ++ ++ buf.start &= ~(mtd->oobsize - 1); ++ ret = mtd->read_oob(mtd, buf.start, &ops); ++ ++ if (put_user(ops.oobretlen, (uint32_t __user *)argp)) ++ ret = -EFAULT; ++ else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf, ++ ops.oobretlen)) ++ ret = -EFAULT; ++ ++ kfree(ops.oobbuf); ++ break; ++ } ++ ++ case MEMLOCK: ++ { ++ struct erase_info_user info; ++ ++ if (copy_from_user(&info, argp, sizeof(info))) ++ return -EFAULT; ++ ++ if (!mtd->lock) ++ ret = -EOPNOTSUPP; ++ else ++ ret = mtd->lock(mtd, info.start, info.length); ++ break; ++ } ++ ++ case MEMUNLOCK: ++ { ++ struct erase_info_user info; ++ ++ if (copy_from_user(&info, argp, sizeof(info))) ++ return -EFAULT; ++ ++ if (!mtd->unlock) ++ ret = -EOPNOTSUPP; ++ else ++ ret = mtd->unlock(mtd, info.start, info.length); ++ break; ++ } ++ ++ /* Legacy interface */ ++ case MEMGETOOBSEL: ++ { ++ struct nand_oobinfo oi; ++ ++ if (!mtd->ecclayout) ++ return -EOPNOTSUPP; ++ if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos)) ++ return -EINVAL; ++ ++ oi.useecc = MTD_NANDECC_AUTOPLACE; ++ memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos)); ++ memcpy(&oi.oobfree, mtd->ecclayout->oobfree, ++ sizeof(oi.oobfree)); ++ oi.eccbytes = mtd->ecclayout->eccbytes; ++ ++ if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo))) ++ return -EFAULT; ++ break; ++ } ++ ++ case MEMGETBADBLOCK: ++ { ++ loff_t offs; ++ ++ if (copy_from_user(&offs, argp, sizeof(loff_t))) ++ return -EFAULT; ++ if (!mtd->block_isbad) ++ ret = -EOPNOTSUPP; ++ else ++ return mtd->block_isbad(mtd, offs); ++ break; ++ } ++ ++ case MEMSETBADBLOCK: ++ { ++ loff_t offs; ++ ++ if (copy_from_user(&offs, argp, sizeof(loff_t))) ++ return -EFAULT; ++ if (!mtd->block_markbad) ++ ret = -EOPNOTSUPP; ++ else ++ return mtd->block_markbad(mtd, offs); ++ break; ++ } ++ ++#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP) ++ case OTPSELECT: ++ { ++ int mode; ++ if (copy_from_user(&mode, argp, sizeof(int))) ++ return -EFAULT; ++ ++ mfi->mode = MTD_MODE_NORMAL; ++ ++ ret = otp_select_filemode(mfi, mode); ++ ++ file->f_pos = 0; ++ break; ++ } ++ ++ case OTPGETREGIONCOUNT: ++ case OTPGETREGIONINFO: ++ { ++ struct otp_info *buf = kmalloc(4096, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ret = -EOPNOTSUPP; ++ switch (mfi->mode) { ++ case MTD_MODE_OTP_FACTORY: ++ if (mtd->get_fact_prot_info) ++ ret = mtd->get_fact_prot_info(mtd, buf, 4096); ++ break; ++ case MTD_MODE_OTP_USER: ++ if (mtd->get_user_prot_info) ++ ret = mtd->get_user_prot_info(mtd, buf, 4096); ++ break; ++ default: ++ break; ++ } ++ if (ret >= 0) { ++ if (cmd == OTPGETREGIONCOUNT) { ++ int nbr = ret / sizeof(struct otp_info); ++ ret = copy_to_user(argp, &nbr, sizeof(int)); ++ } else ++ ret = copy_to_user(argp, buf, ret); ++ if (ret) ++ ret = -EFAULT; ++ } ++ kfree(buf); ++ break; ++ } ++ ++ case OTPLOCK: ++ { ++ struct otp_info info; ++ ++ if (mfi->mode != MTD_MODE_OTP_USER) ++ return -EINVAL; ++ if (copy_from_user(&info, argp, sizeof(info))) ++ return -EFAULT; ++ if (!mtd->lock_user_prot_reg) ++ return -EOPNOTSUPP; ++ ret = mtd->lock_user_prot_reg(mtd, info.start, info.length); ++ break; ++ } ++#endif ++ ++ case ECCGETLAYOUT: ++ { ++ if (!mtd->ecclayout) ++ return -EOPNOTSUPP; ++ ++ if (copy_to_user(argp, mtd->ecclayout, ++ sizeof(struct nand_ecclayout))) ++ return -EFAULT; ++ break; ++ } ++ ++ case ECCGETSTATS: ++ { ++ if (copy_to_user(argp, &mtd->ecc_stats, ++ sizeof(struct mtd_ecc_stats))) ++ return -EFAULT; ++ break; ++ } ++ ++ case MTDFILEMODE: ++ { ++ mfi->mode = 0; ++ ++ switch(arg) { ++ case MTD_MODE_OTP_FACTORY: ++ case MTD_MODE_OTP_USER: ++ ret = otp_select_filemode(mfi, arg); ++ break; ++ ++ case MTD_MODE_RAW: ++ if (!mtd->read_oob || !mtd->write_oob) ++ return -EOPNOTSUPP; ++ mfi->mode = arg; ++ ++ case MTD_MODE_NORMAL: ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ file->f_pos = 0; ++ break; ++ } ++#ifdef CONFIG_MTD_PARTITIONS ++ case MTDREFRESH: ++ { ++ ret = refresh_mtd_partitions(mtd); ++ break; ++ } ++#endif ++ ++ default: ++ ret = -ENOTTY; ++ } ++ ++ return ret; ++} /* memory_ioctl */ ++ ++static const struct file_operations mtd_fops = { ++ .owner = THIS_MODULE, ++ .llseek = mtd_lseek, ++ .read = mtd_read, ++ .write = mtd_write, ++ .ioctl = mtd_ioctl, ++ .open = mtd_open, ++ .release = mtd_close, ++}; ++ ++static int __init init_mtdchar(void) ++{ ++ if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) { ++ printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", ++ MTD_CHAR_MAJOR); ++ return -EAGAIN; ++ } ++ ++ mtd_class = class_create(THIS_MODULE, "mtd"); ++ ++ if (IS_ERR(mtd_class)) { ++ printk(KERN_ERR "Error creating mtd class.\n"); ++ unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); ++ return PTR_ERR(mtd_class); ++ } ++ ++ register_mtd_user(¬ifier); ++ return 0; ++} ++ ++static void __exit cleanup_mtdchar(void) ++{ ++ unregister_mtd_user(¬ifier); ++ class_destroy(mtd_class); ++ unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); ++} ++ ++module_init(init_mtdchar); ++module_exit(cleanup_mtdchar); ++ ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Woodhouse "); ++MODULE_DESCRIPTION("Direct character-device access to MTD devices"); +diff -urN linux-2.6.24.7.old/drivers/mtd/mtdcore.c linux-2.6.24.7/drivers/mtd/mtdcore.c +--- linux-2.6.24.7.old/drivers/mtd/mtdcore.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/mtdcore.c 2009-04-12 18:13:57.000000000 +0200 +@@ -303,10 +303,10 @@ + */ + + int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, +- unsigned long count, loff_t to, size_t *retlen) ++ unsigned long count, loff_mtd_t to, size_mtd_t *retlen) + { + unsigned long i; +- size_t totlen = 0, thislen; ++ size_mtd_t totlen = 0, thislen; + int ret = 0; + + if(!mtd->write) { +@@ -350,7 +350,7 @@ + if (!this) + return 0; + +- return sprintf(buf, "mtd%d: %8.8x %8.8x \"%s\"\n", i, this->size, ++ return sprintf(buf, "mtd%d: %09llx %8.8x \"%s\"\n", i, this->size, + this->erasesize, this->name); + } + +diff -urN linux-2.6.24.7.old/drivers/mtd/mtdpart.c linux-2.6.24.7/drivers/mtd/mtdpart.c +--- linux-2.6.24.7.old/drivers/mtd/mtdpart.c 2009-04-12 18:05:07.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/mtdpart.c 2009-04-12 18:38:31.000000000 +0200 +@@ -30,7 +30,7 @@ + struct mtd_part { + struct mtd_info mtd; + struct mtd_info *master; +- u_int32_t offset; ++ u_int64_t offset; + int index; + struct list_head list; + int registered; +@@ -48,8 +48,8 @@ + * to the _real_ device. + */ + +-static int part_read (struct mtd_info *mtd, loff_t from, size_t len, +- size_t *retlen, u_char *buf) ++static int part_read (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, ++ size_mtd_t *retlen, u_char *buf) + { + struct mtd_part *part = PART(mtd); + int res; +@@ -69,8 +69,8 @@ + return res; + } + +-static int part_point (struct mtd_info *mtd, loff_t from, size_t len, +- size_t *retlen, u_char **buf) ++static int part_point (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, ++ size_mtd_t *retlen, u_char **buf) + { + struct mtd_part *part = PART(mtd); + if (from >= mtd->size) +@@ -81,14 +81,14 @@ + len, retlen, buf); + } + +-static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) ++static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_mtd_t from, size_mtd_t len) + { + struct mtd_part *part = PART(mtd); + + part->master->unpoint (part->master, addr, from + part->offset, len); + } + +-static int part_read_oob(struct mtd_info *mtd, loff_t from, ++static int part_read_oob(struct mtd_info *mtd, loff_mtd_t from, + struct mtd_oob_ops *ops) + { + struct mtd_part *part = PART(mtd); +@@ -109,8 +109,8 @@ + return res; + } + +-static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, +- size_t *retlen, u_char *buf) ++static int part_read_user_prot_reg (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, ++ size_mtd_t *retlen, u_char *buf) + { + struct mtd_part *part = PART(mtd); + return part->master->read_user_prot_reg (part->master, from, +@@ -118,14 +118,14 @@ + } + + static int part_get_user_prot_info (struct mtd_info *mtd, +- struct otp_info *buf, size_t len) ++ struct otp_info *buf, size_mtd_t len) + { + struct mtd_part *part = PART(mtd); + 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) ++static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, ++ size_mtd_t *retlen, u_char *buf) + { + struct mtd_part *part = PART(mtd); + return part->master->read_fact_prot_reg (part->master, from, +@@ -133,14 +133,14 @@ + } + + static int part_get_fact_prot_info (struct mtd_info *mtd, +- struct otp_info *buf, size_t len) ++ struct otp_info *buf, size_mtd_t len) + { + struct mtd_part *part = PART(mtd); + 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) ++static int part_write (struct mtd_info *mtd, loff_mtd_t to, size_mtd_t len, ++ size_mtd_t *retlen, const u_char *buf) + { + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) +@@ -153,7 +153,7 @@ + len, retlen, buf); + } + +-static int part_write_oob(struct mtd_info *mtd, loff_t to, ++static int part_write_oob(struct mtd_info *mtd, loff_mtd_t to, + struct mtd_oob_ops *ops) + { + struct mtd_part *part = PART(mtd); +@@ -168,22 +168,22 @@ + 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) ++static int part_write_user_prot_reg (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, ++ size_mtd_t *retlen, u_char *buf) + { + struct mtd_part *part = PART(mtd); + 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) ++static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len) + { + struct mtd_part *part = PART(mtd); + 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) ++ unsigned long count, loff_mtd_t to, size_mtd_t *retlen) + { + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) +@@ -224,7 +224,7 @@ + } + EXPORT_SYMBOL_GPL(mtd_erase_callback); + +-static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) ++static int part_lock (struct mtd_info *mtd, loff_mtd_t ofs, size_mtd_t len) + { + struct mtd_part *part = PART(mtd); + if ((len + ofs) > mtd->size) +@@ -232,7 +232,7 @@ + return part->master->lock(part->master, ofs + part->offset, len); + } + +-static int part_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) ++static int part_unlock (struct mtd_info *mtd, loff_mtd_t ofs, size_mtd_t len) + { + struct mtd_part *part = PART(mtd); + if ((len + ofs) > mtd->size) +@@ -258,7 +258,7 @@ + part->master->resume(part->master); + } + +-static int part_block_isbad (struct mtd_info *mtd, loff_t ofs) ++static int part_block_isbad (struct mtd_info *mtd, loff_mtd_t ofs) + { + struct mtd_part *part = PART(mtd); + if (ofs >= mtd->size) +@@ -267,7 +267,7 @@ + return part->master->block_isbad(part->master, ofs); + } + +-static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) ++static int part_block_markbad (struct mtd_info *mtd, loff_mtd_t ofs) + { + struct mtd_part *part = PART(mtd); + int res; +diff -urN linux-2.6.24.7.old/drivers/mtd/nand/Kconfig linux-2.6.24.7/drivers/mtd/nand/Kconfig +--- linux-2.6.24.7.old/drivers/mtd/nand/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/nand/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -1,5 +1,5 @@ + # drivers/mtd/nand/Kconfig +-# $Id: Kconfig,v 1.35 2005/11/07 11:14:30 gleixner Exp $ ++# $Id: Kconfig,v 1.3 2008-07-04 08:05:48 lhhuang Exp $ + + menuconfig MTD_NAND + tristate "NAND Device Support" +@@ -306,4 +306,127 @@ + These two (and possibly other) Alauda-based cardreaders for + SmartMedia and xD allow raw flash access. + ++config MTD_NAND_JZ4730 ++ tristate "Support NAND Flash device on Jz4730 board" ++ depends on SOC_JZ4730 ++ help ++ Support NAND Flash device on Jz4730 board ++ ++config MTD_NAND_JZ4740 ++ tristate "Support NAND Flash device on Jz4740 board" ++ depends on SOC_JZ4740 ++ help ++ Support NAND Flash device on Jz4740 board ++ ++config MTD_NAND_JZ4750 ++ tristate "Support NAND Flash device on Jz4750 board" ++ depends on SOC_JZ4750 || SOC_JZ4750D ++ help ++ Support NAND Flash device on Jz4750 board ++ ++config MTD_NAND_CS2 ++ depends on MTD_NAND_JZ4740 || MTD_NAND_JZ4750 ++ bool 'Use NAND on CS2_N of JZSOC' ++ default n ++ ++config MTD_NAND_CS3 ++ depends on MTD_NAND_JZ4740 || MTD_NAND_JZ4750 ++ bool 'Use NAND on CS3_N of JZSOC' ++ default n ++ ++config MTD_NAND_CS4 ++ depends on MTD_NAND_JZ4740 || MTD_NAND_JZ4750 ++ bool 'Use NAND on CS4_N of JZSOC' ++ default n ++ ++config MTD_NAND_MULTI_PLANE ++ depends on MTD_NAND_JZ4730 || MTD_NAND_JZ4740 || MTD_NAND_JZ4750 ++ bool 'Use multiple planes if the NAND supports' ++ default y ++ help ++ It is just supported on jz4740 now. ++ ++if MTD_NAND_JZ4740 || MTD_NAND_JZ4730 || MTD_NAND_JZ4750 ++choice ++ prompt "ECC type" ++ default CONFIG_MTD_SW_HM_ECC ++ ++config MTD_HW_HM_ECC ++ depends on MTD_NAND_JZ4740 || MTD_NAND_JZ4730 ++ bool 'Select hardware HM ECC' ++ ++config MTD_SW_HM_ECC ++ bool 'Select software HM ECC' ++ ++config MTD_HW_RS_ECC ++ depends on MTD_NAND_JZ4740 ++ bool 'Select hardware RS ECC' ++ ++config MTD_HW_BCH_ECC ++ depends on MTD_NAND_JZ4750 ++ bool 'Select hardware BCH ECC' ++endchoice ++ ++choice ++ prompt "4 bit or 8 bit BCH ecc" ++ depends on MTD_HW_BCH_ECC ++ default CONFIG_MTD_HW_BCH_4BIT ++ ++config MTD_HW_BCH_4BIT ++ bool '4 bit' ++ ++config MTD_HW_BCH_8BIT ++ bool '8 bit' ++ ++endchoice ++ ++config MTD_NAND_DMA ++ depends on MTD_HW_BCH_ECC ++ bool 'Use DMA mode' ++ help ++ This enables using DMA for reading and writing NAND flash, if not selected, ++ then CPU mode is used. ++ ++config MTD_NAND_DMABUF ++ depends on MTD_NAND_DMA ++ bool 'use DMA buffer in NAND driver' ++ help ++ It's better to say NO. If saying yes, DMA buffers will be allocated for ++ NAND reading and writing in NAND driver instead of upper layer. It's ++ slower. Just usable on CS1_N now. By saying NO, upper buffers will be ++ used as DMA buffer. It's faster, but kmalloc instead of vmalloc is required. ++endif ++ ++config MTD_MTDBLOCK_WRITE_VERIFY_ENABLE ++ bool "MTDBLOCK write verify enable" ++ default n ++ help ++ This will enable the write verification, which will read back data to check ++ after doing a write opetaion. ++ ++ It will be used by the JZ mtdblock driver (mtdblock-jz.c). ++ ++config MTD_OOB_COPIES ++ int "how many copies of the fs info in the oob area" ++ default 3 ++ range 0 128 ++ depends on MTD ++ help ++ This defines the copies of the fs info in the oob area inside a block. ++ Value ranges from 0 to (pages_per_block - 1). ++ ++ It will be used by the JZ mtdblock driver (mtdblock-jz.c). ++ ++config MTD_BADBLOCK_FLAG_PAGE ++ int "which page inside a block will store the badblock mark" ++ default 0 ++ range 0 127 ++ depends on MTD ++ help ++ This defines which page of a block will store the badblock mark. ++ Value ranges from 0 to (pages_per_block - 1). ++ ++ Old SLC NANDs store the badblock mark in the first page of a block, but ++ most modern MLC NANDs store it in the last page of a block. ++ + endif # MTD_NAND +diff -urN linux-2.6.24.7.old/drivers/mtd/nand/Makefile linux-2.6.24.7/drivers/mtd/nand/Makefile +--- linux-2.6.24.7.old/drivers/mtd/nand/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/nand/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -1,7 +1,7 @@ + # + # linux/drivers/nand/Makefile + # +-# $Id: Makefile.common,v 1.15 2004/11/26 12:28:22 dedekind Exp $ ++# $Id: Makefile,v 1.2 2008-07-04 08:07:45 lhhuang Exp $ + + obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o + obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o +@@ -29,5 +29,8 @@ + obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o + obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o + obj-$(CONFIG_MTD_ALAUDA) += alauda.o ++obj-$(CONFIG_MTD_NAND_JZ4730) += jz4730_nand.o ++obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o ++obj-$(CONFIG_MTD_NAND_JZ4750) += jz4750_nand.o + + nand-objs := nand_base.o nand_bbt.o +diff -urN linux-2.6.24.7.old/drivers/mtd/nand/jz4730_nand.c linux-2.6.24.7/drivers/mtd/nand/jz4730_nand.c +--- linux-2.6.24.7.old/drivers/mtd/nand/jz4730_nand.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/nand/jz4730_nand.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,367 @@ ++/* ++ * linux/drivers/mtd/nand/jz4730_nand.c ++ * ++ * Copyright (c) 2005 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Ingenic JZ4730 NAND driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define NAND_DATA_PORT 0xB4000000 /* read-write area */ ++#define __nand_ecc() (REG_EMC_NFECC & 0x00ffffff) ++#define __nand_ecc_enable() (REG_EMC_NFCSR |= EMC_NFCSR_ECCE | EMC_NFCSR_ERST) ++#define __nand_ecc_disable() (REG_EMC_NFCSR &= ~EMC_NFCSR_ECCE) ++ ++/* ++ * MTD structure for JzSOC board ++ */ ++static struct mtd_info *jz_mtd = NULL; ++ ++/* ++ * Define partitions for flash devices ++ */ ++#ifdef CONFIG_JZ4730_PMP ++static struct mtd_partition partition_info[] = { ++ { name: "NAND BOOT partition", ++ offset: 0 * 0x100000, ++ size: 4 * 0x100000 }, ++ { name: "NAND KERNEL partition", ++ offset: 4 * 0x100000, ++ size: 4 * 0x100000 }, ++ { name: "NAND ROOTFS partition", ++ offset: 8 * 0x100000, ++ size: 56 * 0x100000 }, ++ { name: "NAND SSFDC partition", ++ offset: 64 * 0x100000, ++ size: 64 * 0x100000 }, ++}; ++ ++/* Define max reserved bad blocks for each partition. ++ * This is used by the mtdblock-jz.c NAND FTL driver only. ++ * ++ * The NAND FTL driver reserves some good blocks which can't be ++ * seen by the upper layer. When the bad block number of a partition ++ * exceeds the max reserved blocks, then there is no more reserved ++ * good blocks to be used by the NAND FTL driver when another bad ++ * block generated. ++ */ ++static int partition_reserved_badblocks[]= { ++ 2, /* reserved blocks of mtd0 */ ++ 2, /* reserved blocks of mtd1 */ ++ 10, /* reserved blocks of mtd2 */ ++ 10}; /* reserved blocks of mtd3 */ ++#elif CONFIG_JZ4730_PMPV1 ++static struct mtd_partition partition_info[] = { ++ { name: "NAND ROOTFS partition", ++ offset: 3 * 0x100000, ++ size: (32-3) * 0x100000 }, ++ { name: "NAND DATAFS partition", ++ offset: 32 * 0x100000, ++ size: 32 * 0x100000 }, ++}; ++static int partition_reserved_badblocks[]={ ++ 10, ++ 10}; ++#else ++static struct mtd_partition partition_info[] = { ++ { name: "NAND ROOTFS partition", ++ offset: 3 * 0x100000, ++ size: (128-3) * 0x100000 }, ++}; ++static int partition_reserved_badblocks[]={ ++ 20}; ++#endif ++ ++/*------------------------------------------------------------------------- ++ * Following three functions are exported and used by the mtdblock-jz.c ++ * NAND FTL driver only. ++ */ ++ ++unsigned short get_mtdblock_write_verify_enable(void) ++{ ++#ifdef CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE ++ return 1; ++#endif ++ return 0; ++} ++EXPORT_SYMBOL(get_mtdblock_write_verify_enable); ++ ++unsigned short get_mtdblock_oob_copies(void) ++{ ++ return CONFIG_MTD_OOB_COPIES; ++} ++EXPORT_SYMBOL(get_mtdblock_oob_copies); ++ ++int *get_jz_badblock_table(void) ++{ ++ return partition_reserved_badblocks; ++} ++EXPORT_SYMBOL(get_jz_badblock_table); ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void jz_hwcontrol(struct mtd_info *mtd, int dat, ++ unsigned int ctrl) ++{ ++ struct nand_chip *this = (struct nand_chip *)(mtd->priv); ++ unsigned int nandaddr = (unsigned int)this->IO_ADDR_W; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ if ( ctrl & NAND_ALE ) ++ nandaddr = (unsigned int)((unsigned long)(this->IO_ADDR_W) | 0x00080000); ++ else ++ nandaddr = (unsigned int)((unsigned long)(this->IO_ADDR_W) & ~0x00080000); ++ ++ if ( ctrl & NAND_CLE ) ++ nandaddr = nandaddr | 0x00040000; ++ else ++ nandaddr = nandaddr & ~0x00040000; ++ if ( ctrl & NAND_NCE ) ++ REG_EMC_NFCSR |= EMC_NFCSR_FCE; ++ else ++ REG_EMC_NFCSR &= ~EMC_NFCSR_FCE; ++ } ++ ++ this->IO_ADDR_W = (void __iomem *)nandaddr; ++ if (dat != NAND_CMD_NONE) ++ writeb(dat , this->IO_ADDR_W); ++ ++} ++ ++static int jz_device_ready(struct mtd_info *mtd) ++{ ++ int ready; ++ ready = (REG_EMC_NFCSR & EMC_NFCSR_RB) ? 1 : 0; ++ return ready; ++} ++ ++/* ++ * EMC setup ++ */ ++static void jz_device_setup(void) ++{ ++ /* Set NFE bit */ ++ REG_EMC_NFCSR |= EMC_NFCSR_NFE; ++} ++ ++static void jzsoc_nand_enable_hwecc(struct mtd_info* mtd, int mode) ++{ ++ __nand_ecc_enable(); ++} ++ ++static int jzsoc_nand_calculate_ecc(struct mtd_info* mtd, const u_char* dat, ++ u_char* ecc_code) ++{ ++ unsigned int calc_ecc; ++ unsigned char *tmp; ++ ++ __nand_ecc_disable(); ++ ++ calc_ecc = ~(__nand_ecc()) | 0x00030000; ++ ++ tmp = (unsigned char *)&calc_ecc; ++ ++ ecc_code[0] = tmp[1]; ++ ecc_code[1] = tmp[0]; ++ ecc_code[2] = tmp[2]; ++ ++ return 0; ++} ++ ++/* ECC handling functions */ ++ ++static int jzsoc_nand_correct_data(struct mtd_info *mtd, u_char *dat, ++ u_char *read_ecc, u_char *calc_ecc) ++{ ++ u_char a, b, c, d1, d2, d3, add, bit, i; ++ ++ /* Do error detection */ ++ d1 = calc_ecc[0] ^ read_ecc[0]; ++ d2 = calc_ecc[1] ^ read_ecc[1]; ++ d3 = calc_ecc[2] ^ read_ecc[2]; ++ ++ if ((d1 | d2 | d3) == 0) { ++ /* No errors */ ++ return 0; ++ } ++ else { ++ a = (d1 ^ (d1 >> 1)) & 0x55; ++ b = (d2 ^ (d2 >> 1)) & 0x55; ++ c = (d3 ^ (d3 >> 1)) & 0x54; ++ ++ /* Found and will correct single bit error in the data */ ++ if ((a == 0x55) && (b == 0x55) && (c == 0x54)) { ++ c = 0x80; ++ add = 0; ++ a = 0x80; ++ for (i=0; i<4; i++) { ++ if (d1 & c) ++ add |= a; ++ c >>= 2; ++ a >>= 1; ++ } ++ c = 0x80; ++ for (i=0; i<4; i++) { ++ if (d2 & c) ++ add |= a; ++ c >>= 2; ++ a >>= 1; ++ } ++ bit = 0; ++ b = 0x04; ++ c = 0x80; ++ for (i=0; i<3; i++) { ++ if (d3 & c) ++ bit |= b; ++ c >>= 2; ++ b >>= 1; ++ } ++ b = 0x01; ++ a = dat[add]; ++ a ^= (b << bit); ++ dat[add] = a; ++ return 0; ++ } ++ else { ++ i = 0; ++ while (d1) { ++ if (d1 & 0x01) ++ ++i; ++ d1 >>= 1; ++ } ++ while (d2) { ++ if (d2 & 0x01) ++ ++i; ++ d2 >>= 1; ++ } ++ while (d3) { ++ if (d3 & 0x01) ++ ++i; ++ d3 >>= 1; ++ } ++ if (i == 1) { ++ /* ECC Code Error Correction */ ++ read_ecc[0] = calc_ecc[0]; ++ read_ecc[1] = calc_ecc[1]; ++ read_ecc[2] = calc_ecc[2]; ++ return 0; ++ } ++ else { ++ /* Uncorrectable Error */ ++ printk("uncorrectable ECC error\n"); ++ return -1; ++ } ++ } ++ } ++ ++ /* Should never happen */ ++ return -1; ++} ++ ++ ++/* ++ * Main initialization routine ++ */ ++int __init jznand_init(void) ++{ ++ struct nand_chip *this; ++ int nr_partitions; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ jz_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), ++ GFP_KERNEL); ++ if (!jz_mtd) { ++ printk ("Unable to allocate JzSOC NAND MTD device structure.\n"); ++ return -ENOMEM; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&jz_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) jz_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ jz_mtd->priv = this; ++ ++ /* Set & initialize NAND Flash controller */ ++ jz_device_setup(); ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = (void __iomem *) NAND_DATA_PORT; ++ this->IO_ADDR_W = (void __iomem *) NAND_DATA_PORT; ++ this->cmd_ctrl = jz_hwcontrol; ++ this->dev_ready = jz_device_ready; ++ ++#ifdef CONFIG_MTD_HW_HM_ECC ++ this->ecc.calculate = jzsoc_nand_calculate_ecc; ++ this->ecc.correct = jzsoc_nand_correct_data; ++ this->ecc.hwctl = jzsoc_nand_enable_hwecc; ++ this->ecc.mode = NAND_ECC_HW; ++ this->ecc.size = 256; ++ this->ecc.bytes = 3; ++ ++#endif ++ ++#ifdef CONFIG_MTD_SW_HM_ECC ++ this->eccmode = NAND_ECC_SOFT; ++#endif ++ ++ /* 20 us command delay time */ ++ this->chip_delay = 20; ++ ++ /* Scan to find existance of the device */ ++ if (nand_scan(jz_mtd, 1)) { ++ kfree (jz_mtd); ++ return -ENXIO; ++ } ++ ++ /* Register the partitions */ ++ nr_partitions = sizeof(partition_info) / sizeof(struct mtd_partition); ++ printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nr_partitions, jz_mtd->name); ++ add_mtd_partitions(jz_mtd, partition_info, nr_partitions); ++ ++ return 0; ++} ++module_init(jznand_init); ++ ++/* ++ * Clean up routine ++ */ ++#ifdef MODULE ++static void __exit jznand_cleanup(void) ++{ ++ struct nand_chip *this = (struct nand_chip *) &jz_mtd[1]; ++ ++ /* Unregister partitions */ ++ del_mtd_partitions(jz_mtd); ++ ++ /* Unregister the device */ ++ del_mtd_device (jz_mtd); ++ ++ /* Free internal data buffers */ ++ kfree (this->data_buf); ++ ++ /* Free the MTD device structure */ ++ kfree (jz_mtd); ++} ++module_exit(jznand_cleanup); ++#endif +diff -urN linux-2.6.24.7.old/drivers/mtd/nand/jz4740_nand.c linux-2.6.24.7/drivers/mtd/nand/jz4740_nand.c +--- linux-2.6.24.7.old/drivers/mtd/nand/jz4740_nand.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/nand/jz4740_nand.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1037 @@ ++/* ++ * linux/drivers/mtd/nand/jz4740_nand.c ++ * ++ * Copyright (c) 2005 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Ingenic JZ4740 NAND driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define NAND_DATA_PORT1 0xB8000000 /* read-write area in static bank 1 */ ++#define NAND_DATA_PORT2 0xB4000000 /* read-write area in static bank 2 */ ++#define NAND_DATA_PORT3 0xAC000000 /* read-write area in static bank 3 */ ++#define NAND_DATA_PORT4 0xA8000000 /* read-write area in static bank 4 */ ++ ++#define PAR_SIZE 9 ++ ++#define __nand_enable() (REG_EMC_NFCSR |= EMC_NFCSR_NFE1 | EMC_NFCSR_NFCE1) ++#define __nand_disable() (REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1) ++ ++#define __nand_ecc_enable() (REG_EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_ERST ) ++#define __nand_ecc_disable() (REG_EMC_NFECR &= ~EMC_NFECR_ECCE) ++ ++#define __nand_select_hm_ecc() (REG_EMC_NFECR &= ~EMC_NFECR_RS ) ++#define __nand_select_rs_ecc() (REG_EMC_NFECR |= EMC_NFECR_RS) ++ ++#define __nand_read_hm_ecc() (REG_EMC_NFECC & 0x00ffffff) ++ ++#define __nand_rs_ecc_encoding() (REG_EMC_NFECR |= EMC_NFECR_RS_ENCODING) ++#define __nand_rs_ecc_decoding() (REG_EMC_NFECR &= ~EMC_NFECR_RS_ENCODING) ++#define __nand_ecc_encode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_ENCF)) ++#define __nand_ecc_decode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_DECF)) ++ ++/* ++ * MTD structure for JzSOC board ++ */ ++static struct mtd_info *jz_mtd = NULL; ++extern struct mtd_info *jz_mtd1; ++extern char all_use_planes; ++extern int global_page; /* for two-plane operations */ ++ ++/* ++ * Define partitions for flash devices ++ */ ++#ifdef CONFIG_JZ4740_PAVO ++static struct mtd_partition partition_info[] = { ++ { name: "NAND BOOT partition", ++ offset: 0 * 0x100000, ++ size: 4 * 0x100000, ++ use_planes: 0 }, ++ { name: "NAND KERNEL partition", ++ offset: 4 * 0x100000, ++ size: 4 * 0x100000, ++ use_planes: 0 }, ++ { name: "NAND ROOTFS partition", ++ offset: 8 * 0x100000, ++ size: 120 * 0x100000, ++ use_planes: 0 }, ++ { name: "NAND DATA1 partition", ++ offset: 128 * 0x100000, ++ size: 128 * 0x100000, ++ use_planes: 1 }, ++ { name: "NAND DATA2 partition", ++ offset: 256 * 0x100000, ++ size: 256 * 0x100000, ++ use_planes: 1 }, ++ { name: "NAND VFAT partition", ++ offset: 512 * 0x100000, ++ size: 512 * 0x100000, ++ use_planes: 1 }, ++}; ++ ++ ++/* Define max reserved bad blocks for each partition. ++ * This is used by the mtdblock-jz.c NAND FTL driver only. ++ * ++ * The NAND FTL driver reserves some good blocks which can't be ++ * seen by the upper layer. When the bad block number of a partition ++ * exceeds the max reserved blocks, then there is no more reserved ++ * good blocks to be used by the NAND FTL driver when another bad ++ * block generated. ++ */ ++static int partition_reserved_badblocks[] = { ++ 2, /* reserved blocks of mtd0 */ ++ 2, /* reserved blocks of mtd1 */ ++ 10, /* reserved blocks of mtd2 */ ++ 10, /* reserved blocks of mtd3 */ ++ 20, /* reserved blocks of mtd4 */ ++ 20}; /* reserved blocks of mtd5 */ ++#endif /* CONFIG_JZ4740_PAVO */ ++ ++#ifdef CONFIG_JZ4740_LEO ++static struct mtd_partition partition_info[] = { ++ { name: "NAND BOOT partition", ++ offset: 0 * 0x100000, ++ size: 4 * 0x100000 }, ++ { name: "NAND KERNEL partition", ++ offset: 4 * 0x100000, ++ size: 4 * 0x100000 }, ++ { name: "NAND ROOTFS partition", ++ offset: 8 * 0x100000, ++ size: 56 * 0x100000 }, ++ { name: "NAND VFAT partition", ++ offset: 64 * 0x100000, ++ size: 64 * 0x100000 }, ++}; ++static int partition_reserved_badblocks[] = { ++ 2, /* reserved blocks of mtd0 */ ++ 2, /* reserved blocks of mtd1 */ ++ 10, /* reserved blocks of mtd2 */ ++ 10}; /* reserved blocks of mtd3 */ ++#endif /* CONFIG_JZ4740_LEO */ ++ ++#ifdef CONFIG_JZ4740_LYRA ++static struct mtd_partition partition_info[] = { ++ { name: "NAND BOOT partition", ++ offset: 0 * 0x100000, ++ size: 4 * 0x100000 }, ++ { name: "NAND KERNEL partition", ++ offset: 4 * 0x100000, ++ size: 4 * 0x100000 }, ++ { name: "NAND ROOTFS partition", ++ offset: 8 * 0x100000, ++ size: 120 * 0x100000 }, ++ { name: "NAND DATA1 partition", ++ offset: 128 * 0x100000, ++ size: 128 * 0x100000 }, ++ { name: "NAND DATA2 partition", ++ offset: 256 * 0x100000, ++ size: 256 * 0x100000 }, ++ { name: "NAND VFAT partition", ++ offset: 512 * 0x100000, ++ size: 512 * 0x100000 }, ++}; ++ ++/* Define max reserved bad blocks for each partition. ++ * This is used by the mtdblock-jz.c NAND FTL driver only. ++ * ++ * The NAND FTL driver reserves some good blocks which can't be ++ * seen by the upper layer. When the bad block number of a partition ++ * exceeds the max reserved blocks, then there is no more reserved ++ * good blocks to be used by the NAND FTL driver when another bad ++ * block generated. ++ */ ++static int partition_reserved_badblocks[] = { ++ 2, /* reserved blocks of mtd0 */ ++ 2, /* reserved blocks of mtd1 */ ++ 10, /* reserved blocks of mtd2 */ ++ 10, /* reserved blocks of mtd3 */ ++ 20, /* reserved blocks of mtd4 */ ++ 20}; /* reserved blocks of mtd5 */ ++#endif /* CONFIG_JZ4740_LYRA */ ++ ++#ifdef CONFIG_JZ4725_DIPPER ++static struct mtd_partition partition_info[] = { ++ { name: "NAND BOOT partition", ++ offset: 0 * 0x100000, ++ size: 4 * 0x100000 }, ++ { name: "NAND KERNEL partition", ++ offset: 4 * 0x100000, ++ size: 4 * 0x100000 }, ++ { name: "NAND ROOTFS partition", ++ offset: 8 * 0x100000, ++ size: 56 * 0x100000 }, ++ { name: "NAND VFAT partition", ++ offset: 64 * 0x100000, ++ size: 64 * 0x100000 }, ++}; ++ ++/* Define max reserved bad blocks for each partition. ++ * This is used by the mtdblock-jz.c NAND FTL driver only. ++ * ++ * The NAND FTL driver reserves some good blocks which can't be ++ * seen by the upper layer. When the bad block number of a partition ++ * exceeds the max reserved blocks, then there is no more reserved ++ * good blocks to be used by the NAND FTL driver when another bad ++ * block generated. ++ */ ++static int partition_reserved_badblocks[] = { ++ 2, /* reserved blocks of mtd0 */ ++ 2, /* reserved blocks of mtd1 */ ++ 10, /* reserved blocks of mtd2 */ ++ 10}; /* reserved blocks of mtd3 */ ++#endif /* CONFIG_JZ4740_DIPPER */ ++ ++#ifdef CONFIG_JZ4720_VIRGO ++static struct mtd_partition partition_info[] = { ++ { name: "NAND BOOT partition", ++ offset: 0 * 0x100000, ++ size: 4 * 0x100000 }, ++ { name: "NAND KERNEL partition", ++ offset: 4 * 0x100000, ++ size: 4 * 0x100000 }, ++ { name: "NAND ROOTFS partition", ++ offset: 8 * 0x100000, ++ size: 120 * 0x100000 }, ++ { name: "NAND DATA1 partition", ++ offset: 128 * 0x100000, ++ size: 128 * 0x100000 }, ++ { name: "NAND DATA2 partition", ++ offset: 256 * 0x100000, ++ size: 256 * 0x100000 }, ++ { name: "NAND VFAT partition", ++ offset: 512 * 0x100000, ++ size: 512 * 0x100000 }, ++}; ++ ++ ++/* Define max reserved bad blocks for each partition. ++ * This is used by the mtdblock-jz.c NAND FTL driver only. ++ * ++ * The NAND FTL driver reserves some good blocks which can't be ++ * seen by the upper layer. When the bad block number of a partition ++ * exceeds the max reserved blocks, then there is no more reserved ++ * good blocks to be used by the NAND FTL driver when another bad ++ * block generated. ++ */ ++static int partition_reserved_badblocks[] = { ++ 2, /* reserved blocks of mtd0 */ ++ 2, /* reserved blocks of mtd1 */ ++ 10, /* reserved blocks of mtd2 */ ++ 10, /* reserved blocks of mtd3 */ ++ 20, /* reserved blocks of mtd4 */ ++ 20}; /* reserved blocks of mtd5 */ ++#endif /* CONFIG_JZ4720_VIRGO */ ++/*------------------------------------------------------------------------- ++ * Following three functions are exported and used by the mtdblock-jz.c ++ * NAND FTL driver only. ++ */ ++ ++unsigned short get_mtdblock_write_verify_enable(void) ++{ ++#ifdef CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE ++ return 1; ++#endif ++ return 0; ++} ++EXPORT_SYMBOL(get_mtdblock_write_verify_enable); ++ ++unsigned short get_mtdblock_oob_copies(void) ++{ ++ return CONFIG_MTD_OOB_COPIES; ++} ++EXPORT_SYMBOL(get_mtdblock_oob_copies); ++ ++int *get_jz_badblock_table(void) ++{ ++ return partition_reserved_badblocks; ++} ++EXPORT_SYMBOL(get_jz_badblock_table); ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void jz_hwcontrol(struct mtd_info *mtd, int dat, ++ unsigned int ctrl) ++{ ++ struct nand_chip *this = (struct nand_chip *)(mtd->priv); ++ unsigned int nandaddr = (unsigned int)this->IO_ADDR_W; ++ extern u8 nand_nce; /* in nand_base.c, indicates which chip select is used for current nand chip */ ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ if (ctrl & NAND_NCE) { ++ switch (nand_nce) { ++ case NAND_NCE1: ++ this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT1; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; ++ REG_EMC_NFCSR |= EMC_NFCSR_NFCE1; ++ break; ++ case NAND_NCE2: ++ this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT2; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; ++ REG_EMC_NFCSR |= EMC_NFCSR_NFCE2; ++ break; ++ case NAND_NCE3: ++ this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT3; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; ++ REG_EMC_NFCSR |= EMC_NFCSR_NFCE3; ++ break; ++ case NAND_NCE4: ++ this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT4; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; ++ REG_EMC_NFCSR |= EMC_NFCSR_NFCE4; ++ break; ++ default: ++ printk("error: no nand_nce 0x%x\n",nand_nce); ++ break; ++ } ++ } else { ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; ++ } ++ ++ if ( ctrl & NAND_ALE ) ++ nandaddr = (unsigned int)((unsigned long)(this->IO_ADDR_W) | 0x00010000); ++ else ++ nandaddr = (unsigned int)((unsigned long)(this->IO_ADDR_W) & ~0x00010000); ++ ++ if ( ctrl & NAND_CLE ) ++ nandaddr = nandaddr | 0x00008000; ++ else ++ nandaddr = nandaddr & ~0x00008000; ++ } ++ ++ this->IO_ADDR_W = (void __iomem *)nandaddr; ++ if (dat != NAND_CMD_NONE) ++ writeb(dat, this->IO_ADDR_W); ++} ++ ++static int jz_device_ready(struct mtd_info *mtd) ++{ ++ int ready, wait = 10; ++ while (wait--); ++ ready = __gpio_get_pin(94); ++ return ready; ++} ++ ++/* ++ * EMC setup ++ */ ++static void jz_device_setup(void) ++{ ++// PORT 0: ++// ... ++// PORT 1: ++// PIN/BIT N FUNC0 FUNC1 ++// 25 CS1# - ++// 26 CS2# - ++// 27 CS3# - ++// 28 CS4# - ++#define GPIO_CS2_N (32+26) ++#define GPIO_CS3_N (32+27) ++#define GPIO_CS4_N (32+28) ++#define SMCR_VAL 0x0d221200 ++ ++ /* Set NFE bit */ ++ REG_EMC_NFCSR |= EMC_NFCSR_NFE1; ++ /* Read/Write timings */ ++ REG_EMC_SMCR1 = SMCR_VAL; ++ ++#if defined(CONFIG_MTD_NAND_CS2) ++ /* Set CS2# pin as function 0 */ ++ __gpio_as_func0(GPIO_CS2_N); ++ REG_EMC_NFCSR |= EMC_NFCSR_NFE2; ++ REG_EMC_SMCR2 = SMCR_VAL; ++#endif ++ ++#if defined(CONFIG_MTD_NAND_CS3) ++ __gpio_as_func0(GPIO_CS3_N); ++ REG_EMC_NFCSR |= EMC_NFCSR_NFE3; ++ REG_EMC_SMCR3 = SMCR_VAL; ++#endif ++ ++#if defined(CONFIG_MTD_NAND_CS4) ++ __gpio_as_func0(GPIO_CS4_N); ++ REG_EMC_NFCSR |= EMC_NFCSR_NFE4; ++ REG_EMC_SMCR4 = SMCR_VAL; ++#endif ++} ++ ++#ifdef CONFIG_MTD_HW_HM_ECC ++ ++static int jzsoc_nand_calculate_hm_ecc(struct mtd_info* mtd, ++ const u_char* dat, u_char* ecc_code) ++{ ++ unsigned int calc_ecc; ++ unsigned char *tmp; ++ ++ __nand_ecc_disable(); ++ ++ calc_ecc = ~(__nand_read_hm_ecc()) | 0x00030000; ++ ++ tmp = (unsigned char *)&calc_ecc; ++ //adjust eccbytes order for compatible with software ecc ++ ecc_code[0] = tmp[1]; ++ ecc_code[1] = tmp[0]; ++ ecc_code[2] = tmp[2]; ++ ++ return 0; ++} ++ ++static void jzsoc_nand_enable_hm_hwecc(struct mtd_info* mtd, int mode) ++{ ++ __nand_ecc_enable(); ++ __nand_select_hm_ecc(); ++} ++ ++static int jzsoc_nand_hm_correct_data(struct mtd_info *mtd, u_char *dat, ++ u_char *read_ecc, u_char *calc_ecc) ++{ ++ u_char a, b, c, d1, d2, d3, add, bit, i; ++ ++ /* Do error detection */ ++ d1 = calc_ecc[0] ^ read_ecc[0]; ++ d2 = calc_ecc[1] ^ read_ecc[1]; ++ d3 = calc_ecc[2] ^ read_ecc[2]; ++ ++ if ((d1 | d2 | d3) == 0) { ++ /* No errors */ ++ return 0; ++ } ++ else { ++ a = (d1 ^ (d1 >> 1)) & 0x55; ++ b = (d2 ^ (d2 >> 1)) & 0x55; ++ c = (d3 ^ (d3 >> 1)) & 0x54; ++ ++ /* Found and will correct single bit error in the data */ ++ if ((a == 0x55) && (b == 0x55) && (c == 0x54)) { ++ c = 0x80; ++ add = 0; ++ a = 0x80; ++ for (i=0; i<4; i++) { ++ if (d1 & c) ++ add |= a; ++ c >>= 2; ++ a >>= 1; ++ } ++ c = 0x80; ++ for (i=0; i<4; i++) { ++ if (d2 & c) ++ add |= a; ++ c >>= 2; ++ a >>= 1; ++ } ++ bit = 0; ++ b = 0x04; ++ c = 0x80; ++ for (i=0; i<3; i++) { ++ if (d3 & c) ++ bit |= b; ++ c >>= 2; ++ b >>= 1; ++ } ++ b = 0x01; ++ a = dat[add]; ++ a ^= (b << bit); ++ dat[add] = a; ++ return 0; ++ } ++ else { ++ i = 0; ++ while (d1) { ++ if (d1 & 0x01) ++ ++i; ++ d1 >>= 1; ++ } ++ while (d2) { ++ if (d2 & 0x01) ++ ++i; ++ d2 >>= 1; ++ } ++ while (d3) { ++ if (d3 & 0x01) ++ ++i; ++ d3 >>= 1; ++ } ++ if (i == 1) { ++ /* ECC Code Error Correction */ ++ read_ecc[0] = calc_ecc[0]; ++ read_ecc[1] = calc_ecc[1]; ++ read_ecc[2] = calc_ecc[2]; ++ return 0; ++ } ++ else { ++ /* Uncorrectable Error */ ++ printk("NAND: uncorrectable ECC error\n"); ++ return -1; ++ } ++ } ++ } ++ ++ /* Should never happen */ ++ return -1; ++} ++ ++#endif /* CONFIG_MTD_HW_HM_ECC */ ++ ++#ifdef CONFIG_MTD_HW_RS_ECC ++ ++static void jzsoc_nand_enable_rs_hwecc(struct mtd_info* mtd, int mode) ++{ ++ REG_EMC_NFINTS = 0x0; ++ __nand_ecc_enable(); ++ __nand_select_rs_ecc(); ++ ++ if (mode == NAND_ECC_READ) ++ __nand_rs_ecc_decoding(); ++ ++ if (mode == NAND_ECC_WRITE) ++ __nand_rs_ecc_encoding(); ++} ++ ++static void jzsoc_rs_correct(unsigned char *dat, int idx, int mask) ++{ ++ int i; ++ ++ idx--; ++ ++ i = idx + (idx >> 3); ++ if (i >= 512) ++ return; ++ ++ mask <<= (idx & 0x7); ++ ++ dat[i] ^= mask & 0xff; ++ if (i < 511) ++ dat[i+1] ^= (mask >> 8) & 0xff; ++} ++ ++/* ++ * calc_ecc points to oob_buf for us ++ */ ++static int jzsoc_nand_rs_correct_data(struct mtd_info *mtd, u_char *dat, ++ u_char *read_ecc, u_char *calc_ecc) ++{ ++ volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0; ++ short k; ++ u32 stat; ++ ++ /* Set PAR values */ ++ for (k = 0; k < PAR_SIZE; k++) { ++ *paraddr++ = read_ecc[k]; ++ } ++ ++ /* Set PRDY */ ++ REG_EMC_NFECR |= EMC_NFECR_PRDY; ++ ++ /* Wait for completion */ ++ __nand_ecc_decode_sync(); ++ __nand_ecc_disable(); ++ ++ /* Check decoding */ ++ stat = REG_EMC_NFINTS; ++ ++ if (stat & EMC_NFINTS_ERR) { ++ /* Error occurred */ ++ if (stat & EMC_NFINTS_UNCOR) { ++ printk("NAND: Uncorrectable ECC error\n"); ++ return -1; ++ } else { ++ u32 errcnt = (stat & EMC_NFINTS_ERRCNT_MASK) >> EMC_NFINTS_ERRCNT_BIT; ++ switch (errcnt) { ++ case 4: ++ jzsoc_rs_correct(dat, (REG_EMC_NFERR3 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR3 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); ++ /* FALL-THROUGH */ ++ case 3: ++ jzsoc_rs_correct(dat, (REG_EMC_NFERR2 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR2 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); ++ /* FALL-THROUGH */ ++ case 2: ++ jzsoc_rs_correct(dat, (REG_EMC_NFERR1 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR1 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); ++ /* FALL-THROUGH */ ++ case 1: ++ jzsoc_rs_correct(dat, (REG_EMC_NFERR0 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR0 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); ++ return 0; ++ default: ++ break; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int jzsoc_nand_calculate_rs_ecc(struct mtd_info* mtd, const u_char* dat, ++ u_char* ecc_code) ++{ ++ volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0; ++ short i; ++ ++ __nand_ecc_encode_sync(); ++ __nand_ecc_disable(); ++ ++ for(i = 0; i < PAR_SIZE; i++) { ++ ecc_code[i] = *paraddr++; ++ } ++ ++ return 0; ++} ++ ++#endif /* CONFIG_MTD_HW_RS_ECC */ ++ ++/* Nand optimized functions */ ++static int dma_chan; ++static unsigned int dma_src_phys_addr, dma_dst_phys_addr; ++extern int jz_request_dma(int dev_id, const char *dev_str, ++ irqreturn_t (*irqhandler)(int, void *), ++ unsigned long irqflags, void *irq_dev_id); ++ ++static void dma_setup(void) ++{ ++ /* Request DMA channel and setup irq handler */ ++ dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", NULL, IRQF_DISABLED, NULL); ++ if (dma_chan < 0) { ++ printk("Setup irq for nand failed!\n"); ++ return; ++ } else ++ printk("Nand DMA request channel %d.\n",dma_chan); ++} ++ ++static void jz4740_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ int i; ++ struct nand_chip *chip = mtd->priv; ++ ++ if ((len <= 32) || (len & 0xf) || ((u32)buf >= (u32)high_memory)) ++ { ++ for (i = 0; i < len; i++) ++ buf[i] = readb(chip->IO_ADDR_R); ++ } else { ++ REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO; ++ dma_src_phys_addr = CPHYSADDR(chip->IO_ADDR_R); ++ dma_dst_phys_addr = CPHYSADDR(buf); ++ dma_cache_inv((u32)buf, len); ++ REG_DMAC_DSAR(dma_chan) = dma_src_phys_addr; ++ REG_DMAC_DTAR(dma_chan) = dma_dst_phys_addr; ++ REG_DMAC_DTCR(dma_chan) = len / 16; ++ REG_DMAC_DCMD(dma_chan) = DMAC_DCMD_DAI | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_16BYTE; ++ REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; ++ REG_DMAC_DMACR = DMAC_DMACR_DMAE; /* global DMA enable bit */ ++ ++ while(!(REG_DMAC_DCCSR(dma_chan) & DMAC_DCCSR_TT)); ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ __dmac_channel_clear_transmit_end(dma_chan); ++ } ++} ++ ++static void jz4740_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ int i; ++ struct nand_chip *chip = mtd->priv; ++ ++ if ((len <= 32) || (len & 0xf) || ((u32)buf >= (u32)high_memory)) ++ { ++ for (i = 0; i < len; i++) ++ writeb(buf[i], chip->IO_ADDR_W); ++ } else { ++ REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO; ++ dma_dst_phys_addr = CPHYSADDR(chip->IO_ADDR_R); ++ dma_src_phys_addr = CPHYSADDR(buf); ++ dma_cache_wback((unsigned long)buf, len); ++ REG_DMAC_DSAR(dma_chan) = dma_src_phys_addr; ++ REG_DMAC_DTAR(dma_chan) = dma_dst_phys_addr; ++ REG_DMAC_DTCR(dma_chan) = len / 16; ++ REG_DMAC_DCMD(dma_chan) = DMAC_DCMD_SAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_16BYTE ; ++ REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; ++ REG_DMAC_DMACR = DMAC_DMACR_DMAE; /* global DMA enable bit */ ++ ++ while(!(REG_DMAC_DCCSR(dma_chan) & DMAC_DCCSR_TT)); ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ __dmac_channel_clear_transmit_end(dma_chan); ++ } ++} ++ ++static int nand_read_page_hwecc_rs_planes(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf) ++{ ++ int i, eccsize = chip->ecc.size; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps >> 1; ++ uint8_t *p; ++ uint8_t *ecc_calc = chip->buffers->ecccalc; ++ uint8_t *ecc_code = chip->buffers->ecccode; ++ uint32_t *eccpos = chip->ecc.layout->eccpos; ++ uint32_t page; ++ uint8_t flag = 0; ++ int oobsize = mtd->oobsize >> 1; ++ int ppb = mtd->erasesize / mtd->writesize; ++ int ecctotal = chip->ecc.total >> 1; ++ ++ page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ ++ ++ /* Read first page */ ++ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); ++ chip->read_buf(mtd, chip->oob_poi, oobsize); ++ for (i = 0; i < ecctotal; i++) { ++ ecc_code[i] = chip->oob_poi[eccpos[i]]; ++ if (ecc_code[i] != 0xff) flag = 1; ++ } ++ ++ p = buf; ++ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0x00, -1); ++ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ int stat; ++ if (flag) { ++ chip->ecc.hwctl(mtd, NAND_ECC_READ); ++ chip->read_buf(mtd, p, eccsize); ++ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); ++ if (stat < 0) ++ mtd->ecc_stats.failed++; ++ else ++ mtd->ecc_stats.corrected += stat; ++ } ++ else { ++ chip->ecc.hwctl(mtd, NAND_ECC_READ); ++ chip->read_buf(mtd, p, eccsize); ++ } ++ } ++ /* Read second page */ ++ page += ppb; ++ flag = 0; ++ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); ++ chip->read_buf(mtd, chip->oob_poi + oobsize, oobsize); ++ for (i = 0; i < ecctotal; i++) { ++ ecc_code[i] = chip->oob_poi[oobsize + eccpos[i]]; ++ if (ecc_code[i] != 0xff) flag = 1; ++ } ++ ++ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0x00, -1); ++ eccsteps = chip->ecc.steps >> 1; ++ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ int stat; ++ if (flag) { ++ chip->ecc.hwctl(mtd, NAND_ECC_READ); ++ chip->read_buf(mtd, p, eccsize); ++ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); ++ if (stat < 0) ++ mtd->ecc_stats.failed++; ++ else ++ mtd->ecc_stats.corrected += stat; ++ } ++ else { ++ chip->ecc.hwctl(mtd, NAND_ECC_READ); ++ chip->read_buf(mtd, p, eccsize); ++ } ++ } ++ ++ return 0; ++} ++ ++static int nand_read_oob_std_planes(struct mtd_info *mtd, struct nand_chip *chip, ++ int global_page, int sndcmd) ++{ ++ int page; ++ int oobsize = mtd->oobsize >> 1; ++ int ppb = mtd->erasesize / mtd->writesize; ++ ++ page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ ++ ++ /* Read first page OOB */ ++ if (sndcmd) { ++ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); ++ } ++ chip->read_buf(mtd, chip->oob_poi, oobsize); ++ /* Read second page OOB */ ++ page += ppb; ++ if (sndcmd) { ++ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); ++ sndcmd = 0; ++ } ++ chip->read_buf(mtd, chip->oob_poi+oobsize, oobsize); ++ return 0; ++} ++ ++static int nand_write_oob_std_planes(struct mtd_info *mtd, struct nand_chip *chip, ++ int global_page) ++{ ++ int status = 0,page; ++ int pagesize = mtd->writesize >> 1; ++ int oobsize = mtd->oobsize >> 1; ++ int ppb = mtd->erasesize / mtd->writesize; ++ const uint8_t *buf = chip->oob_poi; ++ ++ page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ ++ ++ /* send cmd 0x80, the MSB should be valid if realplane is 4 */ ++ if (chip->realplanenum == 2) ++ chip->cmdfunc(mtd, 0x80, pagesize, 0x00); ++ else ++ chip->cmdfunc(mtd, 0x80, pagesize, page & (1 << (chip->chip_shift - chip->page_shift))); ++ ++ chip->write_buf(mtd, buf, oobsize); ++ /* Send first command to program the OOB data */ ++ chip->cmdfunc(mtd, 0x11, -1, -1); ++ ndelay(100); ++ status = chip->waitfunc(mtd, chip); ++ ++ page += ppb; ++ buf += oobsize; ++ chip->cmdfunc(mtd, 0x81, pagesize, page); ++ chip->write_buf(mtd, buf, oobsize); ++ /* Send command to program the OOB data */ ++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); ++ /* Wait long R/B */ ++ ndelay(100); ++ status = chip->waitfunc(mtd, chip); ++ ++ return status & NAND_STATUS_FAIL ? -EIO : 0; ++} ++ ++static void nand_write_page_hwecc_planes(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf) ++{ ++ int i, eccsize = chip->ecc.size; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps >> 1; ++ uint8_t *ecc_calc = chip->buffers->ecccalc; ++ uint8_t *p = (uint8_t *)buf; ++ uint32_t *eccpos = chip->ecc.layout->eccpos; ++ int oobsize = mtd->oobsize >> 1; ++ int ppb = mtd->erasesize / mtd->writesize; ++ int ecctotal = chip->ecc.total >> 1; ++ int page; ++ ++ page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ ++ ++ /* send cmd 0x80, the MSB should be valid if realplane is 4 */ ++ if (chip->realplanenum == 2) ++ chip->cmdfunc(mtd, 0x80, 0x00, 0x00); ++ else ++ chip->cmdfunc(mtd, 0x80, 0x00, page & (1 << (chip->chip_shift - chip->page_shift))); ++ ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); ++ chip->write_buf(mtd, p, eccsize); ++ chip->ecc.calculate(mtd, p, &ecc_calc[i]); ++ } ++ for (i = 0; i < ecctotal; i++) ++ chip->oob_poi[eccpos[i]] = ecc_calc[i]; ++ ++ chip->write_buf(mtd, chip->oob_poi, oobsize); ++ ++ chip->cmdfunc(mtd, 0x11, -1, -1); /* send cmd 0x11 */ ++ ndelay(100); ++ while(!chip->dev_ready(mtd)); ++ ++ page += ppb; ++ chip->cmdfunc(mtd, 0x81, 0x00, page); /* send cmd 0x81 */ ++ eccsteps = chip->ecc.steps >> 1; ++ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); ++ chip->write_buf(mtd, p, eccsize); ++ chip->ecc.calculate(mtd, p, &ecc_calc[i]); ++ } ++ ++ for (i = 0; i < ecctotal; i++) ++ chip->oob_poi[eccpos[i]] = ecc_calc[i]; ++ ++ chip->write_buf(mtd, chip->oob_poi, oobsize); ++} ++ ++static void single_erase_cmd_planes(struct mtd_info *mtd, int global_page) ++{ ++ struct nand_chip *chip = mtd->priv; ++ ++ /* Send commands to erase a block */ ++ int page; ++ int ppb = mtd->erasesize / mtd->writesize; ++ ++ page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ ++ ++ /* send cmd 0x60, the MSB should be valid if realplane is 4 */ ++ if (chip->realplanenum == 2) ++ chip->cmdfunc(mtd, 0x60, -1, 0x00); ++ else ++ chip->cmdfunc(mtd, 0x60, -1, page & (1 << (chip->chip_shift - chip->page_shift))); ++ ++ page += ppb; ++ chip->cmdfunc(mtd, 0x60, -1, page & (~(ppb-1))); /* send cmd 0x60 */ ++ ++ chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); /* send cmd 0xd0 */ ++ /* Do not need wait R/B or check status */ ++} ++ ++/* ++ * Main initialization routine ++ */ ++int __init jznand_init(void) ++{ ++ struct nand_chip *this; ++ int nr_partitions, ret, i; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ jz_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), ++ GFP_KERNEL); ++ if (!jz_mtd) { ++ printk ("Unable to allocate JzSOC NAND MTD device structure.\n"); ++ return -ENOMEM; ++ } ++ ++ jz_mtd1 = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), ++ GFP_KERNEL); ++ if (!jz_mtd1) { ++ printk ("Unable to allocate JzSOC NAND MTD device structure 1.\n"); ++ kfree(jz_mtd); ++ return -ENOMEM; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&jz_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) jz_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ jz_mtd->priv = this; ++ ++ /* Set & initialize NAND Flash controller */ ++ jz_device_setup(); ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = (void __iomem *) NAND_DATA_PORT1; ++ this->IO_ADDR_W = (void __iomem *) NAND_DATA_PORT1; ++ this->cmd_ctrl = jz_hwcontrol; ++ this->dev_ready = jz_device_ready; ++ ++#ifdef CONFIG_MTD_HW_HM_ECC ++ this->ecc.calculate = jzsoc_nand_calculate_hm_ecc; ++ this->ecc.correct = jzsoc_nand_hm_correct_data; ++ this->ecc.hwctl = jzsoc_nand_enable_hm_hwecc; ++ this->ecc.mode = NAND_ECC_HW; ++ this->ecc.size = 256; ++ this->ecc.bytes = 3; ++ ++#endif ++ ++#ifdef CONFIG_MTD_HW_RS_ECC ++ this->ecc.calculate = jzsoc_nand_calculate_rs_ecc; ++ this->ecc.correct = jzsoc_nand_rs_correct_data; ++ this->ecc.hwctl = jzsoc_nand_enable_rs_hwecc; ++ this->ecc.mode = NAND_ECC_HW; ++ this->ecc.size = 512; ++ this->ecc.bytes = 9; ++#endif ++ ++#ifdef CONFIG_MTD_SW_HM_ECC ++ this->ecc.mode = NAND_ECC_SOFT; ++#endif ++ /* 20 us command delay time */ ++ this->chip_delay = 20; ++ ++ dma_setup(); ++ ++ /* Scan to find existance of the device */ ++ ret = nand_scan_ident(jz_mtd, NAND_MAX_CHIPS); ++ if (!ret) { ++ if (this->planenum == 2) { ++ /* reset nand functions */ ++ this->erase_cmd = single_erase_cmd_planes; ++ this->ecc.read_page = nand_read_page_hwecc_rs_planes; //Muti planes read ++ this->ecc.write_page = nand_write_page_hwecc_planes; ++ this->ecc.read_oob = nand_read_oob_std_planes; ++ this->ecc.write_oob = nand_write_oob_std_planes; ++ this->write_buf = jz4740_nand_write_buf; ++ this->read_buf = jz4740_nand_read_buf; ++ ++ printk(KERN_INFO "Nand using two-plane mode, " ++ "and resized to writesize:%d oobsize:%d blocksize:0x%x \n", ++ jz_mtd->writesize, jz_mtd->oobsize, jz_mtd->erasesize); ++ } else ++ return -ENXIO; ++ } ++ ++ /* Determine whether all the partitions will use multiple planes if supported */ ++ nr_partitions = sizeof(partition_info) / sizeof(struct mtd_partition); ++ all_use_planes = 1; ++ for (i = 0; i < nr_partitions; i++) { ++ all_use_planes &= partition_info[i].use_planes; ++ } ++ ++ if (!ret) ++ ret = nand_scan_tail(jz_mtd); ++ ++ if (ret){ ++ kfree (jz_mtd1); ++ kfree (jz_mtd); ++ return -ENXIO; ++ } ++ ++ /* Register the partitions */ ++ printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nr_partitions, jz_mtd->name); ++ ++ if ((this->planenum == 2) && !all_use_planes) { ++ for (i = 0; i < nr_partitions; i++) { ++ if (partition_info[i].use_planes) ++ add_mtd_partitions(jz_mtd, &partition_info[i], 1); ++ else ++ add_mtd_partitions(jz_mtd1, &partition_info[i], 1); ++ } ++ } else { ++ kfree(jz_mtd1); ++ add_mtd_partitions(jz_mtd, partition_info, nr_partitions); ++ } ++ return 0; ++} ++module_init(jznand_init); ++ ++/* ++ * Clean up routine ++ */ ++#ifdef MODULE ++static void __exit jznand_cleanup(void) ++{ ++ struct nand_chip *this = (struct nand_chip *) &jz_mtd[1]; ++ ++ /* Unregister partitions */ ++ del_mtd_partitions(jz_mtd); ++ ++ /* Unregister the device */ ++ del_mtd_device (jz_mtd); ++ ++ /* Free internal data buffers */ ++ kfree (this->data_buf); ++ ++ /* Free the MTD device structure */ ++ if ((this->planenum == 2) && !all_use_planes) ++ kfree (jz_mtd1); ++ kfree (jz_mtd); ++} ++module_exit(jznand_cleanup); ++#endif +diff -urN linux-2.6.24.7.old/drivers/mtd/nand/jz4750_nand.c linux-2.6.24.7/drivers/mtd/nand/jz4750_nand.c +--- linux-2.6.24.7.old/drivers/mtd/nand/jz4750_nand.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/nand/jz4750_nand.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1746 @@ ++/* ++ * linux/drivers/mtd/nand/jz4750_nand.c ++ * ++ * JZ4750 NAND driver ++ * ++ * Copyright (c) 2005 - 2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* 32bit instead of 16byte burst is used by DMA to read or ++ write NAND and BCH avoiding grabbing bus for too long */ ++#define DMAC_DCMD_DS_NAND DMAC_DCMD_DS_32BIT ++#define DIV_DS_NAND 4 ++ ++#define DMAC_DCMD_DS_BCH DMAC_DCMD_DS_32BIT ++#define DIV_DS_BCH 4 ++ ++#define DEBUG1 0 ++#if DEBUG1 ++#define dprintk(n,x...) printk(n,##x) ++#else ++#define dprintk(n,x...) ++#endif ++ ++#if defined(CONFIG_MTD_HW_BCH_8BIT) ++#define __ECC_ENCODING __ecc_encoding_8bit ++#define __ECC_DECODING __ecc_decoding_8bit ++#define ERRS_SIZE 5 /* 5 words */ ++#else ++#define __ECC_ENCODING __ecc_encoding_4bit ++#define __ECC_DECODING __ecc_decoding_4bit ++#define ERRS_SIZE 3 /* 3 words */ ++#endif ++ ++#define NAND_DATA_PORT1 0xB8000000 /* read-write area in static bank 1 */ ++#define NAND_DATA_PORT2 0xB4000000 /* read-write area in static bank 2 */ ++#define NAND_DATA_PORT3 0xAC000000 /* read-write area in static bank 3 */ ++#define NAND_DATA_PORT4 0xA8000000 /* read-write area in static bank 4 */ ++ ++#define NAND_ADDR_OFFSET0 0x00010000 /* address port offset for share mode */ ++#define NAND_CMD_OFFSET0 0x00008000 /* command port offset for share mode */ ++#define NAND_ADDR_OFFSET1 0x00000010 /* address port offset for unshare mode */ ++#define NAND_CMD_OFFSET1 0x00000008 /* command port offset for unshare mode */ ++ ++#if defined(CONFIG_MTD_NAND_DMA) ++#define USE_IRQ 1 ++enum { ++ NAND_NONE, ++ NAND_PROG, ++ NAND_READ ++}; ++static volatile u8 nand_status; ++static volatile int dma_ack = 0; ++static volatile int dma_ack1 = 0; ++static char nand_dma_chan; /* automatically select a free channel */ ++static char bch_dma_chan = 0; /* fixed to channel 0 */ ++static u32 *errs; ++static jz_dma_desc_8word *dma_desc_enc, *dma_desc_enc1, *dma_desc_dec, *dma_desc_dec1, *dma_desc_dec2, ++ *dma_desc_nand_prog, *dma_desc_nand_read; ++static u32 *pval_nand_ddr; ++static u8 *pval_nand_cmd_pgprog; /* for sending 0x11 or 0x10 when programing*/ ++#if defined(CONFIG_MTD_NAND_DMABUF) ++u8 *prog_buf, *read_buf; ++#endif ++DECLARE_WAIT_QUEUE_HEAD(nand_prog_wait_queue); ++DECLARE_WAIT_QUEUE_HEAD(nand_read_wait_queue); ++#endif ++ ++struct buf_be_corrected { ++ u8 *data; ++ u8 *oob; ++}; ++ ++static u32 addr_offset; ++static u32 cmd_offset; ++ ++extern int global_page; /* for two-plane operations */ ++ ++/* ++ * MTD structure for JzSOC board ++ */ ++static struct mtd_info *jz_mtd = NULL; ++extern struct mtd_info *jz_mtd1; ++extern char all_use_planes; ++ ++/* ++ * Define partitions for flash devices ++ */ ++#if defined(CONFIG_JZ4750_FUWA) || defined(CONFIG_JZ4750D_FUWA1) ++static struct mtd_partition partition_info[] = { ++ {name:"NAND BOOT partition", ++ offset:0 * 0x100000, ++ size:4 * 0x100000, ++ use_planes: 0}, ++ {name:"NAND KERNEL partition", ++ offset:4 * 0x100000, ++ size:4 * 0x100000, ++ use_planes: 0}, ++ {name:"NAND ROOTFS partition", ++ offset:8 * 0x100000, ++ size:120 * 0x100000, ++ use_planes: 1}, ++ {name:"NAND DATA1 partition", ++ offset:128 * 0x100000, ++ size:128 * 0x100000, ++ use_planes: 1}, ++ {name:"NAND DATA2 partition", ++ offset:256 * 0x100000, ++ size:256 * 0x100000, ++ use_planes: 1}, ++ {name:"NAND VFAT partition", ++ offset:512 * 0x100000, ++ size:512 * 0x100000, ++ use_planes: 1}, ++}; ++ ++/* Define max reserved bad blocks for each partition. ++ * This is used by the mtdblock-jz.c NAND FTL driver only. ++ * ++ * The NAND FTL driver reserves some good blocks which can't be ++ * seen by the upper layer. When the bad block number of a partition ++ * exceeds the max reserved blocks, then there is no more reserved ++ * good blocks to be used by the NAND FTL driver when another bad ++ * block generated. ++ */ ++static int partition_reserved_badblocks[] = { ++ 2, /* reserved blocks of mtd0 */ ++ 2, /* reserved blocks of mtd1 */ ++ 10, /* reserved blocks of mtd2 */ ++ 10, /* reserved blocks of mtd3 */ ++ 20, /* reserved blocks of mtd4 */ ++ 20 ++}; /* reserved blocks of mtd5 */ ++#endif /* CONFIG_JZ4750_FUWA or CONFIG_JZ4750_APUS */ ++ ++#if defined(CONFIG_JZ4750_APUS) ++static struct mtd_partition partition_info[] = { ++ {name:"NAND BOOT partition", ++ offset:0 * 0x100000, ++ size:4 * 0x100000, ++ use_planes: 0}, ++ {name:"NAND KERNEL partition", ++ offset:4 * 0x100000, ++ size:4 * 0x100000, ++ use_planes: 0}, ++ {name:"NAND ROOTFS partition", ++ offset:8 * 0x100000, ++ size:504 * 0x100000, ++ use_planes: 0}, ++ {name:"NAND VFAT partition", ++ offset:512 * 0x100000, ++ size:512 * 0x100000, ++ use_planes: 1}, ++}; ++ ++ ++/* Define max reserved bad blocks for each partition. ++ * This is used by the mtdblock-jz.c NAND FTL driver only. ++ * ++ * The NAND FTL driver reserves some good blocks which can't be ++ * seen by the upper layer. When the bad block number of a partition ++ * exceeds the max reserved blocks, then there is no more reserved ++ * good blocks to be used by the NAND FTL driver when another bad ++ * block generated. ++ */ ++static int partition_reserved_badblocks[] = { ++ 2, /* reserved blocks of mtd0 */ ++ 2, /* reserved blocks of mtd1 */ ++ 10, /* reserved blocks of mtd2 */ ++ 10, /* reserved blocks of mtd3 */ ++}; ++#endif /* CONFIG_JZ4750_FUWA or CONFIG_JZ4750_APUS */ ++ ++/*------------------------------------------------------------------------- ++ * Following three functions are exported and used by the mtdblock-jz.c ++ * NAND FTL driver only. ++ */ ++ ++unsigned short get_mtdblock_write_verify_enable(void) ++{ ++#ifdef CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE ++ return 1; ++#endif ++ return 0; ++} ++ ++EXPORT_SYMBOL(get_mtdblock_write_verify_enable); ++ ++unsigned short get_mtdblock_oob_copies(void) ++{ ++ return CONFIG_MTD_OOB_COPIES; ++} ++ ++EXPORT_SYMBOL(get_mtdblock_oob_copies); ++ ++int *get_jz_badblock_table(void) ++{ ++ return partition_reserved_badblocks; ++} ++ ++EXPORT_SYMBOL(get_jz_badblock_table); ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void jz_hwcontrol(struct mtd_info *mtd, int dat, u32 ctrl) ++{ ++ struct nand_chip *this = (struct nand_chip *)(mtd->priv); ++ u32 nandaddr = (u32)this->IO_ADDR_W; ++ extern u8 nand_nce; /* defined in nand_base.c, indicates which chip select is used for current nand chip */ ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ if (ctrl & NAND_NCE) { ++ switch (nand_nce) { ++ case NAND_NCE1: ++ this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT1; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; ++ REG_EMC_NFCSR |= EMC_NFCSR_NFCE1; ++ break; ++ case NAND_NCE2: ++ this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT2; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; ++ REG_EMC_NFCSR |= EMC_NFCSR_NFCE2; ++ break; ++ case NAND_NCE3: ++ this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT3; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; ++ REG_EMC_NFCSR |= EMC_NFCSR_NFCE3; ++ break; ++ case NAND_NCE4: ++ this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT4; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; ++ REG_EMC_NFCSR |= EMC_NFCSR_NFCE4; ++ break; ++ default: ++ printk("error: no nand_nce 0x%x\n",nand_nce); ++ break; ++ } ++ } else { ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; ++ REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; ++ } ++ ++ if (ctrl & NAND_ALE) ++ nandaddr = (u32)((u32)(this->IO_ADDR_W) | addr_offset); ++ else ++ nandaddr = (u32)((u32)(this->IO_ADDR_W) & ~addr_offset); ++ if (ctrl & NAND_CLE) ++ nandaddr = (u32)(nandaddr | cmd_offset); ++ else ++ nandaddr = (u32)(nandaddr & ~cmd_offset); ++ } ++ ++ this->IO_ADDR_W = (void __iomem *)nandaddr; ++ if (dat != NAND_CMD_NONE) { ++ writeb(dat, this->IO_ADDR_W); ++ /* printk("write cmd:0x%x to 0x%x\n",dat,(u32)this->IO_ADDR_W); */ ++ } ++} ++ ++static int jz_device_ready(struct mtd_info *mtd) ++{ ++ int ready, wait = 10; ++ while (wait--); ++ ready = __gpio_get_pin(91); ++ return ready; ++} ++ ++/* ++ * EMC setup ++ */ ++static void jz_device_setup(void) ++{ ++// PORT 0: ++// PORT 1: ++// PORT 2: ++// PIN/BIT N FUNC0 FUNC1 ++// 21 CS1# - ++// 22 CS2# - ++// 23 CS3# - ++// 24 CS4# - ++#define GPIO_CS2_N (32*2+22) ++#define GPIO_CS3_N (32*2+23) ++#define GPIO_CS4_N (32*2+24) ++#define SMCR_VAL 0x0d444400 ++ ++ __gpio_as_nand_8bit(1); ++ /* Set NFE bit */ ++ REG_EMC_NFCSR |= EMC_NFCSR_NFE1; ++ /* Read/Write timings */ ++ REG_EMC_SMCR1 = SMCR_VAL; ++ ++#if defined(CONFIG_MTD_NAND_CS2) ++ __gpio_as_func0(GPIO_CS2_N); ++ /* Set NFE bit */ ++ REG_EMC_NFCSR |= EMC_NFCSR_NFE2; ++ /* Read/Write timings */ ++ REG_EMC_SMCR2 = SMCR_VAL; ++#endif ++ ++#if defined(CONFIG_MTD_NAND_CS3) ++ __gpio_as_func0(GPIO_CS3_N); ++ /* Set NFE bit */ ++ REG_EMC_NFCSR |= EMC_NFCSR_NFE3; ++ /* Read/Write timings */ ++ REG_EMC_SMCR3 = SMCR_VAL; ++#endif ++ ++#if defined(CONFIG_MTD_NAND_CS4) ++ __gpio_as_func0(GPIO_CS4_N); ++ /* Set NFE bit */ ++ REG_EMC_NFCSR |= EMC_NFCSR_NFE4; ++ /* Read/Write timings */ ++ REG_EMC_SMCR4 = SMCR_VAL; ++#endif ++} ++ ++#ifdef CONFIG_MTD_HW_BCH_ECC ++ ++static void jzsoc_nand_enable_bch_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct nand_chip *this = (struct nand_chip *)(mtd->priv); ++ int eccsize = this->ecc.size; ++ int eccbytes = this->ecc.bytes; ++ int eccsteps = this->ecc.steps / this->planenum; ++ int oob_per_eccsize = this->ecc.layout->eccpos[0] / eccsteps; ++ ++ REG_BCH_INTS = 0xffffffff; ++ if (mode == NAND_ECC_READ) { ++ __ECC_DECODING(); ++ __ecc_cnt_dec(eccsize + oob_per_eccsize + eccbytes); ++#if defined(CONFIG_MTD_NAND_DMA) ++ __ecc_dma_enable(); ++#endif ++ } ++ ++ if (mode == NAND_ECC_WRITE) { ++ __ECC_ENCODING(); ++ __ecc_cnt_enc(eccsize + oob_per_eccsize); ++#if defined(CONFIG_MTD_NAND_DMA) ++ __ecc_dma_enable(); ++#endif ++ } ++} ++ ++/** ++ * bch_correct ++ * @dat: data to be corrected ++ * @idx: the index of error bit in an eccsize ++ */ ++static void bch_correct(struct mtd_info *mtd, u8 * dat, int idx) ++{ ++ struct nand_chip *this = (struct nand_chip *)(mtd->priv); ++ int eccsize = this->ecc.size; ++ int eccsteps = this->ecc.steps / this->planenum; ++ int ecc_pos = this->ecc.layout->eccpos[0]; ++ int oob_per_eccsize = ecc_pos / eccsteps; ++ int i, bit; /* the 'bit' of i byte is error */ ++ ++ i = (idx - 1) >> 3; ++ bit = (idx - 1) & 0x7; ++ ++ dprintk("error:i=%d, bit=%d\n",i,bit); ++ ++ if (i < eccsize){ ++ ((struct buf_be_corrected *)dat)->data[i] ^= (1 << bit); ++ } else if (i < eccsize + oob_per_eccsize) { ++ ((struct buf_be_corrected *)dat)->oob[i-eccsize] ^= (1 << bit); ++ } ++} ++ ++#if defined(CONFIG_MTD_NAND_DMA) ++ ++/** ++ * jzsoc_nand_bch_correct_data ++ * @mtd: mtd info structure ++ * @dat: data to be corrected ++ * @errs0: pointer to the dma target buffer of bch decoding which stores BHINTS and ++ * BHERR0~3(8-bit BCH) or BHERR0~1(4-bit BCH) ++ * @calc_ecc: no used ++ */ ++static int jzsoc_nand_bch_correct_data(struct mtd_info *mtd, u_char * dat, u_char * errs0, u_char * calc_ecc) ++{ ++ u32 stat; ++ u32 *errs = (u32 *)errs0; ++ ++ if (REG_DMAC_DCCSR(0) & DMAC_DCCSR_BERR) { ++ stat = errs[0]; ++ dprintk("stat=%x err0:%x err1:%x \n", stat, errs[1], errs[2]); ++ ++ if (stat & BCH_INTS_ERR) { ++ if (stat & BCH_INTS_UNCOR) { ++ printk("NAND: Uncorrectable ECC error\n"); ++ return -1; ++ } else { ++ u32 errcnt = (stat & BCH_INTS_ERRC_MASK) >> BCH_INTS_ERRC_BIT; ++ switch (errcnt) { ++#if defined(CONFIG_MTD_HW_BCH_8BIT) ++ case 8: ++ bch_correct(mtd, dat, (errs[4] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); ++ case 7: ++ bch_correct(mtd, dat, (errs[4] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); ++ case 6: ++ bch_correct(mtd, dat, (errs[3] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); ++ case 5: ++ bch_correct(mtd, dat, (errs[3] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); ++#endif ++ case 4: ++ bch_correct(mtd, dat, (errs[2] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); ++ case 3: ++ bch_correct(mtd, dat, (errs[2] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); ++ case 2: ++ bch_correct(mtd, dat, (errs[1] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); ++ case 1: ++ bch_correct(mtd, dat, (errs[1] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); ++ default: ++ break; ++ } ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++#else /* cpu mode */ ++ ++/** ++ * jzsoc_nand_bch_correct_data ++ * @mtd: mtd info structure ++ * @dat: data to be corrected ++ * @read_ecc: pointer to ecc buffer calculated when nand writing ++ * @calc_ecc: no used ++ */ ++static int jzsoc_nand_bch_correct_data(struct mtd_info *mtd, u_char * dat, u_char * read_ecc, u_char * calc_ecc) ++{ ++ struct nand_chip *this = (struct nand_chip *)(mtd->priv); ++ int eccsize = this->ecc.size; ++ int eccbytes = this->ecc.bytes; ++ int eccsteps = this->ecc.steps / this->planenum; ++ int ecc_pos = this->ecc.layout->eccpos[0]; ++ int oob_per_eccsize = ecc_pos / eccsteps; ++ short k; ++ u32 stat; ++ ++ /* Write data to REG_BCH_DR */ ++ for (k = 0; k < eccsize; k++) { ++ REG_BCH_DR = ((struct buf_be_corrected *)dat)->data[k]; ++ } ++ /* Write oob to REG_BCH_DR */ ++ for (k = 0; k < oob_per_eccsize; k++) { ++ REG_BCH_DR = ((struct buf_be_corrected *)dat)->oob[k]; ++ } ++ /* Write parities to REG_BCH_DR */ ++ for (k = 0; k < eccbytes; k++) { ++ REG_BCH_DR = read_ecc[k]; ++ } ++ ++ /* Wait for completion */ ++ __ecc_decode_sync(); ++ __ecc_disable(); ++ ++ /* Check decoding */ ++ stat = REG_BCH_INTS; ++ ++ if (stat & BCH_INTS_ERR) { ++ /* Error occurred */ ++ if (stat & BCH_INTS_UNCOR) { ++ printk("NAND: Uncorrectable ECC error--\n"); ++ return -1; ++ } else { ++ u32 errcnt = (stat & BCH_INTS_ERRC_MASK) >> BCH_INTS_ERRC_BIT; ++ switch (errcnt) { ++#if defined(CONFIG_MTD_HW_BCH_8BIT) ++ case 8: ++ bch_correct(mtd, dat, (REG_BCH_ERR3 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); ++ /* FALL-THROUGH */ ++ case 7: ++ bch_correct(mtd, dat, (REG_BCH_ERR3 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); ++ /* FALL-THROUGH */ ++ case 6: ++ bch_correct(mtd, dat, (REG_BCH_ERR2 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); ++ /* FALL-THROUGH */ ++ case 5: ++ bch_correct(mtd, dat, (REG_BCH_ERR2 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); ++ /* FALL-THROUGH */ ++#endif ++ case 4: ++ bch_correct(mtd, dat, (REG_BCH_ERR1 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); ++ /* FALL-THROUGH */ ++ case 3: ++ bch_correct(mtd, dat, (REG_BCH_ERR1 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); ++ /* FALL-THROUGH */ ++ case 2: ++ bch_correct(mtd, dat, (REG_BCH_ERR0 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); ++ /* FALL-THROUGH */ ++ case 1: ++ bch_correct(mtd, dat, (REG_BCH_ERR0 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); ++ return 0; ++ default: ++ break; ++ } ++ } ++ } ++ ++ return 0; ++} ++#endif /* CONFIG_MTD_NAND_DMA */ ++ ++static int jzsoc_nand_calculate_bch_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code) ++{ ++ struct nand_chip *this = (struct nand_chip *)(mtd->priv); ++ int eccsize = this->ecc.size; ++ int eccbytes = this->ecc.bytes; ++ int eccsteps = this->ecc.steps / this->planenum; ++ int ecc_pos = this->ecc.layout->eccpos[0]; ++ int oob_per_eccsize = ecc_pos / eccsteps; ++ volatile u8 *paraddr = (volatile u8 *)BCH_PAR0; ++ short i; ++ ++ /* Write data to REG_BCH_DR */ ++ for (i = 0; i < eccsize; i++) { ++ REG_BCH_DR = ((struct buf_be_corrected *)dat)->data[i]; ++ } ++ /* Write oob to REG_BCH_DR */ ++ for (i = 0; i < oob_per_eccsize; i++) { ++ REG_BCH_DR = ((struct buf_be_corrected *)dat)->oob[i]; ++ } ++ __ecc_encode_sync(); ++ __ecc_disable(); ++ ++ for (i = 0; i < eccbytes; i++) { ++ ecc_code[i] = *paraddr++; ++ } ++ ++ return 0; ++} ++ ++#if defined(CONFIG_MTD_NAND_DMA) ++ ++/** ++ * nand_write_page_hwecc_bch - [REPLACABLE] hardware ecc based page write function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: data buffer ++ */ ++static void nand_write_page_hwecc_bch0(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t * buf, u8 cmd_pgprog) ++{ ++ int eccsize = chip->ecc.size; ++ int eccsteps = chip->ecc.steps / chip->planenum; ++ int eccbytes = chip->ecc.bytes; ++ int ecc_pos = chip->ecc.layout->eccpos[0]; ++ int oob_per_eccsize = ecc_pos / eccsteps; ++ int pagesize = mtd->writesize / chip->planenum; ++ int oobsize = mtd->oobsize / chip->planenum; ++ int i, err, timeout; ++ const u8 *databuf; ++ u8 *oobbuf; ++ jz_dma_desc_8word *desc; ++ ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ memcpy(prog_buf, buf, pagesize); ++ memcpy(prog_buf + pagesize, chip->oob_poi, oobsize); ++ dma_cache_wback_inv((u32)prog_buf, pagesize + oobsize); ++#else ++ databuf = buf; ++ oobbuf = chip->oob_poi; ++ ++ /* descriptors for encoding data blocks */ ++ desc = dma_desc_enc; ++ for (i = 0; i < eccsteps; i++) { ++ desc->dsadr = CPHYSADDR((u32)databuf) + i * eccsize; /* DMA source address */ ++ desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */ ++ dprintk("dma_desc_enc:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, ++ desc->ddadr); ++ desc++; ++ } ++ ++ /* descriptors for encoding oob blocks */ ++ desc = dma_desc_enc1; ++ for (i = 0; i < eccsteps; i++) { ++ desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address, 28/4 = 7bytes */ ++ desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */ ++ dprintk("dma_desc_enc1:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, ++ desc->ddadr); ++ desc++; ++ } ++ ++ /* descriptor for nand programing data block */ ++ desc = dma_desc_nand_prog; ++ desc->dsadr = CPHYSADDR((u32)databuf); /* DMA source address */ ++ desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_W); /* It will be changed when using multiply chip select */ ++ dprintk("dma_desc_nand_prog:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, ++ desc->ddadr); ++ ++ /* descriptor for nand programing oob block */ ++ desc++; ++ desc->dsadr = CPHYSADDR((u32)oobbuf); /* DMA source address */ ++ desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_W); /* It will be changed when using multiply chip select */ ++ dprintk("dma_desc_oob_prog:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, ++ desc->ddadr); ++ ++ /* descriptor for __nand_cmd(CMD_PGPROG) */ ++ desc++; ++ *pval_nand_cmd_pgprog = cmd_pgprog; ++ desc->dsadr = CPHYSADDR((u32)pval_nand_cmd_pgprog); ++ desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_R | cmd_offset); /* DMA target address: cmdport */ ++ if (cmd_pgprog == 0x10) ++ desc->dcmd |= DMAC_DCMD_LINK; /* __nand_sync() by a DMA descriptor */ ++ else if (cmd_pgprog == 0x11) ++ desc->dcmd &= ~DMAC_DCMD_LINK; /* __nand_sync() by polling */ ++ ++ dma_cache_wback_inv((u32)dma_desc_enc, (eccsteps * 2 + 2 + 1) * (sizeof(jz_dma_desc_8word))); ++ dma_cache_wback_inv((u32)databuf, pagesize); ++ dma_cache_wback_inv((u32)oobbuf, oobsize); ++ /* 4*6: pval_nand_ddr, pval_nand_dcs, pval_bch_ddr, pval_bch_dcs, dummy, pval_nand_cmd_pgprog */ ++ dma_cache_wback_inv((u32)pval_nand_ddr, 4 * 8); /* 8 words, a cache line */ ++#endif ++ ++ REG_DMAC_DCCSR(bch_dma_chan) = 0; ++ REG_DMAC_DCCSR(nand_dma_chan) = 0; ++ ++ /* Setup DMA descriptor address */ ++ REG_DMAC_DDA(bch_dma_chan) = CPHYSADDR((u32)dma_desc_enc); ++ REG_DMAC_DDA(nand_dma_chan) = CPHYSADDR((u32)dma_desc_nand_prog); ++ ++ /* Setup request source */ ++ REG_DMAC_DRSR(bch_dma_chan) = DMAC_DRSR_RS_BCH_ENC; ++ REG_DMAC_DRSR(nand_dma_chan) = DMAC_DRSR_RS_AUTO; ++ ++ /* Setup DMA channel control/status register */ ++ REG_DMAC_DCCSR(bch_dma_chan) = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; /* descriptor transfer, clear status, start channel */ ++ ++ /* Enable DMA */ ++ REG_DMAC_DMACR(0) |= DMAC_DMACR_DMAE; ++ REG_DMAC_DMACR(nand_dma_chan/HALF_DMA_NUM) |= DMAC_DMACR_DMAE; ++ ++ /* Enable BCH encoding */ ++ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); ++ ++ dma_ack1 = 0; ++ nand_status = NAND_PROG; ++ ++ /* DMA doorbell set -- start DMA now ... */ ++ __dmac_channel_set_doorbell(bch_dma_chan); ++ ++#if USE_IRQ ++ if (cmd_pgprog == 0x10) { ++ dprintk("nand prog before wake up\n"); ++ err = wait_event_interruptible_timeout(nand_prog_wait_queue, dma_ack1, 3 * HZ); ++ nand_status = NAND_NONE; ++ dprintk("nand prog after wake up\n"); ++ if (!err) { ++ printk("*** NAND WRITE, Warning, wait event 3s timeout!\n"); ++ dump_jz_dma_channel(0); ++ dump_jz_dma_channel(nand_dma_chan); ++ printk("REG_BCH_CR=%x REG_BCH_CNT=0x%x REG_BCH_INTS=%x\n", REG_BCH_CR, REG_BCH_CNT, REG_BCH_INTS); ++ } ++ dprintk("timeout remain = %d\n", err); ++ } else if (cmd_pgprog == 0x11) { ++ timeout = 100000; ++ while ((!__dmac_channel_transmit_end_detected(nand_dma_chan)) && (timeout--)); ++ if (timeout <= 0) ++ printk("two-plane prog 0x11 timeout!\n"); ++ } ++#else ++ timeout = 100000; ++ while ((!__dmac_channel_transmit_end_detected(nand_dma_chan)) && (timeout--)); ++ while(!chip->dev_ready(mtd)); ++ if (timeout <= 0) ++ printk("not use irq, prog timeout!\n"); ++#endif ++} ++ ++static void nand_write_page_hwecc_bch(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t * buf) ++{ ++ nand_write_page_hwecc_bch0(mtd, chip, buf, 0x10); ++} ++ ++static void nand_write_page_hwecc_bch_planes(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t * buf) ++{ ++ int page; ++ int pagesize = mtd->writesize >> 1; ++ int ppb = mtd->erasesize / mtd->writesize; ++ ++ page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ ++ ++ /* send cmd 0x80, the MSB should be valid if realplane is 4 */ ++ if (chip->realplanenum == 2) ++ chip->cmdfunc(mtd, 0x80, 0x00, 0x00); ++ else ++ chip->cmdfunc(mtd, 0x80, 0x00, page & (1 << (chip->chip_shift - chip->page_shift))); ++ ++ nand_write_page_hwecc_bch0(mtd, chip, buf, 0x11); ++ chip->cmdfunc(mtd, 0x81, 0x00, page + ppb); ++ nand_write_page_hwecc_bch0(mtd, chip, buf + pagesize, 0x10); ++} ++ ++#else /* nand write in cpu mode */ ++ ++static void nand_write_page_hwecc_bch(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf) ++{ ++ int i, eccsize = chip->ecc.size; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps / chip->planenum; ++ int oob_per_eccsize = chip->ecc.layout->eccpos[0] / eccsteps; ++ int oobsize = mtd->oobsize / chip->planenum; ++ int ecctotal = chip->ecc.total / chip->planenum; ++ uint8_t *p = (uint8_t *)buf; ++ uint8_t *ecc_calc = chip->buffers->ecccalc; ++ uint32_t *eccpos = chip->ecc.layout->eccpos; ++ static struct buf_be_corrected buf_calc0; ++ struct buf_be_corrected *buf_calc = &buf_calc0; ++ ++ for (i = 0; i < eccsteps; i++, p += eccsize) { ++ buf_calc->data = (u8 *)buf + eccsize * i; ++ buf_calc->oob = chip->oob_poi + oob_per_eccsize * i; ++ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); ++ chip->ecc.calculate(mtd, (u8 *)buf_calc, &ecc_calc[eccbytes*i]); ++ chip->write_buf(mtd, p, eccsize); ++ } ++ ++ for (i = 0; i < ecctotal; i++) ++ chip->oob_poi[eccpos[i]] = ecc_calc[i]; ++ ++ chip->write_buf(mtd, chip->oob_poi, oobsize); ++} ++ ++/* nand write using two-plane mode */ ++static void nand_write_page_hwecc_bch_planes(struct mtd_info *mtd, struct nand_chip *chip, ++ const uint8_t *buf) ++{ ++ int pagesize = mtd->writesize >> 1; ++ int ppb = mtd->erasesize / mtd->writesize; ++ int page; ++ ++ page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ ++ ++ /* send cmd 0x80, the MSB should be valid if realplane is 4 */ ++ if (chip->realplanenum == 2) ++ chip->cmdfunc(mtd, 0x80, 0x00, 0x00); ++ else ++ chip->cmdfunc(mtd, 0x80, 0x00, page & (1 << (chip->chip_shift - chip->page_shift))); ++ ++ nand_write_page_hwecc_bch(mtd, chip, buf); ++ ++ chip->cmdfunc(mtd, 0x11, -1, -1); /* send cmd 0x11 */ ++ ndelay(100); ++ while(!chip->dev_ready(mtd)); ++ ++ chip->cmdfunc(mtd, 0x81, 0x00, page + ppb); /* send cmd 0x81 */ ++ nand_write_page_hwecc_bch(mtd, chip, buf + pagesize); ++} ++#endif /* CONFIG_MTD_NAND_DMA */ ++ ++/** ++ * nand_read_page_hwecc_bch - [REPLACABLE] hardware ecc based page read function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: buffer to store read data ++ * ++ * Not for syndrome calculating ecc controllers which need a special oob layout ++ */ ++#if defined(CONFIG_MTD_NAND_DMA) ++static int nand_read_page_hwecc_bch0(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf, u32 page) ++{ ++ int i, eccsize = chip->ecc.size; ++ int eccsteps = chip->ecc.steps / chip->planenum; ++ int eccbytes = chip->ecc.bytes; ++ int ecc_pos = chip->ecc.layout->eccpos[0]; ++ int oob_per_eccsize = ecc_pos / eccsteps; ++ int pagesize = mtd->writesize / chip->planenum; ++ int oobsize = mtd->oobsize / chip->planenum; ++ u8 *databuf, *oobbuf; ++ jz_dma_desc_8word *desc; ++ int err; ++ u32 addrport, cmdport; ++ static struct buf_be_corrected buf_correct0; ++ ++ addrport = (u32)(chip->IO_ADDR_R) | addr_offset; ++ cmdport = (u32)(chip->IO_ADDR_R) | cmd_offset; ++ ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ databuf = read_buf; ++ oobbuf = read_buf + pagesize; ++ ++ dma_cache_inv((u32)read_buf, pagesize + oobsize); // databuf should be invalidated. ++ memset(errs, 0, eccsteps * ERRS_SIZE * 4); ++ dma_cache_wback_inv((u32)errs, eccsteps * ERRS_SIZE * 4); ++#else ++ ++ databuf = buf; ++ oobbuf = chip->oob_poi; ++ ++ /* descriptor for nand reading data block */ ++ desc = dma_desc_nand_read; ++ desc->dsadr = CPHYSADDR((u32)chip->IO_ADDR_R); /* It will be changed when using multiply chip select */ ++ desc->dtadr = CPHYSADDR((u32)databuf); /* DMA target address */ ++ ++ dprintk("desc_nand_read:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, ++ desc->ddadr); ++ ++ /* descriptor for nand reading oob block */ ++ desc++; ++ desc->dsadr = CPHYSADDR((u32)chip->IO_ADDR_R); /* It will be changed when using multiply chip select */ ++ desc->dtadr = CPHYSADDR((u32)oobbuf); /* DMA target address */ ++ dprintk("desc_oob_read:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, ++ desc->ddadr); ++ ++ /* descriptors for data to be written to bch */ ++ desc = dma_desc_dec; ++ for (i = 0; i < eccsteps; i++) { ++ desc->dsadr = CPHYSADDR((u32)databuf) + i * eccsize; /* DMA source address */ ++ dprintk("dma_desc_dec:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, ++ desc->ddadr); ++ desc++; ++ } ++ ++ /* descriptors for oob to be written to bch */ ++ desc = dma_desc_dec1; ++ for (i = 0; i < eccsteps; i++) { ++ desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address */ ++ dprintk("dma_desc_dec1:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, ++ desc->ddadr); ++ desc++; ++ } ++ ++ /* descriptors for parities to be written to bch */ ++ desc = dma_desc_dec2; ++ for (i = 0; i < eccsteps; i++) { ++ desc->dsadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA source address */ ++ dprintk("dma_desc_dec2:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, ++ desc->ddadr); ++ desc++; ++ } ++ ++ dma_cache_wback_inv((u32)dma_desc_nand_read, (2 + eccsteps * 3) * (sizeof(jz_dma_desc_8word))); ++ ++ memset(errs, 0, eccsteps * ERRS_SIZE * 4); ++ dma_cache_inv((u32)databuf, pagesize); // databuf should be invalidated. ++ dma_cache_inv((u32)oobbuf, oobsize); // oobbuf should be invalidated too ++ dma_cache_wback_inv((u32)errs, eccsteps * ERRS_SIZE * 4); ++#endif ++ REG_DMAC_DCCSR(bch_dma_chan) = 0; ++ REG_DMAC_DCCSR(nand_dma_chan) = 0; ++ ++ /* Setup DMA descriptor address */ ++ REG_DMAC_DDA(nand_dma_chan) = CPHYSADDR((u32)dma_desc_nand_read); ++ REG_DMAC_DDA(bch_dma_chan) = CPHYSADDR((u32)dma_desc_dec); ++ ++ /* Setup request source */ ++ REG_DMAC_DRSR(nand_dma_chan) = DMAC_DRSR_RS_NAND; ++ REG_DMAC_DRSR(bch_dma_chan) = DMAC_DRSR_RS_BCH_DEC; ++ ++ /* Enable DMA */ ++ REG_DMAC_DMACR(0) |= DMAC_DMACR_DMAE; ++ REG_DMAC_DMACR(nand_dma_chan/HALF_DMA_NUM) |= DMAC_DMACR_DMAE; ++ ++ /* Enable BCH decoding */ ++ chip->ecc.hwctl(mtd, NAND_ECC_READ); ++ ++ dma_ack = 0; ++ nand_status = NAND_READ; ++ /* DMA doorbell set -- start nand DMA now ... */ ++ __dmac_channel_set_doorbell(nand_dma_chan); ++ ++ /* Setup DMA channel control/status register */ ++ REG_DMAC_DCCSR(nand_dma_chan) = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; ++ ++#define __nand_cmd(n) (REG8(cmdport) = (n)) ++#define __nand_addr(n) (REG8(addrport) = (n)) ++ ++ __nand_cmd(NAND_CMD_READ0); ++ ++ __nand_addr(0); ++ if (pagesize != 512) ++ __nand_addr(0); ++ ++ __nand_addr(page & 0xff); ++ __nand_addr((page >> 8) & 0xff); ++ ++ /* One more address cycle for the devices whose number of page address bits > 16 */ ++ if (((chip->chipsize >> chip->page_shift) >> 16) - 1 > 0) ++ __nand_addr((page >> 16) & 0xff); ++ ++ if (pagesize != 512) ++ __nand_cmd(NAND_CMD_READSTART); ++ ++#if USE_IRQ ++ do { ++ err = wait_event_interruptible_timeout(nand_read_wait_queue, dma_ack, 3 * HZ); ++ }while(err == -ERESTARTSYS); ++ nand_status = NAND_NONE; ++ ++ if (!err) { ++ printk("*** NAND READ, Warning, wait event 3s timeout!\n"); ++ dump_jz_dma_channel(0); ++ dump_jz_dma_channel(nand_dma_chan); ++ printk("REG_BCH_CR=%x REG_BCH_CNT=0x%x REG_BCH_INTS=%x\n", REG_BCH_CR, REG_BCH_CNT, REG_BCH_INTS); ++ printk("databuf[0]=%x\n", databuf[0]); ++ } ++ dprintk("timeout remain = %d\n", err); ++#else ++ int timeout; ++ timeout = 100000; ++ while ((!__dmac_channel_transmit_end_detected(bch_dma_chan)) && (timeout--)); ++ if (timeout <= 0) { ++ printk("not use irq, NAND READ timeout!\n"); ++ } ++#endif ++ ++ for (i = 0; i < eccsteps; i++) { ++ int stat; ++ struct buf_be_corrected *buf_correct = &buf_correct0; ++ ++ buf_correct->data = databuf + eccsize * i; ++ buf_correct->oob = oobbuf + oob_per_eccsize * i; ++ ++ stat = chip->ecc.correct(mtd, (u8 *)buf_correct, (u8 *)&errs[i * ERRS_SIZE], NULL); ++ if (stat < 0) ++ mtd->ecc_stats.failed++; ++ else ++ mtd->ecc_stats.corrected += stat; ++ } ++ ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ memcpy(buf, read_buf, pagesize); ++ memcpy(chip->oob_poi, read_buf + pagesize, oobsize); ++#endif ++ return 0; ++} ++ ++static int nand_read_page_hwecc_bch(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf) ++{ ++ u32 page = global_page; ++ ++ nand_read_page_hwecc_bch0(mtd, chip, buf, page); ++ return 0; ++} ++ ++static int nand_read_page_hwecc_bch_planes(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf) ++{ ++ u32 page; ++ int pagesize = mtd->writesize >> 1; ++ int ppb = mtd->erasesize / mtd->writesize; ++ ++ page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ ++ ++ /* read 1st page */ ++ nand_read_page_hwecc_bch0(mtd, chip, buf, page); ++ ++ /* read 2nd page */ ++ nand_read_page_hwecc_bch0(mtd, chip, buf + pagesize, page + ppb); ++ return 0; ++} ++ ++#else /* nand read in cpu mode */ ++ ++static int nand_read_page_hwecc_bch(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf) ++{ ++ int i, eccsize = chip->ecc.size; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps / chip->planenum; ++ int ecc_pos = chip->ecc.layout->eccpos[0]; ++ int oob_per_eccsize = ecc_pos / eccsteps; ++ uint8_t *ecc_calc = chip->buffers->ecccalc; ++ uint8_t *ecc_code = chip->buffers->ecccode; ++ uint32_t *eccpos = chip->ecc.layout->eccpos; ++ int pagesize = mtd->writesize / chip->planenum; ++ int oobsize = mtd->oobsize / chip->planenum; ++ int ecctotal = chip->ecc.total / chip->planenum; ++ static struct buf_be_corrected buf_correct0; ++ ++ chip->read_buf(mtd, buf, pagesize); ++ chip->read_buf(mtd, chip->oob_poi, oobsize); ++ ++ for (i = 0; i < ecctotal; i++) { ++ ecc_code[i] = chip->oob_poi[eccpos[i]]; ++ } ++ ++ for (i = 0; i < eccsteps; i++) { ++ int stat; ++ struct buf_be_corrected *buf_correct = &buf_correct0; ++ ++ buf_correct->data = buf + eccsize * i; ++ buf_correct->oob = chip->oob_poi + oob_per_eccsize * i; ++ ++ chip->ecc.hwctl(mtd, NAND_ECC_READ); ++ stat = chip->ecc.correct(mtd, (u8 *)buf_correct, &ecc_code[eccbytes*i], &ecc_calc[eccbytes*i]); ++ if (stat < 0) ++ mtd->ecc_stats.failed++; ++ else ++ mtd->ecc_stats.corrected += stat; ++ } ++ ++ return 0; ++} ++ ++static int nand_read_page_hwecc_bch_planes(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf) ++{ ++ int pagesize = mtd->writesize >> 1; ++ int ppb = mtd->erasesize / mtd->writesize; ++ uint32_t page; ++ ++ page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ ++ ++ /* Read first page */ ++ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); ++ nand_read_page_hwecc_bch(mtd, chip, buf); ++ ++ /* Read 2nd page */ ++ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page + ppb); ++ nand_read_page_hwecc_bch(mtd, chip, buf+pagesize); ++ return 0; ++} ++#endif /* CONFIG_MTD_NAND_DMA */ ++ ++#endif /* CONFIG_MTD_HW_BCH_ECC */ ++ ++/* read oob using two-plane mode */ ++static int nand_read_oob_std_planes(struct mtd_info *mtd, struct nand_chip *chip, ++ int global_page, int sndcmd) ++{ ++ int page; ++ int oobsize = mtd->oobsize >> 1; ++ int ppb = mtd->erasesize / mtd->writesize; ++ ++ page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ ++ ++ /* Read first page OOB */ ++ if (sndcmd) { ++ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); ++ } ++ chip->read_buf(mtd, chip->oob_poi, oobsize); ++ /* Read second page OOB */ ++ page += ppb; ++ if (sndcmd) { ++ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); ++ sndcmd = 0; ++ } ++ chip->read_buf(mtd, chip->oob_poi+oobsize, oobsize); ++ return 0; ++} ++ ++/* write oob using two-plane mode */ ++static int nand_write_oob_std_planes(struct mtd_info *mtd, struct nand_chip *chip, ++ int global_page) ++{ ++ int status = 0, page; ++ const uint8_t *buf = chip->oob_poi; ++ int pagesize = mtd->writesize >> 1; ++ int oobsize = mtd->oobsize >> 1; ++ int ppb = mtd->erasesize / mtd->writesize; ++ ++ page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ ++ ++ /* send cmd 0x80, the MSB should be valid if realplane is 4 */ ++ if (chip->realplanenum == 2) ++ chip->cmdfunc(mtd, 0x80, pagesize, 0x00); ++ else ++ chip->cmdfunc(mtd, 0x80, pagesize, page & (1 << (chip->chip_shift - chip->page_shift))); ++ ++ chip->write_buf(mtd, buf, oobsize); ++ /* Send first command to program the OOB data */ ++ chip->cmdfunc(mtd, 0x11, -1, -1); ++ ndelay(100); ++ status = chip->waitfunc(mtd, chip); ++ ++ page += ppb; ++ buf += oobsize; ++ chip->cmdfunc(mtd, 0x81, pagesize, page); ++ chip->write_buf(mtd, buf, oobsize); ++ /* Send command to program the OOB data */ ++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); ++ /* Wait long R/B */ ++ ndelay(100); ++ status = chip->waitfunc(mtd, chip); ++ ++ return status & NAND_STATUS_FAIL ? -EIO : 0; ++} ++ ++/* nand erase using two-plane mode */ ++static void single_erase_cmd_planes(struct mtd_info *mtd, int global_page) ++{ ++ struct nand_chip *chip = mtd->priv; ++ int page, ppb = mtd->erasesize / mtd->writesize; ++ ++ page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ ++ ++ /* send cmd 0x60, the MSB should be valid if realplane is 4 */ ++ if (chip->realplanenum == 2) ++ chip->cmdfunc(mtd, 0x60, -1, 0x00); ++ else ++ chip->cmdfunc(mtd, 0x60, -1, page & (1 << (chip->chip_shift - chip->page_shift))); ++ ++ page += ppb; ++ chip->cmdfunc(mtd, 0x60, -1, page & (~(ppb-1))); /* send cmd 0x60 */ ++ ++ chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); /* send cmd 0xd0 */ ++ /* Do not need wait R/B or check status */ ++} ++ ++#if defined(CONFIG_MTD_NAND_DMA) ++ ++#if USE_IRQ ++static irqreturn_t nand_dma_irq(int irq, void *dev_id) ++{ ++ u8 dma_chan; ++ volatile int wakeup = 0; ++ ++ dma_chan = irq - IRQ_DMA_0; ++ ++ dprintk("jz4750_dma_irq %d, channel %d\n", irq, dma_chan); ++ ++ if (__dmac_channel_transmit_halt_detected(dma_chan)) { ++ __dmac_channel_clear_transmit_halt(dma_chan); ++ wakeup = 1; ++ printk("DMA HALT\n"); ++ } ++ ++ if (__dmac_channel_address_error_detected(dma_chan)) { ++ ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ __dmac_channel_clear_address_error(dma_chan); ++ ++ REG_DMAC_DSAR(dma_chan) = 0; /* reset source address register */ ++ REG_DMAC_DTAR(dma_chan) = 0; /* reset destination address register */ ++ ++ /* clear address error in DMACR */ ++ REG_DMAC_DMACR((dma_chan / HALF_DMA_NUM)) &= ~(1 << 2); ++ wakeup = 1; ++ printk("DMA address error!\n"); ++ } ++ ++ if (__dmac_channel_descriptor_invalid_detected(dma_chan)) { ++ __dmac_channel_clear_descriptor_invalid(dma_chan); ++ wakeup = 1; ++ printk("DMA DESC INVALID\n"); ++ } ++#if 1 ++ ++ while (!__dmac_channel_transmit_end_detected(dma_chan)); ++ ++ if (__dmac_channel_count_terminated_detected(dma_chan)) { ++ dprintk("DMA CT\n"); ++ __dmac_channel_clear_count_terminated(dma_chan); ++ wakeup = 0; ++ } ++#endif ++ ++ if (__dmac_channel_transmit_end_detected(dma_chan)) { ++ dprintk("DMA TT\n"); ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ __dmac_channel_clear_transmit_end(dma_chan); ++ wakeup = 1; ++ } ++ ++ if (wakeup) { ++ dprintk("ack %d irq , wake up dma_chan %d nand_status %d\n", dma_ack, dma_chan, nand_status); ++ /* wakeup wait event */ ++ if ((dma_chan == nand_dma_chan) && (nand_status == NAND_PROG)) { ++ dprintk("nand prog dma irq, wake up----\n"); ++ dma_ack1 = 1; ++ wake_up_interruptible(&nand_prog_wait_queue); ++ } ++ ++ if ((dma_chan == bch_dma_chan) && (nand_status == NAND_READ)) { ++ dprintk("nand read irq, wake up----\n"); ++ dma_ack = 1; ++ wake_up_interruptible(&nand_read_wait_queue); ++ } ++ wakeup = 0; ++ } ++ ++ return IRQ_HANDLED; ++} ++#endif /* USE_IRQ */ ++ ++static int jz4750_nand_dma_init(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd->priv; ++ int eccsize = chip->ecc.size; ++ int eccsteps = chip->ecc.steps / chip->planenum; ++ int eccbytes = chip->ecc.bytes; ++ int ecc_pos = chip->ecc.layout->eccpos[0]; ++ int oob_per_eccsize = ecc_pos / eccsteps; ++ int pagesize = mtd->writesize / chip->planenum; ++ int oobsize = mtd->oobsize / chip->planenum; ++ int i, err; ++ jz_dma_desc_8word *desc, *dma_desc_bch_ddr, *dma_desc_nand_ddr, *dma_desc_nand_cmd_pgprog; ++ u32 *pval_nand_dcs, *pval_bch_ddr, *pval_bch_dcs, *dummy; ++ u32 next; ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ u8 *oobbuf; ++#endif ++ ++#if USE_IRQ ++ if ((nand_dma_chan = jz_request_dma(DMA_ID_NAND, "nand read or write", nand_dma_irq, IRQF_DISABLED, NULL)) < 0) { ++ printk("can't reqeust DMA nand channel.\n"); ++ return 0; ++ } ++ dprintk("nand dma channel:%d----\n", nand_dma_chan); ++ ++ if ((err = request_irq(IRQ_DMA_0 + bch_dma_chan, nand_dma_irq, IRQF_DISABLED, "bch_dma", NULL))) { ++ printk("bch_dma irq request err\n"); ++ return 0; ++ } ++#else ++ if ((nand_dma_chan = jz_request_dma(DMA_ID_NAND, "nand read or write", NULL, IRQF_DISABLED, NULL)) < 0) { ++ printk("can't reqeust DMA nand channel.\n"); ++ return 0; ++ } ++ dprintk("nand dma channel:%d----\n", nand_dma_chan); ++#endif ++ ++ __dmac_channel_enable_clk(nand_dma_chan); ++ __dmac_channel_enable_clk(bch_dma_chan); ++ ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ if (pagesize < 4096) { ++ read_buf = prog_buf = (u8 *) __get_free_page(GFP_KERNEL); ++ } else { ++ read_buf = prog_buf = (u8 *) __get_free_pages(GFP_KERNEL, 1); ++ } ++ if (!read_buf) ++ return -ENOMEM; ++#endif ++ /* space for the error reports of bch decoding((4 * 5 * eccsteps) bytes), and the space for the value ++ * of ddr and dcs of channel 0 and channel nand_dma_chan (4 * (2 + 2) bytes) */ ++ errs = (u32 *)kmalloc(4 * (2 + 2 + 5 * eccsteps), GFP_KERNEL); ++ if (!errs) ++ return -ENOMEM; ++ ++ pval_nand_ddr = errs + 5 * eccsteps; ++ pval_nand_dcs = pval_nand_ddr + 1; ++ pval_bch_ddr = pval_nand_dcs + 1; ++ pval_bch_dcs = pval_bch_ddr + 1; ++ /* space for nand prog waiting target, the content is useless */ ++ dummy = pval_bch_dcs + 1; ++ /* space to store CMD_PGPROG(0x10) or 0x11 */ ++ pval_nand_cmd_pgprog = (u8 *)(dummy + 1); ++ ++ /* desc can't across 4KB boundary, as desc base address is fixed */ ++ /* space of descriptors for nand reading data and oob blocks */ ++ dma_desc_nand_read = (jz_dma_desc_8word *) __get_free_page(GFP_KERNEL); ++ if (!dma_desc_nand_read) ++ return -ENOMEM; ++ ++ /* space of descriptors for bch decoding */ ++ dma_desc_dec = dma_desc_nand_read + 2; ++ dma_desc_dec1 = dma_desc_dec + eccsteps; ++ dma_desc_dec2 = dma_desc_dec + eccsteps * 2; ++ ++ /* space of descriptors for notifying bch channel */ ++ dma_desc_bch_ddr = dma_desc_dec2 + eccsteps; ++ ++ /* space of descriptors for bch encoding */ ++ dma_desc_enc = dma_desc_bch_ddr + 2; ++ dma_desc_enc1 = dma_desc_enc + eccsteps; ++ ++ /* space of descriptors for nand programing data and oob blocks */ ++ dma_desc_nand_prog = dma_desc_enc1 + eccsteps; ++ ++ /* space of descriptors for nand prog waiting, including pgprog and sync */ ++ dma_desc_nand_cmd_pgprog = dma_desc_nand_prog + 2; ++ ++ /* space of descriptors for notifying nand channel, including ddr and dcsr */ ++ dma_desc_nand_ddr = dma_desc_nand_cmd_pgprog + 2; ++ ++/************************************* ++ * Setup of nand programing descriptors ++ *************************************/ ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ oobbuf = prog_buf + pagesize; ++#endif ++ /* set descriptor for encoding data blocks */ ++ desc = dma_desc_enc; ++ for (i = 0; i < eccsteps; i++) { ++ next = (CPHYSADDR((u32)dma_desc_enc1) + i * (sizeof(jz_dma_desc_8word))) >> 4; ++ ++ desc->dcmd = ++ DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | ++ DMAC_DCMD_DS_BCH | DMAC_DCMD_LINK; ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ desc->dsadr = CPHYSADDR((u32)prog_buf) + i * eccsize; /* DMA source address */ ++ desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */ ++#endif ++ desc->ddadr = (next << 24) + eccsize / DIV_DS_BCH; /* size: eccsize bytes */ ++ desc->dreqt = DMAC_DRSR_RS_BCH_ENC; ++ dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); ++ desc++; ++ } ++ ++ /* set descriptor for encoding oob blocks */ ++ desc = dma_desc_enc1; ++ for (i = 0; i < eccsteps; i++) { ++ next = (CPHYSADDR((u32)dma_desc_enc) + (i + 1) * (sizeof(jz_dma_desc_8word))) >> 4; ++ ++ desc->dcmd = ++ DMAC_DCMD_BLAST | DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | ++ DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_LINK; ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address, 28/4 = 7bytes */ ++ desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */ ++#endif ++ desc->ddadr = (next << 24) + (oob_per_eccsize + 3) / 4; /* size: 7 bytes -> 2 words */ ++ desc->dreqt = DMAC_DRSR_RS_BCH_ENC; ++ dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); ++ desc++; ++ } ++ ++ next = (CPHYSADDR((u32)dma_desc_nand_ddr)) >> 4; ++ desc--; ++ desc->ddadr = (next << 24) + (oob_per_eccsize + 3) / 4; ++ ++ /* set the descriptor to set door bell of nand_dma_chan for programing nand */ ++ desc = dma_desc_nand_ddr; ++ *pval_nand_ddr = 1 << (nand_dma_chan - nand_dma_chan / HALF_DMA_NUM * HALF_DMA_NUM); ++ next = (CPHYSADDR((u32)dma_desc_nand_ddr) + sizeof(jz_dma_desc_8word)) >> 4; ++ desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_LINK; ++ desc->dsadr = CPHYSADDR((u32)pval_nand_ddr); /* DMA source address */ ++ desc->dtadr = CPHYSADDR(DMAC_DMADBSR(nand_dma_chan / HALF_DMA_NUM)); /* nand_dma_chan's descriptor addres register */ ++ desc->ddadr = (next << 24) + 1; /* size: 1 word */ ++ desc->dreqt = DMAC_DRSR_RS_AUTO; ++ dprintk("*pval_nand_ddr=0x%x\n", *pval_nand_ddr); ++ ++ /* set the descriptor to write dccsr of nand_dma_chan for programing nand, dccsr should be set at last */ ++ desc++; ++ *pval_nand_dcs = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; /* set value for writing ddr to enable channel nand_dma_chan */ ++ desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT; ++ desc->dsadr = CPHYSADDR((u32)pval_nand_dcs); /* DMA source address */ ++ desc->dtadr = CPHYSADDR(DMAC_DCCSR(nand_dma_chan)); /* address of dma door bell set register */ ++ desc->ddadr = (0 << 24) + 1; /* size: 1 word */ ++ desc->dreqt = DMAC_DRSR_RS_AUTO; ++ dprintk("*pval_nand_dcs=0x%x\n", *pval_nand_dcs); ++ ++ /* set descriptor for nand programing data block */ ++ desc = dma_desc_nand_prog; ++ next = (CPHYSADDR((u32)dma_desc_nand_prog) + sizeof(jz_dma_desc_8word)) >> 4; ++ desc->dcmd = ++ DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | ++ DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK; ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ desc->dsadr = CPHYSADDR((u32)prog_buf); /* DMA source address */ ++#endif ++ desc->dtadr = CPHYSADDR((u32)(chip->IO_ADDR_W)); /* DMA target address */ ++ desc->ddadr = (next << 24) + pagesize / DIV_DS_NAND; /* size: eccsize bytes */ ++ desc->dreqt = DMAC_DRSR_RS_AUTO; ++ dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); ++ ++ /* set descriptor for nand programing oob block */ ++ desc++; ++ next = (CPHYSADDR((u32)dma_desc_nand_cmd_pgprog)) >> 4; ++ desc->dcmd = ++ DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | ++ DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK; ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ desc->dsadr = CPHYSADDR((u32)oobbuf); /* DMA source address */ ++#endif ++ desc->dtadr = CPHYSADDR((u32)(chip->IO_ADDR_W)); /* DMA target address: dataport */ ++ desc->ddadr = (next << 24) + oobsize / DIV_DS_NAND; /* size: eccsize bytes */ ++ desc->dreqt = DMAC_DRSR_RS_AUTO; ++ dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); ++ ++ /* set descriptor for __nand_cmd(CMD_PGPROG) */ ++ desc = dma_desc_nand_cmd_pgprog; ++ *pval_nand_cmd_pgprog = NAND_CMD_PAGEPROG; ++ next = (CPHYSADDR((u32)dma_desc_nand_cmd_pgprog) + sizeof(jz_dma_desc_8word)) >> 4; ++ desc->dcmd = ++ DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_8BIT | DMAC_DCMD_LINK; ++ desc->dsadr = CPHYSADDR((u32)pval_nand_cmd_pgprog); /* DMA source address */ ++ desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_R | cmd_offset); /* DMA target address: cmdport */ ++ desc->ddadr = (next << 24) + 1; /* size: 1 byte */ ++ desc->dreqt = DMAC_DRSR_RS_AUTO; ++ dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); ++ ++ /* set descriptor for __nand_sync() */ ++ desc++; ++#if USE_IRQ ++ desc->dcmd = ++ DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_TIE; ++#else ++ desc->dcmd = ++ DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT; ++#endif ++ desc->dsadr = CPHYSADDR((u32)pval_nand_ddr); /* DMA source address */ ++ desc->dtadr = CPHYSADDR((u32)dummy); /* DMA target address, the content is useless */ ++ desc->ddadr = (0 << 24) + 1; /* size: 1 word */ ++ desc->dreqt = DMAC_DRSR_RS_NAND; ++ dprintk("1cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); ++ ++ /* eccsteps*2 + 2 + 2 + 2: ++ dma_desc_enc + dma_desc_enc1 + dma_desc_nand_prog(oob) + dma_desc_nand_ddr(csr) ++ + dma_desc_nand_cmd_pgprog(sync) */ ++ dma_cache_wback_inv((u32)dma_desc_enc, (eccsteps * 2 + 2 + 2 + 2) * (sizeof(jz_dma_desc_8word))); ++ /* 4*6: pval_nand_ddr, pval_nand_dcs, pval_bch_ddr, pval_bch_dcs, dummy, pval_nand_cmd_pgprog */ ++ dma_cache_wback_inv((u32)pval_nand_ddr, 4 * 8); /* 8 words, a cache line */ ++ ++/************************************* ++ * Setup of nand reading descriptors ++ *************************************/ ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ oobbuf = read_buf + pagesize; ++#endif ++ /* set descriptor for nand reading data block */ ++ desc = dma_desc_nand_read; ++ next = (CPHYSADDR((u32)dma_desc_nand_read) + sizeof(jz_dma_desc_8word)) >> 4; ++ desc->dcmd = ++ DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | ++ DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK; ++ desc->dsadr = CPHYSADDR((u32)(chip->IO_ADDR_R)); /* DMA source address */ ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ desc->dtadr = CPHYSADDR((u32)read_buf); /* DMA target address */ ++#endif ++ desc->ddadr = (next << 24) + pagesize / DIV_DS_NAND; /* size: eccsize bytes */ ++ desc->dreqt = DMAC_DRSR_RS_NAND; ++ dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); ++ ++ /* set descriptor for nand reading oob block */ ++ desc++; ++ next = (CPHYSADDR((u32)dma_desc_bch_ddr)) >> 4; ++ desc->dcmd = ++ DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | ++ DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK; ++ desc->dsadr = CPHYSADDR((u32)(chip->IO_ADDR_R)); /* DMA source address */ ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ desc->dtadr = CPHYSADDR((u32)oobbuf); /* DMA target address */ ++#endif ++ desc->ddadr = (next << 24) + oobsize / DIV_DS_NAND; /* size: eccsize bytes */ ++ desc->dreqt = DMAC_DRSR_RS_AUTO; ++ dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); ++ ++ /* set the descriptor to set door bell for bch */ ++ desc = dma_desc_bch_ddr; ++ *pval_bch_ddr = DMAC_DMADBSR_DBS0; // set value for writing ddr to enable channel 0 ++ next = (CPHYSADDR((u32)dma_desc_bch_ddr) + sizeof(jz_dma_desc_8word)) >> 4; ++ desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_LINK; ++ desc->dsadr = CPHYSADDR((u32)pval_bch_ddr); /* DMA source address */ ++ desc->dtadr = CPHYSADDR(DMAC_DMADBSR(0)); /* channel 1's descriptor addres register */ ++ desc->ddadr = (next << 24) + 1; /* size: 1 word */ ++ desc->dreqt = DMAC_DRSR_RS_AUTO; ++ ++ /* set descriptor for writing dcsr */ ++ desc++; ++ *pval_bch_dcs = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; // set value for writing ddr to enable channel 1 ++ desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT; ++ desc->dsadr = CPHYSADDR((u32)pval_bch_dcs); /* DMA source address */ ++ desc->dtadr = CPHYSADDR(DMAC_DCCSR(bch_dma_chan)); /* address of dma door bell set register */ ++ desc->ddadr = (0 << 24) + 1; /* size: 1 word */ ++ desc->dreqt = DMAC_DRSR_RS_AUTO; ++ ++ /* descriptors for data to be written to bch */ ++ desc = dma_desc_dec; ++ for (i = 0; i < eccsteps; i++) { ++ next = CPHYSADDR((u32)dma_desc_dec1 + i * (sizeof(jz_dma_desc_8word))) >> 4; ++ ++ desc->dcmd = ++ DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | ++ DMAC_DCMD_DS_BCH | DMAC_DCMD_LINK; ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ desc->dsadr = CPHYSADDR((u32)read_buf) + i * eccsize; /* DMA source address */ ++#endif ++ desc->dtadr = CPHYSADDR((u32)errs) + i * 4 * ERRS_SIZE; /* DMA target address */ ++ desc->ddadr = (next << 24) + eccsize / DIV_DS_BCH; /* size: eccsize bytes */ ++ desc->dreqt = DMAC_DRSR_RS_BCH_DEC; ++ dprintk("desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, ++ desc->ddadr); ++ desc++; ++ } ++ ++ /* descriptors for oob to be written to bch */ ++ desc = dma_desc_dec1; ++ for (i = 0; i < eccsteps; i++) { ++ next = CPHYSADDR((u32)dma_desc_dec2 + i * (sizeof(jz_dma_desc_8word))) >> 4; ++ ++ desc->dcmd = ++ DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | ++ DMAC_DCMD_DS_8BIT | DMAC_DCMD_LINK; ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address */ ++#endif ++ desc->dtadr = CPHYSADDR((u32)errs) + i * 4 * ERRS_SIZE; /* DMA target address */ ++ desc->ddadr = (next << 24) + oob_per_eccsize; /* size: 7 bytes */ ++ desc->dreqt = DMAC_DRSR_RS_BCH_DEC; ++ dprintk("desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, ++ desc->ddadr); ++ desc++; ++ } ++ ++ /* descriptors for parities to be written to bch */ ++ desc = dma_desc_dec2; ++ for (i = 0; i < eccsteps; i++) { ++ next = (CPHYSADDR((u32)dma_desc_dec) + (i + 1) * (sizeof(jz_dma_desc_8word))) >> 4; ++ ++ desc->dcmd = ++ DMAC_DCMD_BLAST | DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | ++ DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_BCH | DMAC_DCMD_LINK; ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ desc->dsadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA source address */ ++#endif ++ desc->dtadr = CPHYSADDR((u32)errs) + i * 4 * ERRS_SIZE; /* DMA target address */ ++ desc->ddadr = (next << 24) + (eccbytes + 15) / DIV_DS_BCH; /* size: eccbytes bytes */ ++ desc->dreqt = DMAC_DRSR_RS_BCH_DEC; ++ dprintk("desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, ++ desc->ddadr); ++ desc++; ++ } ++ desc--; ++ desc->dcmd &= ~DMAC_DCMD_LINK; ++#if USE_IRQ ++ desc->dcmd |= DMAC_DCMD_TIE; ++#endif ++ ++ dma_cache_wback_inv((u32)dma_desc_nand_read, (2 + 2 + eccsteps * 3) * (sizeof(jz_dma_desc_8word))); ++ dma_cache_wback_inv((u32)pval_bch_ddr, 4 * 2); /* two words */ ++ ++ return 0; ++} ++ ++#endif /* CONFIG_MTD_NAND_DMA */ ++/* ++ * Main initialization routine ++ */ ++int __init jznand_init(void) ++{ ++ struct nand_chip *this; ++ int nr_partitions, ret, i; ++ ++ printk(KERN_INFO "JZ NAND init"); ++#if defined(CONFIG_MTD_NAND_DMA) ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ printk(KERN_INFO " DMA mode, using DMA buffer in NAND driver.\n"); ++#else ++ printk(KERN_INFO " DMA mode, using DMA buffer in upper layer.\n"); ++#endif ++#else ++ printk(KERN_INFO " CPU mode.\n"); ++#endif ++ ++ ++ /* Allocate memory for MTD device structure and private data */ ++ jz_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); ++ if (!jz_mtd) { ++ printk("Unable to allocate JzSOC NAND MTD device structure.\n"); ++ return -ENOMEM; ++ } ++ ++ /* Allocate memory for NAND when using only one plane */ ++ jz_mtd1 = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); ++ if (!jz_mtd1) { ++ printk ("Unable to allocate JzSOC NAND MTD device structure 1.\n"); ++ kfree(jz_mtd); ++ return -ENOMEM; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *)(&jz_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *)jz_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *)this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ jz_mtd->priv = this; ++ ++ if (is_share_mode()) { ++ addr_offset = NAND_ADDR_OFFSET0; ++ cmd_offset = NAND_CMD_OFFSET0; ++ } else { ++ addr_offset = NAND_ADDR_OFFSET1; ++ cmd_offset = NAND_CMD_OFFSET1; ++ } ++ ++ /* Set & initialize NAND Flash controller */ ++ jz_device_setup(); ++ ++ /* Set address of NAND IO lines to static bank1 by default */ ++ this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT1; ++ this->IO_ADDR_W = (void __iomem *)NAND_DATA_PORT1; ++ this->cmd_ctrl = jz_hwcontrol; ++ this->dev_ready = jz_device_ready; ++ ++#ifdef CONFIG_MTD_HW_BCH_ECC ++ this->ecc.calculate = jzsoc_nand_calculate_bch_ecc; ++ this->ecc.correct = jzsoc_nand_bch_correct_data; ++ this->ecc.hwctl = jzsoc_nand_enable_bch_hwecc; ++ this->ecc.mode = NAND_ECC_HW; ++ this->ecc.size = 512; ++ this->ecc.read_page = nand_read_page_hwecc_bch; ++ this->ecc.write_page = nand_write_page_hwecc_bch; ++#if defined(CONFIG_MTD_HW_BCH_8BIT) ++ this->ecc.bytes = 13; ++#else ++ this->ecc.bytes = 7; ++#endif ++#endif ++ ++#ifdef CONFIG_MTD_SW_HM_ECC ++ this->ecc.mode = NAND_ECC_SOFT; ++#endif ++ /* 20 us command delay time */ ++ this->chip_delay = 20; ++ /* Scan to find existance of the device */ ++ ret = nand_scan_ident(jz_mtd, NAND_MAX_CHIPS); ++ ++ if (!ret) { ++ if (this->planenum == 2) { ++ /* reset nand functions */ ++ this->erase_cmd = single_erase_cmd_planes; ++ this->ecc.read_page = nand_read_page_hwecc_bch_planes; ++ this->ecc.write_page = nand_write_page_hwecc_bch_planes; ++ this->ecc.read_oob = nand_read_oob_std_planes; ++ this->ecc.write_oob = nand_write_oob_std_planes; ++ ++ printk(KERN_INFO "Nand using two-plane mode, " ++ "and resized to writesize:%d oobsize:%d blocksize:0x%x \n", ++ jz_mtd->writesize, jz_mtd->oobsize, jz_mtd->erasesize); ++ } ++ } ++ ++ /* Determine whether all the partitions will use multiple planes if supported */ ++ nr_partitions = sizeof(partition_info) / sizeof(struct mtd_partition); ++ all_use_planes = 1; ++ for (i = 0; i < nr_partitions; i++) { ++ all_use_planes &= partition_info[i].use_planes; ++ } ++ ++ if (!ret) ++ ret = nand_scan_tail(jz_mtd); ++ ++ if (ret){ ++ kfree (jz_mtd1); ++ kfree (jz_mtd); ++ return -ENXIO; ++ } ++ ++#if defined(CONFIG_MTD_NAND_DMA) ++ jz4750_nand_dma_init(jz_mtd); ++#endif ++ ++ ((struct nand_chip *) (&jz_mtd1[1]))->ecc.read_page = nand_read_page_hwecc_bch; ++ ((struct nand_chip *) (&jz_mtd1[1]))->ecc.write_page = nand_write_page_hwecc_bch; ++ ++ /* Register the partitions */ ++ printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nr_partitions, jz_mtd->name); ++ ++ if ((this->planenum == 2) && !all_use_planes) { ++ for (i = 0; i < nr_partitions; i++) { ++ if (partition_info[i].use_planes) ++ add_mtd_partitions(jz_mtd, &partition_info[i], 1); ++ else ++ add_mtd_partitions(jz_mtd1, &partition_info[i], 1); ++ } ++ } else { ++ kfree(jz_mtd1); ++ add_mtd_partitions(jz_mtd, partition_info, nr_partitions); ++ } ++ return 0; ++} ++ ++module_init(jznand_init); ++ ++/* ++ * Clean up routine ++ */ ++#ifdef MODULE ++ ++#if defined(CONFIG_MTD_NAND_DMA) ++static int jz4750_nand_dma_exit(struct mtd_info *mtd) ++{ ++ int pagesize = mtd->writesize / chip->planenum; ++ ++#if USE_IRQ ++ free_irq(IRQ_DMA_0 + nand_dma_chan, NULL); ++ free_irq(IRQ_DMA_0 + bch_dma_chan, NULL); ++#endif ++ ++ /* space for the error reports of bch decoding((4 * 5 * eccsteps) bytes), ++ * and the space for the value of ddr and dcs of channel 0 and channel ++ * nand_dma_chan (4 * (2 + 2) bytes) */ ++ kfree(errs); ++ ++ /* space for dma_desc_nand_read contains dma_desc_nand_prog, ++ * dma_desc_enc and dma_desc_dec */ ++ free_page((u32)dma_desc_nand_read); ++ ++#if defined(CONFIG_MTD_NAND_DMABUF) ++ if (pagesize < 4096) { ++ free_page((u32)prog_buf); ++ } else { ++ free_pages((u32)prog_buf, 1); ++ } ++#endif ++ ++ return 0; ++} ++#endif ++ ++static void __exit jznand_cleanup(void) ++{ ++#if defined(CONFIG_MTD_NAND_DMA) ++ jz4750_nand_dma_exit(jz_mtd); ++#endif ++ ++ /* Unregister partitions */ ++ del_mtd_partitions(jz_mtd); ++ ++ /* Unregister the device */ ++ del_mtd_device(jz_mtd); ++ ++ /* Free the MTD device structure */ ++ if ((this->planenum == 2) && !all_use_planes) ++ kfree (jz_mtd1); ++ kfree(jz_mtd); ++} ++ ++module_exit(jznand_cleanup); ++#endif +diff -urN linux-2.6.24.7.old/drivers/mtd/nand/nand_base.c linux-2.6.24.7/drivers/mtd/nand/nand_base.c +--- linux-2.6.24.7.old/drivers/mtd/nand/nand_base.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/nand/nand_base.c 2009-04-12 18:13:57.000000000 +0200 +@@ -52,6 +52,16 @@ + #include + #endif + ++#include ++ ++u8 nand_nce; /* indicates which chip select on JZSOC is used for ++ current nand chip */ ++int global_page; /* page index of large page used for nand with multiple planes */ ++ ++struct mtd_info *jz_mtd1 = NULL; /* for 1 plane operation */ ++char all_use_planes = 1; /* indicates whether multiple planes operation is used ++ by all partitions if multiple planes is supported by NAND */ ++ + /* Define default oob placement schemes for large and small page devices */ + static struct nand_ecclayout nand_oob_8 = { + .eccbytes = 3, +@@ -72,6 +82,20 @@ + }; + + static struct nand_ecclayout nand_oob_64 = { ++#if defined(CONFIG_MTD_HW_RS_ECC) || defined(CONFIG_MTD_HW_BCH_ECC) ++/* Reed-Solomon ECC or BCH ECC */ ++ .eccbytes = 36, ++ .eccpos = { ++ 28, 29, 30, 31, ++ 32, 33, 34, 35, 36, 37, 38, 39, ++ 40, 41, 42, 43, 44, 45, 46, 47, ++ 48, 49, 50, 51, 52, 53, 54, 55, ++ 56, 57, 58, 59, 60, 61, 62, 63}, ++ .oobfree = { ++ {.offset = 2, ++ . length = 26}} ++#else ++/* HW&SW Hamming ECC */ + .eccbytes = 24, + .eccpos = { + 40, 41, 42, 43, 44, 45, 46, 47, +@@ -80,12 +104,85 @@ + .oobfree = { + {.offset = 2, + .length = 38}} ++#endif ++}; ++ ++static struct nand_ecclayout nand_oob_128 = { ++#if defined(CONFIG_MTD_HW_RS_ECC) ++/* Reed-Solomon ECC */ ++ .eccbytes = 72, ++ .eccpos = { ++ 56, 57, 58, 59, 60, 61, 62, 63, ++ 64, 65, 66, 67, 68, 69, 70, 71, ++ 72, 73, 74, 75, 76, 77, 78, 79, ++ 80, 81, 82, 83, 84, 85, 86, 87, ++ 88, 89, 90, 91, 92, 93, 94, 95, ++ 96, 97, 98, 99, 100, 101, 102, 103, ++ 104, 105, 106, 107, 108, 109, 110, 111, ++ 112, 113, 114, 115, 116, 117, 118, 119, ++ 120, 121, 122, 123, 124, 125, 126, 127}, ++ .oobfree = { ++ {.offset = 2, ++ . length = 54}} ++ ++#elif defined(CONFIG_MTD_HW_BCH_ECC) ++#if !defined(CONFIG_MTD_HW_BCH_8BIT) ++/* 4-bit BCH ECC */ ++ .eccbytes = 56, ++ .eccpos = { ++ 72, 73, 74, 75, 76, 77, 78, 79, ++ 80, 81, 82, 83, 84, 85, 86, 87, ++ 88, 89, 90, 91, 92, 93, 94, 95, ++ 96, 97, 98, 99, 100, 101, 102, 103, ++ 104, 105, 106, 107, 108, 109, 110, 111, ++ 112, 113, 114, 115, 116, 117, 118, 119, ++ 120, 121, 122, 123, 124, 125, 126, 127}, ++ .oobfree = { ++ {.offset = 2, ++ . length = 70}} ++ ++#else ++/* 8-bit BCH ECC */ ++ .eccbytes = 104, ++ .eccpos = { ++ 24, 25, 26, 27, 28, 29, 30, 31, ++ 32, 33, 34, 35, 36, 37, 38, 39, ++ 40, 41, 42, 43, 44, 45, 46, 47, ++ 48, 49, 50, 51, 52, 53, 54, 55, ++ 56, 57, 58, 59, 60, 61, 62, 63, ++ 64, 65, 66, 67, 68, 69, 70, 71, ++ 72, 73, 74, 75, 76, 77, 78, 79, ++ 80, 81, 82, 83, 84, 85, 86, 87, ++ 88, 89, 90, 91, 92, 93, 94, 95, ++ 96, 97, 98, 99, 100, 101, 102, 103 ++ 104, 105, 106, 107, 108, 109, 110, 111, ++ 112, 113, 114, 115, 116, 117, 118, 119, ++ 120, 121, 122, 123, 124, 125, 126, 127}, ++ .oobfree = { ++ {.offset = 2, ++ . length = 22}} ++ ++#endif ++#else ++/* HW&SW Hamming ECC */ ++ .eccbytes = 48, ++ .eccpos = { ++ 80, 81, 82, 83, 84, 85, 86, 87, ++ 88, 89, 90, 91, 92, 93, 94, 95, ++ 96, 97, 98, 99, 100, 101, 102, 103, ++ 104, 105, 106, 107, 108, 109, 110, 111, ++ 112, 113, 114, 115, 116, 117, 118, 119, ++ 120, 121, 122, 123, 124, 125, 126, 127}, ++ .oobfree = { ++ {.offset = 2, ++ .length = 78}} ++#endif + }; + + static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, + int new_state); + +-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, ++static int nand_do_write_oob(struct mtd_info *mtd, loff_mtd_t to, + struct mtd_oob_ops *ops); + + /* +@@ -95,6 +192,35 @@ + DEFINE_LED_TRIGGER(nand_led_trigger); + + /** ++ * ffs_ll - find first bit set in a 64bit word. ++ * @word: The word to search ++ */ ++static inline int ffs_ll(u64 word) ++{ ++ u32 low = word & 0xffffffff; ++ u32 high = word >> 32; ++ int i; ++ ++ for(i = 0; i < 32; i++) { ++ if (low & 0x1) ++ break; ++ low >>= 1; ++ } ++ if (i == 32) { ++ for(i = 0; i < 32; i++) { ++ if (high & 0x1) ++ break; ++ high >>= 1; ++ } ++ i += 32; ++ } ++ if (i == 64) ++ return 0; ++ else ++ return (i+1); ++} ++ ++/** + * nand_release_device - [GENERIC] release chip + * @mtd: MTD device structure + * +@@ -169,6 +295,20 @@ + chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); + break; + case 0: ++ nand_nce = NAND_NCE1; ++ chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); ++ break; ++ case 1: ++ nand_nce = NAND_NCE2; ++ chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); ++ break; ++ case 2: ++ nand_nce = NAND_NCE3; ++ chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); ++ break; ++ case 3: ++ nand_nce = NAND_NCE4; ++ chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + break; + + default: +@@ -298,13 +438,19 @@ + * + * Check, if the block is bad. + */ +-static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) ++static int nand_block_bad(struct mtd_info *mtd, loff_mtd_t ofs, int getchip) + { +- int page, chipnr, res = 0; ++ int page, page1 = 0, chipnr, res = 0; + struct nand_chip *chip = mtd->priv; + u16 bad; + +- page = (int)(ofs >> chip->page_shift) & chip->pagemask; ++ if (chip->planenum > 1) { ++ page = ((int)(ofs >> chip->page_shift) * chip->planenum + CONFIG_MTD_BADBLOCK_FLAG_PAGE); ++ page1 = page + mtd->erasesize / mtd->writesize; ++ page &= chip->pagemask; ++ page1 &= chip->pagemask; ++ } else ++ page = ((int)(ofs >> chip->page_shift) + CONFIG_MTD_BADBLOCK_FLAG_PAGE) & chip->pagemask; + + if (getchip) { + chipnr = (int)(ofs >> chip->chip_shift); +@@ -327,6 +473,11 @@ + chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); + if (chip->read_byte(mtd) != 0xff) + res = 1; ++ if (chip->planenum > 1) { ++ chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page1); ++ if (chip->read_byte(mtd) != 0xff) ++ res = 1; ++ } + } + + if (getchip) +@@ -343,7 +494,7 @@ + * This is the default implementation, which can be overridden by + * a hardware specific driver. + */ +-static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) ++static int nand_default_block_markbad(struct mtd_info *mtd, loff_mtd_t ofs) + { + struct nand_chip *chip = mtd->priv; + uint8_t buf[2] = { 0, 0 }; +@@ -363,6 +514,7 @@ + */ + nand_get_device(chip, mtd, FL_WRITING); + ofs += mtd->oobsize; ++ ofs += (CONFIG_MTD_BADBLOCK_FLAG_PAGE << chip->page_shift); + chip->ops.len = chip->ops.ooblen = 2; + chip->ops.datbuf = NULL; + chip->ops.oobbuf = buf; +@@ -402,7 +554,7 @@ + * Check, if the block is bad. Either by reading the bad block table or + * calling of the scan function. + */ +-static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, ++static int nand_block_checkbad(struct mtd_info *mtd, loff_mtd_t ofs, int getchip, + int allowbbt) + { + struct nand_chip *chip = mtd->priv; +@@ -554,7 +706,10 @@ + + /* Emulate NAND_CMD_READOOB */ + if (command == NAND_CMD_READOOB) { +- column += mtd->writesize; ++ if (chip->planenum > 1) ++ column += (mtd->writesize / chip->planenum); ++ else ++ column += mtd->writesize; + command = NAND_CMD_READ0; + } + +@@ -600,6 +755,8 @@ + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: ++ case 0x81: /* for two-plane page program */ ++ case 0x11: /* for two-plane page program */ + return; + + /* +@@ -675,7 +832,6 @@ + spin_lock(lock); + + /* Hardware controller shared among independend devices */ +- /* Hardware controller shared among independend devices */ + if (!chip->controller->active) + chip->controller->active = chip; + +@@ -797,6 +953,7 @@ + return 0; + } + ++#ifndef CONFIG_MTD_HW_RS_ECC /* HW&SW Hamming ECC */ + /** + * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function + * @mtd: mtd info structure +@@ -841,6 +998,63 @@ + return 0; + } + ++#else /* CONFIG_MTD_HW_RS_ECC */ ++ ++/** ++ * nand_read_page_hwecc_rs - [REPLACABLE] hardware rs ecc based page read function ++ * @mtd: mtd info structure ++ * @chip: nand chip info structure ++ * @buf: buffer to store read data ++ * ++ * Not for syndrome calculating ecc controllers which need a special oob layout ++ */ ++static int nand_read_page_hwecc_rs(struct mtd_info *mtd, struct nand_chip *chip, ++ uint8_t *buf) ++{ ++ int i, eccsize = chip->ecc.size; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ uint8_t *p = buf; ++ uint8_t *ecc_calc = chip->buffers->ecccalc; ++ uint8_t *ecc_code = chip->buffers->ecccode; ++ uint32_t *eccpos = chip->ecc.layout->eccpos; ++ uint32_t page; ++ uint8_t flag = 0; ++ ++ page = (buf[3]<<24) + (buf[2]<<16) + (buf[1]<<8) + buf[0]; ++ ++ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); ++ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); ++ for (i = 0; i < chip->ecc.total; i++) { ++ ecc_code[i] = chip->oob_poi[eccpos[i]]; ++ if (ecc_code[i] != 0xff) flag = 1; ++ } ++ ++ eccsteps = chip->ecc.steps; ++ p = buf; ++ ++ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0x00, -1); ++ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { ++ int stat; ++ if (flag) { ++ chip->ecc.hwctl(mtd, NAND_ECC_READ); ++ chip->read_buf(mtd, p, eccsize); ++ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); ++ if (stat < 0) ++ mtd->ecc_stats.failed++; ++ else ++ mtd->ecc_stats.corrected += stat; ++ } ++ else { ++ chip->ecc.hwctl(mtd, NAND_ECC_READ); ++ chip->read_buf(mtd, p, eccsize); ++ } ++ } ++ return 0; ++} ++ ++#endif /* CONFIG_MTD_HW_RS_ECC */ ++ + /** + * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read + * @mtd: mtd info structure +@@ -903,7 +1117,7 @@ + * @len: size of oob to transfer + */ + static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, +- struct mtd_oob_ops *ops, size_t len) ++ struct mtd_oob_ops *ops, size_mtd_t len) + { + switch(ops->mode) { + +@@ -915,7 +1129,7 @@ + case MTD_OOB_AUTO: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + uint32_t boffs = 0, roffs = ops->ooboffs; +- size_t bytes = 0; ++ size_mtd_t bytes = 0; + + for(; free->length && len; free++, len -= bytes) { + /* Read request not from offset 0 ? */ +@@ -925,11 +1139,11 @@ + continue; + } + boffs = free->offset + roffs; +- bytes = min_t(size_t, len, ++ bytes = min_t(size_mtd_t, len, + (free->length - roffs)); + roffs = 0; + } else { +- bytes = min_t(size_t, len, free->length); ++ bytes = min_t(size_mtd_t, len, free->length); + boffs = free->offset; + } + memcpy(oob, chip->oob_poi + boffs, bytes); +@@ -952,7 +1166,7 @@ + * + * Internal function. Called with chip held. + */ +-static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, ++static int nand_do_read_ops(struct mtd_info *mtd, loff_mtd_t from, + struct mtd_oob_ops *ops) + { + int chipnr, page, realpage, col, bytes, aligned; +@@ -986,10 +1200,18 @@ + if (realpage != chip->pagebuf || oob) { + bufpoi = aligned ? buf : chip->buffers->databuf; + ++ global_page = page; ++#if defined(CONFIG_MTD_HW_RS_ECC) || defined(CONFIG_MTD_NAND_DMA) ++ bufpoi[0] = (uint8_t)page; ++ bufpoi[1] = (uint8_t)(page >> 8); ++ bufpoi[2] = (uint8_t)(page >> 16); ++ bufpoi[3] = (uint8_t)(page >> 24); ++#else + if (likely(sndcmd)) { + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + sndcmd = 0; + } ++#endif + + /* Now read the page into the buffer */ + if (unlikely(ops->mode == MTD_OOB_RAW)) +@@ -1065,7 +1287,7 @@ + sndcmd = 1; + } + +- ops->retlen = ops->len - (size_t) readlen; ++ ops->retlen = ops->len - (size_mtd_t) readlen; + if (oob) + ops->oobretlen = ops->ooblen - oobreadlen; + +@@ -1088,8 +1310,8 @@ + * + * Get hold of the chip and call nand_do_read + */ +-static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, +- size_t *retlen, uint8_t *buf) ++static int nand_read(struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, ++ size_mtd_t *retlen, uint8_t *buf) + { + struct nand_chip *chip = mtd->priv; + int ret; +@@ -1262,7 +1484,7 @@ + * + * NAND read out-of-band data from the spare area + */ +-static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, ++static int nand_do_read_oob(struct mtd_info *mtd, loff_mtd_t from, + struct mtd_oob_ops *ops) + { + int page, realpage, chipnr, sndcmd = 1; +@@ -1355,7 +1577,7 @@ + * + * NAND read data and/or out-of-band data + */ +-static int nand_read_oob(struct mtd_info *mtd, loff_t from, ++static int nand_read_oob(struct mtd_info *mtd, loff_mtd_t from, + struct mtd_oob_ops *ops) + { + struct nand_chip *chip = mtd->priv; +@@ -1518,12 +1740,17 @@ + { + int status; + +- chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); +- +- if (unlikely(raw)) +- chip->ecc.write_page_raw(mtd, chip, buf); +- else ++ global_page = page; ++ if (chip->planenum > 1) + chip->ecc.write_page(mtd, chip, buf); ++ else { ++ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); ++ ++ if (unlikely(raw)) ++ chip->ecc.write_page_raw(mtd, chip, buf); ++ else ++ chip->ecc.write_page(mtd, chip, buf); ++ } + + /* + * Cached progamming disabled for now, Not sure if its worth the +@@ -1532,9 +1759,17 @@ + cached = 0; + + if (!cached || !(chip->options & NAND_CACHEPRG)) { +- +- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); ++/* ++* __nand_cmd(CMD_PAGEPROG) and __nand_sync() have been done by DMA for jz4750 and ++* later chip, status should still be read by "status = chip->waitfunc(mtd, chip)" ++*/ ++#if defined(CONFIG_SOC_JZ4730) || defined(CONFIG_SOC_JZ4740) || \ ++ (!defined(CONFIG_SOC_JZ4730) && !defined(CONFIG_SOC_JZ4740) \ ++ && !defined(CONFIG_MTD_NAND_DMA)) ++ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); ++#endif + status = chip->waitfunc(mtd, chip); ++ + /* + * See if operation failed and additional status checks are + * available +@@ -1569,7 +1804,7 @@ + static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops) + { +- size_t len = ops->ooblen; ++ size_mtd_t len = ops->ooblen; + + switch(ops->mode) { + +@@ -1581,7 +1816,7 @@ + case MTD_OOB_AUTO: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + uint32_t boffs = 0, woffs = ops->ooboffs; +- size_t bytes = 0; ++ size_mtd_t bytes = 0; + + for(; free->length && len; free++, len -= bytes) { + /* Write request not from offset 0 ? */ +@@ -1591,11 +1826,11 @@ + continue; + } + boffs = free->offset + woffs; +- bytes = min_t(size_t, len, ++ bytes = min_t(size_mtd_t, len, + (free->length - woffs)); + woffs = 0; + } else { +- bytes = min_t(size_t, len, free->length); ++ bytes = min_t(size_mtd_t, len, free->length); + boffs = free->offset; + } + memcpy(chip->oob_poi + boffs, oob, bytes); +@@ -1619,7 +1854,7 @@ + * + * NAND write with ECC + */ +-static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, ++static int nand_do_write_ops(struct mtd_info *mtd, loff_mtd_t to, + struct mtd_oob_ops *ops) + { + int chipnr, realpage, page, blockmask, column; +@@ -1722,8 +1957,8 @@ + * + * NAND write with ECC + */ +-static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, +- size_t *retlen, const uint8_t *buf) ++static int nand_write(struct mtd_info *mtd, loff_mtd_t to, size_mtd_t len, ++ size_mtd_t *retlen, const uint8_t *buf) + { + struct nand_chip *chip = mtd->priv; + int ret; +@@ -1757,7 +1992,7 @@ + * + * NAND write out-of-band + */ +-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, ++static int nand_do_write_oob(struct mtd_info *mtd, loff_mtd_t to, + struct mtd_oob_ops *ops) + { + int chipnr, page, status, len; +@@ -1835,7 +2070,7 @@ + * @to: offset to write to + * @ops: oob operation description structure + */ +-static int nand_write_oob(struct mtd_info *mtd, loff_t to, ++static int nand_write_oob(struct mtd_info *mtd, loff_mtd_t to, + struct mtd_oob_ops *ops) + { + struct nand_chip *chip = mtd->priv; +@@ -1999,7 +2234,7 @@ + /* + * heck if we have a bad block, we do not erase bad blocks ! + */ +- if (nand_block_checkbad(mtd, ((loff_t) page) << ++ if (nand_block_checkbad(mtd, ((loff_mtd_t) page) << + chip->page_shift, 0, allowbbt)) { + printk(KERN_WARNING "nand_erase: attempt to erase a " + "bad block at page 0x%08x\n", page); +@@ -2121,7 +2356,7 @@ + * @mtd: MTD device structure + * @offs: offset relative to mtd start + */ +-static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) ++static int nand_block_isbad(struct mtd_info *mtd, loff_mtd_t offs) + { + /* Check for invalid offset */ + if (offs > mtd->size) +@@ -2135,7 +2370,7 @@ + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +-static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) ++static int nand_block_markbad(struct mtd_info *mtd, loff_mtd_t ofs) + { + struct nand_chip *chip = mtd->priv; + int ret; +@@ -2274,7 +2509,21 @@ + extid >>= 2; + /* Get buswidth information */ + busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; ++ /* The 5th id byte */ ++#if defined(CONFIG_MTD_NAND_MULTI_PLANE) ++ extid = chip->read_byte(mtd); ++ chip->realplanenum = 1 << ((extid & 0x0c) >> 2); ++#else ++ chip->realplanenum = 1; ++#endif + ++ if (chip->realplanenum > 1) { /* use muti planes mode */ ++ chip->planenum = 2; ++ mtd->writesize *= 2; /* two pages as one page */ ++ mtd->oobsize *= 2; ++ mtd->erasesize *= 2; /* two blocks as one block */ ++ } else ++ chip->planenum = 1; + } else { + /* + * Old devices have chip data hardcoded in the device id table +@@ -2312,7 +2561,7 @@ + + chip->bbt_erase_shift = chip->phys_erase_shift = + ffs(mtd->erasesize) - 1; +- chip->chip_shift = ffs(chip->chipsize) - 1; ++ chip->chip_shift = ffs_ll(chip->chipsize) - 1; + + /* Set the bad block position */ + chip->badblockpos = mtd->writesize > 512 ? +@@ -2344,8 +2593,8 @@ + chip->cmdfunc = nand_command_lp; + + printk(KERN_INFO "NAND device: Manufacturer ID:" +- " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id, +- nand_manuf_ids[maf_idx].name, type->name); ++ " 0x%02x, Chip ID: 0x%02x (%s %s) planenum:%d\n", *maf_id, dev_id, ++ nand_manuf_ids[maf_idx].name, type->name, chip->realplanenum); + + return type; + } +@@ -2412,7 +2661,7 @@ + */ + int nand_scan_tail(struct mtd_info *mtd) + { +- int i; ++ int i, res; + struct nand_chip *chip = mtd->priv; + + if (!(chip->options & NAND_OWN_BUFFERS)) +@@ -2437,6 +2686,16 @@ + case 64: + chip->ecc.layout = &nand_oob_64; + break; ++ case 128: ++ if (chip->planenum > 1) ++ chip->ecc.layout = &nand_oob_64; ++ else ++ chip->ecc.layout = &nand_oob_128; ++ break; ++ case 256: ++ if (chip->planenum > 1) ++ chip->ecc.layout = &nand_oob_128; ++ break; + default: + printk(KERN_WARNING "No oob scheme defined for " + "oobsize %d\n", mtd->oobsize); +@@ -2459,8 +2718,13 @@ + switch (chip->ecc.mode) { + case NAND_ECC_HW: + /* Use standard hwecc read page function ? */ +- if (!chip->ecc.read_page) ++ if (!chip->ecc.read_page) { ++#ifndef CONFIG_MTD_HW_RS_ECC + chip->ecc.read_page = nand_read_page_hwecc; ++#else ++ chip->ecc.read_page = nand_read_page_hwecc_rs; ++#endif ++ } + if (!chip->ecc.write_page) + chip->ecc.write_page = nand_write_page_hwecc; + if (!chip->ecc.read_oob) +@@ -2593,8 +2857,58 @@ + if (chip->options & NAND_SKIP_BBTSCAN) + return 0; + +- /* Build bad block table */ +- return chip->scan_bbt(mtd); ++ /* Create jz_mtd1 for one plane operation if the NAND support multiple ++ planes operation, because some partitions will only use one plane. */ ++ if ((chip->planenum == 2) && !all_use_planes) { ++ int i, len, numblocks; ++ struct nand_chip *this = (struct nand_chip *) (&jz_mtd1[1]); ++ ++ memcpy(jz_mtd1, mtd, sizeof(*mtd)); ++ jz_mtd1->priv = this; ++ memcpy(this, chip, sizeof(*chip)); ++ ++ this->planenum = 1; ++ jz_mtd1->writesize >>= 1; ++ jz_mtd1->oobsize >>= 1; ++ jz_mtd1->erasesize >>= 1; ++ this->page_shift = chip->page_shift - 1; ++ this->pagemask = (this->chipsize >> this->page_shift) - 1; ++ this->bbt_erase_shift = this->phys_erase_shift = ++ chip->phys_erase_shift - 1; ++ this->ecc.steps >>= 1; ++ this->ecc.total = this->ecc.steps * this->ecc.bytes; ++ this->subpagesize = jz_mtd1->writesize; ++ ++ this->erase_cmd = single_erase_cmd; ++#if defined(CONFIG_MTD_HW_RS_ECC) ++ this->ecc.read_page = nand_read_page_hwecc_rs; ++ this->ecc.write_page = nand_write_page_hwecc; ++#endif ++ this->ecc.read_oob = nand_read_oob_std; ++ this->ecc.write_oob = nand_write_oob_std; ++ this->write_buf = nand_write_buf; ++ this->read_buf = nand_read_buf; ++ ++ /* Firstly, build bad block table as one plane */ ++ res = this->scan_bbt(jz_mtd1); ++ ++ /* Secondly, build bad block table as 2 plane based on bbt of jz_mtd1 */ ++ numblocks = chip->chipsize >> (chip->bbt_erase_shift - 1); /* = (real numblocks * 2) */ ++ len = mtd->size >> (chip->bbt_erase_shift + 2); ++ chip->bbt = kzalloc(len, GFP_KERNEL); ++ ++#define isbad_2plane(block) (((this->bbt[(block) >> 3] >> ((block) & 0x06)) \ ++ | (this->bbt[((block)+2) >> 3] >> (((block)+2) & 0x06))) & 0x03) ++ ++ for (i = 0; i < numblocks; i += 2) { ++ if (isbad_2plane(2*i)) ++ chip->bbt[i >> 3] |= 0x03 << (i & 0x6); ++ } ++ } else { ++ res = chip->scan_bbt(mtd); ++ } ++ ++ return res; + } + + /* module_text_address() isn't exported, and it's mostly a pointless +diff -urN linux-2.6.24.7.old/drivers/mtd/nand/nand_bbt.c linux-2.6.24.7/drivers/mtd/nand/nand_bbt.c +--- linux-2.6.24.7.old/drivers/mtd/nand/nand_bbt.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/nand/nand_bbt.c 2009-04-12 18:13:57.000000000 +0200 +@@ -6,7 +6,7 @@ + * + * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) + * +- * $Id: nand_bbt.c,v 1.36 2005/11/07 11:14:30 gleixner Exp $ ++ * $Id: nand_bbt.c,v 1.1.1.1 2008-03-28 04:29:21 jlwei Exp $ + * + * 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 +@@ -147,15 +147,15 @@ + { + int res, i, j, act = 0; + struct nand_chip *this = mtd->priv; +- size_t retlen, len, totlen; +- loff_t from; ++ size_mtd_t retlen, len, totlen; ++ loff_mtd_t from; + uint8_t msk = (uint8_t) ((1 << bits) - 1); + + totlen = (num * bits) >> 3; +- from = ((loff_t) page) << this->page_shift; ++ from = ((loff_mtd_t) page) << this->page_shift; + + while (totlen) { +- len = min(totlen, (size_t) (1 << this->bbt_erase_shift)); ++ len = min(totlen, (size_mtd_t) (1 << this->bbt_erase_shift)); + res = mtd->read(mtd, from, len, &retlen, buf); + if (res < 0) { + if (retlen != len) { +@@ -235,8 +235,8 @@ + /* + * Scan read raw data from flash + */ +-static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, +- size_t len) ++static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_mtd_t offs, ++ size_mtd_t len) + { + struct mtd_oob_ops ops; + +@@ -253,7 +253,7 @@ + /* + * Scan write data with oob to flash + */ +-static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, ++static int scan_write_bbt(struct mtd_info *mtd, loff_mtd_t offs, size_mtd_t len, + uint8_t *buf, uint8_t *oob) + { + struct mtd_oob_ops ops; +@@ -308,7 +308,7 @@ + * Scan a given block full + */ + static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, +- loff_t offs, uint8_t *buf, size_t readlen, ++ loff_mtd_t offs, uint8_t *buf, size_mtd_t readlen, + int scanlen, int len) + { + int ret, j; +@@ -328,7 +328,7 @@ + * Scan a given block partially + */ + static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, +- loff_t offs, uint8_t *buf, int len) ++ loff_mtd_t offs, uint8_t *buf, int len) + { + struct mtd_oob_ops ops; + int j, ret; +@@ -374,8 +374,8 @@ + struct nand_chip *this = mtd->priv; + int i, numblocks, len, scanlen; + int startblock; +- loff_t from; +- size_t readlen; ++ loff_mtd_t from; ++ size_mtd_t readlen; + + printk(KERN_INFO "Scanning device for bad blocks\n"); + +@@ -403,7 +403,7 @@ + * below as it makes shifting and masking less painful */ + numblocks = mtd->size >> (this->bbt_erase_shift - 1); + startblock = 0; +- from = 0; ++ from = (CONFIG_MTD_BADBLOCK_FLAG_PAGE << this->page_shift); //from = 0; + } else { + if (chip >= this->numchips) { + printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", +@@ -413,7 +413,7 @@ + numblocks = this->chipsize >> (this->bbt_erase_shift - 1); + startblock = chip * numblocks; + numblocks += startblock; +- from = startblock << (this->bbt_erase_shift - 1); ++ from = (startblock << (this->bbt_erase_shift - 1)) + (CONFIG_MTD_BADBLOCK_FLAG_PAGE << this->page_shift); //from = startblock << (this->bbt_erase_shift - 1); + } + + for (i = startblock; i < numblocks;) { +@@ -430,8 +430,8 @@ + + if (ret) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); +- printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", +- i >> 1, (unsigned int)from); ++ printk(KERN_WARNING "Bad eraseblock %d at 0x%09llx\n", ++ i >> 1, (unsigned long long)from); + mtd->ecc_stats.badblocks++; + } + +@@ -497,7 +497,7 @@ + for (block = 0; block < td->maxblocks; block++) { + + int actblock = startblock + dir * block; +- loff_t offs = actblock << this->bbt_erase_shift; ++ loff_mtd_t offs = actblock << this->bbt_erase_shift; + + /* Read first page */ + scan_read_raw(mtd, buf, offs, mtd->writesize); +@@ -567,8 +567,8 @@ + int nrchips, bbtoffs, pageoffs, ooboffs; + uint8_t msk[4]; + uint8_t rcode = td->reserved_block_code; +- size_t retlen, len = 0; +- loff_t to; ++ size_mtd_t retlen, len = 0; ++ loff_mtd_t to; + struct mtd_oob_ops ops; + + ops.ooblen = mtd->oobsize; +@@ -655,12 +655,12 @@ + + bbtoffs = chip * (numblocks >> 2); + +- to = ((loff_t) page) << this->page_shift; ++ to = ((loff_mtd_t) page) << this->page_shift; + + /* Must we save the block contents ? */ + if (td->options & NAND_BBT_SAVECONTENT) { + /* Make it block aligned */ +- to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); ++ to &= ~((loff_mtd_t) ((1 << this->bbt_erase_shift) - 1)); + len = 1 << this->bbt_erase_shift; + res = mtd->read(mtd, to, len, &retlen, buf); + if (res < 0) { +@@ -685,12 +685,12 @@ + pageoffs = page - (int)(to >> this->page_shift); + offs = pageoffs << this->page_shift; + /* Preset the bbt area with 0xff */ +- memset(&buf[offs], 0xff, (size_t) (numblocks >> sft)); ++ memset(&buf[offs], 0xff, (size_mtd_t) (numblocks >> sft)); + ooboffs = len + (pageoffs * mtd->oobsize); + + } else { + /* Calc length */ +- len = (size_t) (numblocks >> sft); ++ len = (size_mtd_t) (numblocks >> sft); + /* Make it page aligned ! */ + len = (len + (mtd->writesize - 1)) & + ~(mtd->writesize - 1); +@@ -1017,7 +1017,7 @@ + * + * The function updates the bad block table(s) + */ +-int nand_update_bbt(struct mtd_info *mtd, loff_t offs) ++int nand_update_bbt(struct mtd_info *mtd, loff_mtd_t offs) + { + struct nand_chip *this = mtd->priv; + int len, res = 0, writeops = 0; +@@ -1193,7 +1193,7 @@ + * @allowbbt: allow access to bad block table region + * + */ +-int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) ++int nand_isbad_bbt(struct mtd_info *mtd, loff_mtd_t offs, int allowbbt) + { + struct nand_chip *this = mtd->priv; + int block; +diff -urN linux-2.6.24.7.old/drivers/mtd/nand/nand_ids.c linux-2.6.24.7/drivers/mtd/nand/nand_ids.c +--- linux-2.6.24.7.old/drivers/mtd/nand/nand_ids.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/nand/nand_ids.c 2009-04-12 18:13:57.000000000 +0200 +@@ -111,6 +111,9 @@ + {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16}, + {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, + ++ /* 32 Gigabit */ ++ {"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS}, ++ + /* + * Renesas AND 1 Gigabit. Those chips do not support extended id and + * have a strange page/block layout ! The chosen minimum erasesize is +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/Kconfig linux-2.6.24.7/drivers/mtd/ubi/Kconfig +--- linux-2.6.24.7.old/drivers/mtd/ubi/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -55,4 +55,18 @@ + this if no legacy software will be used. + + source "drivers/mtd/ubi/Kconfig.debug" ++ ++config MTD_UBI_BLKDEVS ++ tristate "Common interface to block layer for UBI 'translation layers'" ++ depends on BLOCK ++ default n ++ ++config MTD_UBI_BLOCK ++ tristate "Emulate block devices" ++ default n ++ depends on MTD_UBI_BLKDEVS ++ help ++ This option enables Block layer emulation on top of UBI volumes: for ++ each UBI volumes an block device is created. This is handy to make ++ traditional filesystem (like ext2, VFAT) work on top of UBI. + endmenu +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/Makefile linux-2.6.24.7/drivers/mtd/ubi/Makefile +--- linux-2.6.24.7.old/drivers/mtd/ubi/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -5,3 +5,6 @@ + + ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o + ubi-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o ++ ++obj-$(CONFIG_MTD_UBI_BLKDEVS) += bdev.o ++obj-$(CONFIG_MTD_UBI_BLOCK) += ubiblk.o +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/bdev.c linux-2.6.24.7/drivers/mtd/ubi/bdev.c +--- linux-2.6.24.7.old/drivers/mtd/ubi/bdev.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/ubi/bdev.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,432 @@ ++/* ++ * ++ * (C) 2003 David Woodhouse ++ * ++ * Interface to Linux 2.5 block layer for UBI 'translation layers'. ++ * ++ * 2008 Yurong Tan : ++ * borrow from mtd_blkdevs.c for building block device layer on top of UBI ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ubi.h" ++#include "ubiblk.h" ++ ++static LIST_HEAD(blktrans_majors); ++extern struct mutex vol_table_mutex; ++extern struct ubi_volume *vol_table[]; ++ ++extern void register_vol_user (struct vol_notifier *new); ++extern int unregister_vol_user (struct vol_notifier *old); ++extern int ubi_major2num(int major); ++ ++struct ubi_blkcore_priv { ++ struct task_struct *thread; ++ struct request_queue *rq; ++ spinlock_t queue_lock; ++}; ++ ++static int do_blktrans_request(struct ubi_blktrans_ops *tr, ++ struct ubi_blktrans_dev *dev, ++ struct request *req) ++{ ++ unsigned long block, nsect; ++ char *buf; ++ ++ block = req->sector << 9 >> tr->blkshift; ++ nsect = req->current_nr_sectors << 9 >> tr->blkshift; ++ buf = req->buffer; ++ ++ if (!blk_fs_request(req)) ++ return 0; ++ ++ if (req->sector + req->current_nr_sectors > get_capacity(req->rq_disk)) ++ return 0; ++ ++ switch(rq_data_dir(req)) { ++ case READ: ++ for (; nsect > 0; nsect--, block++, buf += tr->blksize) ++ if (tr->readsect(dev, block, buf)) ++ return 0; ++ return 1; ++ ++ case WRITE: ++ if (!tr->writesect) ++ return 0; ++ ++ for (; nsect > 0; nsect--, block++, buf += tr->blksize) ++ if (tr->writesect(dev, block, buf)) ++ return 0; ++ return 1; ++ ++ default: ++ printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req)); ++ return 0; ++ } ++} ++ ++static int ubi_blktrans_thread(void *arg) ++{ ++ struct ubi_blktrans_ops *tr = arg; ++ struct request_queue *rq = tr->blkcore_priv->rq; ++ ++ /* we might get involved when memory gets low, so use PF_MEMALLOC */ ++ current->flags |= PF_MEMALLOC; ++ ++ spin_lock_irq(rq->queue_lock); ++ while (!kthread_should_stop()) { ++ struct request *req; ++ struct ubi_blktrans_dev *dev; ++ int res = 0; ++ ++ req = elv_next_request(rq); ++ ++ if (!req) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ spin_unlock_irq(rq->queue_lock); ++ schedule(); ++ spin_lock_irq(rq->queue_lock); ++ continue; ++ } ++ dev = req->rq_disk->private_data; ++ tr = dev->tr; ++ ++ spin_unlock_irq(rq->queue_lock); ++ mutex_lock(&dev->lock); ++ res = do_blktrans_request(tr, dev, req); ++ mutex_unlock(&dev->lock); ++ spin_lock_irq(rq->queue_lock); ++ ++ end_request(req, res); ++ } ++ spin_unlock_irq(rq->queue_lock); ++ ++ return 0; ++} ++ ++static void ubi_blktrans_request(struct request_queue *rq) ++{ ++ struct ubi_blktrans_ops *tr = rq->queuedata; ++ wake_up_process(tr->blkcore_priv->thread); ++} ++ ++static int blktrans_open(struct inode *i, struct file *f) ++{ ++ struct ubi_blktrans_dev *dev; ++ struct ubi_blktrans_ops *tr; ++ int ret =0; ++ ++ dev = i->i_bdev->bd_disk->private_data; ++ tr = dev->tr; ++ ++ if (!try_module_get(tr->owner)) ++ goto out_tr; ++ ++ if(!tr->open) ++ return -1; ++ else ++ ret = tr->open(i,f); ++ ++ return ret; ++out_tr: ++ module_put(tr->owner); ++ return -1; ++} ++ ++static int blktrans_release(struct inode *i, struct file *f) ++{ ++ struct ubi_blktrans_dev *dev; ++ struct ubi_blktrans_ops *tr; ++ struct ubi_volume_desc *desc; ++ int ret = 0; ++ ++ dev = i->i_bdev->bd_disk->private_data; ++ tr = dev->tr; ++ desc = dev->uv; ++ ++ if (tr->release) ++ ret = tr->release(dev); ++ ++ module_put(tr->owner); ++ return ret; ++} ++ ++static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) ++{ ++ struct ubi_blktrans_dev *dev = bdev->bd_disk->private_data; ++ ++ if (dev->tr->getgeo) ++ return dev->tr->getgeo(dev, geo); ++ return -ENOTTY; ++} ++ ++static int blktrans_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct ubi_blktrans_dev *dev = inode->i_bdev->bd_disk->private_data; ++ struct ubi_blktrans_ops *tr = dev->tr; ++ ++ switch (cmd) { ++ case BLKFLSBUF: ++ if (tr->flush) ++ return tr->flush(dev); ++ /* The core code did the work, we had nothing to do. */ ++ return 0; ++ default: ++ return -ENOTTY; ++ } ++} ++ ++struct block_device_operations ubi_blktrans_ops = { ++ .owner = THIS_MODULE, ++ .open = blktrans_open, ++ .release = blktrans_release, ++ .ioctl = blktrans_ioctl, ++ .getgeo = blktrans_getgeo, ++}; ++ ++int add_ubi_blktrans_dev(struct ubi_blktrans_dev *new) ++{ ++ struct ubi_blktrans_ops *tr = new->tr; ++ struct list_head *this; ++ int last_devnum = -1; ++ struct gendisk *gd; ++ ++ if (mutex_trylock(&vol_table_mutex)) { ++ mutex_unlock(&vol_table_mutex); ++ BUG(); ++ } ++ ++ list_for_each(this, &tr->devs) { ++ struct ubi_blktrans_dev *d = list_entry(this, struct ubi_blktrans_dev, list); ++ if (new->devnum == -1) { ++ /* Use first free number */ ++ if (d->devnum != last_devnum+1) { ++ /* Found a free devnum. Plug it in here */ ++ new->devnum = last_devnum+1; ++ list_add_tail(&new->list, &d->list); ++ goto added; ++ } ++ } else if (d->devnum == new->devnum) { ++ /* Required number taken */ ++ return -EBUSY; ++ } else if (d->devnum > new->devnum) { ++ /* Required number was free */ ++ list_add_tail(&new->list, &d->list); ++ goto added; ++ } ++ last_devnum = d->devnum; ++ } ++ if (new->devnum == -1) ++ new->devnum = last_devnum+1; ++ ++ if ((new->devnum << tr->part_bits) > 256) { ++ return -EBUSY; ++ } ++ ++ mutex_init(&new->lock); ++ list_add_tail(&new->list, &tr->devs); ++ added: ++ if (!tr->writesect) ++ new->readonly = 1; ++ ++ gd = alloc_disk(1 << tr->part_bits); ++ if (!gd) { ++ list_del(&new->list); ++ return -ENOMEM; ++ } ++ gd->major = tr->major; ++ gd->first_minor = (new->devnum) << tr->part_bits; ++ gd->fops = &ubi_blktrans_ops; ++ ++ if (tr->part_bits) ++ if (new->devnum < 26) ++ snprintf(gd->disk_name, sizeof(gd->disk_name), ++ "%s%c", tr->name, 'a' + new->devnum); ++ else ++ snprintf(gd->disk_name, sizeof(gd->disk_name), ++ "%s%c%c", tr->name, ++ 'a' - 1 + new->devnum / 26, ++ 'a' + new->devnum % 26); ++ else ++ snprintf(gd->disk_name, sizeof(gd->disk_name), ++ "%s%d", tr->name, new->devnum); ++ ++ /* 2.5 has capacity in units of 512 bytes while still ++ having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ ++ set_capacity(gd, (new->size * tr->blksize) >> 9); ++ ++ gd->private_data = new; ++ new->blkcore_priv = gd; ++ gd->queue = tr->blkcore_priv->rq; ++ ++ if (new->readonly) ++ set_disk_ro(gd, 1); ++ ++ add_disk(gd); ++ ++ return 0; ++} ++ ++int del_ubi_blktrans_dev(struct ubi_blktrans_dev *old) ++{ ++ if (mutex_trylock(&vol_table_mutex)) { ++ mutex_unlock(&vol_table_mutex); ++ BUG(); ++ } ++ ++ list_del(&old->list); ++ ++ del_gendisk(old->blkcore_priv); ++ put_disk(old->blkcore_priv); ++ ++ return 0; ++} ++ ++static void blktrans_notify_remove(struct ubi_volume *vol) ++{ ++ struct list_head *this, *this2, *next; ++ ++ list_for_each(this, &blktrans_majors) { ++ struct ubi_blktrans_ops *tr = list_entry(this, struct ubi_blktrans_ops, list); ++ ++ list_for_each_safe(this2, next, &tr->devs) { ++ struct ubi_blktrans_dev *dev = list_entry(this2, struct ubi_blktrans_dev, list); ++ ++ if (dev->uv->vol == vol) ++ tr->remove_vol(dev); ++ } ++ } ++} ++ ++static void blktrans_notify_add(struct ubi_volume *vol) ++{ ++ struct list_head *this; ++ ++ list_for_each(this, &blktrans_majors) { ++ struct ubi_blktrans_ops *tr = list_entry(this, struct ubi_blktrans_ops, list); ++ ++ tr->add_vol(tr,vol); ++ } ++ ++} ++ ++static struct vol_notifier blktrans_notifier = { ++ .add = blktrans_notify_add, ++ .remove = blktrans_notify_remove, ++}; ++ ++ ++int register_ubi_blktrans(struct ubi_blktrans_ops *tr) ++{ ++ int i; ++ ++ /* Register the notifier if/when the first device type is ++ registered, to prevent the link/init ordering from fucking ++ us over. */ ++ if (!blktrans_notifier.list.next) ++ register_vol_user(&blktrans_notifier); ++ ++ tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); ++ if (!tr->blkcore_priv) ++ return -ENOMEM; ++ ++ mutex_lock(&vol_table_mutex); ++ tr->major = register_blkdev(0, tr->name); ++ spin_lock_init(&tr->blkcore_priv->queue_lock); ++ ++ tr->blkcore_priv->rq = blk_init_queue(ubi_blktrans_request, &tr->blkcore_priv->queue_lock); ++ if (!tr->blkcore_priv->rq) { ++ unregister_blkdev(tr->major, tr->name); ++ kfree(tr->blkcore_priv); ++ mutex_unlock(&vol_table_mutex); ++ return -ENOMEM; ++ } ++ ++ tr->blkcore_priv->rq->queuedata = tr; ++ blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize); ++ tr->blkshift = ffs(tr->blksize) - 1; ++ ++ tr->blkcore_priv->thread = kthread_run(ubi_blktrans_thread, tr, ++ "%sd", tr->name); ++ if (IS_ERR(tr->blkcore_priv->thread)) { ++ blk_cleanup_queue(tr->blkcore_priv->rq); ++ unregister_blkdev(tr->major, tr->name); ++ kfree(tr->blkcore_priv); ++ mutex_unlock(&vol_table_mutex); ++ return PTR_ERR(tr->blkcore_priv->thread); ++ } ++ ++ INIT_LIST_HEAD(&tr->devs); ++ list_add(&tr->list, &blktrans_majors); ++ ++ for (i=0; iadd_vol(tr, vol_table[i]); ++ } ++ ++ mutex_unlock(&vol_table_mutex); ++ return 0; ++} ++ ++int deregister_ubi_blktrans(struct ubi_blktrans_ops *tr) ++{ ++ struct list_head *this, *next; ++ ++ mutex_lock(&vol_table_mutex); ++ ++ /* Clean up the kernel thread */ ++ kthread_stop(tr->blkcore_priv->thread); ++ ++ /* Remove it from the list of active majors */ ++ list_del(&tr->list); ++ ++ list_for_each_safe(this, next, &tr->devs) { ++ struct ubi_blktrans_dev *dev = list_entry(this, struct ubi_blktrans_dev, list); ++ tr->remove_vol(dev); ++ } ++ ++ blk_cleanup_queue(tr->blkcore_priv->rq); ++ unregister_blkdev(tr->major, tr->name); ++ ++ mutex_unlock(&vol_table_mutex); ++ ++ kfree(tr->blkcore_priv); ++ ++ BUG_ON(!list_empty(&tr->devs)); ++ return 0; ++} ++ ++static void __exit ubi_blktrans_exit(void) ++{ ++ /* No race here -- if someone's currently in register_ubi_blktrans ++ we're screwed anyway. */ ++ if (blktrans_notifier.list.next) ++ unregister_vol_user(&blktrans_notifier); ++} ++ ++ ++module_exit(ubi_blktrans_exit); ++ ++EXPORT_SYMBOL_GPL(register_ubi_blktrans); ++EXPORT_SYMBOL_GPL(deregister_ubi_blktrans); ++EXPORT_SYMBOL_GPL(add_ubi_blktrans_dev); ++EXPORT_SYMBOL_GPL(del_ubi_blktrans_dev); ++ ++MODULE_AUTHOR("David Woodhouse , Yurong Tan "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Common interface to block layer for UBI 'translation layers'"); ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/build.c linux-2.6.24.7/drivers/mtd/ubi/build.c +--- linux-2.6.24.7.old/drivers/mtd/ubi/build.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/build.c 2009-04-12 18:13:57.000000000 +0200 +@@ -18,14 +18,20 @@ + * + * Author: Artem Bityutskiy (Битюцкий Ðртём), + * Frank Haverkamp ++ * Yurong tan(Nancy) + */ + + /* +- * This file includes UBI initialization and building of UBI devices. At the +- * moment UBI devices may only be added while UBI is initialized, but dynamic +- * device add/remove functionality is planned. Also, at the moment we only +- * attach UBI devices by scanning, which will become a bottleneck when flashes +- * reach certain large size. Then one may improve UBI and add other methods. ++ * This file includes UBI initialization and building of UBI devices. ++ * ++ * When UBI is initialized, it attaches all the MTD devices specified as the ++ * module load parameters or the kernel boot parameters. If MTD devices were ++ * specified, UBI does not attach any MTD device, but it is possible to do ++ * later using the "UBI control device". ++ * ++ * At the moment we only attach UBI devices by scanning, which will become a ++ * bottleneck when flashes reach certain large size. Then one may improve UBI ++ * and add other methods, although it does not seem to be easy to do. + */ + + #include +@@ -33,23 +39,130 @@ + #include + #include + #include ++#include + #include ++#include + #include "ubi.h" + + /* Maximum length of the 'mtd=' parameter */ + #define MTD_PARAM_LEN_MAX 64 + ++/* add by Nancy begin */ ++DEFINE_MUTEX(vol_table_mutex); ++struct ubi_volume *vol_table[UBI_MAX_VOLUMES]; ++ ++EXPORT_SYMBOL_GPL(vol_table_mutex); ++EXPORT_SYMBOL_GPL(vol_table); ++ ++static LIST_HEAD(vol_notifiers); ++ ++int add_vol_device(struct ubi_volume *vol) ++{ ++ mutex_lock(&vol_table_mutex); ++ if (!vol_table[vol->vol_id]) { ++ ++ struct list_head *this; ++ vol_table[vol->vol_id] = vol; ++ /* No need to get a refcount on the module containing ++ the notifier, since we hold the vol_table_mutex */ ++ list_for_each(this, &vol_notifiers) { ++ struct vol_notifier *not = list_entry(this, struct vol_notifier, list); ++ not->add(vol); ++ } ++ mutex_unlock(&vol_table_mutex); ++ /* We _know_ we aren't being removed, because ++ our caller is still holding us here. So none ++ of this try_ nonsense, and no bitching about it ++ either. :) */ ++ return 0; ++ } ++ mutex_unlock(&vol_table_mutex); ++ return 1; ++} ++ ++int del_vol_device (struct ubi_volume *vol) ++{ ++ int ret; ++ struct list_head *this; ++ ++ mutex_lock(&vol_table_mutex); ++ if (vol_table[vol->vol_id] != vol) { ++ ret = -ENODEV; ++ } else if (vol->readers ||vol->writers || vol->exclusive) { ++ printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count 0\n", ++ vol->vol_id, vol->name); ++ ret = -EBUSY; ++ } else { ++ /* No need to get a refcount on the module containing ++ the notifier, since we hold the vol_table_mutex */ ++ list_for_each(this, &vol_notifiers) { ++ struct vol_notifier *not = list_entry(this, struct vol_notifier, list); ++ not->remove(vol); ++ } ++ ++ vol_table[vol->vol_id] = NULL; ++ module_put(THIS_MODULE); ++ ret = 0; ++ } ++ mutex_unlock(&vol_table_mutex); ++ return ret; ++} ++ ++void register_vol_user(struct vol_notifier *new) ++{ ++ int i; ++ ++ mutex_lock(&vol_table_mutex); ++ list_add(&new->list, &vol_notifiers); ++ __module_get(THIS_MODULE); ++ ++ for (i=0; i< UBI_MAX_VOLUMES; i++) ++ if (vol_table[i]) ++ new->add(vol_table[i]); ++ ++ mutex_unlock(&vol_table_mutex); ++} ++ ++int unregister_vol_user(struct vol_notifier *old) ++{ ++ int i; ++ ++ mutex_lock(&vol_table_mutex); ++ module_put(THIS_MODULE); ++ ++ for (i=0; i< UBI_MAX_VOLUMES; i++) ++ if (vol_table[i]) ++ old->remove(vol_table[i]); ++ ++ list_del(&old->list); ++ mutex_unlock(&vol_table_mutex); ++ return 0; ++} ++ ++static int bdev_init(struct ubi_device *ubi){ ++ int i; ++ for(i=0; ivtbl_slots; i++) ++ if(ubi->volumes[i]) ++ add_vol_device(ubi->volumes[i]); ++ return 0; ++} ++ ++EXPORT_SYMBOL_GPL(add_vol_device); ++EXPORT_SYMBOL_GPL(del_vol_device); ++EXPORT_SYMBOL_GPL(register_vol_user); ++EXPORT_SYMBOL_GPL(unregister_vol_user); ++/* add by Nancy end*/ ++ ++ + /** + * struct mtd_dev_param - MTD device parameter description data structure. + * @name: MTD device name or number string + * @vid_hdr_offs: VID header offset +- * @data_offs: data offset + */ + struct mtd_dev_param + { + char name[MTD_PARAM_LEN_MAX]; + int vid_hdr_offs; +- int data_offs; + }; + + /* Numbers of elements set in the @mtd_dev_param array */ +@@ -58,14 +171,28 @@ + /* MTD devices specification parameters */ + static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES]; + +-/* Number of UBI devices in system */ +-int ubi_devices_cnt; ++/* Root UBI "class" object (corresponds to '//class/ubi/') */ ++struct class *ubi_class; ++ ++/* Slab cache for wear-leveling entries */ ++struct kmem_cache *ubi_wl_entry_slab; ++ ++/* UBI control character device */ ++static struct miscdevice ubi_ctrl_cdev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "ubi_ctrl", ++ .fops = &ubi_ctrl_cdev_operations, ++}; + + /* All UBI devices in system */ +-struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; ++static struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; + +-/* Root UBI "class" object (corresponds to '//class/ubi/') */ +-struct class *ubi_class; ++/* Serializes UBI devices creations and removals */ ++DEFINE_MUTEX(ubi_devices_mutex); ++ ++/* Protects @ubi_devices and @ubi->ref_count */ ++static DEFINE_SPINLOCK(ubi_devices_lock); ++EXPORT_SYMBOL_GPL(ubi_devices_lock); + + /* "Show" method for files in '//class/ubi/' */ + static ssize_t ubi_version_show(struct class *class, char *buf) +@@ -101,38 +228,152 @@ + __ATTR(min_io_size, S_IRUGO, dev_attribute_show, NULL); + static struct device_attribute dev_bgt_enabled = + __ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL); ++static struct device_attribute dev_mtd_num = ++ __ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL); ++ ++/** ++ * ubi_get_device - get UBI device. ++ * @ubi_num: UBI device number ++ * ++ * This function returns UBI device description object for UBI device number ++ * @ubi_num, or %NULL if the device does not exist. This function increases the ++ * device reference count to prevent removal of the device. In other words, the ++ * device cannot be removed if its reference count is not zero. ++ */ ++struct ubi_device *ubi_get_device(int ubi_num) ++{ ++ struct ubi_device *ubi; ++ ++ spin_lock(&ubi_devices_lock); ++ ubi = ubi_devices[ubi_num]; ++ if (ubi) { ++ ubi_assert(ubi->ref_count >= 0); ++ ubi->ref_count += 1; ++ get_device(&ubi->dev); ++ } ++ spin_unlock(&ubi_devices_lock); ++ ++ return ubi; ++} ++ ++/** ++ * ubi_put_device - drop an UBI device reference. ++ * @ubi: UBI device description object ++ */ ++void ubi_put_device(struct ubi_device *ubi) ++{ ++ spin_lock(&ubi_devices_lock); ++ ubi->ref_count -= 1; ++ put_device(&ubi->dev); ++ spin_unlock(&ubi_devices_lock); ++} ++ ++/** ++ * ubi_get_by_major - get UBI device description object by character device ++ * major number. ++ * @major: major number ++ * ++ * This function is similar to 'ubi_get_device()', but it searches the device ++ * by its major number. ++ */ ++struct ubi_device *ubi_get_by_major(int major) ++{ ++ int i; ++ struct ubi_device *ubi; ++ ++ spin_lock(&ubi_devices_lock); ++ for (i = 0; i < UBI_MAX_DEVICES; i++) { ++ ubi = ubi_devices[i]; ++ if (ubi && MAJOR(ubi->cdev.dev) == major) { ++ ubi_assert(ubi->ref_count >= 0); ++ ubi->ref_count += 1; ++ get_device(&ubi->dev); ++ spin_unlock(&ubi_devices_lock); ++ return ubi; ++ } ++ } ++ spin_unlock(&ubi_devices_lock); ++ ++ return NULL; ++} ++ ++/** ++ * ubi_major2num - get UBI device number by character device major number. ++ * @major: major number ++ * ++ * This function searches UBI device number object by its major number. If UBI ++ * device was not found, this function returns -ENODEV, otherwise the UBI device ++ * number is returned. ++ */ ++int ubi_major2num(int major) ++{ ++ int i, ubi_num = -ENODEV; ++ ++ spin_lock(&ubi_devices_lock); ++ for (i = 0; i < UBI_MAX_DEVICES; i++) { ++ struct ubi_device *ubi = ubi_devices[i]; ++ ++ if ( (ubi && MAJOR(ubi->cdev.dev) == major) || ++ (ubi && ubi->bdev_major == major)) { ++ ubi_num = ubi->ubi_num; ++ break; ++ } ++ } ++ spin_unlock(&ubi_devices_lock); ++ ++ return ubi_num; ++} ++EXPORT_SYMBOL_GPL(ubi_major2num); + + /* "Show" method for files in '//class/ubi/ubiX/' */ + static ssize_t dev_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf) + { +- const struct ubi_device *ubi; ++ ssize_t ret; ++ struct ubi_device *ubi; + ++ /* ++ * The below code looks weird, but it actually makes sense. We get the ++ * UBI device reference from the contained 'struct ubi_device'. But it ++ * is unclear if the device was removed or not yet. Indeed, if the ++ * device was removed before we increased its reference count, ++ * 'ubi_get_device()' will return -ENODEV and we fail. ++ * ++ * Remember, 'struct ubi_device' is freed in the release function, so ++ * we still can use 'ubi->ubi_num'. ++ */ + ubi = container_of(dev, struct ubi_device, dev); ++ ubi = ubi_get_device(ubi->ubi_num); ++ if (!ubi) ++ return -ENODEV; ++ + if (attr == &dev_eraseblock_size) +- return sprintf(buf, "%d\n", ubi->leb_size); ++ ret = sprintf(buf, "%d\n", ubi->leb_size); + else if (attr == &dev_avail_eraseblocks) +- return sprintf(buf, "%d\n", ubi->avail_pebs); ++ ret = sprintf(buf, "%d\n", ubi->avail_pebs); + else if (attr == &dev_total_eraseblocks) +- return sprintf(buf, "%d\n", ubi->good_peb_count); ++ ret = sprintf(buf, "%d\n", ubi->good_peb_count); + else if (attr == &dev_volumes_count) +- return sprintf(buf, "%d\n", ubi->vol_count); ++ ret = sprintf(buf, "%d\n", ubi->vol_count - UBI_INT_VOL_COUNT); + else if (attr == &dev_max_ec) +- return sprintf(buf, "%d\n", ubi->max_ec); ++ ret = sprintf(buf, "%d\n", ubi->max_ec); + else if (attr == &dev_reserved_for_bad) +- return sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); ++ ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); + else if (attr == &dev_bad_peb_count) +- return sprintf(buf, "%d\n", ubi->bad_peb_count); ++ ret = sprintf(buf, "%d\n", ubi->bad_peb_count); + else if (attr == &dev_max_vol_count) +- return sprintf(buf, "%d\n", ubi->vtbl_slots); ++ ret = sprintf(buf, "%d\n", ubi->vtbl_slots); + else if (attr == &dev_min_io_size) +- return sprintf(buf, "%d\n", ubi->min_io_size); ++ ret = sprintf(buf, "%d\n", ubi->min_io_size); + else if (attr == &dev_bgt_enabled) +- return sprintf(buf, "%d\n", ubi->thread_enabled); ++ ret = sprintf(buf, "%d\n", ubi->thread_enabled); ++ else if (attr == &dev_mtd_num) ++ ret = sprintf(buf, "%d\n", ubi->mtd->index); + else +- BUG(); ++ ret = -EINVAL; + +- return 0; ++ ubi_put_device(ubi); ++ return ret; + } + + /* Fake "release" method for UBI devices */ +@@ -150,68 +391,44 @@ + int err; + + ubi->dev.release = dev_release; +- ubi->dev.devt = MKDEV(ubi->major, 0); ++ ubi->dev.devt = ubi->cdev.dev; + ubi->dev.class = ubi_class; + sprintf(&ubi->dev.bus_id[0], UBI_NAME_STR"%d", ubi->ubi_num); + err = device_register(&ubi->dev); + if (err) +- goto out; ++ return err; + + err = device_create_file(&ubi->dev, &dev_eraseblock_size); + if (err) +- goto out_unregister; ++ return err; + err = device_create_file(&ubi->dev, &dev_avail_eraseblocks); + if (err) +- goto out_eraseblock_size; ++ return err; + err = device_create_file(&ubi->dev, &dev_total_eraseblocks); + if (err) +- goto out_avail_eraseblocks; ++ return err; + err = device_create_file(&ubi->dev, &dev_volumes_count); + if (err) +- goto out_total_eraseblocks; ++ return err; + err = device_create_file(&ubi->dev, &dev_max_ec); + if (err) +- goto out_volumes_count; ++ return err; + err = device_create_file(&ubi->dev, &dev_reserved_for_bad); + if (err) +- goto out_volumes_max_ec; ++ return err; + err = device_create_file(&ubi->dev, &dev_bad_peb_count); + if (err) +- goto out_reserved_for_bad; ++ return err; + err = device_create_file(&ubi->dev, &dev_max_vol_count); + if (err) +- goto out_bad_peb_count; ++ return err; + err = device_create_file(&ubi->dev, &dev_min_io_size); + if (err) +- goto out_max_vol_count; ++ return err; + err = device_create_file(&ubi->dev, &dev_bgt_enabled); + if (err) +- goto out_min_io_size; +- +- return 0; +- +-out_min_io_size: +- device_remove_file(&ubi->dev, &dev_min_io_size); +-out_max_vol_count: +- device_remove_file(&ubi->dev, &dev_max_vol_count); +-out_bad_peb_count: +- device_remove_file(&ubi->dev, &dev_bad_peb_count); +-out_reserved_for_bad: +- device_remove_file(&ubi->dev, &dev_reserved_for_bad); +-out_volumes_max_ec: +- device_remove_file(&ubi->dev, &dev_max_ec); +-out_volumes_count: +- device_remove_file(&ubi->dev, &dev_volumes_count); +-out_total_eraseblocks: +- device_remove_file(&ubi->dev, &dev_total_eraseblocks); +-out_avail_eraseblocks: +- device_remove_file(&ubi->dev, &dev_avail_eraseblocks); +-out_eraseblock_size: +- device_remove_file(&ubi->dev, &dev_eraseblock_size); +-out_unregister: +- device_unregister(&ubi->dev); +-out: +- ubi_err("failed to initialize sysfs for %s", ubi->ubi_name); ++ return err; ++ err = device_create_file(&ubi->dev, &dev_mtd_num); + return err; + } + +@@ -221,6 +438,7 @@ + */ + static void ubi_sysfs_close(struct ubi_device *ubi) + { ++ device_remove_file(&ubi->dev, &dev_mtd_num); + device_remove_file(&ubi->dev, &dev_bgt_enabled); + device_remove_file(&ubi->dev, &dev_min_io_size); + device_remove_file(&ubi->dev, &dev_max_vol_count); +@@ -244,7 +462,7 @@ + + for (i = 0; i < ubi->vtbl_slots; i++) + if (ubi->volumes[i]) +- ubi_free_volume(ubi, i); ++ ubi_free_volume(ubi, ubi->volumes[i]); + } + + /** +@@ -259,9 +477,6 @@ + int i, err; + dev_t dev; + +- mutex_init(&ubi->vtbl_mutex); +- spin_lock_init(&ubi->volumes_lock); +- + sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); + + /* +@@ -278,39 +493,40 @@ + return err; + } + ++ ubi_assert(MINOR(dev) == 0); + cdev_init(&ubi->cdev, &ubi_cdev_operations); +- ubi->major = MAJOR(dev); +- dbg_msg("%s major is %u", ubi->ubi_name, ubi->major); ++ dbg_msg("%s major is %u", ubi->ubi_name, MAJOR(dev)); + ubi->cdev.owner = THIS_MODULE; + +- dev = MKDEV(ubi->major, 0); + err = cdev_add(&ubi->cdev, dev, 1); + if (err) { +- ubi_err("cannot add character device %s", ubi->ubi_name); ++ ubi_err("cannot add character device"); + goto out_unreg; + } + + err = ubi_sysfs_init(ubi); + if (err) +- goto out_cdev; ++ goto out_sysfs; + + for (i = 0; i < ubi->vtbl_slots; i++) + if (ubi->volumes[i]) { +- err = ubi_add_volume(ubi, i); +- if (err) ++ err = ubi_add_volume(ubi, ubi->volumes[i]); ++ if (err) { ++ ubi_err("cannot add volume %d", i); + goto out_volumes; ++ } + } + + return 0; + + out_volumes: + kill_volumes(ubi); ++out_sysfs: + ubi_sysfs_close(ubi); +-out_cdev: + cdev_del(&ubi->cdev); + out_unreg: +- unregister_chrdev_region(MKDEV(ubi->major, 0), +- ubi->vtbl_slots + 1); ++ unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); ++ ubi_err("cannot initialize UBI %s, error %d", ubi->ubi_name, err); + return err; + } + +@@ -323,7 +539,7 @@ + kill_volumes(ubi); + ubi_sysfs_close(ubi); + cdev_del(&ubi->cdev); +- unregister_chrdev_region(MKDEV(ubi->major, 0), ubi->vtbl_slots + 1); ++ unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); + } + + /** +@@ -370,7 +586,11 @@ + out_wl: + ubi_wl_close(ubi); + out_vtbl: ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(ubi->vtbl); ++#else + vfree(ubi->vtbl); ++#endif + out_si: + ubi_scan_destroy_si(si); + return err; +@@ -384,15 +604,21 @@ + * assumed: + * o EC header is always at offset zero - this cannot be changed; + * o VID header starts just after the EC header at the closest address +- * aligned to @io->@hdrs_min_io_size; ++ * aligned to @io->hdrs_min_io_size; + * o data starts just after the VID header at the closest address aligned to +- * @io->@min_io_size ++ * @io->min_io_size + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ + static int io_init(struct ubi_device *ubi) + { ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ printk("kmalloc instead of vmalloc is used.\n"); ++#else ++ printk("vmalloc is used.\n"); ++#endif ++ + if (ubi->mtd->numeraseregions != 0) { + /* + * Some flashes have several erase regions. Different regions +@@ -407,13 +633,18 @@ + return -EINVAL; + } + ++ if (ubi->vid_hdr_offset < 0) ++ return -EINVAL; ++ + /* + * Note, in this implementation we support MTD devices with 0x7FFFFFFF + * physical eraseblocks maximum. + */ + + ubi->peb_size = ubi->mtd->erasesize; +- ubi->peb_count = ubi->mtd->size / ubi->mtd->erasesize; ++// ubi->peb_count = ubi->mtd->size / ubi->mtd->erasesize; ++ ubi->peb_count = ubi->mtd->size >> (ffs(ubi->mtd->erasesize) - 1); ++ + ubi->flash_size = ubi->mtd->size; + + if (ubi->mtd->block_isbad && ubi->mtd->block_markbad) +@@ -424,7 +655,8 @@ + + /* Make sure minimal I/O unit is power of 2 */ + if (!is_power_of_2(ubi->min_io_size)) { +- ubi_err("bad min. I/O unit"); ++ ubi_err("min. I/O unit (%d) is not power of 2", ++ ubi->min_io_size); + return -EINVAL; + } + +@@ -453,10 +685,8 @@ + } + + /* Similar for the data offset */ +- if (ubi->leb_start == 0) { +- ubi->leb_start = ubi->vid_hdr_offset + ubi->vid_hdr_alsize; +- ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size); +- } ++ ubi->leb_start = ubi->vid_hdr_offset + UBI_EC_HDR_SIZE; ++ ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size); + + dbg_msg("vid_hdr_offset %d", ubi->vid_hdr_offset); + dbg_msg("vid_hdr_aloffset %d", ubi->vid_hdr_aloffset); +@@ -499,8 +729,16 @@ + ubi->ro_mode = 1; + } + +- dbg_msg("leb_size %d", ubi->leb_size); +- dbg_msg("ro_mode %d", ubi->ro_mode); ++ ubi_msg("physical eraseblock size: %d bytes (%d KiB)", ++ ubi->peb_size, ubi->peb_size >> 10); ++ ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size); ++ ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size); ++ if (ubi->hdrs_min_io_size != ubi->min_io_size) ++ ubi_msg("sub-page size: %d", ++ ubi->hdrs_min_io_size); ++ ubi_msg("VID header offset: %d (aligned %d)", ++ ubi->vid_hdr_offset, ubi->vid_hdr_aloffset); ++ ubi_msg("data offset: %d", ubi->leb_start); + + /* + * Note, ideally, we have to initialize ubi->bad_peb_count here. But +@@ -514,87 +752,171 @@ + } + + /** +- * attach_mtd_dev - attach an MTD device. +- * @mtd_dev: MTD device name or number string +- * @vid_hdr_offset: VID header offset +- * @data_offset: data offset ++ * autoresize - re-size the volume which has the "auto-resize" flag set. ++ * @ubi: UBI device description object ++ * @vol_id: ID of the volume to re-size + * +- * This function attaches an MTD device to UBI. It first treats @mtd_dev as the +- * MTD device name, and tries to open it by this name. If it is unable to open, +- * it tries to convert @mtd_dev to an integer and open the MTD device by its +- * number. Returns zero in case of success and a negative error code in case of +- * failure. ++ * This function re-sizes the volume marked by the @UBI_VTBL_AUTORESIZE_FLG in ++ * the volume table to the largest possible size. See comments in ubi-header.h ++ * for more description of the flag. Returns zero in case of success and a ++ * negative error code in case of failure. + */ +-static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, +- int data_offset) ++static int autoresize(struct ubi_device *ubi, int vol_id) + { +- struct ubi_device *ubi; +- struct mtd_info *mtd; +- int i, err; ++ struct ubi_volume_desc desc; ++ struct ubi_volume *vol = ubi->volumes[vol_id]; ++ int err, old_reserved_pebs = vol->reserved_pebs; + +- mtd = get_mtd_device_nm(mtd_dev); +- if (IS_ERR(mtd)) { +- int mtd_num; +- char *endp; ++ /* ++ * Clear the auto-resize flag in the volume in-memory copy of the ++ * volume table, and 'ubi_resize_volume()' will propogate this change ++ * to the flash. ++ */ ++ ubi->vtbl[vol_id].flags &= ~UBI_VTBL_AUTORESIZE_FLG; + +- if (PTR_ERR(mtd) != -ENODEV) +- return PTR_ERR(mtd); ++ if (ubi->avail_pebs == 0) { ++ struct ubi_vtbl_record vtbl_rec; + + /* +- * Probably this is not MTD device name but MTD device number - +- * check this out. ++ * No avalilable PEBs to re-size the volume, clear the flag on ++ * flash and exit. + */ +- mtd_num = simple_strtoul(mtd_dev, &endp, 0); +- if (*endp != '\0' || mtd_dev == endp) { +- ubi_err("incorrect MTD device: \"%s\"", mtd_dev); +- return -ENODEV; +- } +- +- mtd = get_mtd_device(NULL, mtd_num); +- if (IS_ERR(mtd)) +- return PTR_ERR(mtd); ++ memcpy(&vtbl_rec, &ubi->vtbl[vol_id], ++ sizeof(struct ubi_vtbl_record)); ++ err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); ++ if (err) ++ ubi_err("cannot clean auto-resize flag for volume %d", ++ vol_id); ++ } else { ++ desc.vol = vol; ++ err = ubi_resize_volume(&desc, ++ old_reserved_pebs + ubi->avail_pebs); ++ if (err) ++ ubi_err("cannot auto-resize volume %d", vol_id); + } + +- /* Check if we already have the same MTD device attached */ +- for (i = 0; i < ubi_devices_cnt; i++) +- if (ubi_devices[i]->mtd->index == mtd->index) { +- ubi_err("mtd%d is already attached to ubi%d", ++ if (err) ++ return err; ++ ++ ubi_msg("volume %d (\"%s\") re-sized from %d to %d LEBs", vol_id, ++ vol->name, old_reserved_pebs, vol->reserved_pebs); ++ return 0; ++} ++ ++/** ++ * ubi_attach_mtd_dev - attach an MTD device. ++ * @mtd_dev: MTD device description object ++ * @ubi_num: number to assign to the new UBI device ++ * @vid_hdr_offset: VID header offset ++ * ++ * This function attaches MTD device @mtd_dev to UBI and assign @ubi_num number ++ * to the newly created UBI device, unless @ubi_num is %UBI_DEV_NUM_AUTO, in ++ * which case this function finds a vacant device nubert and assings it ++ * automatically. Returns the new UBI device number in case of success and a ++ * negative error code in case of failure. ++ * ++ * Note, the invocations of this function has to be serialized by the ++ * @ubi_devices_mutex. ++ */ ++int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) ++{ ++ struct ubi_device *ubi; ++ int i, err; ++ ++ /* ++ * Check if we already have the same MTD device attached. ++ * ++ * Note, this function assumes that UBI devices creations and deletions ++ * are serialized, so it does not take the &ubi_devices_lock. ++ */ ++ for (i = 0; i < UBI_MAX_DEVICES; i++) { ++ ubi = ubi_devices[i]; ++ if (ubi && mtd->index == ubi->mtd->index) { ++ dbg_err("mtd%d is already attached to ubi%d", + mtd->index, i); +- err = -EINVAL; +- goto out_mtd; ++ return -EEXIST; + } ++ } + +- ubi = ubi_devices[ubi_devices_cnt] = kzalloc(sizeof(struct ubi_device), +- GFP_KERNEL); +- if (!ubi) { +- err = -ENOMEM; +- goto out_mtd; ++ /* ++ * Make sure this MTD device is not emulated on top of an UBI volume ++ * already. Well, generally this recursion works fine, but there are ++ * different problems like the UBI module takes a reference to itself ++ * by attaching (and thus, opening) the emulated MTD device. This ++ * results in inability to unload the module. And in general it makes ++ * no sense to attach emulated MTD devices, so we prohibit this. ++ */ ++ if (mtd->type == MTD_UBIVOLUME) { ++ ubi_err("refuse attaching mtd%d - it is already emulated on " ++ "top of UBI", mtd->index); ++ return -EINVAL; + } + +- ubi->ubi_num = ubi_devices_cnt; +- ubi->mtd = mtd; ++ if (ubi_num == UBI_DEV_NUM_AUTO) { ++ /* Search for an empty slot in the @ubi_devices array */ ++ for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) ++ if (!ubi_devices[ubi_num]) ++ break; ++ if (ubi_num == UBI_MAX_DEVICES) { ++ dbg_err("only %d UBI devices may be created", UBI_MAX_DEVICES); ++ return -ENFILE; ++ } ++ } else { ++ if (ubi_num >= UBI_MAX_DEVICES) ++ return -EINVAL; ++ ++ /* Make sure ubi_num is not busy */ ++ if (ubi_devices[ubi_num]) { ++ dbg_err("ubi%d already exists", ubi_num); ++ return -EEXIST; ++ } ++ } + +- dbg_msg("attaching mtd%d to ubi%d: VID header offset %d data offset %d", +- ubi->mtd->index, ubi_devices_cnt, vid_hdr_offset, data_offset); ++ ubi = kzalloc(sizeof(struct ubi_device), GFP_KERNEL); ++ if (!ubi) ++ return -ENOMEM; + ++ ubi->mtd = mtd; ++ ubi->ubi_num = ubi_num; + ubi->vid_hdr_offset = vid_hdr_offset; +- ubi->leb_start = data_offset; ++ ubi->autoresize_vol_id = -1; ++ ++ mutex_init(&ubi->buf_mutex); ++ mutex_init(&ubi->ckvol_mutex); ++ mutex_init(&ubi->volumes_mutex); ++ spin_lock_init(&ubi->volumes_lock); ++ ++ dbg_msg("attaching mtd%d to ubi%d: VID header offset %d", ++ mtd->index, ubi_num, vid_hdr_offset); + err = io_init(ubi); + if (err) + goto out_free; + +- mutex_init(&ubi->buf_mutex); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ ubi->peb_buf1 = kmalloc(ubi->peb_size, GFP_KERNEL); ++#else + ubi->peb_buf1 = vmalloc(ubi->peb_size); ++#endif ++ + if (!ubi->peb_buf1) + goto out_free; + ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ ubi->peb_buf2 = kmalloc(ubi->peb_size, GFP_KERNEL); ++#else + ubi->peb_buf2 = vmalloc(ubi->peb_size); ++#endif + if (!ubi->peb_buf2) + goto out_free; + + #ifdef CONFIG_MTD_UBI_DEBUG + mutex_init(&ubi->dbg_buf_mutex); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ ubi->dbg_peb_buf = kmalloc(ubi->peb_size, GFP_KERNEL); ++#else + ubi->dbg_peb_buf = vmalloc(ubi->peb_size); ++#endif ++ + if (!ubi->dbg_peb_buf) + goto out_free; + #endif +@@ -605,22 +927,33 @@ + goto out_free; + } + ++ if (ubi->autoresize_vol_id != -1) { ++ err = autoresize(ubi, ubi->autoresize_vol_id); ++ if (err) ++ goto out_detach; ++ } ++ + err = uif_init(ubi); + if (err) + goto out_detach; + +- ubi_msg("attached mtd%d to ubi%d", ubi->mtd->index, ubi_devices_cnt); +- ubi_msg("MTD device name: \"%s\"", ubi->mtd->name); ++ err = bdev_init(ubi); ++ if(err) ++ goto out_detach; ++ ++ ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name); ++ if (IS_ERR(ubi->bgt_thread)) { ++ err = PTR_ERR(ubi->bgt_thread); ++ ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name, ++ err); ++ goto out_uif; ++ } ++ ++ ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi_num); ++ ubi_msg("MTD device name: \"%s\"", mtd->name); + ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20); +- ubi_msg("physical eraseblock size: %d bytes (%d KiB)", +- ubi->peb_size, ubi->peb_size >> 10); +- ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size); + ubi_msg("number of good PEBs: %d", ubi->good_peb_count); + ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count); +- ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size); +- ubi_msg("VID header offset: %d (aligned %d)", +- ubi->vid_hdr_offset, ubi->vid_hdr_aloffset); +- ubi_msg("data offset: %d", ubi->leb_start); + ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots); + ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD); + ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT); +@@ -638,50 +971,141 @@ + wake_up_process(ubi->bgt_thread); + } + +- ubi_devices_cnt += 1; +- return 0; ++ ubi_devices[ubi_num] = ubi; ++ return ubi_num; + ++out_uif: ++ uif_close(ubi); + out_detach: + ubi_eba_close(ubi); + ubi_wl_close(ubi); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(ubi->vtbl); ++#else + vfree(ubi->vtbl); ++#endif + out_free: ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(ubi->peb_buf1); ++ kfree(ubi->peb_buf2); ++#else + vfree(ubi->peb_buf1); + vfree(ubi->peb_buf2); ++#endif + #ifdef CONFIG_MTD_UBI_DEBUG ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(ubi->dbg_peb_buf); ++#else + vfree(ubi->dbg_peb_buf); + #endif ++#endif + kfree(ubi); +-out_mtd: +- put_mtd_device(mtd); +- ubi_devices[ubi_devices_cnt] = NULL; + return err; + } + + /** +- * detach_mtd_dev - detach an MTD device. +- * @ubi: UBI device description object ++ * ubi_detach_mtd_dev - detach an MTD device. ++ * @ubi_num: UBI device number to detach from ++ * @anyway: detach MTD even if device reference count is not zero ++ * ++ * This function destroys an UBI device number @ubi_num and detaches the ++ * underlying MTD device. Returns zero in case of success and %-EBUSY if the ++ * UBI device is busy and cannot be destroyed, and %-EINVAL if it does not ++ * exist. ++ * ++ * Note, the invocations of this function has to be serialized by the ++ * @ubi_devices_mutex. + */ +-static void detach_mtd_dev(struct ubi_device *ubi) ++int ubi_detach_mtd_dev(int ubi_num, int anyway) + { +- int ubi_num = ubi->ubi_num, mtd_num = ubi->mtd->index; ++ struct ubi_device *ubi; ++ ++ if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) ++ return -EINVAL; ++ ++ spin_lock(&ubi_devices_lock); ++ ubi = ubi_devices[ubi_num]; ++ if (!ubi) { ++ spin_unlock(&ubi_devices_lock); ++ return -EINVAL; ++ } + ++ if (ubi->ref_count) { ++ if (!anyway) { ++ spin_unlock(&ubi_devices_lock); ++ return -EBUSY; ++ } ++ /* This may only happen if there is a bug */ ++ ubi_err("%s reference count %d, destroy anyway", ++ ubi->ubi_name, ubi->ref_count); ++ } ++ ubi_devices[ubi_num] = NULL; ++ spin_unlock(&ubi_devices_lock); ++ ++ ubi_assert(ubi_num == ubi->ubi_num); + dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num); ++ ++ /* ++ * Before freeing anything, we have to stop the background thread to ++ * prevent it from doing anything on this device while we are freeing. ++ */ ++ if (ubi->bgt_thread) ++ kthread_stop(ubi->bgt_thread); ++ + uif_close(ubi); + ubi_eba_close(ubi); + ubi_wl_close(ubi); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(ubi->vtbl); ++#else + vfree(ubi->vtbl); ++#endif + put_mtd_device(ubi->mtd); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(ubi->peb_buf1); ++ kfree(ubi->peb_buf2); ++#else + vfree(ubi->peb_buf1); + vfree(ubi->peb_buf2); ++#endif + #ifdef CONFIG_MTD_UBI_DEBUG ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(ubi->dbg_peb_buf); ++#else + vfree(ubi->dbg_peb_buf); + #endif +- kfree(ubi_devices[ubi_num]); +- ubi_devices[ubi_num] = NULL; +- ubi_devices_cnt -= 1; +- ubi_assert(ubi_devices_cnt >= 0); +- ubi_msg("mtd%d is detached from ubi%d", mtd_num, ubi_num); ++#endif ++ ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num); ++ kfree(ubi); ++ return 0; ++} ++ ++/** ++ * find_mtd_device - open an MTD device by its name or number. ++ * @mtd_dev: name or number of the device ++ * ++ * This function tries to open and MTD device described by @mtd_dev string, ++ * which is first treated as an ASCII number, and if it is not true, it is ++ * treated as MTD device name. Returns MTD device description object in case of ++ * success and a negative error code in case of failure. ++ */ ++static struct mtd_info * __init open_mtd_device(const char *mtd_dev) ++{ ++ struct mtd_info *mtd; ++ int mtd_num; ++ char *endp; ++ ++ mtd_num = simple_strtoul(mtd_dev, &endp, 0); ++ if (*endp != '\0' || mtd_dev == endp) { ++ /* ++ * This does not look like an ASCII integer, probably this is ++ * MTD device name. ++ */ ++ mtd = get_mtd_device_nm(mtd_dev); ++ } else ++ mtd = get_mtd_device(NULL, mtd_num); ++ ++ return mtd; + } + + static int __init ubi_init(void) +@@ -693,47 +1117,94 @@ + BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64); + + if (mtd_devs > UBI_MAX_DEVICES) { +- printk("UBI error: too many MTD devices, maximum is %d\n", +- UBI_MAX_DEVICES); ++ ubi_err("too many MTD devices, maximum is %d", UBI_MAX_DEVICES); + return -EINVAL; + } + ++ /* Create base sysfs directory and sysfs files */ + ubi_class = class_create(THIS_MODULE, UBI_NAME_STR); +- if (IS_ERR(ubi_class)) +- return PTR_ERR(ubi_class); ++ if (IS_ERR(ubi_class)) { ++ err = PTR_ERR(ubi_class); ++ ubi_err("cannot create UBI class"); ++ goto out; ++ } + + err = class_create_file(ubi_class, &ubi_version); +- if (err) ++ if (err) { ++ ubi_err("cannot create sysfs file"); + goto out_class; ++ } ++ ++ err = misc_register(&ubi_ctrl_cdev); ++ if (err) { ++ ubi_err("cannot register device"); ++ goto out_version; ++ } ++ ++ ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab", ++ sizeof(struct ubi_wl_entry), ++ 0, 0, NULL); ++ if (!ubi_wl_entry_slab) ++ goto out_dev_unreg; + + /* Attach MTD devices */ + for (i = 0; i < mtd_devs; i++) { + struct mtd_dev_param *p = &mtd_dev_param[i]; ++ struct mtd_info *mtd; + + cond_resched(); +- err = attach_mtd_dev(p->name, p->vid_hdr_offs, p->data_offs); +- if (err) ++ ++ mtd = open_mtd_device(p->name); ++ if (IS_ERR(mtd)) { ++ err = PTR_ERR(mtd); ++ goto out_detach; ++ } ++ ++ mutex_lock(&ubi_devices_mutex); ++ err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, ++ p->vid_hdr_offs); ++ mutex_unlock(&ubi_devices_mutex); ++ if (err < 0) { ++ put_mtd_device(mtd); ++ ubi_err("cannot attach mtd%d", mtd->index); + goto out_detach; ++ } + } + + return 0; + + out_detach: + for (k = 0; k < i; k++) +- detach_mtd_dev(ubi_devices[k]); ++ if (ubi_devices[k]) { ++ mutex_lock(&ubi_devices_mutex); ++ ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1); ++ mutex_unlock(&ubi_devices_mutex); ++ } ++ kmem_cache_destroy(ubi_wl_entry_slab); ++out_dev_unreg: ++ misc_deregister(&ubi_ctrl_cdev); ++out_version: + class_remove_file(ubi_class, &ubi_version); + out_class: + class_destroy(ubi_class); ++out: ++ ubi_err("UBI error: cannot initialize UBI, error %d", err); + return err; + } + module_init(ubi_init); + + static void __exit ubi_exit(void) + { +- int i, n = ubi_devices_cnt; ++ int i; + +- for (i = 0; i < n; i++) +- detach_mtd_dev(ubi_devices[i]); ++ for (i = 0; i < UBI_MAX_DEVICES; i++) ++ if (ubi_devices[i]) { ++ mutex_lock(&ubi_devices_mutex); ++ ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1); ++ mutex_unlock(&ubi_devices_mutex); ++ } ++ kmem_cache_destroy(ubi_wl_entry_slab); ++ misc_deregister(&ubi_ctrl_cdev); + class_remove_file(ubi_class, &ubi_version); + class_destroy(ubi_class); + } +@@ -754,7 +1225,8 @@ + + result = simple_strtoul(str, &endp, 0); + if (str == endp || result < 0) { +- printk("UBI error: incorrect bytes count: \"%s\"\n", str); ++ printk(KERN_ERR "UBI error: incorrect bytes count: \"%s\"\n", ++ str); + return -EINVAL; + } + +@@ -764,15 +1236,14 @@ + case 'M': + result *= 1024; + case 'K': +- case 'k': + result *= 1024; +- if (endp[1] == 'i' && (endp[2] == '\0' || +- endp[2] == 'B' || endp[2] == 'b')) ++ if (endp[1] == 'i' && endp[2] == 'B') + endp += 2; + case '\0': + break; + default: +- printk("UBI error: incorrect bytes count: \"%s\"\n", str); ++ printk(KERN_ERR "UBI error: incorrect bytes count: \"%s\"\n", ++ str); + return -EINVAL; + } + +@@ -793,23 +1264,27 @@ + struct mtd_dev_param *p; + char buf[MTD_PARAM_LEN_MAX]; + char *pbuf = &buf[0]; +- char *tokens[3] = {NULL, NULL, NULL}; ++ char *tokens[2] = {NULL, NULL}; ++ ++ if (!val) ++ return -EINVAL; + + if (mtd_devs == UBI_MAX_DEVICES) { +- printk("UBI error: too many parameters, max. is %d\n", ++ printk(KERN_ERR "UBI error: too many parameters, max. is %d\n", + UBI_MAX_DEVICES); + return -EINVAL; + } + + len = strnlen(val, MTD_PARAM_LEN_MAX); + if (len == MTD_PARAM_LEN_MAX) { +- printk("UBI error: parameter \"%s\" is too long, max. is %d\n", +- val, MTD_PARAM_LEN_MAX); ++ printk(KERN_ERR "UBI error: parameter \"%s\" is too long, " ++ "max. is %d\n", val, MTD_PARAM_LEN_MAX); + return -EINVAL; + } + + if (len == 0) { +- printk("UBI warning: empty 'mtd=' parameter - ignored\n"); ++ printk(KERN_WARNING "UBI warning: empty 'mtd=' parameter - " ++ "ignored\n"); + return 0; + } + +@@ -819,11 +1294,12 @@ + if (buf[len - 1] == '\n') + buf[len - 1] = '\0'; + +- for (i = 0; i < 3; i++) ++ for (i = 0; i < 2; i++) + tokens[i] = strsep(&pbuf, ","); + + if (pbuf) { +- printk("UBI error: too many arguments at \"%s\"\n", val); ++ printk(KERN_ERR "UBI error: too many arguments at \"%s\"\n", ++ val); + return -EINVAL; + } + +@@ -832,13 +1308,9 @@ + + if (tokens[1]) + p->vid_hdr_offs = bytes_str_to_int(tokens[1]); +- if (tokens[2]) +- p->data_offs = bytes_str_to_int(tokens[2]); + + if (p->vid_hdr_offs < 0) + return p->vid_hdr_offs; +- if (p->data_offs < 0) +- return p->data_offs; + + mtd_devs += 1; + return 0; +@@ -846,16 +1318,15 @@ + + module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000); + MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: " +- "mtd=[,,]. " ++ "mtd=[,].\n" + "Multiple \"mtd\" parameters may be specified.\n" +- "MTD devices may be specified by their number or name. " +- "Optional \"vid_hdr_offs\" and \"data_offs\" parameters " +- "specify UBI VID header position and data starting " +- "position to be used by UBI.\n" +- "Example: mtd=content,1984,2048 mtd=4 - attach MTD device" +- "with name content using VID header offset 1984 and data " +- "start 2048, and MTD device number 4 using default " +- "offsets"); ++ "MTD devices may be specified by their number or name.\n" ++ "Optional \"vid_hdr_offs\" parameter specifies UBI VID " ++ "header position and data starting position to be used " ++ "by UBI.\n" ++ "Example: mtd=content,1984 mtd=4 - attach MTD device" ++ "with name \"content\" using VID header offset 1984, and " ++ "MTD device number 4 with default VID header offset."); + + MODULE_VERSION(__stringify(UBI_VERSION)); + MODULE_DESCRIPTION("UBI - Unsorted Block Images"); +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/cdev.c linux-2.6.24.7/drivers/mtd/ubi/cdev.c +--- linux-2.6.24.7.old/drivers/mtd/ubi/cdev.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/cdev.c 2009-04-12 18:13:57.000000000 +0200 +@@ -28,6 +28,11 @@ + * + * Major and minor numbers are assigned dynamically to both UBI and volume + * character devices. ++ * ++ * Well, there is the third kind of character devices - the UBI control ++ * character device, which allows to manipulate by UBI devices - create and ++ * delete them. In other words, it is used for attaching and detaching MTD ++ * devices. + */ + + #include +@@ -39,34 +44,6 @@ + #include + #include "ubi.h" + +-/* +- * Maximum sequence numbers of UBI and volume character device IOCTLs (direct +- * logical eraseblock erase is a debug-only feature). +- */ +-#define UBI_CDEV_IOC_MAX_SEQ 2 +-#ifndef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO +-#define VOL_CDEV_IOC_MAX_SEQ 1 +-#else +-#define VOL_CDEV_IOC_MAX_SEQ 2 +-#endif +- +-/** +- * major_to_device - get UBI device object by character device major number. +- * @major: major number +- * +- * This function returns a pointer to the UBI device object. +- */ +-static struct ubi_device *major_to_device(int major) +-{ +- int i; +- +- for (i = 0; i < ubi_devices_cnt; i++) +- if (ubi_devices[i] && ubi_devices[i]->major == major) +- return ubi_devices[i]; +- BUG(); +- return NULL; +-} +- + /** + * get_exclusive - get exclusive access to an UBI volume. + * @desc: volume descriptor +@@ -124,9 +101,11 @@ + static int vol_cdev_open(struct inode *inode, struct file *file) + { + struct ubi_volume_desc *desc; +- const struct ubi_device *ubi = major_to_device(imajor(inode)); +- int vol_id = iminor(inode) - 1; +- int mode; ++ int vol_id = iminor(inode) - 1, mode, ubi_num; ++ ++ ubi_num = ubi_major2num(imajor(inode)); ++ if (ubi_num < 0) ++ return ubi_num; + + if (file->f_mode & FMODE_WRITE) + mode = UBI_READWRITE; +@@ -135,7 +114,7 @@ + + dbg_msg("open volume %d, mode %d", vol_id, mode); + +- desc = ubi_open_volume(ubi->ubi_num, vol_id, mode); ++ desc = ubi_open_volume(ubi_num, vol_id, mode); + if (IS_ERR(desc)) + return PTR_ERR(desc); + +@@ -153,8 +132,23 @@ + if (vol->updating) { + ubi_warn("update of volume %d not finished, volume is damaged", + vol->vol_id); ++ ubi_assert(!vol->changing_leb); + vol->updating = 0; ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(vol->upd_buf); ++#else + vfree(vol->upd_buf); ++#endif ++ } else if (vol->changing_leb) { ++ dbg_msg("only %lld of %lld bytes received for atomic LEB change" ++ " for volume %d:%d, cancel", vol->upd_received, ++ vol->upd_bytes, vol->ubi->ubi_num, vol->vol_id); ++ vol->changing_leb = 0; ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(vol->upd_buf); ++#else ++ vfree(vol->upd_buf); ++#endif + } + + ubi_close_volume(desc); +@@ -205,13 +199,13 @@ + struct ubi_volume_desc *desc = file->private_data; + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; +- int err, lnum, off, len, vol_id = desc->vol->vol_id, tbuf_size; ++ int err, lnum, off, len, tbuf_size; + size_t count_save = count; + void *tbuf; + uint64_t tmp; + + dbg_msg("read %zd bytes from offset %lld of volume %d", +- count, *offp, vol_id); ++ count, *offp, vol->vol_id); + + if (vol->updating) { + dbg_err("updating"); +@@ -225,7 +219,7 @@ + return 0; + + if (vol->corrupted) +- dbg_msg("read from corrupted volume %d", vol_id); ++ dbg_msg("read from corrupted volume %d", vol->vol_id); + + if (*offp + count > vol->used_bytes) + count_save = count = vol->used_bytes - *offp; +@@ -233,7 +227,11 @@ + tbuf_size = vol->usable_leb_size; + if (count < tbuf_size) + tbuf_size = ALIGN(count, ubi->min_io_size); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ tbuf = kmalloc(tbuf_size, GFP_KERNEL); ++#else + tbuf = vmalloc(tbuf_size); ++#endif + if (!tbuf) + return -ENOMEM; + +@@ -249,7 +247,7 @@ + if (off + len >= vol->usable_leb_size) + len = vol->usable_leb_size - off; + +- err = ubi_eba_read_leb(ubi, vol_id, lnum, tbuf, off, len, 0); ++ err = ubi_eba_read_leb(ubi, vol, lnum, tbuf, off, len, 0); + if (err) + break; + +@@ -272,7 +270,11 @@ + len = count > tbuf_size ? tbuf_size : count; + } while (count); + ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(tbuf); ++#else + vfree(tbuf); ++#endif + return err ? err : count_save - count; + } + +@@ -289,13 +291,13 @@ + struct ubi_volume_desc *desc = file->private_data; + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; +- int lnum, off, len, tbuf_size, vol_id = vol->vol_id, err = 0; ++ int lnum, off, len, tbuf_size, err = 0; + size_t count_save = count; + char *tbuf; + uint64_t tmp; + + dbg_msg("requested: write %zd bytes to offset %lld of volume %u", +- count, *offp, desc->vol->vol_id); ++ count, *offp, vol->vol_id); + + if (vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; +@@ -321,7 +323,12 @@ + tbuf_size = vol->usable_leb_size; + if (count < tbuf_size) + tbuf_size = ALIGN(count, ubi->min_io_size); ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ tbuf = kmalloc(tbuf_size, GFP_KERNEL); ++#else + tbuf = vmalloc(tbuf_size); ++#endif + if (!tbuf) + return -ENOMEM; + +@@ -339,7 +346,7 @@ + break; + } + +- err = ubi_eba_write_leb(ubi, vol_id, lnum, tbuf, off, len, ++ err = ubi_eba_write_leb(ubi, vol, lnum, tbuf, off, len, + UBI_UNKNOWN); + if (err) + break; +@@ -356,7 +363,11 @@ + len = count > tbuf_size ? tbuf_size : count; + } + ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(tbuf); ++#else + vfree(tbuf); ++#endif + return err ? err : count_save - count; + } + +@@ -372,22 +383,32 @@ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + +- if (!vol->updating) ++ if (!vol->updating && !vol->changing_leb) + return vol_cdev_direct_write(file, buf, count, offp); + +- err = ubi_more_update_data(ubi, vol->vol_id, buf, count); ++ if (vol->updating) ++ err = ubi_more_update_data(ubi, vol, buf, count); ++ else ++ err = ubi_more_leb_change_data(ubi, vol, buf, count); ++ + if (err < 0) { +- ubi_err("cannot write %zd bytes of update data", count); ++ ubi_err("cannot accept more %zd bytes of data, error %d", ++ count, err); + return err; + } + + if (err) { + /* +- * Update is finished, @err contains number of actually written +- * bytes now. ++ * The operation is finished, @err contains number of actually ++ * written bytes. + */ + count = err; + ++ if (vol->changing_leb) { ++ revoke_exclusive(desc, UBI_READWRITE); ++ return count; ++ } ++ + err = ubi_check_volume(ubi, vol->vol_id); + if (err < 0) + return err; +@@ -402,7 +423,6 @@ + revoke_exclusive(desc, UBI_READWRITE); + } + +- *offp += count; + return count; + } + +@@ -416,6 +436,47 @@ + void __user *argp = (void __user *)arg; + + switch (cmd) { ++ /* LEB read command */ ++ case UBI_IOCLEBREAD: ++ { ++ struct ubi_leb leb; ++ int pnum; ++ char *lebbuf; ++ ++ if (copy_from_user(&leb, argp, sizeof(struct ubi_leb))){ ++ err = -EFAULT; ++ break; ++ } ++ ++ pnum = vol->eba_tbl[leb.lnum]; ++ if (pnum < 0) { ++ //the LEB is clean, no need dump ++ err = 1; ++ break; ++ } ++ ++ lebbuf = kmalloc(vol->ubi->leb_size, GFP_KERNEL); ++ if (!lebbuf){ ++ err = -ENOMEM; ++ break; ++ } ++ ++ err= ubi_eba_read_leb(ubi, vol, leb.lnum, lebbuf, 0, vol->ubi->leb_size, 0); ++ if (err){ ++ kfree(lebbuf); ++ break; ++ } ++ ++ err = copy_to_user(leb.buf, lebbuf, vol->ubi->leb_size); ++ if (err) { ++ kfree(lebbuf); ++ err = -EFAULT; ++ break; ++ } ++ kfree(lebbuf); ++ break; ++ } ++ + /* Volume update command */ + case UBI_IOCVOLUP: + { +@@ -447,11 +508,46 @@ + if (err < 0) + break; + +- err = ubi_start_update(ubi, vol->vol_id, bytes); ++ err = ubi_start_update(ubi, vol, bytes); + if (bytes == 0) + revoke_exclusive(desc, UBI_READWRITE); ++ break; ++ } ++ ++ /* Atomic logical eraseblock change command */ ++ case UBI_IOCEBCH: ++ { ++ struct ubi_leb_change_req req; ++ ++ err = copy_from_user(&req, argp, ++ sizeof(struct ubi_leb_change_req)); ++ if (err) { ++ err = -EFAULT; ++ break; ++ } ++ ++ if (desc->mode == UBI_READONLY || ++ vol->vol_type == UBI_STATIC_VOLUME) { ++ err = -EROFS; ++ break; ++ } ++ ++ /* Validate the request */ ++ err = -EINVAL; ++ if (req.lnum < 0 || req.lnum >= vol->reserved_pebs || ++ req.bytes < 0 || req.lnum >= vol->usable_leb_size) ++ break; ++ if (req.dtype != UBI_LONGTERM && req.dtype != UBI_SHORTTERM && ++ req.dtype != UBI_UNKNOWN) ++ break; + +- file->f_pos = 0; ++ err = get_exclusive(desc); ++ if (err < 0) ++ break; ++ ++ err = ubi_start_leb_change(ubi, vol, &req); ++ if (req.bytes == 0) ++ revoke_exclusive(desc, UBI_READWRITE); + break; + } + +@@ -467,7 +563,8 @@ + break; + } + +- if (desc->mode == UBI_READONLY) { ++ if (desc->mode == UBI_READONLY || ++ vol->vol_type == UBI_STATIC_VOLUME) { + err = -EROFS; + break; + } +@@ -477,13 +574,8 @@ + break; + } + +- if (vol->vol_type != UBI_DYNAMIC_VOLUME) { +- err = -EROFS; +- break; +- } +- + dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); +- err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum); ++ err = ubi_eba_unmap_leb(ubi, vol, lnum); + if (err) + break; + +@@ -580,9 +672,9 @@ + if (!capable(CAP_SYS_RESOURCE)) + return -EPERM; + +- ubi = major_to_device(imajor(inode)); +- if (IS_ERR(ubi)) +- return PTR_ERR(ubi); ++ ubi = ubi_get_by_major(imajor(inode)); ++ if (!ubi) ++ return -ENODEV; + + switch (cmd) { + /* Create volume command */ +@@ -591,8 +683,7 @@ + struct ubi_mkvol_req req; + + dbg_msg("create volume"); +- err = copy_from_user(&req, argp, +- sizeof(struct ubi_mkvol_req)); ++ err = copy_from_user(&req, argp, sizeof(struct ubi_mkvol_req)); + if (err) { + err = -EFAULT; + break; +@@ -604,7 +695,9 @@ + + req.name[req.name_len] = '\0'; + ++ mutex_lock(&ubi->volumes_mutex); + err = ubi_create_volume(ubi, &req); ++ mutex_unlock(&ubi->volumes_mutex); + if (err) + break; + +@@ -633,10 +726,16 @@ + break; + } + ++ mutex_lock(&ubi->volumes_mutex); + err = ubi_remove_volume(desc); +- if (err) +- ubi_close_volume(desc); ++ mutex_unlock(&ubi->volumes_mutex); + ++ /* ++ * The volume is deleted (unless an error occurred), and the ++ * 'struct ubi_volume' object will be freed when ++ * 'ubi_close_volume()' will call 'put_device()'. ++ */ ++ ubi_close_volume(desc); + break; + } + +@@ -648,8 +747,7 @@ + struct ubi_rsvol_req req; + + dbg_msg("re-size volume"); +- err = copy_from_user(&req, argp, +- sizeof(struct ubi_rsvol_req)); ++ err = copy_from_user(&req, argp, sizeof(struct ubi_rsvol_req)); + if (err) { + err = -EFAULT; + break; +@@ -669,7 +767,9 @@ + pebs = !!do_div(tmp, desc->vol->usable_leb_size); + pebs += tmp; + ++ mutex_lock(&ubi->volumes_mutex); + err = ubi_resize_volume(desc, pebs); ++ mutex_unlock(&ubi->volumes_mutex); + ubi_close_volume(desc); + break; + } +@@ -679,9 +779,93 @@ + break; + } + ++ ubi_put_device(ubi); ++ return err; ++} ++ ++static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ int err = 0; ++ void __user *argp = (void __user *)arg; ++ ++ if (!capable(CAP_SYS_RESOURCE)) ++ return -EPERM; ++ ++ switch (cmd) { ++ /* Attach an MTD device command */ ++ case UBI_IOCATT: ++ { ++ struct ubi_attach_req req; ++ struct mtd_info *mtd; ++ ++ dbg_msg("attach MTD device"); ++ err = copy_from_user(&req, argp, sizeof(struct ubi_attach_req)); ++ if (err) { ++ err = -EFAULT; ++ break; ++ } ++ ++ if (req.mtd_num < 0 || ++ (req.ubi_num < 0 && req.ubi_num != UBI_DEV_NUM_AUTO)) { ++ err = -EINVAL; ++ break; ++ } ++ ++ mtd = get_mtd_device(NULL, req.mtd_num); ++ if (IS_ERR(mtd)) { ++ err = PTR_ERR(mtd); ++ break; ++ } ++ ++ /* ++ * Note, further request verification is done by ++ * 'ubi_attach_mtd_dev()'. ++ */ ++ mutex_lock(&ubi_devices_mutex); ++ err = ubi_attach_mtd_dev(mtd, req.ubi_num, req.vid_hdr_offset); ++ mutex_unlock(&ubi_devices_mutex); ++ if (err < 0) ++ put_mtd_device(mtd); ++ else ++ /* @err contains UBI device number */ ++ err = put_user(err, (__user int32_t *)argp); ++ ++ break; ++ } ++ ++ /* Detach an MTD device command */ ++ case UBI_IOCDET: ++ { ++ int ubi_num; ++ ++ dbg_msg("dettach MTD device"); ++ err = get_user(ubi_num, (__user int32_t *)argp); ++ if (err) { ++ err = -EFAULT; ++ break; ++ } ++ ++ mutex_lock(&ubi_devices_mutex); ++ err = ubi_detach_mtd_dev(ubi_num, 0); ++ mutex_unlock(&ubi_devices_mutex); ++ break; ++ } ++ ++ default: ++ err = -ENOTTY; ++ break; ++ } ++ + return err; + } + ++/* UBI control character device operations */ ++struct file_operations ubi_ctrl_cdev_operations = { ++ .ioctl = ctrl_cdev_ioctl, ++ .owner = THIS_MODULE, ++}; ++ + /* UBI character device operations */ + struct file_operations ubi_cdev_operations = { + .owner = THIS_MODULE, +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/debug.h linux-2.6.24.7/drivers/mtd/ubi/debug.h +--- linux-2.6.24.7.old/drivers/mtd/ubi/debug.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/debug.h 2009-04-12 18:13:57.000000000 +0200 +@@ -39,8 +39,9 @@ + + #ifdef CONFIG_MTD_UBI_DEBUG_MSG + /* Generic debugging message */ +-#define dbg_msg(fmt, ...) \ +- printk(KERN_DEBUG "UBI DBG: %s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__) ++#define dbg_msg(fmt, ...) \ ++ printk(KERN_DEBUG "UBI DBG (pid %d): %s: " fmt "\n", \ ++ current->pid, __FUNCTION__, ##__VA_ARGS__) + + #define ubi_dbg_dump_stack() dump_stack() + +@@ -76,38 +77,32 @@ + + #ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA + /* Messages from the eraseblock association unit */ +-#define dbg_eba(fmt, ...) \ +- printk(KERN_DEBUG "UBI DBG eba: %s: " fmt "\n", __FUNCTION__, \ +- ##__VA_ARGS__) ++#define dbg_eba(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) + #else + #define dbg_eba(fmt, ...) ({}) + #endif + + #ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL + /* Messages from the wear-leveling unit */ +-#define dbg_wl(fmt, ...) \ +- printk(KERN_DEBUG "UBI DBG wl: %s: " fmt "\n", __FUNCTION__, \ +- ##__VA_ARGS__) ++#define dbg_wl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) + #else + #define dbg_wl(fmt, ...) ({}) + #endif + + #ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO + /* Messages from the input/output unit */ +-#define dbg_io(fmt, ...) \ +- printk(KERN_DEBUG "UBI DBG io: %s: " fmt "\n", __FUNCTION__, \ +- ##__VA_ARGS__) ++#define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) + #else + #define dbg_io(fmt, ...) ({}) + #endif + + #ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD + /* Initialization and build messages */ +-#define dbg_bld(fmt, ...) \ +- printk(KERN_DEBUG "UBI DBG bld: %s: " fmt "\n", __FUNCTION__, \ +- ##__VA_ARGS__) ++#define dbg_bld(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) ++#define UBI_IO_DEBUG 1 + #else + #define dbg_bld(fmt, ...) ({}) ++#define UBI_IO_DEBUG 0 + #endif + + #ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/eba.c linux-2.6.24.7/drivers/mtd/ubi/eba.c +--- linux-2.6.24.7.old/drivers/mtd/ubi/eba.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/eba.c 2009-04-12 18:13:57.000000000 +0200 +@@ -31,7 +31,7 @@ + * logical eraseblock it is locked for reading or writing. The per-logical + * eraseblock locking is implemented by means of the lock tree. The lock tree + * is an RB-tree which refers all the currently locked logical eraseblocks. The +- * lock tree elements are &struct ltree_entry objects. They are indexed by ++ * lock tree elements are &struct ubi_ltree_entry objects. They are indexed by + * (@vol_id, @lnum) pairs. + * + * EBA also maintains the global sequence counter which is incremented each +@@ -45,34 +45,11 @@ + #include + #include + #include "ubi.h" +- ++#include "ubiblk.h" + /* Number of physical eraseblocks reserved for atomic LEB change operation */ + #define EBA_RESERVED_PEBS 1 + + /** +- * struct ltree_entry - an entry in the lock tree. +- * @rb: links RB-tree nodes +- * @vol_id: volume ID of the locked logical eraseblock +- * @lnum: locked logical eraseblock number +- * @users: how many tasks are using this logical eraseblock or wait for it +- * @mutex: read/write mutex to implement read/write access serialization to +- * the (@vol_id, @lnum) logical eraseblock +- * +- * When a logical eraseblock is being locked - corresponding &struct ltree_entry +- * object is inserted to the lock tree (@ubi->ltree). +- */ +-struct ltree_entry { +- struct rb_node rb; +- int vol_id; +- int lnum; +- int users; +- struct rw_semaphore mutex; +-}; +- +-/* Slab cache for lock-tree entries */ +-static struct kmem_cache *ltree_slab; +- +-/** + * next_sqnum - get next sequence number. + * @ubi: UBI device description object + * +@@ -101,7 +78,7 @@ + */ + static int ubi_get_compat(const struct ubi_device *ubi, int vol_id) + { +- if (vol_id == UBI_LAYOUT_VOL_ID) ++ if (vol_id == UBI_LAYOUT_VOLUME_ID) + return UBI_LAYOUT_VOLUME_COMPAT; + return 0; + } +@@ -112,20 +89,20 @@ + * @vol_id: volume ID + * @lnum: logical eraseblock number + * +- * This function returns a pointer to the corresponding &struct ltree_entry ++ * This function returns a pointer to the corresponding &struct ubi_ltree_entry + * object if the logical eraseblock is locked and %NULL if it is not. + * @ubi->ltree_lock has to be locked. + */ +-static struct ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id, +- int lnum) ++static struct ubi_ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id, ++ int lnum) + { + struct rb_node *p; + + p = ubi->ltree.rb_node; + while (p) { +- struct ltree_entry *le; ++ struct ubi_ltree_entry *le; + +- le = rb_entry(p, struct ltree_entry, rb); ++ le = rb_entry(p, struct ubi_ltree_entry, rb); + + if (vol_id < le->vol_id) + p = p->rb_left; +@@ -155,15 +132,17 @@ + * Returns pointer to the lock tree entry or %-ENOMEM if memory allocation + * failed. + */ +-static struct ltree_entry *ltree_add_entry(struct ubi_device *ubi, int vol_id, +- int lnum) ++static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi, ++ int vol_id, int lnum) + { +- struct ltree_entry *le, *le1, *le_free; ++ struct ubi_ltree_entry *le, *le1, *le_free; + +- le = kmem_cache_alloc(ltree_slab, GFP_NOFS); ++ le = kmalloc(sizeof(struct ubi_ltree_entry), GFP_NOFS); + if (!le) + return ERR_PTR(-ENOMEM); + ++ le->users = 0; ++ init_rwsem(&le->mutex); + le->vol_id = vol_id; + le->lnum = lnum; + +@@ -189,7 +168,7 @@ + p = &ubi->ltree.rb_node; + while (*p) { + parent = *p; +- le1 = rb_entry(parent, struct ltree_entry, rb); ++ le1 = rb_entry(parent, struct ubi_ltree_entry, rb); + + if (vol_id < le1->vol_id) + p = &(*p)->rb_left; +@@ -211,7 +190,7 @@ + spin_unlock(&ubi->ltree_lock); + + if (le_free) +- kmem_cache_free(ltree_slab, le_free); ++ kfree(le_free); + + return le; + } +@@ -227,7 +206,7 @@ + */ + static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum) + { +- struct ltree_entry *le; ++ struct ubi_ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (IS_ERR(le)) +@@ -245,7 +224,7 @@ + static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum) + { + int free = 0; +- struct ltree_entry *le; ++ struct ubi_ltree_entry *le; + + spin_lock(&ubi->ltree_lock); + le = ltree_lookup(ubi, vol_id, lnum); +@@ -259,7 +238,7 @@ + + up_read(&le->mutex); + if (free) +- kmem_cache_free(ltree_slab, le); ++ kfree(le); + } + + /** +@@ -273,7 +252,7 @@ + */ + static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum) + { +- struct ltree_entry *le; ++ struct ubi_ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (IS_ERR(le)) +@@ -283,6 +262,44 @@ + } + + /** ++ * leb_write_lock - lock logical eraseblock for writing. ++ * @ubi: UBI device description object ++ * @vol_id: volume ID ++ * @lnum: logical eraseblock number ++ * ++ * This function locks a logical eraseblock for writing if there is no ++ * contention and does nothing if there is contention. Returns %0 in case of ++ * success, %1 in case of contention, and and a negative error code in case of ++ * failure. ++ */ ++static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum) ++{ ++ int free; ++ struct ubi_ltree_entry *le; ++ ++ le = ltree_add_entry(ubi, vol_id, lnum); ++ if (IS_ERR(le)) ++ return PTR_ERR(le); ++ if (down_write_trylock(&le->mutex)) ++ return 0; ++ ++ /* Contention, cancel */ ++ spin_lock(&ubi->ltree_lock); ++ le->users -= 1; ++ ubi_assert(le->users >= 0); ++ if (le->users == 0) { ++ rb_erase(&le->rb, &ubi->ltree); ++ free = 1; ++ } else ++ free = 0; ++ spin_unlock(&ubi->ltree_lock); ++ if (free) ++ kfree(le); ++ ++ return 1; ++} ++ ++/** + * leb_write_unlock - unlock logical eraseblock. + * @ubi: UBI device description object + * @vol_id: volume ID +@@ -291,7 +308,7 @@ + static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum) + { + int free; +- struct ltree_entry *le; ++ struct ubi_ltree_entry *le; + + spin_lock(&ubi->ltree_lock); + le = ltree_lookup(ubi, vol_id, lnum); +@@ -306,23 +323,23 @@ + + up_write(&le->mutex); + if (free) +- kmem_cache_free(ltree_slab, le); ++ kfree(le); + } + + /** + * ubi_eba_unmap_leb - un-map logical eraseblock. + * @ubi: UBI device description object +- * @vol_id: volume ID ++ * @vol: volume description object + * @lnum: logical eraseblock number + * + * This function un-maps logical eraseblock @lnum and schedules corresponding + * physical eraseblock for erasure. Returns zero in case of success and a + * negative error code in case of failure. + */ +-int ubi_eba_unmap_leb(struct ubi_device *ubi, int vol_id, int lnum) ++int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, ++ int lnum) + { +- int idx = vol_id2idx(ubi, vol_id), err, pnum; +- struct ubi_volume *vol = ubi->volumes[idx]; ++ int err, pnum, vol_id = vol->vol_id; + + if (ubi->ro_mode) + return -EROFS; +@@ -349,7 +366,7 @@ + /** + * ubi_eba_read_leb - read data. + * @ubi: UBI device description object +- * @vol_id: volume ID ++ * @vol: volume description object + * @lnum: logical eraseblock number + * @buf: buffer to store the read data + * @offset: offset from where to read +@@ -365,12 +382,11 @@ + * returned for any volume type if an ECC error was detected by the MTD device + * driver. Other negative error cored may be returned in case of other errors. + */ +-int ubi_eba_read_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, +- int offset, int len, int check) ++int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, ++ void *buf, int offset, int len, int check) + { +- int err, pnum, scrub = 0, idx = vol_id2idx(ubi, vol_id); ++ int err, pnum, scrub = 0, vol_id = vol->vol_id; + struct ubi_vid_hdr *vid_hdr; +- struct ubi_volume *vol = ubi->volumes[idx]; + uint32_t uninitialized_var(crc); + + err = leb_read_lock(ubi, vol_id, lnum); +@@ -578,7 +594,7 @@ + /** + * ubi_eba_write_leb - write data to dynamic volume. + * @ubi: UBI device description object +- * @vol_id: volume ID ++ * @vol: volume description object + * @lnum: logical eraseblock number + * @buf: the data to write + * @offset: offset within the logical eraseblock where to write +@@ -586,15 +602,14 @@ + * @dtype: data type + * + * This function writes data to logical eraseblock @lnum of a dynamic volume +- * @vol_id. Returns zero in case of success and a negative error code in case ++ * @vol. Returns zero in case of success and a negative error code in case + * of failure. In case of error, it is possible that something was still + * written to the flash media, but may be some garbage. + */ +-int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum, ++int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + const void *buf, int offset, int len, int dtype) + { +- int idx = vol_id2idx(ubi, vol_id), err, pnum, tries = 0; +- struct ubi_volume *vol = ubi->volumes[idx]; ++ int err, pnum, tries = 0, vol_id = vol->vol_id; + struct ubi_vid_hdr *vid_hdr; + + if (ubi->ro_mode) +@@ -613,7 +628,8 @@ + if (err) { + ubi_warn("failed to write data to PEB %d", pnum); + if (err == -EIO && ubi->bad_allowed) +- err = recover_peb(ubi, pnum, vol_id, lnum, buf, offset, len); ++ err = recover_peb(ubi, pnum, vol_id, lnum, buf, ++ offset, len); + if (err) + ubi_ro_mode(ubi); + } +@@ -656,11 +672,14 @@ + goto write_error; + } + +- err = ubi_io_write_data(ubi, buf, pnum, offset, len); +- if (err) { +- ubi_warn("failed to write %d bytes at offset %d of LEB %d:%d, " +- "PEB %d", len, offset, vol_id, lnum, pnum); +- goto write_error; ++ if (len) { ++ err = ubi_io_write_data(ubi, buf, pnum, offset, len); ++ if (err) { ++ ubi_warn("failed to write %d bytes at offset %d of " ++ "LEB %d:%d, PEB %d", len, offset, vol_id, ++ lnum, pnum); ++ goto write_error; ++ } + } + + vol->eba_tbl[lnum] = pnum; +@@ -698,7 +717,7 @@ + /** + * ubi_eba_write_leb_st - write data to static volume. + * @ubi: UBI device description object +- * @vol_id: volume ID ++ * @vol: volume description object + * @lnum: logical eraseblock number + * @buf: data to write + * @len: how many bytes to write +@@ -706,7 +725,7 @@ + * @used_ebs: how many logical eraseblocks will this volume contain + * + * This function writes data to logical eraseblock @lnum of static volume +- * @vol_id. The @used_ebs argument should contain total number of logical ++ * @vol. The @used_ebs argument should contain total number of logical + * eraseblock in this static volume. + * + * When writing to the last logical eraseblock, the @len argument doesn't have +@@ -718,12 +737,11 @@ + * volumes. This function returns zero in case of success and a negative error + * code in case of failure. + */ +-int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum, +- const void *buf, int len, int dtype, int used_ebs) ++int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, ++ int lnum, const void *buf, int len, int dtype, ++ int used_ebs) + { +- int err, pnum, tries = 0, data_size = len; +- int idx = vol_id2idx(ubi, vol_id); +- struct ubi_volume *vol = ubi->volumes[idx]; ++ int err, pnum, tries = 0, data_size = len, vol_id = vol->vol_id; + struct ubi_vid_hdr *vid_hdr; + uint32_t crc; + +@@ -819,7 +837,7 @@ + /* + * ubi_eba_atomic_leb_change - change logical eraseblock atomically. + * @ubi: UBI device description object +- * @vol_id: volume ID ++ * @vol: volume description object + * @lnum: logical eraseblock number + * @buf: data to write + * @len: how many bytes to write +@@ -834,17 +852,27 @@ + * UBI reserves one LEB for the "atomic LEB change" operation, so only one + * LEB change may be done at a time. This is ensured by @ubi->alc_mutex. + */ +-int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum, +- const void *buf, int len, int dtype) ++int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, ++ int lnum, const void *buf, int len, int dtype) + { +- int err, pnum, tries = 0, idx = vol_id2idx(ubi, vol_id); +- struct ubi_volume *vol = ubi->volumes[idx]; ++ int err, pnum, tries = 0, vol_id = vol->vol_id; + struct ubi_vid_hdr *vid_hdr; + uint32_t crc; + + if (ubi->ro_mode) + return -EROFS; + ++ if (len == 0) { ++ /* ++ * Special case when data length is zero. In this case the LEB ++ * has to be unmapped and mapped somewhere else. ++ */ ++ err = ubi_eba_unmap_leb(ubi, vol, lnum); ++ if (err) ++ return err; ++ return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype); ++ } ++ + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) + return -ENOMEM; +@@ -928,20 +956,6 @@ + } + + /** +- * ltree_entry_ctor - lock tree entries slab cache constructor. +- * @obj: the lock-tree entry to construct +- * @cache: the lock tree entry slab cache +- * @flags: constructor flags +- */ +-static void ltree_entry_ctor(struct kmem_cache *cache, void *obj) +-{ +- struct ltree_entry *le = obj; +- +- le->users = 0; +- init_rwsem(&le->mutex); +-} +- +-/** + * ubi_eba_copy_leb - copy logical eraseblock. + * @ubi: UBI device description object + * @from: physical eraseblock number from where to copy +@@ -950,14 +964,16 @@ + * + * This function copies logical eraseblock from physical eraseblock @from to + * physical eraseblock @to. The @vid_hdr buffer may be changed by this +- * function. Returns zero in case of success, %UBI_IO_BITFLIPS if the operation +- * was canceled because bit-flips were detected at the target PEB, and a +- * negative error code in case of failure. ++ * function. Returns: ++ * o %0 in case of success; ++ * o %1 if the operation was canceled and should be tried later (e.g., ++ * because a bit-flip was detected at the target PEB); ++ * o %2 if the volume is being deleted and this LEB should not be moved. + */ + int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, + struct ubi_vid_hdr *vid_hdr) + { +- int err, vol_id, lnum, data_size, aldata_size, pnum, idx; ++ int err, vol_id, lnum, data_size, aldata_size, idx; + struct ubi_volume *vol; + uint32_t crc; + +@@ -973,51 +989,67 @@ + data_size = aldata_size = + ubi->leb_size - be32_to_cpu(vid_hdr->data_pad); + +- /* +- * We do not want anybody to write to this logical eraseblock while we +- * are moving it, so we lock it. +- */ +- err = leb_write_lock(ubi, vol_id, lnum); +- if (err) +- return err; +- +- mutex_lock(&ubi->buf_mutex); +- +- /* +- * But the logical eraseblock might have been put by this time. +- * Cancel if it is true. +- */ + idx = vol_id2idx(ubi, vol_id); +- ++ spin_lock(&ubi->volumes_lock); + /* +- * We may race with volume deletion/re-size, so we have to hold +- * @ubi->volumes_lock. ++ * Note, we may race with volume deletion, which means that the volume ++ * this logical eraseblock belongs to might be being deleted. Since the ++ * volume deletion unmaps all the volume's logical eraseblocks, it will ++ * be locked in 'ubi_wl_put_peb()' and wait for the WL worker to finish. + */ +- spin_lock(&ubi->volumes_lock); + vol = ubi->volumes[idx]; + if (!vol) { +- dbg_eba("volume %d was removed meanwhile", vol_id); ++ /* No need to do further work, cancel */ ++ dbg_eba("volume %d is being removed, cancel", vol_id); + spin_unlock(&ubi->volumes_lock); +- goto out_unlock; ++ return 2; + } ++ spin_unlock(&ubi->volumes_lock); + +- pnum = vol->eba_tbl[lnum]; +- if (pnum != from) { +- dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to " +- "PEB %d, cancel", vol_id, lnum, from, pnum); +- spin_unlock(&ubi->volumes_lock); +- goto out_unlock; ++ /* ++ * We do not want anybody to write to this logical eraseblock while we ++ * are moving it, so lock it. ++ * ++ * Note, we are using non-waiting locking here, because we cannot sleep ++ * on the LEB, since it may cause deadlocks. Indeed, imagine a task is ++ * unmapping the LEB which is mapped to the PEB we are going to move ++ * (@from). This task locks the LEB and goes sleep in the ++ * 'ubi_wl_put_peb()' function on the @ubi->move_mutex. In turn, we are ++ * holding @ubi->move_mutex and go sleep on the LEB lock. So, if the ++ * LEB is already locked, we just do not move it and return %1. ++ */ ++ err = leb_write_trylock(ubi, vol_id, lnum); ++ if (err) { ++ dbg_eba("contention on LEB %d:%d, cancel", vol_id, lnum); ++ return err; + } +- spin_unlock(&ubi->volumes_lock); + +- /* OK, now the LEB is locked and we can safely start moving it */ ++ /* ++ * The LEB might have been put meanwhile, and the task which put it is ++ * probably waiting on @ubi->move_mutex. No need to continue the work, ++ * cancel it. ++ */ ++ if (vol->eba_tbl[lnum] != from) { ++ dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to " ++ "PEB %d, cancel", vol_id, lnum, from, ++ vol->eba_tbl[lnum]); ++ err = 1; ++ goto out_unlock_leb; ++ } + ++ /* ++ * OK, now the LEB is locked and we can safely start moving iy. Since ++ * this function utilizes thie @ubi->peb1_buf buffer which is shared ++ * with some other functions, so lock the buffer by taking the ++ * @ubi->buf_mutex. ++ */ ++ mutex_lock(&ubi->buf_mutex); + dbg_eba("read %d bytes of data", aldata_size); + err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size); + if (err && err != UBI_IO_BITFLIPS) { + ubi_warn("error %d while reading data from PEB %d", + err, from); +- goto out_unlock; ++ goto out_unlock_buf; + } + + /* +@@ -1053,7 +1085,7 @@ + + err = ubi_io_write_vid_hdr(ubi, to, vid_hdr); + if (err) +- goto out_unlock; ++ goto out_unlock_buf; + + cond_resched(); + +@@ -1062,13 +1094,15 @@ + if (err) { + if (err != UBI_IO_BITFLIPS) + ubi_warn("cannot read VID header back from PEB %d", to); +- goto out_unlock; ++ else ++ err = 1; ++ goto out_unlock_buf; + } + + if (data_size > 0) { + err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size); + if (err) +- goto out_unlock; ++ goto out_unlock_buf; + + cond_resched(); + +@@ -1082,7 +1116,9 @@ + if (err != UBI_IO_BITFLIPS) + ubi_warn("cannot read data back from PEB %d", + to); +- goto out_unlock; ++ else ++ err = 1; ++ goto out_unlock_buf; + } + + cond_resched(); +@@ -1090,15 +1126,16 @@ + if (memcmp(ubi->peb_buf1, ubi->peb_buf2, aldata_size)) { + ubi_warn("read data back from PEB %d - it is different", + to); +- goto out_unlock; ++ goto out_unlock_buf; + } + } + + ubi_assert(vol->eba_tbl[lnum] == from); + vol->eba_tbl[lnum] = to; + +-out_unlock: ++out_unlock_buf: + mutex_unlock(&ubi->buf_mutex); ++out_unlock_leb: + leb_write_unlock(ubi, vol_id, lnum); + return err; + } +@@ -1125,14 +1162,6 @@ + mutex_init(&ubi->alc_mutex); + ubi->ltree = RB_ROOT; + +- if (ubi_devices_cnt == 0) { +- ltree_slab = kmem_cache_create("ubi_ltree_slab", +- sizeof(struct ltree_entry), 0, +- 0, <ree_entry_ctor); +- if (!ltree_slab) +- return -ENOMEM; +- } +- + ubi->global_sqnum = si->max_sqnum + 1; + num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; + +@@ -1168,6 +1197,15 @@ + } + } + ++ if (ubi->avail_pebs < EBA_RESERVED_PEBS) { ++ ubi_err("no enough physical eraseblocks (%d, need %d)", ++ ubi->avail_pebs, EBA_RESERVED_PEBS); ++ err = -ENOSPC; ++ goto out_free; ++ } ++ ubi->avail_pebs -= EBA_RESERVED_PEBS; ++ ubi->rsvd_pebs += EBA_RESERVED_PEBS; ++ + if (ubi->bad_allowed) { + ubi_calculate_reserved(ubi); + +@@ -1184,15 +1222,6 @@ + ubi->rsvd_pebs += ubi->beb_rsvd_pebs; + } + +- if (ubi->avail_pebs < EBA_RESERVED_PEBS) { +- ubi_err("no enough physical eraseblocks (%d, need %d)", +- ubi->avail_pebs, EBA_RESERVED_PEBS); +- err = -ENOSPC; +- goto out_free; +- } +- ubi->avail_pebs -= EBA_RESERVED_PEBS; +- ubi->rsvd_pebs += EBA_RESERVED_PEBS; +- + dbg_eba("EBA unit is initialized"); + return 0; + +@@ -1202,8 +1231,6 @@ + continue; + kfree(ubi->volumes[i]->eba_tbl); + } +- if (ubi_devices_cnt == 0) +- kmem_cache_destroy(ltree_slab); + return err; + } + +@@ -1222,6 +1249,141 @@ + continue; + kfree(ubi->volumes[i]->eba_tbl); + } +- if (ubi_devices_cnt == 1) +- kmem_cache_destroy(ltree_slab); + } ++ ++/* add by Nancy begin */ ++ ++static int ubiblk_fill_writecache(struct ubiblk_dev *ubiblk) ++{ ++ struct ubi_volume_desc *uv = ubiblk->uv; ++ struct ubi_device *ubi = uv->vol->ubi; ++ int ppb = ubi->leb_size / ubi->min_io_size; ++ unsigned short subpage_shift = 9; ++ unsigned short spp = ubi->min_io_size >> subpage_shift; ++ unsigned short page_shift = ffs(ubi->min_io_size) - 1; ++ unsigned short sectors_in_page_shift = ffs(ubi->min_io_size / 512) - 1; ++ unsigned short page, sector; ++ char page_buf[ubi->min_io_size]; ++ ++ if (!page_buf) ++ return -ENOMEM; ++ ++ for (page = 0; page < ppb; page++) { ++ if ( !ubiblk->page_sts[page]) { ++ ubi_leb_read(uv, ubiblk->vbw, ++ &ubiblk->write_cache[page<min_io_size, 0); ++ }else{ ++ for(sector = 0; sector < spp; sector++) ++ if( !ubiblk->subpage_sts[(page<vbw, ++ page_buf, ++ page<min_io_size, 0); ++ for(sector = 0; sector < spp; sector++) ++ if(!ubiblk->subpage_sts[(page<write_cache[ \ ++ (page<vol_id; ++ struct ubi_vid_hdr *vid_hdr; ++ uint32_t crc; ++ ++ if (ubi->ro_mode) ++ return -EROFS; ++ ++ vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); ++ if (!vid_hdr) ++ return -ENOMEM; ++ ++ ubiblk_fill_writecache(ubiblk); ++ mutex_lock(&ubi->alc_mutex); ++ err = leb_write_lock(ubi, vol_id, lnum); ++ if (err) ++ goto out_mutex; ++ ++ vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); ++ vid_hdr->vol_id = cpu_to_be32(vol_id); ++ vid_hdr->lnum = cpu_to_be32(lnum); ++ vid_hdr->compat = ubi_get_compat(ubi, vol_id); ++ vid_hdr->data_pad = cpu_to_be32(vol->data_pad); ++ ++ crc = crc32(UBI_CRC32_INIT, buf, len); ++ vid_hdr->vol_type = UBI_VID_DYNAMIC; ++ vid_hdr->data_size = cpu_to_be32(len); ++ vid_hdr->copy_flag = 1; ++ vid_hdr->data_crc = cpu_to_be32(crc); ++ ++retry: ++ pnum = ubi_wl_get_peb(ubi, dtype); ++ if (pnum < 0) { ++ err = pnum; ++ goto out_leb_unlock; ++ } ++ ++ dbg_eba("change LEB %d:%d, PEB %d, write VID hdr to PEB %d", ++ vol_id, lnum, vol->eba_tbl[lnum], pnum); ++ ++ err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); ++ if (err) { ++ ubi_warn("failed to write VID header to LEB %d:%d, PEB %d", ++ vol_id, lnum, pnum); ++ goto write_error; ++ } ++ ++ err = ubi_io_write_data(ubi, buf, pnum, 0, len); ++ if (err) { ++ ubi_warn("failed to write %d bytes of data to PEB %d", ++ len, pnum); ++ goto write_error; ++ } ++ if (vol->eba_tbl[lnum] >= 0) { ++ err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 0); ++ if (err) ++ goto out_leb_unlock; ++ } ++ ++ vol->eba_tbl[lnum] = pnum; ++ ++out_leb_unlock: ++ leb_write_unlock(ubi, vol_id, lnum); ++out_mutex: ++ mutex_unlock(&ubi->alc_mutex); ++ ubi_free_vid_hdr(ubi, vid_hdr); ++ return err; ++ ++write_error: ++ if (err != -EIO || !ubi->bad_allowed) { ++ /* ++ * This flash device does not admit of bad eraseblocks or ++ * something nasty and unexpected happened. Switch to read-only ++ * mode just in case. ++ */ ++ ubi_ro_mode(ubi); ++ goto out_leb_unlock; ++ } ++ ++ err = ubi_wl_put_peb(ubi, pnum, 1); ++ if (err || ++tries > UBI_IO_RETRIES) { ++ ubi_ro_mode(ubi); ++ goto out_leb_unlock; ++ } ++ ++ vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); ++ ubi_msg("try another PEB"); ++ goto retry; ++} ++ ++/* add by Nancy end*/ ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/gluebi.c linux-2.6.24.7/drivers/mtd/ubi/gluebi.c +--- linux-2.6.24.7.old/drivers/mtd/ubi/gluebi.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/gluebi.c 2009-04-12 18:13:57.000000000 +0200 +@@ -103,15 +103,15 @@ + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +-static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, +- size_t *retlen, unsigned char *buf) ++static int gluebi_read(struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, ++ size_mtd_t *retlen, unsigned char *buf) + { + int err = 0, lnum, offs, total_read; + struct ubi_volume *vol; + struct ubi_device *ubi; + uint64_t tmp = from; + +- dbg_msg("read %zd bytes from offset %lld", len, from); ++ dbg_msg("read %lld bytes from offset %lld", len, from); + + if (len < 0 || from < 0 || from + len > mtd->size) + return -EINVAL; +@@ -129,8 +129,7 @@ + if (to_read > total_read) + to_read = total_read; + +- err = ubi_eba_read_leb(ubi, vol->vol_id, lnum, buf, offs, +- to_read, 0); ++ err = ubi_eba_read_leb(ubi, vol, lnum, buf, offs, to_read, 0); + if (err) + break; + +@@ -155,15 +154,15 @@ + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +-static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, +- size_t *retlen, const u_char *buf) ++static int gluebi_write(struct mtd_info *mtd, loff_mtd_t to, size_mtd_t len, ++ size_mtd_t *retlen, const u_char *buf) + { + int err = 0, lnum, offs, total_written; + struct ubi_volume *vol; + struct ubi_device *ubi; + uint64_t tmp = to; + +- dbg_msg("write %zd bytes to offset %lld", len, to); ++ dbg_msg("write %lld bytes to offset %lld", len, to); + + if (len < 0 || to < 0 || len + to > mtd->size) + return -EINVAL; +@@ -177,7 +176,7 @@ + offs = do_div(tmp, mtd->erasesize); + lnum = tmp; + +- if (len % mtd->writesize || offs % mtd->writesize) ++ if ((u32)len % mtd->writesize || offs % mtd->writesize) + return -EINVAL; + + total_written = len; +@@ -187,8 +186,8 @@ + if (to_write > total_written) + to_write = total_written; + +- err = ubi_eba_write_leb(ubi, vol->vol_id, lnum, buf, offs, +- to_write, UBI_UNKNOWN); ++ err = ubi_eba_write_leb(ubi, vol, lnum, buf, offs, to_write, ++ UBI_UNKNOWN); + if (err) + break; + +@@ -216,7 +215,7 @@ + struct ubi_volume *vol; + struct ubi_device *ubi; + +- dbg_msg("erase %u bytes at offset %u", instr->len, instr->addr); ++ dbg_msg("erase %llu bytes at offset %llu", instr->len, instr->addr); + + if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize) + return -EINVAL; +@@ -224,11 +223,11 @@ + if (instr->len < 0 || instr->addr + instr->len > mtd->size) + return -EINVAL; + +- if (instr->addr % mtd->writesize || instr->len % mtd->writesize) ++ if ((u32)instr->addr % mtd->writesize || (u32)instr->len % mtd->writesize) + return -EINVAL; + +- lnum = instr->addr / mtd->erasesize; +- count = instr->len / mtd->erasesize; ++ lnum = instr->addr >> (ffs(mtd->erasesize)-1); ++ count = instr->len >> (ffs(mtd->erasesize)-1); + + vol = container_of(mtd, struct ubi_volume, gluebi_mtd); + ubi = vol->ubi; +@@ -237,7 +236,7 @@ + return -EROFS; + + for (i = 0; i < count; i++) { +- err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum + i); ++ err = ubi_eba_unmap_leb(ubi, vol, lnum + i); + if (err) + goto out_err; + } +@@ -292,11 +291,12 @@ + /* + * In case of dynamic volume, MTD device size is just volume size. In + * case of a static volume the size is equivalent to the amount of data +- * bytes, which is zero at this moment and will be changed after volume +- * update. ++ * bytes. + */ + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + mtd->size = vol->usable_leb_size * vol->reserved_pebs; ++ else ++ mtd->size = vol->used_bytes; + + if (add_mtd_device(mtd)) { + ubi_err("cannot not add MTD device\n"); +@@ -304,7 +304,7 @@ + return -ENFILE; + } + +- dbg_msg("added mtd%d (\"%s\"), size %u, EB size %u", ++ dbg_msg("added mtd%d (\"%s\"), size %llu, EB size %u", + mtd->index, mtd->name, mtd->size, mtd->erasesize); + return 0; + } +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/io.c linux-2.6.24.7/drivers/mtd/ubi/io.c +--- linux-2.6.24.7.old/drivers/mtd/ubi/io.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/io.c 2009-04-12 18:13:57.000000000 +0200 +@@ -135,8 +135,8 @@ + int len) + { + int err, retries = 0; +- size_t read; +- loff_t addr; ++ size_mtd_t read; ++ loff_mtd_t addr; + + dbg_io("read %d bytes from PEB %d:%d", len, pnum, offset); + +@@ -148,7 +148,7 @@ + if (err) + return err > 0 ? -EINVAL : err; + +- addr = (loff_t)pnum * ubi->peb_size + offset; ++ addr = (loff_mtd_t)pnum * ubi->peb_size + offset; + retry: + err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf); + if (err) { +@@ -164,15 +164,25 @@ + + if (read != len && retries++ < UBI_IO_RETRIES) { + dbg_io("error %d while reading %d bytes from PEB %d:%d, " +- "read only %zd bytes, retry", ++ "read only %lld bytes, retry", + err, len, pnum, offset, read); + yield(); + goto retry; + } + + ubi_err("error %d while reading %d bytes from PEB %d:%d, " +- "read %zd bytes", err, len, pnum, offset, read); ++ "read %lld bytes", err, len, pnum, offset, read); + ubi_dbg_dump_stack(); ++ ++ /* ++ * The driver should never return -EBADMSG if it failed to read ++ * all the requested data. But some buggy drivers might do ++ * this, so we change it to -EIO. ++ */ ++ if (read != len && err == -EBADMSG) { ++ ubi_assert(0); ++ err = -EIO; ++ } + } else { + ubi_assert(len == read); + +@@ -206,8 +216,8 @@ + int len) + { + int err; +- size_t written; +- loff_t addr; ++ size_mtd_t written; ++ loff_mtd_t addr; + + dbg_io("write %d bytes to PEB %d:%d", len, pnum, offset); + +@@ -252,11 +262,11 @@ + return -EIO; + } + +- addr = (loff_t)pnum * ubi->peb_size + offset; ++ addr = (loff_mtd_t)pnum * ubi->peb_size + offset; + err = ubi->mtd->write(ubi->mtd, addr, len, &written, buf); + if (err) { + ubi_err("error %d while writing %d bytes to PEB %d:%d, written" +- " %zd bytes", err, len, pnum, offset, written); ++ " %lld bytes", err, len, pnum, offset, written); + ubi_dbg_dump_stack(); + } else + ubi_assert(written == len); +@@ -298,7 +308,7 @@ + memset(&ei, 0, sizeof(struct erase_info)); + + ei.mtd = ubi->mtd; +- ei.addr = (loff_t)pnum * ubi->peb_size; ++ ei.addr = (loff_mtd_t)pnum * ubi->peb_size; + ei.len = ubi->peb_size; + ei.callback = erase_callback; + ei.priv = (unsigned long)&wq; +@@ -501,7 +511,7 @@ + if (ubi->bad_allowed) { + int ret; + +- ret = mtd->block_isbad(mtd, (loff_t)pnum * ubi->peb_size); ++ ret = mtd->block_isbad(mtd, (loff_mtd_t)pnum * ubi->peb_size); + if (ret < 0) + ubi_err("error %d while checking if PEB %d is bad", + ret, pnum); +@@ -536,7 +546,7 @@ + if (!ubi->bad_allowed) + return 0; + +- err = mtd->block_markbad(mtd, (loff_t)pnum * ubi->peb_size); ++ err = mtd->block_markbad(mtd, (loff_mtd_t)pnum * ubi->peb_size); + if (err) + ubi_err("cannot mark PEB %d bad, error %d", pnum, err); + return err; +@@ -621,6 +631,8 @@ + + dbg_io("read EC header from PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); ++ if (UBI_IO_DEBUG) ++ verbose = 1; + + err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE); + if (err) { +@@ -894,6 +906,8 @@ + + dbg_io("read VID header from PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); ++ if (UBI_IO_DEBUG) ++ verbose = 1; + + p = (char *)vid_hdr - ubi->vid_hdr_shift; + err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, +@@ -1218,15 +1232,15 @@ + static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, + int len) + { +- size_t read; ++ size_mtd_t read; + int err; +- loff_t addr = (loff_t)pnum * ubi->peb_size + offset; ++ loff_mtd_t addr = (loff_mtd_t)pnum * ubi->peb_size + offset; + + mutex_lock(&ubi->dbg_buf_mutex); + err = ubi->mtd->read(ubi->mtd, addr, len, &read, ubi->dbg_peb_buf); + if (err && err != -EUCLEAN) { + ubi_err("error %d while reading %d bytes from PEB %d:%d, " +- "read %zd bytes", err, len, pnum, offset, read); ++ "read %lld bytes", err, len, pnum, offset, read); + goto error; + } + +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/kapi.c linux-2.6.24.7/drivers/mtd/ubi/kapi.c +--- linux-2.6.24.7.old/drivers/mtd/ubi/kapi.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/kapi.c 2009-04-12 18:13:57.000000000 +0200 +@@ -24,29 +24,36 @@ + #include + #include + #include "ubi.h" ++#include "ubiblk.h" + ++extern int ubiblk_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, ++ int lnum, void *buf, int len, int dtype, struct ubiblk_dev *ubiblk); + /** + * ubi_get_device_info - get information about UBI device. + * @ubi_num: UBI device number + * @di: the information is stored here + * +- * This function returns %0 in case of success and a %-ENODEV if there is no +- * such UBI device. ++ * This function returns %0 in case of success, %-EINVAL if the UBI device ++ * number is invalid, and %-ENODEV if there is no such UBI device. + */ + int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) + { +- const struct ubi_device *ubi; ++ struct ubi_device *ubi; ++ ++ if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) ++ return -EINVAL; + +- if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || +- !ubi_devices[ubi_num]) ++ ubi = ubi_get_device(ubi_num); ++ if (!ubi) + return -ENODEV; + +- ubi = ubi_devices[ubi_num]; + di->ubi_num = ubi->ubi_num; + di->leb_size = ubi->leb_size; + di->min_io_size = ubi->min_io_size; + di->ro_mode = ubi->ro_mode; +- di->cdev = MKDEV(ubi->major, 0); ++ di->cdev = ubi->cdev.dev; ++ ++ ubi_put_device(ubi); + return 0; + } + EXPORT_SYMBOL_GPL(ubi_get_device_info); +@@ -73,7 +80,7 @@ + vi->usable_leb_size = vol->usable_leb_size; + vi->name_len = vol->name_len; + vi->name = vol->name; +- vi->cdev = MKDEV(ubi->major, vi->vol_id + 1); ++ vi->cdev = vol->cdev.dev; + } + EXPORT_SYMBOL_GPL(ubi_get_volume_info); + +@@ -104,37 +111,39 @@ + + dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode); + +- err = -ENODEV; +- if (ubi_num < 0) +- return ERR_PTR(err); +- +- ubi = ubi_devices[ubi_num]; +- +- if (!try_module_get(THIS_MODULE)) +- return ERR_PTR(err); +- +- if (ubi_num >= UBI_MAX_DEVICES || !ubi) +- goto out_put; ++ if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) ++ return ERR_PTR(-EINVAL); + +- err = -EINVAL; +- if (vol_id < 0 || vol_id >= ubi->vtbl_slots) +- goto out_put; + if (mode != UBI_READONLY && mode != UBI_READWRITE && + mode != UBI_EXCLUSIVE) +- goto out_put; ++ return ERR_PTR(-EINVAL); ++ ++ /* ++ * First of all, we have to get the UBI device to prevent its removal. ++ */ ++ ubi = ubi_get_device(ubi_num); ++ if (!ubi) ++ return ERR_PTR(-ENODEV); ++ ++ if (vol_id < 0 || vol_id >= ubi->vtbl_slots) { ++ err = -EINVAL; ++ goto out_put_ubi; ++ } + + desc = kmalloc(sizeof(struct ubi_volume_desc), GFP_KERNEL); + if (!desc) { + err = -ENOMEM; +- goto out_put; ++ goto out_put_ubi; + } + ++ err = -ENODEV; ++ if (!try_module_get(THIS_MODULE)) ++ goto out_free; ++ + spin_lock(&ubi->volumes_lock); + vol = ubi->volumes[vol_id]; +- if (!vol) { +- err = -ENODEV; ++ if (!vol) + goto out_unlock; +- } + + err = -EBUSY; + switch (mode) { +@@ -156,21 +165,19 @@ + vol->exclusive = 1; + break; + } ++ get_device(&vol->dev); ++ vol->ref_count += 1; + spin_unlock(&ubi->volumes_lock); + + desc->vol = vol; + desc->mode = mode; + +- /* +- * To prevent simultaneous checks of the same volume we use @vtbl_mutex, +- * although it is not the purpose it was introduced for. +- */ +- mutex_lock(&ubi->vtbl_mutex); ++ mutex_lock(&ubi->ckvol_mutex); + if (!vol->checked) { + /* This is the first open - check the volume */ + err = ubi_check_volume(ubi, vol_id); + if (err < 0) { +- mutex_unlock(&ubi->vtbl_mutex); ++ mutex_unlock(&ubi->ckvol_mutex); + ubi_close_volume(desc); + return ERR_PTR(err); + } +@@ -181,14 +188,17 @@ + } + vol->checked = 1; + } +- mutex_unlock(&ubi->vtbl_mutex); ++ mutex_unlock(&ubi->ckvol_mutex); ++ + return desc; + + out_unlock: + spin_unlock(&ubi->volumes_lock); +- kfree(desc); +-out_put: + module_put(THIS_MODULE); ++out_free: ++ kfree(desc); ++out_put_ubi: ++ ubi_put_device(ubi); + return ERR_PTR(err); + } + EXPORT_SYMBOL_GPL(ubi_open_volume); +@@ -205,8 +215,8 @@ + int mode) + { + int i, vol_id = -1, len; +- struct ubi_volume_desc *ret; + struct ubi_device *ubi; ++ struct ubi_volume_desc *ret; + + dbg_msg("open volume %s, mode %d", name, mode); + +@@ -217,14 +227,12 @@ + if (len > UBI_VOL_NAME_MAX) + return ERR_PTR(-EINVAL); + +- ret = ERR_PTR(-ENODEV); +- if (!try_module_get(THIS_MODULE)) +- return ret; +- +- if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || !ubi_devices[ubi_num]) +- goto out_put; ++ if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) ++ return ERR_PTR(-EINVAL); + +- ubi = ubi_devices[ubi_num]; ++ ubi = ubi_get_device(ubi_num); ++ if (!ubi) ++ return ERR_PTR(-ENODEV); + + spin_lock(&ubi->volumes_lock); + /* Walk all volumes of this UBI device */ +@@ -238,13 +246,16 @@ + } + spin_unlock(&ubi->volumes_lock); + +- if (vol_id < 0) +- goto out_put; +- +- ret = ubi_open_volume(ubi_num, vol_id, mode); ++ if (vol_id >= 0) ++ ret = ubi_open_volume(ubi_num, vol_id, mode); ++ else ++ ret = ERR_PTR(-ENODEV); + +-out_put: +- module_put(THIS_MODULE); ++ /* ++ * We should put the UBI device even in case of success, because ++ * 'ubi_open_volume()' took a reference as well. ++ */ ++ ubi_put_device(ubi); + return ret; + } + EXPORT_SYMBOL_GPL(ubi_open_volume_nm); +@@ -256,10 +267,11 @@ + void ubi_close_volume(struct ubi_volume_desc *desc) + { + struct ubi_volume *vol = desc->vol; ++ struct ubi_device *ubi = vol->ubi; + + dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode); + +- spin_lock(&vol->ubi->volumes_lock); ++ spin_lock(&ubi->volumes_lock); + switch (desc->mode) { + case UBI_READONLY: + vol->readers -= 1; +@@ -270,9 +282,12 @@ + case UBI_EXCLUSIVE: + vol->exclusive = 0; + } +- spin_unlock(&vol->ubi->volumes_lock); ++ vol->ref_count -= 1; ++ spin_unlock(&ubi->volumes_lock); + + kfree(desc); ++ put_device(&vol->dev); ++ ubi_put_device(ubi); + module_put(THIS_MODULE); + } + EXPORT_SYMBOL_GPL(ubi_close_volume); +@@ -332,7 +347,7 @@ + if (len == 0) + return 0; + +- err = ubi_eba_read_leb(ubi, vol_id, lnum, buf, offset, len, check); ++ err = ubi_eba_read_leb(ubi, vol, lnum, buf, offset, len, check); + if (err && err == -EBADMSG && vol->vol_type == UBI_STATIC_VOLUME) { + ubi_warn("mark volume %d as corrupted", vol_id); + vol->corrupted = 1; +@@ -399,7 +414,7 @@ + if (len == 0) + return 0; + +- return ubi_eba_write_leb(ubi, vol_id, lnum, buf, offset, len, dtype); ++ return ubi_eba_write_leb(ubi, vol, lnum, buf, offset, len, dtype); + } + EXPORT_SYMBOL_GPL(ubi_leb_write); + +@@ -448,7 +463,7 @@ + if (len == 0) + return 0; + +- return ubi_eba_atomic_leb_change(ubi, vol_id, lnum, buf, len, dtype); ++ return ubi_eba_atomic_leb_change(ubi, vol, lnum, buf, len, dtype); + } + EXPORT_SYMBOL_GPL(ubi_leb_change); + +@@ -468,9 +483,9 @@ + { + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; +- int err, vol_id = vol->vol_id; ++ int err; + +- dbg_msg("erase LEB %d:%d", vol_id, lnum); ++ dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; +@@ -481,7 +496,7 @@ + if (vol->upd_marker) + return -EBADF; + +- err = ubi_eba_unmap_leb(ubi, vol_id, lnum); ++ err = ubi_eba_unmap_leb(ubi, vol, lnum); + if (err) + return err; + +@@ -529,9 +544,8 @@ + { + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; +- int vol_id = vol->vol_id; + +- dbg_msg("unmap LEB %d:%d", vol_id, lnum); ++ dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum); + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; +@@ -542,11 +556,55 @@ + if (vol->upd_marker) + return -EBADF; + +- return ubi_eba_unmap_leb(ubi, vol_id, lnum); ++ return ubi_eba_unmap_leb(ubi, vol, lnum); + } + EXPORT_SYMBOL_GPL(ubi_leb_unmap); + + /** ++ * ubi_leb_map - map logical erasblock to a physical eraseblock. ++ * @desc: volume descriptor ++ * @lnum: logical eraseblock number ++ * @dtype: expected data type ++ * ++ * This function maps an un-mapped logical eraseblock @lnum to a physical ++ * eraseblock. This means, that after a successfull invocation of this ++ * function the logical eraseblock @lnum will be empty (contain only %0xFF ++ * bytes) and be mapped to a physical eraseblock, even if an unclean reboot ++ * happens. ++ * ++ * This function returns zero in case of success, %-EBADF if the volume is ++ * damaged because of an interrupted update, %-EBADMSG if the logical ++ * eraseblock is already mapped, and other negative error codes in case of ++ * other failures. ++ */ ++int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype) ++{ ++ struct ubi_volume *vol = desc->vol; ++ struct ubi_device *ubi = vol->ubi; ++ ++ dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum); ++ ++ if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) ++ return -EROFS; ++ ++ if (lnum < 0 || lnum >= vol->reserved_pebs) ++ return -EINVAL; ++ ++ if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && ++ dtype != UBI_UNKNOWN) ++ return -EINVAL; ++ ++ if (vol->upd_marker) ++ return -EBADF; ++ ++ if (vol->eba_tbl[lnum] >= 0) ++ return -EBADMSG; ++ ++ return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype); ++} ++EXPORT_SYMBOL_GPL(ubi_leb_map); ++ ++/** + * ubi_is_mapped - check if logical eraseblock is mapped. + * @desc: volume descriptor + * @lnum: logical eraseblock number +@@ -577,3 +635,138 @@ + return vol->eba_tbl[lnum] >= 0; + } + EXPORT_SYMBOL_GPL(ubi_is_mapped); ++ ++/* add by Nancy start */ ++ ++int ubiblk_leb_change(struct ubiblk_dev *ubiblk) ++{ ++ struct ubi_volume *vol = ubiblk->uv->vol; ++ struct ubi_device *ubi = vol->ubi; ++ int vol_id = vol->vol_id; ++ ++ struct ubi_volume_desc *desc = ubiblk->uv; ++ int lnum = ubiblk->vbw; ++ int len = ubi->leb_size; ++ int dtype = UBI_UNKNOWN; ++ void *buf = ubiblk->write_cache; ++ ++ dbg_msg("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum); ++ ++ if (vol_id < 0 || vol_id >= ubi->vtbl_slots) ++ return -EINVAL; ++ ++ if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) ++ return -EROFS; ++ ++ if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 || ++ len > vol->usable_leb_size || len % ubi->min_io_size) ++ return -EINVAL; ++ ++ if (vol->upd_marker) ++ return -EBADF; ++ ++ if (len == 0) ++ return 0; ++ ++ return ubiblk_eba_atomic_leb_change(ubi, vol, lnum, buf, len, dtype, ubiblk); ++} ++EXPORT_SYMBOL_GPL(ubiblk_leb_change); ++ ++ ++void ubi_open_blkdev(int ubi_num, int vol_id, int mode) ++{ ++ int err; ++ struct ubi_device *ubi; ++ struct ubi_volume *vol; ++ ++ dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode); ++ ++ if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) ++ return; ++ ++ if (mode != UBI_READONLY && mode != UBI_READWRITE && ++ mode != UBI_EXCLUSIVE) ++ return; ++ /* ++ * First of all, we have to get the UBI device to prevent its removal. ++ */ ++ ubi = ubi_get_device(ubi_num); ++ if (!ubi) ++ return; ++ ++ if (vol_id < 0 || vol_id >= ubi->vtbl_slots) { ++ err = -EINVAL; ++ goto out_put_ubi; ++ } ++ ++ err = -ENODEV; ++ if (!try_module_get(THIS_MODULE)) ++ goto out_put_ubi; ++ ++ spin_lock(&ubi->volumes_lock); ++ vol = ubi->volumes[vol_id]; ++ if (!vol) ++ goto out_unlock; ++ ++ err = -EBUSY; ++ switch (mode) { ++ case UBI_READONLY: ++ if (vol->exclusive) ++ goto out_unlock; ++ vol->readers += 1; ++ break; ++ ++ case UBI_READWRITE: ++ if (vol->exclusive || vol->writers > 0) ++ goto out_unlock; ++ vol->writers += 1; ++ break; ++ ++ case UBI_EXCLUSIVE: ++ if (vol->exclusive || vol->writers || vol->readers) ++ goto out_unlock; ++ vol->exclusive = 1; ++ break; ++ } ++ get_device(&vol->dev); ++ vol->ref_count += 1; ++ spin_unlock(&ubi->volumes_lock); ++ return; ++ ++out_unlock: ++ spin_unlock(&ubi->volumes_lock); ++ module_put(THIS_MODULE); ++out_put_ubi: ++ ubi_put_device(ubi); ++ return; ++} ++EXPORT_SYMBOL_GPL(ubi_open_blkdev); ++ ++ ++void ubi_close_blkdev(struct ubi_volume_desc *desc) ++{ ++ struct ubi_volume *vol = desc->vol; ++ struct ubi_device *ubi = vol->ubi; ++ ++ dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode); ++ ++ spin_lock(&ubi->volumes_lock); ++ switch (desc->mode) { ++ case UBI_READONLY: ++ vol->readers -= 1; ++ break; ++ case UBI_READWRITE: ++ vol->writers -= 1; ++ break; ++ case UBI_EXCLUSIVE: ++ vol->exclusive = 0; ++ } ++ vol->ref_count -= 1; ++ spin_unlock(&ubi->volumes_lock); ++ put_device(&vol->dev); ++ ubi_put_device(ubi); ++ module_put(THIS_MODULE); ++} ++EXPORT_SYMBOL_GPL(ubi_close_blkdev); ++/* add by Nancy end */ ++ +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/misc.c linux-2.6.24.7/drivers/mtd/ubi/misc.c +--- linux-2.6.24.7.old/drivers/mtd/ubi/misc.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/misc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -67,7 +67,12 @@ + if (vol->vol_type != UBI_STATIC_VOLUME) + return 0; + ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ buf = kmalloc(vol->usable_leb_size, GFP_KERNEL); ++#else + buf = vmalloc(vol->usable_leb_size); ++#endif ++ + if (!buf) + return -ENOMEM; + +@@ -79,7 +84,7 @@ + else + size = vol->usable_leb_size; + +- err = ubi_eba_read_leb(ubi, vol_id, i, buf, 0, size, 1); ++ err = ubi_eba_read_leb(ubi, vol, i, buf, 0, size, 1); + if (err) { + if (err == -EBADMSG) + err = 1; +@@ -87,7 +92,11 @@ + } + } + ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(buf); ++#else + vfree(buf); ++#endif + return err; + } + +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/scan.c linux-2.6.24.7/drivers/mtd/ubi/scan.c +--- linux-2.6.24.7.old/drivers/mtd/ubi/scan.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/scan.c 2009-04-12 18:13:57.000000000 +0200 +@@ -42,6 +42,7 @@ + + #include + #include ++#include + #include "ubi.h" + + #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +@@ -92,27 +93,6 @@ + } + + /** +- * commit_to_mean_value - commit intermediate results to the final mean erase +- * counter value. +- * @si: scanning information +- * +- * This is a helper function which calculates partial mean erase counter mean +- * value and adds it to the resulting mean value. As we can work only in +- * integer arithmetic and we want to calculate the mean value of erase counter +- * accurately, we first sum erase counter values in @si->ec_sum variable and +- * count these components in @si->ec_count. If this temporary @si->ec_sum is +- * going to overflow, we calculate the partial mean value +- * (@si->ec_sum/@si->ec_count) and add it to @si->mean_ec. +- */ +-static void commit_to_mean_value(struct ubi_scan_info *si) +-{ +- si->ec_sum /= si->ec_count; +- if (si->ec_sum % si->ec_count >= si->ec_count / 2) +- si->mean_ec += 1; +- si->mean_ec += si->ec_sum; +-} +- +-/** + * validate_vid_hdr - check that volume identifier header is correct and + * consistent. + * @vid_hdr: the volume identifier header to check +@@ -286,9 +266,14 @@ + * FIXME: but this is anyway obsolete and will be removed at + * some point. + */ +- + dbg_bld("using old crappy leb_ver stuff"); + ++ if (v1 == v2) { ++ ubi_err("PEB %d and PEB %d have the same version %lld", ++ seb->pnum, pnum, v1); ++ return -EINVAL; ++ } ++ + abs = v1 - v2; + if (abs < 0) + abs = -abs; +@@ -353,7 +338,11 @@ + /* Read the data of the copy and check the CRC */ + + len = be32_to_cpu(vid_hdr->data_size); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ buf = kmalloc(len, GFP_KERNEL); ++#else + buf = vmalloc(len); ++#endif + if (!buf) { + err = -ENOMEM; + goto out_free_vidh; +@@ -376,7 +365,11 @@ + bitflips = !!err; + } + ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(buf); ++#else + vfree(buf); ++#endif + ubi_free_vid_hdr(ubi, vh); + + if (second_is_newer) +@@ -387,10 +380,14 @@ + return second_is_newer | (bitflips << 1) | (corrupted << 2); + + out_free_buf: ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(buf); ++#else + vfree(buf); ++#endif ++ + out_free_vidh: + ubi_free_vid_hdr(ubi, vh); +- ubi_assert(err < 0); + return err; + } + +@@ -769,7 +766,7 @@ + */ + static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum) + { +- long long ec; ++ long long uninitialized_var(ec); + int err, bitflips = 0, vol_id, ec_corr = 0; + + dbg_bld("scan PEB %d", pnum); +@@ -854,7 +851,7 @@ + } + + vol_id = be32_to_cpu(vidh->vol_id); +- if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOL_ID) { ++ if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOLUME_ID) { + int lnum = be32_to_cpu(vidh->lnum); + + /* Unsupported internal volume */ +@@ -897,15 +894,8 @@ + + adjust_mean_ec: + if (!ec_corr) { +- if (si->ec_sum + ec < ec) { +- commit_to_mean_value(si); +- si->ec_sum = 0; +- si->ec_count = 0; +- } else { +- si->ec_sum += ec; +- si->ec_count += 1; +- } +- ++ si->ec_sum += ec; ++ si->ec_count += 1; + if (ec > si->max_ec) + si->max_ec = ec; + if (ec < si->min_ec) +@@ -961,9 +951,11 @@ + + dbg_msg("scanning is finished"); + +- /* Finish mean erase counter calculations */ +- if (si->ec_count) +- commit_to_mean_value(si); ++ /* Calculate mean erase counter */ ++ if (si->ec_count) { ++ do_div(si->ec_sum, si->ec_count); ++ si->mean_ec = si->ec_sum; ++ } + + if (si->is_empty) + ubi_msg("empty MTD device detected"); +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/scan.h linux-2.6.24.7/drivers/mtd/ubi/scan.h +--- linux-2.6.24.7.old/drivers/mtd/ubi/scan.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/scan.h 2009-04-12 18:13:57.000000000 +0200 +@@ -124,7 +124,7 @@ + int max_ec; + unsigned long long max_sqnum; + int mean_ec; +- int ec_sum; ++ uint64_t ec_sum; + int ec_count; + }; + +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/ubi-media.h linux-2.6.24.7/drivers/mtd/ubi/ubi-media.h +--- linux-2.6.24.7.old/drivers/mtd/ubi/ubi-media.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/ubi/ubi-media.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,372 @@ ++/* ++ * Copyright (c) International Business Machines Corp., 2006 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See ++ * the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Thomas Gleixner ++ * Frank Haverkamp ++ * Oliver Lohmann ++ * Andreas Arnez ++ */ ++ ++/* ++ * This file defines the layout of UBI headers and all the other UBI on-flash ++ * data structures. ++ */ ++ ++#ifndef __UBI_MEDIA_H__ ++#define __UBI_MEDIA_H__ ++ ++#include ++ ++/* The version of UBI images supported by this implementation */ ++#define UBI_VERSION 1 ++ ++/* The highest erase counter value supported by this implementation */ ++#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF ++ ++/* The initial CRC32 value used when calculating CRC checksums */ ++#define UBI_CRC32_INIT 0xFFFFFFFFU ++ ++/* Erase counter header magic number (ASCII "UBI#") */ ++#define UBI_EC_HDR_MAGIC 0x55424923 ++/* Volume identifier header magic number (ASCII "UBI!") */ ++#define UBI_VID_HDR_MAGIC 0x55424921 ++ ++/* ++ * Volume type constants used in the volume identifier header. ++ * ++ * @UBI_VID_DYNAMIC: dynamic volume ++ * @UBI_VID_STATIC: static volume ++ */ ++enum { ++ UBI_VID_DYNAMIC = 1, ++ UBI_VID_STATIC = 2 ++}; ++ ++/* ++ * Volume flags used in the volume table record. ++ * ++ * @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume ++ * ++ * %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume ++ * table. UBI automatically re-sizes the volume which has this flag and makes ++ * the volume to be of largest possible size. This means that if after the ++ * initialization UBI finds out that there are available physical eraseblocks ++ * present on the device, it automatically appends all of them to the volume ++ * (the physical eraseblocks reserved for bad eraseblocks handling and other ++ * reserved physical eraseblocks are not taken). So, if there is a volume with ++ * the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical ++ * eraseblocks will be zero after UBI is loaded, because all of them will be ++ * reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared ++ * after the volume had been initialized. ++ * ++ * The auto-resize feature is useful for device production purposes. For ++ * example, different NAND flash chips may have different amount of initial bad ++ * eraseblocks, depending of particular chip instance. Manufacturers of NAND ++ * chips usually guarantee that the amount of initial bad eraseblocks does not ++ * exceed certain percent, e.g. 2%. When one creates an UBI image which will be ++ * flashed to the end devices in production, he does not know the exact amount ++ * of good physical eraseblocks the NAND chip on the device will have, but this ++ * number is required to calculate the volume sized and put them to the volume ++ * table of the UBI image. In this case, one of the volumes (e.g., the one ++ * which will store the root file system) is marked as "auto-resizable", and ++ * UBI will adjust its size on the first boot if needed. ++ * ++ * Note, first UBI reserves some amount of physical eraseblocks for bad ++ * eraseblock handling, and then re-sizes the volume, not vice-versa. This ++ * means that the pool of reserved physical eraseblocks will always be present. ++ */ ++enum { ++ UBI_VTBL_AUTORESIZE_FLG = 0x01, ++}; ++ ++/* ++ * Compatibility constants used by internal volumes. ++ * ++ * @UBI_COMPAT_DELETE: delete this internal volume before anything is written ++ * to the flash ++ * @UBI_COMPAT_RO: attach this device in read-only mode ++ * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its ++ * physical eraseblocks, don't allow the wear-leveling unit to move them ++ * @UBI_COMPAT_REJECT: reject this UBI image ++ */ ++enum { ++ UBI_COMPAT_DELETE = 1, ++ UBI_COMPAT_RO = 2, ++ UBI_COMPAT_PRESERVE = 4, ++ UBI_COMPAT_REJECT = 5 ++}; ++ ++/* Sizes of UBI headers */ ++#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr) ++#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr) ++ ++/* Sizes of UBI headers without the ending CRC */ ++#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(__be32)) ++#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(__be32)) ++ ++/** ++ * struct ubi_ec_hdr - UBI erase counter header. ++ * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC) ++ * @version: version of UBI implementation which is supposed to accept this ++ * UBI image ++ * @padding1: reserved for future, zeroes ++ * @ec: the erase counter ++ * @vid_hdr_offset: where the VID header starts ++ * @data_offset: where the user data start ++ * @padding2: reserved for future, zeroes ++ * @hdr_crc: erase counter header CRC checksum ++ * ++ * The erase counter header takes 64 bytes and has a plenty of unused space for ++ * future usage. The unused fields are zeroed. The @version field is used to ++ * indicate the version of UBI implementation which is supposed to be able to ++ * work with this UBI image. If @version is greater then the current UBI ++ * version, the image is rejected. This may be useful in future if something ++ * is changed radically. This field is duplicated in the volume identifier ++ * header. ++ * ++ * The @vid_hdr_offset and @data_offset fields contain the offset of the the ++ * volume identifier header and user data, relative to the beginning of the ++ * physical eraseblock. These values have to be the same for all physical ++ * eraseblocks. ++ */ ++struct ubi_ec_hdr { ++ __be32 magic; ++ __u8 version; ++ __u8 padding1[3]; ++ __be64 ec; /* Warning: the current limit is 31-bit anyway! */ ++ __be32 vid_hdr_offset; ++ __be32 data_offset; ++ __u8 padding2[36]; ++ __be32 hdr_crc; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubi_vid_hdr - on-flash UBI volume identifier header. ++ * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) ++ * @version: UBI implementation version which is supposed to accept this UBI ++ * image (%UBI_VERSION) ++ * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) ++ * @copy_flag: if this logical eraseblock was copied from another physical ++ * eraseblock (for wear-leveling reasons) ++ * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, ++ * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) ++ * @vol_id: ID of this volume ++ * @lnum: logical eraseblock number ++ * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be ++ * removed, kept only for not breaking older UBI users) ++ * @data_size: how many bytes of data this logical eraseblock contains ++ * @used_ebs: total number of used logical eraseblocks in this volume ++ * @data_pad: how many bytes at the end of this physical eraseblock are not ++ * used ++ * @data_crc: CRC checksum of the data stored in this logical eraseblock ++ * @padding1: reserved for future, zeroes ++ * @sqnum: sequence number ++ * @padding2: reserved for future, zeroes ++ * @hdr_crc: volume identifier header CRC checksum ++ * ++ * The @sqnum is the value of the global sequence counter at the time when this ++ * VID header was created. The global sequence counter is incremented each time ++ * UBI writes a new VID header to the flash, i.e. when it maps a logical ++ * eraseblock to a new physical eraseblock. The global sequence counter is an ++ * unsigned 64-bit integer and we assume it never overflows. The @sqnum ++ * (sequence number) is used to distinguish between older and newer versions of ++ * logical eraseblocks. ++ * ++ * There are 2 situations when there may be more then one physical eraseblock ++ * corresponding to the same logical eraseblock, i.e., having the same @vol_id ++ * and @lnum values in the volume identifier header. Suppose we have a logical ++ * eraseblock L and it is mapped to the physical eraseblock P. ++ * ++ * 1. Because UBI may erase physical eraseblocks asynchronously, the following ++ * situation is possible: L is asynchronously erased, so P is scheduled for ++ * erasure, then L is written to,i.e. mapped to another physical eraseblock P1, ++ * so P1 is written to, then an unclean reboot happens. Result - there are 2 ++ * physical eraseblocks P and P1 corresponding to the same logical eraseblock ++ * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the ++ * flash. ++ * ++ * 2. From time to time UBI moves logical eraseblocks to other physical ++ * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P ++ * to P1, and an unclean reboot happens before P is physically erased, there ++ * are two physical eraseblocks P and P1 corresponding to L and UBI has to ++ * select one of them when the flash is attached. The @sqnum field says which ++ * PEB is the original (obviously P will have lower @sqnum) and the copy. But ++ * it is not enough to select the physical eraseblock with the higher sequence ++ * number, because the unclean reboot could have happen in the middle of the ++ * copying process, so the data in P is corrupted. It is also not enough to ++ * just select the physical eraseblock with lower sequence number, because the ++ * data there may be old (consider a case if more data was added to P1 after ++ * the copying). Moreover, the unclean reboot may happen when the erasure of P ++ * was just started, so it result in unstable P, which is "mostly" OK, but ++ * still has unstable bits. ++ * ++ * UBI uses the @copy_flag field to indicate that this logical eraseblock is a ++ * copy. UBI also calculates data CRC when the data is moved and stores it at ++ * the @data_crc field of the copy (P1). So when UBI needs to pick one physical ++ * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is ++ * examined. If it is cleared, the situation* is simple and the newer one is ++ * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC ++ * checksum is correct, this physical eraseblock is selected (P1). Otherwise ++ * the older one (P) is selected. ++ * ++ * Note, there is an obsolete @leb_ver field which was used instead of @sqnum ++ * in the past. But it is not used anymore and we keep it in order to be able ++ * to deal with old UBI images. It will be removed at some point. ++ * ++ * There are 2 sorts of volumes in UBI: user volumes and internal volumes. ++ * Internal volumes are not seen from outside and are used for various internal ++ * UBI purposes. In this implementation there is only one internal volume - the ++ * layout volume. Internal volumes are the main mechanism of UBI extensions. ++ * For example, in future one may introduce a journal internal volume. Internal ++ * volumes have their own reserved range of IDs. ++ * ++ * The @compat field is only used for internal volumes and contains the "degree ++ * of their compatibility". It is always zero for user volumes. This field ++ * provides a mechanism to introduce UBI extensions and to be still compatible ++ * with older UBI binaries. For example, if someone introduced a journal in ++ * future, he would probably use %UBI_COMPAT_DELETE compatibility for the ++ * journal volume. And in this case, older UBI binaries, which know nothing ++ * about the journal volume, would just delete this volume and work perfectly ++ * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image ++ * - it just ignores the Ext3fs journal. ++ * ++ * The @data_crc field contains the CRC checksum of the contents of the logical ++ * eraseblock if this is a static volume. In case of dynamic volumes, it does ++ * not contain the CRC checksum as a rule. The only exception is when the ++ * data of the physical eraseblock was moved by the wear-leveling unit, then ++ * the wear-leveling unit calculates the data CRC and stores it in the ++ * @data_crc field. And of course, the @copy_flag is %in this case. ++ * ++ * The @data_size field is used only for static volumes because UBI has to know ++ * how many bytes of data are stored in this eraseblock. For dynamic volumes, ++ * this field usually contains zero. The only exception is when the data of the ++ * physical eraseblock was moved to another physical eraseblock for ++ * wear-leveling reasons. In this case, UBI calculates CRC checksum of the ++ * contents and uses both @data_crc and @data_size fields. In this case, the ++ * @data_size field contains data size. ++ * ++ * The @used_ebs field is used only for static volumes and indicates how many ++ * eraseblocks the data of the volume takes. For dynamic volumes this field is ++ * not used and always contains zero. ++ * ++ * The @data_pad is calculated when volumes are created using the alignment ++ * parameter. So, effectively, the @data_pad field reduces the size of logical ++ * eraseblocks of this volume. This is very handy when one uses block-oriented ++ * software (say, cramfs) on top of the UBI volume. ++ */ ++struct ubi_vid_hdr { ++ __be32 magic; ++ __u8 version; ++ __u8 vol_type; ++ __u8 copy_flag; ++ __u8 compat; ++ __be32 vol_id; ++ __be32 lnum; ++ __be32 leb_ver; /* obsolete, to be removed, don't use */ ++ __be32 data_size; ++ __be32 used_ebs; ++ __be32 data_pad; ++ __be32 data_crc; ++ __u8 padding1[4]; ++ __be64 sqnum; ++ __u8 padding2[12]; ++ __be32 hdr_crc; ++} __attribute__ ((packed)); ++ ++/* Internal UBI volumes count */ ++#define UBI_INT_VOL_COUNT 1 ++ ++/* ++ * Starting ID of internal volumes. There is reserved room for 4096 internal ++ * volumes. ++ */ ++#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) ++ ++/* The layout volume contains the volume table */ ++ ++#define UBI_LAYOUT_VOLUME_ID UBI_INTERNAL_VOL_START ++#define UBI_LAYOUT_VOLUME_TYPE UBI_VID_DYNAMIC ++#define UBI_LAYOUT_VOLUME_ALIGN 1 ++#define UBI_LAYOUT_VOLUME_EBS 2 ++#define UBI_LAYOUT_VOLUME_NAME "layout volume" ++#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT ++ ++/* The maximum number of volumes per one UBI device */ ++#define UBI_MAX_VOLUMES 128 ++ ++/* The maximum volume name length */ ++#define UBI_VOL_NAME_MAX 127 ++ ++/* Size of the volume table record */ ++#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record) ++ ++/* Size of the volume table record without the ending CRC */ ++#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(__be32)) ++ ++/** ++ * struct ubi_vtbl_record - a record in the volume table. ++ * @reserved_pebs: how many physical eraseblocks are reserved for this volume ++ * @alignment: volume alignment ++ * @data_pad: how many bytes are unused at the end of the each physical ++ * eraseblock to satisfy the requested alignment ++ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) ++ * @upd_marker: if volume update was started but not finished ++ * @name_len: volume name length ++ * @name: the volume name ++ * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG) ++ * @padding: reserved, zeroes ++ * @crc: a CRC32 checksum of the record ++ * ++ * The volume table records are stored in the volume table, which is stored in ++ * the layout volume. The layout volume consists of 2 logical eraseblock, each ++ * of which contains a copy of the volume table (i.e., the volume table is ++ * duplicated). The volume table is an array of &struct ubi_vtbl_record ++ * objects indexed by the volume ID. ++ * ++ * If the size of the logical eraseblock is large enough to fit ++ * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES ++ * records. Otherwise, it contains as many records as it can fit (i.e., size of ++ * logical eraseblock divided by sizeof(struct ubi_vtbl_record)). ++ * ++ * The @upd_marker flag is used to implement volume update. It is set to %1 ++ * before update and set to %0 after the update. So if the update operation was ++ * interrupted, UBI knows that the volume is corrupted. ++ * ++ * The @alignment field is specified when the volume is created and cannot be ++ * later changed. It may be useful, for example, when a block-oriented file ++ * system works on top of UBI. The @data_pad field is calculated using the ++ * logical eraseblock size and @alignment. The alignment must be multiple to the ++ * minimal flash I/O unit. If @alignment is 1, all the available space of ++ * the physical eraseblocks is used. ++ * ++ * Empty records contain all zeroes and the CRC checksum of those zeroes. ++ */ ++struct ubi_vtbl_record { ++ __be32 reserved_pebs; ++ __be32 alignment; ++ __be32 data_pad; ++ __u8 vol_type; ++ __u8 upd_marker; ++ __be16 name_len; ++ __u8 name[UBI_VOL_NAME_MAX+1]; ++ __u8 flags; ++ __u8 padding[23]; ++ __be32 crc; ++} __attribute__ ((packed)); ++ ++#endif /* !__UBI_MEDIA_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/ubi.h linux-2.6.24.7/drivers/mtd/ubi/ubi.h +--- linux-2.6.24.7.old/drivers/mtd/ubi/ubi.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/ubi.h 2009-04-12 18:13:57.000000000 +0200 +@@ -37,10 +37,9 @@ + #include + #include + #include +- +-#include + #include + ++#include "ubi-media.h" + #include "scan.h" + #include "debug.h" + +@@ -94,8 +93,43 @@ + UBI_IO_BITFLIPS + }; + +-extern int ubi_devices_cnt; +-extern struct ubi_device *ubi_devices[]; ++/** ++ * struct ubi_wl_entry - wear-leveling entry. ++ * @rb: link in the corresponding RB-tree ++ * @ec: erase counter ++ * @pnum: physical eraseblock number ++ * ++ * This data structure is used in the WL unit. Each physical eraseblock has a ++ * corresponding &struct wl_entry object which may be kept in different ++ * RB-trees. See WL unit for details. ++ */ ++struct ubi_wl_entry { ++ struct rb_node rb; ++ int ec; ++ int pnum; ++}; ++ ++/** ++ * struct ubi_ltree_entry - an entry in the lock tree. ++ * @rb: links RB-tree nodes ++ * @vol_id: volume ID of the locked logical eraseblock ++ * @lnum: locked logical eraseblock number ++ * @users: how many tasks are using this logical eraseblock or wait for it ++ * @mutex: read/write mutex to implement read/write access serialization to ++ * the (@vol_id, @lnum) logical eraseblock ++ * ++ * This data structure is used in the EBA unit to implement per-LEB locking. ++ * When a logical eraseblock is being locked - corresponding ++ * &struct ubi_ltree_entry object is inserted to the lock tree (@ubi->ltree). ++ * See EBA unit for details. ++ */ ++struct ubi_ltree_entry { ++ struct rb_node rb; ++ int vol_id; ++ int lnum; ++ int users; ++ struct rw_semaphore mutex; ++}; + + struct ubi_volume_desc; + +@@ -105,11 +139,10 @@ + * @cdev: character device object to create character device + * @ubi: reference to the UBI device description object + * @vol_id: volume ID ++ * @ref_count: volume reference count + * @readers: number of users holding this volume in read-only mode + * @writers: number of users holding this volume in read-write mode + * @exclusive: whether somebody holds this volume in exclusive mode +- * @removed: if the volume was removed +- * @checked: if this static volume was checked + * + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) +@@ -117,21 +150,30 @@ + * @used_ebs: how many logical eraseblocks in this volume contain data + * @last_eb_bytes: how many bytes are stored in the last logical eraseblock + * @used_bytes: how many bytes of data this volume contains +- * @upd_marker: non-zero if the update marker is set for this volume +- * @corrupted: non-zero if the volume is corrupted (static volumes only) + * @alignment: volume alignment + * @data_pad: how many bytes are not used at the end of physical eraseblocks to +- * satisfy the requested alignment ++ * satisfy the requested alignment + * @name_len: volume name length + * @name: volume name + * +- * @updating: whether the volume is being updated + * @upd_ebs: how many eraseblocks are expected to be updated +- * @upd_bytes: how many bytes are expected to be received +- * @upd_received: how many update bytes were already received +- * @upd_buf: update buffer which is used to collect update data ++ * @ch_lnum: LEB number which is being changing by the atomic LEB change ++ * operation ++ * @ch_dtype: data persistency type which is being changing by the atomic LEB ++ * change operation ++ * @upd_bytes: how many bytes are expected to be received for volume update or ++ * atomic LEB change ++ * @upd_received: how many bytes were already received for volume update or ++ * atomic LEB change ++ * @upd_buf: update buffer which is used to collect update data or data for ++ * atomic LEB change + * + * @eba_tbl: EBA table of this volume (LEB->PEB mapping) ++ * @checked: %1 if this static volume was checked ++ * @corrupted: %1 if the volume is corrupted (static volumes only) ++ * @upd_marker: %1 if the update marker is set for this volume ++ * @updating: %1 if the volume is being updated ++ * @changing_leb: %1 if the atomic LEB change ioctl command is in progress + * + * @gluebi_desc: gluebi UBI volume descriptor + * @gluebi_refcount: reference count of the gluebi MTD device +@@ -150,11 +192,10 @@ + struct cdev cdev; + struct ubi_device *ubi; + int vol_id; ++ int ref_count; + int readers; + int writers; + int exclusive; +- int removed; +- int checked; + + int reserved_pebs; + int vol_type; +@@ -162,29 +203,45 @@ + int used_ebs; + int last_eb_bytes; + long long used_bytes; +- int upd_marker; +- int corrupted; + int alignment; + int data_pad; + int name_len; + char name[UBI_VOL_NAME_MAX+1]; + +- int updating; + int upd_ebs; ++ int ch_lnum; ++ int ch_dtype; + long long upd_bytes; + long long upd_received; + void *upd_buf; + + int *eba_tbl; ++ unsigned int checked:1; ++ unsigned int corrupted:1; ++ unsigned int upd_marker:1; ++ unsigned int updating:1; ++ unsigned int changing_leb:1; + + #ifdef CONFIG_MTD_UBI_GLUEBI +- /* Gluebi-related stuff may be compiled out */ ++ /* ++ * Gluebi-related stuff may be compiled out. ++ * TODO: this should not be built into UBI but should be a separate ++ * ubimtd driver which works on top of UBI and emulates MTD devices. ++ */ + struct ubi_volume_desc *gluebi_desc; + int gluebi_refcount; + struct mtd_info gluebi_mtd; + #endif ++ int bdev_mode; //add by Nancy + }; + ++struct vol_notifier { ++ void (*add)(struct ubi_volume *vol); ++ void (*remove)(struct ubi_volume *vol); ++ struct list_head list; ++}; ++ ++ + /** + * struct ubi_volume_desc - descriptor of the UBI volume returned when it is + * opened. +@@ -200,28 +257,31 @@ + + /** + * struct ubi_device - UBI device description structure +- * @dev: class device object to use the the Linux device model ++ * @dev: UBI device object to use the the Linux device model + * @cdev: character device object to create character device + * @ubi_num: UBI device number + * @ubi_name: UBI device name +- * @major: character device major number + * @vol_count: number of volumes in this UBI device + * @volumes: volumes of this UBI device + * @volumes_lock: protects @volumes, @rsvd_pebs, @avail_pebs, beb_rsvd_pebs, +- * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, @vol->readers, +- * @vol->writers, @vol->exclusive, @vol->removed, @vol->mapping and +- * @vol->eba_tbl. ++ * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, ++ * @vol->readers, @vol->writers, @vol->exclusive, ++ * @vol->ref_count, @vol->mapping and @vol->eba_tbl. ++ * @ref_count: count of references on the UBI device + * + * @rsvd_pebs: count of reserved physical eraseblocks + * @avail_pebs: count of available physical eraseblocks + * @beb_rsvd_pebs: how many physical eraseblocks are reserved for bad PEB +- * handling ++ * handling + * @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling + * ++ * @autoresize_vol_id: ID of the volume which has to be auto-resized at the end ++ * of UBI ititializetion + * @vtbl_slots: how many slots are available in the volume table + * @vtbl_size: size of the volume table in bytes + * @vtbl: in-RAM volume table copy +- * @vtbl_mutex: protects on-flash volume table ++ * @volumes_mutex: protects on-flash volume table and serializes volume ++ * changes, like creation, deletion, update, resize + * + * @max_ec: current highest erase counter value + * @mean_ec: current mean erase counter value +@@ -238,15 +298,15 @@ + * @prot.pnum: protection tree indexed by physical eraseblock numbers + * @prot.aec: protection tree indexed by absolute erase counter value + * @wl_lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move_from, +- * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works +- * fields ++ * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works ++ * fields ++ * @move_mutex: serializes eraseblock moves + * @wl_scheduled: non-zero if the wear-leveling was scheduled + * @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any +- * physical eraseblock ++ * physical eraseblock + * @abs_ec: absolute erase counter + * @move_from: physical eraseblock from where the data is being moved + * @move_to: physical eraseblock where the data is being moved to +- * @move_from_put: if the "from" PEB was put + * @move_to_put: if the "to" PEB was put + * @works: list of pending works + * @works_count: count of pending works +@@ -273,36 +333,39 @@ + * @hdrs_min_io_size + * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset + * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or +- * not ++ * not + * @mtd: MTD device descriptor + * + * @peb_buf1: a buffer of PEB size used for different purposes + * @peb_buf2: another buffer of PEB size used for different purposes + * @buf_mutex: proptects @peb_buf1 and @peb_buf2 +- * @dbg_peb_buf: buffer of PEB size used for debugging ++ * @dbg_peb_buf: buffer of PEB size used for debugging + * @dbg_buf_mutex: proptects @dbg_peb_buf + */ + struct ubi_device { + struct cdev cdev; ++ int bdev_major; //add by Nancy + struct device dev; + int ubi_num; + char ubi_name[sizeof(UBI_NAME_STR)+5]; +- int major; + int vol_count; + struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT]; + spinlock_t volumes_lock; ++ int ref_count; + + int rsvd_pebs; + int avail_pebs; + int beb_rsvd_pebs; + int beb_rsvd_level; + ++ int autoresize_vol_id; + int vtbl_slots; + int vtbl_size; + struct ubi_vtbl_record *vtbl; +- struct mutex vtbl_mutex; ++ struct mutex volumes_mutex; + + int max_ec; ++ /* TODO: mean_ec is not updated run-time, fix */ + int mean_ec; + + /* EBA unit's stuff */ +@@ -320,12 +383,13 @@ + struct rb_root aec; + } prot; + spinlock_t wl_lock; ++ struct mutex move_mutex; ++ struct rw_semaphore work_sem; + int wl_scheduled; + struct ubi_wl_entry **lookuptbl; + unsigned long long abs_ec; + struct ubi_wl_entry *move_from; + struct ubi_wl_entry *move_to; +- int move_from_put; + int move_to_put; + struct list_head works; + int works_count; +@@ -355,15 +419,19 @@ + void *peb_buf1; + void *peb_buf2; + struct mutex buf_mutex; ++ struct mutex ckvol_mutex; + #ifdef CONFIG_MTD_UBI_DEBUG + void *dbg_peb_buf; + struct mutex dbg_buf_mutex; + #endif + }; + ++extern struct kmem_cache *ubi_wl_entry_slab; ++extern struct file_operations ubi_ctrl_cdev_operations; + extern struct file_operations ubi_cdev_operations; + extern struct file_operations ubi_vol_cdev_operations; + extern struct class *ubi_class; ++extern struct mutex ubi_devices_mutex; + + /* vtbl.c */ + int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, +@@ -374,13 +442,18 @@ + int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req); + int ubi_remove_volume(struct ubi_volume_desc *desc); + int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs); +-int ubi_add_volume(struct ubi_device *ubi, int vol_id); +-void ubi_free_volume(struct ubi_device *ubi, int vol_id); ++int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol); ++void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol); + + /* upd.c */ +-int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes); +-int ubi_more_update_data(struct ubi_device *ubi, int vol_id, ++int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, ++ long long bytes); ++int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, + const void __user *buf, int count); ++int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, ++ const struct ubi_leb_change_req *req); ++int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, ++ const void __user *buf, int count); + + /* misc.c */ + int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length); +@@ -399,16 +472,17 @@ + #endif + + /* eba.c */ +-int ubi_eba_unmap_leb(struct ubi_device *ubi, int vol_id, int lnum); +-int ubi_eba_read_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, +- int offset, int len, int check); +-int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum, ++int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, ++ int lnum); ++int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, ++ void *buf, int offset, int len, int check); ++int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + const void *buf, int offset, int len, int dtype); +-int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum, +- const void *buf, int len, int dtype, ++int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, ++ int lnum, const void *buf, int len, int dtype, + int used_ebs); +-int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum, +- const void *buf, int len, int dtype); ++int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, ++ int lnum, const void *buf, int len, int dtype); + int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, + struct ubi_vid_hdr *vid_hdr); + int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); +@@ -421,6 +495,7 @@ + int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum); + int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); + void ubi_wl_close(struct ubi_device *ubi); ++int ubi_thread(void *u); + + /* io.c */ + int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, +@@ -438,6 +513,14 @@ + struct ubi_vid_hdr *vid_hdr, int verbose); + int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr); ++ ++/* build.c */ ++int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset); ++int ubi_detach_mtd_dev(int ubi_num, int anyway); ++struct ubi_device *ubi_get_device(int ubi_num); ++void ubi_put_device(struct ubi_device *ubi); ++struct ubi_device *ubi_get_by_major(int major); ++int ubi_major2num(int major); + + /* + * ubi_rb_for_each_entry - walk an RB-tree. +@@ -523,8 +606,10 @@ + */ + static inline void ubi_ro_mode(struct ubi_device *ubi) + { +- ubi->ro_mode = 1; +- ubi_warn("switch to read-only mode"); ++ if (!ubi->ro_mode) { ++ ubi->ro_mode = 1; ++ ubi_warn("switch to read-only mode"); ++ } + } + + /** +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/ubiblk.c linux-2.6.24.7/drivers/mtd/ubi/ubiblk.c +--- linux-2.6.24.7.old/drivers/mtd/ubi/ubiblk.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/ubi/ubiblk.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,359 @@ ++/* ++ * Direct UBI block device access ++ * ++ * (C) 2000-2003 Nicolas Pitre ++ * (C) 1999-2003 David Woodhouse ++ * (C) 2008 Yurong Tan : ++ * borrow mtdblock.c to work on top of UBI ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ubi.h" ++#include "ubiblk.h" ++ ++#define UBIBLK_UNMAPPED 0 ++#define UBIBLK_SECTOR_SIZE 512 ++ ++extern void ubi_open_blkdev(int ubi_num, int vol_id, int mode); ++extern void ubi_close_blkdev(struct ubi_volume_desc *desc); ++static void ubiblk_setup_writecache(struct ubiblk_dev *ubiblk, int virt_block); ++static int ubiblk_flush_writecache(struct ubiblk_dev *ubiblk); ++extern int ubiblk_leb_change(struct ubiblk_dev *ubiblk); ++ ++struct ubiblk_dev *ubiblks[UBI_MAX_VOLUMES]; ++static unsigned short subpage_shift; ++ ++static int ubiblk_flush_writecache(struct ubiblk_dev *ubiblk) ++{ ++ if (STATE_UNUSED == ubiblk->write_cache_state) ++ return 0; ++ ubiblk_leb_change(ubiblk); ++ ubiblk->write_cache_state = STATE_UNUSED; ++ ++ return 0; ++} ++ ++static void ubiblk_setup_writecache(struct ubiblk_dev *ubiblk, int virt_block) ++{ ++ struct ubi_volume_desc *uv = ubiblk->uv; ++ struct ubi_device *ubi = uv->vol->ubi; ++ int ppb = ubi->leb_size / ubi->min_io_size; ++ unsigned short spp = ubi->min_io_size >> subpage_shift; ++ ++ ubiblk->vbw = virt_block; ++ ubiblk->write_cache_state = STATE_USED; ++ ++ memset(ubiblk->page_sts, 0, ppb); ++ memset(ubiblk->subpage_sts, 0, ppb*spp); ++} ++ ++static int do_cached_write (struct ubiblk_dev *ubiblk, unsigned long sector, ++ int len, const char *buf) ++{ ++ struct ubi_volume_desc *uv = ubiblk->uv; ++ struct ubi_device *ubi = uv->vol->ubi; ++ int ppb = ubi->leb_size / ubi->min_io_size; ++ unsigned short sectors_per_page = ubi->min_io_size / len; ++ unsigned short sectors_in_page_shift = ffs(sectors_per_page) - 1; ++ unsigned short page_shift = ffs(ubi->min_io_size) - 1; ++ unsigned short virt_block, page, subpage; ++ unsigned long virt_page; ++ ++ virt_page = sector / sectors_per_page; ++ subpage = sector % sectors_per_page; ++ virt_block = virt_page / ppb; ++ page = virt_page % ppb; ++ ++ if(ubi_is_mapped(uv, virt_block) == UBIBLK_UNMAPPED ){ ++ mutex_lock(&ubiblk->cache_mutex); ++ ubiblk_flush_writecache(ubiblk); ++ mutex_unlock(&ubiblk->cache_mutex); ++ ++ ubiblk_setup_writecache(ubiblk, virt_block); ++ } else { ++ if ( STATE_USED == ubiblk->write_cache_state ) { ++ if ( ubiblk->vbw != virt_block) { ++ // Commit before we start a new cache. ++ mutex_lock(&ubiblk->cache_mutex); ++ ubiblk_flush_writecache(ubiblk); ++ mutex_unlock(&ubiblk->cache_mutex); ++ ++ ubiblk_setup_writecache(ubiblk, virt_block); ++ } else { ++ //printk("cache hit: 0x%x\n", virt_page); ++ } ++ } else { ++// printk("with existing mapping\n"); ++ ubiblk_setup_writecache(ubiblk, virt_block); ++ } ++ } ++ ubiblk->page_sts[page] = 1; ++ ubiblk->subpage_sts[(page<write_cache[(page<uv; ++ int ppb = uv->vol->ubi->leb_size / uv->vol->ubi->min_io_size; ++ unsigned short sectors_per_page = uv->vol->ubi->min_io_size >> 9; ++ unsigned short page_shift = ffs(uv->vol->ubi->min_io_size) - 1; ++ unsigned short virt_block, page, page_offset; ++ unsigned long virt_page; ++ ++ virt_page = sector / sectors_per_page; ++ page_offset = sector % sectors_per_page; ++ virt_block = virt_page / ppb; ++ page = virt_page % ppb; ++ ++ if(ubiblk->vbw == virt_block){ ++ mutex_lock(&ubiblk->cache_mutex); ++ ubiblk_flush_writecache(ubiblk); ++ mutex_unlock(&ubiblk->cache_mutex); ++ } ++ ++ if ( ubi_is_mapped( uv, virt_block) == UBIBLK_UNMAPPED){ ++ /* In a Flash Memory device, there might be a logical block that is ++ * not allcated to a physical block due to the block not being used. ++ * All data returned should be set to 0xFF when accessing this logical ++ * block. ++ */ ++ ++ //printk("address translate fail\n"); ++ memset(buf, 0xFF, UBIBLK_SECTOR_SIZE); ++ } else { ++ ++ if( ubiblk->vbr != virt_block ||ubiblk->read_cache_state == STATE_UNUSED ){ ++ ubiblk->vbr = virt_block; ++ ubi_leb_read(uv, virt_block, ubiblk->read_cache, 0, uv->vol->usable_leb_size, 0); ++ ubiblk->read_cache_state = STATE_USED; ++ } ++ memcpy(buf, &ubiblk->read_cache[(page<devnum]; ++ return do_cached_read(ubiblk, block, UBIBLK_SECTOR_SIZE, buf); ++} ++ ++static int ubiblk_writesect(struct ubi_blktrans_dev *dev, ++ unsigned long block, char *buf) ++{ ++ struct ubiblk_dev *ubiblk = ubiblks[dev->devnum]; ++ return do_cached_write(ubiblk, block, UBIBLK_SECTOR_SIZE, buf); ++} ++ ++static int ubiblk_init_vol(int dev, struct ubi_volume_desc *uv) ++{ ++ struct ubiblk_dev *ubiblk; ++ struct ubi_device *ubi = uv->vol->ubi; ++ int ppb = ubi->leb_size / ubi->min_io_size; ++ unsigned short spp = ubi->min_io_size >> subpage_shift; ++ ++ ubiblk = kmalloc(sizeof(struct ubiblk_dev), GFP_KERNEL); ++ if (!ubiblk) ++ return -ENOMEM; ++ ++ memset(ubiblk, 0, sizeof(*ubiblk)); ++ ++ ubiblk->count = 1; ++ ubiblk->uv = uv; ++ mutex_init (&ubiblk->cache_mutex); ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ ubiblk->write_cache = kmalloc(ubiblk->uv->vol->usable_leb_size, GFP_KERNEL); ++ ubiblk->read_cache = kmalloc(ubiblk->uv->vol->usable_leb_size, GFP_KERNEL); ++ ubiblk->page_sts = kmalloc(ppb, GFP_KERNEL); ++ ubiblk->subpage_sts = kmalloc(ppb*spp, GFP_KERNEL); ++#else ++ ubiblk->write_cache = vmalloc(ubiblk->uv->vol->usable_leb_size); ++ ubiblk->read_cache = vmalloc(ubiblk->uv->vol->usable_leb_size); ++ ubiblk->page_sts = vmalloc(ppb); ++ ubiblk->subpage_sts = vmalloc(ppb*spp); ++#endif ++ ++ if(!ubiblk->write_cache || ++ !ubiblk->read_cache || ++ !ubiblk->page_sts || ++ !ubiblk->subpage_sts) ++ return -ENOMEM; ++ ++ ubiblk->write_cache_state = STATE_UNUSED; ++ ubiblk->read_cache_state = STATE_UNUSED; ++ ++ ubiblks[dev] = ubiblk; ++ DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); ++ return 0; ++} ++ ++static int ubiblk_open(struct inode *i, struct file *f ) ++{ ++ struct ubi_volume_desc *desc; ++ struct ubi_blktrans_dev *dev; ++ int ubi_num = ubi_major2num(imajor(i)); ++ int vol_id = iminor(i); ++ int mode; ++ int ret = 0; ++ ++ if (f->f_mode & FMODE_WRITE) ++ mode = UBI_READWRITE; ++ else ++ mode = UBI_READONLY; ++ ++ dev = i->i_bdev->bd_disk->private_data; ++ if (ubiblks[dev->devnum]) { ++ ubiblks[dev->devnum]->count++; ++ ubi_open_blkdev(ubi_num, vol_id, mode); ++ printk("%s: increase use count\n",__FUNCTION__); ++ return 0; ++ } ++ ++ desc = ubi_open_volume(ubi_num, vol_id, mode); ++ if (IS_ERR(desc)) ++ return PTR_ERR(desc); ++ ++ desc->vol->bdev_mode = mode; ++ dev->uv = desc; ++ ++ subpage_shift = ffs(UBIBLK_SECTOR_SIZE)-1; ++ ret = ubiblk_init_vol(dev->devnum, desc); ++ return ret; ++} ++ ++static int ubiblk_release(struct ubi_blktrans_dev *ubd) ++{ ++ int dev = ubd->devnum; ++ struct ubiblk_dev *ubiblk = ubiblks[dev]; ++ struct ubi_device *ubi = ubiblk->uv->vol->ubi; ++ ++ mutex_lock(&ubiblk->cache_mutex); ++ ubiblk_flush_writecache(ubiblk); ++ mutex_unlock(&ubiblk->cache_mutex); ++ ++ ubiblk->count --; ++ if (!ubiblk->count) { ++ /* It was the last usage. Free the device */ ++ ubiblks[dev] = NULL; ++ ++ if (ubi->mtd->sync) ++ ubi->mtd->sync(ubi->mtd); ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(ubiblk->write_cache); ++ kfree(ubiblk->read_cache); ++#else ++ vfree(ubiblk->write_cache); ++ vfree(ubiblk->read_cache); ++#endif ++ kfree(ubiblk); ++ ++ ubi_close_volume(ubiblk->uv); ++ return 0; ++ } ++ else{ ++ printk("%s: decrease use count\n",__FUNCTION__); ++ ubi_close_blkdev(ubiblk->uv); ++ return 0; ++ } ++ return 1; ++} ++static int ubiblk_flush(struct ubi_blktrans_dev *dev) ++{ ++ struct ubiblk_dev *ubiblk = ubiblks[dev->devnum]; ++ struct ubi_device *ubi = ubiblk->uv->vol->ubi; ++ ++ mutex_lock(&ubiblk->cache_mutex); ++ ubiblk_flush_writecache(ubiblk); ++ mutex_unlock(&ubiblk->cache_mutex); ++ ++ if (ubi->mtd->sync) ++ ubi->mtd->sync(ubi->mtd); ++ return 0; ++} ++ ++void ubiblk_add_vol_dev(struct ubi_blktrans_ops *tr, struct ubi_volume *vol) ++{ ++ struct ubi_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return; ++ ++ dev->devnum = vol->vol_id; ++ dev->size = vol->used_bytes >> 9; ++ dev->tr = tr; ++ ++ if (vol->bdev_mode == UBI_READONLY) ++ dev->readonly = 1; ++ ++ vol->ubi->bdev_major = tr->major; ++ ++ add_ubi_blktrans_dev(dev); ++} ++ ++void ubiblk_remove_vol_dev(struct ubi_blktrans_dev *dev) ++{ ++ del_ubi_blktrans_dev(dev); ++ kfree(dev); ++} ++ ++static int ubiblk_getgeo(struct ubi_blktrans_dev *dev, struct hd_geometry *geo) ++{ ++ memset(geo, 0, sizeof(*geo)); ++ geo->heads = 4; ++ geo->sectors = 16; ++ geo->cylinders = dev->size/(4*16); ++ return 0; ++} ++ ++static struct ubi_blktrans_ops ubiblk_tr = { ++ .name = "ubiblock", ++ .major = 0, ++ .part_bits = 0, ++ .blksize = UBIBLK_SECTOR_SIZE, ++ .open = ubiblk_open, ++ .release = ubiblk_release, ++ .readsect = ubiblk_readsect, ++ .writesect = ubiblk_writesect, ++ .getgeo = ubiblk_getgeo, ++ .flush = ubiblk_flush, ++ .add_vol = ubiblk_add_vol_dev, ++ .remove_vol = ubiblk_remove_vol_dev, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init init_ubiblock(void) ++{ ++ return register_ubi_blktrans(&ubiblk_tr); ++} ++ ++static void __exit cleanup_ubiblock(void) ++{ ++ deregister_ubi_blktrans(&ubiblk_tr); ++} ++ ++module_init(init_ubiblock); ++module_exit(cleanup_ubiblock); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Nicolas Pitre , Yurong Tan "); ++MODULE_DESCRIPTION("Caching read/erase/writeback block device emulation access to UBI volumes"); +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/ubiblk.h linux-2.6.24.7/drivers/mtd/ubi/ubiblk.h +--- linux-2.6.24.7.old/drivers/mtd/ubi/ubiblk.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/ubi/ubiblk.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,85 @@ ++/* ++ * (C) 2003 David Woodhouse ++ * (C) 2008 Yurong Tan : borrow from MTD blktrans.h for UBI used ++ * Interface to Linux block layer for UBI 'translation layers'. ++ */ ++ ++#ifndef __UBI_TRANS_H__ ++#define __UBI_TRANS_H__ ++ ++#include ++#include ++#include "ubi.h" ++ ++struct hd_geometry; ++struct ubi_volume_desc; ++struct ubi_blktrans_ops; ++struct file; ++struct inode; ++ ++struct ubiblk_dev { ++ struct ubi_volume_desc *uv; ++ int count; ++ struct mutex cache_mutex; ++ unsigned short vbw; //virt block number of write cache ++ unsigned short vbr; //virt block number of read cache ++ ++ unsigned char *write_cache; ++ unsigned char *page_sts; ++ unsigned char *subpage_sts; ++ ++ unsigned char *read_cache; ++ enum { STATE_UNUSED, STATE_USED } read_cache_state, write_cache_state; ++}; ++ ++struct ubi_blktrans_dev { ++ struct ubi_blktrans_ops *tr; ++ struct list_head list; ++ struct ubi_volume_desc *uv; ++ struct mutex lock; ++ int devnum; ++ unsigned long size; ++ int readonly; ++ void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */ ++}; ++ ++struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */ ++ ++struct ubi_blktrans_ops { ++ char *name; ++ int major; ++ int part_bits; ++ int blksize; ++ int blkshift; ++ ++ /* Access functions */ ++ int (*readsect)(struct ubi_blktrans_dev *dev, ++ unsigned long block, char *buffer); ++ int (*writesect)(struct ubi_blktrans_dev *dev, ++ unsigned long block, char *buffer); ++ ++ /* Block layer ioctls */ ++ int (*getgeo)(struct ubi_blktrans_dev *dev, struct hd_geometry *geo); ++ int (*flush)(struct ubi_blktrans_dev *dev); ++ ++ /* Called with mtd_table_mutex held; no race with add/remove */ ++ int (*open)(struct inode *i, struct file *f); ++ int (*release)(struct ubi_blktrans_dev *dev); ++ ++ /* Called on {de,}registration and on subsequent addition/removal ++ of devices, with mtd_table_mutex held. */ ++ void (*add_vol)(struct ubi_blktrans_ops *tr, struct ubi_volume *vol); ++ void (*remove_vol)(struct ubi_blktrans_dev *dev); ++ ++ struct list_head devs; ++ struct list_head list; ++ struct module *owner; ++ ++ struct ubi_blkcore_priv *blkcore_priv; ++}; ++ ++extern int add_ubi_blktrans_dev(struct ubi_blktrans_dev *new); ++extern int del_ubi_blktrans_dev(struct ubi_blktrans_dev *old); ++extern int register_ubi_blktrans(struct ubi_blktrans_ops *tr); ++extern int deregister_ubi_blktrans(struct ubi_blktrans_ops *tr); ++#endif /* __UBI_TRANS_H__ */ +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/upd.c linux-2.6.24.7/drivers/mtd/ubi/upd.c +--- linux-2.6.24.7.old/drivers/mtd/ubi/upd.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/upd.c 2009-04-12 18:13:57.000000000 +0200 +@@ -22,7 +22,8 @@ + */ + + /* +- * This file contains implementation of the volume update functionality. ++ * This file contains implementation of the volume update and atomic LEB change ++ * functionality. + * + * The update operation is based on the per-volume update marker which is + * stored in the volume table. The update marker is set before the update +@@ -45,29 +46,31 @@ + /** + * set_update_marker - set update marker. + * @ubi: UBI device description object +- * @vol_id: volume ID ++ * @vol: volume description object + * +- * This function sets the update marker flag for volume @vol_id. Returns zero ++ * This function sets the update marker flag for volume @vol. Returns zero + * in case of success and a negative error code in case of failure. + */ +-static int set_update_marker(struct ubi_device *ubi, int vol_id) ++static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol) + { + int err; + struct ubi_vtbl_record vtbl_rec; +- struct ubi_volume *vol = ubi->volumes[vol_id]; + +- dbg_msg("set update marker for volume %d", vol_id); ++ dbg_msg("set update marker for volume %d", vol->vol_id); + + if (vol->upd_marker) { +- ubi_assert(ubi->vtbl[vol_id].upd_marker); ++ ubi_assert(ubi->vtbl[vol->vol_id].upd_marker); + dbg_msg("already set"); + return 0; + } + +- memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); ++ memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], ++ sizeof(struct ubi_vtbl_record)); + vtbl_rec.upd_marker = 1; + +- err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); ++ mutex_lock(&ubi->volumes_mutex); ++ err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); ++ mutex_unlock(&ubi->volumes_mutex); + vol->upd_marker = 1; + return err; + } +@@ -75,23 +78,24 @@ + /** + * clear_update_marker - clear update marker. + * @ubi: UBI device description object +- * @vol_id: volume ID ++ * @vol: volume description object + * @bytes: new data size in bytes + * +- * This function clears the update marker for volume @vol_id, sets new volume ++ * This function clears the update marker for volume @vol, sets new volume + * data size and clears the "corrupted" flag (static volumes only). Returns + * zero in case of success and a negative error code in case of failure. + */ +-static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long bytes) ++static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol, ++ long long bytes) + { + int err; + uint64_t tmp; + struct ubi_vtbl_record vtbl_rec; +- struct ubi_volume *vol = ubi->volumes[vol_id]; + +- dbg_msg("clear update marker for volume %d", vol_id); ++ dbg_msg("clear update marker for volume %d", vol->vol_id); + +- memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); ++ memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], ++ sizeof(struct ubi_vtbl_record)); + ubi_assert(vol->upd_marker && vtbl_rec.upd_marker); + vtbl_rec.upd_marker = 0; + +@@ -106,7 +110,9 @@ + vol->last_eb_bytes = vol->usable_leb_size; + } + +- err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); ++ mutex_lock(&ubi->volumes_mutex); ++ err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); ++ mutex_unlock(&ubi->volumes_mutex); + vol->upd_marker = 0; + return err; + } +@@ -114,35 +120,36 @@ + /** + * ubi_start_update - start volume update. + * @ubi: UBI device description object +- * @vol_id: volume ID ++ * @vol: volume description object + * @bytes: update bytes + * + * This function starts volume update operation. If @bytes is zero, the volume + * is just wiped out. Returns zero in case of success and a negative error code + * in case of failure. + */ +-int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) ++int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, ++ long long bytes) + { + int i, err; + uint64_t tmp; +- struct ubi_volume *vol = ubi->volumes[vol_id]; + +- dbg_msg("start update of volume %d, %llu bytes", vol_id, bytes); ++ dbg_msg("start update of volume %d, %llu bytes", vol->vol_id, bytes); ++ ubi_assert(!vol->updating && !vol->changing_leb); + vol->updating = 1; + +- err = set_update_marker(ubi, vol_id); ++ err = set_update_marker(ubi, vol); + if (err) + return err; + + /* Before updating - wipe out the volume */ + for (i = 0; i < vol->reserved_pebs; i++) { +- err = ubi_eba_unmap_leb(ubi, vol_id, i); ++ err = ubi_eba_unmap_leb(ubi, vol, i); + if (err) + return err; + } + + if (bytes == 0) { +- err = clear_update_marker(ubi, vol_id, 0); ++ err = clear_update_marker(ubi, vol, 0); + if (err) + return err; + err = ubi_wl_flush(ubi); +@@ -150,7 +157,11 @@ + vol->updating = 0; + } + ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ vol->upd_buf = kmalloc(ubi->leb_size, GFP_KERNEL); ++#else + vol->upd_buf = vmalloc(ubi->leb_size); ++#endif + if (!vol->upd_buf) + return -ENOMEM; + +@@ -163,9 +174,46 @@ + } + + /** ++ * ubi_start_leb_change - start atomic LEB change. ++ * @ubi: UBI device description object ++ * @vol: volume description object ++ * @req: operation request ++ * ++ * This function starts atomic LEB change operation. Returns zero in case of ++ * success and a negative error code in case of failure. ++ */ ++int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, ++ const struct ubi_leb_change_req *req) ++{ ++ ubi_assert(!vol->updating && !vol->changing_leb); ++ ++ dbg_msg("start changing LEB %d:%d, %u bytes", ++ vol->vol_id, req->lnum, req->bytes); ++ if (req->bytes == 0) ++ return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0, ++ req->dtype); ++ ++ vol->upd_bytes = req->bytes; ++ vol->upd_received = 0; ++ vol->changing_leb = 1; ++ vol->ch_lnum = req->lnum; ++ vol->ch_dtype = req->dtype; ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ vol->upd_buf = kmalloc(req->bytes, GFP_KERNEL); ++#else ++ vol->upd_buf = vmalloc(req->bytes); ++#endif ++ if (!vol->upd_buf) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++/** + * write_leb - write update data. + * @ubi: UBI device description object +- * @vol_id: volume ID ++ * @vol: volume description object + * @lnum: logical eraseblock number + * @buf: data to write + * @len: data size +@@ -191,26 +239,22 @@ + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +-static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, +- int len, int used_ebs) ++static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, ++ void *buf, int len, int used_ebs) + { +- int err, l; +- struct ubi_volume *vol = ubi->volumes[vol_id]; ++ int err; + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { +- l = ALIGN(len, ubi->min_io_size); +- memset(buf + len, 0xFF, l - len); ++ len = ALIGN(len, ubi->min_io_size); ++ memset(buf + len, 0xFF, len - len); + +- l = ubi_calc_data_len(ubi, buf, l); +- if (l == 0) { ++ len = ubi_calc_data_len(ubi, buf, len); ++ if (len == 0) { + dbg_msg("all %d bytes contain 0xFF - skip", len); + return 0; + } +- if (len != l) +- dbg_msg("skip last %d bytes (0xFF)", len - l); + +- err = ubi_eba_write_leb(ubi, vol_id, lnum, buf, 0, l, +- UBI_UNKNOWN); ++ err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN); + } else { + /* + * When writing static volume, and this is the last logical +@@ -222,7 +266,7 @@ + * contain zeros, not random trash. + */ + memset(buf + len, 0, vol->usable_leb_size - len); +- err = ubi_eba_write_leb_st(ubi, vol_id, lnum, buf, len, ++ err = ubi_eba_write_leb_st(ubi, vol, lnum, buf, len, + UBI_UNKNOWN, used_ebs); + } + +@@ -236,16 +280,15 @@ + * @count: how much bytes to write + * + * This function writes more data to the volume which is being updated. It may +- * be called arbitrary number of times until all of the update data arrive. +- * This function returns %0 in case of success, number of bytes written during +- * the last call if the whole volume update was successfully finished, and a ++ * be called arbitrary number of times until all the update data arriveis. This ++ * function returns %0 in case of success, number of bytes written during the ++ * last call if the whole volume update has been successfully finished, and a + * negative error code in case of failure. + */ +-int ubi_more_update_data(struct ubi_device *ubi, int vol_id, ++int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, + const void __user *buf, int count) + { + uint64_t tmp; +- struct ubi_volume *vol = ubi->volumes[vol_id]; + int lnum, offs, err = 0, len, to_write = count; + + dbg_msg("write %d of %lld bytes, %lld already passed", +@@ -290,8 +333,8 @@ + * is the last chunk, it's time to flush the buffer. + */ + ubi_assert(flush_len <= vol->usable_leb_size); +- err = write_leb(ubi, vol_id, lnum, vol->upd_buf, +- flush_len, vol->upd_ebs); ++ err = write_leb(ubi, vol, lnum, vol->upd_buf, flush_len, ++ vol->upd_ebs); + if (err) + return err; + } +@@ -312,37 +355,102 @@ + else + len = count; + +- err = copy_from_user(vol->upd_buf, buf, len); ++ err= copy_from_user(&lnum, buf, sizeof(lnum)); ++ if (err) ++ return -EFAULT; ++ err = copy_from_user(vol->upd_buf, &(((char*)buf)[sizeof(unsigned int)]), len); + if (err) + return -EFAULT; + + if (len == vol->usable_leb_size || + vol->upd_received + len == vol->upd_bytes) { +- err = write_leb(ubi, vol_id, lnum, vol->upd_buf, len, +- vol->upd_ebs); ++ err = write_leb(ubi, vol, lnum, vol->upd_buf, ++ len, vol->upd_ebs); + if (err) + break; + } + + vol->upd_received += len; + count -= len; +- lnum += 1; ++// lnum += 1; + buf += len; + } + + ubi_assert(vol->upd_received <= vol->upd_bytes); + if (vol->upd_received == vol->upd_bytes) { + /* The update is finished, clear the update marker */ +- err = clear_update_marker(ubi, vol_id, vol->upd_bytes); ++ err = clear_update_marker(ubi, vol, vol->upd_bytes); + if (err) + return err; + err = ubi_wl_flush(ubi); + if (err == 0) { ++ vol->updating = 0; + err = to_write; ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(vol->upd_buf); ++#else + vfree(vol->upd_buf); +- vol->updating = 0; ++#endif + } + } + + return err; + } ++ ++/** ++ * ubi_more_leb_change_data - accept more data for atomic LEB change. ++ * @vol: volume description object ++ * @buf: write data (user-space memory buffer) ++ * @count: how much bytes to write ++ * ++ * This function accepts more data to the volume which is being under the ++ * "atomic LEB change" operation. It may be called arbitrary number of times ++ * until all data arrives. This function returns %0 in case of success, number ++ * of bytes written during the last call if the whole "atomic LEB change" ++ * operation has been successfully finished, and a negative error code in case ++ * of failure. ++ */ ++int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, ++ const void __user *buf, int count) ++{ ++ int err; ++ ++ dbg_msg("write %d of %lld bytes, %lld already passed", ++ count, vol->upd_bytes, vol->upd_received); ++ ++ if (ubi->ro_mode) ++ return -EROFS; ++ ++ if (vol->upd_received + count > vol->upd_bytes) ++ count = vol->upd_bytes - vol->upd_received; ++ ++ err = copy_from_user(vol->upd_buf + vol->upd_received, buf, count); ++ if (err) ++ return -EFAULT; ++ ++ vol->upd_received += count; ++ ++ if (vol->upd_received == vol->upd_bytes) { ++ int len = ALIGN((int)vol->upd_bytes, ubi->min_io_size); ++ ++ memset(vol->upd_buf + vol->upd_bytes, 0xFF, len - vol->upd_bytes); ++ len = ubi_calc_data_len(ubi, vol->upd_buf, len); ++ err = ubi_eba_atomic_leb_change(ubi, vol, vol->ch_lnum, ++ vol->upd_buf, len, UBI_UNKNOWN); ++ if (err) ++ return err; ++ } ++ ++ ubi_assert(vol->upd_received <= vol->upd_bytes); ++ if (vol->upd_received == vol->upd_bytes) { ++ vol->changing_leb = 0; ++ err = count; ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(vol->upd_buf); ++#else ++ vfree(vol->upd_buf); ++#endif ++ } ++ ++ return err; ++} +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/vmt.c linux-2.6.24.7/drivers/mtd/ubi/vmt.c +--- linux-2.6.24.7.old/drivers/mtd/ubi/vmt.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/vmt.c 2009-04-12 18:13:57.000000000 +0200 +@@ -63,21 +63,30 @@ + * B. process 2 removes volume Y; + * C. process 1 starts reading the //class/ubi/ubiX_Y/reserved_ebs file; + * +- * What we want to do in a situation like that is to return error when the file +- * is read. This is done by means of the 'removed' flag and the 'vol_lock' of +- * the UBI volume description object. ++ * In this situation, this function will return %-ENODEV because it will find ++ * out that the volume was removed from the @ubi->volumes array. + */ + static ssize_t vol_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf) + { + int ret; + struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); ++ struct ubi_device *ubi; + +- spin_lock(&vol->ubi->volumes_lock); +- if (vol->removed) { +- spin_unlock(&vol->ubi->volumes_lock); ++ ubi = ubi_get_device(vol->ubi->ubi_num); ++ if (!ubi) ++ return -ENODEV; ++ ++ spin_lock(&ubi->volumes_lock); ++ if (!ubi->volumes[vol->vol_id]) { ++ spin_unlock(&ubi->volumes_lock); ++ ubi_put_device(ubi); + return -ENODEV; + } ++ /* Take a reference to prevent volume removal */ ++ vol->ref_count += 1; ++ spin_unlock(&ubi->volumes_lock); ++ + if (attr == &attr_vol_reserved_ebs) + ret = sprintf(buf, "%d\n", vol->reserved_pebs); + else if (attr == &attr_vol_type) { +@@ -94,15 +103,22 @@ + ret = sprintf(buf, "%d\n", vol->corrupted); + else if (attr == &attr_vol_alignment) + ret = sprintf(buf, "%d\n", vol->alignment); +- else if (attr == &attr_vol_usable_eb_size) { ++ else if (attr == &attr_vol_usable_eb_size) + ret = sprintf(buf, "%d\n", vol->usable_leb_size); +- } else if (attr == &attr_vol_data_bytes) ++ else if (attr == &attr_vol_data_bytes) + ret = sprintf(buf, "%lld\n", vol->used_bytes); + else if (attr == &attr_vol_upd_marker) + ret = sprintf(buf, "%d\n", vol->upd_marker); + else +- BUG(); +- spin_unlock(&vol->ubi->volumes_lock); ++ /* This must be a bug */ ++ ret = -EINVAL; ++ ++ /* We've done the operation, drop volume and UBI device references */ ++ spin_lock(&ubi->volumes_lock); ++ vol->ref_count -= 1; ++ ubi_assert(vol->ref_count >= 0); ++ spin_unlock(&ubi->volumes_lock); ++ ubi_put_device(ubi); + return ret; + } + +@@ -110,7 +126,7 @@ + static void vol_release(struct device *dev) + { + struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); +- ubi_assert(vol->removed); ++ + kfree(vol); + } + +@@ -152,9 +168,7 @@ + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_upd_marker); +- if (err) +- return err; +- return 0; ++ return err; + } + + /** +@@ -180,16 +194,18 @@ + * @req: volume creation request + * + * This function creates volume described by @req. If @req->vol_id id +- * %UBI_VOL_NUM_AUTO, this function automatically assigne ID to the new volume ++ * %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume + * and saves it in @req->vol_id. Returns zero in case of success and a negative +- * error code in case of failure. ++ * error code in case of failure. Note, the caller has to have the ++ * @ubi->volumes_mutex locked. + */ + int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) + { +- int i, err, vol_id = req->vol_id; ++ int i, err, vol_id = req->vol_id, dont_free = 0; + struct ubi_volume *vol; + struct ubi_vtbl_record vtbl_rec; + uint64_t bytes; ++ dev_t dev; + + if (ubi->ro_mode) + return -EROFS; +@@ -199,7 +215,6 @@ + return -ENOMEM; + + spin_lock(&ubi->volumes_lock); +- + if (vol_id == UBI_VOL_NUM_AUTO) { + /* Find unused volume ID */ + dbg_msg("search for vacant volume ID"); +@@ -252,6 +267,7 @@ + } + ubi->avail_pebs -= vol->reserved_pebs; + ubi->rsvd_pebs += vol->reserved_pebs; ++ spin_unlock(&ubi->volumes_lock); + + vol->vol_id = vol_id; + vol->alignment = req->alignment; +@@ -259,10 +275,7 @@ + vol->vol_type = req->vol_type; + vol->name_len = req->name_len; + memcpy(vol->name, req->name, vol->name_len + 1); +- vol->exclusive = 1; + vol->ubi = ubi; +- ubi->volumes[vol_id] = vol; +- spin_unlock(&ubi->volumes_lock); + + /* + * Finish all pending erases because there may be some LEBs belonging +@@ -299,9 +312,10 @@ + /* Register character device for the volume */ + cdev_init(&vol->cdev, &ubi_vol_cdev_operations); + vol->cdev.owner = THIS_MODULE; +- err = cdev_add(&vol->cdev, MKDEV(ubi->major, vol_id + 1), 1); ++ dev = MKDEV(MAJOR(ubi->cdev.dev), vol_id + 1); ++ err = cdev_add(&vol->cdev, dev, 1); + if (err) { +- ubi_err("cannot add character device for volume %d", vol_id); ++ ubi_err("cannot add character device"); + goto out_mapping; + } + +@@ -311,12 +325,15 @@ + + vol->dev.release = vol_release; + vol->dev.parent = &ubi->dev; +- vol->dev.devt = MKDEV(ubi->major, vol->vol_id + 1); ++ vol->dev.devt = dev; + vol->dev.class = ubi_class; ++ + sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); + err = device_register(&vol->dev); +- if (err) ++ if (err) { ++ ubi_err("cannot register device"); + goto out_gluebi; ++ } + + err = volume_sysfs_init(ubi, vol); + if (err) +@@ -339,15 +356,29 @@ + goto out_sysfs; + + spin_lock(&ubi->volumes_lock); ++ ubi->volumes[vol_id] = vol; + ubi->vol_count += 1; +- vol->exclusive = 0; + spin_unlock(&ubi->volumes_lock); + + paranoid_check_volumes(ubi); + return 0; + ++out_sysfs: ++ /* ++ * We have registered our device, we should not free the volume* ++ * description object in this function in case of an error - it is ++ * freed by the release function. ++ * ++ * Get device reference to prevent the release function from being ++ * called just after sysfs has been closed. ++ */ ++ dont_free = 1; ++ get_device(&vol->dev); ++ volume_sysfs_close(vol); + out_gluebi: +- err = ubi_destroy_gluebi(vol); ++ if (ubi_destroy_gluebi(vol)) ++ dbg_err("cannot destroy gluebi for volume %d:%d", ++ ubi->ubi_num, vol_id); + out_cdev: + cdev_del(&vol->cdev); + out_mapping: +@@ -356,26 +387,13 @@ + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs -= vol->reserved_pebs; + ubi->avail_pebs += vol->reserved_pebs; +- ubi->volumes[vol_id] = NULL; + out_unlock: + spin_unlock(&ubi->volumes_lock); +- kfree(vol); +- return err; +- +- /* +- * We are registered, so @vol is destroyed in the release function and +- * we have to de-initialize differently. +- */ +-out_sysfs: +- err = ubi_destroy_gluebi(vol); +- cdev_del(&vol->cdev); +- kfree(vol->eba_tbl); +- spin_lock(&ubi->volumes_lock); +- ubi->rsvd_pebs -= vol->reserved_pebs; +- ubi->avail_pebs += vol->reserved_pebs; +- ubi->volumes[vol_id] = NULL; +- spin_unlock(&ubi->volumes_lock); +- volume_sysfs_close(vol); ++ if (dont_free) ++ put_device(&vol->dev); ++ else ++ kfree(vol); ++ ubi_err("cannot create volume %d, error %d", vol_id, err); + return err; + } + +@@ -385,7 +403,8 @@ + * + * This function removes volume described by @desc. The volume has to be opened + * in "exclusive" mode. Returns zero in case of success and a negative error +- * code in case of failure. ++ * code in case of failure. The caller has to have the @ubi->volumes_mutex ++ * locked. + */ + int ubi_remove_volume(struct ubi_volume_desc *desc) + { +@@ -400,30 +419,36 @@ + if (ubi->ro_mode) + return -EROFS; + ++ spin_lock(&ubi->volumes_lock); ++ if (vol->ref_count > 1) { ++ /* ++ * The volume is busy, probably someone is reading one of its ++ * sysfs files. ++ */ ++ err = -EBUSY; ++ goto out_unlock; ++ } ++ ubi->volumes[vol_id] = NULL; ++ spin_unlock(&ubi->volumes_lock); ++ + err = ubi_destroy_gluebi(vol); + if (err) +- return err; ++ goto out_err; + + err = ubi_change_vtbl_record(ubi, vol_id, NULL); + if (err) +- return err; ++ goto out_err; + + for (i = 0; i < vol->reserved_pebs; i++) { +- err = ubi_eba_unmap_leb(ubi, vol_id, i); ++ err = ubi_eba_unmap_leb(ubi, vol, i); + if (err) +- return err; ++ goto out_err; + } + +- spin_lock(&ubi->volumes_lock); +- vol->removed = 1; +- ubi->volumes[vol_id] = NULL; +- spin_unlock(&ubi->volumes_lock); +- + kfree(vol->eba_tbl); + vol->eba_tbl = NULL; + cdev_del(&vol->cdev); + volume_sysfs_close(vol); +- kfree(desc); + + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs -= reserved_pebs; +@@ -441,8 +466,15 @@ + spin_unlock(&ubi->volumes_lock); + + paranoid_check_volumes(ubi); +- module_put(THIS_MODULE); + return 0; ++ ++out_err: ++ ubi_err("cannot remove volume %d, error %d", vol_id, err); ++ spin_lock(&ubi->volumes_lock); ++ ubi->volumes[vol_id] = vol; ++out_unlock: ++ spin_unlock(&ubi->volumes_lock); ++ return err; + } + + /** +@@ -450,8 +482,9 @@ + * @desc: volume descriptor + * @reserved_pebs: new size in physical eraseblocks + * +- * This function returns zero in case of success, and a negative error code in +- * case of failure. ++ * This function re-sizes the volume and returns zero in case of success, and a ++ * negative error code in case of failure. The caller has to have the ++ * @ubi->volumes_mutex locked. + */ + int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) + { +@@ -466,8 +499,6 @@ + + dbg_msg("re-size volume %d to from %d to %d PEBs", + vol_id, vol->reserved_pebs, reserved_pebs); +- ubi_assert(desc->mode == UBI_EXCLUSIVE); +- ubi_assert(vol == ubi->volumes[vol_id]); + + if (vol->vol_type == UBI_STATIC_VOLUME && + reserved_pebs < vol->used_ebs) { +@@ -487,6 +518,14 @@ + for (i = 0; i < reserved_pebs; i++) + new_mapping[i] = UBI_LEB_UNMAPPED; + ++ spin_lock(&ubi->volumes_lock); ++ if (vol->ref_count > 1) { ++ spin_unlock(&ubi->volumes_lock); ++ err = -EBUSY; ++ goto out_free; ++ } ++ spin_unlock(&ubi->volumes_lock); ++ + /* Reserve physical eraseblocks */ + pebs = reserved_pebs - vol->reserved_pebs; + if (pebs > 0) { +@@ -516,7 +555,7 @@ + + if (pebs < 0) { + for (i = 0; i < -pebs; i++) { +- err = ubi_eba_unmap_leb(ubi, vol_id, reserved_pebs + i); ++ err = ubi_eba_unmap_leb(ubi, vol, reserved_pebs + i); + if (err) + goto out_acc; + } +@@ -565,27 +604,28 @@ + /** + * ubi_add_volume - add volume. + * @ubi: UBI device description object +- * @vol_id: volume ID ++ * @vol: volume description object + * +- * This function adds an existin volume and initializes all its data +- * structures. Returnes zero in case of success and a negative error code in ++ * This function adds an existing volume and initializes all its data ++ * structures. Returns zero in case of success and a negative error code in + * case of failure. + */ +-int ubi_add_volume(struct ubi_device *ubi, int vol_id) ++int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) + { +- int err; +- struct ubi_volume *vol = ubi->volumes[vol_id]; ++ int err, vol_id = vol->vol_id; ++ dev_t dev; + + dbg_msg("add volume %d", vol_id); + ubi_dbg_dump_vol_info(vol); +- ubi_assert(vol); + + /* Register character device for the volume */ + cdev_init(&vol->cdev, &ubi_vol_cdev_operations); + vol->cdev.owner = THIS_MODULE; +- err = cdev_add(&vol->cdev, MKDEV(ubi->major, vol->vol_id + 1), 1); ++ dev = MKDEV(MAJOR(ubi->cdev.dev), vol->vol_id + 1); ++ err = cdev_add(&vol->cdev, dev, 1); + if (err) { +- ubi_err("cannot add character device for volume %d", vol_id); ++ ubi_err("cannot add character device for volume %d, error %d", ++ vol_id, err); + return err; + } + +@@ -595,7 +635,7 @@ + + vol->dev.release = vol_release; + vol->dev.parent = &ubi->dev; +- vol->dev.devt = MKDEV(ubi->major, vol->vol_id + 1); ++ vol->dev.devt = dev; + vol->dev.class = ubi_class; + sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); + err = device_register(&vol->dev); +@@ -623,22 +663,19 @@ + /** + * ubi_free_volume - free volume. + * @ubi: UBI device description object +- * @vol_id: volume ID ++ * @vol: volume description object + * +- * This function frees all resources for volume @vol_id but does not remove it. ++ * This function frees all resources for volume @vol but does not remove it. + * Used only when the UBI device is detached. + */ +-void ubi_free_volume(struct ubi_device *ubi, int vol_id) ++void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol) + { + int err; +- struct ubi_volume *vol = ubi->volumes[vol_id]; + +- dbg_msg("free volume %d", vol_id); +- ubi_assert(vol); ++ dbg_msg("free volume %d", vol->vol_id); + +- vol->removed = 1; ++ ubi->volumes[vol->vol_id] = NULL; + err = ubi_destroy_gluebi(vol); +- ubi->volumes[vol_id] = NULL; + cdev_del(&vol->cdev); + volume_sysfs_close(vol); + } +@@ -708,11 +745,6 @@ + goto fail; + } + +- if (vol->upd_marker != 0 && vol->upd_marker != 1) { +- ubi_err("bad upd_marker"); +- goto fail; +- } +- + if (vol->upd_marker && vol->corrupted) { + dbg_err("update marker and corrupted simultaneously"); + goto fail; +@@ -747,7 +779,7 @@ + + n = (long long)vol->used_ebs * vol->usable_leb_size; + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { +- if (vol->corrupted != 0) { ++ if (vol->corrupted) { + ubi_err("corrupted dynamic volume"); + goto fail; + } +@@ -764,10 +796,6 @@ + goto fail; + } + } else { +- if (vol->corrupted != 0 && vol->corrupted != 1) { +- ubi_err("bad corrupted"); +- goto fail; +- } + if (vol->used_ebs < 0 || vol->used_ebs > vol->reserved_pebs) { + ubi_err("bad used_ebs"); + goto fail; +@@ -820,9 +848,7 @@ + { + int i; + +- mutex_lock(&ubi->vtbl_mutex); + for (i = 0; i < ubi->vtbl_slots; i++) + paranoid_check_volume(ubi, i); +- mutex_unlock(&ubi->vtbl_mutex); + } + #endif +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/vtbl.c linux-2.6.24.7/drivers/mtd/ubi/vtbl.c +--- linux-2.6.24.7.old/drivers/mtd/ubi/vtbl.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/vtbl.c 2009-04-12 18:13:57.000000000 +0200 +@@ -86,8 +86,10 @@ + { + int i, err; + uint32_t crc; ++ struct ubi_volume *layout_vol; + + ubi_assert(idx >= 0 && idx < ubi->vtbl_slots); ++ layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)]; + + if (!vtbl_rec) + vtbl_rec = &empty_vtbl_record; +@@ -96,31 +98,25 @@ + vtbl_rec->crc = cpu_to_be32(crc); + } + +- mutex_lock(&ubi->vtbl_mutex); + memcpy(&ubi->vtbl[idx], vtbl_rec, sizeof(struct ubi_vtbl_record)); + for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { +- err = ubi_eba_unmap_leb(ubi, UBI_LAYOUT_VOL_ID, i); +- if (err) { +- mutex_unlock(&ubi->vtbl_mutex); ++ err = ubi_eba_unmap_leb(ubi, layout_vol, i); ++ if (err) + return err; +- } +- err = ubi_eba_write_leb(ubi, UBI_LAYOUT_VOL_ID, i, ubi->vtbl, 0, ++ ++ err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0, + ubi->vtbl_size, UBI_LONGTERM); +- if (err) { +- mutex_unlock(&ubi->vtbl_mutex); ++ if (err) + return err; +- } + } + + paranoid_vtbl_check(ubi); +- mutex_unlock(&ubi->vtbl_mutex); +- return ubi_wl_flush(ubi); ++ return 0; + } + + /** +- * vol_til_check - check if volume table is not corrupted and contains sensible +- * data. +- * ++ * vtbl_check - check if volume table is not corrupted and contains sensible ++ * data. + * @ubi: UBI device description object + * @vtbl: volume table + * +@@ -273,7 +269,7 @@ + * this volume table copy was found during scanning. It has to be wiped + * out. + */ +- sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOL_ID); ++ sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID); + if (sv) + old_seb = ubi_scan_find_seb(sv, copy); + +@@ -285,7 +281,7 @@ + } + + vid_hdr->vol_type = UBI_VID_DYNAMIC; +- vid_hdr->vol_id = cpu_to_be32(UBI_LAYOUT_VOL_ID); ++ vid_hdr->vol_id = cpu_to_be32(UBI_LAYOUT_VOLUME_ID); + vid_hdr->compat = UBI_LAYOUT_VOLUME_COMPAT; + vid_hdr->data_size = vid_hdr->used_ebs = + vid_hdr->data_pad = cpu_to_be32(0); +@@ -378,7 +374,11 @@ + + /* Read both LEB 0 and LEB 1 into memory */ + ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) { ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ leb[seb->lnum] = kmalloc(ubi->vtbl_size, GFP_KERNEL); ++#else + leb[seb->lnum] = vmalloc(ubi->vtbl_size); ++#endif + if (!leb[seb->lnum]) { + err = -ENOMEM; + goto out_free; +@@ -414,7 +414,11 @@ + } + + /* Both LEB 1 and LEB 2 are OK and consistent */ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(leb[1]); ++#else + vfree(leb[1]); ++#endif + return leb[0]; + } else { + /* LEB 0 is corrupted or does not exist */ +@@ -434,14 +438,23 @@ + if (err) + goto out_free; + ubi_msg("volume table was restored"); +- ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(leb[0]); ++#else + vfree(leb[0]); ++#endif + return leb[1]; + } + + out_free: ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(leb[0]); ++ kfree(leb[1]); ++#else + vfree(leb[0]); + vfree(leb[1]); ++#endif ++ + return ERR_PTR(err); + } + +@@ -459,7 +472,11 @@ + int i; + struct ubi_vtbl_record *vtbl; + ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ vtbl = kmalloc(ubi->vtbl_size, GFP_KERNEL); ++#else + vtbl = vmalloc(ubi->vtbl_size); ++#endif + if (!vtbl) + return ERR_PTR(-ENOMEM); + memset(vtbl, 0, ubi->vtbl_size); +@@ -472,7 +489,11 @@ + + err = create_vtbl(ubi, si, i, vtbl); + if (err) { ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(vtbl); ++#else + vfree(vtbl); ++#endif + return ERR_PTR(err); + } + } +@@ -518,6 +539,18 @@ + vol->name[vol->name_len] = '\0'; + vol->vol_id = i; + ++ if (vtbl[i].flags & UBI_VTBL_AUTORESIZE_FLG) { ++ /* Auto re-size flag may be set only for one volume */ ++ if (ubi->autoresize_vol_id != -1) { ++ ubi_err("more then one auto-resize volume (%d " ++ "and %d)", ubi->autoresize_vol_id, i); ++ kfree(vol); ++ return -EINVAL; ++ } ++ ++ ubi->autoresize_vol_id = i; ++ } ++ + ubi_assert(!ubi->volumes[i]); + ubi->volumes[i] = vol; + ubi->vol_count += 1; +@@ -568,6 +601,7 @@ + vol->last_eb_bytes = sv->last_data_size; + } + ++ /* And add the layout volume */ + vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); + if (!vol) + return -ENOMEM; +@@ -582,7 +616,8 @@ + vol->last_eb_bytes = vol->reserved_pebs; + vol->used_bytes = + (long long)vol->used_ebs * (ubi->leb_size - vol->data_pad); +- vol->vol_id = UBI_LAYOUT_VOL_ID; ++ vol->vol_id = UBI_LAYOUT_VOLUME_ID; ++ vol->ref_count = 1; + + ubi_assert(!ubi->volumes[i]); + ubi->volumes[vol_id2idx(ubi, vol->vol_id)] = vol; +@@ -734,7 +769,7 @@ + ubi->vtbl_size = ubi->vtbl_slots * UBI_VTBL_RECORD_SIZE; + ubi->vtbl_size = ALIGN(ubi->vtbl_size, ubi->min_io_size); + +- sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOL_ID); ++ sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID); + if (!sv) { + /* + * No logical eraseblocks belonging to the layout volume were +@@ -786,7 +821,11 @@ + return 0; + + out_free: ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(ubi->vtbl); ++#else + vfree(ubi->vtbl); ++#endif + for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) + if (ubi->volumes[i]) { + kfree(ubi->volumes[i]); +diff -urN linux-2.6.24.7.old/drivers/mtd/ubi/wl.c linux-2.6.24.7/drivers/mtd/ubi/wl.c +--- linux-2.6.24.7.old/drivers/mtd/ubi/wl.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/mtd/ubi/wl.c 2009-04-12 18:13:57.000000000 +0200 +@@ -117,21 +117,6 @@ + #define WL_MAX_FAILURES 32 + + /** +- * struct ubi_wl_entry - wear-leveling entry. +- * @rb: link in the corresponding RB-tree +- * @ec: erase counter +- * @pnum: physical eraseblock number +- * +- * Each physical eraseblock has a corresponding &struct wl_entry object which +- * may be kept in different RB-trees. +- */ +-struct ubi_wl_entry { +- struct rb_node rb; +- int ec; +- int pnum; +-}; +- +-/** + * struct ubi_wl_prot_entry - PEB protection entry. + * @rb_pnum: link in the @wl->prot.pnum RB-tree + * @rb_aec: link in the @wl->prot.aec RB-tree +@@ -216,9 +201,6 @@ + #define paranoid_check_in_wl_tree(e, root) + #endif + +-/* Slab cache for wear-leveling entries */ +-static struct kmem_cache *wl_entries_slab; +- + /** + * wl_tree_add - add a wear-leveling entry to a WL RB-tree. + * @e: the wear-leveling entry to add +@@ -267,15 +249,26 @@ + int err; + struct ubi_work *wrk; + +- spin_lock(&ubi->wl_lock); ++ cond_resched(); + ++ /* ++ * @ubi->work_sem is used to synchronize with the workers. Workers take ++ * it in read mode, so many of them may be doing works at a time. But ++ * the queue flush code has to be sure the whole queue of works is ++ * done, and it takes the mutex in write mode. ++ */ ++ down_read(&ubi->work_sem); ++ spin_lock(&ubi->wl_lock); + if (list_empty(&ubi->works)) { + spin_unlock(&ubi->wl_lock); ++ up_read(&ubi->work_sem); + return 0; + } + + wrk = list_entry(ubi->works.next, struct ubi_work, list); + list_del(&wrk->list); ++ ubi->works_count -= 1; ++ ubi_assert(ubi->works_count >= 0); + spin_unlock(&ubi->wl_lock); + + /* +@@ -286,11 +279,8 @@ + err = wrk->func(ubi, wrk, 0); + if (err) + ubi_err("work failed with error code %d", err); ++ up_read(&ubi->work_sem); + +- spin_lock(&ubi->wl_lock); +- ubi->works_count -= 1; +- ubi_assert(ubi->works_count >= 0); +- spin_unlock(&ubi->wl_lock); + return err; + } + +@@ -549,8 +539,12 @@ + * prot_tree_del - remove a physical eraseblock from the protection trees + * @ubi: UBI device description object + * @pnum: the physical eraseblock to remove ++ * ++ * This function returns PEB @pnum from the protection trees and returns zero ++ * in case of success and %-ENODEV if the PEB was not found in the protection ++ * trees. + */ +-static void prot_tree_del(struct ubi_device *ubi, int pnum) ++static int prot_tree_del(struct ubi_device *ubi, int pnum) + { + struct rb_node *p; + struct ubi_wl_prot_entry *pe = NULL; +@@ -561,7 +555,7 @@ + pe = rb_entry(p, struct ubi_wl_prot_entry, rb_pnum); + + if (pnum == pe->e->pnum) +- break; ++ goto found; + + if (pnum < pe->e->pnum) + p = p->rb_left; +@@ -569,10 +563,14 @@ + p = p->rb_right; + } + ++ return -ENODEV; ++ ++found: + ubi_assert(pe->e->pnum == pnum); + rb_erase(&pe->rb_aec, &ubi->prot.aec); + rb_erase(&pe->rb_pnum, &ubi->prot.pnum); + kfree(pe); ++ return 0; + } + + /** +@@ -744,7 +742,8 @@ + static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, + int cancel) + { +- int err, put = 0; ++ int err, put = 0, scrubbing = 0, protect = 0; ++ struct ubi_wl_prot_entry *uninitialized_var(pe); + struct ubi_wl_entry *e1, *e2; + struct ubi_vid_hdr *vid_hdr; + +@@ -757,21 +756,17 @@ + if (!vid_hdr) + return -ENOMEM; + ++ mutex_lock(&ubi->move_mutex); + spin_lock(&ubi->wl_lock); ++ ubi_assert(!ubi->move_from && !ubi->move_to); ++ ubi_assert(!ubi->move_to_put); + +- /* +- * Only one WL worker at a time is supported at this implementation, so +- * make sure a PEB is not being moved already. +- */ +- if (ubi->move_to || !ubi->free.rb_node || ++ if (!ubi->free.rb_node || + (!ubi->used.rb_node && !ubi->scrub.rb_node)) { + /* +- * Only one WL worker at a time is supported at this +- * implementation, so if a LEB is already being moved, cancel. +- * +- * No free physical eraseblocks? Well, we cancel wear-leveling +- * then. It will be triggered again when a free physical +- * eraseblock appears. ++ * No free physical eraseblocks? Well, they must be waiting in ++ * the queue to be erased. Cancel movement - it will be ++ * triggered again when a free physical eraseblock appears. + * + * No used physical eraseblocks? They must be temporarily + * protected from being moved. They will be moved to the +@@ -780,10 +775,7 @@ + */ + dbg_wl("cancel WL, a list is empty: free %d, used %d", + !ubi->free.rb_node, !ubi->used.rb_node); +- ubi->wl_scheduled = 0; +- spin_unlock(&ubi->wl_lock); +- ubi_free_vid_hdr(ubi, vid_hdr); +- return 0; ++ goto out_cancel; + } + + if (!ubi->scrub.rb_node) { +@@ -798,27 +790,24 @@ + if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) { + dbg_wl("no WL needed: min used EC %d, max free EC %d", + e1->ec, e2->ec); +- ubi->wl_scheduled = 0; +- spin_unlock(&ubi->wl_lock); +- ubi_free_vid_hdr(ubi, vid_hdr); +- return 0; ++ goto out_cancel; + } + paranoid_check_in_wl_tree(e1, &ubi->used); + rb_erase(&e1->rb, &ubi->used); + dbg_wl("move PEB %d EC %d to PEB %d EC %d", + e1->pnum, e1->ec, e2->pnum, e2->ec); + } else { ++ /* Perform scrubbing */ ++ scrubbing = 1; + e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + paranoid_check_in_wl_tree(e1, &ubi->scrub); +- rb_erase(&e1->rb, &ubi->scrub); ++ rb_erase(&e1->rb, &ubi->scrub); + dbg_wl("scrub PEB %d to PEB %d", e1->pnum, e2->pnum); + } + + paranoid_check_in_wl_tree(e2, &ubi->free); + rb_erase(&e2->rb, &ubi->free); +- ubi_assert(!ubi->move_from && !ubi->move_to); +- ubi_assert(!ubi->move_to_put && !ubi->move_from_put); + ubi->move_from = e1; + ubi->move_to = e2; + spin_unlock(&ubi->wl_lock); +@@ -828,6 +817,10 @@ + * We so far do not know which logical eraseblock our physical + * eraseblock (@e1) belongs to. We have to read the volume identifier + * header first. ++ * ++ * Note, we are protected from this PEB being unmapped and erased. The ++ * 'ubi_wl_put_peb()' would wait for moving to be finished if the PEB ++ * which is being moved was unmapped. + */ + + err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0); +@@ -842,32 +835,51 @@ + * likely have the VID header in place. + */ + dbg_wl("PEB %d has no VID header", e1->pnum); +- err = 0; +- } else { +- ubi_err("error %d while reading VID header from PEB %d", +- err, e1->pnum); +- if (err > 0) +- err = -EIO; ++ goto out_not_moved; + } +- goto error; ++ ++ ubi_err("error %d while reading VID header from PEB %d", ++ err, e1->pnum); ++ if (err > 0) ++ err = -EIO; ++ goto out_error; + } + + err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); + if (err) { +- if (err == UBI_IO_BITFLIPS) +- err = 0; +- goto error; ++ ++ if (err < 0) ++ goto out_error; ++ if (err == 1) ++ goto out_not_moved; ++ ++ /* ++ * For some reason the LEB was not moved - it might be because ++ * the volume is being deleted. We should prevent this PEB from ++ * being selected for wear-levelling movement for some "time", ++ * so put it to the protection tree. ++ */ ++ ++ dbg_wl("cancelled moving PEB %d", e1->pnum); ++ pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_NOFS); ++ if (!pe) { ++ err = -ENOMEM; ++ goto out_error; ++ } ++ ++ protect = 1; + } + + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&ubi->wl_lock); ++ if (protect) ++ prot_tree_add(ubi, e1, pe, protect); + if (!ubi->move_to_put) + wl_tree_add(e2, &ubi->used); + else + put = 1; + ubi->move_from = ubi->move_to = NULL; +- ubi->move_from_put = ubi->move_to_put = 0; +- ubi->wl_scheduled = 0; ++ ubi->move_to_put = ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + + if (put) { +@@ -877,62 +889,67 @@ + */ + dbg_wl("PEB %d was put meanwhile, erase", e2->pnum); + err = schedule_erase(ubi, e2, 0); +- if (err) { +- kmem_cache_free(wl_entries_slab, e2); +- ubi_ro_mode(ubi); +- } ++ if (err) ++ goto out_error; + } + +- err = schedule_erase(ubi, e1, 0); +- if (err) { +- kmem_cache_free(wl_entries_slab, e1); +- ubi_ro_mode(ubi); ++ if (!protect) { ++ err = schedule_erase(ubi, e1, 0); ++ if (err) ++ goto out_error; + } + ++ + dbg_wl("done"); +- return err; ++ mutex_unlock(&ubi->move_mutex); ++ return 0; + + /* +- * Some error occurred. @e1 was not changed, so return it back. @e2 +- * might be changed, schedule it for erasure. ++ * For some reasons the LEB was not moved, might be an error, might be ++ * something else. @e1 was not changed, so return it back. @e2 might ++ * be changed, schedule it for erasure. + */ +-error: +- if (err) +- dbg_wl("error %d occurred, cancel operation", err); +- ubi_assert(err <= 0); +- ++out_not_moved: + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&ubi->wl_lock); +- ubi->wl_scheduled = 0; +- if (ubi->move_from_put) +- put = 1; ++ if (scrubbing) ++ wl_tree_add(e1, &ubi->scrub); + else + wl_tree_add(e1, &ubi->used); + ubi->move_from = ubi->move_to = NULL; +- ubi->move_from_put = ubi->move_to_put = 0; ++ ubi->move_to_put = ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + +- if (put) { +- /* +- * Well, the target PEB was put meanwhile, schedule it for +- * erasure. +- */ +- dbg_wl("PEB %d was put meanwhile, erase", e1->pnum); +- err = schedule_erase(ubi, e1, 0); +- if (err) { +- kmem_cache_free(wl_entries_slab, e1); +- ubi_ro_mode(ubi); +- } +- } +- + err = schedule_erase(ubi, e2, 0); +- if (err) { +- kmem_cache_free(wl_entries_slab, e2); +- ubi_ro_mode(ubi); +- } ++ if (err) ++ goto out_error; ++ ++ mutex_unlock(&ubi->move_mutex); ++ return 0; ++ ++out_error: ++ ubi_err("error %d while moving PEB %d to PEB %d", ++ err, e1->pnum, e2->pnum); ++ ++ ubi_free_vid_hdr(ubi, vid_hdr); ++ spin_lock(&ubi->wl_lock); ++ ubi->move_from = ubi->move_to = NULL; ++ ubi->move_to_put = ubi->wl_scheduled = 0; ++ spin_unlock(&ubi->wl_lock); + +- yield(); ++ kmem_cache_free(ubi_wl_entry_slab, e1); ++ kmem_cache_free(ubi_wl_entry_slab, e2); ++ ubi_ro_mode(ubi); ++ ++ mutex_unlock(&ubi->move_mutex); + return err; ++ ++out_cancel: ++ ubi->wl_scheduled = 0; ++ spin_unlock(&ubi->wl_lock); ++ mutex_unlock(&ubi->move_mutex); ++ ubi_free_vid_hdr(ubi, vid_hdr); ++ return 0; + } + + /** +@@ -1020,7 +1037,7 @@ + if (cancel) { + dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec); + kfree(wl_wrk); +- kmem_cache_free(wl_entries_slab, e); ++ kmem_cache_free(ubi_wl_entry_slab, e); + return 0; + } + +@@ -1049,7 +1066,7 @@ + + ubi_err("failed to erase PEB %d, error %d", pnum, err); + kfree(wl_wrk); +- kmem_cache_free(wl_entries_slab, e); ++ kmem_cache_free(ubi_wl_entry_slab, e); + + if (err == -EINTR || err == -ENOMEM || err == -EAGAIN || + err == -EBUSY) { +@@ -1119,8 +1136,7 @@ + } + + /** +- * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling +- * unit. ++ * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling unit. + * @ubi: UBI device description object + * @pnum: physical eraseblock to return + * @torture: if this physical eraseblock has to be tortured +@@ -1128,7 +1144,7 @@ + * This function is called to return physical eraseblock @pnum to the pool of + * free physical eraseblocks. The @torture flag has to be set if an I/O error + * occurred to this @pnum and it has to be tested. This function returns zero +- * in case of success and a negative error code in case of failure. ++ * in case of success, and a negative error code in case of failure. + */ + int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) + { +@@ -1139,8 +1155,8 @@ + ubi_assert(pnum >= 0); + ubi_assert(pnum < ubi->peb_count); + ++retry: + spin_lock(&ubi->wl_lock); +- + e = ubi->lookuptbl[pnum]; + if (e == ubi->move_from) { + /* +@@ -1148,17 +1164,22 @@ + * be moved. It will be scheduled for erasure in the + * wear-leveling worker. + */ +- dbg_wl("PEB %d is being moved", pnum); +- ubi_assert(!ubi->move_from_put); +- ubi->move_from_put = 1; ++ dbg_wl("PEB %d is being moved, wait", pnum); + spin_unlock(&ubi->wl_lock); +- return 0; ++ ++ /* Wait for the WL worker by taking the @ubi->move_mutex */ ++ mutex_lock(&ubi->move_mutex); ++ mutex_unlock(&ubi->move_mutex); ++ goto retry; + } else if (e == ubi->move_to) { + /* + * User is putting the physical eraseblock which was selected + * as the target the data is moved to. It may happen if the EBA +- * unit already re-mapped the LEB but the WL unit did has not +- * put the PEB to the "used" tree. ++ * unit already re-mapped the LEB in 'ubi_eba_copy_leb()' but ++ * the WL unit has not put the PEB to the "used" tree yet, but ++ * it is about to do this. So we just set a flag which will ++ * tell the WL worker that the PEB is not needed anymore and ++ * should be scheduled for erasure. + */ + dbg_wl("PEB %d is the target of data moving", pnum); + ubi_assert(!ubi->move_to_put); +@@ -1172,8 +1193,15 @@ + } else if (in_wl_tree(e, &ubi->scrub)) { + paranoid_check_in_wl_tree(e, &ubi->scrub); + rb_erase(&e->rb, &ubi->scrub); +- } else +- prot_tree_del(ubi, e->pnum); ++ } else { ++ err = prot_tree_del(ubi, e->pnum); ++ if (err) { ++ ubi_err("PEB %d not found", pnum); ++ ubi_ro_mode(ubi); ++ spin_unlock(&ubi->wl_lock); ++ return err; ++ } ++ } + } + spin_unlock(&ubi->wl_lock); + +@@ -1227,8 +1255,17 @@ + if (in_wl_tree(e, &ubi->used)) { + paranoid_check_in_wl_tree(e, &ubi->used); + rb_erase(&e->rb, &ubi->used); +- } else +- prot_tree_del(ubi, pnum); ++ } else { ++ int err; ++ ++ err = prot_tree_del(ubi, e->pnum); ++ if (err) { ++ ubi_err("PEB %d not found", pnum); ++ ubi_ro_mode(ubi); ++ spin_unlock(&ubi->wl_lock); ++ return err; ++ } ++ } + + wl_tree_add(e, &ubi->scrub); + spin_unlock(&ubi->wl_lock); +@@ -1249,17 +1286,32 @@ + */ + int ubi_wl_flush(struct ubi_device *ubi) + { +- int err, pending_count; +- +- pending_count = ubi->works_count; +- +- dbg_wl("flush (%d pending works)", pending_count); ++ int err; + + /* + * Erase while the pending works queue is not empty, but not more then + * the number of currently pending works. + */ +- while (pending_count-- > 0) { ++ dbg_wl("flush (%d pending works)", ubi->works_count); ++ while (ubi->works_count) { ++ err = do_work(ubi); ++ if (err) ++ return err; ++ } ++ ++ /* ++ * Make sure all the works which have been done in parallel are ++ * finished. ++ */ ++ down_write(&ubi->work_sem); ++ up_write(&ubi->work_sem); ++ ++ /* ++ * And in case last was the WL worker and it cancelled the LEB ++ * movement, flush again. ++ */ ++ while (ubi->works_count) { ++ dbg_wl("flush more (%d pending works)", ubi->works_count); + err = do_work(ubi); + if (err) + return err; +@@ -1294,7 +1346,7 @@ + rb->rb_right = NULL; + } + +- kmem_cache_free(wl_entries_slab, e); ++ kmem_cache_free(ubi_wl_entry_slab, e); + } + } + } +@@ -1303,7 +1355,7 @@ + * ubi_thread - UBI background thread. + * @u: the UBI device description object pointer + */ +-static int ubi_thread(void *u) ++int ubi_thread(void *u) + { + int failures = 0; + struct ubi_device *ubi = u; +@@ -1394,36 +1446,22 @@ + ubi->used = ubi->free = ubi->scrub = RB_ROOT; + ubi->prot.pnum = ubi->prot.aec = RB_ROOT; + spin_lock_init(&ubi->wl_lock); ++ mutex_init(&ubi->move_mutex); ++ init_rwsem(&ubi->work_sem); + ubi->max_ec = si->max_ec; + INIT_LIST_HEAD(&ubi->works); + + sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num); + +- ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name); +- if (IS_ERR(ubi->bgt_thread)) { +- err = PTR_ERR(ubi->bgt_thread); +- ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name, +- err); +- return err; +- } +- +- if (ubi_devices_cnt == 0) { +- wl_entries_slab = kmem_cache_create("ubi_wl_entry_slab", +- sizeof(struct ubi_wl_entry), +- 0, 0, NULL); +- if (!wl_entries_slab) +- return -ENOMEM; +- } +- + err = -ENOMEM; + ubi->lookuptbl = kzalloc(ubi->peb_count * sizeof(void *), GFP_KERNEL); + if (!ubi->lookuptbl) +- goto out_free; ++ return err; + + list_for_each_entry_safe(seb, tmp, &si->erase, u.list) { + cond_resched(); + +- e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); ++ e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); + if (!e) + goto out_free; + +@@ -1431,7 +1469,7 @@ + e->ec = seb->ec; + ubi->lookuptbl[e->pnum] = e; + if (schedule_erase(ubi, e, 0)) { +- kmem_cache_free(wl_entries_slab, e); ++ kmem_cache_free(ubi_wl_entry_slab, e); + goto out_free; + } + } +@@ -1439,7 +1477,7 @@ + list_for_each_entry(seb, &si->free, u.list) { + cond_resched(); + +- e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); ++ e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); + if (!e) + goto out_free; + +@@ -1453,7 +1491,7 @@ + list_for_each_entry(seb, &si->corr, u.list) { + cond_resched(); + +- e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); ++ e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); + if (!e) + goto out_free; + +@@ -1461,7 +1499,7 @@ + e->ec = seb->ec; + ubi->lookuptbl[e->pnum] = e; + if (schedule_erase(ubi, e, 0)) { +- kmem_cache_free(wl_entries_slab, e); ++ kmem_cache_free(ubi_wl_entry_slab, e); + goto out_free; + } + } +@@ -1470,7 +1508,7 @@ + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { + cond_resched(); + +- e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); ++ e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); + if (!e) + goto out_free; + +@@ -1510,8 +1548,6 @@ + tree_destroy(&ubi->free); + tree_destroy(&ubi->scrub); + kfree(ubi->lookuptbl); +- if (ubi_devices_cnt == 0) +- kmem_cache_destroy(wl_entries_slab); + return err; + } + +@@ -1541,7 +1577,7 @@ + rb->rb_right = NULL; + } + +- kmem_cache_free(wl_entries_slab, pe->e); ++ kmem_cache_free(ubi_wl_entry_slab, pe->e); + kfree(pe); + } + } +@@ -1553,10 +1589,6 @@ + */ + void ubi_wl_close(struct ubi_device *ubi) + { +- dbg_wl("disable \"%s\"", ubi->bgt_name); +- if (ubi->bgt_thread) +- kthread_stop(ubi->bgt_thread); +- + dbg_wl("close the UBI wear-leveling unit"); + + cancel_pending(ubi); +@@ -1565,8 +1597,6 @@ + tree_destroy(&ubi->free); + tree_destroy(&ubi->scrub); + kfree(ubi->lookuptbl); +- if (ubi_devices_cnt == 1) +- kmem_cache_destroy(wl_entries_slab); + } + + #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +diff -urN linux-2.6.24.7.old/drivers/mtd/udc_cache.c linux-2.6.24.7/drivers/mtd/udc_cache.c +--- linux-2.6.24.7.old/drivers/mtd/udc_cache.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/mtd/udc_cache.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,531 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CACHE_MAX_NUM 256 ++#define SECTOR_SIZE 512 ++ ++//#define UDC_CACHE_DEBUG ++ ++#ifdef UDC_CACHE_DEBUG ++#define dprintk(a...) printk(a) ++#else ++#define dprintk(a...) while(0){} ++#endif ++ ++typedef struct { ++ unsigned short CacheState; ++ unsigned short UseCount; ++ unsigned short CacheChange; ++ unsigned short CacheReserve; ++ unsigned int BlockId; ++ unsigned char *aBlockData; ++} SSFDC__LB_CACHE; ++ ++#define FREE_CACHE 0 ++#define PREWRITE_CACHE 2 ++#define OFTEN_USE_CACHE 3 ++#define SECTOR_SHIFT 9 ++ ++#define CACHE_TO_UNCATCH(x) ((unsigned int)x | 0xa0000000) ++static unsigned int __aBlockData[SECTOR_SIZE * CACHE_MAX_NUM / 4] __attribute__ ((aligned (32))); ++static SSFDC__LB_CACHE ssfdc_cache[CACHE_MAX_NUM]; ++static unsigned short Cur_CacheCount = 0; ++int FlushDataState = 0; ++static struct mtdblk_dev *g_udc_mtdblk; ++static struct mtd_info *g_udc_mtd; ++ ++extern int udc_mtdblock_readsect(struct mtdblk_dev *, unsigned long, char *, int); ++extern int udc_mtdblock_writesect(struct mtdblk_dev *, unsigned long, char *); ++extern struct mtdblk_dev *udc_get_mtdblk(void); ++extern struct mtd_info *udc_get_mtd(void); ++extern void udc_flush_cache(struct mtdblk_dev *mtdblk); ++ ++#define _NAND_LB_Write(pCache) udc_mtdblock_writesect(g_udc_mtdblk, pCache->BlockId,pCache->aBlockData) ++#define _NAND_LB_Read(Sector,pBuffer) udc_mtdblock_readsect(g_udc_mtdblk, Sector, pBuffer, SECTOR_SIZE); ++ ++#define DMA_ENABLE 0 ++ ++#if DMA_ENABLE ++#define DMA_CHANNEL 5 ++#define PHYSADDR(x) virt_to_phys((void *)x) ++#else ++#define lb_memcpy memcpy ++#endif ++ ++#if DMA_ENABLE ++static void lb_memcpy(void *target,void* source,unsigned int len) ++{ ++ int ch = DMA_CHANNEL; ++ if(((unsigned int)source < 0xa0000000) && len) ++ dma_cache_wback_inv((unsigned long)source, len); ++ if(((unsigned int)target < 0xa0000000) && len) ++ dma_cache_wback_inv((unsigned long)target, len); ++ ++ REG_DMAC_DSAR(ch) = PHYSADDR((unsigned long)source); ++ REG_DMAC_DTAR(ch) = PHYSADDR((unsigned long)target); ++ REG_DMAC_DTCR(ch) = len / 32; ++ REG_DMAC_DRSR(ch) = DMAC_DRSR_RS_AUTO; ++ REG_DMAC_DCMD(ch) = DMAC_DCMD_SAI| DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32|DMAC_DCMD_DS_32BYTE; ++ REG_DMAC_DCCSR(ch) = DMAC_DCCSR_EN | DMAC_DCCSR_NDES; ++ while ( REG_DMAC_DTCR(ch) ); ++} ++#endif ++ ++static void _NAND_LB_InitCache(void) ++{ ++ int i; ++ SSFDC__LB_CACHE *pCache = ssfdc_cache; ++#if DMA_ENABLE ++ unsigned char * ptr = (unsigned char *)CACHE_TO_UNCATCH(__aBlockData); ++#else ++ unsigned char * ptr = (unsigned char *)(__aBlockData); ++#endif ++ for(i = 0;i < CACHE_MAX_NUM;i++) ++ { ++ pCache->CacheState = FREE_CACHE; ++ pCache->UseCount = 0; ++ pCache->CacheChange = 0; ++ pCache->aBlockData = ptr; ++ ptr+=SECTOR_SIZE; ++ pCache++; ++ } ++ Cur_CacheCount = 0; ++} ++ ++static SSFDC__LB_CACHE * _NAND_LB_GetFreeCache(void) ++{ ++ int ret = 0; ++ SSFDC__LB_CACHE *pCacheInfo = &ssfdc_cache[Cur_CacheCount]; ++ while(1) ++ { ++ if(ret >= CACHE_MAX_NUM) ++ return 0; ++ if(pCacheInfo >= &ssfdc_cache[CACHE_MAX_NUM]) ++ { ++ pCacheInfo = ssfdc_cache; ++ Cur_CacheCount = 0; ++ } ++ ++ if(pCacheInfo->CacheState == FREE_CACHE) ++ { ++ return pCacheInfo; ++ } ++ pCacheInfo++; ++ Cur_CacheCount++; ++ ret++; ++ } ++ return 0; ++} ++ ++static void _NAND_LB_CloseCACHES(unsigned int sectorstart,unsigned int sectorend) ++{ ++ unsigned int i; ++ SSFDC__LB_CACHE *pCache = ssfdc_cache; ++ for( i = 0;i < CACHE_MAX_NUM;i++){ ++ if((pCache->CacheState != FREE_CACHE) && (pCache->BlockId >= sectorstart) && (pCache->BlockId < sectorend)){ ++ pCache->CacheChange = 0; ++ pCache->CacheState = FREE_CACHE; ++ pCache->UseCount = 0; ++ } ++ pCache++; ++ } ++} ++ ++static void _NAND_LB_FLUSHCACHES(unsigned int sectorstart,unsigned int sectorend) ++{ ++ unsigned int i; ++ SSFDC__LB_CACHE *pCache = ssfdc_cache; ++ for( i = 0;i < CACHE_MAX_NUM;i++){ ++ if((pCache->CacheState != FREE_CACHE) && (pCache->BlockId >= sectorstart) && (pCache->BlockId < sectorend)){ ++ if(pCache->CacheChange) ++ _NAND_LB_Write(pCache); ++ pCache->CacheChange = 0; ++ pCache->CacheState = FREE_CACHE; ++ pCache->UseCount = 0; ++ } ++ pCache++; ++ ++ } ++} ++ ++inline static int Get_NAND_CacheFreeCount(void) ++{ ++ SSFDC__LB_CACHE *pCache = ssfdc_cache; ++ SSFDC__LB_CACHE *pEndCache = &ssfdc_cache[CACHE_MAX_NUM]; ++ unsigned int count = 0; ++ while(pCache < pEndCache) ++ { ++ if(pCache->CacheState == FREE_CACHE) ++ count++; ++ pCache++; ++ } ++ return count; ++ ++} ++ ++static unsigned int _NAND_LB_PreWiteToNand(SSFDC__LB_CACHE *pCache,unsigned short *count,unsigned int update) ++{ ++ SSFDC__LB_CACHE *pWriteCache; ++ SSFDC__LB_CACHE *pEndCache = &ssfdc_cache[CACHE_MAX_NUM]; ++ unsigned int sector = -1; ++ unsigned int flag; ++ while(1) ++ { ++ sector = -1; ++ flag = 0; ++ pWriteCache = ssfdc_cache; ++ while(pWriteCache < pEndCache) ++ { ++ if(pWriteCache->CacheState == update) //PREWRITE_CACHE ++ { ++ if(pWriteCache->BlockId < sector) ++ { ++ sector = pWriteCache->BlockId; ++ pCache = pWriteCache; ++ } ++ }else ++ flag++; ++ pWriteCache++; ++ } ++ ++ if(flag < CACHE_MAX_NUM) ++ { ++ if(pCache->CacheChange) ++ { ++ _NAND_LB_Write(pCache); ++ pCache->CacheChange = 0; ++ } ++ pCache->CacheState = FREE_CACHE; ++ pCache->UseCount = 0; ++ (*count)++; ++ }else ++ break; ++ } ++ return 0; ++} ++ ++static void _NAND_LB_OftenToNand(SSFDC__LB_CACHE *pCache,unsigned short *count,unsigned int update) ++{ ++ SSFDC__LB_CACHE *pWriteCache = pCache; ++ SSFDC__LB_CACHE *pOldCache = pCache; ++ SSFDC__LB_CACHE *pEndCache = &ssfdc_cache[CACHE_MAX_NUM]; ++ ++ dprintk("%s!\n",__FUNCTION__); ++ while(pCache) ++ { ++ if(pCache->CacheState == OFTEN_USE_CACHE) ++ { ++ if(pWriteCache->CacheState != OFTEN_USE_CACHE) ++ pWriteCache = pCache; ++ else if(pWriteCache->UseCount > pCache->UseCount) ++ { ++ pWriteCache = pCache; ++ } ++ } ++ pCache++; ++ if(pCache >= pEndCache) ++ break; ++ } ++ if(pWriteCache->CacheState == OFTEN_USE_CACHE) ++ { ++ (*count)++; ++ if(pWriteCache->CacheChange) ++ _NAND_LB_Write(pWriteCache); ++ pWriteCache->CacheState = FREE_CACHE; ++ ++ pWriteCache->UseCount = 0; ++ pWriteCache->CacheChange = 0; ++ if(update != -1) ++ update--; ++ if(update != 0) ++ _NAND_LB_OftenToNand(pOldCache,count,update); ++ } ++} ++ ++static int _NAND_LB_FreeCache(unsigned int update) ++{ ++ unsigned short freecount = 0,totalfree = 0; ++ ++ freecount = 0; ++ _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,PREWRITE_CACHE); ++ ++ totalfree += freecount; ++ dprintk("free count = %d\n",freecount); ++ if(freecount == 0) ++ { ++ freecount = 0; ++ _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,OFTEN_USE_CACHE); ++ totalfree += freecount; ++ update = 0; ++ } ++ if(update) ++ { ++ if(Get_NAND_CacheFreeCount() < CACHE_MAX_NUM * 1 / 4) // because fat is 4 sector ++ { ++ freecount = 0; ++ _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,OFTEN_USE_CACHE); ++ totalfree += freecount; ++ } ++ } ++ ++ dprintk("Free = %d\r\n",totalfree); ++ return totalfree; ++} ++ ++static int _NAND_LB_GetFromCache(unsigned int Sector, void *pBuffer) { ++ ++ SSFDC__LB_CACHE *pCache = &ssfdc_cache[Cur_CacheCount]; ++ SSFDC__LB_CACHE *pUseCache = 0; ++ unsigned short i; ++ dprintk("sector = %x pBuffer = %x\n",Sector,pBuffer); ++ if(pCache >= &ssfdc_cache[CACHE_MAX_NUM]) ++ pCache = ssfdc_cache; ++ ++ i = 0; ++ while (1) { ++ if(pCache->CacheState != FREE_CACHE) ++ { ++ if (Sector == pCache->BlockId) { ++ dprintk("Cache is use = %d\r\n",pCache->BlockId); ++ pUseCache = pCache; ++ pCache->UseCount++; ++ if(pCache->UseCount == 0) ++ pCache->UseCount = -1; ++ pCache->CacheState = OFTEN_USE_CACHE; ++ } ++ } ++ pCache--; ++ if(pCache < ssfdc_cache) ++ pCache = &ssfdc_cache[CACHE_MAX_NUM - 1]; ++ ++ i++; ++ if (i >= CACHE_MAX_NUM) { ++ break; /* Sector not in cache */ ++ } ++ } ++ if (pUseCache) { ++ dprintk("From Cache %d\r\n",Sector); ++ lb_memcpy(pBuffer, pUseCache->aBlockData, SECTOR_SIZE); ++ return 0; ++ } ++ return -1; ++} ++ ++static void _NAND_LB_ClearCache(void) { ++ ++ unsigned short freecount = 0; ++ dprintk("Clear Cache\r\n"); ++ ++ _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,PREWRITE_CACHE); ++ _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,OFTEN_USE_CACHE); ++} ++ ++static void _NAND_LB_CopyToCache(unsigned int Sector, void *pBuffer,unsigned short rw) ++{ ++ SSFDC__LB_CACHE *pCache = _NAND_LB_GetFreeCache(); ++ dprintk("Copy to Cache = 0x%08x 0x%08x\r\n",pCache,ssfdc_cache); ++ ++ if(!pCache) ++ { ++ _NAND_LB_FreeCache(rw); ++ ++ pCache = _NAND_LB_GetFreeCache(); ++ } ++ pCache->BlockId = Sector; ++ pCache->CacheState = PREWRITE_CACHE; ++ pCache->UseCount = 0; ++ pCache->CacheChange = rw; ++ ++ lb_memcpy(pCache->aBlockData,pBuffer,SECTOR_SIZE); ++} ++ ++ ++static int _NAND_LB_UpdateInCache(unsigned int Sector, void *pBuffer) { ++ short i,ret = 0; ++ i = Cur_CacheCount; ++ if(Cur_CacheCount > CACHE_MAX_NUM) ++ i = 0; ++ while(1) ++ { ++ if(ret >= CACHE_MAX_NUM) ++ return -1; ++ if(ssfdc_cache[i].CacheState != FREE_CACHE) ++ { ++ ++ if(ssfdc_cache[i].BlockId == Sector) ++ { ++ dprintk("UpdateInCache = %d\r\n",Sector); ++ ssfdc_cache[i].CacheState = OFTEN_USE_CACHE; ++ ssfdc_cache[i].UseCount++; ++ ssfdc_cache[i].CacheChange = 1; ++ lb_memcpy(ssfdc_cache[i].aBlockData,pBuffer,SECTOR_SIZE); ++ return 0; ++ } ++ } ++ i--; ++ if(i < 0) ++ i = CACHE_MAX_NUM - 1; ++ ret++; ++ } ++ return -1; ++} ++ ++static int NAND_LB_MultiRead(unsigned int Sector, void *pBuffer,unsigned int SectorCount) ++{ ++ int i,ret,end; ++ void *p; ++ ++ dprintk("NAND_LB_MultiRead = %d %d \n",Sector,SectorCount); ++ end = Sector + SectorCount; ++ _NAND_LB_FLUSHCACHES(Sector,end); ++ ++ p = pBuffer; ++ for (i = Sector; i < end; i ++) ++ { ++ ret = udc_mtdblock_readsect(g_udc_mtdblk, i, p, SECTOR_SIZE); ++ p += SECTOR_SIZE; ++ } ++ return ret; ++} ++ ++static int NAND_LB_Read(unsigned int Sector, void *pBuffer) ++{ ++ int x; ++#if DMA_ENABLE ++ unsigned char *ptr = (unsigned char *)CACHE_TO_UNCATCH(pBuffer); ++ dma_cache_wback_inv(pBuffer,SECTOR_SIZE); ++#else ++ unsigned char *ptr = (unsigned char *)pBuffer; ++#endif ++ dprintk("LB_Read = %d \n",Sector); ++ if(_NAND_LB_GetFromCache(Sector,ptr)) ++ { ++ x = _NAND_LB_Read(Sector,ptr); ++ _NAND_LB_CopyToCache(Sector,ptr,0); ++ } ++ return 512; ++} ++ ++static int NAND_LB_MultiWrite(unsigned int Sector, void *pBuffer,unsigned int SectorCount) ++{ ++ int i,ret; ++ unsigned char *p; ++ ++ _NAND_LB_CloseCACHES(Sector,Sector + SectorCount); ++ p = (unsigned char *)pBuffer; ++ for (i = Sector; i < Sector + SectorCount; i ++) ++ { ++ ret = udc_mtdblock_writesect(g_udc_mtdblk, i, p); ++ p += 512; ++ } ++ return ret; ++} ++ ++static int NAND_LB_Write(unsigned int Sector, void *pBuffer) ++{ ++#if DMA_ENABLE ++ unsigned char *ptr = (unsigned char *)CACHE_TO_UNCATCH(pBuffer); ++ dma_cache_wback_inv(pBuffer,SECTOR_SIZE); ++#else ++ unsigned char *ptr = (unsigned char *)pBuffer; ++#endif ++ dprintk("LB_Write = %x %x\r\n",Sector,pBuffer); ++ if(_NAND_LB_UpdateInCache(Sector,ptr)) ++ { ++ _NAND_LB_CopyToCache(Sector,ptr,1); ++ } ++ return 512; ++} ++/********************************************************************* ++* ++* Global functions ++* ++***********************************************************************/ ++ ++int NAND_LB_Init(void) ++{ ++ dprintk("UDC CACHE Init \n"); ++ _NAND_LB_InitCache(); ++ g_udc_mtdblk = udc_get_mtdblk(); ++ g_udc_mtd = udc_get_mtd(); ++ return 0; ++} ++ ++int NAND_LB_FLASHCACHE(void) ++{ ++ dprintk("Flush lb cache !\n"); ++ _NAND_LB_ClearCache(); ++// dprintk("Flush mtd cache !\n"); ++// udc_flush_cache(g_udc_mtdblk); ++ return 0; ++} ++ ++int NAND_MTD_FLASHCACHE(void) ++{ ++ dprintk("Flush mtd cache !\n"); ++ udc_flush_cache(g_udc_mtdblk); ++ return 0; ++} ++ ++int udc_read(unsigned int offset, unsigned int len, unsigned char *buf) ++{ ++ unsigned long block,sector,i; ++ ++ block = offset >> SECTOR_SHIFT; ++ sector = len >> SECTOR_SHIFT; ++ dprintk("read dev = ia:%x, s:%d c:%d\r\n",buf,block,sector); ++ ++ if (sector <= 8) ++ { ++ for(i = 0;i < sector; i++) ++ { ++ NAND_LB_Read(block + i,(void *)(buf)); ++ buf += 512; ++ } ++ } ++ else ++ NAND_LB_MultiRead(block, buf, sector); ++ ++ return len; ++} ++ ++int udc_write(unsigned int offset, unsigned int len, unsigned char *buf) ++{ ++ unsigned long block,sector,i; ++ ++ block = offset >> SECTOR_SHIFT; ++ sector = len >> SECTOR_SHIFT; ++ dprintk("write dev s:%d c:%d\r\n",block,sector); ++ ++ if(sector <= 8) ++ { ++ for(i = 0;i < sector; i++) ++ { ++ NAND_LB_Write(block + i,(void *)(buf)); ++ buf += 512; ++ FlushDataState = 1; ++ } ++ }else ++ NAND_LB_MultiWrite(block,(void *)(buf),sector); ++ ++ return len; ++} ++ ++EXPORT_SYMBOL_GPL(udc_write); ++EXPORT_SYMBOL_GPL(udc_read); ++EXPORT_SYMBOL_GPL(NAND_LB_Init); ++EXPORT_SYMBOL_GPL(NAND_LB_FLASHCACHE); ++EXPORT_SYMBOL_GPL(FlushDataState); ++EXPORT_SYMBOL_GPL(NAND_MTD_FLASHCACHE); +diff -urN linux-2.6.24.7.old/drivers/net/Kconfig linux-2.6.24.7/drivers/net/Kconfig +--- linux-2.6.24.7.old/drivers/net/Kconfig 2009-04-12 18:05:07.000000000 +0200 ++++ linux-2.6.24.7/drivers/net/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -333,6 +333,24 @@ + or internal device. It is safe to say Y or M here even if your + ethernet card lack MII. + ++config JZ_ETH ++ tristate "JZ4730/JZ5730 On-Chip Ethernet support" ++ depends on NET_ETHERNET && (SOC_JZ4730 || SOC_JZ5730 || JZ_FPGA) ++ help ++ Say Y for support of JZ4730/JZ5730 On-Chip Ethernet interface. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called jz_eth. ++ ++config JZCS8900 ++ tristate "JZ CS8900A Ethernet support" ++ depends on NET_ETHERNET && (SOC_JZ4740 || SOC_JZ4750) ++ help ++ Say Y for support of JZ CS8900A Ethernet interface. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called jzcs8900a. ++ + config MACB + tristate "Atmel MACB support" + depends on AVR32 || ARCH_AT91SAM9260 || ARCH_AT91SAM9263 +diff -urN linux-2.6.24.7.old/drivers/net/Kconfig.orig linux-2.6.24.7/drivers/net/Kconfig.orig +--- linux-2.6.24.7.old/drivers/net/Kconfig.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/net/Kconfig.orig 2009-04-12 18:01:55.000000000 +0200 +@@ -0,0 +1,3192 @@ ++ ++# ++# Network device configuration ++# ++ ++menuconfig NETDEVICES ++ default y if UML ++ depends on NET ++ bool "Network device support" ++ ---help--- ++ You can say N here if you don't intend to connect your Linux box to ++ any other computer at all. ++ ++ You'll have to say Y if your computer contains a network card that ++ you want to use under Linux. If you are going to run SLIP or PPP over ++ telephone line or null modem cable you need say Y here. Connecting ++ two machines with parallel ports using PLIP needs this, as well as ++ AX.25/KISS for sending Internet traffic over amateur radio links. ++ ++ See also "The Linux Network Administrator's Guide" by Olaf Kirch and ++ Terry Dawson. Available at . ++ ++ If unsure, say Y. ++ ++# All the following symbols are dependent on NETDEVICES - do not repeat ++# that for each of the symbols. ++if NETDEVICES ++ ++config NETDEVICES_MULTIQUEUE ++ bool "Netdevice multiple hardware queue support" ++ ---help--- ++ Say Y here if you want to allow the network stack to use multiple ++ hardware TX queues on an ethernet device. ++ ++ Most people will say N here. ++ ++config IFB ++ tristate "Intermediate Functional Block support" ++ depends on NET_CLS_ACT ++ ---help--- ++ This is an intermediate driver that allows sharing of ++ resources. ++ To compile this driver as a module, choose M here: the module ++ will be called ifb. If you want to use more than one ifb ++ device at a time, you need to compile this driver as a module. ++ Instead of 'ifb', the devices will then be called 'ifb0', ++ 'ifb1' etc. ++ Look at the iproute2 documentation directory for usage etc ++ ++config DUMMY ++ tristate "Dummy net driver support" ++ ---help--- ++ This is essentially a bit-bucket device (i.e. traffic you send to ++ this device is consigned into oblivion) with a configurable IP ++ address. It is most commonly used in order to make your currently ++ inactive SLIP address seem like a real address for local programs. ++ If you use SLIP or PPP, you might want to say Y here. Since this ++ thing often comes in handy, the default is Y. It won't enlarge your ++ kernel either. What a deal. Read about it in the Network ++ Administrator's Guide, available from ++ . ++ ++ To compile this driver as a module, choose M here: the module ++ will be called dummy. If you want to use more than one dummy ++ device at a time, you need to compile this driver as a module. ++ Instead of 'dummy', the devices will then be called 'dummy0', ++ 'dummy1' etc. ++ ++config BONDING ++ tristate "Bonding driver support" ++ depends on INET ++ ---help--- ++ Say 'Y' or 'M' if you wish to be able to 'bond' multiple Ethernet ++ Channels together. This is called 'Etherchannel' by Cisco, ++ 'Trunking' by Sun, 802.3ad by the IEEE, and 'Bonding' in Linux. ++ ++ The driver supports multiple bonding modes to allow for both high ++ performance and high availability operation. ++ ++ Refer to for more ++ information. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called bonding. ++ ++config MACVLAN ++ tristate "MAC-VLAN support (EXPERIMENTAL)" ++ depends on EXPERIMENTAL ++ ---help--- ++ This allows one to create virtual interfaces that map packets to ++ or from specific MAC addresses to a particular interface. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called macvlan. ++ ++config EQUALIZER ++ tristate "EQL (serial line load balancing) support" ++ ---help--- ++ If you have two serial connections to some other computer (this ++ usually requires two modems and two telephone lines) and you use ++ SLIP (the protocol for sending Internet traffic over telephone ++ lines) or PPP (a better SLIP) on them, you can make them behave like ++ one double speed connection using this driver. Naturally, this has ++ to be supported at the other end as well, either with a similar EQL ++ Linux driver or with a Livingston Portmaster 2e. ++ ++ Say Y if you want this and read ++ . You may also want to read ++ section 6.2 of the NET-3-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here: the module ++ will be called eql. If unsure, say N. ++ ++config IMQ ++ tristate "IMQ (intermediate queueing device) support" ++ depends on NETDEVICES && NETFILTER ++ ---help--- ++ The IMQ device(s) is used as placeholder for QoS queueing ++ disciplines. Every packet entering/leaving the IP stack can be ++ directed through the IMQ device where it's enqueued/dequeued to the ++ attached qdisc. This allows you to treat network devices as classes ++ and distribute bandwidth among them. Iptables is used to specify ++ through which IMQ device, if any, packets travel. ++ ++ More information at: http://www.linuximq.net/ ++ ++ To compile this driver as a module, choose M here: the module ++ will be called imq. If unsure, say N. ++ ++choice ++ prompt "IMQ behavior (PRE/POSTROUTING)" ++ depends on IMQ ++ default IMQ_BEHAVIOR_BB ++ help ++ ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ IMQ can work in any of the following ways: ++ ++ PREROUTING | POSTROUTING ++ -----------------|------------------- ++ #1 After NAT | After NAT ++ #2 After NAT | Before NAT ++ #3 Before NAT | After NAT ++ #4 Before NAT | Before NAT ++ ++ The default behavior is to hook before NAT on PREROUTING ++ and after NAT on POSTROUTING (#3). ++ ++ This settings are specially usefull when trying to use IMQ ++ to shape NATed clients. ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_AA ++ bool "IMQ AA" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: After NAT ++ POSTROUTING: After NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_AB ++ bool "IMQ AB" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: After NAT ++ POSTROUTING: Before NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_BA ++ bool "IMQ BA" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: Before NAT ++ POSTROUTING: After NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_BB ++ bool "IMQ BB" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: Before NAT ++ POSTROUTING: Before NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++endchoice ++ ++config IMQ_NUM_DEVS ++ ++ int "Number of IMQ devices" ++ range 2 16 ++ depends on IMQ ++ default "16" ++ help ++ ++ This settings defines how many IMQ devices will be ++ created. ++ ++ The default value is 16. ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config TUN ++ tristate "Universal TUN/TAP device driver support" ++ select CRC32 ++ ---help--- ++ TUN/TAP provides packet reception and transmission for user space ++ programs. It can be viewed as a simple Point-to-Point or Ethernet ++ device, which instead of receiving packets from a physical media, ++ receives them from user space program and instead of sending packets ++ via physical media writes them to the user space program. ++ ++ When a program opens /dev/net/tun, driver creates and registers ++ corresponding net device tunX or tapX. After a program closed above ++ devices, driver will automatically delete tunXX or tapXX device and ++ all routes corresponding to it. ++ ++ Please read for more ++ information. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called tun. ++ ++ If you don't know what to use this for, you don't need it. ++ ++config VETH ++ tristate "Virtual ethernet pair device" ++ ---help--- ++ This device is a local ethernet tunnel. Devices are created in pairs. ++ When one end receives the packet it appears on its pair and vice ++ versa. ++ ++config NET_SB1000 ++ tristate "General Instruments Surfboard 1000" ++ depends on PNP ++ ---help--- ++ This is a driver for the General Instrument (also known as ++ NextLevel) SURFboard 1000 internal ++ cable modem. This is an ISA card which is used by a number of cable ++ TV companies to provide cable modem access. It's a one-way ++ downstream-only cable modem, meaning that your upstream net link is ++ provided by your regular phone modem. ++ ++ At present this driver only compiles as a module, so say M here if ++ you have this card. The module will be called sb1000. Then read ++ for information on how ++ to use this module, as it needs special ppp scripts for establishing ++ a connection. Further documentation and the necessary scripts can be ++ found at: ++ ++ ++ ++ ++ ++ If you don't have this card, of course say N. ++ ++source "drivers/net/arcnet/Kconfig" ++ ++source "drivers/net/phy/Kconfig" ++ ++# ++# Ethernet ++# ++ ++menuconfig NET_ETHERNET ++ bool "Ethernet (10 or 100Mbit)" ++ depends on !UML ++ ---help--- ++ Ethernet (also called IEEE 802.3 or ISO 8802-2) is the most common ++ type of Local Area Network (LAN) in universities and companies. ++ ++ Common varieties of Ethernet are: 10BASE-2 or Thinnet (10 Mbps over ++ coaxial cable, linking computers in a chain), 10BASE-T or twisted ++ pair (10 Mbps over twisted pair cable, linking computers to central ++ hubs), 10BASE-F (10 Mbps over optical fiber links, using hubs), ++ 100BASE-TX (100 Mbps over two twisted pair cables, using hubs), ++ 100BASE-T4 (100 Mbps over 4 standard voice-grade twisted pair ++ cables, using hubs), 100BASE-FX (100 Mbps over optical fiber links) ++ [the 100BASE varieties are also known as Fast Ethernet], and Gigabit ++ Ethernet (1 Gbps over optical fiber or short copper links). ++ ++ If your Linux machine will be connected to an Ethernet and you have ++ an Ethernet network interface card (NIC) installed in your computer, ++ say Y here and read the Ethernet-HOWTO, available from ++ . You will then also have ++ to say Y to the driver for your particular NIC. ++ ++ Note that the answer to this question won't directly affect the ++ kernel: saying N will just cause the configurator to skip all ++ the questions about Ethernet network cards. If unsure, say N. ++ ++if NET_ETHERNET ++ ++config MII ++ tristate "Generic Media Independent Interface device support" ++ help ++ Most ethernet controllers have MII transceiver either as an external ++ or internal device. It is safe to say Y or M here even if your ++ ethernet card lack MII. ++ ++config MACB ++ tristate "Atmel MACB support" ++ depends on AVR32 || ARCH_AT91SAM9260 || ARCH_AT91SAM9263 ++ select PHYLIB ++ help ++ The Atmel MACB ethernet interface is found on many AT32 and AT91 ++ parts. Say Y to include support for the MACB chip. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called macb. ++ ++source "drivers/net/arm/Kconfig" ++ ++config AX88796 ++ tristate "ASIX AX88796 NE2000 clone support" ++ depends on ARM || MIPS || SUPERH ++ select CRC32 ++ select MII ++ help ++ AX88796 driver, using platform bus to provide ++ chip detection and resources ++ ++config AX88796_93CX6 ++ bool "ASIX AX88796 external 93CX6 eeprom support" ++ depends on AX88796 ++ select EEPROM_93CX6 ++ help ++ Select this if your platform comes with an external 93CX6 eeprom. ++ ++config MACE ++ tristate "MACE (Power Mac ethernet) support" ++ depends on PPC_PMAC && PPC32 ++ select CRC32 ++ help ++ Power Macintoshes and clones with Ethernet built-in on the ++ motherboard will usually use a MACE (Medium Access Control for ++ Ethernet) interface. Say Y to include support for the MACE chip. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called mace. ++ ++config MACE_AAUI_PORT ++ bool "Use AAUI port instead of TP by default" ++ depends on MACE ++ help ++ Some Apple machines (notably the Apple Network Server) which use the ++ MACE ethernet chip have an Apple AUI port (small 15-pin connector), ++ instead of an 8-pin RJ45 connector for twisted-pair ethernet. Say ++ Y here if you have such a machine. If unsure, say N. ++ The driver will default to AAUI on ANS anyway, and if you use it as ++ a module, you can provide the port_aaui=0|1 to force the driver. ++ ++config BMAC ++ tristate "BMAC (G3 ethernet) support" ++ depends on PPC_PMAC && PPC32 ++ select CRC32 ++ help ++ Say Y for support of BMAC Ethernet interfaces. These are used on G3 ++ computers. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called bmac. ++ ++config ARIADNE ++ tristate "Ariadne support" ++ depends on ZORRO ++ help ++ If you have a Village Tronic Ariadne Ethernet adapter, say Y. ++ Otherwise, say N. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called ariadne. ++ ++config A2065 ++ tristate "A2065 support" ++ depends on ZORRO ++ select CRC32 ++ help ++ If you have a Commodore A2065 Ethernet adapter, say Y. Otherwise, ++ say N. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called a2065. ++ ++config HYDRA ++ tristate "Hydra support" ++ depends on ZORRO ++ select CRC32 ++ help ++ If you have a Hydra Ethernet adapter, say Y. Otherwise, say N. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called hydra. ++ ++config ZORRO8390 ++ tristate "Zorro NS8390-based Ethernet support" ++ depends on ZORRO ++ select CRC32 ++ help ++ This driver is for Zorro Ethernet cards using an NS8390-compatible ++ chipset, like the Village Tronic Ariadne II and the Individual ++ Computers X-Surf Ethernet cards. If you have such a card, say Y. ++ Otherwise, say N. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called zorro8390. ++ ++config APNE ++ tristate "PCMCIA NE2000 support" ++ depends on AMIGA_PCMCIA ++ select CRC32 ++ help ++ If you have a PCMCIA NE2000 compatible adapter, say Y. Otherwise, ++ say N. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called apne. ++ ++config APOLLO_ELPLUS ++ tristate "Apollo 3c505 support" ++ depends on APOLLO ++ help ++ Say Y or M here if your Apollo has a 3Com 3c505 ISA Ethernet card. ++ If you don't have one made for Apollos, you can use one from a PC, ++ except that your Apollo won't be able to boot from it (because the ++ code in the ROM will be for a PC). ++ ++config MAC8390 ++ bool "Macintosh NS 8390 based ethernet cards" ++ depends on MAC ++ select CRC32 ++ help ++ If you want to include a driver to support Nubus or LC-PDS ++ Ethernet cards using an NS8390 chipset or its equivalent, say Y ++ and read the Ethernet-HOWTO, available from ++ . ++ ++config MAC89x0 ++ tristate "Macintosh CS89x0 based ethernet cards" ++ depends on MAC ++ ---help--- ++ Support for CS89x0 chipset based Ethernet cards. If you have a ++ Nubus or LC-PDS network (Ethernet) card of this type, say Y and ++ read the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. This module will ++ be called mac89x0. ++ ++config MACSONIC ++ tristate "Macintosh SONIC based ethernet (onboard, NuBus, LC, CS)" ++ depends on MAC ++ ---help--- ++ Support for NatSemi SONIC based Ethernet devices. This includes ++ the onboard Ethernet in many Quadras as well as some LC-PDS, ++ a few Nubus and all known Comm Slot Ethernet cards. If you have ++ one of these say Y and read the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. This module will ++ be called macsonic. ++ ++config MACMACE ++ bool "Macintosh (AV) onboard MACE ethernet" ++ depends on MAC ++ select CRC32 ++ help ++ Support for the onboard AMD 79C940 MACE Ethernet controller used in ++ the 660AV and 840AV Macintosh. If you have one of these Macintoshes ++ say Y and read the Ethernet-HOWTO, available from ++ . ++ ++config MVME147_NET ++ tristate "MVME147 (Lance) Ethernet support" ++ depends on MVME147 ++ select CRC32 ++ help ++ Support for the on-board Ethernet interface on the Motorola MVME147 ++ single-board computer. Say Y here to include the ++ driver for this chip in your kernel. ++ To compile this driver as a module, choose M here. ++ ++config MVME16x_NET ++ tristate "MVME16x Ethernet support" ++ depends on MVME16x ++ help ++ This is the driver for the Ethernet interface on the Motorola ++ MVME162, 166, 167, 172 and 177 boards. Say Y here to include the ++ driver for this chip in your kernel. ++ To compile this driver as a module, choose M here. ++ ++config BVME6000_NET ++ tristate "BVME6000 Ethernet support" ++ depends on BVME6000 ++ help ++ This is the driver for the Ethernet interface on BVME4000 and ++ BVME6000 VME boards. Say Y here to include the driver for this chip ++ in your kernel. ++ To compile this driver as a module, choose M here. ++ ++config ATARILANCE ++ tristate "Atari Lance support" ++ depends on ATARI ++ help ++ Say Y to include support for several Atari Ethernet adapters based ++ on the AMD Lance chipset: RieblCard (with or without battery), or ++ PAMCard VME (also the version by Rhotron, with different addresses). ++ ++config SUN3LANCE ++ tristate "Sun3/Sun3x on-board LANCE support" ++ depends on SUN3 || SUN3X ++ help ++ Most Sun3 and Sun3x motherboards (including the 3/50, 3/60 and 3/80) ++ featured an AMD Lance 10Mbit Ethernet controller on board; say Y ++ here to compile in the Linux driver for this and enable Ethernet. ++ General Linux information on the Sun 3 and 3x series (now ++ discontinued) is at ++ . ++ ++ If you're not building a kernel for a Sun 3, say N. ++ ++config SUN3_82586 ++ bool "Sun3 on-board Intel 82586 support" ++ depends on SUN3 ++ help ++ This driver enables support for the on-board Intel 82586 based ++ Ethernet adapter found on Sun 3/1xx and 3/2xx motherboards. Note ++ that this driver does not support 82586-based adapters on additional ++ VME boards. ++ ++config HPLANCE ++ bool "HP on-board LANCE support" ++ depends on DIO ++ select CRC32 ++ help ++ If you want to use the builtin "LANCE" Ethernet controller on an ++ HP300 machine, say Y here. ++ ++config LASI_82596 ++ tristate "Lasi ethernet" ++ depends on GSC ++ help ++ Say Y here to support the builtin Intel 82596 ethernet controller ++ found in Hewlett-Packard PA-RISC machines with 10Mbit ethernet. ++ ++config SNI_82596 ++ tristate "SNI RM ethernet" ++ depends on NET_ETHERNET && SNI_RM ++ help ++ Say Y here to support the on-board Intel 82596 ethernet controller ++ built into SNI RM machines. ++ ++config MIPS_JAZZ_SONIC ++ tristate "MIPS JAZZ onboard SONIC Ethernet support" ++ depends on MACH_JAZZ ++ help ++ This is the driver for the onboard card of MIPS Magnum 4000, ++ Acer PICA, Olivetti M700-10 and a few other identical OEM systems. ++ ++config MIPS_AU1X00_ENET ++ bool "MIPS AU1000 Ethernet support" ++ depends on SOC_AU1X00 ++ select PHYLIB ++ select CRC32 ++ help ++ If you have an Alchemy Semi AU1X00 based system ++ say Y. Otherwise, say N. ++ ++config SGI_IOC3_ETH ++ bool "SGI IOC3 Ethernet" ++ depends on PCI && SGI_IP27 ++ select CRC32 ++ select MII ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++config MIPS_SIM_NET ++ tristate "MIPS simulator Network device" ++ depends on MIPS_SIM ++ help ++ The MIPSNET device is a simple Ethernet network device which is ++ emulated by the MIPS Simulator. ++ If you are not using a MIPSsim or are unsure, say N. ++ ++config SGI_O2MACE_ETH ++ tristate "SGI O2 MACE Fast Ethernet support" ++ depends on SGI_IP32=y ++ ++config STNIC ++ tristate "National DP83902AV support" ++ depends on SUPERH ++ select CRC32 ++ help ++ Support for cards based on the National Semiconductor DP83902AV ++ ST-NIC Serial Network Interface Controller for Twisted Pair. This ++ is a 10Mbit/sec Ethernet controller. Product overview and specs at ++ . ++ ++ If unsure, say N. ++ ++config SUNLANCE ++ tristate "Sun LANCE support" ++ depends on SBUS ++ select CRC32 ++ help ++ This driver supports the "le" interface present on all 32-bit Sparc ++ systems, on some older Ultra systems and as an Sbus option. These ++ cards are based on the AMD Lance chipset, which is better known ++ via the NE2100 cards. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called sunlance. ++ ++config HAPPYMEAL ++ tristate "Sun Happy Meal 10/100baseT support" ++ depends on SBUS || PCI ++ select CRC32 ++ help ++ This driver supports the "hme" interface present on most Ultra ++ systems and as an option on older Sbus systems. This driver supports ++ both PCI and Sbus devices. This driver also supports the "qfe" quad ++ 100baseT device available in both PCI and Sbus configurations. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called sunhme. ++ ++config SUNBMAC ++ tristate "Sun BigMAC 10/100baseT support (EXPERIMENTAL)" ++ depends on SBUS && EXPERIMENTAL ++ select CRC32 ++ help ++ This driver supports the "be" interface available as an Sbus option. ++ This is Sun's older 100baseT Ethernet device. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called sunbmac. ++ ++config SUNQE ++ tristate "Sun QuadEthernet support" ++ depends on SBUS ++ select CRC32 ++ help ++ This driver supports the "qe" 10baseT Ethernet device, available as ++ an Sbus option. Note that this is not the same as Quad FastEthernet ++ "qfe" which is supported by the Happy Meal driver instead. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called sunqe. ++ ++config SUNGEM ++ tristate "Sun GEM support" ++ depends on PCI ++ select CRC32 ++ help ++ Support for the Sun GEM chip, aka Sun GigabitEthernet/P 2.0. See also ++ . ++ ++config CASSINI ++ tristate "Sun Cassini support" ++ depends on PCI ++ select CRC32 ++ help ++ Support for the Sun Cassini chip, aka Sun GigaSwift Ethernet. See also ++ ++ ++config SUNVNET ++ tristate "Sun Virtual Network support" ++ depends on SUN_LDOMS ++ help ++ Support for virtual network devices under Sun Logical Domains. ++ ++config NET_VENDOR_3COM ++ bool "3COM cards" ++ depends on ISA || EISA || MCA || PCI ++ help ++ If you have a network (Ethernet) card belonging to this class, say Y ++ and read the Ethernet-HOWTO, available from ++ . ++ ++ Note that the answer to this question doesn't directly affect the ++ kernel: saying N will just cause the configurator to skip all ++ the questions about 3COM cards. If you say Y, you will be asked for ++ your specific card in the following questions. ++ ++config EL1 ++ tristate "3c501 \"EtherLink\" support" ++ depends on NET_VENDOR_3COM && ISA ++ ---help--- ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . Also, consider buying a ++ new card, since the 3c501 is slow, broken, and obsolete: you will ++ have problems. Some people suggest to ping ("man ping") a nearby ++ machine every minute ("man cron") when using this card. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called 3c501. ++ ++config EL2 ++ tristate "3c503 \"EtherLink II\" support" ++ depends on NET_VENDOR_3COM && ISA ++ select CRC32 ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called 3c503. ++ ++config ELPLUS ++ tristate "3c505 \"EtherLink Plus\" support" ++ depends on NET_VENDOR_3COM && ISA && ISA_DMA_API ++ ---help--- ++ Information about this network (Ethernet) card can be found in ++ . If you have a card of ++ this type, say Y and read the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called 3c505. ++ ++config EL16 ++ tristate "3c507 \"EtherLink 16\" support (EXPERIMENTAL)" ++ depends on NET_VENDOR_3COM && ISA && EXPERIMENTAL ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called 3c507. ++ ++config EL3 ++ tristate "3c509/3c529 (MCA)/3c579 \"EtherLink III\" support" ++ depends on NET_VENDOR_3COM && (ISA || EISA || MCA) ++ ---help--- ++ If you have a network (Ethernet) card belonging to the 3Com ++ EtherLinkIII series, say Y and read the Ethernet-HOWTO, available ++ from . ++ ++ If your card is not working you may need to use the DOS ++ setup disk to disable Plug & Play mode, and to select the default ++ media type. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called 3c509. ++ ++config 3C515 ++ tristate "3c515 ISA \"Fast EtherLink\"" ++ depends on NET_VENDOR_3COM && (ISA || EISA) && ISA_DMA_API ++ help ++ If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet ++ network card, say Y and read the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called 3c515. ++ ++config ELMC ++ tristate "3c523 \"EtherLink/MC\" support" ++ depends on NET_VENDOR_3COM && MCA_LEGACY ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called 3c523. ++ ++config ELMC_II ++ tristate "3c527 \"EtherLink/MC 32\" support (EXPERIMENTAL)" ++ depends on NET_VENDOR_3COM && MCA && MCA_LEGACY ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called 3c527. ++ ++config VORTEX ++ tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support" ++ depends on NET_VENDOR_3COM && (PCI || EISA) ++ select MII ++ ---help--- ++ This option enables driver support for a large number of 10Mbps and ++ 10/100Mbps EISA, PCI and PCMCIA 3Com network cards: ++ ++ "Vortex" (Fast EtherLink 3c590/3c592/3c595/3c597) EISA and PCI ++ "Boomerang" (EtherLink XL 3c900 or 3c905) PCI ++ "Cyclone" (3c540/3c900/3c905/3c980/3c575/3c656) PCI and Cardbus ++ "Tornado" (3c905) PCI ++ "Hurricane" (3c555/3cSOHO) PCI ++ ++ If you have such a card, say Y and read the Ethernet-HOWTO, ++ available from . More ++ specific information is in ++ and in the comments at ++ the beginning of . ++ ++ To compile this support as a module, choose M here. ++ ++config TYPHOON ++ tristate "3cr990 series \"Typhoon\" support" ++ depends on NET_VENDOR_3COM && PCI ++ select CRC32 ++ ---help--- ++ This option enables driver support for the 3cr990 series of cards: ++ ++ 3C990-TX, 3CR990-TX-95, 3CR990-TX-97, 3CR990-FX-95, 3CR990-FX-97, ++ 3CR990SVR, 3CR990SVR95, 3CR990SVR97, 3CR990-FX-95 Server, ++ 3CR990-FX-97 Server, 3C990B-TX-M, 3C990BSVR ++ ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called typhoon. ++ ++config LANCE ++ tristate "AMD LANCE and PCnet (AT1500 and NE2100) support" ++ depends on ISA && ISA_DMA_API ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . Some LinkSys cards are ++ of this type. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called lance. This is recommended. ++ ++config NET_VENDOR_SMC ++ bool "Western Digital/SMC cards" ++ depends on ISA || MCA || EISA || MAC ++ help ++ If you have a network (Ethernet) card belonging to this class, say Y ++ and read the Ethernet-HOWTO, available from ++ . ++ ++ Note that the answer to this question doesn't directly affect the ++ kernel: saying N will just cause the configurator to skip all ++ the questions about Western Digital cards. If you say Y, you will be ++ asked for your specific card in the following questions. ++ ++config WD80x3 ++ tristate "WD80*3 support" ++ depends on NET_VENDOR_SMC && ISA ++ select CRC32 ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called wd. ++ ++config ULTRAMCA ++ tristate "SMC Ultra MCA support" ++ depends on NET_VENDOR_SMC && MCA ++ select CRC32 ++ help ++ If you have a network (Ethernet) card of this type and are running ++ an MCA based system (PS/2), say Y and read the Ethernet-HOWTO, ++ available from . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called smc-mca. ++ ++config ULTRA ++ tristate "SMC Ultra support" ++ depends on NET_VENDOR_SMC && ISA ++ select CRC32 ++ ---help--- ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ Important: There have been many reports that, with some motherboards ++ mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible, ++ such as some BusLogic models) causes corruption problems with many ++ operating systems. The Linux smc-ultra driver has a work-around for ++ this but keep it in mind if you have such a SCSI card and have ++ problems. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called smc-ultra. ++ ++config ULTRA32 ++ tristate "SMC Ultra32 EISA support" ++ depends on NET_VENDOR_SMC && EISA ++ select CRC32 ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called smc-ultra32. ++ ++config BFIN_MAC ++ tristate "Blackfin 536/537 on-chip mac support" ++ depends on NET_ETHERNET && (BF537 || BF536) && (!BF537_PORT_H) ++ select CRC32 ++ select MII ++ select PHYLIB ++ select BFIN_MAC_USE_L1 if DMA_UNCACHED_NONE ++ help ++ This is the driver for blackfin on-chip mac device. Say Y if you want it ++ compiled into the kernel. This driver is also available as a module ++ ( = code which can be inserted in and removed from the running kernel ++ whenever you want). The module will be called bfin_mac. ++ ++config BFIN_MAC_USE_L1 ++ bool "Use L1 memory for rx/tx packets" ++ depends on BFIN_MAC && BF537 ++ default y ++ help ++ To get maximum network performance, you should use L1 memory as rx/tx buffers. ++ Say N here if you want to reserve L1 memory for other uses. ++ ++config BFIN_TX_DESC_NUM ++ int "Number of transmit buffer packets" ++ depends on BFIN_MAC ++ range 6 10 if BFIN_MAC_USE_L1 ++ range 10 100 ++ default "10" ++ help ++ Set the number of buffer packets used in driver. ++ ++config BFIN_RX_DESC_NUM ++ int "Number of receive buffer packets" ++ depends on BFIN_MAC ++ range 20 100 if BFIN_MAC_USE_L1 ++ range 20 800 ++ default "20" ++ help ++ Set the number of buffer packets used in driver. ++ ++config BFIN_MAC_RMII ++ bool "RMII PHY Interface (EXPERIMENTAL)" ++ depends on BFIN_MAC && EXPERIMENTAL ++ default n ++ help ++ Use Reduced PHY MII Interface ++ ++config SMC9194 ++ tristate "SMC 9194 support" ++ depends on NET_VENDOR_SMC && (ISA || MAC && BROKEN) ++ select CRC32 ++ ---help--- ++ This is support for the SMC9xxx based Ethernet cards. Choose this ++ option if you have a DELL laptop with the docking station, or ++ another SMC9192/9194 based chipset. Say Y if you want it compiled ++ into the kernel, and read the file ++ and the Ethernet-HOWTO, ++ available from . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called smc9194. ++ ++config SMC91X ++ tristate "SMC 91C9x/91C1xxx support" ++ select CRC32 ++ select MII ++ depends on ARM || REDWOOD_5 || REDWOOD_6 || M32R || SUPERH || SOC_AU1X00 || BLACKFIN ++ help ++ This is a driver for SMC's 91x series of Ethernet chipsets, ++ including the SMC91C94 and the SMC91C111. Say Y if you want it ++ compiled into the kernel, and read the file ++ and the Ethernet-HOWTO, ++ available from . ++ ++ This driver is also available as a module ( = code which can be ++ inserted in and removed from the running kernel whenever you want). ++ The module will be called smc91x. If you want to compile it as a ++ module, say M here and read . ++ ++config NET_NETX ++ tristate "NetX Ethernet support" ++ select MII ++ depends on ARCH_NETX ++ help ++ This is support for the Hilscher netX builtin Ethernet ports ++ ++ To compile this driver as a module, choose M here. The module ++ will be called netx-eth. ++ ++config DM9000 ++ tristate "DM9000 support" ++ depends on ARM || BLACKFIN || MIPS ++ select CRC32 ++ select MII ++ ---help--- ++ Support for DM9000 chipset. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called dm9000. ++ ++config SMC911X ++ tristate "SMSC LAN911[5678] support" ++ select CRC32 ++ select MII ++ depends on ARCH_PXA || SH_MAGIC_PANEL_R2 ++ help ++ This is a driver for SMSC's LAN911x series of Ethernet chipsets ++ including the new LAN9115, LAN9116, LAN9117, and LAN9118. ++ Say Y if you want it compiled into the kernel, ++ and read the Ethernet-HOWTO, available from ++ . ++ ++ This driver is also available as a module. The module will be ++ called smc911x. If you want to compile it as a module, say M ++ here and read ++ ++config NET_VENDOR_RACAL ++ bool "Racal-Interlan (Micom) NI cards" ++ depends on ISA ++ help ++ If you have a network (Ethernet) card belonging to this class, such ++ as the NI5010, NI5210 or NI6210, say Y and read the Ethernet-HOWTO, ++ available from . ++ ++ Note that the answer to this question doesn't directly affect the ++ kernel: saying N will just cause the configurator to skip all ++ the questions about NI cards. If you say Y, you will be asked for ++ your specific card in the following questions. ++ ++config NI5010 ++ tristate "NI5010 support (EXPERIMENTAL)" ++ depends on NET_VENDOR_RACAL && ISA && EXPERIMENTAL && BROKEN_ON_SMP ++ ---help--- ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . Note that this is still ++ experimental code. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called ni5010. ++ ++config NI52 ++ tristate "NI5210 support" ++ depends on NET_VENDOR_RACAL && ISA ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called ni52. ++ ++config NI65 ++ tristate "NI6510 support" ++ depends on NET_VENDOR_RACAL && ISA && ISA_DMA_API ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called ni65. ++ ++source "drivers/net/tulip/Kconfig" ++ ++config AT1700 ++ tristate "AT1700/1720 support (EXPERIMENTAL)" ++ depends on (ISA || MCA_LEGACY) && EXPERIMENTAL ++ select CRC32 ++ ---help--- ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called at1700. ++ ++config DEPCA ++ tristate "DEPCA, DE10x, DE200, DE201, DE202, DE422 support" ++ depends on ISA || EISA || MCA ++ select CRC32 ++ ---help--- ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ as well as ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called depca. ++ ++config HP100 ++ tristate "HP 10/100VG PCLAN (ISA, EISA, PCI) support" ++ depends on ISA || EISA || PCI ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called hp100. ++ ++config NET_ISA ++ bool "Other ISA cards" ++ depends on ISA ++ ---help--- ++ If your network (Ethernet) card hasn't been mentioned yet and its ++ bus system (that's the way the cards talks to the other components ++ of your computer) is ISA (as opposed to EISA, VLB or PCI), say Y. ++ Make sure you know the name of your card. Read the Ethernet-HOWTO, ++ available from . ++ ++ If unsure, say Y. ++ ++ Note that the answer to this question doesn't directly affect the ++ kernel: saying N will just cause the configurator to skip all ++ the remaining ISA network card questions. If you say Y, you will be ++ asked for your specific card in the following questions. ++ ++config E2100 ++ tristate "Cabletron E21xx support" ++ depends on NET_ISA ++ select CRC32 ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called e2100. ++ ++config EWRK3 ++ tristate "EtherWORKS 3 (DE203, DE204, DE205) support" ++ depends on NET_ISA ++ select CRC32 ++ ---help--- ++ This driver supports the DE203, DE204 and DE205 network (Ethernet) ++ cards. If this is for you, say Y and read ++ in the kernel source as ++ well as the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called ewrk3. ++ ++config EEXPRESS ++ tristate "EtherExpress 16 support" ++ depends on NET_ISA ++ ---help--- ++ If you have an EtherExpress16 network (Ethernet) card, say Y and ++ read the Ethernet-HOWTO, available from ++ . Note that the Intel ++ EtherExpress16 card used to be regarded as a very poor choice ++ because the driver was very unreliable. We now have a new driver ++ that should do better. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called eexpress. ++ ++config EEXPRESS_PRO ++ tristate "EtherExpressPro support/EtherExpress 10 (i82595) support" ++ depends on NET_ISA ++ ---help--- ++ If you have a network (Ethernet) card of this type, say Y. This ++ driver supports Intel i82595{FX,TX} based boards. Note however ++ that the EtherExpress PRO/100 Ethernet card has its own separate ++ driver. Please read the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called eepro. ++ ++config HPLAN_PLUS ++ tristate "HP PCLAN+ (27247B and 27252A) support" ++ depends on NET_ISA ++ select CRC32 ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called hp-plus. ++ ++config HPLAN ++ tristate "HP PCLAN (27245 and other 27xxx series) support" ++ depends on NET_ISA ++ select CRC32 ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called hp. ++ ++config LP486E ++ tristate "LP486E on board Ethernet" ++ depends on NET_ISA ++ help ++ Say Y here to support the 82596-based on-board Ethernet controller ++ for the Panther motherboard, which is one of the two shipped in the ++ Intel Professional Workstation. ++ ++config ETH16I ++ tristate "ICL EtherTeam 16i/32 support" ++ depends on NET_ISA ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called eth16i. ++ ++config NE2000 ++ tristate "NE2000/NE1000 support" ++ depends on NET_ISA || (Q40 && m) || M32R || TOSHIBA_RBTX4927 || TOSHIBA_RBTX4938 ++ select CRC32 ++ ---help--- ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . Many Ethernet cards ++ without a specific driver are compatible with NE2000. ++ ++ If you have a PCI NE2000 card however, say N here and Y to "PCI ++ NE2000 and clone support" under "EISA, VLB, PCI and on board ++ controllers" below. If you have a NE2000 card and are running on ++ an MCA system (a bus system used on some IBM PS/2 computers and ++ laptops), say N here and Y to "NE/2 (ne2000 MCA version) support", ++ below. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called ne. ++ ++config ZNET ++ tristate "Zenith Z-Note support (EXPERIMENTAL)" ++ depends on NET_ISA && EXPERIMENTAL && ISA_DMA_API ++ help ++ The Zenith Z-Note notebook computer has a built-in network ++ (Ethernet) card, and this is the Linux driver for it. Note that the ++ IBM Thinkpad 300 is compatible with the Z-Note and is also supported ++ by this driver. Read the Ethernet-HOWTO, available from ++ . ++ ++config SEEQ8005 ++ tristate "SEEQ8005 support (EXPERIMENTAL)" ++ depends on NET_ISA && EXPERIMENTAL ++ help ++ This is a driver for the SEEQ 8005 network (Ethernet) card. If this ++ is for you, read the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called seeq8005. ++ ++config NE2_MCA ++ tristate "NE/2 (ne2000 MCA version) support" ++ depends on MCA_LEGACY ++ select CRC32 ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called ne2. ++ ++config IBMLANA ++ tristate "IBM LAN Adapter/A support" ++ depends on MCA && MCA_LEGACY ++ ---help--- ++ This is a Micro Channel Ethernet adapter. You need to set ++ CONFIG_MCA to use this driver. It is both available as an in-kernel ++ driver and as a module. ++ ++ To compile this driver as a module, choose M here. The only ++ currently supported card is the IBM LAN Adapter/A for Ethernet. It ++ will both support 16K and 32K memory windows, however a 32K window ++ gives a better security against packet losses. Usage of multiple ++ boards with this driver should be possible, but has not been tested ++ up to now due to lack of hardware. ++ ++config IBMVETH ++ tristate "IBM LAN Virtual Ethernet support" ++ depends on PPC_PSERIES ++ ---help--- ++ This driver supports virtual ethernet adapters on newer IBM iSeries ++ and pSeries systems. ++ ++ To compile this driver as a module, choose M here. The module will ++ be called ibmveth. ++ ++source "drivers/net/ibm_emac/Kconfig" ++source "drivers/net/ibm_newemac/Kconfig" ++ ++config NET_PCI ++ bool "EISA, VLB, PCI and on board controllers" ++ depends on ISA || EISA || PCI ++ help ++ This is another class of network cards which attach directly to the ++ bus. If you have one of those, say Y and read the Ethernet-HOWTO, ++ available from . ++ ++ Note that the answer to this question doesn't directly affect the ++ kernel: saying N will just cause the configurator to skip all ++ the questions about this class of network cards. If you say Y, you ++ will be asked for your specific card in the following questions. If ++ you are unsure, say Y. ++ ++config PCNET32 ++ tristate "AMD PCnet32 PCI support" ++ depends on NET_PCI && PCI ++ select CRC32 ++ select MII ++ help ++ If you have a PCnet32 or PCnetPCI based network (Ethernet) card, ++ answer Y here and read the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called pcnet32. ++ ++config PCNET32_NAPI ++ bool "Use RX polling (NAPI)" ++ depends on PCNET32 ++ help ++ NAPI is a new driver API designed to reduce CPU and interrupt load ++ when the driver is receiving lots of packets from the card. It is ++ still somewhat experimental and thus not yet enabled by default. ++ ++ If your estimated Rx load is 10kpps or more, or if the card will be ++ deployed on potentially unfriendly networks (e.g. in a firewall), ++ then say Y here. ++ ++ If in doubt, say N. ++ ++config AMD8111_ETH ++ tristate "AMD 8111 (new PCI lance) support" ++ depends on NET_PCI && PCI ++ select CRC32 ++ select MII ++ help ++ If you have an AMD 8111-based PCI lance ethernet card, ++ answer Y here and read the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called amd8111e. ++ ++config AMD8111E_NAPI ++ bool "Use RX polling (NAPI)" ++ depends on AMD8111_ETH ++ help ++ NAPI is a new driver API designed to reduce CPU and interrupt load ++ when the driver is receiving lots of packets from the card. It is ++ still somewhat experimental and thus not yet enabled by default. ++ ++ If your estimated Rx load is 10kpps or more, or if the card will be ++ deployed on potentially unfriendly networks (e.g. in a firewall), ++ then say Y here. ++ ++ If in doubt, say N. ++ ++config ADAPTEC_STARFIRE ++ tristate "Adaptec Starfire/DuraLAN support" ++ depends on NET_PCI && PCI ++ select CRC32 ++ select MII ++ help ++ Say Y here if you have an Adaptec Starfire (or DuraLAN) PCI network ++ adapter. The DuraLAN chip is used on the 64 bit PCI boards from ++ Adaptec e.g. the ANA-6922A. The older 32 bit boards use the tulip ++ driver. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called starfire. This is recommended. ++ ++config ADAPTEC_STARFIRE_NAPI ++ bool "Use Rx Polling (NAPI) (EXPERIMENTAL)" ++ depends on ADAPTEC_STARFIRE && EXPERIMENTAL ++ help ++ NAPI is a new driver API designed to reduce CPU and interrupt load ++ when the driver is receiving lots of packets from the card. It is ++ still somewhat experimental and thus not yet enabled by default. ++ ++ If your estimated Rx load is 10kpps or more, or if the card will be ++ deployed on potentially unfriendly networks (e.g. in a firewall), ++ then say Y here. ++ ++ If in doubt, say N. ++ ++config AC3200 ++ tristate "Ansel Communications EISA 3200 support (EXPERIMENTAL)" ++ depends on NET_PCI && (ISA || EISA) && EXPERIMENTAL ++ select CRC32 ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called ac3200. ++ ++config APRICOT ++ tristate "Apricot Xen-II on board Ethernet" ++ depends on NET_PCI && ISA ++ help ++ If you have a network (Ethernet) controller of this type, say Y and ++ read the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called apricot. ++ ++config B44 ++ tristate "Broadcom 440x/47xx ethernet support" ++ depends on SSB_POSSIBLE ++ select SSB ++ select MII ++ help ++ If you have a network (Ethernet) controller of this type, say Y ++ or M and read the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called b44. ++ ++# Auto-select SSB PCI-HOST support, if possible ++config B44_PCI_AUTOSELECT ++ bool ++ depends on B44 && SSB_PCIHOST_POSSIBLE ++ select SSB_PCIHOST ++ default y ++ ++# Auto-select SSB PCICORE driver, if possible ++config B44_PCICORE_AUTOSELECT ++ bool ++ depends on B44 && SSB_DRIVER_PCICORE_POSSIBLE ++ select SSB_DRIVER_PCICORE ++ default y ++ ++config B44_PCI ++ bool ++ depends on B44_PCI_AUTOSELECT && B44_PCICORE_AUTOSELECT ++ default y ++ ++config FORCEDETH ++ tristate "nForce Ethernet support" ++ depends on NET_PCI && PCI ++ help ++ If you have a network (Ethernet) controller of this type, say Y and ++ read the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called forcedeth. ++ ++config FORCEDETH_NAPI ++ bool "Use Rx Polling (NAPI) (EXPERIMENTAL)" ++ depends on FORCEDETH && EXPERIMENTAL ++ help ++ NAPI is a new driver API designed to reduce CPU and interrupt load ++ when the driver is receiving lots of packets from the card. It is ++ still somewhat experimental and thus not yet enabled by default. ++ ++ If your estimated Rx load is 10kpps or more, or if the card will be ++ deployed on potentially unfriendly networks (e.g. in a firewall), ++ then say Y here. ++ ++ If in doubt, say N. ++ ++config CS89x0 ++ tristate "CS89x0 support" ++ depends on NET_PCI && (ISA || MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X) ++ ---help--- ++ Support for CS89x0 chipset based Ethernet cards. If you have a ++ network (Ethernet) card of this type, say Y and read the ++ Ethernet-HOWTO, available from ++ as well as ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called cs89x0. ++ ++config TC35815 ++ tristate "TOSHIBA TC35815 Ethernet support" ++ depends on NET_PCI && PCI && MIPS ++ select MII ++ ++config EEPRO100 ++ tristate "EtherExpressPro/100 support (eepro100, original Becker driver)" ++ depends on NET_PCI && PCI ++ select MII ++ help ++ If you have an Intel EtherExpress PRO/100 PCI network (Ethernet) ++ card, say Y and read the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called eepro100. ++ ++ ++config E100 ++ tristate "Intel(R) PRO/100+ support" ++ depends on NET_PCI && PCI ++ select MII ++ ---help--- ++ This driver supports Intel(R) PRO/100 family of adapters. ++ To verify that your adapter is supported, find the board ID number ++ on the adapter. Look for a label that has a barcode and a number ++ in the format 123456-001 (six digits hyphen three digits). ++ ++ Use the above information and the Adapter & Driver ID Guide at: ++ ++ ++ ++ to identify the adapter. ++ ++ For the latest Intel PRO/100 network driver for Linux, see: ++ ++ ++ ++ More specific information on configuring the driver is in ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called e100. ++ ++config LNE390 ++ tristate "Mylex EISA LNE390A/B support (EXPERIMENTAL)" ++ depends on NET_PCI && EISA && EXPERIMENTAL ++ select CRC32 ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called lne390. ++ ++config FEALNX ++ tristate "Myson MTD-8xx PCI Ethernet support" ++ depends on NET_PCI && PCI ++ select CRC32 ++ select MII ++ help ++ Say Y here to support the Mysom MTD-800 family of PCI-based Ethernet ++ cards. Specifications and data at ++ . ++ ++config NATSEMI ++ tristate "National Semiconductor DP8381x series PCI Ethernet support" ++ depends on NET_PCI && PCI ++ select CRC32 ++ help ++ This driver is for the National Semiconductor DP83810 series, ++ which is used in cards from PureData, NetGear, Linksys ++ and others, including the 83815 chip. ++ More specific information and updates are available from ++ . ++ ++config NE2K_PCI ++ tristate "PCI NE2000 and clones support (see help)" ++ depends on NET_PCI && PCI ++ select CRC32 ++ ---help--- ++ This driver is for NE2000 compatible PCI cards. It will not work ++ with ISA NE2000 cards (they have their own driver, "NE2000/NE1000 ++ support" below). If you have a PCI NE2000 network (Ethernet) card, ++ say Y and read the Ethernet-HOWTO, available from ++ . ++ ++ This driver also works for the following NE2000 clone cards: ++ RealTek RTL-8029 Winbond 89C940 Compex RL2000 KTI ET32P2 ++ NetVin NV5000SC Via 86C926 SureCom NE34 Winbond ++ Holtek HT80232 Holtek HT80229 ++ ++ To compile this driver as a module, choose M here. The module ++ will be called ne2k-pci. ++ ++config NE3210 ++ tristate "Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)" ++ depends on NET_PCI && EISA && EXPERIMENTAL ++ select CRC32 ++ ---help--- ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . Note that this driver ++ will NOT WORK for NE3200 cards as they are completely different. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called ne3210. ++ ++config ES3210 ++ tristate "Racal-Interlan EISA ES3210 support (EXPERIMENTAL)" ++ depends on NET_PCI && EISA && EXPERIMENTAL ++ select CRC32 ++ help ++ If you have a network (Ethernet) card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called es3210. ++ ++config 8139CP ++ tristate "RealTek RTL-8139 C+ PCI Fast Ethernet Adapter support (EXPERIMENTAL)" ++ depends on NET_PCI && PCI && EXPERIMENTAL ++ select CRC32 ++ select MII ++ help ++ This is a driver for the Fast Ethernet PCI network cards based on ++ the RTL8139C+ chips. If you have one of those, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here: the module ++ will be called 8139cp. This is recommended. ++ ++config 8139TOO ++ tristate "RealTek RTL-8129/8130/8139 PCI Fast Ethernet Adapter support" ++ depends on NET_PCI && PCI ++ select CRC32 ++ select MII ++ ---help--- ++ This is a driver for the Fast Ethernet PCI network cards based on ++ the RTL 8129/8130/8139 chips. If you have one of those, say Y and ++ read the Ethernet-HOWTO . ++ ++ To compile this driver as a module, choose M here: the module ++ will be called 8139too. This is recommended. ++ ++config 8139TOO_PIO ++ bool "Use PIO instead of MMIO" ++ default y ++ depends on 8139TOO ++ help ++ This instructs the driver to use programmed I/O ports (PIO) instead ++ of PCI shared memory (MMIO). This can possibly solve some problems ++ in case your mainboard has memory consistency issues. If unsure, ++ say N. ++ ++config 8139TOO_TUNE_TWISTER ++ bool "Support for uncommon RTL-8139 rev. K (automatic channel equalization)" ++ depends on 8139TOO ++ help ++ This implements a function which might come in handy in case you ++ are using low quality on long cabling. It is required for RealTek ++ RTL-8139 revision K boards, and totally unused otherwise. It tries ++ to match the transceiver to the cable characteristics. This is ++ experimental since hardly documented by the manufacturer. ++ If unsure, say Y. ++ ++config 8139TOO_8129 ++ bool "Support for older RTL-8129/8130 boards" ++ depends on 8139TOO ++ help ++ This enables support for the older and uncommon RTL-8129 and ++ RTL-8130 chips, which support MII via an external transceiver, ++ instead of an internal one. Disabling this option will save some ++ memory by making the code size smaller. If unsure, say Y. ++ ++config 8139_OLD_RX_RESET ++ bool "Use older RX-reset method" ++ depends on 8139TOO ++ help ++ The 8139too driver was recently updated to contain a more rapid ++ reset sequence, in the face of severe receive errors. This "new" ++ RX-reset method should be adequate for all boards. But if you ++ experience problems, you can enable this option to restore the ++ old RX-reset behavior. If unsure, say N. ++ ++config SIS900 ++ tristate "SiS 900/7016 PCI Fast Ethernet Adapter support" ++ depends on NET_PCI && PCI ++ select CRC32 ++ select MII ++ ---help--- ++ This is a driver for the Fast Ethernet PCI network cards based on ++ the SiS 900 and SiS 7016 chips. The SiS 900 core is also embedded in ++ SiS 630 and SiS 540 chipsets. ++ ++ This driver also supports AMD 79C901 HomePNA so that you can use ++ your phone line as a network cable. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called sis900. This is recommended. ++ ++config EPIC100 ++ tristate "SMC EtherPower II" ++ depends on NET_PCI && PCI ++ select CRC32 ++ select MII ++ help ++ This driver is for the SMC EtherPower II 9432 PCI Ethernet NIC, ++ which is based on the SMC83c17x (EPIC/100). ++ More specific information and updates are available from ++ . ++ ++config SUNDANCE ++ tristate "Sundance Alta support" ++ depends on NET_PCI && PCI ++ select CRC32 ++ select MII ++ help ++ This driver is for the Sundance "Alta" chip. ++ More specific information and updates are available from ++ . ++ ++config SUNDANCE_MMIO ++ bool "Use MMIO instead of PIO" ++ depends on SUNDANCE ++ help ++ Enable memory-mapped I/O for interaction with Sundance NIC registers. ++ Do NOT enable this by default, PIO (enabled when MMIO is disabled) ++ is known to solve bugs on certain chips. ++ ++ If unsure, say N. ++ ++config TLAN ++ tristate "TI ThunderLAN support" ++ depends on NET_PCI && (PCI || EISA) && !64BIT ++ ---help--- ++ If you have a PCI Ethernet network card based on the ThunderLAN chip ++ which is supported by this driver, say Y and read the ++ Ethernet-HOWTO, available from ++ . ++ ++ Devices currently supported by this driver are Compaq Netelligent, ++ Compaq NetFlex and Olicom cards. Please read the file ++ for more details. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called tlan. ++ ++ Please email feedback to . ++ ++config VIA_RHINE ++ tristate "VIA Rhine support" ++ depends on NET_PCI && PCI ++ select CRC32 ++ select MII ++ help ++ If you have a VIA "Rhine" based network card (Rhine-I (VT86C100A), ++ Rhine-II (VT6102), or Rhine-III (VT6105)), say Y here. Rhine-type ++ Ethernet functions can also be found integrated on South Bridges ++ (e.g. VT8235). ++ ++ To compile this driver as a module, choose M here. The module ++ will be called via-rhine. ++ ++config VIA_RHINE_MMIO ++ bool "Use MMIO instead of PIO" ++ depends on VIA_RHINE ++ help ++ This instructs the driver to use PCI shared memory (MMIO) instead of ++ programmed I/O ports (PIO). Enabling this gives an improvement in ++ processing time in parts of the driver. ++ ++ If unsure, say Y. ++ ++config VIA_RHINE_NAPI ++ bool "Use Rx Polling (NAPI)" ++ depends on VIA_RHINE ++ help ++ NAPI is a new driver API designed to reduce CPU and interrupt load ++ when the driver is receiving lots of packets from the card. ++ ++ If your estimated Rx load is 10kpps or more, or if the card will be ++ deployed on potentially unfriendly networks (e.g. in a firewall), ++ then say Y here. ++ ++config LAN_SAA9730 ++ bool "Philips SAA9730 Ethernet support" ++ depends on NET_PCI && PCI && MIPS_ATLAS ++ help ++ The SAA9730 is a combined multimedia and peripheral controller used ++ in thin clients, Internet access terminals, and diskless ++ workstations. ++ See . ++ ++config SC92031 ++ tristate "Silan SC92031 PCI Fast Ethernet Adapter driver (EXPERIMENTAL)" ++ depends on NET_PCI && PCI && EXPERIMENTAL ++ select CRC32 ++ ---help--- ++ This is a driver for the Fast Ethernet PCI network cards based on ++ the Silan SC92031 chip (sometimes also called Rsltek 8139D). If you ++ have one of these, say Y here. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called sc92031. This is recommended. ++ ++config CPMAC ++ tristate "TI AR7 CPMAC Ethernet support (EXPERIMENTAL)" ++ depends on NET_ETHERNET && EXPERIMENTAL && AR7 ++ select PHYLIB ++ select FIXED_PHY ++ select FIXED_MII_100_FDX ++ help ++ TI AR7 CPMAC Ethernet support ++ ++config NET_POCKET ++ bool "Pocket and portable adapters" ++ depends on PARPORT ++ ---help--- ++ Cute little network (Ethernet) devices which attach to the parallel ++ port ("pocket adapters"), commonly used with laptops. If you have ++ one of those, say Y and read the Ethernet-HOWTO, available from ++ . ++ ++ If you want to plug a network (or some other) card into the PCMCIA ++ (or PC-card) slot of your laptop instead (PCMCIA is the standard for ++ credit card size extension cards used by all modern laptops), you ++ need the pcmcia-cs package (location contained in the file ++ ) and you can say N here. ++ ++ Laptop users should read the Linux Laptop home page at ++ or ++ Tuxmobil - Linux on Mobile Computers at . ++ ++ Note that the answer to this question doesn't directly affect the ++ kernel: saying N will just cause the configurator to skip all ++ the questions about this class of network devices. If you say Y, you ++ will be asked for your specific device in the following questions. ++ ++config ATP ++ tristate "AT-LAN-TEC/RealTek pocket adapter support" ++ depends on NET_POCKET && PARPORT && X86 ++ select CRC32 ++ ---help--- ++ This is a network (Ethernet) device which attaches to your parallel ++ port. Read as well as the Ethernet-HOWTO, ++ available from , if you ++ want to use this. If you intend to use this driver, you should have ++ said N to the "Parallel printer support", because the two drivers ++ don't like each other. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called atp. ++ ++config DE600 ++ tristate "D-Link DE600 pocket adapter support" ++ depends on NET_POCKET && PARPORT ++ ---help--- ++ This is a network (Ethernet) device which attaches to your parallel ++ port. Read as well as the ++ Ethernet-HOWTO, available from ++ , if you want to use ++ this. It is possible to have several devices share a single parallel ++ port and it is safe to compile the corresponding drivers into the ++ kernel. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called de600. ++ ++config DE620 ++ tristate "D-Link DE620 pocket adapter support" ++ depends on NET_POCKET && PARPORT ++ ---help--- ++ This is a network (Ethernet) device which attaches to your parallel ++ port. Read as well as the ++ Ethernet-HOWTO, available from ++ , if you want to use ++ this. It is possible to have several devices share a single parallel ++ port and it is safe to compile the corresponding drivers into the ++ kernel. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called de620. ++ ++config SGISEEQ ++ tristate "SGI Seeq ethernet controller support" ++ depends on SGI_IP22 ++ help ++ Say Y here if you have an Seeq based Ethernet network card. This is ++ used in many Silicon Graphics machines. ++ ++config DECLANCE ++ tristate "DEC LANCE ethernet controller support" ++ depends on MACH_DECSTATION ++ select CRC32 ++ help ++ This driver is for the series of Ethernet controllers produced by ++ DEC (now Compaq) based on the AMD Lance chipset, including the ++ DEPCA series. (This chipset is better known via the NE2100 cards.) ++ ++config 68360_ENET ++ bool "Motorola 68360 ethernet controller" ++ depends on M68360 ++ help ++ Say Y here if you want to use the built-in ethernet controller of ++ the Motorola 68360 processor. ++ ++config FEC ++ bool "FEC ethernet controller (of ColdFire CPUs)" ++ depends on M523x || M527x || M5272 || M528x || M520x ++ help ++ Say Y here if you want to use the built-in 10/100 Fast ethernet ++ controller on some Motorola ColdFire processors. ++ ++config FEC2 ++ bool "Second FEC ethernet controller (on some ColdFire CPUs)" ++ depends on FEC ++ help ++ Say Y here if you want to use the second built-in 10/100 Fast ++ ethernet controller on some Motorola ColdFire processors. ++ ++config FEC_MPC52xx ++ tristate "MPC52xx FEC driver" ++ depends on PPC_MERGE && PPC_MPC52xx && PPC_BESTCOMM_FEC ++ select CRC32 ++ select PHYLIB ++ ---help--- ++ This option enables support for the MPC5200's on-chip ++ Fast Ethernet Controller ++ If compiled as module, it will be called 'fec_mpc52xx.ko'. ++ ++config FEC_MPC52xx_MDIO ++ bool "MPC52xx FEC MDIO bus driver" ++ depends on FEC_MPC52xx ++ default y ++ ---help--- ++ The MPC5200's FEC can connect to the Ethernet either with ++ an external MII PHY chip or 10 Mbps 7-wire interface ++ (Motorola? industry standard). ++ If your board uses an external PHY connected to FEC, enable this. ++ If not sure, enable. ++ If compiled as module, it will be called 'fec_mpc52xx_phy.ko'. ++ ++config NE_H8300 ++ tristate "NE2000 compatible support for H8/300" ++ depends on H8300 ++ help ++ Say Y here if you want to use the NE2000 compatible ++ controller on the Renesas H8/300 processor. ++ ++source "drivers/net/fec_8xx/Kconfig" ++source "drivers/net/fs_enet/Kconfig" ++ ++endif # NET_ETHERNET ++ ++# ++# Gigabit Ethernet ++# ++ ++menuconfig NETDEV_1000 ++ bool "Ethernet (1000 Mbit)" ++ depends on !UML ++ default y ++ ---help--- ++ Ethernet (also called IEEE 802.3 or ISO 8802-2) is the most common ++ type of Local Area Network (LAN) in universities and companies. ++ ++ Say Y here to get to see options for Gigabit Ethernet drivers. ++ This option alone does not add any kernel code. ++ Note that drivers supporting both 100 and 1000 MBit may be listed ++ under "Ethernet (10 or 100MBit)" instead. ++ ++ If you say N, all options in this submenu will be skipped and disabled. ++ ++if NETDEV_1000 ++ ++config ACENIC ++ tristate "Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support" ++ depends on PCI ++ ---help--- ++ Say Y here if you have an Alteon AceNIC, 3Com 3C985(B), NetGear ++ GA620, SGI Gigabit or Farallon PN9000-SX PCI Gigabit Ethernet ++ adapter. The driver allows for using the Jumbo Frame option (9000 ++ bytes/frame) however it requires that your switches can handle this ++ as well. To enable Jumbo Frames, add `mtu 9000' to your ifconfig ++ line. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called acenic. ++ ++config ACENIC_OMIT_TIGON_I ++ bool "Omit support for old Tigon I based AceNICs" ++ depends on ACENIC ++ help ++ Say Y here if you only have Tigon II based AceNICs and want to leave ++ out support for the older Tigon I based cards which are no longer ++ being sold (ie. the original Alteon AceNIC and 3Com 3C985 (non B ++ version)). This will reduce the size of the driver object by ++ app. 100KB. If you are not sure whether your card is a Tigon I or a ++ Tigon II, say N here. ++ ++ The safe and default value for this is N. ++ ++config DL2K ++ tristate "DL2000/TC902x-based Gigabit Ethernet support" ++ depends on PCI ++ select CRC32 ++ help ++ This driver supports DL2000/TC902x-based Gigabit ethernet cards, ++ which includes ++ D-Link DGE-550T Gigabit Ethernet Adapter. ++ D-Link DL2000-based Gigabit Ethernet Adapter. ++ Sundance/Tamarack TC902x Gigabit Ethernet Adapter. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called dl2k. ++ ++config E1000 ++ tristate "Intel(R) PRO/1000 Gigabit Ethernet support" ++ depends on PCI ++ ---help--- ++ This driver supports Intel(R) PRO/1000 gigabit ethernet family of ++ adapters. For more information on how to identify your adapter, go ++ to the Adapter & Driver ID Guide at: ++ ++ ++ ++ For general information and support, go to the Intel support ++ website at: ++ ++ ++ ++ More specific information on configuring the driver is in ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called e1000. ++ ++config E1000_NAPI ++ bool "Use Rx Polling (NAPI)" ++ depends on E1000 ++ help ++ NAPI is a new driver API designed to reduce CPU and interrupt load ++ when the driver is receiving lots of packets from the card. It is ++ still somewhat experimental and thus not yet enabled by default. ++ ++ If your estimated Rx load is 10kpps or more, or if the card will be ++ deployed on potentially unfriendly networks (e.g. in a firewall), ++ then say Y here. ++ ++ If in doubt, say N. ++ ++config E1000_DISABLE_PACKET_SPLIT ++ bool "Disable Packet Split for PCI express adapters" ++ depends on E1000 ++ help ++ Say Y here if you want to use the legacy receive path for PCI express ++ hardware. ++ ++ If in doubt, say N. ++ ++config E1000E ++ tristate "Intel(R) PRO/1000 PCI-Express Gigabit Ethernet support" ++ depends on PCI ++ ---help--- ++ This driver supports the PCI-Express Intel(R) PRO/1000 gigabit ++ ethernet family of adapters. For PCI or PCI-X e1000 adapters, ++ use the regular e1000 driver For more information on how to ++ identify your adapter, go to the Adapter & Driver ID Guide at: ++ ++ ++ ++ For general information and support, go to the Intel support ++ website at: ++ ++ ++ ++ To compile this driver as a module, choose M here. The module ++ will be called e1000e. ++ ++config IP1000 ++ tristate "IP1000 Gigabit Ethernet support" ++ depends on PCI && EXPERIMENTAL ++ select MII ++ ---help--- ++ This driver supports IP1000 gigabit Ethernet cards. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called ipg. This is recommended. ++ ++source "drivers/net/ixp2000/Kconfig" ++ ++config MYRI_SBUS ++ tristate "MyriCOM Gigabit Ethernet support" ++ depends on SBUS ++ help ++ This driver supports MyriCOM Sbus gigabit Ethernet cards. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called myri_sbus. This is recommended. ++ ++config NS83820 ++ tristate "National Semiconductor DP83820 support" ++ depends on PCI ++ help ++ This is a driver for the National Semiconductor DP83820 series ++ of gigabit ethernet MACs. Cards using this chipset include ++ the D-Link DGE-500T, PureData's PDP8023Z-TG, SMC's SMC9462TX, ++ SOHO-GA2000T, SOHO-GA2500T. The driver supports the use of ++ zero copy. ++ ++config HAMACHI ++ tristate "Packet Engines Hamachi GNIC-II support" ++ depends on PCI ++ select MII ++ help ++ If you have a Gigabit Ethernet card of this type, say Y and read ++ the Ethernet-HOWTO, available from ++ . ++ ++ To compile this driver as a module, choose M here. The module will be ++ called hamachi. ++ ++config YELLOWFIN ++ tristate "Packet Engines Yellowfin Gigabit-NIC support (EXPERIMENTAL)" ++ depends on PCI && EXPERIMENTAL ++ select CRC32 ++ ---help--- ++ Say Y here if you have a Packet Engines G-NIC PCI Gigabit Ethernet ++ adapter or the SYM53C885 Ethernet controller. The Gigabit adapter is ++ used by the Beowulf Linux cluster project. See ++ for more ++ information about this driver in particular and Beowulf in general. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called yellowfin. This is recommended. ++ ++config R8169 ++ tristate "Realtek 8169 gigabit ethernet support" ++ depends on PCI ++ select CRC32 ++ ---help--- ++ Say Y here if you have a Realtek 8169 PCI Gigabit Ethernet adapter. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called r8169. This is recommended. ++ ++config R8169_NAPI ++ bool "Use Rx Polling (NAPI) (EXPERIMENTAL)" ++ depends on R8169 && EXPERIMENTAL ++ help ++ NAPI is a new driver API designed to reduce CPU and interrupt load ++ when the driver is receiving lots of packets from the card. It is ++ still somewhat experimental and thus not yet enabled by default. ++ ++ If your estimated Rx load is 10kpps or more, or if the card will be ++ deployed on potentially unfriendly networks (e.g. in a firewall), ++ then say Y here. ++ ++ If in doubt, say N. ++ ++config R8169_VLAN ++ bool "VLAN support" ++ depends on R8169 && VLAN_8021Q ++ ---help--- ++ Say Y here for the r8169 driver to support the functions required ++ by the kernel 802.1Q code. ++ ++ If in doubt, say Y. ++ ++config SB1250_MAC ++ tristate "SB1250 Gigabit Ethernet support" ++ depends on SIBYTE_SB1xxx_SOC ++ select PHYLIB ++ ---help--- ++ This driver supports Gigabit Ethernet interfaces based on the ++ Broadcom SiByte family of System-On-a-Chip parts. They include ++ the BCM1120, BCM1125, BCM1125H, BCM1250, BCM1255, BCM1280, BCM1455 ++ and BCM1480 chips. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called sb1250-mac. ++ ++config SIS190 ++ tristate "SiS190/SiS191 gigabit ethernet support" ++ depends on PCI ++ select CRC32 ++ select MII ++ ---help--- ++ Say Y here if you have a SiS 190 PCI Fast Ethernet adapter or ++ a SiS 191 PCI Gigabit Ethernet adapter. Both are expected to ++ appear in lan on motherboard designs which are based on SiS 965 ++ and SiS 966 south bridge. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called sis190. This is recommended. ++ ++config SKGE ++ tristate "New SysKonnect GigaEthernet support" ++ depends on PCI ++ select CRC32 ++ ---help--- ++ This driver support the Marvell Yukon or SysKonnect SK-98xx/SK-95xx ++ and related Gigabit Ethernet adapters. It is a new smaller driver ++ with better performance and more complete ethtool support. ++ ++ It does not support the link failover and network management ++ features that "portable" vendor supplied sk98lin driver does. ++ ++ This driver supports adapters based on the original Yukon chipset: ++ Marvell 88E8001, Belkin F5D5005, CNet GigaCard, DLink DGE-530T, ++ Linksys EG1032/EG1064, 3Com 3C940/3C940B, SysKonnect SK-9871/9872. ++ ++ It does not support the newer Yukon2 chipset: a separate driver, ++ sky2, is provided for Yukon2-based adapters. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called skge. This is recommended. ++ ++config SKGE_DEBUG ++ bool "Debugging interface" ++ depends on SKGE && DEBUG_FS ++ help ++ This option adds the ability to dump driver state for debugging. ++ The file debugfs/skge/ethX displays the state of the internal ++ transmit and receive rings. ++ ++ If unsure, say N. ++ ++config SKY2 ++ tristate "SysKonnect Yukon2 support" ++ depends on PCI ++ select CRC32 ++ ---help--- ++ This driver supports Gigabit Ethernet adapters based on the ++ Marvell Yukon 2 chipset: ++ Marvell 88E8021/88E8022/88E8035/88E8036/88E8038/88E8050/88E8052/ ++ 88E8053/88E8055/88E8061/88E8062, SysKonnect SK-9E21D/SK-9S21 ++ ++ There is companion driver for the older Marvell Yukon and ++ Genesis based adapters: skge. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called sky2. This is recommended. ++ ++config SKY2_DEBUG ++ bool "Debugging interface" ++ depends on SKY2 && DEBUG_FS ++ help ++ This option adds the ability to dump driver state for debugging. ++ The file debugfs/sky2/ethX displays the state of the internal ++ transmit and receive rings. ++ ++ If unsure, say N. ++ ++config SK98LIN ++ tristate "Marvell Yukon Chipset / SysKonnect SK-98xx Support (DEPRECATED)" ++ depends on PCI ++ ---help--- ++ Say Y here if you have a Marvell Yukon or SysKonnect SK-98xx/SK-95xx ++ compliant Gigabit Ethernet Adapter. ++ ++ This driver supports the original Yukon chipset. This driver is ++ deprecated and will be removed from the kernel in the near future, ++ it has been replaced by the skge driver. skge is cleaner and ++ seems to work better. ++ ++ This driver does not support the newer Yukon2 chipset. A separate ++ driver, sky2, is provided to support Yukon2-based adapters. ++ ++ The following adapters are supported by this driver: ++ - 3Com 3C940 Gigabit LOM Ethernet Adapter ++ - 3Com 3C941 Gigabit LOM Ethernet Adapter ++ - Allied Telesyn AT-2970LX Gigabit Ethernet Adapter ++ - Allied Telesyn AT-2970LX/2SC Gigabit Ethernet Adapter ++ - Allied Telesyn AT-2970SX Gigabit Ethernet Adapter ++ - Allied Telesyn AT-2970SX/2SC Gigabit Ethernet Adapter ++ - Allied Telesyn AT-2970TX Gigabit Ethernet Adapter ++ - Allied Telesyn AT-2970TX/2TX Gigabit Ethernet Adapter ++ - Allied Telesyn AT-2971SX Gigabit Ethernet Adapter ++ - Allied Telesyn AT-2971T Gigabit Ethernet Adapter ++ - Belkin Gigabit Desktop Card 10/100/1000Base-T Adapter, Copper RJ-45 ++ - EG1032 v2 Instant Gigabit Network Adapter ++ - EG1064 v2 Instant Gigabit Network Adapter ++ - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Abit) ++ - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Albatron) ++ - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Asus) ++ - Marvell 88E8001 Gigabit LOM Ethernet Adapter (ECS) ++ - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Epox) ++ - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Foxconn) ++ - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Gigabyte) ++ - Marvell 88E8001 Gigabit LOM Ethernet Adapter (Iwill) ++ - Marvell 88E8050 Gigabit LOM Ethernet Adapter (Intel) ++ - Marvell RDK-8001 Adapter ++ - Marvell RDK-8002 Adapter ++ - Marvell RDK-8003 Adapter ++ - Marvell RDK-8004 Adapter ++ - Marvell RDK-8006 Adapter ++ - Marvell RDK-8007 Adapter ++ - Marvell RDK-8008 Adapter ++ - Marvell RDK-8009 Adapter ++ - Marvell RDK-8010 Adapter ++ - Marvell RDK-8011 Adapter ++ - Marvell RDK-8012 Adapter ++ - Marvell RDK-8052 Adapter ++ - Marvell Yukon Gigabit Ethernet 10/100/1000Base-T Adapter (32 bit) ++ - Marvell Yukon Gigabit Ethernet 10/100/1000Base-T Adapter (64 bit) ++ - N-Way PCI-Bus Giga-Card 1000/100/10Mbps(L) ++ - SK-9521 10/100/1000Base-T Adapter ++ - SK-9521 V2.0 10/100/1000Base-T Adapter ++ - SK-9821 Gigabit Ethernet Server Adapter (SK-NET GE-T) ++ - SK-9821 V2.0 Gigabit Ethernet 10/100/1000Base-T Adapter ++ - SK-9822 Gigabit Ethernet Server Adapter (SK-NET GE-T dual link) ++ - SK-9841 Gigabit Ethernet Server Adapter (SK-NET GE-LX) ++ - SK-9841 V2.0 Gigabit Ethernet 1000Base-LX Adapter ++ - SK-9842 Gigabit Ethernet Server Adapter (SK-NET GE-LX dual link) ++ - SK-9843 Gigabit Ethernet Server Adapter (SK-NET GE-SX) ++ - SK-9843 V2.0 Gigabit Ethernet 1000Base-SX Adapter ++ - SK-9844 Gigabit Ethernet Server Adapter (SK-NET GE-SX dual link) ++ - SK-9851 V2.0 Gigabit Ethernet 1000Base-SX Adapter ++ - SK-9861 Gigabit Ethernet Server Adapter (SK-NET GE-SX Volition) ++ - SK-9861 V2.0 Gigabit Ethernet 1000Base-SX Adapter ++ - SK-9862 Gigabit Ethernet Server Adapter (SK-NET GE-SX Volition dual link) ++ - SK-9871 Gigabit Ethernet Server Adapter (SK-NET GE-ZX) ++ - SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter ++ - SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) ++ - SMC EZ Card 1000 (SMC9452TXV.2) ++ ++ The adapters support Jumbo Frames. ++ The dual link adapters support link-failover and dual port features. ++ Both Marvell Yukon and SysKonnect SK-98xx/SK-95xx adapters support ++ the scatter-gather functionality with sendfile(). Please refer to ++ for more information about ++ optional driver parameters. ++ Questions concerning this driver may be addressed to: ++ ++ ++ If you want to compile this driver as a module ( = code which can be ++ inserted in and removed from the running kernel whenever you want), ++ say M here and read . The module will ++ be called sk98lin. This is recommended. ++ ++config VIA_VELOCITY ++ tristate "VIA Velocity support" ++ depends on PCI ++ select CRC32 ++ select CRC_CCITT ++ select MII ++ help ++ If you have a VIA "Velocity" based network card say Y here. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called via-velocity. ++ ++config TIGON3 ++ tristate "Broadcom Tigon3 support" ++ depends on PCI ++ help ++ This driver supports Broadcom Tigon3 based gigabit Ethernet cards. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called tg3. This is recommended. ++ ++config BNX2 ++ tristate "Broadcom NetXtremeII support" ++ depends on PCI ++ select CRC32 ++ select ZLIB_INFLATE ++ help ++ This driver supports Broadcom NetXtremeII gigabit Ethernet cards. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called bnx2. This is recommended. ++ ++config SPIDER_NET ++ tristate "Spider Gigabit Ethernet driver" ++ depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB) ++ select FW_LOADER ++ help ++ This driver supports the Gigabit Ethernet chips present on the ++ Cell Processor-Based Blades from IBM. ++ ++config TSI108_ETH ++ tristate "Tundra TSI108 gigabit Ethernet support" ++ depends on TSI108_BRIDGE ++ help ++ This driver supports Tundra TSI108 gigabit Ethernet ports. ++ To compile this driver as a module, choose M here: the module ++ will be called tsi108_eth. ++ ++config GELIC_NET ++ tristate "PS3 Gigabit Ethernet driver" ++ depends on PPC_PS3 ++ help ++ This driver supports the network device on the PS3 game ++ console. This driver has built-in support for Ethernet. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ps3_gelic. ++ ++config GIANFAR ++ tristate "Gianfar Ethernet" ++ depends on 85xx || 83xx || PPC_86xx ++ select PHYLIB ++ select CRC32 ++ help ++ This driver supports the Gigabit TSEC on the MPC83xx, MPC85xx, ++ and MPC86xx family of chips, and the FEC on the 8540. ++ ++config GFAR_NAPI ++ bool "Use Rx Polling (NAPI)" ++ depends on GIANFAR ++ ++config UCC_GETH ++ tristate "Freescale QE Gigabit Ethernet" ++ depends on QUICC_ENGINE ++ select PHYLIB ++ help ++ This driver supports the Gigabit Ethernet mode of the QUICC Engine, ++ which is available on some Freescale SOCs. ++ ++config UGETH_NAPI ++ bool "Use Rx Polling (NAPI)" ++ depends on UCC_GETH ++ ++config UGETH_MAGIC_PACKET ++ bool "Magic Packet detection support" ++ depends on UCC_GETH ++ ++config UGETH_FILTERING ++ bool "Mac address filtering support" ++ depends on UCC_GETH ++ ++config UGETH_TX_ON_DEMAND ++ bool "Transmit on Demand support" ++ depends on UCC_GETH ++ ++config MV643XX_ETH ++ tristate "Marvell Discovery (643XX) and Orion ethernet support" ++ depends on MV64360 || MV64X60 || (PPC_MULTIPLATFORM && PPC32) || ARCH_ORION ++ select MII ++ help ++ This driver supports the gigabit ethernet MACs in the ++ Marvell Discovery PPC/MIPS chipset family (MV643XX) and ++ in the Marvell Orion ARM SoC family. ++ ++ Some boards that use the Discovery chipset are the Momenco ++ Ocelot C and Jaguar ATX and Pegasos II. ++ ++config QLA3XXX ++ tristate "QLogic QLA3XXX Network Driver Support" ++ depends on PCI ++ help ++ This driver supports QLogic ISP3XXX gigabit Ethernet cards. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called qla3xxx. ++ ++config ATL1 ++ tristate "Attansic L1 Gigabit Ethernet support (EXPERIMENTAL)" ++ depends on PCI && EXPERIMENTAL ++ select CRC32 ++ select MII ++ help ++ This driver supports the Attansic L1 gigabit ethernet adapter. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called atl1. ++ ++endif # NETDEV_1000 ++ ++# ++# 10 Gigabit Ethernet ++# ++ ++menuconfig NETDEV_10000 ++ bool "Ethernet (10000 Mbit)" ++ depends on !UML ++ default y ++ ---help--- ++ Say Y here to get to see options for 10 Gigabit Ethernet drivers. ++ This option alone does not add any kernel code. ++ ++ If you say N, all options in this submenu will be skipped and disabled. ++ ++if NETDEV_10000 ++ ++config CHELSIO_T1 ++ tristate "Chelsio 10Gb Ethernet support" ++ depends on PCI ++ select CRC32 ++ help ++ This driver supports Chelsio gigabit and 10-gigabit ++ Ethernet cards. More information about adapter features and ++ performance tuning is in . ++ ++ For general information about Chelsio and our products, visit ++ our website at . ++ ++ For customer support, please visit our customer support page at ++ . ++ ++ Please send feedback to . ++ ++ To compile this driver as a module, choose M here: the module ++ will be called cxgb. ++ ++config CHELSIO_T1_1G ++ bool "Chelsio gigabit Ethernet support" ++ depends on CHELSIO_T1 ++ help ++ Enables support for Chelsio's gigabit Ethernet PCI cards. If you ++ are using only 10G cards say 'N' here. ++ ++config CHELSIO_T1_NAPI ++ bool "Use Rx Polling (NAPI)" ++ depends on CHELSIO_T1 ++ default y ++ help ++ NAPI is a driver API designed to reduce CPU and interrupt load ++ when the driver is receiving lots of packets from the card. ++ ++config CHELSIO_T3 ++ tristate "Chelsio Communications T3 10Gb Ethernet support" ++ depends on PCI ++ select FW_LOADER ++ help ++ This driver supports Chelsio T3-based gigabit and 10Gb Ethernet ++ adapters. ++ ++ For general information about Chelsio and our products, visit ++ our website at . ++ ++ For customer support, please visit our customer support page at ++ . ++ ++ Please send feedback to . ++ ++ To compile this driver as a module, choose M here: the module ++ will be called cxgb3. ++ ++config EHEA ++ tristate "eHEA Ethernet support" ++ depends on IBMEBUS && INET ++ select INET_LRO ++ ---help--- ++ This driver supports the IBM pSeries eHEA ethernet adapter. ++ ++ To compile the driver as a module, choose M here. The module ++ will be called ehea. ++ ++config IXGBE ++ tristate "Intel(R) 10GbE PCI Express adapters support" ++ depends on PCI ++ ---help--- ++ This driver supports Intel(R) 10GbE PCI Express family of ++ adapters. For more information on how to identify your adapter, go ++ to the Adapter & Driver ID Guide at: ++ ++ ++ ++ For general information and support, go to the Intel support ++ website at: ++ ++ ++ ++ To compile this driver as a module, choose M here. The module ++ will be called ixgbe. ++ ++config IXGB ++ tristate "Intel(R) PRO/10GbE support" ++ depends on PCI ++ ---help--- ++ This driver supports Intel(R) PRO/10GbE family of adapters for ++ PCI-X type cards. For PCI-E type cards, use the "ixgbe" driver ++ instead. For more information on how to identify your adapter, go ++ to the Adapter & Driver ID Guide at: ++ ++ ++ ++ For general information and support, go to the Intel support ++ website at: ++ ++ ++ ++ More specific information on configuring the driver is in ++ . ++ ++ To compile this driver as a module, choose M here. The module ++ will be called ixgb. ++ ++config IXGB_NAPI ++ bool "Use Rx Polling (NAPI) (EXPERIMENTAL)" ++ depends on IXGB && EXPERIMENTAL ++ help ++ NAPI is a new driver API designed to reduce CPU and interrupt load ++ when the driver is receiving lots of packets from the card. It is ++ still somewhat experimental and thus not yet enabled by default. ++ ++ If your estimated Rx load is 10kpps or more, or if the card will be ++ deployed on potentially unfriendly networks (e.g. in a firewall), ++ then say Y here. ++ ++ If in doubt, say N. ++ ++config S2IO ++ tristate "S2IO 10Gbe XFrame NIC" ++ depends on PCI ++ ---help--- ++ This driver supports the 10Gbe XFrame NIC of S2IO. ++ More specific information on configuring the driver is in ++ . ++ ++config S2IO_NAPI ++ bool "Use Rx Polling (NAPI) (EXPERIMENTAL)" ++ depends on S2IO && EXPERIMENTAL ++ help ++ NAPI is a new driver API designed to reduce CPU and interrupt load ++ when the driver is receiving lots of packets from the card. It is ++ still somewhat experimental and thus not yet enabled by default. ++ ++ If your estimated Rx load is 10kpps or more, or if the card will be ++ deployed on potentially unfriendly networks (e.g. in a firewall), ++ then say Y here. ++ ++ If in doubt, say N. ++ ++config MYRI10GE ++ tristate "Myricom Myri-10G Ethernet support" ++ depends on PCI && INET ++ select FW_LOADER ++ select CRC32 ++ select INET_LRO ++ ---help--- ++ This driver supports Myricom Myri-10G Dual Protocol interface in ++ Ethernet mode. If the eeprom on your board is not recent enough, ++ you will need a newer firmware image. ++ You may get this image or more information, at: ++ ++ ++ ++ To compile this driver as a module, choose M here. The module ++ will be called myri10ge. ++ ++config NETXEN_NIC ++ tristate "NetXen Multi port (1/10) Gigabit Ethernet NIC" ++ depends on PCI ++ help ++ This enables the support for NetXen's Gigabit Ethernet card. ++ ++config NIU ++ tristate "Sun Neptune 10Gbit Ethernet support" ++ depends on PCI ++ help ++ This enables support for cards based upon Sun's ++ Neptune chipset. ++ ++config PASEMI_MAC ++ tristate "PA Semi 1/10Gbit MAC" ++ depends on PPC64 && PCI ++ select PHYLIB ++ help ++ This driver supports the on-chip 1/10Gbit Ethernet controller on ++ PA Semi's PWRficient line of chips. ++ ++config MLX4_CORE ++ tristate ++ depends on PCI ++ default n ++ ++config MLX4_DEBUG ++ bool "Verbose debugging output" if (MLX4_CORE && EMBEDDED) ++ depends on MLX4_CORE ++ default y ++ ---help--- ++ This option causes debugging code to be compiled into the ++ mlx4_core driver. The output can be turned on via the ++ debug_level module parameter (which can also be set after ++ the driver is loaded through sysfs). ++ ++config TEHUTI ++ tristate "Tehuti Networks 10G Ethernet" ++ depends on PCI ++ help ++ Tehuti Networks 10G Ethernet NIC ++ ++endif # NETDEV_10000 ++ ++source "drivers/net/tokenring/Kconfig" ++ ++source "drivers/net/wireless/Kconfig" ++ ++source "drivers/net/usb/Kconfig" ++ ++source "drivers/net/pcmcia/Kconfig" ++ ++source "drivers/net/wan/Kconfig" ++ ++source "drivers/atm/Kconfig" ++ ++source "drivers/s390/net/Kconfig" ++ ++config XEN_NETDEV_FRONTEND ++ tristate "Xen network device frontend driver" ++ depends on XEN ++ default y ++ help ++ The network device frontend driver allows the kernel to ++ access network devices exported exported by a virtual ++ machine containing a physical network device driver. The ++ frontend driver is intended for unprivileged guest domains; ++ if you are compiling a kernel for a Xen guest, you almost ++ certainly want to enable this. ++ ++config ISERIES_VETH ++ tristate "iSeries Virtual Ethernet driver support" ++ depends on PPC_ISERIES ++ ++config RIONET ++ tristate "RapidIO Ethernet over messaging driver support" ++ depends on RAPIDIO ++ ++config RIONET_TX_SIZE ++ int "Number of outbound queue entries" ++ depends on RIONET ++ default "128" ++ ++config RIONET_RX_SIZE ++ int "Number of inbound queue entries" ++ depends on RIONET ++ default "128" ++ ++config FDDI ++ bool "FDDI driver support" ++ depends on (PCI || EISA || TC) ++ help ++ Fiber Distributed Data Interface is a high speed local area network ++ design; essentially a replacement for high speed Ethernet. FDDI can ++ run over copper or fiber. If you are connected to such a network and ++ want a driver for the FDDI card in your computer, say Y here (and ++ then also Y to the driver for your FDDI card, below). Most people ++ will say N. ++ ++config DEFXX ++ tristate "Digital DEFTA/DEFEA/DEFPA adapter support" ++ depends on FDDI && (PCI || EISA || TC) ++ ---help--- ++ This is support for the DIGITAL series of TURBOchannel (DEFTA), ++ EISA (DEFEA) and PCI (DEFPA) controllers which can connect you ++ to a local FDDI network. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called defxx. If unsure, say N. ++ ++config DEFXX_MMIO ++ bool ++ prompt "Use MMIO instead of PIO" if PCI || EISA ++ depends on DEFXX ++ default n if PCI || EISA ++ default y ++ ---help--- ++ This instructs the driver to use EISA or PCI memory-mapped I/O ++ (MMIO) as appropriate instead of programmed I/O ports (PIO). ++ Enabling this gives an improvement in processing time in parts ++ of the driver, but it may cause problems with EISA (DEFEA) ++ adapters. TURBOchannel does not have the concept of I/O ports, ++ so MMIO is always used for these (DEFTA) adapters. ++ ++ If unsure, say N. ++ ++config SKFP ++ tristate "SysKonnect FDDI PCI support" ++ depends on FDDI && PCI ++ select BITREVERSE ++ ---help--- ++ Say Y here if you have a SysKonnect FDDI PCI adapter. ++ The following adapters are supported by this driver: ++ - SK-5521 (SK-NET FDDI-UP) ++ - SK-5522 (SK-NET FDDI-UP DAS) ++ - SK-5541 (SK-NET FDDI-FP) ++ - SK-5543 (SK-NET FDDI-LP) ++ - SK-5544 (SK-NET FDDI-LP DAS) ++ - SK-5821 (SK-NET FDDI-UP64) ++ - SK-5822 (SK-NET FDDI-UP64 DAS) ++ - SK-5841 (SK-NET FDDI-FP64) ++ - SK-5843 (SK-NET FDDI-LP64) ++ - SK-5844 (SK-NET FDDI-LP64 DAS) ++ - Netelligent 100 FDDI DAS Fibre SC ++ - Netelligent 100 FDDI SAS Fibre SC ++ - Netelligent 100 FDDI DAS UTP ++ - Netelligent 100 FDDI SAS UTP ++ - Netelligent 100 FDDI SAS Fibre MIC ++ ++ Read for information about ++ the driver. ++ ++ Questions concerning this driver can be addressed to: ++ ++ ++ To compile this driver as a module, choose M here: the module ++ will be called skfp. This is recommended. ++ ++config HIPPI ++ bool "HIPPI driver support (EXPERIMENTAL)" ++ depends on EXPERIMENTAL && INET && PCI ++ help ++ HIgh Performance Parallel Interface (HIPPI) is a 800Mbit/sec and ++ 1600Mbit/sec dual-simplex switched or point-to-point network. HIPPI ++ can run over copper (25m) or fiber (300m on multi-mode or 10km on ++ single-mode). HIPPI networks are commonly used for clusters and to ++ connect to super computers. If you are connected to a HIPPI network ++ and have a HIPPI network card in your computer that you want to use ++ under Linux, say Y here (you must also remember to enable the driver ++ for your HIPPI card below). Most people will say N here. ++ ++config ROADRUNNER ++ tristate "Essential RoadRunner HIPPI PCI adapter support (EXPERIMENTAL)" ++ depends on HIPPI && PCI ++ help ++ Say Y here if this is your PCI HIPPI network card. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called rrunner. If unsure, say N. ++ ++config ROADRUNNER_LARGE_RINGS ++ bool "Use large TX/RX rings (EXPERIMENTAL)" ++ depends on ROADRUNNER ++ help ++ If you say Y here, the RoadRunner driver will preallocate up to 2 MB ++ of additional memory to allow for fastest operation, both for ++ transmitting and receiving. This memory cannot be used by any other ++ kernel code or by user space programs. Say Y here only if you have ++ the memory. ++ ++config PLIP ++ tristate "PLIP (parallel port) support" ++ depends on PARPORT ++ ---help--- ++ PLIP (Parallel Line Internet Protocol) is used to create a ++ reasonably fast mini network consisting of two (or, rarely, more) ++ local machines. A PLIP link from a Linux box is a popular means to ++ install a Linux distribution on a machine which doesn't have a ++ CD-ROM drive (a minimal system has to be transferred with floppies ++ first). The kernels on both machines need to have this PLIP option ++ enabled for this to work. ++ ++ The PLIP driver has two modes, mode 0 and mode 1. The parallel ++ ports (the connectors at the computers with 25 holes) are connected ++ with "null printer" or "Turbo Laplink" cables which can transmit 4 ++ bits at a time (mode 0) or with special PLIP cables, to be used on ++ bidirectional parallel ports only, which can transmit 8 bits at a ++ time (mode 1); you can find the wiring of these cables in ++ . The cables can be up to ++ 15m long. Mode 0 works also if one of the machines runs DOS/Windows ++ and has some PLIP software installed, e.g. the Crynwr PLIP packet ++ driver () ++ and winsock or NCSA's telnet. ++ ++ If you want to use PLIP, say Y and read the PLIP mini-HOWTO as well ++ as the NET-3-HOWTO, both available from ++ . Note that the PLIP ++ protocol has been changed and this PLIP driver won't work together ++ with the PLIP support in Linux versions 1.0.x. This option enlarges ++ your kernel by about 8 KB. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called plip. If unsure, say Y or M, in case you buy ++ a laptop later. ++ ++config PPP ++ tristate "PPP (point-to-point protocol) support" ++ select SLHC ++ ---help--- ++ PPP (Point to Point Protocol) is a newer and better SLIP. It serves ++ the same purpose: sending Internet traffic over telephone (and other ++ serial) lines. Ask your access provider if they support it, because ++ otherwise you can't use it; most Internet access providers these ++ days support PPP rather than SLIP. ++ ++ To use PPP, you need an additional program called pppd as described ++ in the PPP-HOWTO, available at ++ . Make sure that you have ++ the version of pppd recommended in . ++ The PPP option enlarges your kernel by about 16 KB. ++ ++ There are actually two versions of PPP: the traditional PPP for ++ asynchronous lines, such as regular analog phone lines, and ++ synchronous PPP which can be used over digital ISDN lines for ++ example. If you want to use PPP over phone lines or other ++ asynchronous serial lines, you need to say Y (or M) here and also to ++ the next option, "PPP support for async serial ports". For PPP over ++ synchronous lines, you should say Y (or M) here and to "Support ++ synchronous PPP", below. ++ ++ If you said Y to "Version information on all symbols" above, then ++ you cannot compile the PPP driver into the kernel; you can then only ++ compile it as a module. To compile this driver as a module, choose M ++ here. The module will be called ppp_generic. ++ ++config PPP_MULTILINK ++ bool "PPP multilink support (EXPERIMENTAL)" ++ depends on PPP && EXPERIMENTAL ++ help ++ PPP multilink is a protocol (defined in RFC 1990) which allows you ++ to combine several (logical or physical) lines into one logical PPP ++ connection, so that you can utilize your full bandwidth. ++ ++ This has to be supported at the other end as well and you need a ++ version of the pppd daemon which understands the multilink protocol. ++ ++ If unsure, say N. ++ ++config PPP_FILTER ++ bool "PPP filtering" ++ depends on PPP ++ help ++ Say Y here if you want to be able to filter the packets passing over ++ PPP interfaces. This allows you to control which packets count as ++ activity (i.e. which packets will reset the idle timer or bring up ++ a demand-dialed link) and which packets are to be dropped entirely. ++ You need to say Y here if you wish to use the pass-filter and ++ active-filter options to pppd. ++ ++ If unsure, say N. ++ ++config PPP_ASYNC ++ tristate "PPP support for async serial ports" ++ depends on PPP ++ select CRC_CCITT ++ ---help--- ++ Say Y (or M) here if you want to be able to use PPP over standard ++ asynchronous serial ports, such as COM1 or COM2 on a PC. If you use ++ a modem (not a synchronous or ISDN modem) to contact your ISP, you ++ need this option. ++ ++ To compile this driver as a module, choose M here. ++ ++ If unsure, say Y. ++ ++config PPP_SYNC_TTY ++ tristate "PPP support for sync tty ports" ++ depends on PPP ++ help ++ Say Y (or M) here if you want to be able to use PPP over synchronous ++ (HDLC) tty devices, such as the SyncLink adapter. These devices ++ are often used for high-speed leased lines like T1/E1. ++ ++ To compile this driver as a module, choose M here. ++ ++config PPP_DEFLATE ++ tristate "PPP Deflate compression" ++ depends on PPP ++ select ZLIB_INFLATE ++ select ZLIB_DEFLATE ++ ---help--- ++ Support for the Deflate compression method for PPP, which uses the ++ Deflate algorithm (the same algorithm that gzip uses) to compress ++ each PPP packet before it is sent over the wire. The machine at the ++ other end of the PPP link (usually your ISP) has to support the ++ Deflate compression method as well for this to be useful. Even if ++ they don't support it, it is safe to say Y here. ++ ++ To compile this driver as a module, choose M here. ++ ++config PPP_BSDCOMP ++ tristate "PPP BSD-Compress compression" ++ depends on PPP ++ ---help--- ++ Support for the BSD-Compress compression method for PPP, which uses ++ the LZW compression method to compress each PPP packet before it is ++ sent over the wire. The machine at the other end of the PPP link ++ (usually your ISP) has to support the BSD-Compress compression ++ method as well for this to be useful. Even if they don't support it, ++ it is safe to say Y here. ++ ++ The PPP Deflate compression method ("PPP Deflate compression", ++ above) is preferable to BSD-Compress, because it compresses better ++ and is patent-free. ++ ++ Note that the BSD compression code will always be compiled as a ++ module; it is called bsd_comp and will show up in the directory ++ modules once you have said "make modules". If unsure, say N. ++ ++config PPP_MPPE ++ tristate "PPP MPPE compression (encryption) (EXPERIMENTAL)" ++ depends on PPP && EXPERIMENTAL ++ select CRYPTO ++ select CRYPTO_SHA1 ++ select CRYPTO_ARC4 ++ select CRYPTO_ECB ++ ---help--- ++ Support for the MPPE Encryption protocol, as employed by the ++ Microsoft Point-to-Point Tunneling Protocol. ++ ++ See http://pptpclient.sourceforge.net/ for information on ++ configuring PPTP clients and servers to utilize this method. ++ ++config PPPOE ++ tristate "PPP over Ethernet (EXPERIMENTAL)" ++ depends on EXPERIMENTAL && PPP ++ help ++ Support for PPP over Ethernet. ++ ++ This driver requires the latest version of pppd from the CVS ++ repository at cvs.samba.org. Alternatively, see the ++ RoaringPenguin package () ++ which contains instruction on how to use this driver (under ++ the heading "Kernel mode PPPoE"). ++ ++config PPPOATM ++ tristate "PPP over ATM" ++ depends on ATM && PPP ++ help ++ Support PPP (Point to Point Protocol) encapsulated in ATM frames. ++ This implementation does not yet comply with section 8 of RFC2364, ++ which can lead to bad results if the ATM peer loses state and ++ changes its encapsulation unilaterally. ++ ++config PPPOL2TP ++ tristate "PPP over L2TP (EXPERIMENTAL)" ++ depends on EXPERIMENTAL && PPP && INET ++ help ++ Support for PPP-over-L2TP socket family. L2TP is a protocol ++ used by ISPs and enterprises to tunnel PPP traffic over UDP ++ tunnels. L2TP is replacing PPTP for VPN uses. ++ ++ This kernel component handles only L2TP data packets: a ++ userland daemon handles L2TP the control protocol (tunnel ++ and session setup). One such daemon is OpenL2TP ++ (http://openl2tp.sourceforge.net/). ++ ++config SLIP ++ tristate "SLIP (serial line) support" ++ ---help--- ++ Say Y if you intend to use SLIP or CSLIP (compressed SLIP) to ++ connect to your Internet service provider or to connect to some ++ other local Unix box or if you want to configure your Linux box as a ++ Slip/CSlip server for other people to dial in. SLIP (Serial Line ++ Internet Protocol) is a protocol used to send Internet traffic over ++ serial connections such as telephone lines or null modem cables; ++ nowadays, the protocol PPP is more commonly used for this same ++ purpose. ++ ++ Normally, your access provider has to support SLIP in order for you ++ to be able to use it, but there is now a SLIP emulator called SLiRP ++ around (available from ++ ) which ++ allows you to use SLIP over a regular dial up shell connection. If ++ you plan to use SLiRP, make sure to say Y to CSLIP, below. The ++ NET-3-HOWTO, available from ++ , explains how to ++ configure SLIP. Note that you don't need this option if you just ++ want to run term (term is a program which gives you almost full ++ Internet connectivity if you have a regular dial up shell account on ++ some Internet connected Unix computer. Read ++ ). SLIP ++ support will enlarge your kernel by about 4 KB. If unsure, say N. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called slip. ++ ++config SLIP_COMPRESSED ++ bool "CSLIP compressed headers" ++ depends on SLIP ++ select SLHC ++ ---help--- ++ This protocol is faster than SLIP because it uses compression on the ++ TCP/IP headers (not on the data itself), but it has to be supported ++ on both ends. Ask your access provider if you are not sure and ++ answer Y, just in case. You will still be able to use plain SLIP. If ++ you plan to use SLiRP, the SLIP emulator (available from ++ ) which ++ allows you to use SLIP over a regular dial up shell connection, you ++ definitely want to say Y here. The NET-3-HOWTO, available from ++ , explains how to configure ++ CSLIP. This won't enlarge your kernel. ++ ++config SLHC ++ tristate ++ help ++ This option enables Van Jacobsen serial line header compression ++ routines. ++ ++config SLIP_SMART ++ bool "Keepalive and linefill" ++ depends on SLIP ++ help ++ Adds additional capabilities to the SLIP driver to support the ++ RELCOM line fill and keepalive monitoring. Ideal on poor quality ++ analogue lines. ++ ++config SLIP_MODE_SLIP6 ++ bool "Six bit SLIP encapsulation" ++ depends on SLIP ++ help ++ Just occasionally you may need to run IP over hostile serial ++ networks that don't pass all control characters or are only seven ++ bit. Saying Y here adds an extra mode you can use with SLIP: ++ "slip6". In this mode, SLIP will only send normal ASCII symbols over ++ the serial device. Naturally, this has to be supported at the other ++ end of the link as well. It's good enough, for example, to run IP ++ over the async ports of a Camtec JNT Pad. If unsure, say N. ++ ++config NET_FC ++ bool "Fibre Channel driver support" ++ depends on SCSI && PCI ++ help ++ Fibre Channel is a high speed serial protocol mainly used to connect ++ large storage devices to the computer; it is compatible with and ++ intended to replace SCSI. ++ ++ If you intend to use Fibre Channel, you need to have a Fibre channel ++ adaptor card in your computer; say Y here and to the driver for your ++ adaptor below. You also should have said Y to "SCSI support" and ++ "SCSI generic support". ++ ++config SHAPER ++ tristate "Traffic Shaper (OBSOLETE)" ++ depends on EXPERIMENTAL ++ ---help--- ++ The traffic shaper is a virtual network device that allows you to ++ limit the rate of outgoing data flow over some other network device. ++ The traffic that you want to slow down can then be routed through ++ these virtual devices. See ++ for more information. ++ ++ An alternative to this traffic shaper are traffic schedulers which ++ you'll get if you say Y to "QoS and/or fair queuing" in ++ "Networking options". ++ ++ To compile this driver as a module, choose M here: the module ++ will be called shaper. If unsure, say N. ++ ++config NETCONSOLE ++ tristate "Network console logging support (EXPERIMENTAL)" ++ depends on EXPERIMENTAL ++ ---help--- ++ If you want to log kernel messages over the network, enable this. ++ See for details. ++ ++config NETCONSOLE_DYNAMIC ++ bool "Dynamic reconfiguration of logging targets (EXPERIMENTAL)" ++ depends on NETCONSOLE && SYSFS && EXPERIMENTAL ++ select CONFIGFS_FS ++ help ++ This option enables the ability to dynamically reconfigure target ++ parameters (interface, IP addresses, port numbers, MAC addresses) ++ at runtime through a userspace interface exported using configfs. ++ See for details. ++ ++config NETPOLL ++ def_bool NETCONSOLE ++ ++config NETPOLL_TRAP ++ bool "Netpoll traffic trapping" ++ default n ++ depends on NETPOLL ++ ++config NET_POLL_CONTROLLER ++ def_bool NETPOLL ++ ++config VIRTIO_NET ++ tristate "Virtio network driver (EXPERIMENTAL)" ++ depends on EXPERIMENTAL && VIRTIO ++ ---help--- ++ This is the virtual network driver for lguest. Say Y or M. ++ ++endif # NETDEVICES +diff -urN linux-2.6.24.7.old/drivers/net/Makefile linux-2.6.24.7/drivers/net/Makefile +--- linux-2.6.24.7.old/drivers/net/Makefile 2009-04-12 18:05:07.000000000 +0200 ++++ linux-2.6.24.7/drivers/net/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -84,6 +84,8 @@ + obj-$(CONFIG_MII) += mii.o + obj-$(CONFIG_PHYLIB) += phy/ + ++obj-$(CONFIG_JZ_ETH) += jz_eth.o ++obj-$(CONFIG_JZCS8900) += jzcs8900a.o + obj-$(CONFIG_SUNDANCE) += sundance.o + obj-$(CONFIG_HAMACHI) += hamachi.o + obj-$(CONFIG_NET) += Space.o loopback.o +diff -urN linux-2.6.24.7.old/drivers/net/jz_eth.c linux-2.6.24.7/drivers/net/jz_eth.c +--- linux-2.6.24.7.old/drivers/net/jz_eth.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/net/jz_eth.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1290 @@ ++/* ++ * linux/drivers/net/jz_eth.c ++ * ++ * Jz4730/Jz5730 On-Chip ethernet driver. ++ * ++ * Copyright (C) 2005 - 2007 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "jz_eth.h" ++ ++#define P2ADDR(a) (((unsigned long)(a) & 0x1fffffff) | 0xa0000000) ++#define P1ADDR(a) (((unsigned long)(a) & 0x1fffffff) | 0x80000000) ++ ++//#define DEBUG ++#ifdef DEBUG ++# define DBPRINTK(fmt,args...) printk(KERN_DEBUG fmt,##args) ++#else ++# define DBPRINTK(fmt,args...) do {} while(0) ++#endif ++ ++#define errprintk(fmt,args...) printk(KERN_ERR fmt,##args); ++#define infoprintk(fmt,args...) printk(KERN_INFO fmt,##args); ++ ++#define DRV_NAME "jz_eth" ++#define DRV_VERSION "1.2" ++#define DRV_AUTHOR "Peter Wei " ++#define DRV_DESC "JzSOC On-chip Ethernet driver" ++ ++MODULE_AUTHOR(DRV_AUTHOR); ++MODULE_DESCRIPTION(DRV_DESC); ++MODULE_LICENSE("GPL"); ++ ++/* ++ * Local variables ++ */ ++static struct net_device *netdev; ++static char * hwaddr = NULL; ++static int debug = -1; ++static struct mii_if_info mii_info; ++ ++MODULE_PARM_DESC(debug, "i"); ++MODULE_PARM_DESC(hwaddr,"s"); ++ ++/* ++ * Local routines ++ */ ++static irqreturn_t jz_eth_interrupt(int irq, void *dev_id); ++ ++static int link_check_thread (void *data); ++ ++/* ++ * Get MAC address ++ */ ++ ++#define I2C_DEVICE 0x57 ++#define MAC_OFFSET 64 ++ ++extern void i2c_open(void); ++extern void i2c_close(void); ++extern int i2c_read(unsigned char device, unsigned char *buf, ++ unsigned char address, int count); ++ ++static inline unsigned char str2hexnum(unsigned char c) ++{ ++ if (c >= '0' && c <= '9') ++ return c - '0'; ++ if (c >= 'a' && c <= 'f') ++ return c - 'a' + 10; ++ if (c >= 'A' && c <= 'F') ++ return c - 'A' + 10; ++ return 0; /* foo */ ++} ++ ++static inline void str2eaddr(unsigned char *ea, unsigned char *str) ++{ ++ int i; ++ ++ for (i = 0; i < 6; i++) { ++ unsigned char num; ++ ++ if((*str == '.') || (*str == ':')) ++ str++; ++ num = str2hexnum(*str++) << 4; ++ num |= (str2hexnum(*str++)); ++ ea[i] = num; ++ } ++} ++ ++static int ethaddr_cmd = 0; ++static unsigned char ethaddr_hex[6]; ++ ++static int __init ethernet_addr_setup(char *str) ++{ ++ if (!str) { ++ printk("ethaddr not set in command line\n"); ++ return -1; ++ } ++ ethaddr_cmd = 1; ++ str2eaddr(ethaddr_hex, str); ++ ++ return 0; ++} ++ ++__setup("ethaddr=", ethernet_addr_setup); ++ ++static int get_mac_address(struct net_device *dev) ++{ ++ int i; ++ unsigned char flag0=0; ++ unsigned char flag1=0xff; ++ ++ dev->dev_addr[0] = 0xff; ++ if (hwaddr != NULL) { ++ /* insmod jz-ethc.o hwaddr=00:ef:a3:c1:00:10 */ ++ str2eaddr(dev->dev_addr, hwaddr); ++ } else if (ethaddr_cmd) { ++ /* linux command line: ethaddr=00:ef:a3:c1:00:10 */ ++ for (i=0; i<6; i++) ++ dev->dev_addr[i] = ethaddr_hex[i]; ++ } else { ++#if 0 ++ /* mac address in eeprom: byte 0x40-0x45 */ ++ i2c_open(); ++ i2c_read(I2C_DEVICE, dev->dev_addr, MAC_OFFSET, 6); ++ i2c_close(); ++#endif ++ } ++ ++ /* check whether valid MAC address */ ++ for (i=0; i<6; i++) { ++ flag0 |= dev->dev_addr[i]; ++ flag1 &= dev->dev_addr[i]; ++ } ++ if ((dev->dev_addr[0] & 0xC0) || (flag0 == 0) || (flag1 == 0xff)) { ++ printk("WARNING: There is not MAC address, use default ..\n"); ++ dev->dev_addr[0] = 0x00; ++ dev->dev_addr[1] = 0xef; ++ dev->dev_addr[2] = 0xa3; ++ dev->dev_addr[3] = 0xc1; ++ dev->dev_addr[4] = 0x00; ++ dev->dev_addr[5] = 0x10; ++ dev->dev_addr[5] = 0x03; ++ } ++ return 0; ++} ++ ++/*---------------------------------------------------------------------*/ ++ ++static u32 jz_eth_curr_mode(struct net_device *dev); ++ ++/* ++ * Ethernet START/STOP routines ++ */ ++#define START_ETH { \ ++ s32 val; \ ++ val = readl(DMA_OMR); \ ++ val |= OMR_ST | OMR_SR; \ ++ writel(val, DMA_OMR); \ ++} ++ ++#define STOP_ETH { \ ++ s32 val; \ ++ val = readl(DMA_OMR); \ ++ val &= ~(OMR_ST|OMR_SR); \ ++ writel(val, DMA_OMR); \ ++} ++ ++/* ++ * Link check routines ++ */ ++static void start_check(struct net_device *dev) ++{ ++ struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; ++ ++ np->thread_die = 0; ++ init_waitqueue_head(&np->thr_wait); ++ init_completion (&np->thr_exited); ++ np->thr_pid = kernel_thread (link_check_thread,(void *)dev, ++ CLONE_FS | CLONE_FILES); ++ if (np->thr_pid < 0) ++ errprintk("%s: unable to start kernel thread\n",dev->name); ++} ++ ++static int close_check(struct net_device *dev) ++{ ++ struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; ++ int ret = 0; ++ ++ if (np->thr_pid >= 0) { ++ np->thread_die = 1; ++ wmb(); ++ ret = kill_proc (np->thr_pid, SIGTERM, 1); ++ if (ret) { ++ errprintk("%s: unable to signal thread\n", dev->name); ++ return 1; ++ } ++ wait_for_completion (&np->thr_exited); ++ } ++ return 0; ++} ++ ++static int link_check_thread(void *data) ++{ ++ struct net_device *dev=(struct net_device *)data; ++ struct jz_eth_private *np = (struct jz_eth_private *)netdev->priv; ++ unsigned char current_link; ++ unsigned long timeout; ++ ++ daemonize("%s", dev->name); ++ spin_lock_irq(¤t->sighand->siglock); ++ sigemptyset(¤t->blocked); ++ recalc_sigpending(); ++ spin_unlock_irq(¤t->sighand->siglock); ++ ++ strncpy (current->comm, dev->name, sizeof(current->comm) - 1); ++ current->comm[sizeof(current->comm) - 1] = '\0'; ++ ++ while (1) { ++ timeout = 3*HZ; ++ do { ++ timeout = interruptible_sleep_on_timeout (&np->thr_wait, timeout); ++ /* make swsusp happy with our thread */ ++// if (current->flags & PF_FREEZE) ++// refrigerator(PF_FREEZE); ++ } while (!signal_pending (current) && (timeout > 0)); ++ ++ if (signal_pending (current)) { ++ spin_lock_irq(¤t->sighand->siglock); ++ flush_signals(current); ++ spin_unlock_irq(¤t->sighand->siglock); ++ } ++ ++ if (np->thread_die) ++ break; ++ ++ current_link=mii_link_ok(&mii_info); ++ if (np->link_state!=current_link) { ++ if (current_link) { ++ infoprintk("%s: Ethernet Link OK!\n",dev->name); ++ jz_eth_curr_mode(dev); ++ netif_carrier_on(dev); ++ } ++ else { ++ errprintk("%s: Ethernet Link offline!\n",dev->name); ++ netif_carrier_off(dev); ++ } ++ } ++ np->link_state=current_link; ++ ++ } ++ complete_and_exit (&np->thr_exited, 0); ++} ++ ++#ifdef DEBUG ++/* ++ * Display ethernet packet header ++ * This routine is used for test function ++ */ ++static void eth_dbg_rx(struct sk_buff *skb, int len) ++{ ++ ++ int i, j; ++ ++ printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n", ++ (u8)skb->data[0], ++ (u8)skb->data[1], ++ (u8)skb->data[2], ++ (u8)skb->data[3], ++ (u8)skb->data[4], ++ (u8)skb->data[5], ++ (u8)skb->data[6], ++ (u8)skb->data[7], ++ (u8)skb->data[8], ++ (u8)skb->data[9], ++ (u8)skb->data[10], ++ (u8)skb->data[11], ++ (u8)skb->data[12], ++ (u8)skb->data[13], ++ len); ++ for (j=0; len>0; j+=16, len-=16) { ++ printk(" %03x: ",j); ++ for (i=0; i<16 && idata[i+j]); ++ } ++ printk("\n"); ++ } ++ return; ++ } ++#endif ++ ++/* ++ * Reset ethernet device ++ */ ++static inline void jz_eth_reset(void) ++{ ++ u32 i; ++ i = readl(DMA_BMR); ++ writel(i | BMR_SWR, DMA_BMR); ++ for(i = 0; i < 1000; i++) { ++ if(!(readl(DMA_BMR) & BMR_SWR)) break; ++ mdelay(1); ++ } ++} ++ ++/* ++ * MII operation routines ++ */ ++static inline void mii_wait(void) ++{ ++ int i; ++ for(i = 0; i < 10000; i++) { ++ if(!(readl(MAC_MIIA) & 0x1)) ++ break; ++ mdelay(1); ++ } ++ if (i >= 10000) ++ printk("MII wait timeout : %d.\n", i); ++} ++ ++static int mdio_read(struct net_device *dev,int phy_id, int location) ++{ ++ u32 mii_cmd = (phy_id << 11) | (location << 6) | 1; ++ int retval = 0; ++ ++ writel(mii_cmd, MAC_MIIA); ++ mii_wait(); ++ retval = readl(MAC_MIID) & 0x0000ffff; ++ ++ return retval; ++ ++} ++ ++static void mdio_write(struct net_device *dev,int phy_id, int location, int data) ++{ ++ u32 mii_cmd = (phy_id << 11) | (location << 6) | 0x2 | 1; ++ ++ writel(mii_cmd, MAC_MIIA); ++ writel(data & 0x0000ffff, MAC_MIID); ++ mii_wait(); ++} ++ ++ ++/* ++ * Search MII phy ++ */ ++static int jz_search_mii_phy(struct net_device *dev) ++{ ++ ++ struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; ++ int phy, phy_idx = 0; ++ ++ np->valid_phy = 0xff; ++ for (phy = 0; phy < 32; phy++) { ++ int mii_status = mdio_read(dev,phy, 1); ++ if (mii_status != 0xffff && mii_status != 0x0000) { ++ np->phys[phy_idx] = phy; ++ np->ecmds[phy_idx].speed=SPEED_100; ++ np->ecmds[phy_idx].duplex=DUPLEX_FULL; ++ np->ecmds[phy_idx].port=PORT_MII; ++ np->ecmds[phy_idx].transceiver=XCVR_INTERNAL; ++ np->ecmds[phy_idx].phy_address=np->phys[phy_idx]; ++ np->ecmds[phy_idx].autoneg=AUTONEG_ENABLE; ++ np->ecmds[phy_idx].advertising=(ADVERTISED_10baseT_Half | ++ ADVERTISED_10baseT_Full | ++ ADVERTISED_100baseT_Half | ++ ADVERTISED_100baseT_Full); ++ phy_idx++; ++ } ++ } ++ if (phy_idx == 1) { ++ np->valid_phy = np->phys[0]; ++ np->phy_type = 0; ++ } ++ if (phy_idx != 0) { ++ phy = np->valid_phy; ++ np->advertising = mdio_read(dev,phy, 4); ++ } ++ return phy_idx; ++} ++ ++/* ++ * CRC calc for Destination Address for gets hashtable index ++ */ ++ ++#define POLYNOMIAL 0x04c11db7UL ++static u16 jz_hashtable_index(u8 *addr) ++{ ++#if 1 ++ u32 crc = 0xffffffff, msb; ++ int i, j; ++ u32 byte; ++ for (i = 0; i < 6; i++) { ++ byte = *addr++; ++ for (j = 0; j < 8; j++) { ++ msb = crc >> 31; ++ crc <<= 1; ++ if (msb ^ (byte & 1)) crc ^= POLYNOMIAL; ++ byte >>= 1; ++ } ++ } ++ return ((int)(crc >> 26)); ++#endif ++#if 0 ++ int crc = -1; ++ int length=6; ++ int bit; ++ unsigned char current_octet; ++ while (--length >= 0) { ++ current_octet = *addr++; ++ for (bit = 0; bit < 8; bit++, current_octet >>= 1) ++ crc = (crc << 1) ^ ((crc < 0) ^ (current_octet & 1) ? ++ POLYNOMIAL : 0); ++ } ++ return ((int)(crc >> 26)); ++#endif ++} ++ ++/* ++ * Multicast filter and config multicast hash table ++ */ ++#define MULTICAST_FILTER_LIMIT 64 ++ ++static void jz_set_multicast_list(struct net_device *dev) ++{ ++ int i, hash_index; ++ u32 mcr, hash_h, hash_l, hash_bit; ++ ++ mcr = readl(MAC_MCR); ++ mcr &= ~(MCR_PR | MCR_PM | MCR_HP); ++ ++ if (dev->flags & IFF_PROMISC) { ++ /* Accept any kinds of packets */ ++ mcr |= MCR_PR; ++ hash_h = 0xffffffff; ++ hash_l = 0xffffffff; ++ DBPRINTK("%s: enter promisc mode!\n",dev->name); ++ } ++ else if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > MULTICAST_FILTER_LIMIT)){ ++ /* Accept all multicast packets */ ++ mcr |= MCR_PM; ++ hash_h = 0xffffffff; ++ hash_l = 0xffffffff; ++ DBPRINTK("%s: enter allmulticast mode! %d \n",dev->name,dev->mc_count); ++ } ++ else if (dev->flags & IFF_MULTICAST) ++ { ++ /* Update multicast hash table */ ++ struct dev_mc_list *mclist; ++ hash_h = readl(MAC_HTH); ++ hash_l = readl(MAC_HTL); ++ for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; ++ i++, mclist = mclist->next) ++ { ++ hash_index = jz_hashtable_index(mclist->dmi_addr); ++ hash_bit=0x00000001; ++ hash_bit <<= (hash_index & 0x1f); ++ if (hash_index > 0x1f) ++ hash_h |= hash_bit; ++ else ++ hash_l |= hash_bit; ++ DBPRINTK("----------------------------\n"); ++#ifdef DEBUG ++ int j; ++ for (j=0;jdmi_addrlen;j++) ++ printk("%2.2x:",mclist->dmi_addr[j]); ++ printk("\n"); ++#endif ++ DBPRINTK("dmi.addrlen => %d\n",mclist->dmi_addrlen); ++ DBPRINTK("dmi.users => %d\n",mclist->dmi_users); ++ DBPRINTK("dmi.gusers => %d\n",mclist->dmi_users); ++ } ++ writel(hash_h,MAC_HTH); ++ writel(hash_l,MAC_HTL); ++ mcr |= MCR_HP; ++ DBPRINTK("This is multicast hash table high bits [%4.4x]\n",readl(MAC_HTH)); ++ DBPRINTK("This is multicast hash table low bits [%4.4x]\n",readl(MAC_HTL)); ++ DBPRINTK("%s: enter multicast mode!\n",dev->name); ++ } ++ writel(mcr,MAC_MCR); ++} ++ ++static inline int jz_phy_reset(struct net_device *dev) ++{ ++ struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; ++ unsigned int mii_reg0; ++ unsigned int count; ++ ++ mii_reg0 = mdio_read(dev,np->valid_phy,MII_BMCR); ++ mii_reg0 |=MII_CR_RST; ++ mdio_write(dev,np->valid_phy,MII_BMCR,mii_reg0); //reset phy ++ for ( count = 0; count < 1000; count++) { ++ mdelay(1); ++ mii_reg0 = mdio_read(dev,np->valid_phy,MII_BMCR); ++ if (!(mii_reg0 & MII_CR_RST)) break; //reset completed ++ } ++ if (count>=100) ++ return 1; //phy error ++ else ++ return 0; ++} ++ ++/* ++ * Show all mii registers - this routine is used for test ++ */ ++#ifdef DEBUG ++static void mii_db_out(struct net_device *dev) ++{ ++ struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; ++ unsigned int mii_test; ++ ++ mii_test = mdio_read(dev,np->valid_phy,MII_BMCR); ++ DBPRINTK("BMCR ====> 0x%4.4x \n",mii_test); ++ ++ mii_test = mdio_read(dev,np->valid_phy,MII_BMSR); ++ DBPRINTK("BMSR ====> 0x%4.4x \n",mii_test); ++ ++ mii_test = mdio_read(dev,np->valid_phy,MII_ANAR); ++ DBPRINTK("ANAR ====> 0x%4.4x \n",mii_test); ++ ++ mii_test = mdio_read(dev,np->valid_phy,MII_ANLPAR); ++ DBPRINTK("ANLPAR ====> 0x%4.4x \n",mii_test); ++ ++ mii_test = mdio_read(dev,np->valid_phy,16); ++ DBPRINTK("REG16 ====> 0x%4.4x \n",mii_test); ++ ++ mii_test = mdio_read(dev,np->valid_phy,17); ++ DBPRINTK("REG17 ====> 0x%4.4x \n",mii_test); ++} ++#endif ++ ++/* ++ * Start Auto-Negotiation function for PHY ++ */ ++static int jz_autonet_complete(struct net_device *dev) ++{ ++ struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; ++ int count; ++ u32 mii_reg1, timeout = 3000; ++ ++ for (count = 0; count < timeout; count++) { ++ mdelay(1); ++ mii_reg1 = mdio_read(dev,np->valid_phy,MII_BMSR); ++ if (mii_reg1 & 0x0020) break; ++ } ++ //mii_db_out(dev); //for debug to display all register of MII ++ if (count >= timeout) ++ return 1; //auto negotiation error ++ else ++ return 0; ++} ++ ++/* ++ * Get current mode of eth phy ++ */ ++static u32 jz_eth_curr_mode(struct net_device *dev) ++{ ++ struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; ++ unsigned int mii_reg17; ++ u32 flag = 0; ++ ++ mii_reg17 = mdio_read(dev,np->valid_phy,MII_DSCSR); ++ np->media = mii_reg17>>12; ++ if (np->media==8) { ++ infoprintk("%s: Current Operation Mode is [100M Full Duplex]",dev->name); ++ flag = 0; ++ np->full_duplex=1; ++ } ++ if (np->media==4) { ++ infoprintk("%s: Current Operation Mode is [100M Half Duplex]",dev->name); ++ flag = 0; ++ np->full_duplex=0; ++ } ++ if (np->media==2) { ++ infoprintk("%s: Current Operation Mode is [10M Full Duplex]",dev->name); ++ flag = OMR_TTM; ++ np->full_duplex=1; ++ } ++ if (np->media==1) { ++ infoprintk("%s: Current Operation Mode is [10M Half Duplex]",dev->name); ++ flag = OMR_TTM; ++ np->full_duplex=0; ++ } ++ printk("\n"); ++ return flag; ++} ++ ++/* ++ * Ethernet device hardware init ++ * This routine initializes the ethernet device hardware and PHY ++ */ ++static int jz_init_hw(struct net_device *dev) ++{ ++ struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; ++ struct ethtool_cmd ecmd; ++ u32 mcr, omr; ++ u32 sts, flag = 0; ++ int i; ++ ++ jz_eth_reset(); ++ STOP_ETH; ++#if 0 ++ /* mii operation */ ++ if (jz_phy_reset(dev)) { ++ errprintk("PHY device do not reset!\n"); ++ return -EPERM; // return operation not permitted ++ } ++#endif ++ /* Set MAC address */ ++ writel(le32_to_cpu(*(unsigned long *)&dev->dev_addr[0]), MAC_MAL); ++ writel(le32_to_cpu(*(unsigned long *)&dev->dev_addr[4]), MAC_MAH); ++ printk("%s: JZ On-Chip ethernet (MAC ", dev->name); ++ for (i = 0; i < 5; i++) { ++ printk("%2.2x:", dev->dev_addr[i]); ++ } ++ printk("%2.2x, IRQ %d)\n", dev->dev_addr[i], dev->irq); ++ ++ np->mii_phy_cnt = jz_search_mii_phy(dev); ++ printk("%s: Found %d PHY on JZ MAC\n", dev->name, np->mii_phy_cnt); ++ ++ mii_info.phy_id = np->valid_phy; ++ mii_info.dev = dev; ++ mii_info.mdio_read = &mdio_read; ++ mii_info.mdio_write = &mdio_write; ++ ++ ecmd.speed = SPEED_100; ++ ecmd.duplex = DUPLEX_FULL; ++ ecmd.port = PORT_MII; ++ ecmd.transceiver = XCVR_INTERNAL; ++ ecmd.phy_address = np->valid_phy; ++ ecmd.autoneg = AUTONEG_ENABLE; ++ ++ mii_ethtool_sset(&mii_info,&ecmd); ++ if (jz_autonet_complete(dev)) ++ errprintk("%s: Ethernet Module AutoNegotiation failed\n",dev->name); ++ mii_ethtool_gset(&mii_info,&ecmd); ++ ++ infoprintk("%s: Provide Modes: ",dev->name); ++ for (i = 0; i < 5;i++) ++ if (ecmd.advertising & (1<full_duplex) ++ mcr |= MCR_FDX; ++ mcr |= MCR_BFD | MCR_TE | MCR_RE | MCR_OWD|MCR_HBD; ++ writel(mcr, MAC_MCR); ++// mcr &= (readl(MAC_MCR) & ~(MCR_PM | MCR_PR | MCR_IF | MCR_HO | MCR_HP)); ++// mcr &= 0xffdf; ++// mcr |= 0x0020; ++// writel(mcr, MAC_MCR); ++ ++ /* Set base address of TX and RX descriptors */ ++ writel(np->dma_rx_ring, DMA_RRBA); ++ writel(np->dma_tx_ring, DMA_TRBA); ++ ++ START_ETH; ++ ++ /* set interrupt mask */ ++ writel(IMR_DEFAULT | IMR_ENABLE, DMA_IMR); ++ ++ /* Reset any pending (stale) interrupts */ ++ sts = readl(DMA_STS); ++ writel(sts, DMA_STS); ++ ++ return 0; ++} ++ ++static int jz_eth_open(struct net_device *dev) ++{ ++ struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; ++ int retval, i; ++ ++ retval = request_irq(dev->irq, jz_eth_interrupt, 0, dev->name, dev); ++ if (retval) { ++ errprintk("%s: unable to get IRQ %d .\n", dev->name, dev->irq); ++ return -EAGAIN; ++ } ++ ++ for (i = 0; i < NUM_RX_DESCS; i++) { ++ np->rx_ring[i].status = cpu_to_le32(R_OWN); ++ np->rx_ring[i].desc1 = cpu_to_le32(RX_BUF_SIZE | RD_RCH); ++ np->rx_ring[i].buf1_addr = cpu_to_le32(np->dma_rx_buf + i*RX_BUF_SIZE); ++ np->rx_ring[i].next_addr = cpu_to_le32(np->dma_rx_ring + (i+1) * sizeof (jz_desc_t)); ++ } ++ np->rx_ring[NUM_RX_DESCS - 1].next_addr = cpu_to_le32(np->dma_rx_ring); ++ ++ for (i = 0; i < NUM_TX_DESCS; i++) { ++ np->tx_ring[i].status = cpu_to_le32(0); ++ np->tx_ring[i].desc1 = cpu_to_le32(TD_TCH); ++ np->tx_ring[i].buf1_addr = 0; ++ np->tx_ring[i].next_addr = cpu_to_le32(np->dma_tx_ring + (i+1) * sizeof (jz_desc_t)); ++ } ++ np->tx_ring[NUM_TX_DESCS - 1].next_addr = cpu_to_le32(np->dma_tx_ring); ++ ++ np->rx_head = 0; ++ np->tx_head = np->tx_tail = 0; ++ ++ jz_init_hw(dev); ++ ++ dev->trans_start = jiffies; ++ netif_start_queue(dev); ++ start_check(dev); ++ ++ return 0; ++} ++ ++static int jz_eth_close(struct net_device *dev) ++{ ++ netif_stop_queue(dev); ++ close_check(dev); ++ STOP_ETH; ++ free_irq(dev->irq, dev); ++ return 0; ++} ++ ++/* ++ * Get the current statistics. ++ * This may be called with the device open or closed. ++ */ ++static struct net_device_stats * jz_eth_get_stats(struct net_device *dev) ++{ ++ struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; ++ int tmp; ++ ++ tmp = readl(DMA_MFC); // After read clear to zero ++ np->stats.rx_missed_errors += (tmp & MFC_CNT2) + ((tmp & MFC_CNT1) >> 16); ++ ++ return &np->stats; ++} ++ ++/* ++ * ethtool routines ++ */ ++static int jz_ethtool_ioctl(struct net_device *dev, void *useraddr) ++{ ++ struct jz_eth_private *np = dev->priv; ++ u32 ethcmd; ++ ++ /* dev_ioctl() in ../../net/core/dev.c has already checked ++ capable(CAP_NET_ADMIN), so don't bother with that here. */ ++ ++ if (get_user(ethcmd, (u32 *)useraddr)) ++ return -EFAULT; ++ ++ switch (ethcmd) { ++ ++ case ETHTOOL_GDRVINFO: { ++ struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; ++ strcpy (info.driver, DRV_NAME); ++ strcpy (info.version, DRV_VERSION); ++ strcpy (info.bus_info, "OCS"); ++ if (copy_to_user (useraddr, &info, sizeof (info))) ++ return -EFAULT; ++ return 0; ++ } ++ ++ /* get settings */ ++ case ETHTOOL_GSET: { ++ struct ethtool_cmd ecmd = { ETHTOOL_GSET }; ++ spin_lock_irq(&np->lock); ++ mii_ethtool_gset(&mii_info, &ecmd); ++ spin_unlock_irq(&np->lock); ++ if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) ++ return -EFAULT; ++ return 0; ++ } ++ /* set settings */ ++ case ETHTOOL_SSET: { ++ int r; ++ struct ethtool_cmd ecmd; ++ if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) ++ return -EFAULT; ++ spin_lock_irq(&np->lock); ++ r = mii_ethtool_sset(&mii_info, &ecmd); ++ spin_unlock_irq(&np->lock); ++ return r; ++ } ++ /* restart autonegotiation */ ++ case ETHTOOL_NWAY_RST: { ++ return mii_nway_restart(&mii_info); ++ } ++ /* get link status */ ++ case ETHTOOL_GLINK: { ++ struct ethtool_value edata = {ETHTOOL_GLINK}; ++ edata.data = mii_link_ok(&mii_info); ++ if (copy_to_user(useraddr, &edata, sizeof(edata))) ++ return -EFAULT; ++ return 0; ++ } ++ ++ /* get message-level */ ++ case ETHTOOL_GMSGLVL: { ++ struct ethtool_value edata = {ETHTOOL_GMSGLVL}; ++ edata.data = debug; ++ if (copy_to_user(useraddr, &edata, sizeof(edata))) ++ return -EFAULT; ++ return 0; ++ } ++ /* set message-level */ ++ case ETHTOOL_SMSGLVL: { ++ struct ethtool_value edata; ++ if (copy_from_user(&edata, useraddr, sizeof(edata))) ++ return -EFAULT; ++ debug = edata.data; ++ return 0; ++ } ++ ++ ++ default: ++ break; ++ } ++ ++ return -EOPNOTSUPP; ++ ++} ++ ++/* ++ * Config device ++ */ ++static int jz_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ++{ ++ struct jz_eth_private *np =(struct jz_eth_private *)dev->priv; ++ struct mii_ioctl_data *data, rdata; ++ ++ switch (cmd) { ++ case SIOCETHTOOL: ++ return jz_ethtool_ioctl(dev, (void *) rq->ifr_data); ++ case SIOCGMIIPHY: ++ case SIOCDEVPRIVATE: ++ data = (struct mii_ioctl_data *)&rq->ifr_data; ++ data->phy_id = np->valid_phy; ++ case SIOCGMIIREG: ++ case SIOCDEVPRIVATE+1: ++ data = (struct mii_ioctl_data *)&rq->ifr_data; ++ data->val_out = mdio_read(dev,np->valid_phy, data->reg_num & 0x1f); ++ return 0; ++ case SIOCSMIIREG: ++ case SIOCDEVPRIVATE+2: ++ data = (struct mii_ioctl_data *)&rq->ifr_data; ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ mdio_write(dev,np->valid_phy, data->reg_num & 0x1f, data->val_in); ++ return 0; ++ case READ_COMMAND: ++ data = (struct mii_ioctl_data *)rq->ifr_data; ++ if (copy_from_user(&rdata,data,sizeof(rdata))) ++ return -EFAULT; ++ rdata.val_out = mdio_read(dev,rdata.phy_id, rdata.reg_num & 0x1f); ++ if (copy_to_user(data,&rdata,sizeof(rdata))) ++ return -EFAULT; ++ return 0; ++ case WRITE_COMMAND: ++ if (np->phy_type==1) { ++ data = (struct mii_ioctl_data *)rq->ifr_data; ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ if (copy_from_user(&rdata,data,sizeof(rdata))) ++ return -EFAULT; ++ mdio_write(dev,rdata.phy_id, rdata.reg_num & 0x1f, rdata.val_in); ++ } ++ return 0; ++ case GETDRIVERINFO: ++ if (np->phy_type==1) { ++ data = (struct mii_ioctl_data *)rq->ifr_data; ++ if (copy_from_user(&rdata,data,sizeof(rdata))) ++ return -EFAULT; ++ rdata.val_in = 0x1; ++ rdata.val_out = 0x00d0; ++ if (copy_to_user(data,&rdata,sizeof(rdata))) ++ return -EFAULT; ++ } ++ return 0; ++ default: ++ return -EOPNOTSUPP; ++ } ++ return 0; ++} ++ ++/* ++ * Received one packet ++ */ ++static void eth_rxready(struct net_device *dev) ++{ ++ struct jz_eth_private *np = (struct jz_eth_private*)dev->priv; ++ struct sk_buff *skb; ++ unsigned char *pkt_ptr; ++ u32 pkt_len; ++ u32 status; ++ ++ status = le32_to_cpu(np->rx_ring[np->rx_head].status); ++ while (!(status & R_OWN)) { /* owner bit = 0 */ ++ if (status & RD_ES) { /* error summary */ ++ np->stats.rx_errors++; /* Update the error stats. */ ++ if (status & (RD_RF | RD_TL)) ++ np->stats.rx_frame_errors++; ++ if (status & RD_CE) ++ np->stats.rx_crc_errors++; ++ if (status & RD_TL) ++ np->stats.rx_length_errors++; ++ } else { ++ pkt_ptr = bus_to_virt(le32_to_cpu(np->rx_ring[np->rx_head].buf1_addr)); ++ pkt_len = ((status & RD_FL) >> 16) - 4; ++ ++ skb = dev_alloc_skb(pkt_len + 2); ++ if (skb == NULL) { ++ printk("%s: Memory squeeze, dropping.\n", ++ dev->name); ++ np->stats.rx_dropped++; ++ break; ++ } ++ skb->dev = dev; ++ skb_reserve(skb, 2); /* 16 byte align */ ++ ++ //pkt_ptr = P1ADDR(pkt_ptr); ++ //dma_cache_inv(pkt_ptr, pkt_len); ++ memcpy(skb->data, pkt_ptr, pkt_len); ++ skb_put(skb, pkt_len); ++ ++ //eth_dbg_rx(skb, pkt_len); ++ skb->protocol = eth_type_trans(skb,dev); ++ netif_rx(skb); /* pass the packet to upper layers */ ++ dev->last_rx = jiffies; ++ np->stats.rx_packets++; ++ np->stats.rx_bytes += pkt_len; ++ } ++ np->rx_ring[np->rx_head].status = cpu_to_le32(R_OWN); ++ ++ np->rx_head ++; ++ if (np->rx_head >= NUM_RX_DESCS) ++ np->rx_head = 0; ++ status = le32_to_cpu(np->rx_ring[np->rx_head].status); ++ } ++} ++ ++/* ++ * Tx timeout routine ++ */ ++static void jz_eth_tx_timeout(struct net_device *dev) ++{ ++ struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; ++ ++ jz_init_hw(dev); ++ np->stats.tx_errors ++; ++ netif_wake_queue(dev); ++} ++ ++/* ++ * One packet was transmitted ++ */ ++static void eth_txdone(struct net_device *dev) ++{ ++ struct jz_eth_private *np = (struct jz_eth_private*)dev->priv; ++ int tx_tail = np->tx_tail; ++ ++ while (tx_tail != np->tx_head) { ++ int entry = tx_tail % NUM_TX_DESCS; ++ s32 status = le32_to_cpu(np->tx_ring[entry].status); ++ if(status < 0) break; ++ if (status & TD_ES ) { /* Error summary */ ++ np->stats.tx_errors++; ++ if (status & TD_NC) np->stats.tx_carrier_errors++; ++ if (status & TD_LC) np->stats.tx_window_errors++; ++ if (status & TD_UF) np->stats.tx_fifo_errors++; ++ if (status & TD_DE) np->stats.tx_aborted_errors++; ++ if (np->tx_head != np->tx_tail) ++ writel(1, DMA_TPD); /* Restart a stalled TX */ ++ } else ++ np->stats.tx_packets++; ++ /* Update the collision counter */ ++ np->stats.collisions += ((status & TD_EC) ? 16 : ((status & TD_CC) >> 3)); ++ /* Free the original skb */ ++ if (np->tx_skb[entry]) { ++ dev_kfree_skb_irq(np->tx_skb[entry]); ++ np->tx_skb[entry] = 0; ++ } ++ tx_tail++; ++ } ++ if (np->tx_full && (tx_tail + NUM_TX_DESCS > np->tx_head + 1)) { ++ /* The ring is no longer full */ ++ np->tx_full = 0; ++ netif_start_queue(dev); ++ } ++ np->tx_tail = tx_tail; ++} ++ ++/* ++ * Update the tx descriptor ++ */ ++static void load_tx_packet(struct net_device *dev, char *buf, u32 flags, struct sk_buff *skb) ++{ ++ struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; ++ int entry = np->tx_head % NUM_TX_DESCS; ++ ++ np->tx_ring[entry].buf1_addr = cpu_to_le32(virt_to_bus(buf)); ++ np->tx_ring[entry].desc1 &= cpu_to_le32((TD_TER | TD_TCH)); ++ np->tx_ring[entry].desc1 |= cpu_to_le32(flags); ++ np->tx_ring[entry].status = cpu_to_le32(T_OWN); ++ np->tx_skb[entry] = skb; ++} ++ ++/* ++ * Transmit one packet ++ */ ++static int jz_eth_send_packet(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; ++ u32 length; ++ ++ if (np->tx_full) { ++ return 0; ++ } ++#ifdef CONFIG_FPGA ++ mdelay(10); ++#else ++ udelay(500); /* FIXME: can we remove this delay ? */ ++#endif ++ length = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; ++ dma_cache_wback((unsigned long)skb->data, length); ++ load_tx_packet(dev, (char *)skb->data, TD_IC | TD_LS | TD_FS | length, skb); ++ spin_lock_irq(&np->lock); ++ np->tx_head ++; ++ np->stats.tx_bytes += length; ++ writel(1, DMA_TPD); /* Start the TX */ ++ dev->trans_start = jiffies; /* for timeout */ ++ if (np->tx_tail + NUM_TX_DESCS > np->tx_head + 1) { ++ np->tx_full = 0; ++ } ++ else { ++ np->tx_full = 1; ++ netif_stop_queue(dev); ++ } ++ spin_unlock_irq(&np->lock); ++ ++ return 0; ++} ++ ++/* ++ * Interrupt service routine ++ */ ++static irqreturn_t jz_eth_interrupt(int irq, void *dev_id) ++{ ++ struct net_device *dev = (struct net_device *)dev_id; ++ struct jz_eth_private *np = dev->priv; ++ u32 sts; ++ int i; ++ ++ spin_lock(&np->lock); ++ ++ writel((readl(DMA_IMR) & ~IMR_ENABLE), DMA_IMR); /* Disable interrupt */ ++ ++ for (i = 0; i < 100; i++) { ++ sts = readl(DMA_STS); ++ writel(sts, DMA_STS); /* clear status */ ++ ++ if (!(sts & IMR_DEFAULT)) break; ++ ++ if (sts & (DMA_INT_RI | DMA_INT_RU)) /* Rx IRQ */ ++ eth_rxready(dev); ++ if (sts & (DMA_INT_TI | DMA_INT_TU)) /* Tx IRQ */ ++ eth_txdone(dev); ++ ++ /* check error conditions */ ++ if (sts & DMA_INT_FB){ /* fatal bus error */ ++ STOP_ETH; ++ errprintk("%s: Fatal bus error occurred, sts=%#8x, device stopped.\n",dev->name, sts); ++ break; ++ } ++ ++ if (sts & DMA_INT_UN) { /* Transmit underrun */ ++ u32 omr; ++ omr = readl(DMA_OMR); ++ if (!(omr & OMR_SF)) { ++ omr &= ~(OMR_ST | OMR_SR); ++ writel(omr, DMA_OMR); ++ while (readl(DMA_STS) & STS_TS); /* wait for stop */ ++ if ((omr & OMR_TR) < OMR_TR) { /* ? */ ++ omr += TR_24; ++ } else { ++ omr |= OMR_SF; ++ } ++ writel(omr | OMR_ST | OMR_SR, DMA_OMR); ++ } ++ } ++ } ++ ++ writel(readl(DMA_IMR) | IMR_ENABLE, DMA_IMR); /* enable interrupt */ ++ ++ spin_unlock(&np->lock); ++ ++ return IRQ_HANDLED; ++} ++ ++#if 0 //def CONFIG_PM ++/* ++ * Suspend the ETH interface. ++ */ ++static int jz_eth_suspend(struct net_device *dev, int state) ++{ ++ struct jz_eth_private *jep = (struct jz_eth_private *)dev->priv; ++ unsigned long flags, tmp; ++ ++ printk("ETH suspend.\n"); ++ ++ if (!netif_running(dev)) { ++ return 0; ++ } ++ ++ netif_device_detach(dev); ++ ++ spin_lock_irqsave(&jep->lock, flags); ++ ++ /* Disable interrupts, stop Tx and Rx. */ ++ REG32(DMA_IMR) = 0; ++ STOP_ETH; ++ ++ /* Update the error counts. */ ++ tmp = REG32(DMA_MFC); ++ jep->stats.rx_missed_errors += (tmp & 0x1ffff); ++ jep->stats.rx_fifo_errors += ((tmp >> 17) & 0x7ff); ++ ++ spin_unlock_irqrestore(&jep->lock, flags); ++ ++ return 0; ++} ++ ++/* ++ * Resume the ETH interface. ++ */ ++static int jz_eth_resume(struct net_device *dev) ++{ ++ printk("ETH resume.\n"); ++ ++ if (!netif_running(dev)) ++ return 0; ++ ++ jz_init_hw(dev); ++ ++ netif_device_attach(dev); ++ jz_eth_tx_timeout(dev); ++ netif_wake_queue(dev); ++ ++ return 0; ++} ++ ++static int jz_eth_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) ++{ ++ int ret; ++ ++ if (!dev->data) ++ return -EINVAL; ++ ++ switch (rqst) { ++ case PM_SUSPEND: ++ ret = jz_eth_suspend((struct net_device *)dev->data, ++ (int)data); ++ break; ++ ++ case PM_RESUME: ++ ret = jz_eth_resume((struct net_device *)dev->data); ++ break; ++ ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++#endif /* CONFIG_PM */ ++ ++static int __init jz_eth_init(void) ++{ ++ struct net_device *dev; ++ struct jz_eth_private *np; ++ int err; ++ ++ dev = alloc_etherdev(sizeof(struct jz_eth_private)); ++ if (!dev) { ++ printk(KERN_ERR "%s: alloc_etherdev failed\n", DRV_NAME); ++ return -ENOMEM; ++ } ++ netdev = dev; ++ ++ np = (struct jz_eth_private *)P2ADDR(dev->priv); ++ dev->priv = np; ++ memset(np, 0, sizeof(struct jz_eth_private)); ++ ++ np->vaddr_rx_buf = (u32)dma_alloc_noncoherent(NULL, NUM_RX_DESCS*RX_BUF_SIZE, ++ &np->dma_rx_buf, 0); ++ ++ if (!np->vaddr_rx_buf) { ++ printk(KERN_ERR "%s: Cannot alloc dma buffers\n", DRV_NAME); ++ unregister_netdev(dev); ++ free_netdev(dev); ++ return -ENOMEM; ++ } ++ ++ np->dma_rx_ring = virt_to_bus(np->rx_ring); ++ np->dma_tx_ring = virt_to_bus(np->tx_ring); ++ np->full_duplex = 1; ++ np->link_state = 1; ++ ++ spin_lock_init(&np->lock); ++ ++ ether_setup(dev); ++ dev->irq = IRQ_ETH; ++ dev->open = jz_eth_open; ++ dev->stop = jz_eth_close; ++ dev->hard_start_xmit = jz_eth_send_packet; ++ dev->get_stats = jz_eth_get_stats; ++ dev->set_multicast_list = jz_set_multicast_list; ++ dev->do_ioctl = jz_eth_ioctl; ++ dev->tx_timeout = jz_eth_tx_timeout; ++ dev->watchdog_timeo = ETH_TX_TIMEOUT; ++ ++ /* configure MAC address */ ++ get_mac_address(dev); ++ ++ if ((err = register_netdev(dev)) != 0) { ++ printk(KERN_ERR "%s: Cannot register net device, error %d\n", ++ DRV_NAME, err); ++ free_netdev(dev); ++ return -ENOMEM; ++ } ++ ++//#ifdef 0 //CONFIG_PM ++// np->pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, jz_eth_pm_callback); ++// if (np->pmdev) ++// np->pmdev->data = dev; ++//#endif ++ ++ return 0; ++} ++ ++static void __exit jz_eth_exit(void) ++{ ++ struct net_device *dev = netdev; ++ struct jz_eth_private *np = dev->priv; ++ ++ unregister_netdev(dev); ++ dma_free_noncoherent(NULL, NUM_RX_DESCS * RX_BUF_SIZE, ++ (void *)np->vaddr_rx_buf, np->dma_rx_buf); ++ free_netdev(dev); ++} ++ ++module_init(jz_eth_init); ++module_exit(jz_eth_exit); +diff -urN linux-2.6.24.7.old/drivers/net/jz_eth.h linux-2.6.24.7/drivers/net/jz_eth.h +--- linux-2.6.24.7.old/drivers/net/jz_eth.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/net/jz_eth.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,403 @@ ++/* ++ * linux/drivers/net/jz_eth.h ++ * ++ * Jz4730/Jz5730 On-Chip ethernet driver. ++ * ++ * Copyright (C) 2005 - 2007 Ingenic Semiconductor Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++#ifndef __JZ_ETH_H__ ++#define __JZ_ETH_H__ ++ ++/* DMA control and status registers */ ++#define DMA_BMR (ETH_BASE + 0x1000) // Bus mode ++#define DMA_TPD (ETH_BASE + 0x1004) // Transmit poll demand register ++#define DMA_RPD (ETH_BASE + 0x1008) // Receieve poll demand register ++#define DMA_RRBA (ETH_BASE + 0x100C) // Receieve descriptor base address ++#define DMA_TRBA (ETH_BASE + 0x1010) // Transmit descriptor base address ++#define DMA_STS (ETH_BASE + 0x1014) // Status register ++#define DMA_OMR (ETH_BASE + 0x1018) // Command register ++#define DMA_IMR (ETH_BASE + 0x101C) ++#define DMA_MFC (ETH_BASE + 0x1020) ++ ++/* DMA CSR8-CSR19 reserved */ ++#define DMA_CTA (ETH_BASE + 0x1050) ++#define DMA_CRA (ETH_BASE + 0x1054) ++ ++/* Mac control and status registers */ ++#define MAC_MCR (ETH_BASE + 0x0000) ++#define MAC_MAH (ETH_BASE + 0x0004) ++#define MAC_MAL (ETH_BASE + 0x0008) ++#define MAC_HTH (ETH_BASE + 0x000C) ++#define MAC_HTL (ETH_BASE + 0x0010) ++#define MAC_MIIA (ETH_BASE + 0x0014) ++#define MAC_MIID (ETH_BASE + 0x0018) ++#define MAC_FCR (ETH_BASE + 0x001C) ++#define MAC_VTR1 (ETH_BASE + 0x0020) ++#define MAC_VTR2 (ETH_BASE + 0x0024) ++ ++/* ++ * Bus Mode Register (DMA_BMR) ++ */ ++#define BMR_PBL 0x00003f00 /* Programmable Burst Length */ ++#define BMR_DSL 0x0000007c /* Descriptor Skip Length */ ++#define BMR_BAR 0x00000002 /* Bus ARbitration */ ++#define BMR_SWR 0x00000001 /* Software Reset */ ++ ++#define PBL_0 0x00000000 /* DMA burst length = amount in RX FIFO */ ++#define PBL_1 0x00000100 /* 1 longword DMA burst length */ ++#define PBL_2 0x00000200 /* 2 longwords DMA burst length */ ++#define PBL_4 0x00000400 /* 4 longwords DMA burst length */ ++#define PBL_8 0x00000800 /* 8 longwords DMA burst length */ ++#define PBL_16 0x00001000 /* 16 longwords DMA burst length */ ++#define PBL_32 0x00002000 /* 32 longwords DMA burst length */ ++ ++#define DSL_0 0x00000000 /* 0 longword / descriptor */ ++#define DSL_1 0x00000004 /* 1 longword / descriptor */ ++#define DSL_2 0x00000008 /* 2 longwords / descriptor */ ++#define DSL_4 0x00000010 /* 4 longwords / descriptor */ ++#define DSL_8 0x00000020 /* 8 longwords / descriptor */ ++#define DSL_16 0x00000040 /* 16 longwords / descriptor */ ++#define DSL_32 0x00000080 /* 32 longwords / descriptor */ ++ ++/* ++ * Status Register (DMA_STS) ++ */ ++#define STS_BE 0x03800000 /* Bus Error Bits */ ++#define STS_TS 0x00700000 /* Transmit Process State */ ++#define STS_RS 0x000e0000 /* Receive Process State */ ++ ++#define TS_STOP 0x00000000 /* Stopped */ ++#define TS_FTD 0x00100000 /* Running Fetch Transmit Descriptor */ ++#define TS_WEOT 0x00200000 /* Running Wait for End Of Transmission */ ++#define TS_QDAT 0x00300000 /* Running Queue skb data into TX FIFO */ ++#define TS_RES 0x00400000 /* Reserved */ ++#define TS_SPKT 0x00500000 /* Reserved */ ++#define TS_SUSP 0x00600000 /* Suspended */ ++#define TS_CLTD 0x00700000 /* Running Close Transmit Descriptor */ ++ ++#define RS_STOP 0x00000000 /* Stopped */ ++#define RS_FRD 0x00020000 /* Running Fetch Receive Descriptor */ ++#define RS_CEOR 0x00040000 /* Running Check for End of Receive Packet */ ++#define RS_WFRP 0x00060000 /* Running Wait for Receive Packet */ ++#define RS_SUSP 0x00080000 /* Suspended */ ++#define RS_CLRD 0x000a0000 /* Running Close Receive Descriptor */ ++#define RS_FLUSH 0x000c0000 /* Running Flush RX FIFO */ ++#define RS_QRFS 0x000e0000 /* Running Queue RX FIFO into RX Skb */ ++ ++/* ++ * Operation Mode Register (DMA_OMR) ++ */ ++#define OMR_TTM 0x00400000 /* Transmit Threshold Mode */ ++#define OMR_SF 0x00200000 /* Store and Forward */ ++#define OMR_TR 0x0000c000 /* Threshold Control Bits */ ++#define OMR_ST 0x00002000 /* Start/Stop Transmission Command */ ++#define OMR_OSF 0x00000004 /* Operate on Second Frame */ ++#define OMR_SR 0x00000002 /* Start/Stop Receive */ ++ ++#define TR_18 0x00000000 /* Threshold set to 18 (32) bytes */ ++#define TR_24 0x00004000 /* Threshold set to 24 (64) bytes */ ++#define TR_32 0x00008000 /* Threshold set to 32 (128) bytes */ ++#define TR_40 0x0000c000 /* Threshold set to 40 (256) bytes */ ++ ++/* ++ * Missed Frames Counters (DMA_MFC) ++ */ ++//#define MFC_CNT1 0xffff0000 /* Missed Frames Counter Bits by application */ ++#define MFC_CNT1 0x0ffe0000 /* Missed Frames Counter Bits by application */ ++#define MFC_CNT2 0x0000ffff /* Missed Frames Counter Bits by controller */ ++ ++/* ++ * Mac control Register (MAC_MCR) ++ */ ++#define MCR_RA 0x80000000 /* Receive All */ ++#define MCR_HBD 0x10000000 /* HeartBeat Disable */ ++#define MCR_PS 0x08000000 /* Port Select */ ++#define MCR_OWD 0x00800000 /* Receive own Disable */ ++#define MCR_OM 0x00600000 /* Operating(loopback) Mode */ ++#define MCR_FDX 0x00100000 /* Full Duplex Mode */ ++#define MCR_PM 0x00080000 /* Pass All Multicast */ ++#define MCR_PR 0x00040000 /* Promiscuous Mode */ ++#define MCR_IF 0x00020000 /* Inverse Filtering */ ++#define MCR_PB 0x00010000 /* Pass Bad Frames */ ++#define MCR_HO 0x00008000 /* Hash Only Filtering Mode */ ++#define MCR_HP 0x00002000 /* Hash/Perfect Receive Filtering Mode */ ++#define MCR_FC 0x00001000 /* Late Collision control */ ++#define MCR_BFD 0x00000800 /* Boardcast frame Disable */ ++#define MCR_RED 0x00000400 /* Retry Disable */ ++#define MCR_APS 0x00000100 /* Automatic pad stripping */ ++#define MCR_BL 0x000000c0 /* Back off Limit */ ++#define MCR_DC 0x00000020 /* Deferral check */ ++#define MCR_TE 0x00000008 /* Transmitter enable */ ++#define MCR_RE 0x00000004 /* Receiver enable */ ++ ++#define MCR_MII_10 ( OMR_TTM | MCR_PS) ++#define MCR_MII_100 ( MCR_HBD | MCR_PS) ++ ++/* Flow control Register (MAC_FCR) */ ++#define FCR_PT 0xffff0000 /* Pause time */ ++#define FCR_PCF 0x00000004 /* Pass control frames */ ++#define FCR_FCE 0x00000002 /* Flow control enable */ ++#define FCR_FCB 0x00000001 /* Flow control busy */ ++ ++ ++/* Constants for the interrupt mask and ++ * interrupt status registers. (DMA_SIS and DMA_IMR) ++ */ ++#define DMA_INT_NI 0x00010000 // Normal interrupt summary ++#define DMA_INT_AI 0x00008000 // Abnormal interrupt summary ++#define DMA_INT_ER 0x00004000 // Early receive interrupt ++#define DMA_INT_FB 0x00002000 // Fatal bus error ++#define DMA_INT_ET 0x00000400 // Early transmit interrupt ++#define DMA_INT_RW 0x00000200 // Receive watchdog timeout ++#define DMA_INT_RS 0x00000100 // Receive stop ++#define DMA_INT_RU 0x00000080 // Receive buffer unavailble ++#define DMA_INT_RI 0x00000040 // Receive interrupt ++#define DMA_INT_UN 0x00000020 // Underflow ++#define DMA_INT_TJ 0x00000008 // Transmit jabber timeout ++#define DMA_INT_TU 0x00000004 // Transmit buffer unavailble ++#define DMA_INT_TS 0x00000002 // Transmit stop ++#define DMA_INT_TI 0x00000001 // Transmit interrupt ++ ++/* ++ * Receive Descriptor Bit Summary ++ */ ++#define R_OWN 0x80000000 /* Own Bit */ ++#define RD_FF 0x40000000 /* Filtering Fail */ ++#define RD_FL 0x3fff0000 /* Frame Length */ ++#define RD_ES 0x00008000 /* Error Summary */ ++#define RD_DE 0x00004000 /* Descriptor Error */ ++#define RD_LE 0x00001000 /* Length Error */ ++#define RD_RF 0x00000800 /* Runt Frame */ ++#define RD_MF 0x00000400 /* Multicast Frame */ ++#define RD_FS 0x00000200 /* First Descriptor */ ++#define RD_LS 0x00000100 /* Last Descriptor */ ++#define RD_TL 0x00000080 /* Frame Too Long */ ++#define RD_CS 0x00000040 /* Collision Seen */ ++#define RD_FT 0x00000020 /* Frame Type */ ++#define RD_RJ 0x00000010 /* Receive Watchdog timeout*/ ++#define RD_RE 0x00000008 /* Report on MII Error */ ++#define RD_DB 0x00000004 /* Dribbling Bit */ ++#define RD_CE 0x00000002 /* CRC Error */ ++ ++#define RD_RER 0x02000000 /* Receive End Of Ring */ ++#define RD_RCH 0x01000000 /* Second Address Chained */ ++#define RD_RBS2 0x003ff800 /* Buffer 2 Size */ ++#define RD_RBS1 0x000007ff /* Buffer 1 Size */ ++ ++/* ++ * Transmit Descriptor Bit Summary ++ */ ++#define T_OWN 0x80000000 /* Own Bit */ ++#define TD_ES 0x00008000 /* Frame Aborted (error summary)*/ ++#define TD_LO 0x00000800 /* Loss Of Carrier */ ++#define TD_NC 0x00000400 /* No Carrier */ ++#define TD_LC 0x00000200 /* Late Collision */ ++#define TD_EC 0x00000100 /* Excessive Collisions */ ++#define TD_HF 0x00000080 /* Heartbeat Fail */ ++#define TD_CC 0x0000003c /* Collision Counter */ ++#define TD_UF 0x00000002 /* Underflow Error */ ++#define TD_DE 0x00000001 /* Deferred */ ++ ++#define TD_IC 0x80000000 /* Interrupt On Completion */ ++#define TD_LS 0x40000000 /* Last Segment */ ++#define TD_FS 0x20000000 /* First Segment */ ++#define TD_FT1 0x10000000 /* Filtering Type */ ++#define TD_SET 0x08000000 /* Setup Packet */ ++#define TD_AC 0x04000000 /* Add CRC Disable */ ++#define TD_TER 0x02000000 /* Transmit End Of Ring */ ++#define TD_TCH 0x01000000 /* Second Address Chained */ ++#define TD_DPD 0x00800000 /* Disabled Padding */ ++#define TD_FT0 0x00400000 /* Filtering Type */ ++#define TD_TBS2 0x003ff800 /* Buffer 2 Size */ ++#define TD_TBS1 0x000007ff /* Buffer 1 Size */ ++ ++#define PERFECT_F 0x00000000 ++#define HASH_F TD_FT0 ++#define INVERSE_F TD_FT1 ++#define HASH_O_F (TD_FT1 | TD_F0) ++ ++/* ++ * Constant setting ++ */ ++ ++#define IMR_DEFAULT ( DMA_INT_TI | DMA_INT_RI | \ ++ DMA_INT_TS | DMA_INT_RS | \ ++ DMA_INT_TU | DMA_INT_RU | \ ++ DMA_INT_FB ) ++ ++#define IMR_ENABLE (DMA_INT_NI | DMA_INT_AI) ++ ++#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */ ++#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */ ++ ++#define HASH_TABLE_LEN 512 /* Bits */ ++#define HASH_BITS 0x01ff /* 9 LS bits */ ++ ++#define SETUP_FRAME_LEN 192 /* Bytes */ ++#define IMPERF_PA_OFFSET 156 /* Bytes */ ++ ++/* ++ * Address Filtering Modes ++ */ ++#define PERFECT 0 /* 16 perfect physical addresses */ ++#define HASH_PERF 1 /* 1 perfect, 512 multicast addresses */ ++#define PERFECT_REJ 2 /* Reject 16 perfect physical addresses */ ++#define ALL_HASH 3 /* Hashes all physical & multicast addrs */ ++ ++#define ALL 0 /* Clear out all the setup frame */ ++#define PHYS_ADDR_ONLY 1 /* Update the physical address only */ ++ ++/* MII register */ ++#define MII_BMCR 0x00 /* MII Basic Mode Control Register */ ++#define MII_BMSR 0x01 /* MII Basic Mode Status Register */ ++#define MII_ID1 0x02 /* PHY Identifier Register 1 */ ++#define MII_ID2 0x03 /* PHY Identifier Register 2 */ ++#define MII_ANAR 0x04 /* Auto Negotiation Advertisement Register */ ++#define MII_ANLPAR 0x05 /* Auto Negotiation Link Partner Ability */ ++#define MII_ANER 0x06 /* Auto Negotiation Expansion */ ++#define MII_DSCR 0x10 /* Davicom Specified Configration Register */ ++#define MII_DSCSR 0x11 /* Davicom Specified Configration/Status Register */ ++#define MII_10BTCSR 0x12 /* 10base-T Specified Configration/Status Register */ ++ ++ ++#define MII_PREAMBLE 0xffffffff /* MII Management Preamble */ ++#define MII_TEST 0xaaaaaaaa /* MII Test Signal */ ++#define MII_STRD 0x06 /* Start of Frame+Op Code: use low nibble */ ++#define MII_STWR 0x0a /* Start of Frame+Op Code: use low nibble */ ++ ++/* ++ * MII Management Control Register ++ */ ++#define MII_CR_RST 0x8000 /* RESET the PHY chip */ ++#define MII_CR_LPBK 0x4000 /* Loopback enable */ ++#define MII_CR_SPD 0x2000 /* 0: 10Mb/s; 1: 100Mb/s */ ++#define MII_CR_ASSE 0x1000 /* Auto Speed Select Enable */ ++#define MII_CR_PD 0x0800 /* Power Down */ ++#define MII_CR_ISOL 0x0400 /* Isolate Mode */ ++#define MII_CR_RAN 0x0200 /* Restart Auto Negotiation */ ++#define MII_CR_FDM 0x0100 /* Full Duplex Mode */ ++#define MII_CR_CTE 0x0080 /* Collision Test Enable */ ++ ++/* ++ * MII Management Status Register ++ */ ++#define MII_SR_T4C 0x8000 /* 100BASE-T4 capable */ ++#define MII_SR_TXFD 0x4000 /* 100BASE-TX Full Duplex capable */ ++#define MII_SR_TXHD 0x2000 /* 100BASE-TX Half Duplex capable */ ++#define MII_SR_TFD 0x1000 /* 10BASE-T Full Duplex capable */ ++#define MII_SR_THD 0x0800 /* 10BASE-T Half Duplex capable */ ++#define MII_SR_ASSC 0x0020 /* Auto Speed Selection Complete*/ ++#define MII_SR_RFD 0x0010 /* Remote Fault Detected */ ++#define MII_SR_ANC 0x0008 /* Auto Negotiation capable */ ++#define MII_SR_LKS 0x0004 /* Link Status */ ++#define MII_SR_JABD 0x0002 /* Jabber Detect */ ++#define MII_SR_XC 0x0001 /* Extended Capabilities */ ++ ++/* ++ * MII Management Auto Negotiation Advertisement Register ++ */ ++#define MII_ANA_TAF 0x03e0 /* Technology Ability Field */ ++#define MII_ANA_T4AM 0x0200 /* T4 Technology Ability Mask */ ++#define MII_ANA_TXAM 0x0180 /* TX Technology Ability Mask */ ++#define MII_ANA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ ++#define MII_ANA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ ++#define MII_ANA_100M 0x0380 /* 100Mb Technology Ability Mask */ ++#define MII_ANA_10M 0x0060 /* 10Mb Technology Ability Mask */ ++#define MII_ANA_CSMA 0x0001 /* CSMA-CD Capable */ ++ ++/* ++ * MII Management Auto Negotiation Remote End Register ++ */ ++#define MII_ANLPA_NP 0x8000 /* Next Page (Enable) */ ++#define MII_ANLPA_ACK 0x4000 /* Remote Acknowledge */ ++#define MII_ANLPA_RF 0x2000 /* Remote Fault */ ++#define MII_ANLPA_TAF 0x03e0 /* Technology Ability Field */ ++#define MII_ANLPA_T4AM 0x0200 /* T4 Technology Ability Mask */ ++#define MII_ANLPA_TXAM 0x0180 /* TX Technology Ability Mask */ ++#define MII_ANLPA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ ++#define MII_ANLPA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ ++#define MII_ANLPA_100M 0x0380 /* 100Mb Technology Ability Mask */ ++#define MII_ANLPA_10M 0x0060 /* 10Mb Technology Ability Mask */ ++#define MII_ANLPA_CSMA 0x0001 /* CSMA-CD Capable */ ++ ++/* ++ * MII Management DAVICOM Specified Configuration And Status Register ++ */ ++#define MII_DSCSR_100FDX 0x8000 /* 100M Full Duplex Operation Mode */ ++#define MII_DSCSR_100HDX 0x4000 /* 100M Half Duplex Operation Mode */ ++#define MII_DSCSR_10FDX 0x2000 /* 10M Full Duplex Operation Mode */ ++#define MII_DSCSR_10HDX 0x1000 /* 10M Half Duplex Operation Mode */ ++#define MII_DSCSR_ANMB 0x000f /* Auto-Negotiation Monitor Bits */ ++ ++ ++/* ++ * Used by IOCTL ++ */ ++#define READ_COMMAND (SIOCDEVPRIVATE+4) ++#define WRITE_COMMAND (SIOCDEVPRIVATE+5) ++#define GETDRIVERINFO (SIOCDEVPRIVATE+6) ++ ++/* ++ * Device data and structure ++ */ ++ ++#define ETH_TX_TIMEOUT (6*HZ) ++ ++#define RX_BUF_SIZE 1536 ++ ++#define NUM_RX_DESCS 32 ++#define NUM_TX_DESCS 16 ++ ++static const char *media_types[] = { ++ "10BaseT-HD ", "10BaseT-FD ","100baseTx-HD ", ++ "100baseTx-FD", "100baseT4", 0 ++}; ++ ++typedef struct { ++ unsigned int status; ++ unsigned int desc1; ++ unsigned int buf1_addr; ++ unsigned int next_addr; ++} jz_desc_t; ++ ++struct jz_eth_private { ++ jz_desc_t tx_ring[NUM_TX_DESCS]; /* transmit descriptors */ ++ jz_desc_t rx_ring[NUM_RX_DESCS]; /* receive descriptors */ ++ dma_addr_t dma_tx_ring; /* bus address of tx ring */ ++ dma_addr_t dma_rx_ring; /* bus address of rx ring */ ++ dma_addr_t dma_rx_buf; /* DMA address of rx buffer */ ++ unsigned int vaddr_rx_buf; /* virtual address of rx buffer */ ++ ++ unsigned int rx_head; /* first rx descriptor */ ++ unsigned int tx_head; /* first tx descriptor */ ++ unsigned int tx_tail; /* last unacked transmit packet */ ++ unsigned int tx_full; /* transmit buffers are full */ ++ struct sk_buff *tx_skb[NUM_TX_DESCS]; /* skbuffs for packets to transmit */ ++ ++ struct net_device_stats stats; ++ spinlock_t lock; ++ ++ int media; /* Media (eg TP), mode (eg 100B)*/ ++ int full_duplex; /* Current duplex setting. */ ++ int link_state; ++ char phys[32]; /* List of attached PHY devices */ ++ char valid_phy; /* Current linked phy-id with MAC */ ++ int mii_phy_cnt; ++ int phy_type; /* 1-RTL8309,0-DVCOM */ ++ struct ethtool_cmd ecmds[32]; ++ u16 advertising; /* NWay media advertisement */ ++ ++ pid_t thr_pid; /* Link cheak thread ID */ ++ int thread_die; ++ struct completion thr_exited; ++ wait_queue_head_t thr_wait; ++ ++ struct pm_dev *pmdev; ++}; ++ ++#endif /* __JZ_ETH_H__ */ +diff -urN linux-2.6.24.7.old/drivers/net/jzcs8900a.c linux-2.6.24.7/drivers/net/jzcs8900a.c +--- linux-2.6.24.7.old/drivers/net/jzcs8900a.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/net/jzcs8900a.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,649 @@ ++ ++/* ++ * linux/drivers/net/jzcs8900a.c ++ * ++ * Author: Lucifer ++ * ++ * A Cirrus Logic CS8900A driver for Linux ++ * based on the cs89x0 driver written by Russell Nelson, ++ * Donald Becker, and others. ++ * ++ * This source code 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. ++ */ ++ ++/* ++ * At the moment the driver does not support memory mode operation. ++ * It is trivial to implement this, but not worth the effort. ++ */ ++ ++/* ++ * TODO: ++ * ++ * 1. If !ready in send_start(), queue buffer and send it in interrupt handler ++ * when we receive a BufEvent with Rdy4Tx, send it again. dangerous! ++ * 2. how do we prevent interrupt handler destroying integrity of get_stats()? ++ * 3. Change reset code to check status. ++ * 4. Implement set_mac_address and remove fake mac address ++ * 5. Link status detection stuff ++ * 6. Write utility to write EEPROM, do self testing, etc. ++ * 7. Implement DMA routines (I need a board w/ DMA support for that) ++ * 8. Power management ++ * 9. Add support for multiple ethernet chips ++ * 10. Add support for other cs89xx chips (need hardware for that) ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "jzcs8900a.h" ++ ++#define FULL_DUPLEX ++#define INT_PIN 0 ++#ifdef CONFIG_SOC_JZ4740 ++#define CIRRUS_DEFAULT_IO 0xa8000000 ++#define CIRRUS_DEFAULT_IRQ 107 ++ ++#elif CONFIG_SOC_JZ4750 ++#define CIRRUS_DEFAULT_IO 0xac000000 ++ ++#ifdef CONFIG_JZ4750_FUWA ++#define CIRRUS_DEFAULT_IRQ (32*4+20+48) ++#else ++#define CIRRUS_DEFAULT_IRQ (32*2 +6+48) ++#endif ++ ++ ++#endif ++ ++typedef struct { ++ struct net_device_stats stats; ++ u16 txlen; ++} cirrus_t; ++ ++static int ethaddr_cmd = 0; ++static unsigned char ethaddr_hex[6]; ++static struct net_device *dev; ++ ++/* ++ * I/O routines ++ */ ++static void gpio_init_cs8900(void) ++{ ++#ifdef CONFIG_SOC_JZ4740 ++ __gpio_as_func0(60); //CS4# ++ __gpio_as_func0(61); //RD# ++ __gpio_as_func0(62); //WR# ++ __gpio_as_irq_high_level(59); //irq ++ __gpio_disable_pull(59); //disable pull ++ REG_EMC_SMCR4 |= (1 << 6); //16bit ++#elif CONFIG_SOC_JZ4750 ++ __gpio_as_func0(32*2+23); //CS3# ++ __gpio_as_func0(32*2+25); //RD# ++ __gpio_as_func0(32*2+26); //WR# ++ ++#ifdef CONFIG_JZ4750_FUWA ++ __gpio_as_irq_high_level(32*4+20); //irq ++ __gpio_disable_pull(32*4+20); //disable pull ++#else ++ __gpio_as_irq_high_level(32*2 +6); //irq ++ __gpio_disable_pull(32*2 +6); //disable pull ++#endif ++ ++ REG_EMC_SMCR3 |= (1 << 6); //16bit ++#endif ++ udelay(1); ++} ++ ++static inline u16 cirrus_read (struct net_device *dev,u16 reg) ++{ ++ outw (reg,dev->base_addr + PP_Address); ++ return (inw (dev->base_addr + PP_Data)); ++} ++ ++static inline void cirrus_write (struct net_device *dev,u16 reg,u16 value) ++{ ++ outw (reg,dev->base_addr + PP_Address); ++ outw (value,dev->base_addr + PP_Data); ++} ++ ++static inline void cirrus_set (struct net_device *dev,u16 reg,u16 value) ++{ ++ cirrus_write (dev,reg,cirrus_read (dev,reg) | value); ++} ++ ++static inline void cirrus_clear (struct net_device *dev,u16 reg,u16 value) ++{ ++ cirrus_write (dev,reg,cirrus_read (dev,reg) & ~value); ++} ++ ++static inline void cirrus_frame_read (struct net_device *dev,struct sk_buff *skb,u16 length) ++{ ++ insw (dev->base_addr,skb_put (skb,length),(length + 1) / 2); ++} ++ ++static inline void cirrus_frame_write (struct net_device *dev,struct sk_buff *skb) ++{ ++ outsw (dev->base_addr,skb->data,(skb->len + 1) / 2); ++} ++ ++/* ++ * Debugging functions ++ */ ++ ++#ifdef DEBUG ++static inline int printable (int c) ++{ ++ return ((c >= 32 && c <= 126) || ++ (c >= 174 && c <= 223) || ++ (c >= 242 && c <= 243) || ++ (c >= 252 && c <= 253)); ++} ++ ++static void dump16 (struct net_device *dev,const u8 *s,size_t len) ++{ ++ int i; ++ char str[128]; ++ ++ if (!len) return; ++ ++ *str = '\0'; ++ ++ for (i = 0; i < len; i++) { ++ if (i && !(i % 4)) strcat (str," "); ++ sprintf (str,"%s%.2x ",str,s[i]); ++ } ++ ++ for ( ; i < 16; i++) { ++ if (i && !(i % 4)) strcat (str," "); ++ strcat (str," "); ++ } ++ ++ strcat (str," "); ++ for (i = 0; i < len; i++) sprintf (str,"%s%c",str,printable (s[i]) ? s[i] : '.'); ++ ++ printk (KERN_DEBUG "%s: %s\n",dev->name,str); ++} ++ ++static void hexdump (struct net_device *dev,const void *ptr,size_t size) ++{ ++ const u8 *s = (u8 *) ptr; ++ int i; ++ for (i = 0; i < size / 16; i++, s += 16) dump16 (dev,s,16); ++ dump16 (dev,s,size % 16); ++} ++ ++static void dump_packet (struct net_device *dev,struct sk_buff *skb,const char *type) ++{ ++ printk (KERN_INFO "%s: %s %d byte frame %.2x:%.2x:%.2x:%.2x:%.2x:%.2x to %.2x:%.2x:%.2x:%.2x:%.2x:%.2x type %.4x\n", ++ dev->name, ++ type, ++ skb->len, ++ skb->data[0],skb->data[1],skb->data[2],skb->data[3],skb->data[4],skb->data[5], ++ skb->data[6],skb->data[7],skb->data[8],skb->data[9],skb->data[10],skb->data[11], ++ (skb->data[12] << 8) | skb->data[13]); ++ if (skb->len < 0x100) hexdump (dev,skb->data,skb->len); ++} ++#endif /* #ifdef DEBUG */ ++ ++/* ++ * Driver functions ++ */ ++ ++static void cirrus_receive (struct net_device *dev) ++{ ++ cirrus_t *priv = (cirrus_t *) dev->priv; ++ struct sk_buff *skb; ++ u16 status,length; ++ ++ status = cirrus_read (dev,PP_RxStatus); ++ length = cirrus_read (dev,PP_RxLength); ++ ++ if (!(status & RxOK)) { ++ priv->stats.rx_errors++; ++ if ((status & (Runt | Extradata))) priv->stats.rx_length_errors++; ++ if ((status & CRCerror)) priv->stats.rx_crc_errors++; ++ return; ++ } ++ ++ if ((skb = dev_alloc_skb (length + 4)) == NULL) { ++ priv->stats.rx_dropped++; ++ return; ++ } ++ ++ skb->dev = dev; ++ skb_reserve (skb,2); ++ ++ cirrus_frame_read (dev,skb,length); ++ skb->protocol = eth_type_trans (skb,dev); ++ ++ netif_rx (skb); ++ dev->last_rx = jiffies; ++ ++ priv->stats.rx_packets++; ++ priv->stats.rx_bytes += length; ++} ++ ++static int cirrus_send_start (struct sk_buff *skb,struct net_device *dev) ++{ ++ cirrus_t *priv = (cirrus_t *) dev->priv; ++ u16 status; ++ ++ mdelay(10); ++ netif_stop_queue (dev); ++ ++ cirrus_write (dev,PP_TxCMD,TxStart (After5)); ++ cirrus_write (dev,PP_TxLength,skb->len); ++ ++ status = cirrus_read (dev,PP_BusST); ++ ++ if ((status & TxBidErr)) { ++ printk (KERN_WARNING "%s: Invalid frame size %d!\n",dev->name,skb->len); ++ priv->stats.tx_errors++; ++ priv->stats.tx_aborted_errors++; ++ priv->txlen = 0; ++ return (1); ++ } ++ ++ if (!(status & Rdy4TxNOW)) { ++ //printk (KERN_WARNING "%s: Transmit buffer not free!\n",dev->name); ++ priv->stats.tx_errors++; ++ priv->txlen = 0; ++ /* FIXME: store skb and send it in interrupt handler */ ++ return (1); ++ } ++ ++ cirrus_frame_write (dev,skb); ++ dev->trans_start = jiffies; ++ ++ dev_kfree_skb (skb); ++ ++ priv->txlen = skb->len; ++ ++ return (0); ++} ++ ++static irqreturn_t cirrus_interrupt(int irq, void *id) ++{ ++ struct net_device *dev = (struct net_device *) id; ++ cirrus_t *priv; ++ u16 status; ++ ++ if (dev->priv == NULL) { ++ return IRQ_NONE; ++ } ++ ++ priv = (cirrus_t *) dev->priv; ++ ++ while ((status = cirrus_read (dev,PP_ISQ))) { ++ switch (RegNum (status)) { ++ case RxEvent: ++ cirrus_receive (dev); ++ break; ++ ++ case TxEvent: ++ priv->stats.collisions += ColCount (cirrus_read (dev,PP_TxCOL)); ++ if (!(RegContent (status) & TxOK)) { ++ priv->stats.tx_errors++; ++ if ((RegContent (status) & Out_of_window)) priv->stats.tx_window_errors++; ++ if ((RegContent (status) & Jabber)) priv->stats.tx_aborted_errors++; ++ break; ++ } else if (priv->txlen) { ++ priv->stats.tx_packets++; ++ priv->stats.tx_bytes += priv->txlen; ++ } ++ priv->txlen = 0; ++ netif_wake_queue (dev); ++ break; ++ ++ case BufEvent: ++ if ((RegContent (status) & RxMiss)) { ++ u16 missed = MissCount (cirrus_read (dev,PP_RxMISS)); ++ priv->stats.rx_errors += missed; ++ priv->stats.rx_missed_errors += missed; ++ } ++ if ((RegContent (status) & TxUnderrun)) { ++ priv->stats.tx_errors++; ++ priv->stats.tx_fifo_errors++; ++ } ++ /* FIXME: if Rdy4Tx, transmit last sent packet (if any) */ ++ priv->txlen = 0; ++ netif_wake_queue (dev); ++ break; ++ ++ case TxCOL: ++ priv->stats.collisions += ColCount (cirrus_read (dev,PP_TxCOL)); ++ break; ++ ++ case RxMISS: ++ status = MissCount (cirrus_read (dev,PP_RxMISS)); ++ priv->stats.rx_errors += status; ++ priv->stats.rx_missed_errors += status; ++ break; ++ default: ++ return IRQ_HANDLED; ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void cirrus_transmit_timeout (struct net_device *dev) ++{ ++ cirrus_t *priv = (cirrus_t *) dev->priv; ++ priv->stats.tx_errors++; ++ priv->stats.tx_heartbeat_errors++; ++ priv->txlen = 0; ++ netif_wake_queue (dev); ++} ++ ++static int cirrus_start (struct net_device *dev) ++{ ++ int result; ++ ++ /* valid ethernet address? */ ++ if (!is_valid_ether_addr(dev->dev_addr)) { ++ printk(KERN_ERR "%s: invalid ethernet MAC address\n",dev->name); ++ return (-EINVAL); ++ } ++ ++ /* install interrupt handler */ ++ if ((result = request_irq (dev->irq, &cirrus_interrupt, IRQF_DISABLED, dev->name, dev)) < 0) { ++ printk (KERN_ERR "%s: could not register interrupt %d\n",dev->name,dev->irq); ++ return (result); ++ } ++ ++ /* enable the ethernet controller */ ++ cirrus_set (dev,PP_RxCFG,RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE); ++ cirrus_set (dev,PP_RxCTL,RxOKA | IndividualA | BroadcastA); ++ cirrus_set (dev,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE); ++ cirrus_set (dev,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE); ++ cirrus_set (dev,PP_LineCTL,SerRxON | SerTxON); ++ cirrus_set (dev,PP_BusCTL,EnableRQ); ++ ++#ifdef FULL_DUPLEX ++ cirrus_set (dev,PP_TestCTL,FDX); ++#endif /* #ifdef FULL_DUPLEX */ ++ ++ /* start the queue */ ++ netif_start_queue (dev); ++ __gpio_unmask_irq(59); ++ ++ //MOD_INC_USE_COUNT; ++ return (0); ++} ++ ++static int cirrus_stop (struct net_device *dev) ++{ ++ /* disable ethernet controller */ ++ cirrus_write (dev,PP_BusCTL,0); ++ cirrus_write (dev,PP_TestCTL,0); ++ cirrus_write (dev,PP_SelfCTL,0); ++ cirrus_write (dev,PP_LineCTL,0); ++ cirrus_write (dev,PP_BufCFG,0); ++ cirrus_write (dev,PP_TxCFG,0); ++ cirrus_write (dev,PP_RxCTL,0); ++ cirrus_write (dev,PP_RxCFG,0); ++ ++ /* uninstall interrupt handler */ ++ free_irq (dev->irq,dev); ++ ++ /* stop the queue */ ++ netif_stop_queue (dev); ++ ++ //MOD_DEC_USE_COUNT; ++ ++ return (0); ++} ++ ++static int cirrus_set_mac_address (struct net_device *dev, void *p) ++{ ++ struct sockaddr *addr = (struct sockaddr *)p; ++ int i; ++ ++ if (netif_running(dev)) ++ return -EBUSY; ++ ++ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); ++ ++ /* configure MAC address */ ++ for (i = 0; i < ETH_ALEN; i += 2) ++ cirrus_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8)); ++ ++ return 0; ++} ++ ++static struct net_device_stats *cirrus_get_stats (struct net_device *dev) ++{ ++ cirrus_t *priv = (cirrus_t *) dev->priv; ++ return (&priv->stats); ++} ++ ++static void cirrus_set_receive_mode (struct net_device *dev) ++{ ++ if ((dev->flags & IFF_PROMISC)) ++ cirrus_set (dev,PP_RxCTL,PromiscuousA); ++ else ++ cirrus_clear (dev,PP_RxCTL,PromiscuousA); ++ ++ if ((dev->flags & IFF_ALLMULTI) && dev->mc_list) ++ cirrus_set (dev,PP_RxCTL,MulticastA); ++ else ++ cirrus_clear (dev,PP_RxCTL,MulticastA); ++} ++ ++/* ++ * Architecture dependant code ++ */ ++ ++/* ++ * Driver initialization routines ++ */ ++ ++int __init cirrus_probe(void) ++{ ++ static cirrus_t priv; ++ int i; ++ u16 value; ++ ++ printk ("Jz CS8900A driver for Linux (V0.02)\n"); ++ ++ /* Init hardware for PAVO board */ ++ gpio_init_cs8900(); ++ ++ /* Allocate ethernet device */ ++ dev = alloc_etherdev(sizeof(struct net_device)); ++ ++ memset (&priv,0,sizeof (cirrus_t)); ++ ++ ether_setup (dev); ++ ++ dev->open = cirrus_start; ++ dev->stop = cirrus_stop; ++ dev->hard_start_xmit = cirrus_send_start; ++ dev->get_stats = cirrus_get_stats; ++ dev->set_multicast_list = cirrus_set_receive_mode; ++ dev->set_mac_address = cirrus_set_mac_address; ++ dev->tx_timeout = cirrus_transmit_timeout; ++ dev->watchdog_timeo = HZ; ++ ++ if (ethaddr_cmd==1) ++ { ++ dev->dev_addr[0] = ethaddr_hex[0]; ++ dev->dev_addr[1] = ethaddr_hex[1]; ++ dev->dev_addr[2] = ethaddr_hex[2]; ++ dev->dev_addr[3] = ethaddr_hex[3]; ++ dev->dev_addr[4] = ethaddr_hex[4]; ++ dev->dev_addr[5] = ethaddr_hex[5]; ++ } ++ else //default mac address 00:2a:cc:2a:af:fe ++ { ++ dev->dev_addr[0] = 0x00; ++ dev->dev_addr[1] = 0x62; ++ dev->dev_addr[2] = 0x9c; ++ dev->dev_addr[3] = 0x61; ++ dev->dev_addr[4] = 0xcf; ++ dev->dev_addr[5] = 0x16; ++ } ++ dev->if_port = IF_PORT_10BASET; ++ dev->priv = (void *) &priv; ++ ++ dev->base_addr = CIRRUS_DEFAULT_IO; ++ dev->irq = CIRRUS_DEFAULT_IRQ; ++ ++ ++ /* module parameters override everything */ ++ if (!dev->base_addr) { ++ printk (KERN_ERR ++ "%s: No default I/O base address defined. Use io=... or\n" ++ "%s: define CIRRUS_DEFAULT_IO for your platform\n", ++ dev->name,dev->name); ++ return (-EINVAL); ++ } ++ ++ if (!dev->irq) { ++ printk (KERN_ERR ++ "%s: No default IRQ number defined. Use irq=... or\n" ++ "%s: define CIRRUS_DEFAULT_IRQ for your platform\n", ++ dev->name,dev->name); ++ return (-EINVAL); ++ } ++#if 0 ++ if ((result = check_region (dev->base_addr,16))) { ++ printk (KERN_ERR "%s: can't get I/O port address 0x%lx\n",dev->name,dev->base_addr); ++ return (result); ++ } ++#endif ++ if (!request_region (dev->base_addr,16,dev->name)) ++ return -EBUSY; ++#if 0 ++ /* verify EISA registration number for Cirrus Logic */ ++ if ((value = cirrus_read (dev,PP_ProductID)) != EISA_REG_CODE) { ++ printk (KERN_ERR "%s: incorrect signature 0x%.4x\n",dev->name,value); ++ return (-ENXIO); ++ } ++#endif ++ ++ /* verify chip version */ ++ value = cirrus_read (dev,PP_ProductID + 2); ++ if (VERSION (value) != CS8900A) { ++ printk (KERN_ERR "%s: unknown chip version 0x%.8x\n",dev->name,VERSION (value)); ++ return (-ENXIO); ++ } ++ printk (KERN_INFO "%s: CS8900A rev %c detected\n",dev->name,'B' + REVISION (value) - REV_B); ++ ++ /* setup interrupt number */ ++ cirrus_write (dev,PP_IntNum,INT_PIN); ++ ++ /* configure MAC address */ ++ for (i = 0; i < ETH_ALEN; i += 2) ++ { ++ //printk(" %x",dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8)); ++ cirrus_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8)); ++ } ++ ++ if (register_netdev(dev) != 0) { ++ printk(KERN_ERR " Cannot register net device\n"); ++ free_netdev(dev); ++ return -ENOMEM; ++ } ++ ++ return (0); ++} ++ ++static inline unsigned char str2hexnum(unsigned char c) ++{ ++ if(c >= '0' && c <= '9') ++ return c - '0'; ++ if(c >= 'a' && c <= 'f') ++ return c - 'a' + 10; ++ if(c >= 'A' && c <= 'F') ++ return c - 'A' + 10; ++ return 0; /* foo */ ++} ++ ++static inline void str2eaddr(unsigned char *ea, unsigned char *str) ++{ ++ int i; ++ ++ for(i = 0; i < 6; i++) { ++ unsigned char num; ++ ++ if((*str == '.') || (*str == ':')) ++ str++; ++ num = str2hexnum(*str++) << 4; ++ num |= (str2hexnum(*str++)); ++ ea[i] = num; ++ } ++} ++ ++static int __init ethernet_addr_setup(char *str) ++{ ++ if (!str) { ++ printk("ethaddr not set in command line\n"); ++ return -1; ++ } ++ ethaddr_cmd = 1; ++ str2eaddr(ethaddr_hex, str); ++ return 0; ++} ++ ++__setup("ethaddr=", ethernet_addr_setup); ++ ++//EXPORT_NO_SYMBOLS; ++ ++MODULE_AUTHOR ("Lucifer "); ++MODULE_DESCRIPTION ("Jz CS8900A driver for Linux (V0.02)"); ++MODULE_LICENSE ("GPL"); ++ ++//#ifdef MODULE ++ ++ ++#if 0 ++static int io = 0; ++static int irq = 0; ++ ++module_param(io, int, 0); ++MODULE_PARM_DESC (io,"I/O Base Address"); ++//MODULE_PARM (io,"i"); ++ ++module_param(irq, int, 0); ++MODULE_PARM_DESC (irq,"IRQ Number"); ++//MODULE_PARM (irq,"i"); ++#endif ++ ++static int __init jzcs8900_init(void) ++{ ++ if (cirrus_probe()) { ++ printk(KERN_WARNING "jzcs8900: No cs8900a found\n"); ++ } ++ ++ return 0; ++} ++ ++static void __exit jzcs8900_exit(void) ++{ ++ release_region(dev->base_addr,16); ++ unregister_netdev(dev); ++ free_netdev(dev); ++} ++ ++module_init(jzcs8900_init); ++module_exit(jzcs8900_exit); +diff -urN linux-2.6.24.7.old/drivers/net/jzcs8900a.h linux-2.6.24.7/drivers/net/jzcs8900a.h +--- linux-2.6.24.7.old/drivers/net/jzcs8900a.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/net/jzcs8900a.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,235 @@ ++#ifndef JZCS8900A_H ++#define JZCS8900A_H ++ ++/* ++ * linux/drivers/net/jzcs8900a.h ++ * ++ * Author: Lucifer ++ * ++ * A Cirrus Logic CS8900A driver for Linux ++ * based on the cs89x0 driver written by Russell Nelson, ++ * Donald Becker, and others. ++ * ++ * This source code 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. ++ */ ++ ++/* ++ * Ports ++ */ ++ ++#define PP_Address 0x0a /* PacketPage Pointer Port (Section 4.10.10) */ ++#define PP_Data 0x0c /* PacketPage Data Port (Section 4.10.10) */ ++ ++/* ++ * Registers ++ */ ++ ++#define PP_ProductID 0x0000 /* Section 4.3.1 Product Identification Code */ ++#define PP_MemBase 0x002c /* Section 4.9.2 Memory Base Address Register */ ++#define PP_IntNum 0x0022 /* Section 3.2.3 Interrupt Number */ ++#define PP_EEPROMCommand 0x0040 /* Section 4.3.11 EEPROM Command */ ++#define PP_EEPROMData 0x0042 /* Section 4.3.12 EEPROM Data */ ++#define PP_RxCFG 0x0102 /* Section 4.4.6 Receiver Configuration */ ++#define PP_RxCTL 0x0104 /* Section 4.4.8 Receiver Control */ ++#define PP_TxCFG 0x0106 /* Section 4.4.9 Transmit Configuration */ ++#define PP_BufCFG 0x010a /* Section 4.4.12 Buffer Configuration */ ++#define PP_LineCTL 0x0112 /* Section 4.4.16 Line Control */ ++#define PP_SelfCTL 0x0114 /* Section 4.4.18 Self Control */ ++#define PP_BusCTL 0x0116 /* Section 4.4.20 Bus Control */ ++#define PP_TestCTL 0x0118 /* Section 4.4.22 Test Control */ ++#define PP_ISQ 0x0120 /* Section 4.4.5 Interrupt Status Queue */ ++#define PP_TxEvent 0x0128 /* Section 4.4.10 Transmitter Event */ ++#define PP_BufEvent 0x012c /* Section 4.4.13 Buffer Event */ ++#define PP_RxMISS 0x0130 /* Section 4.4.14 Receiver Miss Counter */ ++#define PP_TxCOL 0x0132 /* Section 4.4.15 Transmit Collision Counter */ ++#define PP_SelfST 0x0136 /* Section 4.4.19 Self Status */ ++#define PP_BusST 0x0138 /* Section 4.4.21 Bus Status */ ++#define PP_TxCMD 0x0144 /* Section 4.4.11 Transmit Command */ ++#define PP_TxLength 0x0146 /* Section 4.5.2 Transmit Length */ ++#define PP_IA 0x0158 /* Section 4.6.2 Individual Address (IEEE Address) */ ++#define PP_RxStatus 0x0400 /* Section 4.7.1 Receive Status */ ++#define PP_RxLength 0x0402 /* Section 4.7.1 Receive Length (in bytes) */ ++#define PP_RxFrame 0x0404 /* Section 4.7.2 Receive Frame Location */ ++#define PP_TxFrame 0x0a00 /* Section 4.7.2 Transmit Frame Location */ ++ ++/* ++ * Values ++ */ ++ ++/* PP_IntNum */ ++#define INTRQ0 0x0000 ++#define INTRQ1 0x0001 ++#define INTRQ2 0x0002 ++#define INTRQ3 0x0003 ++ ++/* PP_ProductID */ ++#define EISA_REG_CODE 0x630e ++#define REVISION(x) (((x) & 0x1f00) >> 8) ++#define VERSION(x) ((x) & ~0x1f00) ++ ++#define CS8900A 0x0000 ++#define REV_B 7 ++#define REV_C 8 ++#define REV_D 9 ++ ++/* PP_RxCFG */ ++#define Skip_1 0x0040 ++#define StreamE 0x0080 ++#define RxOKiE 0x0100 ++#define RxDMAonly 0x0200 ++#define AutoRxDMAE 0x0400 ++#define BufferCRC 0x0800 ++#define CRCerroriE 0x1000 ++#define RuntiE 0x2000 ++#define ExtradataiE 0x4000 ++ ++/* PP_RxCTL */ ++#define IAHashA 0x0040 ++#define PromiscuousA 0x0080 ++#define RxOKA 0x0100 ++#define MulticastA 0x0200 ++#define IndividualA 0x0400 ++#define BroadcastA 0x0800 ++#define CRCerrorA 0x1000 ++#define RuntA 0x2000 ++#define ExtradataA 0x4000 ++ ++/* PP_TxCFG */ ++#define Loss_of_CRSiE 0x0040 ++#define SQErroriE 0x0080 ++#define TxOKiE 0x0100 ++#define Out_of_windowiE 0x0200 ++#define JabberiE 0x0400 ++#define AnycolliE 0x0800 ++#define T16colliE 0x8000 ++ ++/* PP_BufCFG */ ++#define SWint_X 0x0040 ++#define RxDMAiE 0x0080 ++#define Rdy4TxiE 0x0100 ++#define TxUnderruniE 0x0200 ++#define RxMissiE 0x0400 ++#define Rx128iE 0x0800 ++#define TxColOvfiE 0x1000 ++#define MissOvfloiE 0x2000 ++#define RxDestiE 0x8000 ++ ++/* PP_LineCTL */ ++#define SerRxON 0x0040 ++#define SerTxON 0x0080 ++#define AUIonly 0x0100 ++#define AutoAUI_10BT 0x0200 ++#define ModBackoffE 0x0800 ++#define PolarityDis 0x1000 ++#define L2_partDefDis 0x2000 ++#define LoRxSquelch 0x4000 ++ ++/* PP_SelfCTL */ ++#define RESET 0x0040 ++#define SWSuspend 0x0100 ++#define HWSleepE 0x0200 ++#define HWStandbyE 0x0400 ++#define HC0E 0x1000 ++#define HC1E 0x2000 ++#define HCB0 0x4000 ++#define HCB1 0x8000 ++ ++/* PP_BusCTL */ ++#define ResetRxDMA 0x0040 ++#define DMAextend 0x0100 ++#define UseSA 0x0200 ++#define MemoryE 0x0400 ++#define DMABurst 0x0800 ++#define IOCHRDYE 0x1000 ++#define RxDMAsize 0x2000 ++#define EnableRQ 0x8000 ++ ++/* PP_TestCTL */ ++#define DisableLT 0x0080 ++#define ENDECloop 0x0200 ++#define AUIloop 0x0400 ++#define DisableBackoff 0x0800 ++#define FDX 0x4000 ++ ++/* PP_ISQ */ ++#define RegNum(x) ((x) & 0x3f) ++#define RegContent(x) ((x) & ~0x3d) ++ ++#define RxEvent 0x0004 ++#define TxEvent 0x0008 ++#define BufEvent 0x000c ++#define RxMISS 0x0010 ++#define TxCOL 0x0012 ++ ++/* PP_RxStatus */ ++#define IAHash 0x0040 ++#define Dribblebits 0x0080 ++#define RxOK 0x0100 ++#define Hashed 0x0200 ++#define IndividualAdr 0x0400 ++#define Broadcast 0x0800 ++#define CRCerror 0x1000 ++#define Runt 0x2000 ++#define Extradata 0x4000 ++ ++#define HashTableIndex(x) ((x) >> 0xa) ++ ++/* PP_TxCMD */ ++#define After5 0 ++#define After381 1 ++#define After1021 2 ++#define AfterAll 3 ++#define TxStart(x) ((x) << 6) ++ ++#define Force 0x0100 ++#define Onecoll 0x0200 ++#define InhibitCRC 0x1000 ++#define TxPadDis 0x2000 ++ ++/* PP_BusST */ ++#define TxBidErr 0x0080 ++#define Rdy4TxNOW 0x0100 ++ ++/* PP_TxEvent */ ++#define Loss_of_CRS 0x0040 ++#define SQEerror 0x0080 ++#define TxOK 0x0100 ++#define Out_of_window 0x0200 ++#define Jabber 0x0400 ++#define T16coll 0x8000 ++ ++#define TX_collisions(x) (((x) >> 0xb) & ~0x8000) ++ ++/* PP_BufEvent */ ++#define SWint 0x0040 ++#define RxDMAFrame 0x0080 ++#define Rdy4Tx 0x0100 ++#define TxUnderrun 0x0200 ++#define RxMiss 0x0400 ++#define Rx128 0x0800 ++#define RxDest 0x8000 ++ ++/* PP_RxMISS */ ++#define MissCount(x) ((x) >> 6) ++ ++/* PP_TxCOL */ ++#define ColCount(x) ((x) >> 6) ++ ++/* PP_SelfST */ ++#define T3VActive 0x0040 ++#define INITD 0x0080 ++#define SIBUSY 0x0100 ++#define EEPROMpresent 0x0200 ++#define EEPROMOK 0x0400 ++#define ELpresent 0x0800 ++#define EEsize 0x1000 ++ ++/* PP_EEPROMCommand */ ++#define EEWriteRegister 0x0100 ++#define EEReadRegister 0x0200 ++#define EEEraseRegister 0x0300 ++#define ELSEL 0x0400 ++ ++#endif /* #ifndef CIRRUS_H */ +diff -urN linux-2.6.24.7.old/drivers/pnp/Kconfig linux-2.6.24.7/drivers/pnp/Kconfig +--- linux-2.6.24.7.old/drivers/pnp/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/pnp/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -5,7 +5,7 @@ + menuconfig PNP + bool "Plug and Play support" + depends on HAS_IOMEM +- depends on ISA || ACPI ++# depends on ISA || ACPI + ---help--- + Plug and Play (PnP) is a standard for peripherals which allows those + peripherals to be configured by software, e.g. assign IRQ's or other +diff -urN linux-2.6.24.7.old/drivers/pnp/core.c linux-2.6.24.7/drivers/pnp/core.c +--- linux-2.6.24.7.old/drivers/pnp/core.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/pnp/core.c 2009-04-12 18:13:57.000000000 +0200 +@@ -74,6 +74,8 @@ + return device_register(&protocol->dev); + } + ++EXPORT_SYMBOL(pnp_register_protocol); ++ + /** + * pnp_protocol_unregister - removes a pnp protocol from the pnp layer + * @protocol: pointer to the corresponding pnp_protocol structure +@@ -86,6 +88,8 @@ + device_unregister(&protocol->dev); + } + ++EXPORT_SYMBOL(pnp_unregister_protocol); ++ + static void pnp_free_ids(struct pnp_dev *dev) + { + struct pnp_id *id; +@@ -166,6 +170,8 @@ + return 0; + } + ++EXPORT_SYMBOL(pnp_add_device); ++ + void __pnp_remove_device(struct pnp_dev *dev) + { + spin_lock(&pnp_lock); +@@ -175,6 +181,21 @@ + device_unregister(&dev->dev); + } + ++/** ++ * pnp_remove_device - removes a pnp device from the pnp layer ++ * @dev: pointer to dev to add ++ * ++ * this function will free all mem used by dev ++ */ ++void pnp_remove_device(struct pnp_dev *dev) ++{ ++ if (!dev || dev->card) ++ return; ++ __pnp_remove_device(dev); ++} ++ ++EXPORT_SYMBOL(pnp_remove_device); ++ + static int __init pnp_init(void) + { + printk(KERN_INFO "Linux Plug and Play Support v0.97 (c) Adam Belay\n"); +diff -urN linux-2.6.24.7.old/drivers/pnp/driver.c linux-2.6.24.7/drivers/pnp/driver.c +--- linux-2.6.24.7.old/drivers/pnp/driver.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/pnp/driver.c 2009-04-12 18:13:57.000000000 +0200 +@@ -246,3 +246,4 @@ + EXPORT_SYMBOL(pnp_unregister_driver); + EXPORT_SYMBOL(pnp_device_attach); + EXPORT_SYMBOL(pnp_device_detach); ++EXPORT_SYMBOL(pnp_add_id); +diff -urN linux-2.6.24.7.old/drivers/pnp/resource.c linux-2.6.24.7/drivers/pnp/resource.c +--- linux-2.6.24.7.old/drivers/pnp/resource.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/pnp/resource.c 2009-04-12 18:13:57.000000000 +0200 +@@ -404,6 +404,10 @@ + + int pnp_check_dma(struct pnp_dev *dev, int idx) + { ++ printk("*********** %s, %s, line[%d]: Fix me, this should update in the future *********\n", ++ __FILE__, __FUNCTION__, __LINE__); ++ return 0; /* should be update in the future. Wolfgang ???*/ ++ + #ifndef CONFIG_IA64 + int tmp; + struct pnp_dev *tdev; +diff -urN linux-2.6.24.7.old/drivers/serial/8250.c linux-2.6.24.7/drivers/serial/8250.c +--- linux-2.6.24.7.old/drivers/serial/8250.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/serial/8250.c 2009-04-12 18:13:57.000000000 +0200 +@@ -12,7 +12,7 @@ + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * +- * $Id: 8250.c,v 1.90 2002/07/28 10:03:27 rmk Exp $ ++ * $Id: 8250.c,v 1.5 2008-07-14 06:40:28 lhhuang Exp $ + * + * A note about mapbase / membase + * +@@ -181,7 +181,7 @@ + [PORT_16550A] = { + .name = "16550A", + .fifo_size = 16, +- .tx_loadsz = 16, ++ .tx_loadsz = 8, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO, + }, +@@ -400,6 +400,10 @@ + break; + + case UPIO_MEM: ++#if defined(CONFIG_JZSOC) ++ if (offset == (UART_FCR << up->port.regshift)) ++ value |= 0x10; /* set FCR.UUE */ ++#endif + writeb(value, up->port.membase + offset); + break; + +@@ -1981,6 +1985,83 @@ + serial_unlink_irq_chain(up); + } + ++#if defined(CONFIG_JZSOC) && !defined(CONFIG_SOC_JZ4730) ++static unsigned short quot1[3] = {0}; /* quot[0]:baud_div, quot[1]:umr, quot[2]:uacr */ ++static unsigned short * serial8250_get_divisor(struct uart_port *port, unsigned int baud) ++{ ++ int err, sum, i, j; ++ int a[12], b[12]; ++ unsigned short div, umr, uacr; ++ unsigned short umr_best, div_best, uacr_best; ++ long long t0, t1, t2, t3; ++ ++ sum = 0; ++ umr_best = div_best = uacr_best = 0; ++ div = 1; ++ ++ if ((port->uartclk % (16 * baud)) == 0) { ++ quot1[0] = port->uartclk / (16 * baud); ++ quot1[1] = 16; ++ quot1[2] = 0; ++ return quot1; ++ } ++ ++ while (1) { ++ umr = port->uartclk / (baud * div); ++ if (umr > 32) { ++ div++; ++ continue; ++ } ++ if (umr < 4) { ++ break; ++ } ++ for (i = 0; i < 12; i++) { ++ a[i] = umr; ++ b[i] = 0; ++ sum = 0; ++ for (j = 0; j <= i; j++) { ++ sum += a[j]; ++ } ++ ++ /* the precision could be 1/2^(36) due to the value of t0 */ ++ t0 = 0x1000000000LL; ++ t1 = (i + 1) * t0; ++ t2 = (sum * div) * t0; ++ t3 = div * t0; ++ do_div(t1, baud); ++ do_div(t2, port->uartclk); ++ do_div(t3, (2 * port->uartclk)); ++ err = t1 - t2 - t3; ++ ++ if (err > 0) { ++ a[i] += 1; ++ b[i] = 1; ++ } ++ } ++ ++ uacr = 0; ++ for (i = 0; i < 12; i++) { ++ if (b[i] == 1) { ++ uacr |= 1 << i; ++ } ++ } ++ ++ /* the best value of umr should be near 16, and the value of uacr should better be smaller */ ++ if (abs(umr - 16) < abs(umr_best - 16) || (abs(umr - 16) == abs(umr_best - 16) && uacr_best > uacr)) { ++ div_best = div; ++ umr_best = umr; ++ uacr_best = uacr; ++ } ++ div++; ++ } ++ ++ quot1[0] = div_best; ++ quot1[1] = umr_best; ++ quot1[2] = uacr_best; ++ ++ return quot1; ++} ++#else + static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud) + { + unsigned int quot; +@@ -2000,6 +2081,7 @@ + + return quot; + } ++#endif + + static void + serial8250_set_termios(struct uart_port *port, struct ktermios *termios, +@@ -2009,6 +2091,9 @@ + unsigned char cval, fcr = 0; + unsigned long flags; + unsigned int baud, quot; ++#if defined(CONFIG_JZSOC) && !defined(CONFIG_SOC_JZ4730) ++ unsigned short *quot1; ++#endif + + switch (termios->c_cflag & CSIZE) { + case CS5: +@@ -2041,7 +2126,12 @@ + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); ++#if defined(CONFIG_JZSOC) && !defined(CONFIG_SOC_JZ4730) ++ quot1 = serial8250_get_divisor(port, baud); ++ quot = quot1[0]; /* not usefull, just let gcc happy */ ++#else + quot = serial8250_get_divisor(port, baud); ++#endif + + /* + * Oxford Semi 952 rev B workaround +@@ -2119,6 +2209,10 @@ + if (up->capabilities & UART_CAP_UUE) + up->ier |= UART_IER_UUE | UART_IER_RTOIE; + ++#ifdef CONFIG_JZSOC ++ up->ier |= UART_IER_RTOIE; /* Set this flag, or very slow */ ++#endif ++ + serial_out(up, UART_IER, up->ier); + + if (up->capabilities & UART_CAP_EFR) { +@@ -2153,7 +2247,15 @@ + serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ + } + ++#if defined(CONFIG_JZSOC) && !defined(CONFIG_SOC_JZ4730) ++#define UART_UMR 9 ++#define UART_UACR 10 ++ serial_dl_write(up, quot1[0]); ++ serial_outp(up, UART_UMR, quot1[1]); ++ serial_outp(up, UART_UACR, quot1[2]); ++#else + serial_dl_write(up, quot); ++#endif + + /* + * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR +@@ -2866,7 +2968,7 @@ + if (nr_uarts > UART_NR) + nr_uarts = UART_NR; + +- printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.90 $ " ++ printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.5 $ " + "%d ports, IRQ sharing %sabled\n", nr_uarts, + share_irqs ? "en" : "dis"); + +@@ -2927,7 +3029,7 @@ + EXPORT_SYMBOL(serial8250_resume_port); + + MODULE_LICENSE("GPL"); +-MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.90 $"); ++MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.5 $"); + + module_param(share_irqs, uint, 0644); + MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices" +diff -urN linux-2.6.24.7.old/drivers/usb/Kconfig linux-2.6.24.7/drivers/usb/Kconfig +--- linux-2.6.24.7.old/drivers/usb/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/usb/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -41,6 +41,7 @@ + default y if PPC_MPC52xx + # MIPS: + default y if SOC_AU1X00 ++ default y if JZSOC + # more: + default PCI + +diff -urN linux-2.6.24.7.old/drivers/usb/core/hub.c linux-2.6.24.7/drivers/usb/core/hub.c +--- linux-2.6.24.7.old/drivers/usb/core/hub.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/usb/core/hub.c 2009-04-12 18:13:57.000000000 +0200 +@@ -2484,11 +2484,35 @@ + struct device *hub_dev = hub->intfdev; + u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); + int status, i; +- ++#ifdef CONFIG_JZSOC ++ static char jzhub = 1; /* the hub first to be initialized is jzsoc on-chip hub */ ++#endif ++ + dev_dbg (hub_dev, + "port %d, status %04x, change %04x, %s\n", + port1, portstatus, portchange, portspeed (portstatus)); + ++#ifdef CONFIG_SOC_JZ4730 ++ /* ++ * On Jz4730, we assume that the first USB port was used as device. ++ * If not, please comment next lines. ++ */ ++ if ((port1 == 1) && (jzhub)) { ++ jzhub = 0; ++ return; ++ } ++#endif ++ ++#if defined(CONFIG_SOC_JZ4740) || defined(CONFIG_SOC_JZ4750) ++ /* ++ * On Jz4740 and Jz4750, the second USB port was used as device. ++ */ ++ if ((port1 == 2) && (jzhub)) { ++ jzhub = 0; ++ return; ++ } ++#endif ++ + if (hub->has_indicators) { + set_port_led(hub, port1, HUB_LED_AUTO); + hub->indicator[port1-1] = INDICATOR_AUTO; +diff -urN linux-2.6.24.7.old/drivers/usb/gadget/Kconfig linux-2.6.24.7/drivers/usb/gadget/Kconfig +--- linux-2.6.24.7.old/drivers/usb/gadget/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/usb/gadget/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -93,6 +93,47 @@ + Many controller drivers are platform-specific; these + often need board-specific hooks. + ++config USB_GADGET_JZ4740 ++ boolean "JZ4740 UDC" ++ depends on SOC_JZ4740 ++ select USB_GADGET_DUALSPEED ++ help ++ Select this to support the Ingenic JZ4740 processor ++ high speed USB device controller. ++ ++config USB_JZ4740 ++ tristate ++ depends on USB_GADGET_JZ4740 ++ default USB_GADGET ++ select USB_GADGET_SELECTED ++ ++config USB_GADGET_JZ4750 ++ boolean "JZ4750 UDC" ++ depends on SOC_JZ4750 ++ select USB_GADGET_DUALSPEED ++ help ++ Select this to support the Ingenic JZ4750 processor ++ high speed USB device controller. ++ ++config USB_JZ4750 ++ tristate ++ depends on USB_GADGET_JZ4750 ++ default USB_GADGET ++ select USB_GADGET_SELECTED ++ ++config USB_GADGET_JZ4730 ++ boolean "JZ4730 UDC" ++ depends on SOC_JZ4730 ++ help ++ Select this to support the Ingenic JZ4730 processor ++ full speed USB device controller. ++ ++config USB_JZ4730 ++ tristate ++ depends on USB_GADGET_JZ4730 ++ default USB_GADGET ++ select USB_GADGET_SELECTED ++ + config USB_GADGET_AMD5536UDC + boolean "AMD5536 UDC" + depends on PCI +@@ -509,6 +550,14 @@ + behavior of USB Mass Storage hosts. Not needed for + normal operation. + ++config UDC_USE_LB_CACHE ++ bool "enable lb cache" ++ depends on USB_FILE_STORAGE && (SOC_JZ4740 || SOC_JZ4750) ++ default y ++ help ++ say "y" to enable lb cache and UDC work in faster speed. ++ say "n" to disable lb cache and UDC work in normal speed. ++ + config USB_G_SERIAL + tristate "Serial Gadget (with CDC ACM support)" + help +diff -urN linux-2.6.24.7.old/drivers/usb/gadget/Makefile linux-2.6.24.7/drivers/usb/gadget/Makefile +--- linux-2.6.24.7.old/drivers/usb/gadget/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/usb/gadget/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -17,6 +17,9 @@ + obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o + obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o + obj-$(CONFIG_USB_M66592) += m66592-udc.o ++obj-$(CONFIG_USB_JZ4740) += jz4740_udc.o ++obj-$(CONFIG_USB_JZ4730) += jz4730_udc.o ++obj-$(CONFIG_USB_JZ4750) += jz4740_udc.o + + # + # USB gadget drivers +diff -urN linux-2.6.24.7.old/drivers/usb/gadget/file_storage.c linux-2.6.24.7/drivers/usb/gadget/file_storage.c +--- linux-2.6.24.7.old/drivers/usb/gadget/file_storage.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/usb/gadget/file_storage.c 2009-04-12 18:13:57.000000000 +0200 +@@ -274,6 +274,18 @@ + + + /*-------------------------------------------------------------------------*/ ++#if defined(CONFIG_UDC_USE_LB_CACHE) ++#define GHOST ++#endif ++ ++#ifdef GHOST ++extern unsigned long udc_read(unsigned int offset, unsigned int len, unsigned char *); ++extern unsigned long udc_write(unsigned int offset, unsigned int len, unsigned char *); ++extern int NAND_LB_Init(void); ++extern int NAND_LB_FLASHCACHE(void); ++extern int NAND_MTD_FLASHCACHE(void); ++extern int FlushDataState; ++#endif + + #ifdef DEBUG + #define LDBG(lun,fmt,args...) \ +@@ -349,8 +361,8 @@ + } mod_data = { // Default values + .transport_parm = "BBB", + .protocol_parm = "SCSI", +- .removable = 0, +- .can_stall = 1, ++ .removable = 1, ++ .can_stall = 0, + .vendor = DRIVER_VENDOR_ID, + .product = DRIVER_PRODUCT_ID, + .release = 0xffff, // Use controller chip type +@@ -810,6 +822,7 @@ + #define STRING_SERIAL 3 + #define STRING_CONFIG 4 + #define STRING_INTERFACE 5 ++#define STRING_MS_OS 0xee + + /* There is only one configuration. */ + #define CONFIG_VALUE 1 +@@ -997,6 +1010,7 @@ + {STRING_SERIAL, serial}, + {STRING_CONFIG, "Self-powered"}, + {STRING_INTERFACE, "Mass Storage"}, ++ {STRING_MS_OS, "Microsoft"}, + {} + }; + +@@ -1618,9 +1632,14 @@ + + /* Perform the read */ + file_offset_tmp = file_offset; ++#ifdef GHOST ++ nread = udc_read(file_offset_tmp, amount, bh->buf); ++#else + nread = vfs_read(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); ++#endif ++ + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nread); +@@ -1799,9 +1818,13 @@ + + /* Perform the write */ + file_offset_tmp = file_offset; ++#ifdef GHOST ++ nwritten = udc_write(file_offset_tmp, amount, bh->buf); ++#else + nwritten = vfs_write(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); ++#endif + VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nwritten); +@@ -1976,9 +1999,19 @@ + + /* Perform the read */ + file_offset_tmp = file_offset; ++#ifdef GHOST ++ nread = udc_read(file_offset_tmp, amount, bh->buf); ++#else + nread = vfs_read(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); ++#endif ++ ++#if 0 ++ nread = vfs_read(curlun->filp, ++ (char __user *) bh->buf, ++ amount, &file_offset_tmp); ++#endif + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nread); +@@ -2013,7 +2046,7 @@ + { + u8 *buf = (u8 *) bh->buf; + +- static char vendor_id[] = "Linux "; ++ static char vendor_id[] = "Ingenic "; + static char product_id[] = "File-Stor Gadget"; + + if (!fsg->curlun) { // Unsupported LUNs are okay +@@ -2857,6 +2890,15 @@ + reply = check_command(fsg, 6, DATA_DIR_NONE, + 0, 1, + "TEST UNIT READY"); ++#ifdef GHOST ++ if( FlushDataState >= 1) ++ FlushDataState++; ++ if(FlushDataState > 6) ++ { ++ NAND_LB_FLASHCACHE(); ++ FlushDataState = 0; ++ } ++#endif + break; + + /* Although optional, this command is used by MS-Windows. We +@@ -3404,6 +3446,13 @@ + + /* The main loop */ + while (fsg->state != FSG_STATE_TERMINATED) { ++#ifdef GHOST ++ if ((fsg->atomic_bitflags & SUSPENDED)) ++ { ++ NAND_LB_FLASHCACHE(); ++ NAND_MTD_FLASHCACHE(); ++ } ++#endif + if (exception_in_progress(fsg) || signal_pending(current)) { + handle_exception(fsg); + continue; +@@ -3525,6 +3574,9 @@ + curlun->num_sectors = num_sectors; + LDBG(curlun, "open backing file: %s\n", filename); + rc = 0; ++#ifdef GHOST ++ NAND_LB_Init(); ++#endif + + out: + filp_close(filp, current->files); +diff -urN linux-2.6.24.7.old/drivers/usb/gadget/gadget_chips.h linux-2.6.24.7/drivers/usb/gadget/gadget_chips.h +--- linux-2.6.24.7.old/drivers/usb/gadget/gadget_chips.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/usb/gadget/gadget_chips.h 2009-04-12 18:13:57.000000000 +0200 +@@ -11,6 +11,18 @@ + * Some are available on 2.4 kernels; several are available, but not + * yet pushed in the 2.6 mainline tree. + */ ++#ifdef CONFIG_USB_GADGET_JZ4740 ++#define gadget_is_jz4740(g) !strcmp("jz4740_udc", (g)->name) ++#else ++#define gadget_is_jz4740(g) 0 ++#endif ++ ++#ifdef CONFIG_USB_GADGET_JZ4730 ++#define gadget_is_jz4730(g) !strcmp("jz4730_udc", (g)->name) ++#else ++#define gadget_is_jz4730(g) 0 ++#endif ++ + #ifdef CONFIG_USB_GADGET_NET2280 + #define gadget_is_net2280(g) !strcmp("net2280", (g)->name) + #else +@@ -212,5 +224,9 @@ + return 0x20; + else if (gadget_is_m66592(gadget)) + return 0x21; ++ else if (gadget_is_jz4730(gadget)) ++ return 0x22; ++ else if (gadget_is_jz4740(gadget)) ++ return 0x23; + return -ENOENT; + } +diff -urN linux-2.6.24.7.old/drivers/usb/gadget/jz4730_udc.c linux-2.6.24.7/drivers/usb/gadget/jz4730_udc.c +--- linux-2.6.24.7.old/drivers/usb/gadget/jz4730_udc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/usb/gadget/jz4730_udc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1403 @@ ++/* ++ * JZ4730 USB Device Controller driver ++ * ++ * 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. ++ */ ++ ++/* ++ * This device has ep0 and six bulk/interrupt/iso endpoints. ++ * ++ * - Endpoint numbering is fixed: ep0, ep1in-int, ep2in-bulk, ep3in-bulk, ++ * ep4in-iso, ep5out-bulk, ep6out-bulk, ep7out-iso. ++ * - Gadget drivers can choose ep maxpacket (8/16/32/64). ++ * - Just PIO mode currently. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "jz4730_udc.h" ++ ++//#define DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args) ++//#define DEBUG_EP0(fmt,args...) printk(KERN_DEBUG fmt , ## args) ++ ++#ifndef DEBUG ++# define DEBUG(fmt,args...) do {} while(0) ++#endif ++#ifndef DEBUG_EP0 ++# define DEBUG_EP0(fmt,args...) do {} while(0) ++#endif ++ ++#define DRIVER_DESC "JZ4730 USB Device Controller" ++#define DRIVER_VERSION "20 Sep 2007" ++ ++static const char driver_name [] = "jz4730_udc"; ++static const char driver_desc [] = DRIVER_DESC; ++ ++static unsigned int udc_debug = 0; /* 0: normal mode, 1: test udc cable type mode */ ++ ++module_param(udc_debug, int, 0); ++MODULE_PARM_DESC(udc_debug, "test udc cable type"); ++ ++#ifdef CONFIG_JZ_UDC_HOTPLUG ++extern int jz_udc_active; /* 0: No actions; 1: Have actions */ ++#endif ++ ++/* ++ * Local declarations. ++ */ ++static void nuke(struct jz4730_ep *, int status); ++static inline void pio_irq_enable(struct jz4730_ep *ep); ++static inline void pio_irq_disable(struct jz4730_ep *ep); ++static void jz4730_udc_release (struct device *dev) {} ++/*-------------------------------------------------------------------------*/ ++ ++static int jz4730_ep_enable(struct usb_ep *_ep, ++ const struct usb_endpoint_descriptor *desc) ++{ ++ struct jz4730_udc *dev; ++ struct jz4730_ep *ep; ++ unsigned long flags; ++ u32 max; ++ ++ ep = container_of(_ep, struct jz4730_ep, ep); ++ if (!_ep || !desc || ep->desc ++ || desc->bDescriptorType != USB_DT_ENDPOINT) ++ return -EINVAL; ++ dev = ep->dev; ++ if (ep == &dev->ep[0]) ++ return -EINVAL; ++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) ++ return -ESHUTDOWN; ++ if (ep->index != (desc->bEndpointAddress & 0x0f)) ++ return -EINVAL; ++ ++ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { ++ case USB_ENDPOINT_XFER_BULK: ++ case USB_ENDPOINT_XFER_INT: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++// max = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)); ++ max = 64; ++ ep->is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0; ++ ++ spin_lock_irqsave(&ep->dev->lock, flags); ++ ++ ep->stopped = 0; ++ ep->desc = desc; ++ ep->ep.maxpacket = max; ++ ++ spin_unlock_irqrestore(&ep->dev->lock, flags); ++ ++ DEBUG("enable %s %s maxpacket %u\n", ep->ep.name, ++ ep->is_in ? "IN" : "OUT", max); ++ ++ return 0; ++} ++ ++static int jz4730_ep_disable(struct usb_ep *_ep) ++{ ++ struct jz4730_ep *ep; ++ struct jz4730_udc *dev; ++ unsigned long flags; ++ ++ ep = container_of(_ep, struct jz4730_ep, ep); ++ if (!_ep || !ep->desc) ++ return -ENODEV; ++ dev = ep->dev; ++ if (dev->ep0state == EP0_SUSPEND) ++ return -EBUSY; ++ ++ DEBUG("disable %s\n", _ep->name); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* Nuke all pending requests */ ++ nuke(ep, -ESHUTDOWN); ++ ++ /* Disable ep IRQ */ ++ pio_irq_disable(ep); ++ ++ ep->desc = 0; ++ ep->stopped = 1; ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return 0; ++} ++ ++static struct usb_request *jz4730_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) ++{ ++ struct jz4730_request *req; ++ ++ req = kzalloc(sizeof *req, gfp_flags); ++ if (!req) ++ return 0; ++ ++ INIT_LIST_HEAD(&req->queue); ++ return &req->req; ++} ++ ++static void jz4730_free_request(struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct jz4730_request *req; ++ ++ req = container_of(_req, struct jz4730_request, req); ++ WARN_ON(!list_empty(&req->queue)); ++ kfree(req); ++} ++ ++static void *jz4730_alloc_buffer(struct usb_ep *_ep, unsigned bytes, ++ dma_addr_t *dma, gfp_t gfp_flags) ++{ ++ void *retval; ++ ++ retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM)); ++ if (retval) ++ *dma = virt_to_phys(retval); ++ return retval; ++} ++ ++static void jz4730_free_buffer(struct usb_ep *_ep, void *buf, ++ dma_addr_t dma, unsigned bytes) ++{ ++ kfree(buf); ++} ++ ++/* ++ * done - retire a request; caller blocked irqs ++ */ ++static void done(struct jz4730_ep *ep, struct jz4730_request *req, int status) ++{ ++ struct jz4730_udc *dev; ++ unsigned stopped = ep->stopped; ++ ++ list_del_init(&req->queue); ++ ++ if (likely(req->req.status == -EINPROGRESS)) ++ req->req.status = status; ++ else ++ status = req->req.status; ++ ++ if (status && status != -ESHUTDOWN) ++ DEBUG("complete %s req %p stat %d len %u/%u\n", ++ ep->ep.name, &req->req, status, ++ req->req.actual, req->req.length); ++ ++ dev = ep->dev; ++ ++ /* don't modify queue heads during completion callback */ ++ ep->stopped = 1; ++ spin_unlock(&dev->lock); ++ req->req.complete(&ep->ep, &req->req); ++ spin_lock(&dev->lock); ++ ep->stopped = stopped; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static __inline__ int write_packet(struct jz4730_ep *ep, ++ struct jz4730_request *req, int max) ++{ ++ u8 *buf; ++ int length, nlong, nbyte; ++ volatile u32 *fifo = (volatile u32 *)ep->fifo; ++ ++ buf = req->req.buf + req->req.actual; ++ prefetch(buf); ++ ++ length = req->req.length - req->req.actual; ++ length = min(length, max); ++ req->req.actual += length; ++ ++ DEBUG("Write %d (max %d), fifo %p\n", length, max, fifo); ++ ++ if (!length) { ++ /* Send ZLP */ ++ writel(0, (unsigned int *)UDC_TXZLP); ++ writel(0x12345678, (unsigned int *)fifo); ++ } ++ else { ++ nlong = length >> 2; ++ nbyte = length & 0x3; ++ while (nlong--) { ++ *fifo = *((u32 *)buf); ++ buf += 4; ++ } ++ while (nbyte--) { ++ *((volatile u8 *)fifo) = *buf++; ++ } ++ } ++ ++ writel(0, (unsigned int *)UDC_TXCONFIRM); ++ ++ return length; ++} ++ ++static __inline__ int read_packet(struct jz4730_ep *ep, ++ struct jz4730_request *req, int count) ++{ ++ u8 *buf; ++ int length, nlong, nbyte; ++ volatile u32 *fifo = (volatile u32 *)ep->fifo; ++ ++ buf = req->req.buf + req->req.actual; ++ prefetchw(buf); ++ ++ length = req->req.length - req->req.actual; ++ length = min(length, count); ++ req->req.actual += length; ++ ++ DEBUG("Read %d, fifo %p\n", length, fifo); ++ ++ nlong = length >> 2; ++ nbyte = length & 0x3; ++ while (nlong--) { ++ *((u32 *)buf) = *fifo; ++ buf += 4; ++ } ++ if (nbyte) { ++ u32 data = *fifo; ++ while (nbyte--) { ++ *buf++ = data & 0x0ff; ++ data >>= 8; ++ } ++ } ++ ++ REG32(UDC_RXCONFIRM); ++ ++ return length; ++} ++ ++/** Write request to FIFO (max write == maxp size) ++ * Return: 0 = still running, 1 = completed, negative = errno ++ */ ++static int write_fifo(struct jz4730_ep *ep, struct jz4730_request *req) ++{ ++ u32 max, count; ++ int is_last; ++ ++ max = ep->ep.maxpacket; ++ ++ count = write_packet(ep, req, max); ++ ++ /* last packet often short (sometimes a zlp, especially on ep0) */ ++ if (unlikely(count != max)) { ++ is_last = 1; ++ } else { ++ if (likely(req->req.length != req->req.actual) ++ || req->req.zero) ++ is_last = 0; ++ else ++ is_last = 1; ++ } ++ ++ DEBUG("write %s (%d)(IN) %d bytes%s req %p %d/%d is_last %d\n", ++ ep->ep.name, ep->index, count, ++ (count != ep->ep.maxpacket) ? " (short)" : "", ++ req, req->req.actual, req->req.length, is_last); ++ ++ /* requests complete when all IN data is in the FIFO, ++ * or sometimes later, if a zlp was needed. ++ */ ++ if (is_last) { ++ done(ep, req, 0); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/** Read to request from FIFO (max read == bytes in fifo) ++ * Return: 0 = still running, 1 = completed, negative = errno ++ */ ++static int read_fifo(struct jz4730_ep *ep, struct jz4730_request *req, u32 count) ++{ ++ int is_short; ++ ++ is_short = (count < ep->ep.maxpacket); ++ ++ count = read_packet(ep, req, count); ++ ++ DEBUG("read %s %u bytes%s OUT req %p %u/%u is_short %d\n", ++ ep->ep.name, count, (count < ep->ep.maxpacket) ? "(short)" : "", ++ req, req->req.actual, req->req.length, is_short); ++ ++ /* completion */ ++ if (is_short || req->req.actual == req->req.length) { ++ done(ep, req, 0); ++ return 1; ++ } ++ ++ /* finished that packet. the next one may be waiting... */ ++ return 0; ++} ++ ++static inline void pio_irq_enable(struct jz4730_ep *ep) ++{ ++ switch (ep->index) { ++ case 0: ++ REG_UDC_EPIntMR &= ~0x1; ++ break; ++ case 1: ++ case 2: ++ case 3: ++ case 4: ++ REG_UDC_EPIntMR &= ~(1 << ep->index); ++ break; ++ case 5: ++ case 6: ++ case 7: ++ REG_UDC_EPIntMR &= ~(1 << (ep->index + 16)); ++ break; ++ } ++} ++ ++static inline void pio_irq_disable(struct jz4730_ep *ep) ++{ ++ switch (ep->index) { ++ case 0: ++ REG_UDC_EPIntMR |= 0x1; ++ break; ++ case 1: ++ case 2: ++ case 3: ++ case 4: ++ REG_UDC_EPIntMR |= (1 << ep->index); ++ break; ++ case 5: ++ case 6: ++ case 7: ++ REG_UDC_EPIntMR |= (1 << (ep->index + 16)); ++ break; ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int ++jz4730_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) ++{ ++ struct jz4730_request *req; ++ struct jz4730_ep *ep; ++ struct jz4730_udc *dev; ++ unsigned long flags; ++ int status; ++ ++ /* always require a cpu-view buffer so pio works */ ++ req = container_of(_req, struct jz4730_request, req); ++ if (unlikely(!_req || !_req->complete ++ || !_req->buf || !list_empty(&req->queue))) ++ return -EINVAL; ++ ep = container_of(_ep, struct jz4730_ep, ep); ++ if (unlikely(!_ep || (!ep->desc && ep->index != 0))) ++ return -EINVAL; ++ dev = ep->dev; ++ if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) ++ return -ESHUTDOWN; ++ ++ DEBUG("%s queue req %p, len %u buf %p\n", ++ _ep->name, _req, _req->length, _req->buf); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ _req->status = -EINPROGRESS; ++ _req->actual = 0; ++ ++ /* for ep0 IN without premature status, zlp is required and ++ * writing EOP starts the status stage (OUT). ++ */ ++ if (unlikely(ep->index == 0 && ep->is_in)) ++ _req->zero = 1; ++ ++ /* kickstart this i/o queue? */ ++ status = 0; ++ if (list_empty(&ep->queue) && likely(!ep->stopped)) { ++ if (unlikely(ep->index == 0)) { ++ pio_irq_enable(ep); ++ if (ep->irq_pending || ++ (REG_UDC_EPIntR & UDC_EPIntR_OUTEP0)) { ++ u32 stats, count; ++ ++ stats = REG_UDC_EP0OutSR; ++ if (stats & UDC_EPSR_OUT_RCVDATA) { ++ ep->irq_pending = 0; ++ REG_UDC_EP0OutSR &= ~UDC_EPSR_OUT_MASK; ++ if (REG_UDC_EPIntR & UDC_EPIntR_OUTEP0) ++ REG_UDC_EPIntR = UDC_EPIntR_OUTEP0; ++ ++ count = OUT_COUNT(stats); ++ if (read_fifo(ep, req, count) == 1) ++ req = 0; ++ } ++ } ++ ++ } else if (ep->is_in) { ++ /* EP1 ~ EP4 */ ++ if (ep->irq_pending || ++ (REG_UDC_EPIntR & UDC_EPIntR_INEP2)) { ++ if (REG_UDC_EP2InSR & UDC_EPSR_IN) { ++ ep->irq_pending = 0; ++ REG_UDC_EP2InSR &= ~UDC_EPSR_IN; ++ if (REG_UDC_EPIntR & UDC_EPIntR_INEP2) ++ REG_UDC_EPIntR = UDC_EPIntR_INEP2; ++ ++ if (write_fifo(ep, req) == 1) ++ req = 0; ++ } ++ } ++ pio_irq_enable(ep); ++ } else { ++ /* EP5 ~ EP7 */ ++ pio_irq_enable(ep); ++ ++ if (ep->irq_pending || ++ (REG_UDC_EPIntR & UDC_EPIntR_OUTEP5)) { ++ u32 stats, count; ++ ++ stats = REG_UDC_EP5OutSR; ++ if (stats & UDC_EPSR_OUT_RCVDATA) { ++ ep->irq_pending = 0; ++ REG_UDC_EP5OutSR &= ~UDC_EPSR_OUT_MASK; ++ if (REG_UDC_EPIntR & UDC_EPIntR_OUTEP5) ++ REG_UDC_EPIntR = UDC_EPIntR_OUTEP5; ++ ++ count = OUT_COUNT(stats); ++ if (read_fifo(ep, req, count) == 1) ++ req = 0; ++ } ++ } ++ } ++ } ++ ++ /* pio or dma irq handler advances the queue. */ ++ if (likely(req != 0)) { ++ list_add_tail(&req->queue, &ep->queue); ++ } ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return status; ++} ++ ++/* dequeue ALL requests */ ++static void nuke(struct jz4730_ep *ep, int status) ++{ ++ struct jz4730_request *req; ++ ++ if (list_empty(&ep->queue)) ++ return; ++ while (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, struct jz4730_request, queue); ++ done(ep, req, status); ++ } ++} ++ ++/* dequeue JUST ONE request */ ++static int jz4730_dequeue(struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct jz4730_request *req; ++ struct jz4730_ep *ep; ++ struct jz4730_udc *dev; ++ unsigned long flags; ++ int stopped; ++ ++ ep = container_of(_ep, struct jz4730_ep, ep); ++ if (!_ep || !_req || (!ep->desc && ep->index != 0)) ++ return -EINVAL; ++ dev = ep->dev; ++ if (!dev->driver) ++ return -ESHUTDOWN; ++ ++ DEBUG("%s %s %p\n", __FUNCTION__, _ep->name,_req); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ stopped = ep->stopped; ++ ep->stopped = 1; ++ ++ /* make sure it's actually queued on this endpoint */ ++ list_for_each_entry (req, &ep->queue, queue) { ++ if (&req->req == _req) ++ break; ++ } ++ if (&req->req != _req) { ++ spin_unlock_irqrestore (&dev->lock, flags); ++ return -EINVAL; ++ } ++ ++ /* queue head may be partially complete. */ ++ if (ep->queue.next == &req->queue) { ++ done (ep, req, -ECONNRESET); ++ req = 0; ++ } ++ ++ if (req) ++ done (ep, req, -ECONNRESET); ++ ep->stopped = stopped; ++ ++ spin_unlock_irqrestore (&ep->dev->lock, flags); ++ return req ? 0 : -EOPNOTSUPP; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void jz4730_clear_halt(struct jz4730_ep *ep) ++{ ++ if (ep->stopped) { ++ ep->stopped = 0; ++ } ++} ++ ++static int jz4730_set_halt(struct usb_ep *_ep, int value) ++{ ++ struct jz4730_ep *ep; ++ unsigned long flags; ++ int retval = 0; ++ ++ if (!_ep) ++ return -ENODEV; ++ ep = container_of (_ep, struct jz4730_ep, ep); ++ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) ++ return -ESHUTDOWN; ++ if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03) ++ == USB_ENDPOINT_XFER_ISOC) ++ return -EINVAL; ++ ++ if (ep->index == 0) { ++ if (value) { ++ ep->dev->ep0state = EP0_STALL; ++ ep->dev->ep[0].stopped = 1; ++ } else ++ return -EINVAL; ++ ++ /* don't change EPxSTATUS_EP_INVALID to READY */ ++ } else if (!ep->desc) { ++ DEBUG("%s %s inactive?\n", __FUNCTION__, ep->ep.name); ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&ep->dev->lock, flags); ++ ++ if (!list_empty(&ep->queue)) ++ retval = -EAGAIN; ++ else if (!value) ++ jz4730_clear_halt(ep); ++ else { ++ ep->stopped = 1; ++ } ++ ++ spin_unlock_irqrestore(&ep->dev->lock, flags); ++ return retval; ++} ++ ++static int jz4730_fifo_status(struct usb_ep *_ep) ++{ ++ struct jz4730_ep *ep; ++ u32 size = 0; ++ ++ if (!_ep) ++ return -ENODEV; ++ ep = container_of(_ep, struct jz4730_ep, ep); ++ ++ /* size is only reported sanely for OUT */ ++ if (ep->is_in) ++ return -EOPNOTSUPP; ++ ++ return size; ++} ++ ++static void jz4730_fifo_flush(struct usb_ep *_ep) ++{ ++ struct jz4730_ep *ep; ++ ++ if (!_ep) ++ return; ++ ep = container_of(_ep, struct jz4730_ep, ep); ++ ++ /* don't change EPxSTATUS_EP_INVALID to READY */ ++ if (!ep->desc && ep->index != 0) { ++ return; ++ } ++} ++ ++static struct usb_ep_ops jz4730_ep_ops = { ++ .enable = jz4730_ep_enable, ++ .disable = jz4730_ep_disable, ++ ++ .alloc_request = jz4730_alloc_request, ++ .free_request = jz4730_free_request, ++#if 0 ++ .alloc_buffer = jz4730_alloc_buffer, ++ .free_buffer = jz4730_free_buffer, ++#endif ++ .queue = jz4730_queue, ++ .dequeue = jz4730_dequeue, ++ ++ .set_halt = jz4730_set_halt, ++ .fifo_status = jz4730_fifo_status, ++ .fifo_flush = jz4730_fifo_flush, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int jz4730_get_frame(struct usb_gadget *_gadget) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static const struct usb_gadget_ops jz4730_ops = { ++ .get_frame = jz4730_get_frame, ++ // no remote wakeup ++ // not selfpowered ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void udc_reinit(struct jz4730_udc *dev) ++{ ++ static char *names [] = { "ep0", "ep1in-int", "ep2in-bulk", "ep3in-bulk", ++ "ep4in-iso", "ep5out-bulk", "ep6out-bulk", ++ "ep7out-iso" }; ++ int i; ++ ++ INIT_LIST_HEAD (&dev->gadget.ep_list); ++ dev->gadget.ep0 = &dev->ep[0].ep; ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ dev->ep0state = EP0_DISCONNECT; ++ ++ for (i = 0; i < MAX_EP_NUM; i++) { ++ struct jz4730_ep *ep = &dev->ep[i]; ++ ++ ep->index = i; ++ ep->ep.name = names[i]; ++ ep->fifo = ep_fifo[i]; ++ ep->ep.maxpacket = 64; ++ ++ ep->ep.ops = &jz4730_ep_ops; ++ list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); ++ ep->dev = dev; ++ INIT_LIST_HEAD(&ep->queue); ++ ++ ep->desc = 0; ++ ep->stopped = 1; ++ ep->irq_pending = 0; ++ } ++ ++ dev->ep[0].ep.maxpacket = MAX_EP0_SIZE; ++ list_del_init(&dev->ep[0].ep.ep_list); ++} ++ ++/* Reset udc registers */ ++static void udc_reset(struct jz4730_udc *dev) ++{ ++ REG_UDC_DevIntMR = 0x32; /* Enable RESET and SC interrupts */ ++ REG_UDC_EPIntMR = 0x0; /* Enable all EP interrupts */ ++ REG_UDC_DevCFGR = 0x17; ++ REG_UDC_DevCR = 0x0; ++ ++ REG_UDC_EP0InCR = (0 << 4) | (1 << 1); ++ REG_UDC_EP0InCR = (0 << 4); ++ REG_UDC_EP1InCR = (3 << 4) | (1 << 1); ++ REG_UDC_EP1InCR = (3 << 4); ++ REG_UDC_EP2InCR = (2 << 4) | (1 << 1); ++ REG_UDC_EP2InCR = (2 << 4); ++ REG_UDC_EP3InCR = (2 << 4) | (1 << 1); ++ REG_UDC_EP3InCR = (2 << 4); ++ REG_UDC_EP4InCR = (1 << 4) | (1 << 1); ++ REG_UDC_EP4InCR = (1 << 4); ++ ++ REG_UDC_EP0OutCR = (0 << 4); ++ REG_UDC_EP5OutCR = (2 << 4); ++ REG_UDC_EP6OutCR = (2 << 4); ++ REG_UDC_EP7OutCR = (1 << 4); ++ ++ REG_UDC_EP0InSR = 0; ++ REG_UDC_EP1InSR = 0; ++ REG_UDC_EP2InSR = 0; ++ REG_UDC_EP3InSR = 0; ++ REG_UDC_EP4InSR = 0; ++ REG_UDC_EP5OutSR = 0; ++ REG_UDC_EP6OutSR = 0; ++ REG_UDC_EP7OutSR = 0; ++ ++ REG_UDC_EP0InBSR = MAX_EP0_SIZE/4; ++ REG_UDC_EP1InBSR = MAX_EP1_SIZE/4; ++ REG_UDC_EP2InBSR = MAX_EP2_SIZE/4; ++ REG_UDC_EP3InBSR = MAX_EP3_SIZE/4; ++ REG_UDC_EP4InBSR = MAX_EP4_SIZE/4; ++ ++ REG_UDC_EP0InMPSR = MAX_EP0_SIZE; ++ REG_UDC_EP1InMPSR = MAX_EP1_SIZE; ++ REG_UDC_EP2InMPSR = MAX_EP2_SIZE; ++ REG_UDC_EP3InMPSR = MAX_EP3_SIZE; ++ REG_UDC_EP4InMPSR = MAX_EP4_SIZE; ++ ++ REG_UDC_EP0OutMPSR = MAX_EP0_SIZE; ++ REG_UDC_EP5OutMPSR = MAX_EP5_SIZE; ++ REG_UDC_EP6OutMPSR = MAX_EP6_SIZE; ++ REG_UDC_EP7OutMPSR = MAX_EP7_SIZE; ++ ++ REG_UDC_EP0InfR = (MAX_EP0_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (0 << 5) | (0 << 4) | (0 << 0); ++ REG_UDC_EP1InfR = (MAX_EP1_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (3 << 5) | (1 << 4) | (1 << 0); ++ REG_UDC_EP2InfR = (MAX_EP2_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (2 << 5) | (1 << 4) | (2 << 0); ++ REG_UDC_EP3InfR = (MAX_EP3_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (2 << 5) | (1 << 4) | (3 << 0); ++ REG_UDC_EP4InfR = (MAX_EP4_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (1 << 5) | (1 << 4) | (4 << 0); ++ REG_UDC_EP5InfR = (MAX_EP5_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (2 << 5) | (0 << 4) | (5 << 0); ++ REG_UDC_EP6InfR = (MAX_EP6_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (2 << 5) | (0 << 4) | (6 << 0); ++ REG_UDC_EP7InfR = (MAX_EP7_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (1 << 5) | (0 << 4) | (7 << 0); ++ ++ REG_UDC_STCMAR = 0xffff; ++} ++ ++static void ep0_start(struct jz4730_udc *dev) ++{ ++ udc_reset(dev); ++ udc_reinit(dev); ++ ++ /* expect ep0 requests when the host drops reset */ ++ dev->gadget.speed = USB_SPEED_FULL; ++ dev->ep0state = EP0_IDLE; ++} ++ ++static void udc_enable(struct jz4730_udc *dev) ++{ ++ /* Enable udc and enable all interrupts */ ++ __intc_unmask_irq(IRQ_UDC); ++ __harb_usb0_udc(); ++ ++ /* start enumeration now, or after power detect irq */ ++ ep0_start(dev); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* keeping it simple: ++ * - one bus driver, initted first; ++ * - one function driver, initted second ++ */ ++ ++static struct jz4730_udc *the_controller; ++ ++/* when a driver is successfully registered, it will receive ++ * control requests including set_configuration(), which enables ++ * non-control requests. then usb traffic follows until a ++ * disconnect is reported. then a host may connect again, or ++ * the driver might get unbound. ++ */ ++int usb_gadget_register_driver(struct usb_gadget_driver *driver) ++{ ++ struct jz4730_udc *dev = the_controller; ++ int retval; ++ ++ if (!driver ++// || driver->speed != USB_SPEED_FULL ++ || !driver->bind ++ || !driver->unbind ++ || !driver->disconnect ++ || !driver->setup) ++ { ++ printk("\n -EINVAL"); ++ return -EINVAL; ++ } ++ if (!dev) ++ return -ENODEV; ++ ++ if (dev->driver) ++ return -EBUSY; ++ ++ /* hook up the driver */ ++ dev->driver = driver; ++ retval = driver->bind(&dev->gadget); ++ if (retval) { ++ DEBUG("bind to driver %s --> error %d\n", ++ driver->driver.name, retval); ++ dev->driver = 0; ++ return retval; ++ } ++ /* then enable host detection and ep0; and we're ready ++ * for set_configuration as well as eventual disconnect. ++ */ ++ udc_enable(dev); ++ ++ DEBUG("registered gadget driver '%s'\n", driver->driver.name); ++ return 0; ++} ++EXPORT_SYMBOL(usb_gadget_register_driver); ++ ++static void ++stop_activity(struct jz4730_udc *dev, struct usb_gadget_driver *driver) ++{ ++ unsigned i; ++ ++ DEBUG("%s\n", __FUNCTION__); ++ ++ if (dev->gadget.speed == USB_SPEED_UNKNOWN) ++ driver = 0; ++ ++ /* disconnect gadget driver after quiesceing hw and the driver */ ++ udc_reset (dev); ++ for (i = 0; i < MAX_EP_NUM; i++) ++ nuke(&dev->ep [i], -ESHUTDOWN); ++ if (driver) { ++ spin_unlock(&dev->lock); ++ driver->disconnect(&dev->gadget); ++ spin_lock(&dev->lock); ++ } ++ ++ if (dev->driver) ++ udc_enable(dev); ++} ++ ++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ++{ ++ struct jz4730_udc *dev = the_controller; ++ unsigned long flags; ++ ++ /* disable UDC irq */ ++ __intc_mask_irq(IRQ_UDC); ++ __harb_usb0_uhc(); ++ ++ if (!dev) ++ return -ENODEV; ++ if (!driver || driver != dev->driver) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ dev->driver = 0; ++ stop_activity(dev, driver); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ driver->unbind(&dev->gadget); ++ ++ DEBUG("unregistered driver '%s'\n", driver->driver.name); ++ return 0; ++} ++EXPORT_SYMBOL(usb_gadget_unregister_driver); ++ ++static void jz4730_epn_out(struct jz4730_udc *dev, int ep_idx, u32 count) ++{ ++ struct jz4730_request *req; ++ struct jz4730_ep *ep = &dev->ep[ep_idx]; ++ ++ req = list_entry(ep->queue.next, struct jz4730_request, queue); ++ read_fifo(ep, req, count); ++} ++ ++static void jz4730_epn_in(struct jz4730_udc *dev, int ep_idx) ++{ ++ struct jz4730_request *req; ++ struct jz4730_ep *ep = &dev->ep[ep_idx]; ++ ++ req = list_entry(ep->queue.next, struct jz4730_request, queue); ++ write_fifo(ep, req); ++} ++ ++/****************************************************************/ ++/* End Point 0 related functions */ ++/****************************************************************/ ++ ++/* return: 0 = still running, 1 = completed, negative = errno */ ++static int write_fifo_ep0(struct jz4730_ep *ep, struct jz4730_request *req) ++{ ++ u32 max, count; ++ int is_last; ++ ++ max = ep->ep.maxpacket; ++ ++ count = write_packet(ep, req, max); ++ ++ /* last packet is usually short (or a zlp) */ ++ if (unlikely(count != max)) ++ is_last = 1; ++ else { ++ if (likely(req->req.length != req->req.actual) || req->req.zero) ++ is_last = 0; ++ else ++ is_last = 1; ++ } ++ ++ DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __FUNCTION__, ++ ep->ep.name, count, ++ is_last ? "/L" : "", req->req.length - req->req.actual, req); ++ ++ /* requests complete when all IN data is in the FIFO */ ++ if (is_last) { ++ done(ep, req, 0); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Simulate a USB_REQ_SET_CONFIGURATION to the function driver, ++ * this is required to enable the endpoints of the function driver. ++ * UDC should let software have the chance to handle this standard ++ * request, unfortunately UDC can't do that. ++ */ ++static void psudo_set_config(void) ++{ ++ struct jz4730_udc *dev = (struct jz4730_udc *) the_controller; ++ struct usb_ctrlrequest ctrl; ++ int tmp; ++ ++ /* SETUP packet */ ++ ctrl.bRequestType = 0x00; ++ ctrl.bRequest = USB_REQ_SET_CONFIGURATION; ++ ctrl.wValue = 1; ++ ctrl.wIndex = 0; ++ ctrl.wLength = 0; ++ ++ nuke(&dev->ep[0], 0); ++ dev->ep[0].stopped = 0; ++ ++ if (likely(ctrl.bRequestType & USB_DIR_IN)) { ++ dev->ep[0].is_in = 1; ++ dev->ep0state = EP0_IN; ++ } else { ++ dev->ep[0].is_in = 0; ++ dev->ep0state = EP0_OUT; ++ } ++ ++ /* delegate everything to the gadget driver. ++ * it may respond after this irq handler returns. ++ */ ++ spin_unlock (&dev->lock); ++ tmp = dev->driver->setup(&dev->gadget, &ctrl); ++ spin_lock (&dev->lock); ++ if (unlikely(tmp < 0)) { ++ DEBUG_EP0("req %02x.%02x protocol STALL; err %d\n", ++ ctrl.bRequestType, ctrl.bRequest, tmp); ++ dev->ep[0].stopped = 1; ++ dev->ep0state = EP0_STALL; ++ } ++} ++ ++/* ++ * Read 8 bytes setup packet from EP0 RX buffer ++ */ ++static void read_setup_packet(u8 *buf) ++{ ++ u32 *tmp = (u32 *)buf; ++ ++ *tmp++ = readl((unsigned int *)RXFIFO); ++ *tmp++ = readl((unsigned int *)RXFIFO); ++ ++ REG32(UDC_RXCONFIRM); ++} ++ ++static void jz4730_ep0_setup(struct jz4730_udc *dev) ++{ ++ struct jz4730_ep *ep = &dev->ep[0]; ++ struct usb_ctrlrequest ctrl; ++ int tmp; ++ ++ /* read control req from fifo (8 bytes) */ ++ read_setup_packet((unsigned char *) &ctrl); ++ ++ DEBUG_EP0("SETUP %02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bRequestType, ctrl.bRequest, ++ ctrl.wValue, ctrl.wIndex, ctrl.wLength); ++ ++ /* Set direction of EP0 */ ++ if (likely(ctrl.bRequestType & USB_DIR_IN)) { ++ ep->is_in = 1; ++ dev->ep0state = EP0_IN; ++ } else { ++ ep->is_in = 0; ++ dev->ep0state = EP0_OUT; ++ } ++ ++ /* Nuke all previous transfers */ ++ nuke(ep, 0); ++ ep->stopped = 0; ++ ++ /* delegate everything to the gadget driver. ++ * it may respond after this irq handler returns. ++ */ ++ if (likely((u32)dev->driver)) { ++ /* device-2-host (IN) or no data setup command, process immediately */ ++ spin_unlock(&dev->lock); ++ tmp = dev->driver->setup(&dev->gadget, &ctrl); ++ spin_lock(&dev->lock); ++ ++ if (unlikely(tmp < 0)) { ++ /* setup processing failed, force stall */ ++ DEBUG_EP0("req %02x.%02x protocol STALL; err %d\n", ++ ctrl.bRequestType, ctrl.bRequest, tmp); ++ dev->ep0state = EP0_STALL; ++ } ++ } ++} ++ ++static int jz4730_ep0_in(struct jz4730_udc *dev) ++{ ++ struct jz4730_request *req; ++ struct jz4730_ep *ep = &dev->ep[0]; ++ int ret; ++ ++ if (list_empty(&ep->queue)) ++ req = 0; ++ else ++ req = list_entry(ep->queue.next, struct jz4730_request, queue); ++ ++ if (!req) { ++ DEBUG_EP0("%s: NULL REQ\n", __FUNCTION__); ++ return 0; ++ } ++ ++ ret = write_fifo_ep0(ep, req); ++ ++ return ret; ++} ++ ++static void jz4730_ep0_out(struct jz4730_udc *dev) ++{ ++ u32 epsr; ++ struct jz4730_ep *ep = &dev->ep[0]; ++ ++ epsr = REG_UDC_EP0OutSR; ++ REG_UDC_EP0OutSR &= ~UDC_EPSR_OUT_MASK; ++ ++ if (epsr & UDC_EPSR_OUT_RCVSETUP) { ++ jz4730_ep0_setup(dev); ++ } ++ else if (epsr & UDC_EPSR_OUT_RCVDATA) { ++ u32 count = __udc_ep0out_packet_size(); ++ if (count == 0) { ++ readl((unsigned int *)UDC_RXCONFIRM); // ack zero packet ++ } ++ else { ++ /* EP0 OUT Data */ ++ if (list_empty(&ep->queue)) { ++ ep->irq_pending = 1; ++ pio_irq_disable(ep); ++ } ++ else ++ jz4730_epn_out(dev, 0, count); ++ ++ } ++ } ++} ++ ++static void handle_reset_irq(struct jz4730_udc *dev) ++{ ++ int i; ++ ++ /* clear any status */ ++ REG_UDC_EPIntR = 0xffffffff; ++ REG_UDC_DevIntR = 0xffffffff; ++ ++ /* reset udc */ ++ udc_reset(dev); ++ ++ /* reset driver status */ ++ for (i = 0; i < MAX_EP_NUM; i++) { ++ struct jz4730_ep *ep = &dev->ep[i]; ++ ++ ep->irq_pending = 0; ++// nuke(ep, 0); ++ nuke(ep, -ESHUTDOWN); ++ } ++} ++ ++static irqreturn_t jz4730_udc_irq(int irq, void *_dev) ++{ ++ struct jz4730_udc *dev = _dev; ++ struct jz4730_ep *ep; ++ ++ u32 intr_dev, intr_ep, stats, count; ++ ++ spin_lock(&dev->lock); ++ ++ intr_dev = REG_UDC_DevIntR; ++ intr_ep = REG_UDC_EPIntR; ++ ++ DEBUG("*** udc irq intr_dev=0x%x intr_ep=0x%x\n", intr_dev, intr_ep); ++ ++ if (!intr_dev && !intr_ep) { ++ spin_unlock(&dev->lock); ++ return IRQ_HANDLED; ++ } ++ ++ if (udc_debug) { ++#ifdef CONFIG_JZ_UDC_HOTPLUG ++ jz_udc_active = 1; ++#endif ++ REG_UDC_DevIntR = intr_dev; ++ REG_UDC_EPIntR = intr_ep; ++ __harb_usb0_uhc(); ++ __intc_mask_irq(IRQ_UDC); ++ spin_unlock(&dev->lock); ++ return IRQ_HANDLED; ++ } ++ ++ if (intr_dev) { ++ if (intr_dev & UDC_DevIntR_SC) { ++ psudo_set_config(); ++ udelay(100); ++ } ++ ++ if (intr_dev & UDC_DevIntR_UR) { ++#ifdef CONFIG_JZ_UDC_HOTPLUG ++ jz_udc_active = 1; ++#endif ++ handle_reset_irq(dev); ++ } ++ ++ REG_UDC_DevIntR = intr_dev; ++ } ++ ++ if (intr_ep & UDC_EPIntR_OUTEP0) { ++ REG_UDC_EPIntR = UDC_EPIntR_OUTEP0; ++ jz4730_ep0_out(dev); ++ } ++ ++ if (intr_ep & UDC_EPIntR_INEP0) { ++ ep = &dev->ep[0]; ++ if (list_empty(&ep->queue)) { ++ pio_irq_disable(ep); ++ } ++ else { ++ stats = REG_UDC_EP0InSR; ++ if (stats & UDC_EPSR_IN) { ++ REG_UDC_EPIntR = UDC_EPIntR_INEP0; ++ REG_UDC_EP0InSR &= ~UDC_EPSR_IN; ++ ++ jz4730_ep0_in(dev); ++ } ++ } ++ } ++ ++ if (intr_ep & UDC_EPIntR_OUTEP5) { ++ REG_UDC_EPIntR = UDC_EPIntR_OUTEP5; ++ ep = &dev->ep[5]; ++ if (list_empty(&ep->queue)) { ++ ep->irq_pending = 1; ++ pio_irq_disable(ep); ++ } ++ else { ++ stats = REG_UDC_EP5OutSR; ++ if (stats & UDC_EPSR_OUT_RCVDATA) { ++ REG_UDC_EP5OutSR &= ~UDC_EPSR_OUT_MASK; ++ ++ count = OUT_COUNT(stats); ++ jz4730_epn_out(dev, 5, count); ++ } ++ } ++ } ++ ++ if (intr_ep & UDC_EPIntR_INEP2) { ++ ep = &dev->ep[2]; ++ if (list_empty(&ep->queue)) { ++ ep->irq_pending = 1; ++ pio_irq_disable(ep); ++ } ++ else { ++ stats = REG_UDC_EP2InSR; ++ if (stats & UDC_EPSR_IN) { ++ REG_UDC_EP2InSR &= ~UDC_EPSR_IN; ++ jz4730_epn_in(dev, 2); ++ } ++ } ++ ++ REG_UDC_EPIntR = UDC_EPIntR_INEP2; ++ } ++ ++ if (intr_ep & UDC_EPIntR_INEP1) { ++ ep = &dev->ep[1]; ++ if (list_empty(&ep->queue)) { ++ ep->irq_pending = 1; ++ pio_irq_disable(ep); ++ } ++ else { ++ stats = REG_UDC_EP1InSR; ++ if (stats & UDC_EPSR_IN) { ++ REG_UDC_EP1InSR &= ~UDC_EPSR_IN; ++ jz4730_epn_in(dev, 1); ++ } ++ } ++ ++ REG_UDC_EPIntR = UDC_EPIntR_INEP1; ++ } ++ ++ spin_unlock(&dev->lock); ++ return IRQ_HANDLED; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct jz4730_udc udc_dev = { ++ .usb_address = 0, ++ ++ .gadget = { ++ .ops = &jz4730_ops, ++ .ep0 = &udc_dev.ep[0].ep, ++ .name = driver_name, ++ .dev = { ++ .bus_id = "gadget", ++ }, ++ }, ++ /* control endpoint no need to init here!*/ ++ /* control endpoint */ ++}; ++ ++ ++/* tear down the binding between this driver and the pci device */ ++static int jz4730_udc_remove(struct platform_device *pdev) ++{ ++ struct jz4730_udc *dev = platform_get_drvdata(pdev); ++ ++ if (dev->driver) ++ return -EBUSY; ++ ++ /* USB port0 as UHC */ ++ __harb_usb0_uhc(); ++ ++ /* reset udc */ ++ udc_reset(dev); ++ ++ /* clear any status */ ++ REG_UDC_EPIntR = 0xffffffff; ++ REG_UDC_DevIntR = 0xffffffff; ++ ++ /* disable all UDC interrupts */ ++ REG_UDC_DevIntMR = 0xffffffff; ++ REG_UDC_EPIntMR = 0xffffffff; ++ ++ free_irq(IRQ_UDC, dev); ++ platform_set_drvdata(pdev, 0); ++ device_unregister(&dev->gadget.dev); ++ the_controller = 0; ++ ++ return 0; ++} ++ ++static int jz4730_udc_probe(struct platform_device *pdev) ++{ ++ struct jz4730_udc *dev = &udc_dev; ++ int retval,rc; ++ ++ /* if you want to support more than one controller in a system, ++ * usb_gadget_driver_{register,unregister}() must change. ++ */ ++ if (the_controller) { ++ printk("Check the_controller: %s\n", driver_name); ++ return -EBUSY; ++ } ++ ++ spin_lock_init(&dev->lock); ++ device_initialize(&dev->gadget.dev); ++ dev->gadget.dev.parent = &pdev->dev; //if no,can only insmod once!! ++ dev->gadget.dev.release = jz4730_udc_release; ++ rc = device_register (&dev->gadget.dev); ++ if (rc < 0) ++ return rc; ++ platform_set_drvdata(pdev, dev); ++ ++ /* ++ * Note: we just mask INTC irq but allow UDC irq. ++ * This avoid that we miss any UDC irqs. ++ */ ++ ++ /* To avoid any UDC irqs here, we call cli() first */ ++// cli(); ++ ++ /* disable INTC irq */ ++ __intc_mask_irq(IRQ_UDC); ++ ++ /* init to known state, then setup irqs */ ++ udc_reset(dev); ++ udc_reinit(dev); ++ ++ /* request UDC irq */ ++ if (request_irq(IRQ_UDC, jz4730_udc_irq, IRQF_DISABLED, // SA_INTERRUPT, ++ driver_name, dev) != 0) { ++ printk(KERN_INFO "request UDC interrupt %d failed\n", IRQ_UDC); ++ retval = -EBUSY; ++ goto done; ++ } ++ ++ /* disable INTC irq again since request_irq has enabled it */ ++ __intc_mask_irq(IRQ_UDC); ++ __intc_ack_irq(IRQ_UDC); ++ ++ /* Re-enable irqs */ ++// sti(); ++ ++ printk(KERN_INFO "%s\n", driver_desc); ++ printk(KERN_INFO "version: " DRIVER_VERSION "\n"); ++ ++ /* done */ ++ the_controller = dev; ++ ++ return 0; ++ ++done: ++ if (dev) ++ jz4730_udc_remove (pdev); ++ return retval; ++} ++ ++static struct platform_driver udc_driver = { ++ .probe = jz4730_udc_probe, ++ .remove = jz4730_udc_remove, ++ .suspend = NULL, ++ .resume = NULL, ++ .driver = { ++ .name = (char *) driver_name, ++ .owner = THIS_MODULE, ++ }, ++}; ++static struct platform_device the_udc_pdev = { ++ .name = (char *) driver_name, ++ .id = -1, ++ .dev = { ++ .release = jz4730_udc_release, ++ }, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init udc_init (void) ++{ ++ platform_driver_register(&udc_driver); ++ return platform_device_register (&the_udc_pdev); ++} ++ ++static void __exit udc_exit (void) ++{ ++ platform_driver_unregister(&udc_driver); ++ platform_device_unregister(&the_udc_pdev); ++} ++ ++module_init(udc_init); ++module_exit(udc_exit); ++ ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_AUTHOR("Wei Jianli "); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/drivers/usb/gadget/jz4730_udc.h linux-2.6.24.7/drivers/usb/gadget/jz4730_udc.h +--- linux-2.6.24.7.old/drivers/usb/gadget/jz4730_udc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/usb/gadget/jz4730_udc.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,107 @@ ++/* ++ * JZ4730 USB Device Controller driver ++ * ++ * Copyright (C) 2005 by Wei Jianli ++ * ++ * 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 __JZ4730_UDC_H__ ++#define __JZ4730_UDC_H__ ++ ++/* DRIVER DATA STRUCTURES and UTILITIES */ ++#define MAX_EP_NUM 8 /* Number of endpoints on this UDC */ ++ ++#define MAX_EP0_SIZE 32 ++#define MAX_EP1_SIZE 64 ++#define MAX_EP2_SIZE 64 ++#define MAX_EP3_SIZE 64 ++#define MAX_EP4_SIZE 64 ++#define MAX_EP5_SIZE 64 ++#define MAX_EP6_SIZE 64 ++#define MAX_EP7_SIZE 64 ++ ++// UDC FIFO ++#define RXFIFO (UDC_RXFIFO) /* EP0 OUT, EP5-7 OUT */ ++#define TXFIFOEP0 (UDC_TXFIFOEP0) /* EP0 IN */ ++#define TXFIFOEP1 (TXFIFOEP0 + MAX_EP0_SIZE) /* EP1 IN */ ++#define TXFIFOEP2 (TXFIFOEP1 + MAX_EP1_SIZE) /* EP2 IN */ ++#define TXFIFOEP3 (TXFIFOEP2 + MAX_EP2_SIZE) /* EP3 IN */ ++#define TXFIFOEP4 (TXFIFOEP3 + MAX_EP3_SIZE) /* EP4 IN */ ++ ++static u32 ep_fifo[MAX_EP_NUM] = {TXFIFOEP0, TXFIFOEP1, TXFIFOEP2, ++ TXFIFOEP3, TXFIFOEP4, RXFIFO, RXFIFO, ++ RXFIFO}; ++ ++#define OUT_COUNT(stats) \ ++ ((stats&UDC_EPSR_RXPKTSIZE_MASK)>>UDC_EPSR_RXPKTSIZE_BIT) ++ ++struct jz4730_ep { ++ struct usb_ep ep; ++ struct jz4730_udc *dev; ++ ++ u8 index; ++ u8 is_in; ++ u8 stopped; ++ u8 irq_pending; ++ u32 fifo; ++ ++ struct list_head queue; ++ const struct usb_endpoint_descriptor *desc; ++}; ++ ++struct jz4730_request { ++ struct usb_request req; ++ struct list_head queue; ++}; ++ ++enum ep0state { ++ EP0_DISCONNECT, /* no host */ ++ EP0_IDLE, /* between STATUS ack and SETUP report */ ++ EP0_IN, EP0_OUT, /* data stage */ ++ EP0_STATUS, /* status stage */ ++ EP0_STALL, /* data or status stages */ ++ EP0_SUSPEND, /* usb suspend */ ++}; ++ ++struct jz4730_udc { ++ struct usb_gadget gadget; ++ struct usb_gadget_driver *driver; ++ spinlock_t lock; ++ ++ struct jz4730_ep ep[MAX_EP_NUM]; ++ enum ep0state ep0state; ++ unsigned char usb_address; ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* 2.5 stuff that's sometimes missing in 2.4 */ ++ ++#ifndef container_of ++#define container_of list_entry ++#endif ++ ++#ifndef likely ++#define likely(x) (x) ++#define unlikely(x) (x) ++#endif ++ ++#ifndef BUG_ON ++#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) ++#endif ++ ++#ifndef WARN_ON ++#define WARN_ON(x) do { } while (0) ++#endif ++ ++#ifndef IRQ_NONE ++typedef void irqreturn_t; ++#define IRQ_NONE ++#define IRQ_HANDLED ++#define IRQ_RETVAL(x) ++#endif ++ ++#endif /* __JZ4730_UDC_H__ */ +diff -urN linux-2.6.24.7.old/drivers/usb/gadget/jz4740_udc.c linux-2.6.24.7/drivers/usb/gadget/jz4740_udc.c +--- linux-2.6.24.7.old/drivers/usb/gadget/jz4740_udc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/usb/gadget/jz4740_udc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,2251 @@ ++/* ++ * linux/drivers/usb/gadget/jz4740_udc.c ++ * ++ * Ingenic JZ4740 on-chip high speed USB device controller ++ * ++ * Copyright (C) 2006 - 2008 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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 device has ep0, two bulk-in/interrupt-in endpoints, and one bulk-out endpoint. ++ * ++ * - Endpoint numbering is fixed: ep0, ep1in-int, ep2in-bulk, ep1out-bulk. ++ * - DMA works with bulk-in (channel 1) and bulk-out (channel 2) endpoints. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "jz4740_udc.h" ++ ++//#define DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args) ++//#define DEBUG(fmt,args...) printk(fmt , ## args) ++//#define DEBUG_EP0(fmt,args...) printk(fmt , ## args) ++//#define DEBUG_SETUP(fmt,args...) printk(fmt , ## args) ++ ++#ifndef DEBUG ++# define DEBUG(fmt,args...) do {} while(0) ++#endif ++#ifndef DEBUG_EP0 ++# define NO_STATES ++# define DEBUG_EP0(fmt,args...) do {} while(0) ++#endif ++#ifndef DEBUG_SETUP ++# define DEBUG_SETUP(fmt,args...) do {} while(0) ++#endif ++ ++static unsigned int udc_debug = 0; /* 0: normal mode, 1: test udc cable type mode */ ++ ++module_param(udc_debug, int, 0); ++MODULE_PARM_DESC(udc_debug, "test udc cable or power type"); ++ ++static unsigned int use_dma = 1; /* 1: use DMA, 0: use PIO */ ++ ++module_param(use_dma, int, 0); ++MODULE_PARM_DESC(use_dma, "DMA mode enable flag"); ++ ++#ifdef CONFIG_JZ_UDC_HOTPLUG ++extern int jz_udc_active; /* 0: No actions; 1: Have actions */ ++#endif ++ ++/* ++ * Local definintions. ++ */ ++ ++#define DRIVER_VERSION "13-Mar-2008" ++#define DRIVER_DESC "JZ4740 USB Device Controller" ++ ++static const char gadget_name [] = "jz4740_udc"; ++ ++struct jz4740_udc *the_controller; ++ ++static const char driver_name [] = "jz4740_udc"; ++static const char driver_desc [] = DRIVER_DESC; ++static const char ep0name[] = "ep0"; ++ ++#ifndef NO_STATES ++static char *state_names[] = { ++ "WAIT_FOR_SETUP", ++ "DATA_STATE_XMIT", ++ "DATA_STATE_NEED_ZLP", ++ "WAIT_FOR_OUT_STATUS", ++ "DATA_STATE_RECV" ++}; ++#endif ++ ++/* ++ * Local declarations. ++ */ ++static int jz4740_ep_enable(struct usb_ep *_ep, ++ const struct usb_endpoint_descriptor *desc); ++static int jz4740_ep_disable(struct usb_ep *_ep); ++static struct usb_request *jz4740_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags); ++static void jz4740_free_request(struct usb_ep *_ep, struct usb_request *_req); ++ ++static int jz4740_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags); ++static int jz4740_dequeue(struct usb_ep *_ep, struct usb_request *_req); ++static int jz4740_set_halt(struct usb_ep *_ep, int value); ++static int jz4740_fifo_status(struct usb_ep *_ep); ++static void jz4740_fifo_flush(struct usb_ep *_ep); ++ ++static void jz4740_ep0_kick(struct jz4740_udc *dev, struct jz4740_ep *ep); ++static void jz4740_handle_ep0(struct jz4740_udc *dev, u32 intr); ++ ++static void done(struct jz4740_ep *ep, struct jz4740_request *req, ++ int status); ++static void pio_irq_enable(struct jz4740_ep *ep); ++static void pio_irq_disable(struct jz4740_ep *ep); ++static void stop_activity(struct jz4740_udc *dev, ++ struct usb_gadget_driver *driver); ++static void nuke(struct jz4740_ep *ep, int status); ++static void flush(struct jz4740_ep *ep); ++static void udc_enable(struct jz4740_udc *dev); ++static void udc_set_address(struct jz4740_udc *dev, unsigned char address); ++static void jz4740_udc_release (struct device *dev) {} ++ ++extern void *dma_alloc_noncoherent(struct device *dev, size_t size, ++ dma_addr_t *dma_handle, gfp_t flag); ++extern void dma_free_noncoherent(struct device *dev, size_t size, ++ void *vaddr, dma_addr_t dma_handle); ++ ++static struct usb_ep_ops jz4740_ep_ops = { ++ .enable = jz4740_ep_enable, ++ .disable = jz4740_ep_disable, ++ ++ .alloc_request = jz4740_alloc_request, ++ .free_request = jz4740_free_request, ++ ++ .queue = jz4740_queue, ++ .dequeue = jz4740_dequeue, ++ ++ .set_halt = jz4740_set_halt, ++ .fifo_status = jz4740_fifo_status, ++ .fifo_flush = jz4740_fifo_flush, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* inline functions of register read/write/set/clear */ ++ ++static __inline__ u8 usb_readb(u32 port) ++{ ++ return *(volatile u8 *)port; ++} ++ ++static __inline__ u16 usb_readw(u32 port) ++{ ++ return *(volatile u16 *)port; ++} ++ ++static __inline__ u32 usb_readl(u32 port) ++{ ++ return *(volatile u32 *)port; ++} ++ ++static __inline__ void usb_writeb(u32 port, u8 val) ++{ ++ *(volatile u8 *)port = val; ++} ++ ++static __inline__ void usb_writew(u32 port, u16 val) ++{ ++ *(volatile u16 *)port = val; ++} ++ ++static __inline__ void usb_writel(u32 port, u32 val) ++{ ++ *(volatile u32 *)port = val; ++} ++ ++static __inline__ void usb_setb(u32 port, u8 val) ++{ ++ volatile u8 *ioport = (volatile u8 *)(port); ++ *ioport = (*ioport) | val; ++} ++ ++static __inline__ void usb_setw(u32 port, u16 val) ++{ ++ volatile u16 *ioport = (volatile u16 *)(port); ++ *ioport = (*ioport) | val; ++} ++ ++static __inline__ void usb_setl(u32 port, u32 val) ++{ ++ volatile u32 *ioport = (volatile u32 *)(port); ++ *ioport = (*ioport) | val; ++} ++ ++static __inline__ void usb_clearb(u32 port, u8 val) ++{ ++ volatile u8 *ioport = (volatile u8 *)(port); ++ *ioport = (*ioport) & ~val; ++} ++ ++static __inline__ void usb_clearw(u32 port, u16 val) ++{ ++ volatile u16 *ioport = (volatile u16 *)(port); ++ *ioport = (*ioport) & ~val; ++} ++ ++static __inline__ void usb_clearl(u32 port, u32 val) ++{ ++ volatile u32 *ioport = (volatile u32 *)(port); ++ *ioport = (*ioport) & ~val; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static __inline__ int write_packet(struct jz4740_ep *ep, ++ struct jz4740_request *req, int max) ++{ ++ u8 *buf; ++ int length, nlong, nbyte; ++ volatile u32 *fifo = (volatile u32 *)ep->fifo; ++ ++ buf = req->req.buf + req->req.actual; ++ prefetch(buf); ++ ++ length = req->req.length - req->req.actual; ++ length = min(length, max); ++ req->req.actual += length; ++ ++ DEBUG("Write %d (max %d), fifo %p\n", length, max, fifo); ++ ++ nlong = length >> 2; ++ nbyte = length & 0x3; ++ while (nlong--) { ++ *fifo = *((u32 *)buf); ++ buf += 4; ++ } ++ while (nbyte--) { ++ *((volatile u8 *)fifo) = *buf++; ++ } ++ ++ return length; ++} ++ ++static __inline__ int read_packet(struct jz4740_ep *ep, ++ struct jz4740_request *req, int count) ++{ ++ u8 *buf; ++ int length, nlong, nbyte; ++ volatile u32 *fifo = (volatile u32 *)ep->fifo; ++ ++ buf = req->req.buf + req->req.actual; ++ prefetchw(buf); ++ ++ length = req->req.length - req->req.actual; ++ length = min(length, count); ++ req->req.actual += length; ++ ++ DEBUG("Read %d, fifo %p\n", length, fifo); ++ ++ nlong = length >> 2; ++ nbyte = length & 0x3; ++ while (nlong--) { ++ *((u32 *)buf) = *fifo; ++ buf += 4; ++ } ++ while (nbyte--) { ++ *buf++ = *((volatile u8 *)fifo); ++ } ++ ++ return length; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * udc_disable - disable USB device controller ++ */ ++static void udc_disable(struct jz4740_udc *dev) ++{ ++ DEBUG("%s, %p\n", __FUNCTION__, dev); ++ ++ udc_set_address(dev, 0); ++ ++ /* Disable interrupts */ ++ usb_writew(USB_REG_INTRINE, 0); ++ usb_writew(USB_REG_INTROUTE, 0); ++ usb_writeb(USB_REG_INTRUSBE, 0); ++ ++ /* Disable DMA */ ++ usb_writel(USB_REG_CNTL1, 0); ++ usb_writel(USB_REG_CNTL2, 0); ++ ++ /* Disconnect from usb */ ++ usb_clearb(USB_REG_POWER, USB_POWER_SOFTCONN); ++ ++ /* Disable the USB PHY */ ++#ifdef CONFIG_SOC_JZ4740 ++ REG_CPM_SCR &= ~CPM_SCR_USBPHY_ENABLE; ++#elif CONFIG_SOC_JZ4750 ++ REG_CPM_OPCR &= ~CPM_OPCR_UDCPHY_ENABLE; ++#endif ++ ++ dev->ep0state = WAIT_FOR_SETUP; ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++} ++ ++/* ++ * udc_reinit - initialize software state ++ */ ++static void udc_reinit(struct jz4740_udc *dev) ++{ ++ u32 i; ++ ++ DEBUG("%s, %p\n", __FUNCTION__, dev); ++ ++ /* device/ep0 records init */ ++ INIT_LIST_HEAD(&dev->gadget.ep_list); ++ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); ++ dev->ep0state = WAIT_FOR_SETUP; ++ ++ for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { ++ struct jz4740_ep *ep = &dev->ep[i]; ++ ++ if (i != 0) ++ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); ++ ++ INIT_LIST_HEAD(&ep->queue); ++ ep->desc = 0; ++ ep->stopped = 0; ++ ep->pio_irqs = 0; ++ } ++} ++ ++/* until it's enabled, this UDC should be completely invisible ++ * to any USB host. ++ */ ++static void udc_enable(struct jz4740_udc *dev) ++{ ++ int i; ++ ++ DEBUG("%s, %p\n", __FUNCTION__, dev); ++ ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ ++ /* Flush FIFO for each */ ++ for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { ++ struct jz4740_ep *ep = &dev->ep[i]; ++ ++ usb_set_index(ep_index(ep)); ++ flush(ep); ++ } ++ ++ /* Set this bit to allow the UDC entering low-power mode when ++ * there are no actions on the USB bus. ++ * UDC still works during this bit was set. ++ */ ++ __cpm_stop_udc(); ++ ++ /* Enable the USB PHY */ ++#ifdef CONFIG_SOC_JZ4740 ++ REG_CPM_SCR |= CPM_SCR_USBPHY_ENABLE; ++#elif CONFIG_SOC_JZ4750 ++ REG_CPM_OPCR |= CPM_OPCR_UDCPHY_ENABLE; ++#endif ++ ++ /* Disable interrupts */ ++ usb_writew(USB_REG_INTRINE, 0); ++ usb_writew(USB_REG_INTROUTE, 0); ++ usb_writeb(USB_REG_INTRUSBE, 0); ++ ++ /* Enable interrupts */ ++ usb_setw(USB_REG_INTRINE, USB_INTR_EP0); ++ usb_setb(USB_REG_INTRUSBE, USB_INTR_RESET); ++ /* Don't enable rest of the interrupts */ ++ /* usb_setw(USB_REG_INTRINE, USB_INTR_INEP1 | USB_INTR_INEP2); ++ usb_setw(USB_REG_INTROUTE, USB_INTR_OUTEP1); */ ++ ++ /* Enable SUSPEND */ ++ /* usb_setb(USB_REG_POWER, USB_POWER_SUSPENDM); */ ++ ++ /* Enable HS Mode */ ++ usb_setb(USB_REG_POWER, USB_POWER_HSENAB); ++ ++ /* Let host detect UDC: ++ * Software must write a 1 to the PMR:USB_POWER_SOFTCONN bit to turn this ++ * transistor on and pull the USBDP pin HIGH. ++ */ ++ usb_setb(USB_REG_POWER, USB_POWER_SOFTCONN); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* keeping it simple: ++ * - one bus driver, initted first; ++ * - one function driver, initted second ++ */ ++ ++/* ++ * Register entry point for the peripheral controller driver. ++ */ ++ ++int usb_gadget_register_driver(struct usb_gadget_driver *driver) ++{ ++ struct jz4740_udc *dev = the_controller; ++ int retval; ++ ++ if (!driver ++ || !driver->bind ++ || !driver->unbind || !driver->disconnect || !driver->setup) ++ { ++ printk("\n-EINVAL"); ++ return -EINVAL; ++ } ++ if (!dev) ++ { ++ printk("\n-ENODEV"); ++ return -ENODEV; ++ } ++ if (dev->driver) ++ { ++ printk("\n-ENODEV"); ++ return -EBUSY; ++ } ++ ++ /* hook up the driver */ ++ dev->driver = driver; ++ retval = driver->bind(&dev->gadget); ++ if (retval) { ++ DEBUG("%s: bind to driver %s --> error %d\n", dev->gadget.name, ++ driver->driver.name, retval); ++ dev->driver = 0; ++ return retval; ++ } ++ ++ /* then enable host detection and ep0; and we're ready ++ * for set_configuration as well as eventual disconnect. ++ */ ++ udc_enable(dev); ++ DEBUG("%s: registered gadget driver '%s'\n", dev->gadget.name, ++ driver->driver.name); ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL(usb_gadget_register_driver); ++ ++ ++static void stop_activity(struct jz4740_udc *dev, ++ struct usb_gadget_driver *driver) ++{ ++ int i; ++ ++ DEBUG("%s\n", __FUNCTION__); ++ ++ /* don't disconnect drivers more than once */ ++ if (dev->gadget.speed == USB_SPEED_UNKNOWN) ++ driver = 0; ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ ++ /* prevent new request submissions, kill any outstanding requests */ ++ for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { ++ struct jz4740_ep *ep = &dev->ep[i]; ++ ++ ep->stopped = 1; ++ ++ usb_set_index(ep_index(ep)); ++ nuke(ep, -ESHUTDOWN); ++ } ++ ++ /* report disconnect; the driver is already quiesced */ ++ if (driver) { ++ spin_unlock(&dev->lock); ++ driver->disconnect(&dev->gadget); ++ spin_lock(&dev->lock); ++ } ++ ++ /* re-init driver-visible data structures */ ++ udc_reinit(dev); ++} ++ ++ ++/* ++ * Unregister entry point for the peripheral controller driver. ++ */ ++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ++{ ++ struct jz4740_udc *dev = the_controller; ++ unsigned long flags; ++ ++ if (!dev) ++ return -ENODEV; ++ if (!driver || driver != dev->driver) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ dev->driver = 0; ++ stop_activity(dev, driver); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ driver->unbind(&dev->gadget); ++ ++ udc_disable(dev); ++ ++ DEBUG("unregistered driver '%s'\n", driver->driver.name); ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL(usb_gadget_unregister_driver); ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * Starting DMA using mode 1 ++ */ ++static void kick_dma(struct jz4740_ep *ep, struct jz4740_request *req) ++{ ++ u32 count = req->req.length; ++ u32 physaddr = virt_to_phys((void *)req->req.buf); ++ ++ usb_set_index(ep_index(ep)); ++ if (ep_is_in(ep)) { /* Bulk-IN transfer using DMA channel 1 */ ++ ep->reg_addr = USB_REG_ADDR1; ++ ++ dma_cache_wback_inv((unsigned long)req->req.buf, count); ++ ++ pio_irq_enable(ep); ++ ++ usb_writeb(USB_REG_INCSRH, ++ USB_INCSRH_DMAREQENAB | USB_INCSRH_AUTOSET | USB_INCSRH_DMAREQMODE); ++ ++ usb_writel(USB_REG_ADDR1, physaddr); ++ usb_writel(USB_REG_COUNT1, count); ++ usb_writel(USB_REG_CNTL1, USB_CNTL_ENA | USB_CNTL_DIR_IN | USB_CNTL_MODE_1 | ++ USB_CNTL_INTR_EN | USB_CNTL_BURST_16 | USB_CNTL_EP(ep_index(ep))); ++ } ++ else { /* Bulk-OUT transfer using DMA channel 2 */ ++ ep->reg_addr = USB_REG_ADDR2; ++ ++ dma_cache_wback_inv((unsigned long)req->req.buf, count); ++ ++ pio_irq_enable(ep); ++ ++ usb_setb(USB_REG_OUTCSRH, ++ USB_OUTCSRH_DMAREQENAB | USB_OUTCSRH_AUTOCLR | USB_OUTCSRH_DMAREQMODE); ++ ++ usb_writel(USB_REG_ADDR2, physaddr); ++ usb_writel(USB_REG_COUNT2, count); ++ usb_writel(USB_REG_CNTL2, USB_CNTL_ENA | USB_CNTL_MODE_1 | ++ USB_CNTL_INTR_EN | USB_CNTL_BURST_16 | USB_CNTL_EP(ep_index(ep))); ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/** Write request to FIFO (max write == maxp size) ++ * Return: 0 = still running, 1 = completed, negative = errno ++ * NOTE: INDEX register must be set for EP ++ */ ++static int write_fifo(struct jz4740_ep *ep, struct jz4740_request *req) ++{ ++ u32 max, csr; ++ u32 physaddr = virt_to_phys((void *)req->req.buf); ++ ++ max = le16_to_cpu(ep->desc->wMaxPacketSize); ++ ++ if (use_dma) { ++ u32 dma_count; ++ ++ /* DMA interrupt generated due to the last packet loaded into the FIFO */ ++ ++ dma_count = usb_readl(ep->reg_addr) - physaddr; ++ req->req.actual += dma_count; ++ ++ if (dma_count % max) { ++ /* If the last packet is less than MAXP, set INPKTRDY manually */ ++ usb_setb(ep->csr, USB_INCSR_INPKTRDY); ++ } ++ ++ done(ep, req, 0); ++ if (list_empty(&ep->queue)) { ++ pio_irq_disable(ep); ++ return 1; ++ } ++ else { ++ /* advance the request queue */ ++ req = list_entry(ep->queue.next, struct jz4740_request, queue); ++ kick_dma(ep, req); ++ return 0; ++ } ++ } ++ ++ /* ++ * PIO mode handling starts here ... ++ */ ++ ++ csr = usb_readb(ep->csr); ++ ++ if (!(csr & USB_INCSR_FFNOTEMPT)) { ++ unsigned count; ++ int is_last, is_short; ++ ++ count = write_packet(ep, req, max); ++ usb_setb(ep->csr, USB_INCSR_INPKTRDY); ++ ++ /* last packet is usually short (or a zlp) */ ++ if (unlikely(count != max)) ++ is_last = is_short = 1; ++ else { ++ if (likely(req->req.length != req->req.actual) ++ || req->req.zero) ++ is_last = 0; ++ else ++ is_last = 1; ++ /* interrupt/iso maxpacket may not fill the fifo */ ++ is_short = unlikely(max < ep_maxpacket(ep)); ++ } ++ ++ DEBUG("%s: wrote %s %d bytes%s%s %d left %p\n", __FUNCTION__, ++ ep->ep.name, count, ++ is_last ? "/L" : "", is_short ? "/S" : "", ++ req->req.length - req->req.actual, req); ++ ++ /* requests complete when all IN data is in the FIFO */ ++ if (is_last) { ++ done(ep, req, 0); ++ if (list_empty(&ep->queue)) { ++ pio_irq_disable(ep); ++ } ++ return 1; ++ } ++ } else { ++ DEBUG("Hmm.. %d ep FIFO is not empty!\n", ep_index(ep)); ++ } ++ ++ return 0; ++} ++ ++/** Read to request from FIFO (max read == bytes in fifo) ++ * Return: 0 = still running, 1 = completed, negative = errno ++ * NOTE: INDEX register must be set for EP ++ */ ++static int read_fifo(struct jz4740_ep *ep, struct jz4740_request *req) ++{ ++ u32 csr; ++ unsigned count, is_short; ++ u32 physaddr = virt_to_phys((void *)req->req.buf); ++ ++ if (use_dma) { ++ u32 dma_count; ++ ++ /* DMA interrupt generated due to a packet less than MAXP loaded into the FIFO */ ++ ++ dma_count = usb_readl(ep->reg_addr) - physaddr; ++ req->req.actual += dma_count; ++ ++ /* Disable interrupt and DMA */ ++ pio_irq_disable(ep); ++ usb_writel(USB_REG_CNTL2, 0); ++ ++ /* Read all bytes from this packet */ ++ count = usb_readw(USB_REG_OUTCOUNT); ++ count = read_packet(ep, req, count); ++ ++ if (count) { ++ /* If the last packet is greater than zero, clear OUTPKTRDY manually */ ++ usb_clearb(ep->csr, USB_OUTCSR_OUTPKTRDY); ++ } ++ ++ done(ep, req, 0); ++ ++ if (!list_empty(&ep->queue)) { ++ /* advance the request queue */ ++ req = list_entry(ep->queue.next, struct jz4740_request, queue); ++ kick_dma(ep, req); ++ } ++ ++ return 1; ++ } ++ ++ /* ++ * PIO mode handling starts here ... ++ */ ++ ++ /* make sure there's a packet in the FIFO. */ ++ csr = usb_readb(ep->csr); ++ if (!(csr & USB_OUTCSR_OUTPKTRDY)) { ++ DEBUG("%s: Packet NOT ready!\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ /* read all bytes from this packet */ ++ count = usb_readw(USB_REG_OUTCOUNT); ++ ++ is_short = (count < ep->ep.maxpacket); ++ ++ count = read_packet(ep, req, count); ++ ++ DEBUG("read %s %02x, %d bytes%s req %p %d/%d\n", ++ ep->ep.name, csr, count, ++ is_short ? "/S" : "", req, req->req.actual, req->req.length); ++ ++ /* Clear OutPktRdy */ ++ usb_clearb(ep->csr, USB_OUTCSR_OUTPKTRDY); ++ ++ /* completion */ ++ if (is_short || req->req.actual == req->req.length) { ++ done(ep, req, 0); ++ ++ if (list_empty(&ep->queue)) ++ pio_irq_disable(ep); ++ return 1; ++ } ++ ++ /* finished that packet. the next one may be waiting... */ ++ return 0; ++} ++ ++/* ++ * done - retire a request; caller blocked irqs ++ * INDEX register is preserved to keep same ++ */ ++static void done(struct jz4740_ep *ep, struct jz4740_request *req, int status) ++{ ++ unsigned int stopped = ep->stopped; ++ u32 index; ++ ++ DEBUG("%s, %p\n", __FUNCTION__, ep); ++ list_del_init(&req->queue); ++ ++ if (likely(req->req.status == -EINPROGRESS)) ++ req->req.status = status; ++ else ++ status = req->req.status; ++ ++ if (status && status != -ESHUTDOWN) ++ DEBUG("complete %s req %p stat %d len %u/%u\n", ++ ep->ep.name, &req->req, status, ++ req->req.actual, req->req.length); ++ ++ /* don't modify queue heads during completion callback */ ++ ep->stopped = 1; ++ /* Read current index (completion may modify it) */ ++ index = usb_readb(USB_REG_INDEX); ++ ++ spin_unlock(&ep->dev->lock); ++ req->req.complete(&ep->ep, &req->req); ++ spin_lock(&ep->dev->lock); ++ ++ /* Restore index */ ++ usb_set_index(index); ++ ep->stopped = stopped; ++} ++ ++/** Enable EP interrupt */ ++static void pio_irq_enable(struct jz4740_ep *ep) ++{ ++ DEBUG("%s: EP%d %s\n", __FUNCTION__, ep_index(ep), ep_is_in(ep) ? "IN": "OUT"); ++ ++ if (ep_is_in(ep)) { ++ switch (ep_index(ep)) { ++ case 1: ++ usb_setw(USB_REG_INTRINE, USB_INTR_INEP1); ++ break; ++ case 2: ++ usb_setw(USB_REG_INTRINE, USB_INTR_INEP2); ++ break; ++ default: ++ DEBUG("Unknown endpoint: %d\n", ep_index(ep)); ++ break; ++ } ++ } ++ else { ++ switch (ep_index(ep)) { ++ case 1: ++ usb_setw(USB_REG_INTROUTE, USB_INTR_OUTEP1); ++ break; ++ default: ++ DEBUG("Unknown endpoint: %d\n", ep_index(ep)); ++ break; ++ } ++ } ++} ++ ++/** Disable EP interrupt */ ++static void pio_irq_disable(struct jz4740_ep *ep) ++{ ++ DEBUG("%s: EP%d %s\n", __FUNCTION__, ep_index(ep), ep_is_in(ep) ? "IN": "OUT"); ++ ++ if (ep_is_in(ep)) { ++ switch (ep_index(ep)) { ++ case 1: ++ usb_clearw(USB_REG_INTRINE, USB_INTR_INEP1); ++ break; ++ case 2: ++ usb_clearw(USB_REG_INTRINE, USB_INTR_INEP2); ++ break; ++ default: ++ DEBUG("Unknown endpoint: %d\n", ep_index(ep)); ++ break; ++ } ++ } ++ else { ++ switch (ep_index(ep)) { ++ case 1: ++ usb_clearw(USB_REG_INTROUTE, USB_INTR_OUTEP1); ++ break; ++ default: ++ DEBUG("Unknown endpoint: %d\n", ep_index(ep)); ++ break; ++ } ++ } ++} ++ ++/* ++ * nuke - dequeue ALL requests ++ */ ++static void nuke(struct jz4740_ep *ep, int status) ++{ ++ struct jz4740_request *req; ++ ++ DEBUG("%s, %p\n", __FUNCTION__, ep); ++ ++ /* Flush FIFO */ ++ flush(ep); ++ ++ /* called with irqs blocked */ ++ while (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, struct jz4740_request, queue); ++ done(ep, req, status); ++ } ++ ++ /* Disable IRQ if EP is enabled (has descriptor) */ ++ if (ep->desc) ++ pio_irq_disable(ep); ++} ++ ++/** Flush EP FIFO ++ * NOTE: INDEX register must be set before this call ++ */ ++static void flush(struct jz4740_ep *ep) ++{ ++ DEBUG("%s, %p\n", __FUNCTION__, ep); ++ ++ switch (ep->ep_type) { ++ case ep_control: ++ break; ++ ++ case ep_bulk_in: ++ case ep_interrupt: ++ usb_setb(ep->csr, USB_INCSR_FF); ++ break; ++ ++ case ep_bulk_out: ++ usb_setb(ep->csr, USB_OUTCSR_FF); ++ break; ++ } ++} ++ ++/** ++ * jz4740_in_epn - handle IN interrupt ++ */ ++static void jz4740_in_epn(struct jz4740_udc *dev, u32 ep_idx, u32 intr) ++{ ++ u32 csr; ++ struct jz4740_ep *ep = &dev->ep[ep_idx + 1]; ++ struct jz4740_request *req; ++ ++ usb_set_index(ep_index(ep)); ++ ++ csr = usb_readb(ep->csr); ++ DEBUG("%s: %d, csr %x\n", __FUNCTION__, ep_idx, csr); ++ ++ if (csr & USB_INCSR_SENTSTALL) { ++ DEBUG("USB_INCSR_SENTSTALL\n"); ++ usb_clearb(ep->csr, USB_INCSR_SENTSTALL); ++ return; ++ } ++ ++ if (!ep->desc) { ++ DEBUG("%s: NO EP DESC\n", __FUNCTION__); ++ return; ++ } ++ ++ if (list_empty(&ep->queue)) ++ req = 0; ++ else ++ req = list_entry(ep->queue.next, struct jz4740_request, queue); ++ ++ DEBUG("req: %p\n", req); ++ ++ if (!req) ++ return; ++ ++ write_fifo(ep, req); ++} ++ ++/* ++ * Bulk OUT (recv) ++ */ ++static void jz4740_out_epn(struct jz4740_udc *dev, u32 ep_idx, u32 intr) ++{ ++ struct jz4740_ep *ep = &dev->ep[ep_idx]; ++ struct jz4740_request *req; ++ ++ DEBUG("%s: %d\n", __FUNCTION__, ep_idx); ++ ++ usb_set_index(ep_index(ep)); ++ if (ep->desc) { ++ u32 csr; ++ ++ if (use_dma) { ++ /* DMA starts here ... */ ++ if (list_empty(&ep->queue)) ++ req = 0; ++ else ++ req = list_entry(ep->queue.next, struct jz4740_request, queue); ++ ++ if (req) ++ read_fifo(ep, req); ++ return; ++ } ++ ++ /* ++ * PIO mode starts here ... ++ */ ++ ++ while ((csr = usb_readb(ep->csr)) & ++ (USB_OUTCSR_OUTPKTRDY | USB_OUTCSR_SENTSTALL)) { ++ DEBUG("%s: %x\n", __FUNCTION__, csr); ++ ++ if (csr & USB_OUTCSR_SENTSTALL) { ++ DEBUG("%s: stall sent, flush fifo\n", ++ __FUNCTION__); ++ /* usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); */ ++ flush(ep); ++ } else if (csr & USB_OUTCSR_OUTPKTRDY) { ++ if (list_empty(&ep->queue)) ++ req = 0; ++ else ++ req = ++ list_entry(ep->queue.next, ++ struct jz4740_request, ++ queue); ++ ++ if (!req) { ++ DEBUG("%s: NULL REQ %d\n", ++ __FUNCTION__, ep_idx); ++ break; ++ } else { ++ read_fifo(ep, req); ++ } ++ } ++ } ++ } else { ++ /* Throw packet away.. */ ++ printk("%s: ep %p ep_indx %d No descriptor?!?\n", __FUNCTION__, ep, ep_idx); ++ flush(ep); ++ } ++} ++ ++static int jz4740_ep_enable(struct usb_ep *_ep, ++ const struct usb_endpoint_descriptor *desc) ++{ ++ struct jz4740_ep *ep; ++ struct jz4740_udc *dev; ++ unsigned long flags; ++ u32 max, csrh = 0; ++ ++ ep = container_of(_ep, struct jz4740_ep, ep); ++ if (!_ep || !desc || ep->desc || _ep->name == ep0name ++ || desc->bDescriptorType != USB_DT_ENDPOINT ++ || ep->bEndpointAddress != desc->bEndpointAddress) { ++ DEBUG("%s, bad ep or descriptor\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ /* xfer types must match, except that interrupt ~= bulk */ ++ if (ep->bmAttributes != desc->bmAttributes ++ && ep->bmAttributes != USB_ENDPOINT_XFER_BULK ++ && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { ++ DEBUG("%s, %s type mismatch\n", __FUNCTION__, _ep->name); ++ return -EINVAL; ++ } ++ ++ dev = ep->dev; ++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { ++ DEBUG("%s, bogus device state\n", __FUNCTION__); ++ return -ESHUTDOWN; ++ } ++ ++ max = le16_to_cpu(desc->wMaxPacketSize); ++ ++ /* Configure the endpoint */ ++ usb_set_index(desc->bEndpointAddress & 0x0F); ++ if (ep_is_in(ep)) { ++ usb_writew(USB_REG_INMAXP, max); ++ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { ++ case USB_ENDPOINT_XFER_BULK: ++ case USB_ENDPOINT_XFER_INT: ++ csrh &= ~USB_INCSRH_ISO; ++ break; ++ case USB_ENDPOINT_XFER_ISOC: ++ csrh |= USB_INCSRH_ISO; ++ break; ++ } ++ usb_writeb(USB_REG_INCSRH, csrh); ++ } ++ else { ++ usb_writew(USB_REG_OUTMAXP, max); ++ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { ++ case USB_ENDPOINT_XFER_BULK: ++ csrh &= ~USB_OUTCSRH_ISO; ++ break; ++ case USB_ENDPOINT_XFER_INT: ++ csrh &= ~USB_OUTCSRH_ISO; ++ csrh |= USB_OUTCSRH_DNYT; ++ break; ++ case USB_ENDPOINT_XFER_ISOC: ++ csrh |= USB_OUTCSRH_ISO; ++ break; ++ } ++ usb_writeb(USB_REG_OUTCSRH, csrh); ++ } ++ ++ spin_lock_irqsave(&ep->dev->lock, flags); ++ ++ ep->stopped = 0; ++ ep->desc = desc; ++ ep->pio_irqs = 0; ++ ep->ep.maxpacket = max; ++ ++ spin_unlock_irqrestore(&ep->dev->lock, flags); ++ ++ /* Reset halt state (does flush) */ ++ jz4740_set_halt(_ep, 0); ++ ++ DEBUG("%s: enabled %s\n", __FUNCTION__, _ep->name); ++ ++ return 0; ++} ++ ++/** Disable EP ++ * NOTE: Sets INDEX register ++ */ ++static int jz4740_ep_disable(struct usb_ep *_ep) ++{ ++ struct jz4740_ep *ep; ++ unsigned long flags; ++ ++ DEBUG("%s, %p\n", __FUNCTION__, _ep); ++ ++ ep = container_of(_ep, struct jz4740_ep, ep); ++ if (!_ep || !ep->desc) { ++ DEBUG("%s, %s not enabled\n", __FUNCTION__, ++ _ep ? ep->ep.name : NULL); ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&ep->dev->lock, flags); ++ ++ usb_set_index(ep_index(ep)); ++ ++ /* Nuke all pending requests (does flush) */ ++ nuke(ep, -ESHUTDOWN); ++ ++ /* Disable ep IRQ */ ++ pio_irq_disable(ep); ++ ++ ep->desc = 0; ++ ep->stopped = 1; ++ ++ spin_unlock_irqrestore(&ep->dev->lock, flags); ++ ++ DEBUG("%s: disabled %s\n", __FUNCTION__, _ep->name); ++ return 0; ++} ++ ++static struct usb_request *jz4740_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) ++{ ++ struct jz4740_request *req; ++ ++ DEBUG("%s, %p\n", __FUNCTION__, ep); ++ ++ req = kzalloc(sizeof(*req), gfp_flags); ++ if (!req) ++ return 0; ++ ++ INIT_LIST_HEAD(&req->queue); ++ ++ return &req->req; ++} ++ ++static void jz4740_free_request(struct usb_ep *ep, struct usb_request *_req) ++{ ++ struct jz4740_request *req; ++ ++ DEBUG("%s, %p\n", __FUNCTION__, ep); ++ ++ req = container_of(_req, struct jz4740_request, req); ++ WARN_ON(!list_empty(&req->queue)); ++ kfree(req); ++} ++ ++/*--------------------------------------------------------------------*/ ++ ++/** Queue one request ++ * Kickstart transfer if needed ++ * NOTE: Sets INDEX register ++ */ ++static int jz4740_queue(struct usb_ep *_ep, struct usb_request *_req, ++ gfp_t gfp_flags) ++{ ++ struct jz4740_request *req; ++ struct jz4740_ep *ep; ++ struct jz4740_udc *dev; ++ unsigned long flags; ++ ++ DEBUG("%s, %p\n", __FUNCTION__, _ep); ++ ++ req = container_of(_req, struct jz4740_request, req); ++ if (unlikely ++ (!_req || !_req->complete || !_req->buf ++ || !list_empty(&req->queue))) { ++ DEBUG("%s, bad params\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ ep = container_of(_ep, struct jz4740_ep, ep); ++ if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { ++ DEBUG("%s, bad ep\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ dev = ep->dev; ++ if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { ++ DEBUG("%s, bogus device state %p\n", __FUNCTION__, dev->driver); ++ return -ESHUTDOWN; ++ } ++ ++ DEBUG("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, ++ _req->buf); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ _req->status = -EINPROGRESS; ++ _req->actual = 0; ++ ++ /* kickstart this i/o queue? */ ++ DEBUG("Add to %d Q %d %d\n", ep_index(ep), list_empty(&ep->queue), ++ ep->stopped); ++ if (list_empty(&ep->queue) && likely(!ep->stopped)) { ++ u32 csr; ++ ++ if (unlikely(ep_index(ep) == 0)) { ++ /* EP0 */ ++ list_add_tail(&req->queue, &ep->queue); ++ jz4740_ep0_kick(dev, ep); ++ req = 0; ++ } else if (use_dma) { ++ /* DMA */ ++ kick_dma(ep, req); ++ } ++ /* PIO */ ++ else if (ep_is_in(ep)) { ++ /* EP1 & EP2 */ ++ usb_set_index(ep_index(ep)); ++ csr = usb_readb(ep->csr); ++ pio_irq_enable(ep); ++ if (!(csr & USB_INCSR_FFNOTEMPT)) { ++ if (write_fifo(ep, req) == 1) ++ req = 0; ++ } ++ } else { ++ /* EP1 */ ++ usb_set_index(ep_index(ep)); ++ csr = usb_readb(ep->csr); ++ pio_irq_enable(ep); ++ if (csr & USB_OUTCSR_OUTPKTRDY) { ++ if (read_fifo(ep, req) == 1) ++ req = 0; ++ } ++ } ++ } ++ ++ /* pio or dma irq handler advances the queue. */ ++ if (likely(req != 0)) ++ list_add_tail(&req->queue, &ep->queue); ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return 0; ++} ++ ++/* dequeue JUST ONE request */ ++static int jz4740_dequeue(struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct jz4740_ep *ep; ++ struct jz4740_request *req; ++ unsigned long flags; ++ ++ DEBUG("%s, %p\n", __FUNCTION__, _ep); ++ ++ ep = container_of(_ep, struct jz4740_ep, ep); ++ if (!_ep || ep->ep.name == ep0name) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&ep->dev->lock, flags); ++ ++ /* make sure it's actually queued on this endpoint */ ++ list_for_each_entry(req, &ep->queue, queue) { ++ if (&req->req == _req) ++ break; ++ } ++ if (&req->req != _req) { ++ spin_unlock_irqrestore(&ep->dev->lock, flags); ++ return -EINVAL; ++ } ++ ++ done(ep, req, -ECONNRESET); ++ ++ spin_unlock_irqrestore(&ep->dev->lock, flags); ++ return 0; ++} ++ ++/** Halt specific EP ++ * Return 0 if success ++ * NOTE: Sets INDEX register to EP ! ++ */ ++static int jz4740_set_halt(struct usb_ep *_ep, int value) ++{ ++ struct jz4740_ep *ep; ++ unsigned long flags; ++ ++ ep = container_of(_ep, struct jz4740_ep, ep); ++ if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { ++ DEBUG("%s, bad ep\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ usb_set_index(ep_index(ep)); ++ ++ DEBUG("%s, ep %d, val %d\n", __FUNCTION__, ep_index(ep), value); ++ ++ spin_lock_irqsave(&ep->dev->lock, flags); ++ ++ if (ep_index(ep) == 0) { ++ /* EP0 */ ++ usb_setb(USB_REG_CSR0, USB_CSR0_SENDSTALL); ++ } else if (ep_is_in(ep)) { ++ u32 csr = usb_readb(ep->csr); ++ if (value && ((csr & USB_INCSR_FFNOTEMPT) ++ || !list_empty(&ep->queue))) { ++ /* ++ * Attempts to halt IN endpoints will fail (returning -EAGAIN) ++ * if any transfer requests are still queued, or if the controller ++ * FIFO still holds bytes that the host hasnÂ’t collected. ++ */ ++ spin_unlock_irqrestore(&ep->dev->lock, flags); ++ DEBUG ++ ("Attempt to halt IN endpoint failed (returning -EAGAIN) %d %d\n", ++ (csr & USB_INCSR_FFNOTEMPT), ++ !list_empty(&ep->queue)); ++ return -EAGAIN; ++ } ++ flush(ep); ++ if (value) { ++ usb_setb(ep->csr, USB_INCSR_SENDSTALL); ++ } ++ else { ++ usb_clearb(ep->csr, USB_INCSR_SENDSTALL); ++ usb_setb(ep->csr, USB_INCSR_CDT); ++ } ++ } else { ++ ++ flush(ep); ++ if (value) { ++ usb_setb(ep->csr, USB_OUTCSR_SENDSTALL); ++ } ++ else { ++ usb_clearb(ep->csr, USB_OUTCSR_SENDSTALL); ++ usb_setb(ep->csr, USB_OUTCSR_CDT); ++ } ++ } ++ ++ if (value) { ++ ep->stopped = 1; ++ } else { ++ ep->stopped = 0; ++ } ++ ++ spin_unlock_irqrestore(&ep->dev->lock, flags); ++ ++ DEBUG("%s %s halted\n", _ep->name, value == 0 ? "NOT" : "IS"); ++ ++ return 0; ++} ++ ++/** Return bytes in EP FIFO ++ * NOTE: Sets INDEX register to EP ++ */ ++static int jz4740_fifo_status(struct usb_ep *_ep) ++{ ++ u32 csr; ++ int count = 0; ++ struct jz4740_ep *ep; ++ ++ ep = container_of(_ep, struct jz4740_ep, ep); ++ if (!_ep) { ++ DEBUG("%s, bad ep\n", __FUNCTION__); ++ return -ENODEV; ++ } ++ ++ DEBUG("%s, %d\n", __FUNCTION__, ep_index(ep)); ++ ++ /* LPD can't report unclaimed bytes from IN fifos */ ++ if (ep_is_in(ep)) ++ return -EOPNOTSUPP; ++ ++ usb_set_index(ep_index(ep)); ++ ++ csr = usb_readb(ep->csr); ++ if (ep->dev->gadget.speed != USB_SPEED_UNKNOWN || ++ csr & 0x1) { ++ count = usb_readw(USB_REG_OUTCOUNT); ++ } ++ ++ return count; ++} ++ ++/** Flush EP FIFO ++ * NOTE: Sets INDEX register to EP ++ */ ++static void jz4740_fifo_flush(struct usb_ep *_ep) ++{ ++ struct jz4740_ep *ep; ++ ++ ep = container_of(_ep, struct jz4740_ep, ep); ++ if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { ++ DEBUG("%s, bad ep\n", __FUNCTION__); ++ return; ++ } ++ ++ usb_set_index(ep_index(ep)); ++ flush(ep); ++} ++ ++/****************************************************************/ ++/* End Point 0 related functions */ ++/****************************************************************/ ++ ++/* return: 0 = still running, 1 = completed, negative = errno */ ++static int write_fifo_ep0(struct jz4740_ep *ep, struct jz4740_request *req) ++{ ++ u32 max; ++ unsigned count; ++ int is_last; ++ ++ max = ep_maxpacket(ep); ++ ++ count = write_packet(ep, req, max); ++ ++ /* last packet is usually short (or a zlp) */ ++ if (unlikely(count != max)) ++ is_last = 1; ++ else { ++ if (likely(req->req.length != req->req.actual) || req->req.zero) ++ is_last = 0; ++ else ++ is_last = 1; ++ } ++ ++ DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __FUNCTION__, ++ ep->ep.name, count, ++ is_last ? "/L" : "", req->req.length - req->req.actual, req); ++ ++ /* requests complete when all IN data is in the FIFO */ ++ if (is_last) { ++ done(ep, req, 0); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static __inline__ int jz4740_fifo_read(struct jz4740_ep *ep, ++ unsigned char *cp, int max) ++{ ++ int bytes; ++ int count = usb_readw(USB_REG_OUTCOUNT); ++ volatile u8 *fifo = (volatile u8 *)ep->fifo; ++ ++ if (count > max) ++ count = max; ++ bytes = count; ++ while (count--) ++ *cp++ = *fifo; ++ return bytes; ++} ++ ++static __inline__ void jz4740_fifo_write(struct jz4740_ep *ep, ++ unsigned char *cp, int count) ++{ ++ volatile u8 *fifo = (volatile u8 *)ep->fifo; ++ DEBUG_EP0("fifo_write: %d %d\n", ep_index(ep), count); ++ while (count--) ++ *fifo = *cp++; ++} ++ ++static int read_fifo_ep0(struct jz4740_ep *ep, struct jz4740_request *req) ++{ ++ u32 csr; ++ u8 *buf; ++ unsigned bufferspace, count, is_short; ++ volatile u8 *fifo = (volatile u8 *)ep->fifo; ++ ++ DEBUG_EP0("%s\n", __FUNCTION__); ++ ++ csr = usb_readb(USB_REG_CSR0); ++ if (!(csr & USB_CSR0_OUTPKTRDY)) ++ return 0; ++ ++ buf = req->req.buf + req->req.actual; ++ prefetchw(buf); ++ bufferspace = req->req.length - req->req.actual; ++ ++ /* read all bytes from this packet */ ++ if (likely(csr & USB_CSR0_OUTPKTRDY)) { ++ count = usb_readw(USB_REG_OUTCOUNT); ++ req->req.actual += min(count, bufferspace); ++ } else /* zlp */ ++ count = 0; ++ ++ is_short = (count < ep->ep.maxpacket); ++ DEBUG_EP0("read %s %02x, %d bytes%s req %p %d/%d\n", ++ ep->ep.name, csr, count, ++ is_short ? "/S" : "", req, req->req.actual, req->req.length); ++ ++ while (likely(count-- != 0)) { ++ u8 byte = (u8) (*fifo & 0xff); ++ ++ if (unlikely(bufferspace == 0)) { ++ /* this happens when the driver's buffer ++ * is smaller than what the host sent. ++ * discard the extra data. ++ */ ++ if (req->req.status != -EOVERFLOW) ++ DEBUG_EP0("%s overflow %d\n", ep->ep.name, ++ count); ++ req->req.status = -EOVERFLOW; ++ } else { ++ *buf++ = byte; ++ bufferspace--; ++ } ++ } ++ ++ /* completion */ ++ if (is_short || req->req.actual == req->req.length) { ++ done(ep, req, 0); ++ return 1; ++ } ++ ++ /* finished that packet. the next one may be waiting... */ ++ return 0; ++} ++ ++/** ++ * udc_set_address - set the USB address for this device ++ * @address: ++ * ++ * Called from control endpoint function after it decodes a set address setup packet. ++ */ ++static void udc_set_address(struct jz4740_udc *dev, unsigned char address) ++{ ++ DEBUG_EP0("%s: %d\n", __FUNCTION__, address); ++ ++ dev->usb_address = address; ++ usb_writeb(USB_REG_FADDR, address); ++} ++ ++/* ++ * DATA_STATE_RECV (USB_CSR0_OUTPKTRDY) ++ * - if error ++ * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND | USB_CSR0_SENDSTALL bits ++ * - else ++ * set USB_CSR0_SVDOUTPKTRDY bit ++ if last set USB_CSR0_DATAEND bit ++ */ ++static void jz4740_ep0_out(struct jz4740_udc *dev, u32 csr) ++{ ++ struct jz4740_request *req; ++ struct jz4740_ep *ep = &dev->ep[0]; ++ int ret; ++ ++ DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); ++ ++ if (list_empty(&ep->queue)) ++ req = 0; ++ else ++ req = list_entry(ep->queue.next, struct jz4740_request, queue); ++ ++ if (req) { ++ if (req->req.length == 0) { ++ DEBUG_EP0("ZERO LENGTH OUT!\n"); ++/* usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); */ ++ usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY)); ++ dev->ep0state = WAIT_FOR_SETUP; ++ return; ++ } ++ ret = read_fifo_ep0(ep, req); ++ if (ret) { ++ /* Done! */ ++ DEBUG_EP0("%s: finished, waiting for status\n", ++ __FUNCTION__); ++ usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); ++ dev->ep0state = WAIT_FOR_SETUP; ++ } else { ++ /* Not done yet.. */ ++ DEBUG_EP0("%s: not finished\n", __FUNCTION__); ++ usb_setb(USB_REG_CSR0, USB_CSR0_SVDOUTPKTRDY); ++ } ++ } else { ++ DEBUG_EP0("NO REQ??!\n"); ++ } ++} ++ ++/* ++ * DATA_STATE_XMIT ++ */ ++static int jz4740_ep0_in(struct jz4740_udc *dev, u32 csr) ++{ ++ struct jz4740_request *req; ++ struct jz4740_ep *ep = &dev->ep[0]; ++ int ret, need_zlp = 0; ++ ++ DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); ++ ++ if (list_empty(&ep->queue)) ++ req = 0; ++ else ++ req = list_entry(ep->queue.next, struct jz4740_request, queue); ++ ++ if (!req) { ++ DEBUG_EP0("%s: NULL REQ\n", __FUNCTION__); ++ return 0; ++ } ++ ++ if (req->req.length == 0) { ++ usb_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); ++ dev->ep0state = WAIT_FOR_SETUP; ++ return 1; ++ } ++ ++ if (req->req.length - req->req.actual == EP0_MAXPACKETSIZE) { ++ /* Next write will end with the packet size, */ ++ /* so we need zero-length-packet */ ++ need_zlp = 1; ++ } ++ ++ ret = write_fifo_ep0(ep, req); ++ ++ if (ret == 1 && !need_zlp) { ++ /* Last packet */ ++ DEBUG_EP0("%s: finished, waiting for status\n", __FUNCTION__); ++ ++ usb_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); ++ dev->ep0state = WAIT_FOR_SETUP; ++ } else { ++ DEBUG_EP0("%s: not finished\n", __FUNCTION__); ++ usb_setb(USB_REG_CSR0, USB_CSR0_INPKTRDY); ++ } ++ ++ if (need_zlp) { ++ DEBUG_EP0("%s: Need ZLP!\n", __FUNCTION__); ++ usb_setb(USB_REG_CSR0, USB_CSR0_INPKTRDY); ++ dev->ep0state = DATA_STATE_NEED_ZLP; ++ } ++ ++ return 1; ++} ++ ++#if 0 ++static int jz4740_handle_get_status(struct jz4740_udc *dev, ++ struct usb_ctrlrequest *ctrl) ++{ ++ struct jz4740_ep *ep0 = &dev->ep[0]; ++ struct jz4740_ep *qep; ++ int reqtype = (ctrl->bRequestType & USB_RECIP_MASK); ++ u16 val = 0; ++ ++ if (reqtype == USB_RECIP_INTERFACE) { ++ /* This is not supported. ++ * And according to the USB spec, this one does nothing.. ++ * Just return 0 ++ */ ++ DEBUG_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n"); ++ } else if (reqtype == USB_RECIP_DEVICE) { ++ DEBUG_SETUP("GET_STATUS: USB_RECIP_DEVICE\n"); ++ val |= (1 << 0); /* Self powered */ ++ /*val |= (1<<1); *//* Remote wakeup */ ++ } else if (reqtype == USB_RECIP_ENDPOINT) { ++ int ep_num = (ctrl->wIndex & ~USB_DIR_IN); ++ ++ DEBUG_SETUP ++ ("GET_STATUS: USB_RECIP_ENDPOINT (%d), ctrl->wLength = %d\n", ++ ep_num, ctrl->wLength); ++ ++ if (ctrl->wLength > 2 || ep_num > 3) ++ return -EOPNOTSUPP; ++ ++ qep = &dev->ep[ep_num]; ++ if (ep_is_in(qep) != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0) ++ && ep_index(qep) != 0) { ++ return -EOPNOTSUPP; ++ } ++ ++ usb_set_index(ep_index(qep)); ++ ++ /* Return status on next IN token */ ++ switch (qep->ep_type) { ++ case ep_control: ++ val = ++ (usb_readb(qep->csr) & USB_CSR0_SENDSTALL) == ++ USB_CSR0_SENDSTALL; ++ break; ++ case ep_bulk_in: ++ case ep_interrupt: ++ val = ++ (usb_readb(qep->csr) & USB_INCSR_SENDSTALL) == ++ USB_INCSR_SENDSTALL; ++ break; ++ case ep_bulk_out: ++ val = ++ (usb_readb(qep->csr) & USB_OUTCSR_SENDSTALL) == ++ USB_OUTCSR_SENDSTALL; ++ break; ++ } ++ ++ /* Back to EP0 index */ ++ usb_set_index(0); ++ ++ DEBUG_SETUP("GET_STATUS, ep: %d (%x), val = %d\n", ep_num, ++ ctrl->wIndex, val); ++ } else { ++ DEBUG_SETUP("Unknown REQ TYPE: %d\n", reqtype); ++ return -EOPNOTSUPP; ++ } ++ ++ /* Clear "out packet ready" */ ++ usb_setb(USB_REG_CSR0, USB_CSR0_SVDOUTPKTRDY); ++ /* Put status to FIFO */ ++ jz4740_fifo_write(ep0, (u8 *) & val, sizeof(val)); ++ /* Issue "In packet ready" */ ++ usb_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); ++ ++ return 0; ++} ++#endif ++ ++/* ++ * WAIT_FOR_SETUP (OUTPKTRDY) ++ * - read data packet from EP0 FIFO ++ * - decode command ++ * - if error ++ * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND | USB_CSR0_SENDSTALL bits ++ * - else ++ * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND bits ++ */ ++static void jz4740_ep0_setup(struct jz4740_udc *dev, u32 csr) ++{ ++ struct jz4740_ep *ep = &dev->ep[0]; ++ struct usb_ctrlrequest ctrl; ++ int i; ++ ++ DEBUG_SETUP("%s: %x\n", __FUNCTION__, csr); ++ ++ /* Nuke all previous transfers */ ++ nuke(ep, -EPROTO); ++ ++ /* read control req from fifo (8 bytes) */ ++ jz4740_fifo_read(ep, (unsigned char *)&ctrl, 8); ++ ++ DEBUG_SETUP("SETUP %02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bRequestType, ctrl.bRequest, ++ ctrl.wValue, ctrl.wIndex, ctrl.wLength); ++ ++ /* Set direction of EP0 */ ++ if (likely(ctrl.bRequestType & USB_DIR_IN)) { ++ ep->bEndpointAddress |= USB_DIR_IN; ++ } else { ++ ep->bEndpointAddress &= ~USB_DIR_IN; ++ } ++ ++ /* Handle some SETUP packets ourselves */ ++ switch (ctrl.bRequest) { ++ case USB_REQ_SET_ADDRESS: ++ if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) ++ break; ++ ++ DEBUG_SETUP("USB_REQ_SET_ADDRESS (%d)\n", ctrl.wValue); ++ udc_set_address(dev, ctrl.wValue); ++ usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); ++ return; ++ ++ case USB_REQ_SET_CONFIGURATION: ++ if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) ++ break; ++ ++ DEBUG_SETUP("USB_REQ_SET_CONFIGURATION (%d)\n", ctrl.wValue); ++ usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); ++ ++ /* Enable RESUME and SUSPEND interrupts */ ++ usb_setb(USB_REG_INTRUSBE, (USB_INTR_RESUME | USB_INTR_SUSPEND)); ++ break; ++ ++ case USB_REQ_SET_INTERFACE: ++ if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) ++ break; ++ ++ DEBUG_SETUP("USB_REQ_SET_INTERFACE (%d)\n", ctrl.wValue); ++ usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); ++ break; ++ ++// case USB_REQ_GET_STATUS: ++// if (jz4740_handle_get_status(dev, &ctrl) == 0) ++// return; ++ ++ case USB_REQ_CLEAR_FEATURE: ++ case USB_REQ_SET_FEATURE: ++ if (ctrl.bRequestType == USB_RECIP_ENDPOINT) { ++ struct jz4740_ep *qep; ++ int ep_num = (ctrl.wIndex & 0x0f); ++ ++ /* Support only HALT feature */ ++ if (ctrl.wValue != 0 || ctrl.wLength != 0 ++ || ep_num > 3 || ep_num < 1) ++ break; ++ ++ qep = &dev->ep[ep_num]; ++ spin_unlock(&dev->lock); ++ if (ctrl.bRequest == USB_REQ_SET_FEATURE) { ++ DEBUG_SETUP("SET_FEATURE (%d)\n", ++ ep_num); ++ jz4740_set_halt(&qep->ep, 1); ++ } else { ++ DEBUG_SETUP("CLR_FEATURE (%d)\n", ++ ep_num); ++ jz4740_set_halt(&qep->ep, 0); ++ } ++ spin_lock(&dev->lock); ++ ++ usb_set_index(0); ++ ++ /* Reply with a ZLP on next IN token */ ++ usb_setb(USB_REG_CSR0, ++ (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); ++ return; ++ } ++ break; ++ ++ default: ++ break; ++ } ++ ++ /* gadget drivers see class/vendor specific requests, ++ * {SET,GET}_{INTERFACE,DESCRIPTOR,CONFIGURATION}, ++ * and more. ++ */ ++ if (likely((u32)dev->driver)) { ++ /* device-2-host (IN) or no data setup command, process immediately */ ++ spin_unlock(&dev->lock); ++ ++ i = dev->driver->setup(&dev->gadget, &ctrl); ++ spin_lock(&dev->lock); ++ ++ if (unlikely(i < 0)) { ++ /* setup processing failed, force stall */ ++ DEBUG_SETUP ++ (" --> ERROR: gadget setup FAILED (stalling), setup returned %d\n", ++ i); ++ usb_set_index(0); ++ usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND | USB_CSR0_SENDSTALL)); ++ ++ /* ep->stopped = 1; */ ++ dev->ep0state = WAIT_FOR_SETUP; ++ } ++ else { ++ DEBUG_SETUP("gadget driver setup ok (%d)\n", ctrl.wLength); ++ if (!ctrl.wLength) { ++ usb_setb(USB_REG_CSR0, USB_CSR0_SVDOUTPKTRDY); ++ } ++ } ++ } ++} ++ ++/* ++ * DATA_STATE_NEED_ZLP ++ */ ++static void jz4740_ep0_in_zlp(struct jz4740_udc *dev, u32 csr) ++{ ++ DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); ++ ++ usb_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); ++ dev->ep0state = WAIT_FOR_SETUP; ++} ++ ++/* ++ * handle ep0 interrupt ++ */ ++static void jz4740_handle_ep0(struct jz4740_udc *dev, u32 intr) ++{ ++ struct jz4740_ep *ep = &dev->ep[0]; ++ u32 csr; ++ ++ /* Set index 0 */ ++ usb_set_index(0); ++ csr = usb_readb(USB_REG_CSR0); ++ ++ DEBUG_EP0("%s: csr = %x state = \n", __FUNCTION__, csr);//, state_names[dev->ep0state]); ++ ++ /* ++ * if SENT_STALL is set ++ * - clear the SENT_STALL bit ++ */ ++ if (csr & USB_CSR0_SENTSTALL) { ++ DEBUG_EP0("%s: USB_CSR0_SENTSTALL is set: %x\n", __FUNCTION__, csr); ++ usb_clearb(USB_REG_CSR0, USB_CSR0_SENDSTALL | USB_CSR0_SENTSTALL); ++ nuke(ep, -ECONNABORTED); ++ dev->ep0state = WAIT_FOR_SETUP; ++ return; ++ } ++ ++ /* ++ * if a transfer is in progress && INPKTRDY and OUTPKTRDY are clear ++ * - fill EP0 FIFO ++ * - if last packet ++ * - set IN_PKT_RDY | DATA_END ++ * - else ++ * set IN_PKT_RDY ++ */ ++ if (!(csr & (USB_CSR0_INPKTRDY | USB_CSR0_OUTPKTRDY))) { ++ DEBUG_EP0("%s: INPKTRDY and OUTPKTRDY are clear\n", ++ __FUNCTION__); ++ ++ switch (dev->ep0state) { ++ case DATA_STATE_XMIT: ++ DEBUG_EP0("continue with DATA_STATE_XMIT\n"); ++ jz4740_ep0_in(dev, csr); ++ return; ++ case DATA_STATE_NEED_ZLP: ++ DEBUG_EP0("continue with DATA_STATE_NEED_ZLP\n"); ++ jz4740_ep0_in_zlp(dev, csr); ++ return; ++ default: ++ /* Stall? */ ++// DEBUG_EP0("Odd state!! state = %s\n", ++// state_names[dev->ep0state]); ++ dev->ep0state = WAIT_FOR_SETUP; ++ /* nuke(ep, 0); */ ++ /* usb_setb(ep->csr, USB_CSR0_SENDSTALL); */ ++// break; ++ return; ++ } ++ } ++ ++ /* ++ * if SETUPEND is set ++ * - abort the last transfer ++ * - set SERVICED_SETUP_END_BIT ++ */ ++ if (csr & USB_CSR0_SETUPEND) { ++ DEBUG_EP0("%s: USB_CSR0_SETUPEND is set: %x\n", __FUNCTION__, csr); ++ ++ usb_setb(USB_REG_CSR0, USB_CSR0_SVDSETUPEND); ++ nuke(ep, 0); ++ dev->ep0state = WAIT_FOR_SETUP; ++ } ++ ++ /* ++ * if USB_CSR0_OUTPKTRDY is set ++ * - read data packet from EP0 FIFO ++ * - decode command ++ * - if error ++ * set SVDOUTPKTRDY | DATAEND | SENDSTALL bits ++ * - else ++ * set SVDOUTPKTRDY | DATAEND bits ++ */ ++ if (csr & USB_CSR0_OUTPKTRDY) { ++ ++ DEBUG_EP0("%s: EP0_OUT_PKT_RDY is set: %x\n", __FUNCTION__, ++ csr); ++ ++ switch (dev->ep0state) { ++ case WAIT_FOR_SETUP: ++ DEBUG_EP0("WAIT_FOR_SETUP\n"); ++ jz4740_ep0_setup(dev, csr); ++ break; ++ ++ case DATA_STATE_RECV: ++ DEBUG_EP0("DATA_STATE_RECV\n"); ++ jz4740_ep0_out(dev, csr); ++ break; ++ ++ default: ++ /* send stall? */ ++ DEBUG_EP0("strange state!! 2. send stall? state = %d\n", ++ dev->ep0state); ++ break; ++ } ++ } ++} ++ ++static void jz4740_ep0_kick(struct jz4740_udc *dev, struct jz4740_ep *ep) ++{ ++ u32 csr; ++ ++ usb_set_index(0); ++ csr = usb_readb(USB_REG_CSR0); ++ ++ DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); ++ ++ /* Clear "out packet ready" */ ++ usb_setb(USB_REG_CSR0, USB_CSR0_SVDOUTPKTRDY); ++ ++ if (ep_is_in(ep)) { ++ dev->ep0state = DATA_STATE_XMIT; ++ jz4740_ep0_in(dev, csr); ++ } else { ++ dev->ep0state = DATA_STATE_RECV; ++ jz4740_ep0_out(dev, csr); ++ } ++} ++ ++/** Handle USB RESET interrupt ++ */ ++static void jz4740_reset_irq(struct jz4740_udc *dev) ++{ ++ dev->gadget.speed = (usb_readb(USB_REG_POWER) & USB_POWER_HSMODE) ? ++ USB_SPEED_HIGH : USB_SPEED_FULL; ++ ++ DEBUG_SETUP("%s: address = %d, speed = %s\n", __FUNCTION__, dev->usb_address, ++ (dev->gadget.speed == USB_SPEED_HIGH) ? "HIGH":"FULL" ); ++} ++ ++/* ++ * jz4740 usb device interrupt handler. ++ */ ++static irqreturn_t jz4740_udc_irq(int irq, void *_dev) ++{ ++ struct jz4740_udc *dev = _dev; ++ ++ u32 intr_usb = usb_readb(USB_REG_INTRUSB) & 0x7; /* mask SOF */ ++ u32 intr_in = usb_readw(USB_REG_INTRIN); ++ u32 intr_out = usb_readw(USB_REG_INTROUT); ++ u32 intr_dma = usb_readb(USB_REG_INTR); ++ ++ if (!intr_usb && !intr_in && !intr_out && !intr_dma) ++ return IRQ_HANDLED; ++ ++ DEBUG("intr_out = %x intr_in=%x intr_usb=%x\n", ++ intr_out, intr_in, intr_usb); ++ ++ spin_lock(&dev->lock); ++ ++ /* Check for resume from suspend mode */ ++ if ((intr_usb & USB_INTR_RESUME) && ++ (usb_readb(USB_REG_INTRUSBE) & USB_INTR_RESUME)) { ++ DEBUG("USB resume\n"); ++ } ++ ++ /* Check for system interrupts */ ++ if (intr_usb & USB_INTR_RESET) { ++ DEBUG("USB reset\n"); ++#ifdef CONFIG_JZ_UDC_HOTPLUG ++ jz_udc_active = 1; ++#endif ++ if (udc_debug) { ++ /* We have tested the cable type, disable module and ++ * disconnect from host right now. ++ */ ++ udc_disable(dev); ++ spin_unlock(&dev->lock); ++ return IRQ_HANDLED; ++ } ++ jz4740_reset_irq(dev); ++ } ++ ++ /* Check for endpoint 0 interrupt */ ++ if (intr_in & USB_INTR_EP0) { ++ DEBUG("USB_INTR_EP0 (control)\n"); ++ jz4740_handle_ep0(dev, intr_in); ++ } ++ ++ /* Check for Bulk-IN DMA interrupt */ ++ if (intr_dma & 0x1) { ++ int ep_num; ++ ep_num = (usb_readl(USB_REG_CNTL1) >> 4) & 0xf; ++ jz4740_in_epn(dev, ep_num, intr_in); ++ } ++ ++ /* Check for Bulk-OUT DMA interrupt */ ++ if (intr_dma & 0x2) { ++ int ep_num; ++ ep_num = (usb_readl(USB_REG_CNTL2) >> 4) & 0xf; ++ jz4740_out_epn(dev, ep_num, intr_out); ++ } ++ ++ /* Check for each configured endpoint interrupt */ ++ if (intr_in & USB_INTR_INEP1) { ++ DEBUG("USB_INTR_INEP1\n"); ++ jz4740_in_epn(dev, 1, intr_in); ++ } ++ ++ if (intr_in & USB_INTR_INEP2) { ++ DEBUG("USB_INTR_INEP2\n"); ++ jz4740_in_epn(dev, 2, intr_in); ++ } ++ ++ if (intr_out & USB_INTR_OUTEP1) { ++ DEBUG("USB_INTR_OUTEP1\n"); ++ jz4740_out_epn(dev, 1, intr_out); ++ } ++ ++ /* Check for suspend mode */ ++ if ((intr_usb & USB_INTR_SUSPEND) && ++ (usb_readb(USB_REG_INTRUSBE) & USB_INTR_SUSPEND)) { ++ DEBUG("USB suspend\n"); ++ dev->driver->suspend(&dev->gadget); ++ /* Host unloaded from us, can do something, such as flushing ++ the NAND block cache etc. */ ++ } ++ ++#ifdef CONFIG_JZ_UDC_HOTPLUG ++ jz_udc_active = 1; ++#endif ++ ++ spin_unlock(&dev->lock); ++ return IRQ_HANDLED; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int jz4740_udc_get_frame(struct usb_gadget *_gadget) ++{ ++ DEBUG("%s, %p\n", __FUNCTION__, _gadget); ++ return usb_readw(USB_REG_FRAME); ++} ++ ++static int jz4740_udc_wakeup(struct usb_gadget *_gadget) ++{ ++ /* host may not have enabled remote wakeup */ ++ /*if ((UDCCS0 & UDCCS0_DRWF) == 0) ++ return -EHOSTUNREACH; ++ udc_set_mask_UDCCR(UDCCR_RSM); */ ++ return -ENOTSUPP; ++} ++ ++static const struct usb_gadget_ops jz4740_udc_ops = { ++ .get_frame = jz4740_udc_get_frame, ++ .wakeup = jz4740_udc_wakeup, ++ /* current versions must always be self-powered */ ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct jz4740_udc udc_dev = { ++ .usb_address = 0, ++ ++ .gadget = { ++ .ops = &jz4740_udc_ops, ++ .ep0 = &udc_dev.ep[0].ep, ++ .name = driver_name, ++ .dev = { ++ .bus_id = "gadget", ++ }, ++ }, ++ ++ /* control endpoint */ ++ .ep[0] = { ++ .ep = { ++ .name = ep0name, ++ .ops = &jz4740_ep_ops, ++ .maxpacket = EP0_MAXPACKETSIZE, ++ }, ++ .dev = &udc_dev, ++ ++ .bEndpointAddress = 0, ++ .bmAttributes = 0, ++ ++ .ep_type = ep_control, ++ .fifo = USB_FIFO_EP0, ++ .csr = USB_REG_CSR0, ++ }, ++ ++ /* bulk out endpoint */ ++ .ep[1] = { ++ .ep = { ++ .name = "ep1out-bulk", ++ .ops = &jz4740_ep_ops, ++ .maxpacket = EPBULK_MAXPACKETSIZE, ++ }, ++ .dev = &udc_dev, ++ ++ .bEndpointAddress = 1, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ ++ .ep_type = ep_bulk_out, ++ .fifo = USB_FIFO_EP1, ++ .csr = USB_REG_OUTCSR, ++ }, ++ ++ /* bulk in endpoint */ ++ .ep[2] = { ++ .ep = { ++ .name = "ep1in-bulk", ++ .ops = &jz4740_ep_ops, ++ .maxpacket = EPBULK_MAXPACKETSIZE, ++ }, ++ .dev = &udc_dev, ++ ++ .bEndpointAddress = USB_DIR_IN | 1, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ ++ .ep_type = ep_bulk_in, ++ .fifo = USB_FIFO_EP1, ++ .csr = USB_REG_INCSR, ++ }, ++ ++ /* interrupt in endpoint */ ++ .ep[3] = { ++ .ep = { ++ .name = "ep2in-int", ++ .ops = &jz4740_ep_ops, ++ .maxpacket = EPINTR_MAXPACKETSIZE, ++ }, ++ .dev = &udc_dev, ++ ++ .bEndpointAddress = USB_DIR_IN | 2, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ ++ .ep_type = ep_interrupt, ++ .fifo = USB_FIFO_EP2, ++ .csr = USB_REG_INCSR, ++ }, ++}; ++ ++static int jz4740_udc_probe(struct platform_device *pdev) ++{ ++ struct jz4740_udc *dev = &udc_dev; ++ int rc; ++ ++ DEBUG("%s\n", __FUNCTION__); ++ ++ spin_lock_init(&dev->lock); ++ the_controller = dev; ++ ++ dev->dev = &pdev->dev; ++ device_initialize(&dev->gadget.dev); ++ dev->gadget.dev.parent = &pdev->dev; ++ ++// strcpy (dum->gadget.dev.bus_id, "gadget"); ++ dev->gadget.dev.release = jz4740_udc_release; ++ if ((rc = device_register (&dev->gadget.dev)) < 0) ++ return rc; ++ platform_set_drvdata(pdev, dev); ++ ++ udc_disable(dev); ++ udc_reinit(dev); ++ ++ /* irq setup */ ++ if (request_irq(IRQ_UDC, jz4740_udc_irq, IRQF_DISABLED,//SA_SHIRQ/*|SA_SAMPLE_RANDOM*/, ++ driver_name, dev) != 0) { ++ printk(KERN_INFO "request UDC interrupt %d failed\n", IRQ_UDC); ++ return -EBUSY; ++ } ++ ++ printk(KERN_INFO "%s\n", driver_desc); ++ printk(KERN_INFO "version: " DRIVER_VERSION "\n"); ++ ++ return 0; ++} ++ ++static int jz4740_udc_remove(struct platform_device *pdev) ++{ ++ struct jz4740_udc *dev = platform_get_drvdata(pdev); ++ DEBUG("%s: %p\n", __FUNCTION__, dev); ++ ++ if (dev->driver) ++ return -EBUSY; ++ ++ udc_disable(dev); ++#ifdef UDC_PROC_FILE ++ remove_proc_entry(proc_node_name, NULL); ++#endif ++ ++ free_irq(IRQ_UDC, dev); ++ platform_set_drvdata(pdev, 0); ++ device_unregister(&dev->gadget.dev); ++ the_controller = 0; ++ ++ return 0; ++} ++ ++static struct platform_driver udc_driver = { ++ .probe = jz4740_udc_probe, ++ .remove = jz4740_udc_remove, ++ .suspend = NULL, ++ .resume = NULL, ++ .driver = { ++ .name = (char *) driver_name, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++ ++ ++static struct platform_device the_udc_pdev = { ++ .name = (char *) gadget_name, ++ .id = -1, ++ .dev = { ++ .release = jz4740_udc_release, ++ }, ++}; ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init udc_init (void) ++{ ++ platform_driver_register(&udc_driver); ++ return platform_device_register (&the_udc_pdev); ++} ++ ++static void __exit udc_exit (void) ++{ ++ platform_driver_unregister(&udc_driver); ++ platform_device_unregister(&the_udc_pdev); ++} ++ ++module_init(udc_init); ++module_exit(udc_exit); ++ ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_AUTHOR("Wei Jianli "); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/drivers/usb/gadget/jz4740_udc.h linux-2.6.24.7/drivers/usb/gadget/jz4740_udc.h +--- linux-2.6.24.7.old/drivers/usb/gadget/jz4740_udc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/usb/gadget/jz4740_udc.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,112 @@ ++/* ++ * linux/drivers/usb/gadget/jz4740_udc.h ++ * ++ * Ingenic JZ4740 on-chip high speed USB device controller ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#ifndef __USB_GADGET_JZ4740_H__ ++#define __USB_GADGET_JZ4740_H__ ++ ++/*-------------------------------------------------------------------------*/ ++ ++// Max packet size ++#define EP0_MAXPACKETSIZE 64 ++#define EPBULK_MAXPACKETSIZE 512 ++#define EPINTR_MAXPACKETSIZE 64 ++ ++#define UDC_MAX_ENDPOINTS 4 ++ ++/*-------------------------------------------------------------------------*/ ++ ++typedef enum ep_type { ++ ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt ++} ep_type_t; ++ ++struct jz4740_ep { ++ struct usb_ep ep; ++ struct jz4740_udc *dev; ++ ++ const struct usb_endpoint_descriptor *desc; ++ struct list_head queue; ++ unsigned long pio_irqs; ++ ++ u8 stopped; ++ u8 bEndpointAddress; ++ u8 bmAttributes; ++ ++ ep_type_t ep_type; ++ u32 fifo; ++ u32 csr; ++ ++ u32 reg_addr; ++}; ++ ++struct jz4740_request { ++ struct usb_request req; ++ struct list_head queue; ++}; ++ ++enum ep0state { ++ WAIT_FOR_SETUP, /* between STATUS ack and SETUP report */ ++ DATA_STATE_XMIT, /* data tx stage */ ++ DATA_STATE_NEED_ZLP, /* data tx zlp stage */ ++ WAIT_FOR_OUT_STATUS, /* status stages */ ++ DATA_STATE_RECV, /* data rx stage */ ++}; ++ ++struct jz4740_udc { ++ struct usb_gadget gadget; ++ struct usb_gadget_driver *driver; ++ struct device *dev; ++ spinlock_t lock; ++ ++ enum ep0state ep0state; ++ struct jz4740_ep ep[UDC_MAX_ENDPOINTS]; ++ ++ unsigned char usb_address; ++}; ++ ++extern struct jz4740_udc *the_controller; ++ ++#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN)==USB_DIR_IN) ++#define ep_maxpacket(EP) ((EP)->ep.maxpacket) ++#define ep_index(EP) ((EP)->bEndpointAddress&0xF) ++#define usb_set_index(i) (REG8(USB_REG_INDEX) = (i)) ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* 2.5 stuff that's sometimes missing in 2.4 */ ++ ++#ifndef container_of ++#define container_of list_entry ++#endif ++ ++#ifndef likely ++#define likely(x) (x) ++#define unlikely(x) (x) ++#endif ++ ++#ifndef BUG_ON ++#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) ++#endif ++ ++#ifndef WARN_ON ++#define WARN_ON(x) do { } while (0) ++#endif ++ ++#ifndef IRQ_NONE ++typedef void irqreturn_t; ++#define IRQ_NONE ++#define IRQ_HANDLED ++#define IRQ_RETVAL(x) ++#endif ++ ++#endif /* __USB_GADGET_JZ4740_H__ */ +diff -urN linux-2.6.24.7.old/drivers/usb/host/ohci-hcd.c linux-2.6.24.7/drivers/usb/host/ohci-hcd.c +--- linux-2.6.24.7.old/drivers/usb/host/ohci-hcd.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/usb/host/ohci-hcd.c 2009-04-12 18:13:57.000000000 +0200 +@@ -977,6 +977,11 @@ + #define PCI_DRIVER ohci_pci_driver + #endif + ++#ifdef CONFIG_JZSOC ++#include "ohci-jz.c" ++#define PLATFORM_DRIVER ohci_hcd_jz_driver ++#endif ++ + #ifdef CONFIG_SA1111 + #include "ohci-sa1111.c" + #define SA1111_DRIVER ohci_hcd_sa1111_driver +diff -urN linux-2.6.24.7.old/drivers/usb/host/ohci-jz.c linux-2.6.24.7/drivers/usb/host/ohci-jz.c +--- linux-2.6.24.7.old/drivers/usb/host/ohci-jz.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/usb/host/ohci-jz.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,260 @@ ++/* ++ * OHCI HCD (Host Controller Driver) for USB. ++ * ++ * (C) Copyright 1999 Roman Weissgaerber ++ * (C) Copyright 2000-2002 David Brownell ++ * (C) Copyright 2002 Hewlett-Packard Company ++ * ++ * Bus Glue for Ingenic Jz47xx. ++ * ++ * Written by Christopher Hoover ++ * Based on fragments of previous driver by Rusell King et al. ++ * ++ * Modified for LH7A404 from ohci-sa1111.c ++ * by Durgesh Pattamatta ++ * Modified for AMD Alchemy Au1xxx ++ * by Matt Porter ++ * Modified for Jz47xx from ohci-au1xxx.c ++ * by Peter ++ * ++ * This file is licenced under the GPL. ++ */ ++ ++#include ++#include ++ ++#include ++ ++extern int usb_disabled(void); ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void jz_start_ohc(struct platform_device *dev) ++{ ++ printk(KERN_DEBUG __FILE__ ++ ": starting JZ OHCI USB Controller\n"); ++ ++ /* enable host controller */ ++ __cpm_start_uhc(); ++ ++#ifdef CONFIG_SOC_JZ4750 ++ __cpm_enable_uhcphy(); ++#endif ++ ++ printk(KERN_DEBUG __FILE__ ++ ": Clock to USB host has been enabled \n"); ++} ++ ++static void jz_stop_ohc(struct platform_device *dev) ++{ ++ printk(KERN_DEBUG __FILE__ ++ ": stopping JZ OHCI USB Controller\n"); ++ ++#ifdef CONFIG_SOC_JZ4750 ++ __cpm_suspend_uhcphy(); ++#endif ++ ++ __cpm_stop_uhc(); ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* configure so an HC device and id are always provided */ ++/* always called with process context; sleeping is OK */ ++ ++ ++/** ++ * usb_ohci_jz_probe - initialize Jz-based HCDs ++ * Context: !in_interrupt() ++ * ++ * Allocates basic resources for this USB host controller, and ++ * then invokes the start() method for the HCD associated with it ++ * through the hotplug entry's driver_data. ++ * ++ */ ++static int usb_ohci_jz_probe(const struct hc_driver *driver, ++ struct platform_device *dev) ++{ ++ int retval; ++ struct usb_hcd *hcd; ++ ++ if (dev->resource[1].flags != IORESOURCE_IRQ) { ++ pr_debug("resource[1] is not IORESOURCE_IRQ\n"); ++ return -ENOMEM; ++ } ++ ++ hcd = usb_create_hcd(driver, &dev->dev, "jz"); ++ if (!hcd) ++ return -ENOMEM; ++ hcd->rsrc_start = dev->resource[0].start; ++ hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; ++ ++ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { ++ pr_debug("request_mem_region failed\n"); ++ retval = -EBUSY; ++ goto err1; ++ } ++ ++ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); ++ if (!hcd->regs) { ++ pr_debug("ioremap failed\n"); ++ retval = -ENOMEM; ++ goto err2; ++ } ++ ++ jz_start_ohc(dev); ++ ohci_hcd_init(hcd_to_ohci(hcd)); ++ ++ retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED | IRQF_SHARED); ++ if (retval == 0) ++ return retval; ++ ++ jz_stop_ohc(dev); ++ iounmap(hcd->regs); ++ err2: ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++ err1: ++ usb_put_hcd(hcd); ++ return retval; ++} ++ ++ ++/* may be called without controller electrically present */ ++/* may be called with controller, bus, and devices active */ ++ ++/** ++ * usb_hcd_jz_remove - shutdown processing for Jz-based HCDs ++ * @dev: USB Host Controller being removed ++ * Context: !in_interrupt() ++ * ++ * Reverses the effect of usb_hcd_jz_probe(), first invoking ++ * the HCD's stop() method. It is always called from a thread ++ * context, normally "rmmod", "apmd", or something similar. ++ * ++ */ ++static void usb_ohci_jz_remove(struct usb_hcd *hcd, struct platform_device *dev) ++{ ++ usb_remove_hcd(hcd); ++ jz_stop_ohc(dev); ++ iounmap(hcd->regs); ++ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); ++ usb_put_hcd(hcd); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __devinit ++ohci_jz_start (struct usb_hcd *hcd) ++{ ++ struct ohci_hcd *ohci = hcd_to_ohci (hcd); ++ int ret; ++ ++ ohci_dbg (ohci, "ohci_jz_start, ohci:%p", ohci); ++ ++ if ((ret = ohci_init (ohci)) < 0) ++ return ret; ++ ++ if ((ret = ohci_run (ohci)) < 0) { ++ err ("can't start %s", hcd->self.bus_name); ++ ohci_stop (hcd); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static const struct hc_driver ohci_jz_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "JZ OHCI", ++ .hcd_priv_size = sizeof(struct ohci_hcd), ++ ++ /* ++ * generic hardware linkage ++ */ ++ .irq = ohci_irq, ++ .flags = HCD_USB11 | HCD_MEMORY, ++ ++ /* ++ * basic lifecycle operations ++ */ ++ .start = ohci_jz_start, ++ .stop = ohci_stop, ++ .shutdown = ohci_shutdown, ++ ++ /* ++ * managing i/o requests and associated device resources ++ */ ++ .urb_enqueue = ohci_urb_enqueue, ++ .urb_dequeue = ohci_urb_dequeue, ++ .endpoint_disable = ohci_endpoint_disable, ++ ++ /* ++ * scheduling support ++ */ ++ .get_frame_number = ohci_get_frame, ++ ++ /* ++ * root hub support ++ */ ++ .hub_status_data = ohci_hub_status_data, ++ .hub_control = ohci_hub_control, ++ .hub_irq_enable = ohci_rhsc_enable, ++#ifdef CONFIG_PM ++ .bus_suspend = ohci_bus_suspend, ++ .bus_resume = ohci_bus_resume, ++#endif ++ .start_port_reset = ohci_start_port_reset, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int ohci_hcd_jz_drv_probe(struct platform_device *pdev) ++{ ++ int ret; ++ ++ pr_debug ("In ohci_hcd_jz_drv_probe"); ++ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ ret = usb_ohci_jz_probe(&ohci_jz_hc_driver, pdev); ++ return ret; ++} ++ ++static int ohci_hcd_jz_drv_remove(struct platform_device *pdev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(pdev); ++ ++ usb_ohci_jz_remove(hcd, pdev); ++ return 0; ++} ++ /*TBD*/ ++/*static int ohci_hcd_jz_drv_suspend(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ ++ return 0; ++} ++static int ohci_hcd_jz_drv_resume(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ ++ return 0; ++} ++*/ ++ ++static struct platform_driver ohci_hcd_jz_driver = { ++ .probe = ohci_hcd_jz_drv_probe, ++ .remove = ohci_hcd_jz_drv_remove, ++ .shutdown = usb_hcd_platform_shutdown, ++ /*.suspend = ohci_hcd_jz_drv_suspend, */ ++ /*.resume = ohci_hcd_jz_drv_resume, */ ++ .driver = { ++ .name = "jz-ohci", ++ .owner = THIS_MODULE, ++ }, ++}; ++ +diff -urN linux-2.6.24.7.old/drivers/video/Kconfig linux-2.6.24.7/drivers/video/Kconfig +--- linux-2.6.24.7.old/drivers/video/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/video/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -201,6 +201,222 @@ + comment "Frame buffer hardware drivers" + depends on FB + ++config FB_JZSOC ++ tristate "JZSOC LCD controller support" ++ depends on FB && JZSOC ++ select FB_CFB_FILLRECT ++ select FB_CFB_COPYAREA ++ select FB_CFB_IMAGEBLIT ++ ---help--- ++ JZSOC LCD Controller and Smart LCD Controller driver support. ++ ++config FB_JZ4740_SLCD ++ tristate "JZ4740 Smart LCD controller support" ++ depends on FB_JZSOC && SOC_JZ4740 ++ default n ++ ---help--- ++ This is the frame buffer device driver for the JZ4740 Smart LCD controller. ++ If select this, please set to . ++ ++choice ++ depends on FB_JZ4740_SLCD ++ prompt "SLCD Panel" ++ default JZ_SLCD_LGDP4551_8BUS ++ ++config JZ_SLCD_LGDP4551 ++ bool "LG LGDP4551 Smart LCD panel" ++ ---help--- ++ Driver for Smart LCD LGDP4551, 8-bit sytem interface, 16BPP. ++ ++config JZ_SLCD_SPFD5420A ++ bool "SPFD5420A Smart LCD panel" ++ ---help--- ++ Driver for Smart LCD SPFD5420A 18-bit sytem interface, 18BPP. ++ ++config JZ_SLCD_TRULY ++ bool "TRULY Smart LCD panel (MAX Pixels 400x240)" ++ ---help--- ++ ++endchoice ++ ++config FB_JZLCD_4730_4740 ++ tristate "JZ4730 JZ4740 LCD controller support" ++ depends on FB_JZSOC && (SOC_JZ4730 || SOC_JZ4740) ++ help ++ This is the frame buffer device driver for the JZ4730 and JZ4740 LCD controller. ++config JZLCD_FRAMEBUFFER_MAX ++ int "Default FrameBuffer num" ++ depends on FB_JZLCD_4730_4740 ++ default "1" ++ ---help--- ++ JZ LCD driver support multi-framebuffers for video applications. ++config JZLCD_FRAMEBUFFER_ROTATE_SUPPORT ++ bool "JZLCD FrameBuffer Rotate Support(For TEST)" ++ depends on FB_JZLCD_4730_4740 ++ default n ++ ---help--- ++ JZ LCD driver framebuffer rotate support. Rotate angle can be 0,90,180,270. ++ Note, this fearture is implemented by software, and will cost a lot of cpu capcity. ++ That is to say, if you select this function, you system will become slowly. ++ Rotate cost cpu about: ++ ratate angle 0'C: 0% cpu ++ ratate angle 90'C: 40% cpu ++ ratate angle 180'C: 20% cpu ++ ratate angle 270'C: 40% cpu ++ ++config JZLCD_FRAMEBUFFER_DEFAULT_ROTATE_ANGLE ++ int "FrameBuffer default rotate angle" ++ depends on JZLCD_FRAMEBUFFER_ROTATE_SUPPORT ++ default 0 ++ ---help--- ++ JZ LCD driver framebuffer angle value can be: ++ 0: 0'C ++ 1: 90'C ++ 2: 180'C ++ 3: 270'C ++config JZLCD_FRAMEBUFFER_BPP ++ int "FrameBuffer bit per pixel" ++ depends on JZLCD_FRAMEBUFFER_ROTATE_SUPPORT ++ default 32 ++ ---help--- ++ JZ LCD driver framebuffer support 8bpp, 16bpp, 32bpp ++choice ++ depends on FB_JZLCD_4730_4740 ++ prompt "LCD Panel" ++ default JZLCD_SAMSUNG_LTP400WQF01 ++ ++config JZLCD_SHARP_LQ035Q7 ++ bool "SHARP LQ035Q7 TFT panel (240x320)" ++ ++config JZLCD_SAMSUNG_LTS350Q1 ++ bool "SAMSUNG LTS350Q1 TFT panel (240x320)" ++ ++config JZLCD_SAMSUNG_LTV350QVF04 ++ bool "SAMSUNG LTV350QV_F04 TFT panel (320x240)" ++ ++config JZLCD_SAMSUNG_LTP400WQF01 ++ bool "SAMSUNG LTP400WQF01 TFT panel (480x272)(16bits)" ++ ++config JZLCD_SAMSUNG_LTP400WQF02 ++ bool "SAMSUNG LTP400WQF02 TFT panel (480x272)(18bits)" ++ ++config JZLCD_AUO_A030FL01_V1 ++ bool "AUO A030FL01_V1 TFT panel (480x272)" ++ ++config JZLCD_TRULY_TFTG320240DTSW ++ bool "TRULY TFTG320240DTSW TFT panel (320x240)" ++ ++config JZLCD_TRULY_TFTG320240DTSW_SERIAL ++ bool "TRULY TFTG320240DTSW TFT panel (320x240)(8bit-serial mode)" ++ ++config JZLCD_TRULY_TFTG240320UTSW_63W_E ++ bool "TRULY TFTG240320UTSW-63W-E TFT panel (240x320,2.5in)" ++ ++config JZLCD_FOXCONN_PT035TN01 ++ bool "FOXCONN PT035TN01 TFT panel (320x240)" ++ ++config JZLCD_INNOLUX_PT035TN01_SERIAL ++ bool "INNOLUX PT035TN01 TFT panel (320x240,3.5in)(8bit-serial mode)" ++ ++config JZLCD_TOSHIBA_LTM084P363 ++ bool "Toshiba LTM084P363 TFT panel (800x600)" ++ ++config JZLCD_HYNIX_HT10X21 ++ bool "Hynix HT10X21_300 TFT panel (1024x768)" ++ ++config JZLCD_INNOLUX_AT080TN42 ++ bool "INNOLUX AT080TN42 TFT panel (800x600)" ++ ++config JZLCD_CSTN_800x600 ++ bool "800x600 colorDSTN panel" ++ ++config JZLCD_CSTN_320x240 ++ bool "320x240 colorSTN panel" ++ ++config JZLCD_MSTN_480x320 ++ bool "480x320 monoSTN panel" ++ ++config JZLCD_MSTN_320x240 ++ bool "320x240 monoSTN panel" ++ ++config JZLCD_MSTN_240x128 ++ bool "240x128 monoSTN panel" ++ ++config JZLCD_MSTN_INVERSE ++ bool "Use an inverse color display." ++ depends on (JZLCD_MSTN_480x320 || JZLCD_MSTN_240x128) ++ ++endchoice ++ ++ ++config FB_JZ4750_TVE ++ tristate ++ depends on FB_JZSOC && SOC_JZ4750 ++ default n ++ ++config IPU_JZ4750 ++ bool "Jz4750 ipu support" ++ depends on FB_JZSOC && SOC_JZ4750 ++ ---help--- ++ Use JZ4750 IPU, if need, please select this. ++ ++config FB_JZ4750_LCD ++ tristate "JZ4750 LCD Controller support" ++ depends on FB_JZSOC && SOC_JZ4750 ++ select FB_JZ4750_TVE ++ ---help--- ++ JZ4750 LCD Controller driver. ++ JZ4750 LCD Controller support OSD function(refer jz4750_lcdc_spec.pdf).JZ4750 LCD OSD implement 2 framebuffer layers: foreground0 and foreground1. JZ4750 LCD driver support only foreground0 default. ++ ++config FB_JZ4750_LCD_USE_2LAYER_FRAMEBUFFER ++ bool "JZ4750 LCD driver 2 layers framebuffer support." ++ depends on FB_JZ4750_LCD ++ ---help--- ++ JZ4750 LCD driver support only foreground0 by default. ++ If you need both foreground0 and foreground1, please select this. ++ ++config FB_JZ4750_SLCD ++ bool ++ depends on FB_JZ4750_LCD ++ default n ++ ++choice ++ depends on FB_JZ4750_LCD ++ prompt "JZ4750 LCD Panels Support" ++ default JZ4750_LCD_SAMSUNG_LTP400WQF02 ++ ---help--- ++ Please select the lcd panel in you board ++ ++config JZ4750_LCD_SAMSUNG_LTP400WQF01 ++ bool "SAMSUNG LTP400WQF01 TFT panel (480x272)(16bits)" ++ ++config JZ4750_LCD_SAMSUNG_LTP400WQF02 ++ bool "SAMSUNG LTP400WQF02 TFT panel (480x272)(18bits)" ++ ++config JZ4750_LCD_AUO_A043FL01V2 ++ bool "AUO A043FL01V2 TFT panel (480x272)(24bits)" ++ ++config JZ4750_LCD_FOXCONN_PT035TN01 ++ bool "FOXCONN PT035TN01 TFT panel (320x240,3.5in)(18bit-parallel mode)" ++ ++config JZ4750_LCD_INNOLUX_PT035TN01_SERIAL ++ bool "INNOLUX PT035TN01 TFT panel (320x240,3.5in)(8bit-serial mode)" ++ ++config JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DELTA ++ bool "TOPPOLY_TD025THEA7 TFT panel(320x240)(serial RGB delta mode)" ++ ++config JZ4750_LCD_TRULY_TFTG320240DTSW_18BIT ++ bool "TRULY_TFTG320240DTSW TFT panel (320x240) (Parallel 18bit mode)" ++ ++config JZ4750_LCD_TRULY_TFT_GG1P0319LTSW_W ++ bool "TRULY_TFT_GG1P0319LTSW_W (240x320) (Smart LCD 16bit)" ++ ++config JZ4750_SLCD_KGM701A3_TFT_SPFD5420A ++ bool "KGM701A3_TFT_SPFD5420A (400x240) (Smart LCD 18bit)" ++ select FB_JZ4750_SLCD ++endchoice ++ ++ + config FB_CIRRUS + tristate "Cirrus Logic support" + depends on FB && (ZORRO || PCI) +diff -urN linux-2.6.24.7.old/drivers/video/Makefile linux-2.6.24.7/drivers/video/Makefile +--- linux-2.6.24.7.old/drivers/video/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/video/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -28,6 +28,10 @@ + obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o + + # Hardware specific drivers go first ++obj-$(CONFIG_FB_JZLCD_4730_4740) += jzlcd.o ++obj-$(CONFIG_FB_JZ4740_SLCD) += jz4740_slcd.o ++obj-$(CONFIG_FB_JZ4750_LCD) += jz4750_lcd.o ++obj-$(CONFIG_FB_JZ4750_TVE) += jz4750_tve.o + obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o + obj-$(CONFIG_FB_ARC) += arcfb.o + obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o +diff -urN linux-2.6.24.7.old/drivers/video/console/Kconfig linux-2.6.24.7/drivers/video/console/Kconfig +--- linux-2.6.24.7.old/drivers/video/console/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/video/console/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -134,6 +134,14 @@ + + If unsure, select n. + ++config FRAMEBUFFER_CONSOLE_CURSOR_FLASH ++ bool "Framebuffer Console Cursor flash" ++ depends on FRAMEBUFFER_CONSOLE ++ help ++ Enable cursor flush for the framebuffer console. This is done ++ in software and may be significantly slower than a normally oriented ++ display. ++ + config FRAMEBUFFER_CONSOLE_ROTATION + bool "Framebuffer Console Rotation" + depends on FRAMEBUFFER_CONSOLE +diff -urN linux-2.6.24.7.old/drivers/video/console/fbcon.c linux-2.6.24.7/drivers/video/console/fbcon.c +--- linux-2.6.24.7.old/drivers/video/console/fbcon.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/video/console/fbcon.c 2009-04-12 18:13:57.000000000 +0200 +@@ -399,6 +399,7 @@ + + static void fb_flashcursor(struct work_struct *work) + { ++#ifdef CONFIG_FRAMEBUFFER_CONSOLE_CURSOR_FLASH + struct fb_info *info = container_of(work, struct fb_info, queue); + struct fbcon_ops *ops = info->fbcon_par; + struct display *p; +@@ -424,6 +425,7 @@ + ops->cursor(vc, info, mode, softback_lines, get_color(vc, info, c, 1), + get_color(vc, info, c, 0)); + release_console_sem(); ++#endif + } + + #if defined(CONFIG_ATARI) || defined(CONFIG_MAC) +diff -urN linux-2.6.24.7.old/drivers/video/jz4740_slcd.c linux-2.6.24.7/drivers/video/jz4740_slcd.c +--- linux-2.6.24.7.old/drivers/video/jz4740_slcd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/video/jz4740_slcd.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1334 @@ ++/* ++ * linux/drivers/video/jzslcd.c -- Ingenic On-Chip Smart LCD frame buffer device ++ * ++ * Copyright (C) 2005-2007, Ingenic Semiconductor Inc. ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "console/fbcon.h" ++ ++#include "jz4740_slcd.h" ++ ++#undef DEBUG ++//#define DEBUG ++#ifdef DEBUG ++#define dprintk(x...) printk(x) ++#else ++#define dprintk(x...) ++#endif ++ ++#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) ++#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) ++#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) ++#ifdef DEBUG ++#define print_dbg(f, arg...) printk("dbg::" __FILE__ ",LINE(%d): " f "\n", __LINE__, ## arg) ++#else ++#define print_dbg(f, arg...) do {} while (0) ++#endif ++ ++static jz_dma_desc slcd_palette_desc __attribute__ ((aligned (16))); ++static jz_dma_desc slcd_frame_desc __attribute__ ((aligned (16))); ++ ++static int dma_chan; ++static dma_addr_t slcd_frame_desc_phys_addr, slcd_palette_desc_phys_addr; ++ ++static unsigned char non_link_desp = 0; ++static unsigned char is_set_reg = 0; ++struct lcd_cfb_info { ++ struct fb_info fb; ++ struct display_switch *dispsw; ++ signed int currcon; ++ int func_use_count; ++ ++ struct { ++ u16 red, green, blue; ++ } palette[NR_PALETTE]; ++#ifdef CONFIG_PM ++ struct pm_dev *pm; ++#endif ++}; ++ ++struct slcd_reg_info { ++ unsigned int cmd; ++ unsigned int data; ++}; ++static struct slcd_reg_info reg_buf; ++static struct lcd_cfb_info *jzslcd_info; ++ ++struct jzfb_info { ++ unsigned int cfg; /* panel mode and pin usage etc. */ ++ unsigned int w; ++ unsigned int h; ++ unsigned int bpp; /* bit per pixel */ ++ unsigned int bus; ++ unsigned int pclk; /* pixel clk */ ++ ++}; ++ ++static struct jzfb_info jzfb = { ++#ifdef CONFIG_JZ_SLCD_LGDP4551 ++ SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_TYPE_PARALLEL, ++ 400, 240, 16, 8, 16000000 /*16 bpp, 8 bus*/ ++// 240, 400, 18, 8, 16000000 /*18 bpp, 8 bus*/ ++// 400, 240, 18, 8, 16000000 /*18 bpp, 8 bus*/ ++#endif ++ ++#ifdef CONFIG_JZ_SLCD_SPFD5420A ++ SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_TYPE_PARALLEL, ++ 400, 240, 18, 18, 16000000 /*18 bpp, 18 bus*/ ++#endif ++}; ++ ++ ++static volatile unsigned char *slcd_palette; ++static volatile unsigned char *slcd_frame; ++ ++//extern struct display fb_display[MAX_NR_CONSOLES]; ++static irqreturn_t slcd_dma_irq(int irq, void *dev_id); ++ ++ ++static void Mcupanel_RegSet(UINT32 cmd, UINT32 data) ++{ ++ switch (jzfb.bus) { ++ case 8: ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_DATA | (data&0xffff); ++ break; ++ case 9: ++ data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); ++ data = ((data << 6) & 0xfc0000) | ((data << 4) & 0xfc00) | ((data << 2) & 0xfc); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_DATA | data; ++ break; ++ case 16: ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | (cmd&0xffff); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_DATA | (data&0xffff); ++ break; ++ case 18: ++ cmd = ((cmd & 0xff) << 1) | ((cmd & 0xff00) << 2); ++ data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | cmd; ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_DATA | ((data<<6)&0xfc0000)|((data<<4)&0xfc00) | ((data<<2)&0xfc); ++ break; ++ default: ++ printk("Don't support %d bit Bus\n", jzfb.bus ); ++ break; ++ } ++} ++ ++/* Sent a command withou data */ ++static void Mcupanel_Command(UINT32 cmd) { ++ switch (jzfb.bus) { ++ case 8: ++ case 9: ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); ++ break; ++ case 16: ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | (cmd&0xffff); ++ break; ++ case 18: ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) << 2) | ((cmd&0xff) << 1); ++ break; ++ default: ++ printk("Don't support %d bit Bus\n", jzfb.bus ); ++ break; ++ } ++} ++ ++/* Set the start address of screen, for example (0, 0) */ ++#ifdef CONFIG_JZ_SLCD_LGDP4551 ++static void Mcupanel_SetAddr(UINT16 x, UINT16 y) ++{ ++ Mcupanel_RegSet(0x20,x) ; ++ udelay(1); ++ Mcupanel_RegSet(0x21,y) ; ++ udelay(1); ++ Mcupanel_Command(0x22); ++ ++} ++#endif ++#ifdef CONFIG_JZ_SLCD_SPFD5420A ++void Mcupanel_SetAddr(u32 x, u32 y) //u32 ++{ ++ Mcupanel_RegSet(0x200,x) ; ++ udelay(1); ++ Mcupanel_RegSet(0x201,y) ; ++ udelay(1); ++ Mcupanel_Command(0x202); ++ ++} ++ ++#endif ++ ++static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) ++{ ++ chan &= 0xffff; ++ chan >>= 16 - bf->length; ++ return chan << bf->offset; ++} ++ ++static int jzfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, ++ u_int transp, struct fb_info *info) ++{ ++ struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; ++ unsigned short *ptr, ctmp; ++ ++ print_dbg("regno:%d,RGBt:(%d,%d,%d,%d)\t", regno, red, green, blue, transp); ++ if (regno >= NR_PALETTE) ++ return 1; ++ ++ cfb->palette[regno].red = red ; ++ cfb->palette[regno].green = green; ++ cfb->palette[regno].blue = blue; ++ if (cfb->fb.var.bits_per_pixel <= 16) { ++ red >>= 8; ++ green >>= 8; ++ blue >>= 8; ++ ++ red &= 0xff; ++ green &= 0xff; ++ blue &= 0xff; ++ } ++ switch (cfb->fb.var.bits_per_pixel) { ++ case 1: ++ case 2: ++ case 4: ++ case 8: ++ /* RGB 565 */ ++ if (((red >> 3) == 0) && ((red >> 2) != 0)) ++ red = 1 << 3; ++ if (((blue >> 3) == 0) && ((blue >> 2) != 0)) ++ blue = 1 << 3; ++ ctmp = ((red >> 3) << 11) ++ | ((green >> 2) << 5) | (blue >> 3); ++ ++ ptr = (unsigned short *)slcd_palette; ++ ptr = (unsigned short *)(((u32)ptr)|0xa0000000); ++ ptr[regno] = ctmp; ++ ++ break; ++ ++ case 15: ++ if (regno < 16) ++ ((u32 *)cfb->fb.pseudo_palette)[regno] = ++ ((red >> 3) << 10) | ++ ((green >> 3) << 5) | ++ (blue >> 3); ++ break; ++ case 16: ++ if (regno < 16) { ++ ((u32 *)cfb->fb.pseudo_palette)[regno] = ++ ((red >> 3) << 11) | ++ ((green >> 2) << 5) | ++ (blue >> 3); ++ } ++ break; ++ case 18: ++ case 24: ++ case 32: ++ if (regno < 16) ++ ((u32 *)cfb->fb.pseudo_palette)[regno] = ++ (red << 16) | ++ (green << 8) | ++ (blue << 0); ++ ++/* if (regno < 16) { ++ unsigned val; ++ val = chan_to_field(red, &cfb->fb.var.red); ++ val |= chan_to_field(green, &cfb->fb.var.green); ++ val |= chan_to_field(blue, &cfb->fb.var.blue); ++ ((u32 *)cfb->fb.pseudo_palette)[regno] = val; ++ } ++*/ ++ ++ break; ++ } ++ return 0; ++} ++ ++static int jzfb_ioctl (struct fb_info *info, unsigned int cmd, unsigned long arg ) ++{ ++ int ret = 0; ++ void __user *argp = (void __user *)arg; ++ ++ switch (cmd) { ++ case FBIOSETBACKLIGHT: ++ __slcd_set_backlight_level(arg); /* We support 8 levels here. */ ++ break; ++ case FBIODISPON: ++ __slcd_display_on(); ++ break; ++ case FBIODISPOFF: ++ __slcd_display_off(); ++ break; ++ case FBIO_REFRESH_ALWAYS: ++ dprintk("slcd_frame_desc.dcmd = 0x%08x\n", slcd_frame_desc.dcmd); ++ if (slcd_frame_desc.dcmd & DMAC_DCMD_LINK) ++ printk("The Smart LCD refreshes automatically. Option is omitted!\n"); ++ else { ++ dprintk("OPEN DMAC_DCMD_LINK \n"); ++ slcd_frame_desc.dcmd &= ~DMAC_DCMD_TIE; ++ slcd_frame_desc.dcmd |= DMAC_DCMD_LINK; ++ dma_cache_wback((unsigned long)(&slcd_frame_desc), 16); ++ REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_TIE; ++ __dmac_channel_set_doorbell(dma_chan); ++ } ++ break; ++ case FBIO_REFRESH_EVENTS: ++ dprintk("slcd_frame_desc.dcmd = 0x%08x\n", slcd_frame_desc.dcmd); ++ if (!(slcd_frame_desc.dcmd & DMAC_DCMD_LINK)) ++ printk("The Smart LCD is refreshed by envents. Option is omitted!\n"); ++ else { ++ non_link_desp = 1; ++ REG_DMAC_DCMD(dma_chan) |= DMAC_DCMD_TIE; ++ REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK; ++ } ++ break; ++ case FBIO_DO_REFRESH: ++ ++ dprintk("slcd_frame_desc.dcmd = 0x%08x\n", slcd_frame_desc.dcmd); ++ if (slcd_frame_desc.dcmd & DMAC_DCMD_LINK) ++ printk("The Smart LCD can refresh automatically. Option is omitted!\n"); ++ else { ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ __dmac_channel_set_doorbell(dma_chan); ++ } ++ break; ++ case FBIO_SET_REG: ++ if (copy_from_user(®_buf, argp, sizeof(reg_buf))) ++ return -EFAULT; ++ is_set_reg = 1; ++ REG_DMAC_DCMD(dma_chan) |= DMAC_DCMD_TIE; ++ REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK; ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */ ++static int jzfb_mmap(struct fb_info *info, struct vm_area_struct *vma) ++{ ++ struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; ++ unsigned long start; ++ unsigned long off; ++ u32 len; ++ ++ off = vma->vm_pgoff << PAGE_SHIFT; ++ //fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); ++ ++ /* frame buffer memory */ ++ start = cfb->fb.fix.smem_start; ++ len = PAGE_ALIGN((start & ~PAGE_MASK) + cfb->fb.fix.smem_len); ++ start &= PAGE_MASK; ++ ++ if ((vma->vm_end - vma->vm_start + off) > len) ++ return -EINVAL; ++ off += start; ++ ++ vma->vm_pgoff = off >> PAGE_SHIFT; ++ vma->vm_flags |= VM_IO; ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* Uncacheable */ ++ ++#if 1 ++ pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; ++// pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; /* Uncacheable */ ++ pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; /* Write-Through */ ++#endif ++ ++ if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, ++ vma->vm_end - vma->vm_start, ++ vma->vm_page_prot)) { ++ return -EAGAIN; ++ } ++ return 0; ++} ++ ++/* checks var and eventually tweaks it to something supported, ++ * DO NOT MODIFY PAR */ ++static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) ++{ ++ print_dbg("jzfb_check_var"); ++ return 0; ++} ++ ++ ++/* ++ * set the video mode according to info->var ++ */ ++static int jzfb_set_par(struct fb_info *info) ++{ ++// print_dbg("jzfb_set_par"); ++ printk("jzfb_set_par"); ++ return 0; ++} ++ ++ ++/* ++ * (Un)Blank the display. ++ * Fix me: should we use VESA value? ++ */ ++static int jzfb_blank(int blank_mode, struct fb_info *info) ++{ ++ ++ dprintk("fb_blank %d %p", blank_mode, info); ++ ++ switch (blank_mode) { ++ ++ case FB_BLANK_UNBLANK: ++ /* Turn on panel */ ++ break; ++ ++ case FB_BLANK_NORMAL: ++ case FB_BLANK_VSYNC_SUSPEND: ++ case FB_BLANK_HSYNC_SUSPEND: ++ case FB_BLANK_POWERDOWN: ++ /* Turn off panel */ ++ break; ++ default: ++ break; ++ ++ } ++ return 0; ++} ++ ++/* ++ * pan display ++ */ ++static int jzfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) ++{ ++ struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; ++ int dy; ++ ++ if (!var || !cfb) { ++ return -EINVAL; ++ } ++ ++ if (var->xoffset - cfb->fb.var.xoffset) { ++ /* No support for X panning for now! */ ++ return -EINVAL; ++ } ++ ++ dy = var->yoffset - cfb->fb.var.yoffset; ++ print_dbg("var.yoffset: %d", dy); ++ if (dy) { ++ ++ print_dbg("Panning screen of %d lines", dy); ++// slcd_frame_desc->databuf += (cfb->fb.fix.line_length * dy); ++// slcd_frame_desc->dsadr += (cfb->fb.fix.line_length * dy); ++ /* TODO: Wait for current frame to finished */ ++ } ++ ++ return 0; ++} ++ ++ ++/* use default function cfb_fillrect, cfb_copyarea, cfb_imageblit */ ++static struct fb_ops jzfb_ops = { ++ .owner = THIS_MODULE, ++ .fb_setcolreg = jzfb_setcolreg, ++ .fb_check_var = jzfb_check_var, ++ .fb_set_par = jzfb_set_par, ++ .fb_blank = jzfb_blank, ++ .fb_pan_display = jzfb_pan_display, ++ .fb_fillrect = cfb_fillrect, ++ .fb_copyarea = cfb_copyarea, ++ .fb_imageblit = cfb_imageblit, ++ .fb_mmap = jzfb_mmap, ++ .fb_ioctl = jzfb_ioctl, ++}; ++ ++static int jzfb_set_var(struct fb_var_screeninfo *var, int con, ++ struct fb_info *info) ++{ ++ struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; ++ //struct display *display; ++ int chgvar = 0; ++ ++ var->height = jzfb.h ; ++ var->width = jzfb.w ; ++ var->bits_per_pixel = jzfb.bpp; ++ ++ var->vmode = FB_VMODE_NONINTERLACED; ++ var->activate = cfb->fb.var.activate; ++ var->xres = var->width; ++ var->yres = var->height; ++ var->xres_virtual = var->width; ++ var->yres_virtual = var->height; ++ var->xoffset = 0; ++ var->yoffset = 0; ++ var->pixclock = 0; ++ var->left_margin = 0; ++ var->right_margin = 0; ++ var->upper_margin = 0; ++ var->lower_margin = 0; ++ var->hsync_len = 0; ++ var->vsync_len = 0; ++ var->sync = 0; ++ var->activate &= ~FB_ACTIVATE_TEST; ++ ++ /* ++ * CONUPDATE and SMOOTH_XPAN are equal. However, ++ * SMOOTH_XPAN is only used internally by fbcon. ++ */ ++ if (var->vmode & FB_VMODE_CONUPDATE) { ++ var->vmode |= FB_VMODE_YWRAP; ++ var->xoffset = cfb->fb.var.xoffset; ++ var->yoffset = cfb->fb.var.yoffset; ++ } ++ ++ if (var->activate & FB_ACTIVATE_TEST) ++ return 0; ++ ++ if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) ++ return -EINVAL; ++ ++ if (cfb->fb.var.xres != var->xres) ++ chgvar = 1; ++ if (cfb->fb.var.yres != var->yres) ++ chgvar = 1; ++ if (cfb->fb.var.xres_virtual != var->xres_virtual) ++ chgvar = 1; ++ if (cfb->fb.var.yres_virtual != var->yres_virtual) ++ chgvar = 1; ++ if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel) ++ chgvar = 1; ++ ++ //display = fb_display + con; ++ ++ var->red.msb_right = 0; ++ var->green.msb_right = 0; ++ var->blue.msb_right = 0; ++ ++ switch(var->bits_per_pixel){ ++ case 1: /* Mono */ ++ cfb->fb.fix.visual = FB_VISUAL_MONO01; ++ cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; ++ break; ++ case 2: /* Mono */ ++ var->red.offset = 0; ++ var->red.length = 2; ++ var->green.offset = 0; ++ var->green.length = 2; ++ var->blue.offset = 0; ++ var->blue.length = 2; ++ ++ cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; ++ cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; ++ break; ++ case 4: /* PSEUDOCOLOUR*/ ++ var->red.offset = 0; ++ var->red.length = 4; ++ var->green.offset = 0; ++ var->green.length = 4; ++ var->blue.offset = 0; ++ var->blue.length = 4; ++ ++ cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; ++ cfb->fb.fix.line_length = var->xres / 2; ++ break; ++ case 8: /* PSEUDOCOLOUR, 256 */ ++ var->red.offset = 0; ++ var->red.length = 8; ++ var->green.offset = 0; ++ var->green.length = 8; ++ var->blue.offset = 0; ++ var->blue.length = 8; ++ ++ cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; ++ cfb->fb.fix.line_length = var->xres ; ++ break; ++ case 15: /* DIRECTCOLOUR, 32k */ ++ var->bits_per_pixel = 15; ++ var->red.offset = 10; ++ var->red.length = 5; ++ var->green.offset = 5; ++ var->green.length = 5; ++ var->blue.offset = 0; ++ var->blue.length = 5; ++ ++ cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR; ++ cfb->fb.fix.line_length = var->xres_virtual * 2; ++ break; ++ case 16: /* DIRECTCOLOUR, 64k */ ++ var->bits_per_pixel = 16; ++ var->red.offset = 11; ++ var->red.length = 5; ++ var->green.offset = 5; ++ var->green.length = 6; ++ var->blue.offset = 0; ++ var->blue.length = 5; ++ ++ cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; ++ cfb->fb.fix.line_length = var->xres_virtual * 2; ++ break; ++ case 18: ++ case 24: ++ case 32: ++ /* DIRECTCOLOUR, 256 */ ++ var->bits_per_pixel = 32; ++ ++ var->red.offset = 16; ++ var->red.length = 8; ++ var->green.offset = 8; ++ var->green.length = 8; ++ var->blue.offset = 0; ++ var->blue.length = 8; ++ var->transp.offset = 24; ++ var->transp.length = 8; ++ ++ cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; ++ cfb->fb.fix.line_length = var->xres_virtual * 4; ++ break; ++ ++ default: /* in theory this should never happen */ ++ printk(KERN_WARNING "%s: don't support for %dbpp\n", ++ cfb->fb.fix.id, var->bits_per_pixel); ++ break; ++ } ++ ++ cfb->fb.var = *var; ++ cfb->fb.var.activate &= ~FB_ACTIVATE_ALL; ++ ++ /* ++ * If we are setting all the virtual consoles, also set the ++ * defaults used to create new consoles. ++ */ ++ fb_set_cmap(&cfb->fb.cmap, &cfb->fb); ++ dprintk("jzfb_set_var: after fb_set_cmap...\n"); ++ ++ return 0; ++} ++ ++static struct lcd_cfb_info * jzfb_alloc_fb_info(void) ++{ ++ struct lcd_cfb_info *cfb; ++ ++ cfb = kmalloc(sizeof(struct lcd_cfb_info) + sizeof(u32) * 16, GFP_KERNEL); ++ ++ if (!cfb) ++ return NULL; ++ ++ jzslcd_info = cfb; ++ ++ memset(cfb, 0, sizeof(struct lcd_cfb_info) ); ++ ++ cfb->currcon = -1; ++ ++ ++ strcpy(cfb->fb.fix.id, "jz-slcd"); ++ cfb->fb.fix.type = FB_TYPE_PACKED_PIXELS; ++ cfb->fb.fix.type_aux = 0; ++ cfb->fb.fix.xpanstep = 1; ++ cfb->fb.fix.ypanstep = 1; ++ cfb->fb.fix.ywrapstep = 0; ++ cfb->fb.fix.accel = FB_ACCEL_NONE; ++ ++ cfb->fb.var.nonstd = 0; ++ cfb->fb.var.activate = FB_ACTIVATE_NOW; ++ cfb->fb.var.height = -1; ++ cfb->fb.var.width = -1; ++ cfb->fb.var.accel_flags = FB_ACCELF_TEXT; ++ ++ cfb->fb.fbops = &jzfb_ops; ++ cfb->fb.flags = FBINFO_FLAG_DEFAULT; ++ ++ cfb->fb.pseudo_palette = (void *)(cfb + 1); ++ ++ switch (jzfb.bpp) { ++ case 1: ++ fb_alloc_cmap(&cfb->fb.cmap, 4, 0); ++ break; ++ case 2: ++ fb_alloc_cmap(&cfb->fb.cmap, 8, 0); ++ break; ++ case 4: ++ fb_alloc_cmap(&cfb->fb.cmap, 32, 0); ++ break; ++ case 8: ++ ++ default: ++ fb_alloc_cmap(&cfb->fb.cmap, 256, 0); ++ break; ++ } ++ dprintk("fb_alloc_cmap,fb.cmap.len:%d....\n", cfb->fb.cmap.len); ++ ++ return cfb; ++} ++ ++/* ++ * Map screen memory ++ */ ++static int jzfb_map_smem(struct lcd_cfb_info *cfb) ++{ ++ struct page * map = NULL; ++ unsigned char *tmp; ++ unsigned int page_shift, needroom, t; ++ ++ t = jzfb.bpp; ++ if (jzfb.bpp == 15) ++ t = 16; ++ if (jzfb.bpp == 18 || jzfb.bpp == 24) ++ t = 32; ++ needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h; ++ ++ for (page_shift = 0; page_shift < 12; page_shift++) ++ if ((PAGE_SIZE << page_shift) >= needroom) ++ break; ++ ++ slcd_palette = (unsigned char *)__get_free_pages(GFP_KERNEL, 0); ++ slcd_frame = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); ++ if ((!slcd_palette) || (!slcd_frame)) ++ return -ENOMEM; ++ ++ memset((void *)slcd_palette, 0, PAGE_SIZE); ++ memset((void *)slcd_frame, 0, PAGE_SIZE << page_shift); ++ ++ map = virt_to_page(slcd_palette); ++ set_bit(PG_reserved, &map->flags); ++ ++ for (tmp=(unsigned char *)slcd_frame; ++ tmp < slcd_frame + (PAGE_SIZE << page_shift); ++ tmp += PAGE_SIZE) { ++ map = virt_to_page(tmp); ++ set_bit(PG_reserved, &map->flags); ++ } ++ ++ cfb->fb.fix.smem_start = virt_to_phys((void *)slcd_frame); ++ ++ cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift); ++ ++ cfb->fb.screen_base = ++ (unsigned char *)(((unsigned int)slcd_frame & 0x1fffffff) | 0xa0000000); ++ ++ if (!cfb->fb.screen_base) { ++ printk("%s: unable to map screen memory\n", cfb->fb.fix.id); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void jzfb_free_fb_info(struct lcd_cfb_info *cfb) ++{ ++ if (cfb) { ++ fb_alloc_cmap(&cfb->fb.cmap, 0, 0); ++ kfree(cfb); ++ } ++} ++ ++static void jzfb_unmap_smem(struct lcd_cfb_info *cfb) ++{ ++ struct page * map = NULL; ++ unsigned char *tmp; ++ unsigned int page_shift, needroom, t; ++ ++ t = jzfb.bpp; ++ if (jzfb.bpp == 18 || jzfb.bpp == 24) ++ t = 32; ++ if (jzfb.bpp == 15) ++ t = 16; ++ needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h; ++ for (page_shift = 0; page_shift < 12; page_shift++) ++ if ((PAGE_SIZE << page_shift) >= needroom) ++ break; ++ ++ if (cfb && cfb->fb.screen_base) { ++ iounmap(cfb->fb.screen_base); ++ cfb->fb.screen_base = NULL; ++ release_mem_region(cfb->fb.fix.smem_start, ++ cfb->fb.fix.smem_len); ++ } ++ ++ if (slcd_palette) { ++ map = virt_to_page(slcd_palette); ++ clear_bit(PG_reserved, &map->flags); ++ free_pages((int)slcd_palette, 0); ++ } ++ ++ if (slcd_frame) { ++ ++ for (tmp=(unsigned char *)slcd_frame; ++ tmp < slcd_frame + (PAGE_SIZE << page_shift); ++ tmp += PAGE_SIZE) { ++ map = virt_to_page(tmp); ++ clear_bit(PG_reserved, &map->flags); ++ } ++ ++ free_pages((int)slcd_frame, page_shift); ++ } ++} ++ ++static void slcd_descriptor_init(void) ++{ ++ int i; ++ int frm_size, pal_size; ++ unsigned int next; ++ unsigned int slcd_frame_src_phys_addr, slcd_palette_src_phys_addr, slcd_dma_dst_phys_addr; ++ ++ i = jzfb.bpp; ++ if (i == 18 || i == 24) ++ i = 32; ++ if (i == 15) ++ i = 16; ++ ++ switch (jzfb.bpp) { ++ case 1: ++ pal_size = 4; ++ break; ++ case 2: ++ pal_size = 8; ++ break; ++ case 4: ++ pal_size = 32; ++ break; ++ case 8: ++ default: ++ pal_size = 512; ++ } ++ ++ frm_size = jzfb.w * jzfb.h * jzfb.bpp / 8; ++ ++ /*Offset of next descriptor*/ ++ slcd_frame_desc_phys_addr = (dma_addr_t)CPHYSADDR((unsigned long)(&slcd_frame_desc)); ++ slcd_palette_desc_phys_addr = (dma_addr_t)CPHYSADDR((unsigned long)(&slcd_palette_desc)); ++ ++ /*Soure address and Target address*/ ++ slcd_palette_src_phys_addr = (unsigned int)virt_to_phys(slcd_palette); ++ slcd_frame_src_phys_addr = (unsigned int)virt_to_phys(slcd_frame); ++ slcd_dma_dst_phys_addr = (unsigned int)CPHYSADDR(SLCD_FIFO); ++ next = slcd_frame_desc_phys_addr >> 4; ++ ++ /* Prepare Palette Descriptor */ ++ slcd_palette_desc.dcmd = DMAC_DCMD_SAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 ++ | DMAC_DCMD_DWDH_16 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_TM | DMAC_DCMD_DES_V ++ | DMAC_DCMD_DES_VIE | DMAC_DCMD_LINK; ++ switch (slcd_palette_desc.dcmd & DMAC_DCMD_DS_MASK) { ++ case DMAC_DCMD_DS_32BYTE: ++ pal_size /= 32; ++ break; ++ case DMAC_DCMD_DS_16BYTE: ++ pal_size /= 16; ++ break; ++ case DMAC_DCMD_DS_32BIT: ++ pal_size /= 4; ++ break; ++ case DMAC_DCMD_DS_16BIT: ++ pal_size /= 2; ++ break; ++ case DMAC_DCMD_DS_8BIT: ++ default: ++ break; ++ } ++ ++ slcd_palette_desc.dsadr = (unsigned int)virt_to_phys(slcd_palette); /* DMA source address */ ++ slcd_palette_desc.dtadr = (unsigned int)CPHYSADDR(SLCD_FIFO); /* DMA target address */ ++ slcd_palette_desc.ddadr = (volatile unsigned int)((next << 24) | (pal_size & 0xffffff)); /* offset and size*/ ++ dma_cache_wback((unsigned long)(&slcd_palette_desc), 16); ++ ++ /*Prepare Frame Descriptor in memory*/ ++ switch (jzfb.bpp) { ++ case 8 ... 16: ++ slcd_frame_desc.dcmd = DMAC_DCMD_SAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 ++ | DMAC_DCMD_DWDH_16 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_TM | DMAC_DCMD_DES_V ++ | DMAC_DCMD_DES_VIE | DMAC_DCMD_LINK; ++ break; ++ ++ case 17 ... 32: ++ slcd_frame_desc.dcmd = DMAC_DCMD_SAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 ++ | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_TM | DMAC_DCMD_DES_V ++ | DMAC_DCMD_DES_VIE | DMAC_DCMD_LINK; ++ break; ++ } ++ switch (slcd_frame_desc.dcmd & DMAC_DCMD_DS_MASK) { ++ case DMAC_DCMD_DS_32BYTE: ++ frm_size /= 32; ++ break; ++ case DMAC_DCMD_DS_16BYTE: ++ frm_size /= 16; ++ break; ++ case DMAC_DCMD_DS_32BIT: ++ frm_size /= 4; ++ break; ++ case DMAC_DCMD_DS_16BIT: ++ frm_size /= 2; ++ break; ++ case DMAC_DCMD_DS_8BIT: ++ default: ++ break; ++ } ++ ++ slcd_frame_desc.dsadr = slcd_frame_src_phys_addr; /* DMA source address */ ++ slcd_frame_desc.dtadr = slcd_dma_dst_phys_addr; /* DMA target address */ ++ slcd_frame_desc.ddadr = (volatile unsigned int)((next << 24) | (frm_size & 0xffffff)); /* offset and size*/ ++ dma_cache_wback((unsigned long)(&slcd_frame_desc), 16); ++} ++ ++void slcd_hw_init(void) ++{ ++ unsigned int val, pclk; ++ int pll_div; ++ ++ REG_LCD_CFG &= ~LCD_CFG_LCDPIN_MASK; ++ REG_LCD_CFG |= LCD_CFG_LCDPIN_SLCD; ++ ++ if ((jzfb.bpp == 18) | (jzfb.bpp == 24)) ++ jzfb.bpp = 32; ++ ++ /* Configure SLCD module for initialize smart lcd registers*/ ++ switch (jzfb.bus) { ++ case 8: ++ REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_8_x2 ++ | SLCD_CFG_CWIDTH_8BIT | SLCD_CFG_CS_ACTIVE_LOW ++ | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING ++ | SLCD_CFG_TYPE_PARALLEL; ++ __gpio_as_slcd_8bit(); ++ break; ++ case 9: ++ REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_8_x2 ++ | SLCD_CFG_CWIDTH_8BIT | SLCD_CFG_CS_ACTIVE_LOW ++ | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING ++ | SLCD_CFG_TYPE_PARALLEL; ++ __gpio_as_slcd_9bit(); ++ break; ++ case 16: ++ REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_16 ++ | SLCD_CFG_CWIDTH_16BIT | SLCD_CFG_CS_ACTIVE_LOW ++ | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING ++ | SLCD_CFG_TYPE_PARALLEL; ++ __gpio_as_slcd_16bit(); ++ break; ++ case 18: ++ REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_18 ++ | SLCD_CFG_CWIDTH_18BIT | SLCD_CFG_CS_ACTIVE_LOW ++ | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING ++ | SLCD_CFG_TYPE_PARALLEL; ++ __gpio_as_slcd_18bit(); ++ break; ++ default: ++ printk("Error: Don't support BUS %d!\n", jzfb.bus); ++ break; ++ } ++ ++ REG_SLCD_CTRL = SLCD_CTRL_DMA_EN; ++ __cpm_stop_lcd(); ++ pclk = jzfb.pclk; ++ pll_div = ( REG_CPM_CPCCR & CPM_CPCCR_PCS ); /* clock source,0:pllout/2 1: pllout */ ++ pll_div = pll_div ? 1 : 2 ; ++ val = ( __cpm_get_pllout()/pll_div ) / pclk; ++ val--; ++ if ( val > 0x1ff ) { ++ printk("CPM_LPCDR too large, set it to 0x1ff\n"); ++ val = 0x1ff; ++ } ++ __cpm_set_pixdiv(val); ++ ++ REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */ ++ ++ jz_clocks.pixclk = __cpm_get_pixclk(); ++ jz_clocks.lcdclk = __cpm_get_lcdclk(); ++ printk("SLCDC: PixClock:%d LcdClock:%d\n", ++ jz_clocks.pixclk, jz_clocks.lcdclk); ++ ++ __cpm_start_lcd(); ++ udelay(1000); ++ __slcd_display_pin_init(); ++ __slcd_special_on(); ++ ++ /* Configure SLCD module for transfer data to smart lcd GRAM*/ ++ switch (jzfb.bus) { ++ case 8: ++ switch (jzfb.bpp) { ++ case 8: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; ++ break; ++ case 15: ++ case 16: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; ++ break; ++ case 17 ... 32: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; ++ break; ++ default: ++ printk("The BPP %d is not supported\n", jzfb.bpp); ++ break; ++ } ++ break; ++ case 9: ++ switch (jzfb.bpp) { ++ case 8: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; ++ break; ++ case 15 ... 16: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; ++ break; ++ case 17 ... 32: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_9_x2; ++ break; ++ default: ++ printk("The BPP %d is not supported\n", jzfb.bpp); ++ break; ++ } ++ break; ++ case 16: ++ switch (jzfb.bpp) { ++ case 8: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; ++ break; ++ case 15 ... 16: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_16; ++ break; ++ case 17 ... 32: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; ++ break; ++ default: ++ printk("The BPP %d is not supported\n", jzfb.bpp); ++ break; ++ } ++ break; ++ case 18: ++ switch (jzfb.bpp) { ++ case 8: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; ++ break; ++ case 15: ++ case 16: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_16; ++ break; ++ case 17 ... 32: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_18; ++ break; ++ default: ++ printk("The BPP %d is not supported\n", jzfb.bpp); ++ break; ++ } ++ break; ++ default: ++ printk("Error: The BUS %d is not supported\n", jzfb.bus); ++ break; ++ } ++ dprintk("SLCD_CFG=0x%x\n", REG_SLCD_CFG); ++} ++ ++static irqreturn_t slcd_dma_irq(int irq, void *dev_id) ++{ ++ ++ if (__dmac_channel_transmit_halt_detected(dma_chan)) { ++ dprintk("DMA HALT\n"); ++ __dmac_channel_clear_transmit_halt(dma_chan); ++ } ++ ++ if (__dmac_channel_address_error_detected(dma_chan)) { ++ dprintk("DMA ADDR ERROR\n"); ++ __dmac_channel_clear_address_error(dma_chan); ++ } ++ ++ if (__dmac_channel_descriptor_invalid_detected(dma_chan)) { ++ dprintk("DMA DESC INVALID\n"); ++ __dmac_channel_clear_descriptor_invalid(dma_chan); ++ } ++ ++ if (__dmac_channel_count_terminated_detected(dma_chan)) { ++ dprintk("DMA CT\n"); ++ __dmac_channel_clear_count_terminated(dma_chan); ++ if(is_set_reg){ ++ printk("Close DMAC_DCMD_LINK \n"); ++ REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK; ++ } ++ if (non_link_desp) { ++ printk("Close DMAC_DCMD_LINK \n"); ++ /*Set to Non-Link Descriptor*/ ++ REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK; ++ } ++ } ++ ++ if (__dmac_channel_transmit_end_detected(dma_chan)) { ++ printk("DMA TT\n"); ++ __dmac_channel_clear_transmit_end(dma_chan); ++ if (non_link_desp) { ++ slcd_frame_desc.dcmd |= DMAC_DCMD_TIE; ++ slcd_frame_desc.dcmd &= ~DMAC_DCMD_LINK; ++ dma_cache_wback((unsigned long)(&slcd_frame_desc), 16); ++ non_link_desp = 0; ++ } ++ if (is_set_reg) { ++ is_set_reg = 0; ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_DMAC_DMACR &= ~DMAC_DMACR_DMAE; /* disable DMA */ ++ REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ ++ REG_SLCD_CTRL = 0; ++ ++ /* ++ *add operation here ++ */ ++ Mcupanel_RegSet(reg_buf.cmd, reg_buf.data); ++ Mcupanel_Command(0x0022);/*Write Data to GRAM */ ++ mdelay(100); ++ REG_SLCD_CTRL = SLCD_CTRL_DMA_EN; ++ REG_DMAC_DMACR = DMAC_DMACR_DMAE; ++ REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_EN; ++ __dmac_channel_set_doorbell(dma_chan); ++ } ++ } ++ return IRQ_HANDLED; ++} ++ ++static int slcd_dma_init(void) ++{ ++ /* Request DMA channel and setup irq handler */ ++ dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", slcd_dma_irq, 0, NULL); ++ if (dma_chan < 0) { ++ printk("Request DMA Failed\n"); ++ return -1; ++ } ++ printk("DMA channel %d is requested by SLCD!\n", dma_chan); ++ ++ /*Init the SLCD DMA and Enable*/ ++ REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_SLCD; ++ REG_DMAC_DMACR = DMAC_DMACR_DMAE; ++ REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_EN; /*Descriptor Transfer*/ ++ ++ if (jzfb.bpp <= 8) ++ REG_DMAC_DDA(dma_chan) = slcd_palette_desc_phys_addr; ++ else ++ REG_DMAC_DDA(dma_chan) = slcd_frame_desc_phys_addr; ++ ++ /* DMA doorbell set -- start DMA now ... */ ++ __dmac_channel_set_doorbell(dma_chan); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++/* ++ * Suspend the LCDC. ++ */ ++static int jzfb_suspend(void) ++{ ++ ++ __slcd_close_backlight(); ++ __dmac_disable_channel(dma_chan); ++ __slcd_dma_disable(); /* Quick Disable */ ++ __slcd_special_off(); ++ __cpm_stop_lcd(); ++ return 0; ++} ++ ++/* ++ * Resume the LCDC. ++ */ ++ ++static int jzfb_resume(void) ++{ ++ __cpm_start_lcd(); ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ switch (jzfb.bpp) { ++ case 8: ++ /* DATA 8-bit once*/ ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; ++ break; ++ case 15: ++ case 16: ++ case 18: ++ case 24: ++ case 32: ++ /* DATA 8-bit twice*/ ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; ++ break; ++ default: ++ REG_SLCD_CFG = SLCD_CFG_DWIDTH_8_x2; ++ break; ++ } ++ __slcd_display_pin_init(); ++ __slcd_special_on(); ++ ++ if (jzfb.bpp == 32) { ++ /* DATA 8-bit three time*/ ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; ++ } ++ __slcd_dma_enable(); ++ udelay(100); ++ __dmac_enable_channel(dma_chan); ++ __dmac_channel_set_doorbell(dma_chan); ++ mdelay(200); ++ __slcd_set_backlight_level(80); ++ return 0; ++} ++ ++/* ++ * Power management hook. Note that we won't be called from IRQ context, ++ * unlike the blank functions above, so we may sleep. ++ */ ++static int jzslcd_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) ++{ ++ int ret; ++ struct lcd_cfb_info *cfb = pm_dev->data; ++ ++ if (!cfb) return -EINVAL; ++ ++ switch (req) { ++ case PM_SUSPEND: ++ ret = jzfb_suspend(); ++ break; ++ ++ case PM_RESUME: ++ ret = jzfb_resume(); ++ break; ++ ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++#else ++#define jzfb_suspend NULL ++#define jzfb_resume NULL ++#endif /* CONFIG_PM */ ++static int __init jzslcd_fb_init(void) ++{ ++ ++ struct lcd_cfb_info *cfb; ++ int err = 0; ++ ++ /*the parameters of slcd*/ ++ cfb = jzfb_alloc_fb_info(); ++ if (!cfb) ++ goto failed; ++ ++ err = jzfb_map_smem(cfb); ++ if (err) ++ goto failed; ++ jzfb_set_var(&cfb->fb.var, -1, &cfb->fb); ++ ++ slcd_hw_init(); ++ ++ err = register_framebuffer(&cfb->fb); ++ if (err < 0) { ++ printk("jzslcd_fb_init(): slcd register framebuffer err.\n"); ++ goto failed; ++ } ++ ++ printk("fb%d: %s frame buffer device, using %dK of video memory\n", ++ cfb->fb.node, cfb->fb.fix.id, cfb->fb.fix.smem_len>>10); ++ ++ slcd_descriptor_init(); ++ err = slcd_dma_init(); ++ if (err != 0) { ++ printk("SLCD Init DMA Fail!\n"); ++ return err; ++ } ++ mdelay(100); ++ __slcd_set_backlight_level(80); ++ ++#ifdef CONFIG_PM ++ /* ++ * Note that the console registers this as well, but we want to ++ * power down the display prior to sleeping. ++ */ ++//struct pm_dev __deprecated *pm_register(pm_dev_t type, unsigned long id, pm_callback callback); ++ ++ cfb->pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, jzslcd_pm_callback); ++ if (cfb->pm) ++ cfb->pm->data = cfb; ++ ++#endif ++ return 0; ++ ++failed: ++ jzfb_unmap_smem(cfb); ++ jzfb_free_fb_info(cfb); ++ ++ return err; ++} ++ ++#if 0 ++static int jzfb_remove(struct device *dev) ++{ ++ struct lcd_cfb_info *cfb = dev_get_drvdata(dev); ++ jzfb_unmap_smem(cfb); ++ jzfb_free_fb_info(cfb); ++ return 0; ++} ++#endif ++ ++#if 0 ++static struct device_driver jzfb_driver = { ++ .name = "jz-slcd", ++ .bus = &platform_bus_type, ++ .probe = jzfb_probe, ++ .remove = jzfb_remove, ++ .suspend = jzfb_suspend, ++ .resume = jzfb_resume, ++}; ++#endif ++ ++static void __exit jzslcd_fb_cleanup(void) ++{ ++ //driver_unregister(&jzfb_driver); ++ //jzfb_remove(); ++} ++ ++module_init(jzslcd_fb_init); ++module_exit(jzslcd_fb_cleanup); ++ ++MODULE_DESCRIPTION("JzSOC SLCD Controller driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/drivers/video/jz4740_slcd.h linux-2.6.24.7/drivers/video/jz4740_slcd.h +--- linux-2.6.24.7.old/drivers/video/jz4740_slcd.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/video/jz4740_slcd.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,376 @@ ++/* ++ * linux/drivers/video/jzslcd.h -- Ingenic On-Chip SLCD frame buffer device ++ * ++ * Copyright (C) 2005-2007, Ingenic Semiconductor Inc. ++ * ++ * 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 __JZSLCD_H__ ++#define __JZSLCD_H__ ++ ++#define UINT16 unsigned short ++#define UINT32 unsigned int ++ ++#define NR_PALETTE 256 ++/* Jz LCDFB supported I/O controls. */ ++#define FBIOSETBACKLIGHT 0x4688 ++#define FBIODISPON 0x4689 ++#define FBIODISPOFF 0x468a ++#define FBIORESET 0x468b ++#define FBIOPRINT_REG 0x468c ++#define FBIO_REFRESH_ALWAYS 0x468d ++#define FBIO_REFRESH_EVENTS 0x468e ++#define FBIO_DO_REFRESH 0x468f ++#define FBIO_SET_REG 0x4690 ++ ++#ifdef CONFIG_JZ_SLCD_LGDP4551 ++#define PIN_CS_N (32*2+18) /* Chip select :SLCD_WR: GPC18 */ ++#define PIN_RESET_N (32*2+21) /* LCD reset :SLCD_RST: GPC21*/ ++#define PIN_RS_N (32*2+19) ++ ++#define __slcd_special_pin_init() \ ++do { \ ++ __gpio_as_output(PIN_CS_N); \ ++ __gpio_as_output(PIN_RESET_N); \ ++ __gpio_clear_pin(PIN_CS_N); /* Clear CS */\ ++ mdelay(100); \ ++} while(0) ++ ++#define __slcd_special_on() \ ++do { /* RESET# */ \ ++ __gpio_set_pin(PIN_RESET_N); \ ++ mdelay(10); \ ++ __gpio_clear_pin(PIN_RESET_N); \ ++ mdelay(10); \ ++ __gpio_set_pin(PIN_RESET_N); \ ++ mdelay(100); \ ++ Mcupanel_RegSet(0x0015,0x0050); \ ++ Mcupanel_RegSet(0x0011,0x0000); \ ++ Mcupanel_RegSet(0x0010,0x3628); \ ++ Mcupanel_RegSet(0x0012,0x0002); \ ++ Mcupanel_RegSet(0x0013,0x0E47); \ ++ udelay(100); \ ++ Mcupanel_RegSet(0x0012,0x0012); \ ++ udelay(100); \ ++ Mcupanel_RegSet(0x0010,0x3620); \ ++ Mcupanel_RegSet(0x0013,0x2E47); \ ++ udelay(50); \ ++ Mcupanel_RegSet(0x0030,0x0000); \ ++ Mcupanel_RegSet(0x0031,0x0502); \ ++ Mcupanel_RegSet(0x0032,0x0307); \ ++ Mcupanel_RegSet(0x0033,0x0304); \ ++ Mcupanel_RegSet(0x0034,0x0004); \ ++ Mcupanel_RegSet(0x0035,0x0401); \ ++ Mcupanel_RegSet(0x0036,0x0707); \ ++ Mcupanel_RegSet(0x0037,0x0303); \ ++ Mcupanel_RegSet(0x0038,0x1E02); \ ++ Mcupanel_RegSet(0x0039,0x1E02); \ ++ Mcupanel_RegSet(0x0001,0x0000); \ ++ Mcupanel_RegSet(0x0002,0x0300); \ ++ if (jzfb.bpp == 16) \ ++ Mcupanel_RegSet(0x0003,0x10B8); /*8-bit system interface two transfers ++ up:0x10B8 down:0x1088 left:0x1090 right:0x10a0*/ \ ++ else \ ++ if (jzfb.bpp == 32)\ ++ Mcupanel_RegSet(0x0003,0xD0B8);/*8-bit system interface three transfers,666 ++ up:0xD0B8 down:0xD088 left:0xD090 right:0xD0A0*/ \ ++ Mcupanel_RegSet(0x0008,0x0204);\ ++ Mcupanel_RegSet(0x000A,0x0008);\ ++ Mcupanel_RegSet(0x0060,0x3100);\ ++ Mcupanel_RegSet(0x0061,0x0001);\ ++ Mcupanel_RegSet(0x0090,0x0052);\ ++ Mcupanel_RegSet(0x0092,0x000F);\ ++ Mcupanel_RegSet(0x0093,0x0001);\ ++ Mcupanel_RegSet(0x009A,0x0008);\ ++ Mcupanel_RegSet(0x00A3,0x0010);\ ++ Mcupanel_RegSet(0x0050,0x0000);\ ++ Mcupanel_RegSet(0x0051,0x00EF);\ ++ Mcupanel_RegSet(0x0052,0x0000);\ ++ Mcupanel_RegSet(0x0053,0x018F);\ ++ /*===Display_On_Function=== */ \ ++ Mcupanel_RegSet(0x0007,0x0001);\ ++ Mcupanel_RegSet(0x0007,0x0021);\ ++ Mcupanel_RegSet(0x0007,0x0023);\ ++ Mcupanel_RegSet(0x0007,0x0033);\ ++ Mcupanel_RegSet(0x0007,0x0133);\ ++ Mcupanel_Command(0x0022);/*Write Data to GRAM */ \ ++ udelay(1); \ ++ Mcupanel_SetAddr(0,0); \ ++ mdelay(100); \ ++} while (0) ++ ++#define __slcd_special_off() \ ++do { \ ++} while(0) ++#endif /*CONFIG_JZ_SLCD_LGDP4551_xxBUS*/ ++ ++#ifdef CONFIG_JZ_SLCD_SPFD5420A ++ ++ //#define PIN_CS_N (32*2+18) // Chip select //GPC18; ++#define PIN_CS_N (32*2+22) // Chip select //GPC18; ++#define PIN_RESET_N (32*1+18) // LCD reset //GPB18; ++#define PIN_RS_N (32*2+19) // LCD RS //GPC19; ++#define PIN_POWER_N (32*3+0) //Power off //GPD0; ++#define PIN_FMARK_N (32*3+1) //fmark //GPD1; ++ ++#define GAMMA() \ ++do { \ ++ Mcupanel_RegSet(0x0300,0x0101); \ ++ Mcupanel_RegSet(0x0301,0x0b27); \ ++ Mcupanel_RegSet(0x0302,0x132a); \ ++ Mcupanel_RegSet(0x0303,0x2a13); \ ++ Mcupanel_RegSet(0x0304,0x270b); \ ++ Mcupanel_RegSet(0x0305,0x0101); \ ++ Mcupanel_RegSet(0x0306,0x1205); \ ++ Mcupanel_RegSet(0x0307,0x0512); \ ++ Mcupanel_RegSet(0x0308,0x0005); \ ++ Mcupanel_RegSet(0x0309,0x0003); \ ++ Mcupanel_RegSet(0x030a,0x0f04); \ ++ Mcupanel_RegSet(0x030b,0x0f00); \ ++ Mcupanel_RegSet(0x030c,0x000f); \ ++ Mcupanel_RegSet(0x030d,0x040f); \ ++ Mcupanel_RegSet(0x030e,0x0300); \ ++ Mcupanel_RegSet(0x030f,0x0500); \ ++ /*** secorrect gamma2 ***/ \ ++ Mcupanel_RegSet(0x0400,0x3500); \ ++ Mcupanel_RegSet(0x0401,0x0001); \ ++ Mcupanel_RegSet(0x0404,0x0000); \ ++ Mcupanel_RegSet(0x0500,0x0000); \ ++ Mcupanel_RegSet(0x0501,0x0000); \ ++ Mcupanel_RegSet(0x0502,0x0000); \ ++ Mcupanel_RegSet(0x0503,0x0000); \ ++ Mcupanel_RegSet(0x0504,0x0000); \ ++ Mcupanel_RegSet(0x0505,0x0000); \ ++ Mcupanel_RegSet(0x0600,0x0000); \ ++ Mcupanel_RegSet(0x0606,0x0000); \ ++ Mcupanel_RegSet(0x06f0,0x0000); \ ++ Mcupanel_RegSet(0x07f0,0x5420); \ ++ Mcupanel_RegSet(0x07f3,0x288a); \ ++ Mcupanel_RegSet(0x07f4,0x0022); \ ++ Mcupanel_RegSet(0x07f5,0x0001); \ ++ Mcupanel_RegSet(0x07f0,0x0000); \ ++} while(0) ++ ++#define __slcd_special_on() \ ++do { \ ++ __gpio_set_pin(PIN_RESET_N); \ ++ mdelay(10); \ ++ __gpio_clear_pin(PIN_RESET_N); \ ++ mdelay(10); \ ++ __gpio_set_pin(PIN_RESET_N); \ ++ mdelay(100); \ ++ if (jzfb.bus == 18) {\ ++ Mcupanel_RegSet(0x0606,0x0000); \ ++ udelay(10); \ ++ Mcupanel_RegSet(0x0007,0x0001); \ ++ udelay(10); \ ++ Mcupanel_RegSet(0x0110,0x0001); \ ++ udelay(10); \ ++ Mcupanel_RegSet(0x0100,0x17b0); \ ++ Mcupanel_RegSet(0x0101,0x0147); \ ++ Mcupanel_RegSet(0x0102,0x019d); \ ++ Mcupanel_RegSet(0x0103,0x8600); \ ++ Mcupanel_RegSet(0x0281,0x0010); \ ++ udelay(10); \ ++ Mcupanel_RegSet(0x0102,0x01bd); \ ++ udelay(10); \ ++ /************initial************/\ ++ Mcupanel_RegSet(0x0000,0x0000); \ ++ Mcupanel_RegSet(0x0001,0x0000); \ ++ Mcupanel_RegSet(0x0002,0x0400); \ ++ Mcupanel_RegSet(0x0003,0x1288); /*up:0x1288 down:0x12B8 left:0x1290 right:0x12A0*/ \ ++ Mcupanel_RegSet(0x0006,0x0000); \ ++ Mcupanel_RegSet(0x0008,0x0503); \ ++ Mcupanel_RegSet(0x0009,0x0001); \ ++ Mcupanel_RegSet(0x000b,0x0010); \ ++ Mcupanel_RegSet(0x000c,0x0000); \ ++ Mcupanel_RegSet(0x000f,0x0000); \ ++ Mcupanel_RegSet(0x0007,0x0001); \ ++ Mcupanel_RegSet(0x0010,0x0010); \ ++ Mcupanel_RegSet(0x0011,0x0202); \ ++ Mcupanel_RegSet(0x0012,0x0300); \ ++ Mcupanel_RegSet(0x0020,0x021e); \ ++ Mcupanel_RegSet(0x0021,0x0202); \ ++ Mcupanel_RegSet(0x0022,0x0100); \ ++ Mcupanel_RegSet(0x0090,0x0000); \ ++ Mcupanel_RegSet(0x0092,0x0000); \ ++ Mcupanel_RegSet(0x0100,0x16b0); \ ++ Mcupanel_RegSet(0x0101,0x0147); \ ++ Mcupanel_RegSet(0x0102,0x01bd); \ ++ Mcupanel_RegSet(0x0103,0x2c00); \ ++ Mcupanel_RegSet(0x0107,0x0000); \ ++ Mcupanel_RegSet(0x0110,0x0001); \ ++ Mcupanel_RegSet(0x0210,0x0000); \ ++ Mcupanel_RegSet(0x0211,0x00ef); \ ++ Mcupanel_RegSet(0x0212,0x0000); \ ++ Mcupanel_RegSet(0x0213,0x018f); \ ++ Mcupanel_RegSet(0x0280,0x0000); \ ++ Mcupanel_RegSet(0x0281,0x0001); \ ++ Mcupanel_RegSet(0x0282,0x0000); \ ++ GAMMA(); \ ++ Mcupanel_RegSet(0x0007,0x0173); \ ++ } else { \ ++ Mcupanel_RegSet(0x0600, 0x0001); /*soft reset*/ \ ++ mdelay(10); \ ++ Mcupanel_RegSet(0x0600, 0x0000); /*soft reset*/ \ ++ mdelay(10); \ ++ Mcupanel_RegSet(0x0606, 0x0000); /*i80-i/F Endian Control*/ \ ++ /*===User setting=== */ \ ++ Mcupanel_RegSet(0x0001, 0x0000);/* Driver Output Control-----0x0100 SM(bit10) | 0x400*/ \ ++ Mcupanel_RegSet(0x0002, 0x0100); /*LCD Driving Wave Control 0x0100 */ \ ++ if (jzfb.bpp == 16) \ ++ Mcupanel_RegSet(0x0003, 0x50A8);/*Entry Mode 0x1030*/ \ ++ else /*bpp = 18*/ \ ++ Mcupanel_RegSet(0x0003, 0x1010 | 0xC8); /*Entry Mode 0x1030*/ \ ++ /*#endif */ \ ++ Mcupanel_RegSet(0x0006, 0x0000); /*Outline Sharpening Control*/\ ++ Mcupanel_RegSet(0x0008, 0x0808); /*Sets the number of lines for front/back porch period*/\ ++ Mcupanel_RegSet(0x0009, 0x0001); /*Display Control 3 */\ ++ Mcupanel_RegSet(0x000B, 0x0010); /*Low Power Control*/\ ++ Mcupanel_RegSet(0x000C, 0x0000); /*External Display Interface Control 1 /*0x0001*/\ ++ Mcupanel_RegSet(0x000F, 0x0000); /*External Display Interface Control 2 */\ ++ Mcupanel_RegSet(0x0400, 0xB104);/*Base Image Number of Line---GS(bit15) | 0x8000*/ \ ++ Mcupanel_RegSet(0x0401, 0x0001); /*Base Image Display 0x0001*/\ ++ Mcupanel_RegSet(0x0404, 0x0000); /*Base Image Vertical Scroll Control 0x0000*/\ ++ Mcupanel_RegSet(0x0500, 0x0000); /*Partial Image 1: Display Position*/\ ++ Mcupanel_RegSet(0x0501, 0x0000); /*RAM Address (Start Line Address) */\ ++ Mcupanel_RegSet(0x0502, 0x018f); /*RAM Address (End Line Address) */ \ ++ Mcupanel_RegSet(0x0503, 0x0000); /*Partial Image 2: Display Position RAM Address*/\ ++ Mcupanel_RegSet(0x0504, 0x0000); /*RAM Address (Start Line Address) */\ ++ Mcupanel_RegSet(0x0505, 0x0000); /*RAM Address (End Line Address)*/\ ++ /*Panel interface control===*/\ ++ Mcupanel_RegSet(0x0010, 0x0011); /*Division Ratio,Clocks per Line 14 */\ ++ mdelay(10); \ ++ Mcupanel_RegSet(0x0011, 0x0202); /*Division Ratio,Clocks per Line*/\ ++ Mcupanel_RegSet(0x0012, 0x0300); /*Sets low power VCOM drive period. */\ ++ mdelay(10); \ ++ Mcupanel_RegSet(0x0020, 0x021e); /*Panel Interface Control 4 */\ ++ Mcupanel_RegSet(0x0021, 0x0202); /*Panel Interface Control 5 */\ ++ Mcupanel_RegSet(0x0022, 0x0100); /*Panel Interface Control 6*/\ ++ Mcupanel_RegSet(0x0090, 0x0000); /*Frame Marker Control */\ ++ Mcupanel_RegSet(0x0092, 0x0000); /*MDDI Sub-display Control */\ ++ /*===Gamma setting=== */\ ++ Mcupanel_RegSet(0x0300, 0x0101); /*γ Control*/\ ++ Mcupanel_RegSet(0x0301, 0x0000); /*γ Control*/\ ++ Mcupanel_RegSet(0x0302, 0x0016); /*γ Control*/\ ++ Mcupanel_RegSet(0x0303, 0x2913); /*γ Control*/\ ++ Mcupanel_RegSet(0x0304, 0x260B); /*γ Control*/\ ++ Mcupanel_RegSet(0x0305, 0x0101); /*γ Control*/\ ++ Mcupanel_RegSet(0x0306, 0x1204); /*γ Control*/\ ++ Mcupanel_RegSet(0x0307, 0x0415); /*γ Control*/\ ++ Mcupanel_RegSet(0x0308, 0x0205); /*γ Control*/\ ++ Mcupanel_RegSet(0x0309, 0x0303); /*γ Control*/\ ++ Mcupanel_RegSet(0x030a, 0x0E05); /*γ Control*/\ ++ Mcupanel_RegSet(0x030b, 0x0D01); /*γ Control*/\ ++ Mcupanel_RegSet(0x030c, 0x010D); /*γ Control*/\ ++ Mcupanel_RegSet(0x030d, 0x050E); /*γ Control*/\ ++ Mcupanel_RegSet(0x030e, 0x0303); /*γ Control*/\ ++ Mcupanel_RegSet(0x030f, 0x0502); /*γ Control*/\ ++ /*===Power on sequence===*/\ ++ Mcupanel_RegSet(0x0007, 0x0001); /*Display Control 1*/\ ++ Mcupanel_RegSet(0x0110, 0x0001); /*Power supply startup enable bit*/\ ++ Mcupanel_RegSet(0x0112, 0x0060); /*Power Control 7*/\ ++ Mcupanel_RegSet(0x0100, 0x16B0); /*Power Control 1 */\ ++ Mcupanel_RegSet(0x0101, 0x0115); /*Power Control 2*/\ ++ Mcupanel_RegSet(0x0102, 0x0119); /*Starts VLOUT3,Sets the VREG1OUT.*/\ ++ mdelay(50); \ ++ Mcupanel_RegSet(0x0103, 0x2E00); /*set the amplitude of VCOM*/\ ++ mdelay(50);\ ++ Mcupanel_RegSet(0x0282, 0x0093);/*0x008E);/*0x0093); /*VCOMH voltage*/\ ++ Mcupanel_RegSet(0x0281, 0x000A); /*Selects the factor of VREG1OUT to generate VCOMH. */\ ++ Mcupanel_RegSet(0x0102, 0x01BE); /*Starts VLOUT3,Sets the VREG1OUT.*/\ ++ mdelay(10);\ ++ /*Address */\ ++ Mcupanel_RegSet(0x0210, 0x0000); /*Window Horizontal RAM Address Start*/\ ++ Mcupanel_RegSet(0x0211, 0x00ef); /*Window Horizontal RAM Address End*/\ ++ Mcupanel_RegSet(0x0212, 0x0000); /*Window Vertical RAM Address Start*/\ ++ Mcupanel_RegSet(0x0213, 0x018f); /*Window Vertical RAM Address End */\ ++ Mcupanel_RegSet(0x0200, 0x0000); /*RAM Address Set (Horizontal Address)*/\ ++ Mcupanel_RegSet(0x0201, 0x018f); /*RAM Address Set (Vertical Address)*/ \ ++ /*===Display_On_Function===*/\ ++ Mcupanel_RegSet(0x0007, 0x0021); /*Display Control 1 */\ ++ mdelay(50); /*40*/\ ++ Mcupanel_RegSet(0x0007, 0x0061); /*Display Control 1 */\ ++ mdelay(50); /*100*/\ ++ Mcupanel_RegSet(0x0007, 0x0173); /*Display Control 1 */\ ++ mdelay(50); /*300*/\ ++ }\ ++ Mcupanel_Command(0x0202); /*Write Data to GRAM */ \ ++ udelay(10);\ ++ Mcupanel_SetAddr(0,0);\ ++ udelay(100);\ ++} while(0) ++ ++#define __slcd_special_pin_init() \ ++do { \ ++ __gpio_as_output(PIN_CS_N); \ ++ __gpio_as_output(PIN_RESET_N); \ ++ __gpio_clear_pin(PIN_CS_N); /* Clear CS */ \ ++ __gpio_as_output(PIN_POWER_N); \ ++ mdelay(100); \ ++} while(0) ++ ++#endif /*CONFIG_JZ_SLCD_SPFD5420A*/ ++ ++#ifndef __slcd_special_pin_init ++#define __slcd_special_pin_init() ++#endif ++#ifndef __slcd_special_on ++#define __slcd_special_on() ++#endif ++#ifndef __slcd_special_off ++#define __slcd_special_off() ++#endif ++ ++/* ++ * Platform specific definition ++ */ ++#if defined(CONFIG_SOC_JZ4740) ++#if defined(CONFIG_JZ4740_PAVO) ++#define GPIO_PWM 123 /* GP_D27 */ ++#define PWM_CHN 4 /* pwm channel */ ++#define PWM_FULL 101 ++/* 100 level: 0,1,...,100 */ ++#define __slcd_set_backlight_level(n)\ ++do { \ ++ __gpio_as_output(32*3+27); \ ++ __gpio_set_pin(32*3+27); \ ++} while (0) ++ ++#define __slcd_close_backlight() \ ++do { \ ++ __gpio_as_output(GPIO_PWM); \ ++ __gpio_clear_pin(GPIO_PWM); \ ++} while (0) ++ ++#else ++ ++#define __slcd_set_backlight_level(n) ++#define __slcd_close_backlight() ++ ++#endif /* #if defined(CONFIG_MIPS_JZ4740_PAVO) */ ++ ++#define __slcd_display_pin_init() \ ++do { \ ++ __slcd_special_pin_init(); \ ++} while (0) ++ ++#define __slcd_display_on() \ ++do { \ ++ __slcd_special_on(); \ ++ __slcd_set_backlight_level(80); \ ++} while (0) ++ ++#define __slcd_display_off() \ ++do { \ ++ __slcd_special_off(); \ ++ __slcd_close_backlight(); \ ++} while (0) ++ ++#endif /* CONFIG_SOC_JZ4740 */ ++#endif /*__JZSLCD_H__*/ ++ +diff -urN linux-2.6.24.7.old/drivers/video/jz4750_lcd.c linux-2.6.24.7/drivers/video/jz4750_lcd.c +--- linux-2.6.24.7.old/drivers/video/jz4750_lcd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/video/jz4750_lcd.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,2139 @@ ++/* ++ * linux/drivers/video/jz4750_lcd.c -- Ingenic Jz4750 LCD frame buffer device ++ * ++ * Copyright (C) 2005-2008, Ingenic Semiconductor Inc. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * -------------------------------- ++ * NOTE: ++ * This LCD driver support TFT16 TFT32 LCD, not support STN and Special TFT LCD ++ * now. ++ * It seems not necessory to support STN and Special TFT. ++ * If it's necessary, update this driver in the future. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "console/fbcon.h" ++ ++#include "jz4750_lcd.h" ++#include "jz4750_tve.h" ++ ++#ifdef CONFIG_JZ4750_SLCD_KGM701A3_TFT_SPFD5420A ++#include "jz_kgm_spfd5420a.h" ++#endif ++ ++MODULE_DESCRIPTION("Jz4750 LCD Controller driver"); ++MODULE_AUTHOR("Wolfgang Wang, "); ++MODULE_LICENSE("GPL"); ++ ++ ++//#define DEBUG ++#undef DEBUG ++ ++#ifdef DEBUG ++#define dprintk(x...) printk(x) ++#define print_dbg(f, arg...) printk("dbg::" __FILE__ ",LINE(%d): " f "\n", __LINE__, ## arg) ++#else ++#define dprintk(x...) ++#define print_dbg(f, arg...) do {} while (0) ++#endif ++ ++#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) ++#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) ++#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) ++ ++struct lcd_cfb_info { ++ struct fb_info fb; ++ struct display_switch *dispsw; ++ signed int currcon; ++ int func_use_count; ++ ++ struct { ++ u16 red, green, blue; ++ } palette[NR_PALETTE]; ++#ifdef CONFIG_PM ++ struct pm_dev *pm; ++#endif ++}; ++ ++static struct lcd_cfb_info *jz4750fb_info; ++static struct jz4750_lcd_dma_desc *dma_desc_base; ++static struct jz4750_lcd_dma_desc *dma0_desc_palette, *dma0_desc0, *dma0_desc1, *dma1_desc0, *dma1_desc1; ++#define DMA_DESC_NUM 6 ++ ++static unsigned char *lcd_palette; ++static unsigned char *lcd_frame0; ++static unsigned char *lcd_frame1; ++ ++static struct jz4750_lcd_dma_desc *dma0_desc_cmd0, *dma0_desc_cmd; ++static unsigned char *lcd_cmdbuf ; ++ ++static void jz4750fb_set_mode( struct jz4750lcd_info * lcd_info ); ++static void jz4750fb_deep_set_mode( struct jz4750lcd_info * lcd_info ); ++ ++ ++ ++struct jz4750lcd_info jz4750_lcd_panel = { ++#if defined(CONFIG_JZ4750_LCD_SAMSUNG_LTP400WQF02) ++ .panel = { ++ .cfg = LCD_CFG_LCDPIN_LCD | LCD_CFG_RECOVER | /* Underrun recover */ ++ LCD_CFG_NEWDES | /* 8words descriptor */ ++ LCD_CFG_MODE_GENERIC_TFT | /* General TFT panel */ ++ LCD_CFG_MODE_TFT_18BIT | /* output 18bpp */ ++ LCD_CFG_HSP | /* Hsync polarity: active low */ ++ LCD_CFG_VSP, /* Vsync polarity: leading edge is falling edge */ ++ .slcd_cfg = 0, ++ .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ ++ 480, 272, 60, 41, 10, 2, 2, 2, 2, ++ }, ++ .osd = { ++ .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ ++// LCD_OSDC_ALPHAEN | /* enable alpha */ ++ LCD_OSDC_F0EN, /* enable Foreground0 */ ++ .osd_ctrl = 0, /* disable ipu, */ ++ .rgb_ctrl = 0, ++ .bgcolor = 0x000000, /* set background color Black */ ++ .colorkey0 = 0, /* disable colorkey */ ++ .colorkey1 = 0, /* disable colorkey */ ++ .alpha = 0xA0, /* alpha value */ ++ .ipu_restart = 0x80001000, /* ipu restart */ ++ .fg_change = FG_CHANGE_ALL, /* change all initially */ ++ .fg0 = {32, 0, 0, 480, 272}, /* bpp, x, y, w, h */ ++ .fg1 = {32, 0, 0, 480, 272}, /* bpp, x, y, w, h */ ++ }, ++#elif defined(CONFIG_JZ4750_LCD_AUO_A043FL01V2) ++ .panel = { ++ .cfg = LCD_CFG_LCDPIN_LCD | LCD_CFG_RECOVER | /* Underrun recover */ ++ LCD_CFG_NEWDES | /* 8words descriptor */ ++ LCD_CFG_MODE_GENERIC_TFT | /* General TFT panel */ ++ LCD_CFG_MODE_TFT_24BIT | /* output 18bpp */ ++ LCD_CFG_HSP | /* Hsync polarity: active low */ ++ LCD_CFG_VSP, /* Vsync polarity: leading edge is falling edge */ ++ .slcd_cfg = 0, ++ .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ ++ 480, 272, 60, 41, 10, 8, 4, 4, 2, ++ }, ++ .osd = { ++ .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ ++// LCD_OSDC_ALPHAEN | /* enable alpha */ ++// LCD_OSDC_F1EN | /* enable Foreground1 */ ++ LCD_OSDC_F0EN, /* enable Foreground0 */ ++ .osd_ctrl = 0, /* disable ipu, */ ++ .rgb_ctrl = 0, ++ .bgcolor = 0x000000, /* set background color Black */ ++ .colorkey0 = 0, /* disable colorkey */ ++ .colorkey1 = 0, /* disable colorkey */ ++ .alpha = 0xA0, /* alpha value */ ++ .ipu_restart = 0x80001000, /* ipu restart */ ++ .fg_change = FG_CHANGE_ALL, /* change all initially */ ++ .fg0 = {32, 0, 0, 480, 272}, /* bpp, x, y, w, h */ ++ .fg1 = {32, 0, 0, 480, 272}, /* bpp, x, y, w, h */ ++ }, ++#elif defined(CONFIG_JZ4750_LCD_TRULY_TFT_GG1P0319LTSW_W) ++ .panel = { ++// .cfg = LCD_CFG_LCDPIN_SLCD | LCD_CFG_RECOVER | /* Underrun recover*/ ++ .cfg = LCD_CFG_LCDPIN_SLCD | /* Underrun recover*/ ++ LCD_CFG_NEWDES | /* 8words descriptor */ ++ LCD_CFG_MODE_SLCD, /* TFT Smart LCD panel */ ++ .slcd_cfg = SLCD_CFG_DWIDTH_16BIT | SLCD_CFG_CWIDTH_16BIT | SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING | SLCD_CFG_TYPE_PARALLEL, ++ .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ ++ 240, 320, 60, 0, 0, 0, 0, 0, 0, ++ }, ++ .osd = { ++ .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ ++// LCD_OSDC_ALPHAEN | /* enable alpha */ ++// LCD_OSDC_F1EN | /* enable Foreground0 */ ++ LCD_OSDC_F0EN, /* enable Foreground0 */ ++ .osd_ctrl = 0, /* disable ipu, */ ++ .rgb_ctrl = 0, ++ .bgcolor = 0x000000, /* set background color Black */ ++ .colorkey0 = 0, /* disable colorkey */ ++ .colorkey1 = 0, /* disable colorkey */ ++ .alpha = 0xA0, /* alpha value */ ++ .ipu_restart = 0x80001000, /* ipu restart */ ++ .fg_change = FG_CHANGE_ALL, /* change all initially */ ++ .fg0 = {32, 0, 0, 240, 320}, /* bpp, x, y, w, h */ ++ .fg1 = {32, 0, 0, 240, 320}, /* bpp, x, y, w, h */ ++ }, ++ ++#elif defined(CONFIG_JZ4750_LCD_FOXCONN_PT035TN01) ++ .panel = { ++ .cfg = LCD_CFG_LCDPIN_LCD | LCD_CFG_RECOVER | /* Underrun recover */ ++ LCD_CFG_NEWDES | /* 8words descriptor */ ++ LCD_CFG_MODE_GENERIC_TFT | /* General TFT panel */ ++// LCD_CFG_MODE_TFT_18BIT | /* output 18bpp */ ++ LCD_CFG_MODE_TFT_24BIT | /* output 24bpp */ ++ LCD_CFG_HSP | /* Hsync polarity: active low */ ++ LCD_CFG_VSP | /* Vsync polarity: leading edge is falling edge */ ++ LCD_CFG_PCP, /* Pix-CLK polarity: data translations at falling edge */ ++ .slcd_cfg = 0, ++ .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ ++ 320, 240, 80, 1, 1, 10, 50, 10, 13 ++ }, ++ .osd = { ++ .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ ++// LCD_OSDC_ALPHAEN | /* enable alpha */ ++// LCD_OSDC_F1EN | /* enable Foreground1 */ ++ LCD_OSDC_F0EN, /* enable Foreground0 */ ++ .osd_ctrl = 0, /* disable ipu, */ ++ .rgb_ctrl = 0, ++ .bgcolor = 0x000000, /* set background color Black */ ++ .colorkey0 = 0, /* disable colorkey */ ++ .colorkey1 = 0, /* disable colorkey */ ++ .alpha = 0xA0, /* alpha value */ ++ .ipu_restart = 0x80001000, /* ipu restart */ ++ .fg_change = FG_CHANGE_ALL, /* change all initially */ ++ .fg0 = {32, 0, 0, 320, 240}, /* bpp, x, y, w, h */ ++ .fg1 = {32, 0, 0, 320, 240}, /* bpp, x, y, w, h */ ++ }, ++#elif defined(CONFIG_JZ4750_LCD_INNOLUX_PT035TN01_SERIAL) ++ .panel = { ++ .cfg = LCD_CFG_LCDPIN_LCD | LCD_CFG_RECOVER | /* Underrun recover */ ++ LCD_CFG_NEWDES | /* 8words descriptor */ ++ LCD_CFG_MODE_SERIAL_TFT | /* Serial TFT panel */ ++ LCD_CFG_MODE_TFT_18BIT | /* output 18bpp */ ++ LCD_CFG_HSP | /* Hsync polarity: active low */ ++ LCD_CFG_VSP | /* Vsync polarity: leading edge is falling edge */ ++ LCD_CFG_PCP, /* Pix-CLK polarity: data translations at falling edge */ ++ .slcd_cfg = 0, ++ .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ ++ 320, 240, 60, 1, 1, 10, 50, 10, 13 ++ }, ++ .osd = { ++ .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ ++// LCD_OSDC_ALPHAEN | /* enable alpha */ ++ LCD_OSDC_F0EN, /* enable Foreground0 */ ++ .osd_ctrl = 0, /* disable ipu, */ ++ .rgb_ctrl = 0, ++ .bgcolor = 0x000000, /* set background color Black */ ++ .colorkey0 = 0, /* disable colorkey */ ++ .colorkey1 = 0, /* disable colorkey */ ++ .alpha = 0xA0, /* alpha value */ ++ .ipu_restart = 0x80001000, /* ipu restart */ ++ .fg_change = FG_CHANGE_ALL, /* change all initially */ ++ .fg0 = {32, 0, 0, 320, 240}, /* bpp, x, y, w, h */ ++ .fg1 = {32, 0, 0, 320, 240}, /* bpp, x, y, w, h */ ++ }, ++#elif defined(CONFIG_JZ4750_SLCD_KGM701A3_TFT_SPFD5420A) ++ .panel = { ++// .cfg = LCD_CFG_LCDPIN_SLCD | LCD_CFG_RECOVER | /* Underrun recover*/ ++ .cfg = LCD_CFG_LCDPIN_SLCD | /* Underrun recover*/ ++// LCD_CFG_DITHER | /* dither */ ++ LCD_CFG_NEWDES | /* 8words descriptor */ ++ LCD_CFG_MODE_SLCD, /* TFT Smart LCD panel */ ++ .slcd_cfg = SLCD_CFG_DWIDTH_18BIT | SLCD_CFG_CWIDTH_18BIT | SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING | SLCD_CFG_TYPE_PARALLEL, ++ .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ ++ 400, 240, 60, 0, 0, 0, 0, 0, 0, ++ }, ++ .osd = { ++ .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ ++// LCD_OSDC_ALPHAEN | /* enable alpha */ ++// LCD_OSDC_ALPHAMD | /* alpha blending mode */ ++// LCD_OSDC_F1EN | /* enable Foreground1 */ ++ LCD_OSDC_F0EN, /* enable Foreground0 */ ++ .osd_ctrl = 0, /* disable ipu, */ ++ .rgb_ctrl = 0, ++ .bgcolor = 0x000000, /* set background color Black */ ++ .colorkey0 = 0, /* disable colorkey */ ++ .colorkey1 = 0, /* disable colorkey */ ++ .alpha = 0xA0, /* alpha value */ ++ .ipu_restart = 0x80001000, /* ipu restart */ ++ .fg_change = FG_CHANGE_ALL, /* change all initially */ ++ .fg0 = {32, 0, 0, 400, 240}, /* bpp, x, y, w, h */ ++ .fg1 = {32, 0, 0, 400, 240}, /* bpp, x, y, w, h */ ++ }, ++#else ++#error "Select LCD panel first!!!" ++#endif ++}; ++ ++struct jz4750lcd_info jz4750_info_tve = { ++ .panel = { ++ .cfg = LCD_CFG_TVEN | /* output to tve */ ++ LCD_CFG_NEWDES | /* 8words descriptor */ ++ LCD_CFG_RECOVER | /* underrun protect */ ++ LCD_CFG_MODE_INTER_CCIR656, /* Interlace CCIR656 mode */ ++ .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst */ ++ TVE_WIDTH_PAL, TVE_HEIGHT_PAL, TVE_FREQ_PAL, 0, 0, 0, 0, 0, 0, ++ }, ++ .osd = { ++ .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ ++// LCD_OSDC_ALPHAEN | /* enable alpha */ ++ LCD_OSDC_F0EN, /* enable Foreground0 */ ++ .osd_ctrl = 0, /* disable ipu, */ ++ .rgb_ctrl = LCD_RGBC_YCC, /* enable RGB => YUV */ ++ .bgcolor = 0x00000000, /* set background color Black */ ++ .colorkey0 = 0, /* disable colorkey */ ++ .colorkey1 = 0, /* disable colorkey */ ++ .alpha = 0xA0, /* alpha value */ ++ .ipu_restart = 0x80000100, /* ipu restart */ ++ .fg_change = FG_CHANGE_ALL, /* change all initially */ ++ .fg0 = {32,}, /* */ ++ .fg0 = {32,}, ++ }, ++}; ++ ++ ++struct jz4750lcd_info *jz4750_lcd_info = &jz4750_lcd_panel; /* default output to lcd panel */ ++ ++#ifdef DEBUG ++static void print_lcdc_registers(void) /* debug */ ++{ ++ /* LCD Controller Resgisters */ ++ printk("REG_LCD_CFG:\t0x%08x\n", REG_LCD_CFG); ++ printk("REG_LCD_CTRL:\t0x%08x\n", REG_LCD_CTRL); ++ printk("REG_LCD_STATE:\t0x%08x\n", REG_LCD_STATE); ++ printk("REG_LCD_OSDC:\t0x%08x\n", REG_LCD_OSDC); ++ printk("REG_LCD_OSDCTRL:\t0x%08x\n", REG_LCD_OSDCTRL); ++ printk("REG_LCD_OSDS:\t0x%08x\n", REG_LCD_OSDS); ++ printk("REG_LCD_BGC:\t0x%08x\n", REG_LCD_BGC); ++ printk("REG_LCD_KEK0:\t0x%08x\n", REG_LCD_KEY0); ++ printk("REG_LCD_KEY1:\t0x%08x\n", REG_LCD_KEY1); ++ printk("REG_LCD_ALPHA:\t0x%08x\n", REG_LCD_ALPHA); ++ printk("REG_LCD_IPUR:\t0x%08x\n", REG_LCD_IPUR); ++ printk("REG_LCD_VAT:\t0x%08x\n", REG_LCD_VAT); ++ printk("REG_LCD_DAH:\t0x%08x\n", REG_LCD_DAH); ++ printk("REG_LCD_DAV:\t0x%08x\n", REG_LCD_DAV); ++ printk("REG_LCD_XYP0:\t0x%08x\n", REG_LCD_XYP0); ++ printk("REG_LCD_XYP1:\t0x%08x\n", REG_LCD_XYP1); ++ printk("REG_LCD_SIZE0:\t0x%08x\n", REG_LCD_SIZE0); ++ printk("REG_LCD_SIZE1:\t0x%08x\n", REG_LCD_SIZE1); ++ printk("REG_LCD_RGBC\t0x%08x\n", REG_LCD_RGBC); ++ printk("REG_LCD_VSYNC:\t0x%08x\n", REG_LCD_VSYNC); ++ printk("REG_LCD_HSYNC:\t0x%08x\n", REG_LCD_HSYNC); ++ printk("REG_LCD_PS:\t0x%08x\n", REG_LCD_PS); ++ printk("REG_LCD_CLS:\t0x%08x\n", REG_LCD_CLS); ++ printk("REG_LCD_SPL:\t0x%08x\n", REG_LCD_SPL); ++ printk("REG_LCD_REV:\t0x%08x\n", REG_LCD_REV); ++ printk("REG_LCD_IID:\t0x%08x\n", REG_LCD_IID); ++ printk("REG_LCD_DA0:\t0x%08x\n", REG_LCD_DA0); ++ printk("REG_LCD_SA0:\t0x%08x\n", REG_LCD_SA0); ++ printk("REG_LCD_FID0:\t0x%08x\n", REG_LCD_FID0); ++ printk("REG_LCD_CMD0:\t0x%08x\n", REG_LCD_CMD0); ++ printk("REG_LCD_OFFS0:\t0x%08x\n", REG_LCD_OFFS0); ++ printk("REG_LCD_PW0:\t0x%08x\n", REG_LCD_PW0); ++ printk("REG_LCD_CNUM0:\t0x%08x\n", REG_LCD_CNUM0); ++ printk("REG_LCD_DESSIZE0:\t0x%08x\n", REG_LCD_DESSIZE0); ++ printk("REG_LCD_DA1:\t0x%08x\n", REG_LCD_DA1); ++ printk("REG_LCD_SA1:\t0x%08x\n", REG_LCD_SA1); ++ printk("REG_LCD_FID1:\t0x%08x\n", REG_LCD_FID1); ++ printk("REG_LCD_CMD1:\t0x%08x\n", REG_LCD_CMD1); ++ printk("REG_LCD_OFFS1:\t0x%08x\n", REG_LCD_OFFS1); ++ printk("REG_LCD_PW1:\t0x%08x\n", REG_LCD_PW1); ++ printk("REG_LCD_CNUM1:\t0x%08x\n", REG_LCD_CNUM1); ++ printk("REG_LCD_DESSIZE1:\t0x%08x\n", REG_LCD_DESSIZE1); ++ printk("==================================\n"); ++ printk("REG_LCD_VSYNC:\t%d:%d\n", REG_LCD_VSYNC>>16, REG_LCD_VSYNC&0xfff); ++ printk("REG_LCD_HSYNC:\t%d:%d\n", REG_LCD_HSYNC>>16, REG_LCD_HSYNC&0xfff); ++ printk("REG_LCD_VAT:\t%d:%d\n", REG_LCD_VAT>>16, REG_LCD_VAT&0xfff); ++ printk("REG_LCD_DAH:\t%d:%d\n", REG_LCD_DAH>>16, REG_LCD_DAH&0xfff); ++ printk("REG_LCD_DAV:\t%d:%d\n", REG_LCD_DAV>>16, REG_LCD_DAV&0xfff); ++ printk("==================================\n"); ++ ++ /* Smart LCD Controller Resgisters */ ++ printk("REG_SLCD_CFG:\t0x%08x\n", REG_SLCD_CFG); ++ printk("REG_SLCD_CTRL:\t0x%08x\n", REG_SLCD_CTRL); ++ printk("REG_SLCD_STATE:\t0x%08x\n", REG_SLCD_STATE); ++ printk("==================================\n"); ++ ++ /* TVE Controller Resgisters */ ++ printk("REG_TVE_CTRL:\t0x%08x\n", REG_TVE_CTRL); ++ printk("REG_TVE_FRCFG:\t0x%08x\n", REG_TVE_FRCFG); ++ printk("REG_TVE_SLCFG1:\t0x%08x\n", REG_TVE_SLCFG1); ++ printk("REG_TVE_SLCFG2:\t0x%08x\n", REG_TVE_SLCFG2); ++ printk("REG_TVE_SLCFG3:\t0x%08x\n", REG_TVE_SLCFG3); ++ printk("REG_TVE_LTCFG1:\t0x%08x\n", REG_TVE_LTCFG1); ++ printk("REG_TVE_LTCFG2:\t0x%08x\n", REG_TVE_LTCFG2); ++ printk("REG_TVE_CFREQ:\t0x%08x\n", REG_TVE_CFREQ); ++ printk("REG_TVE_CPHASE:\t0x%08x\n", REG_TVE_CPHASE); ++ printk("REG_TVE_CBCRCFG:\t0x%08x\n", REG_TVE_CBCRCFG); ++ printk("REG_TVE_WSSCR:\t0x%08x\n", REG_TVE_WSSCR); ++ printk("REG_TVE_WSSCFG1:\t0x%08x\n", REG_TVE_WSSCFG1); ++ printk("REG_TVE_WSSCFG2:\t0x%08x\n", REG_TVE_WSSCFG2); ++ printk("REG_TVE_WSSCFG3:\t0x%08x\n", REG_TVE_WSSCFG3); ++ ++ printk("==================================\n"); ++ ++ if ( 1 ) { ++ unsigned int * pii = (unsigned int *)dma_desc_base; ++ int i, j; ++ for (j=0;j< DMA_DESC_NUM ; j++) { ++ printk("dma_desc%d(0x%08x):\n", j, (unsigned int)pii); ++ for (i =0; i<8; i++ ) { ++ printk("\t\t0x%08x\n", *pii++); ++ } ++ } ++ } ++} ++#else ++#define print_lcdc_registers() ++#endif ++ ++static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) ++{ ++ chan &= 0xffff; ++ chan >>= 16 - bf->length; ++ return chan << bf->offset; ++} ++ ++static int jz4750fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, ++ u_int transp, struct fb_info *info) ++{ ++ struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; ++ unsigned short *ptr, ctmp; ++ ++// print_dbg("regno:%d,RGBt:(%d,%d,%d,%d)\t", regno, red, green, blue, transp); ++ if (regno >= NR_PALETTE) ++ return 1; ++ ++ cfb->palette[regno].red = red ; ++ cfb->palette[regno].green = green; ++ cfb->palette[regno].blue = blue; ++ if (cfb->fb.var.bits_per_pixel <= 16) { ++ red >>= 8; ++ green >>= 8; ++ blue >>= 8; ++ ++ red &= 0xff; ++ green &= 0xff; ++ blue &= 0xff; ++ } ++ switch (cfb->fb.var.bits_per_pixel) { ++ case 1: ++ case 2: ++ case 4: ++ case 8: ++ if (((jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_MASK) == LCD_CFG_MODE_SINGLE_MSTN ) || ++ ((jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_MASK) == LCD_CFG_MODE_DUAL_MSTN )) { ++ ctmp = (77L * red + 150L * green + 29L * blue) >> 8; ++ ctmp = ((ctmp >> 3) << 11) | ((ctmp >> 2) << 5) | ++ (ctmp >> 3); ++ } else { ++ /* RGB 565 */ ++ if (((red >> 3) == 0) && ((red >> 2) != 0)) ++ red = 1 << 3; ++ if (((blue >> 3) == 0) && ((blue >> 2) != 0)) ++ blue = 1 << 3; ++ ctmp = ((red >> 3) << 11) ++ | ((green >> 2) << 5) | (blue >> 3); ++ } ++ ++ ptr = (unsigned short *)lcd_palette; ++ ptr = (unsigned short *)(((u32)ptr)|0xa0000000); ++ ptr[regno] = ctmp; ++ ++ break; ++ ++ case 15: ++ if (regno < 16) ++ ((u32 *)cfb->fb.pseudo_palette)[regno] = ++ ((red >> 3) << 10) | ++ ((green >> 3) << 5) | ++ (blue >> 3); ++ break; ++ case 16: ++ if (regno < 16) { ++ ((u32 *)cfb->fb.pseudo_palette)[regno] = ++ ((red >> 3) << 11) | ++ ((green >> 2) << 5) | ++ (blue >> 3); ++ } ++ break; ++ case 17 ... 32: ++ if (regno < 16) ++ ((u32 *)cfb->fb.pseudo_palette)[regno] = ++ (red << 16) | ++ (green << 8) | ++ (blue << 0); ++ ++/* if (regno < 16) { ++ unsigned val; ++ val = chan_to_field(red, &cfb->fb.var.red); ++ val |= chan_to_field(green, &cfb->fb.var.green); ++ val |= chan_to_field(blue, &cfb->fb.var.blue); ++ ((u32 *)cfb->fb.pseudo_palette)[regno] = val; ++ } ++*/ ++ ++ break; ++ } ++ return 0; ++} ++ ++ ++/* ++ * switch to tve mode from lcd mode ++ * mode: ++ * PANEL_MODE_TVE_PAL: switch to TVE_PAL mode ++ * PANEL_MODE_TVE_NTSC: switch to TVE_NTSC mode ++ */ ++static void jz4750lcd_info_switch_to_TVE(int mode) ++{ ++ struct jz4750lcd_info *info; ++ struct jz4750lcd_osd_t *osd_lcd; ++ int x, y, w, h; ++ ++ info = jz4750_lcd_info = &jz4750_info_tve; ++ osd_lcd = &jz4750_lcd_panel.osd; ++ ++ switch ( mode ) { ++ case PANEL_MODE_TVE_PAL: ++ info->panel.cfg |= LCD_CFG_TVEPEH; /* TVE PAL enable extra halfline signal */ ++ info->panel.w = TVE_WIDTH_PAL; ++ info->panel.h = TVE_HEIGHT_PAL; ++ info->panel.fclk = TVE_FREQ_PAL; ++ w = ( osd_lcd->fg0.w < TVE_WIDTH_PAL )? osd_lcd->fg0.w:TVE_WIDTH_PAL; ++ h = ( osd_lcd->fg0.h < TVE_HEIGHT_PAL )?osd_lcd->fg0.h:TVE_HEIGHT_PAL; ++ x = (TVE_WIDTH_PAL-w)/2; ++ y = (TVE_HEIGHT_PAL-h)/2; ++ info->osd.fg0.bpp = osd_lcd->fg0.bpp; ++ info->osd.fg0.x = x; ++ info->osd.fg0.y = y; ++ info->osd.fg0.w = w; ++ info->osd.fg0.h = h; ++ w = ( osd_lcd->fg1.w < TVE_WIDTH_PAL )? osd_lcd->fg1.w:TVE_WIDTH_PAL; ++ h = ( osd_lcd->fg1.h < TVE_HEIGHT_PAL )?osd_lcd->fg1.h:TVE_HEIGHT_PAL; ++ x = (TVE_WIDTH_PAL-w)/2; ++ y = (TVE_HEIGHT_PAL-h)/2; ++ info->osd.fg1.bpp = 32; /* use RGB888 in TVE mode*/ ++ info->osd.fg1.x = x; ++ info->osd.fg1.y = y; ++ info->osd.fg1.w = w; ++ info->osd.fg1.h = h; ++ break; ++ case PANEL_MODE_TVE_NTSC: ++ info->panel.cfg &= ~LCD_CFG_TVEPEH; /* TVE NTSC disable extra halfline signal */ ++ info->panel.w = TVE_WIDTH_NTSC; ++ info->panel.h = TVE_HEIGHT_NTSC; ++ info->panel.fclk = TVE_FREQ_NTSC; ++ w = ( osd_lcd->fg0.w < TVE_WIDTH_NTSC )? osd_lcd->fg0.w:TVE_WIDTH_NTSC; ++ h = ( osd_lcd->fg0.h < TVE_HEIGHT_NTSC)?osd_lcd->fg0.h:TVE_HEIGHT_NTSC; ++ x = (TVE_WIDTH_NTSC - w)/2; ++ y = (TVE_HEIGHT_NTSC - h)/2; ++ info->osd.fg0.bpp = osd_lcd->fg0.bpp; ++ info->osd.fg0.x = x; ++ info->osd.fg0.y = y; ++ info->osd.fg0.w = w; ++ info->osd.fg0.h = h; ++ w = ( osd_lcd->fg1.w < TVE_WIDTH_NTSC )? osd_lcd->fg1.w:TVE_WIDTH_NTSC; ++ h = ( osd_lcd->fg1.h < TVE_HEIGHT_NTSC)?osd_lcd->fg1.h:TVE_HEIGHT_NTSC; ++ x = (TVE_WIDTH_NTSC - w)/2; ++ y = (TVE_HEIGHT_NTSC - h)/2; ++ info->osd.fg1.bpp = 32; /* use RGB888 int TVE mode */ ++ info->osd.fg1.x = x; ++ info->osd.fg1.y = y; ++ info->osd.fg1.w = w; ++ info->osd.fg1.h = h; ++ break; ++ default: ++ printk("%s, %s: Unknown tve mode\n", __FILE__, __FUNCTION__); ++ } ++} ++ ++static int jz4750fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) ++{ ++ int ret = 0; ++ ++ void __user *argp = (void __user *)arg; ++ ++// struct jz4750lcd_info *lcd_info = jz4750_lcd_info; ++ ++ ++ switch (cmd) { ++ case FBIOSETBACKLIGHT: ++ __lcd_set_backlight_level(arg); /* We support 8 levels here. */ ++ break; ++ case FBIODISPON: ++ REG_LCD_STATE = 0; /* clear lcdc status */ ++ __lcd_slcd_special_on(); ++ __lcd_clr_dis(); ++ __lcd_set_ena(); /* enable lcdc */ ++ __lcd_display_on(); ++ break; ++ case FBIODISPOFF: ++ __lcd_display_off(); ++ if ( jz4750_lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD || ++ jz4750_lcd_info->panel.cfg & LCD_CFG_TVEN ) /* */ ++ __lcd_clr_ena(); /* Smart lcd and TVE mode only support quick disable */ ++ else ++ __lcd_set_dis(); /* regular disable */ ++ break; ++ case FBIOPRINT_REG: ++ print_lcdc_registers(); ++ break; ++ case FBIO_GET_MODE: ++ print_dbg("fbio get mode\n"); ++ if (copy_to_user(argp, jz4750_lcd_info, sizeof(struct jz4750lcd_info))) ++ return -EFAULT; ++ break; ++ case FBIO_SET_MODE: ++ print_dbg("fbio set mode\n"); ++ if (copy_from_user(jz4750_lcd_info, argp, sizeof(struct jz4750lcd_info))) ++ return -EFAULT; ++ /* set mode */ ++ jz4750fb_set_mode(jz4750_lcd_info); ++ break; ++ case FBIO_DEEP_SET_MODE: ++ print_dbg("fbio deep set mode\n"); ++ if (copy_from_user(jz4750_lcd_info, argp, sizeof(struct jz4750lcd_info))) ++ return -EFAULT; ++ jz4750fb_deep_set_mode(jz4750_lcd_info); ++ break; ++ case FBIO_MODE_SWITCH: ++ print_dbg("lcd mode switch between tve and lcd, arg=%lu\n", arg); ++ switch ( arg ) { ++ case PANEL_MODE_TVE_PAL: /* switch to TVE_PAL mode */ ++ case PANEL_MODE_TVE_NTSC: /* switch to TVE_NTSC mode */ ++ jz4750lcd_info_switch_to_TVE(arg); ++ jz4750tve_init(arg); /* tve controller init */ ++ jz4750tve_enable_tve(); ++ /* turn off lcd backlight */ ++ __lcd_display_off(); ++ break; ++ case PANEL_MODE_LCD_PANEL: /* switch to LCD mode */ ++ default : ++ /* turn off TVE, turn off DACn... */ ++ jz4750tve_disable_tve(); ++ jz4750_lcd_info = &jz4750_lcd_panel; ++ /* turn on lcd backlight */ ++ __lcd_display_on(); ++ break; ++ } ++ jz4750fb_deep_set_mode(jz4750_lcd_info); ++ break; ++ case FBIO_GET_TVE_MODE: ++ print_dbg("fbio get TVE mode\n"); ++ if (copy_to_user(argp, jz4750_tve_info, sizeof(struct jz4750tve_info))) ++ return -EFAULT; ++ break; ++ case FBIO_SET_TVE_MODE: ++ print_dbg("fbio set TVE mode\n"); ++ if (copy_from_user(jz4750_tve_info, argp, sizeof(struct jz4750tve_info))) ++ return -EFAULT; ++ /* set tve mode */ ++ jz4750tve_set_tve_mode(jz4750_tve_info); ++ break; ++ default: ++ printk("%s, unknown command(0x%x)", __FILE__, cmd); ++ break; ++ } ++ ++ return ret; ++} ++ ++/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */ ++static int jz4750fb_mmap(struct fb_info *info, struct vm_area_struct *vma) ++{ ++ struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; ++ unsigned long start; ++ unsigned long off; ++ u32 len; ++ dprintk("%s, %s, %d\n", __FILE__, __FUNCTION__, __LINE__); ++ off = vma->vm_pgoff << PAGE_SHIFT; ++ //fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); ++ ++ /* frame buffer memory */ ++ start = cfb->fb.fix.smem_start; ++ len = PAGE_ALIGN((start & ~PAGE_MASK) + cfb->fb.fix.smem_len); ++ start &= PAGE_MASK; ++ ++ if ((vma->vm_end - vma->vm_start + off) > len) ++ return -EINVAL; ++ off += start; ++ ++ vma->vm_pgoff = off >> PAGE_SHIFT; ++ vma->vm_flags |= VM_IO; ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* Uncacheable */ ++ ++#if 1 ++ pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; ++ pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; /* Uncacheable */ ++// pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; /* Write-Back */ ++#endif ++ ++ if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, ++ vma->vm_end - vma->vm_start, ++ vma->vm_page_prot)) { ++ return -EAGAIN; ++ } ++ return 0; ++} ++ ++/* checks var and eventually tweaks it to something supported, ++ * DO NOT MODIFY PAR */ ++static int jz4750fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) ++{ ++ printk("jz4750fb_check_var, not implement\n"); ++ return 0; ++} ++ ++ ++/* ++ * set the video mode according to info->var ++ */ ++static int jz4750fb_set_par(struct fb_info *info) ++{ ++ printk("jz4750fb_set_par, not implemented\n"); ++ return 0; ++} ++ ++ ++/* ++ * (Un)Blank the display. ++ * Fix me: should we use VESA value? ++ */ ++static int jz4750fb_blank(int blank_mode, struct fb_info *info) ++{ ++ dprintk("jz4750 fb_blank %d %p", blank_mode, info); ++ switch (blank_mode) { ++ case FB_BLANK_UNBLANK: ++ //case FB_BLANK_NORMAL: ++ /* Turn on panel */ ++ __lcd_set_ena(); ++ __lcd_display_on(); ++ break; ++ ++ case FB_BLANK_NORMAL: ++ case FB_BLANK_VSYNC_SUSPEND: ++ case FB_BLANK_HSYNC_SUSPEND: ++ case FB_BLANK_POWERDOWN: ++#if 0 ++ /* Turn off panel */ ++ __lcd_display_off(); ++ __lcd_set_dis(); ++#endif ++ break; ++ default: ++ break; ++ ++ } ++ return 0; ++} ++ ++/* ++ * pan display ++ */ ++static int jz4750fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) ++{ ++ struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; ++ int dy; ++ ++ if (!var || !cfb) { ++ return -EINVAL; ++ } ++ ++ if (var->xoffset - cfb->fb.var.xoffset) { ++ /* No support for X panning for now! */ ++ return -EINVAL; ++ } ++ ++ dy = var->yoffset - cfb->fb.var.yoffset; ++ print_dbg("var.yoffset: %d", dy); ++ if (dy) { ++ print_dbg("Panning screen of %d lines", dy); ++ dma0_desc0->databuf += (cfb->fb.fix.line_length * dy); ++ /* TODO: Wait for current frame to finished */ ++ } ++ ++ return 0; ++} ++ ++ ++/* use default function cfb_fillrect, cfb_copyarea, cfb_imageblit */ ++static struct fb_ops jz4750fb_ops = { ++ .owner = THIS_MODULE, ++ .fb_setcolreg = jz4750fb_setcolreg, ++ .fb_check_var = jz4750fb_check_var, ++ .fb_set_par = jz4750fb_set_par, ++ .fb_blank = jz4750fb_blank, ++ .fb_pan_display = jz4750fb_pan_display, ++ .fb_fillrect = cfb_fillrect, ++ .fb_copyarea = cfb_copyarea, ++ .fb_imageblit = cfb_imageblit, ++ .fb_mmap = jz4750fb_mmap, ++ .fb_ioctl = jz4750fb_ioctl, ++}; ++ ++static int jz4750fb_set_var(struct fb_var_screeninfo *var, int con, ++ struct fb_info *info) ++{ ++ struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; ++ struct jz4750lcd_info *lcd_info = jz4750_lcd_info; ++ int chgvar = 0; ++ ++ var->height = lcd_info->osd.fg0.h; /* tve mode */ ++ var->width = lcd_info->osd.fg0.w; ++ var->bits_per_pixel = lcd_info->osd.fg0.bpp; ++ ++ var->vmode = FB_VMODE_NONINTERLACED; ++ var->activate = cfb->fb.var.activate; ++ var->xres = var->width; ++ var->yres = var->height; ++ var->xres_virtual = var->width; ++ var->yres_virtual = var->height; ++ var->xoffset = 0; ++ var->yoffset = 0; ++ var->pixclock = 0; ++ var->left_margin = 0; ++ var->right_margin = 0; ++ var->upper_margin = 0; ++ var->lower_margin = 0; ++ var->hsync_len = 0; ++ var->vsync_len = 0; ++ var->sync = 0; ++ var->activate &= ~FB_ACTIVATE_TEST; ++ ++ /* ++ * CONUPDATE and SMOOTH_XPAN are equal. However, ++ * SMOOTH_XPAN is only used internally by fbcon. ++ */ ++ if (var->vmode & FB_VMODE_CONUPDATE) { ++ var->vmode |= FB_VMODE_YWRAP; ++ var->xoffset = cfb->fb.var.xoffset; ++ var->yoffset = cfb->fb.var.yoffset; ++ } ++ ++ if (var->activate & FB_ACTIVATE_TEST) ++ return 0; ++ ++ if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) ++ return -EINVAL; ++ ++ if (cfb->fb.var.xres != var->xres) ++ chgvar = 1; ++ if (cfb->fb.var.yres != var->yres) ++ chgvar = 1; ++ if (cfb->fb.var.xres_virtual != var->xres_virtual) ++ chgvar = 1; ++ if (cfb->fb.var.yres_virtual != var->yres_virtual) ++ chgvar = 1; ++ if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel) ++ chgvar = 1; ++ ++ //display = fb_display + con; ++ ++ var->red.msb_right = 0; ++ var->green.msb_right = 0; ++ var->blue.msb_right = 0; ++ ++ switch(var->bits_per_pixel){ ++ case 1: /* Mono */ ++ cfb->fb.fix.visual = FB_VISUAL_MONO01; ++ cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; ++ break; ++ case 2: /* Mono */ ++ var->red.offset = 0; ++ var->red.length = 2; ++ var->green.offset = 0; ++ var->green.length = 2; ++ var->blue.offset = 0; ++ var->blue.length = 2; ++ ++ cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; ++ cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; ++ break; ++ case 4: /* PSEUDOCOLOUR*/ ++ var->red.offset = 0; ++ var->red.length = 4; ++ var->green.offset = 0; ++ var->green.length = 4; ++ var->blue.offset = 0; ++ var->blue.length = 4; ++ ++ cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; ++ cfb->fb.fix.line_length = var->xres / 2; ++ break; ++ case 8: /* PSEUDOCOLOUR, 256 */ ++ var->red.offset = 0; ++ var->red.length = 8; ++ var->green.offset = 0; ++ var->green.length = 8; ++ var->blue.offset = 0; ++ var->blue.length = 8; ++ ++ cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; ++ cfb->fb.fix.line_length = var->xres ; ++ break; ++ case 15: /* DIRECTCOLOUR, 32k */ ++ var->bits_per_pixel = 15; ++ var->red.offset = 10; ++ var->red.length = 5; ++ var->green.offset = 5; ++ var->green.length = 5; ++ var->blue.offset = 0; ++ var->blue.length = 5; ++ ++ cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR; ++ cfb->fb.fix.line_length = var->xres_virtual * 2; ++ break; ++ case 16: /* DIRECTCOLOUR, 64k */ ++ var->bits_per_pixel = 16; ++ var->red.offset = 11; ++ var->red.length = 5; ++ var->green.offset = 5; ++ var->green.length = 6; ++ var->blue.offset = 0; ++ var->blue.length = 5; ++ ++ cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; ++ cfb->fb.fix.line_length = var->xres_virtual * 2; ++ break; ++ case 17 ... 32: ++ /* DIRECTCOLOUR, 256 */ ++ var->bits_per_pixel = 32; ++ ++ var->red.offset = 16; ++ var->red.length = 8; ++ var->green.offset = 8; ++ var->green.length = 8; ++ var->blue.offset = 0; ++ var->blue.length = 8; ++ var->transp.offset = 24; ++ var->transp.length = 8; ++ ++ cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; ++ cfb->fb.fix.line_length = var->xres_virtual * 4; ++ break; ++ ++ default: /* in theory this should never happen */ ++ printk(KERN_WARNING "%s: don't support for %dbpp\n", ++ cfb->fb.fix.id, var->bits_per_pixel); ++ break; ++ } ++ ++ cfb->fb.var = *var; ++ cfb->fb.var.activate &= ~FB_ACTIVATE_ALL; ++ ++ /* ++ * Update the old var. The fbcon drivers still use this. ++ * Once they are using cfb->fb.var, this can be dropped. ++ * --rmk ++ */ ++ //display->var = cfb->fb.var; ++ /* ++ * If we are setting all the virtual consoles, also set the ++ * defaults used to create new consoles. ++ */ ++ fb_set_cmap(&cfb->fb.cmap, &cfb->fb); ++ dprintk("jz4750fb_set_var: after fb_set_cmap...\n"); ++ ++ return 0; ++} ++ ++static struct lcd_cfb_info * jz4750fb_alloc_fb_info(void) ++{ ++ struct lcd_cfb_info *cfb; ++ ++ cfb = kmalloc(sizeof(struct lcd_cfb_info) + sizeof(u32) * 16, GFP_KERNEL); ++ ++ if (!cfb) ++ return NULL; ++ ++ jz4750fb_info = cfb; ++ ++ memset(cfb, 0, sizeof(struct lcd_cfb_info) ); ++ ++ cfb->currcon = -1; ++ ++ ++ strcpy(cfb->fb.fix.id, "jz-lcd"); ++ cfb->fb.fix.type = FB_TYPE_PACKED_PIXELS; ++ cfb->fb.fix.type_aux = 0; ++ cfb->fb.fix.xpanstep = 1; ++ cfb->fb.fix.ypanstep = 1; ++ cfb->fb.fix.ywrapstep = 0; ++ cfb->fb.fix.accel = FB_ACCEL_NONE; ++ ++ cfb->fb.var.nonstd = 0; ++ cfb->fb.var.activate = FB_ACTIVATE_NOW; ++ cfb->fb.var.height = -1; ++ cfb->fb.var.width = -1; ++ cfb->fb.var.accel_flags = FB_ACCELF_TEXT; ++ ++ cfb->fb.fbops = &jz4750fb_ops; ++ cfb->fb.flags = FBINFO_FLAG_DEFAULT; ++ ++ cfb->fb.pseudo_palette = (void *)(cfb + 1); ++ ++ switch (jz4750_lcd_info->osd.fg0.bpp) { ++ case 1: ++ fb_alloc_cmap(&cfb->fb.cmap, 4, 0); ++ break; ++ case 2: ++ fb_alloc_cmap(&cfb->fb.cmap, 8, 0); ++ break; ++ case 4: ++ fb_alloc_cmap(&cfb->fb.cmap, 32, 0); ++ break; ++ case 8: ++ ++ default: ++ fb_alloc_cmap(&cfb->fb.cmap, 256, 0); ++ break; ++ } ++ dprintk("fb_alloc_cmap,fb.cmap.len:%d....\n", cfb->fb.cmap.len); ++ ++ return cfb; ++} ++ ++/* ++ * Map screen memory ++ */ ++static int jz4750fb_map_smem(struct lcd_cfb_info *cfb) ++{ ++ unsigned long page; ++ unsigned int page_shift, needroom, needroom1, bpp, w, h; ++ ++ bpp = jz4750_lcd_info->osd.fg0.bpp; ++ if ( bpp == 18 || bpp == 24) ++ bpp = 32; ++ if ( bpp == 15 ) ++ bpp = 16; ++ w = jz4750_lcd_info->osd.fg0.w; ++ h = jz4750_lcd_info->osd.fg0.h; ++ needroom1 = needroom = ((w * bpp + 7) >> 3) * h; ++#if defined(CONFIG_FB_JZ4750_LCD_USE_2LAYER_FRAMEBUFFER) ++ bpp = jz4750_lcd_info->osd.fg1.bpp; ++ if ( bpp == 18 || bpp == 24) ++ bpp = 32; ++ if ( bpp == 15 ) ++ bpp = 16; ++ w = jz4750_lcd_info->osd.fg1.w; ++ h = jz4750_lcd_info->osd.fg1.h; ++ needroom += ((w * bpp + 7) >> 3) * h; ++#endif ++ ++ for (page_shift = 0; page_shift < 12; page_shift++) ++ if ((PAGE_SIZE << page_shift) >= needroom) ++ break; ++ lcd_palette = (unsigned char *)__get_free_pages(GFP_KERNEL, 0); ++ lcd_frame0 = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); ++ ++ if ((!lcd_palette) || (!lcd_frame0)) ++ return -ENOMEM; ++ memset((void *)lcd_palette, 0, PAGE_SIZE); ++ memset((void *)lcd_frame0, 0, PAGE_SIZE << page_shift); ++ ++ dma_desc_base = (struct jz4750_lcd_dma_desc *)((void*)lcd_palette + ((PALETTE_SIZE+3)/4)*4); ++ ++#if defined(CONFIG_FB_JZ4750_SLCD) ++ ++ lcd_cmdbuf = (unsigned char *)__get_free_pages(GFP_KERNEL, 0); ++ memset((void *)lcd_cmdbuf, 0, PAGE_SIZE); ++ ++ { int data, i, *ptr; ++ ptr = (unsigned int *)lcd_cmdbuf; ++ data = WR_GRAM_CMD; ++ data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); ++ for(i = 0; i < 3; i++){ ++ ptr[i] = data; ++ } ++ } ++#endif ++ ++#if defined(CONFIG_FB_JZ4750_LCD_USE_2LAYER_FRAMEBUFFER) ++ lcd_frame1 = lcd_frame0 + needroom1; ++#endif ++ ++ /* ++ * Set page reserved so that mmap will work. This is necessary ++ * since we'll be remapping normal memory. ++ */ ++ page = (unsigned long)lcd_palette; ++ SetPageReserved(virt_to_page((void*)page)); ++ ++ for (page = (unsigned long)lcd_frame0; ++ page < PAGE_ALIGN((unsigned long)lcd_frame0 + (PAGE_SIZE<fb.fix.smem_start = virt_to_phys((void *)lcd_frame0); ++ cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift); /* page_shift/2 ??? */ ++ cfb->fb.screen_base = ++ (unsigned char *)(((unsigned int)lcd_frame0&0x1fffffff) | 0xa0000000); ++ ++ if (!cfb->fb.screen_base) { ++ printk("jz4750fb, %s: unable to map screen memory\n", cfb->fb.fix.id); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void jz4750fb_free_fb_info(struct lcd_cfb_info *cfb) ++{ ++ if (cfb) { ++ fb_alloc_cmap(&cfb->fb.cmap, 0, 0); ++ kfree(cfb); ++ } ++} ++ ++static void jz4750fb_unmap_smem(struct lcd_cfb_info *cfb) ++{ ++ struct page * map = NULL; ++ unsigned char *tmp; ++ unsigned int page_shift, needroom, bpp, w, h; ++ ++ bpp = jz4750_lcd_info->osd.fg0.bpp; ++ if ( bpp == 18 || bpp == 24) ++ bpp = 32; ++ if ( bpp == 15 ) ++ bpp = 16; ++ w = jz4750_lcd_info->osd.fg0.w; ++ h = jz4750_lcd_info->osd.fg0.h; ++ needroom = ((w * bpp + 7) >> 3) * h; ++#if defined(CONFIG_FB_JZ4750_LCD_USE_2LAYER_FRAMEBUFFER) ++ bpp = jz4750_lcd_info->osd.fg1.bpp; ++ if ( bpp == 18 || bpp == 24) ++ bpp = 32; ++ if ( bpp == 15 ) ++ bpp = 16; ++ w = jz4750_lcd_info->osd.fg1.w; ++ h = jz4750_lcd_info->osd.fg1.h; ++ needroom += ((w * bpp + 7) >> 3) * h; ++#endif ++ ++ for (page_shift = 0; page_shift < 12; page_shift++) ++ if ((PAGE_SIZE << page_shift) >= needroom) ++ break; ++ ++ if (cfb && cfb->fb.screen_base) { ++ iounmap(cfb->fb.screen_base); ++ cfb->fb.screen_base = NULL; ++ release_mem_region(cfb->fb.fix.smem_start, ++ cfb->fb.fix.smem_len); ++ } ++ ++ if (lcd_palette) { ++ map = virt_to_page(lcd_palette); ++ clear_bit(PG_reserved, &map->flags); ++ free_pages((int)lcd_palette, 0); ++ } ++ ++ if (lcd_frame0) { ++ for (tmp=(unsigned char *)lcd_frame0; ++ tmp < lcd_frame0 + (PAGE_SIZE << page_shift); ++ tmp += PAGE_SIZE) { ++ map = virt_to_page(tmp); ++ clear_bit(PG_reserved, &map->flags); ++ } ++ free_pages((int)lcd_frame0, page_shift); ++ } ++} ++ ++/* initial dma descriptors */ ++static void jz4750fb_descriptor_init( struct jz4750lcd_info * lcd_info ) ++{ ++ unsigned int pal_size; ++ ++ switch ( lcd_info->osd.fg0.bpp ) { ++ case 1: ++ pal_size = 4; ++ break; ++ case 2: ++ pal_size = 8; ++ break; ++ case 4: ++ pal_size = 32; ++ break; ++ case 8: ++ default: ++ pal_size = 512; ++ } ++ ++ pal_size /= 4; ++ ++ dma0_desc_palette = dma_desc_base + 0; ++ dma0_desc0 = dma_desc_base + 1; ++ dma0_desc1 = dma_desc_base + 2; ++ dma0_desc_cmd0 = dma_desc_base + 3; /* use only once */ ++ dma0_desc_cmd = dma_desc_base + 4; ++ dma1_desc0 = dma_desc_base + 5; ++ dma1_desc1 = dma_desc_base + 6; ++ ++ /* ++ * Normal TFT panel's DMA Chan0: ++ * TO LCD Panel: ++ * no palette: dma0_desc0 <<==>> dma0_desc0 ++ * palette : dma0_desc_palette <<==>> dma0_desc0 ++ * TO TV Encoder: ++ * no palette: dma0_desc0 <<==>> dma0_desc1 ++ * palette: dma0_desc_palette --> dma0_desc0 ++ * --> dma0_desc1 --> dma0_desc_palette --> ... ++ * ++ * SMART LCD TFT panel(dma0_desc_cmd)'s DMA Chan0: ++ * TO LCD Panel: ++ * no palette: dma0_desc_cmd <<==>> dma0_desc0 ++ * palette : dma0_desc_palette --> dma0_desc_cmd ++ * --> dma0_desc0 --> dma0_desc_palette --> ... ++ * TO TV Encoder: ++ * no palette: dma0_desc_cmd --> dma0_desc0 ++ * --> dma0_desc1 --> dma0_desc_cmd --> ... ++ * palette: dma0_desc_palette --> dma0_desc_cmd ++ * --> dma0_desc0 --> dma0_desc1 ++ * --> dma0_desc_palette --> ... ++ * DMA Chan1: ++ * TO LCD Panel: ++ * dma1_desc0 <<==>> dma1_desc0 ++ * TO TV Encoder: ++ * dma1_desc0 <<==>> dma1_desc1 ++ */ ++ ++#if defined(CONFIG_FB_JZ4750_SLCD) ++ /* First CMD descriptors, use only once, cmd_num isn't 0 */ ++ dma0_desc_cmd0->next_desc = (unsigned int)virt_to_phys(dma0_desc0); ++ dma0_desc_cmd0->databuf = (unsigned int)virt_to_phys((void *)lcd_cmdbuf); ++ dma0_desc_cmd0->frame_id = (unsigned int)0x0da0cad0; /* dma0's cmd0 */ ++ dma0_desc_cmd0->cmd = LCD_CMD_CMD | 3; /* command */ ++ dma0_desc_cmd0->offsize = 0; ++ dma0_desc_cmd0->page_width = 0; ++ dma0_desc_cmd0->cmd_num = 3; ++ ++ /* Dummy Command Descriptor, cmd_num is 0 */ ++ dma0_desc_cmd->next_desc = (unsigned int)virt_to_phys(dma0_desc0); ++ dma0_desc_cmd->databuf = 0; ++ dma0_desc_cmd->frame_id = (unsigned int)0x0da000cd; /* dma0's cmd0 */ ++ dma0_desc_cmd->cmd = LCD_CMD_CMD | 0; /* dummy command */ ++ dma0_desc_cmd->cmd_num = 0; ++ dma0_desc_cmd->offsize = 0; ++ dma0_desc_cmd->page_width = 0; ++ ++ /* Palette Descriptor */ ++ dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd0); ++#else ++ /* Palette Descriptor */ ++ dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc0); ++#endif ++ dma0_desc_palette->databuf = (unsigned int)virt_to_phys((void *)lcd_palette); ++ dma0_desc_palette->frame_id = (unsigned int)0xaaaaaaaa; ++ dma0_desc_palette->cmd = LCD_CMD_PAL | pal_size; /* Palette Descriptor */ ++ ++ /* DMA0 Descriptor0 */ ++ if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) /* TVE mode */ ++ dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc1); ++ else{ /* Normal TFT LCD */ ++#if defined(CONFIG_FB_JZ4750_SLCD) ++ dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); ++#else ++ dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc0); ++#endif ++ } ++ ++ dma0_desc0->databuf = virt_to_phys((void *)lcd_frame0); ++ dma0_desc0->frame_id = (unsigned int)0x0000da00; /* DMA0'0 */ ++ ++ /* DMA0 Descriptor1 */ ++ if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* TVE mode */ ++ ++ ++ if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup */ ++ dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc_palette); ++ else ++#if defined(CONFIG_FB_JZ4750_SLCD) /* for smatlcd */ ++ dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); ++#else ++ dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc0); ++#endif ++ dma0_desc1->frame_id = (unsigned int)0x0000da01; /* DMA0'1 */ ++ } ++ ++ if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup */ ++ REG_LCD_DA0 = virt_to_phys(dma0_desc_palette); ++ else { ++#if defined(CONFIG_FB_JZ4750_SLCD) /* for smartlcd */ ++ REG_LCD_DA0 = virt_to_phys(dma0_desc_cmd0); //smart lcd ++#else ++ REG_LCD_DA0 = virt_to_phys(dma0_desc0); //tft ++#endif ++ } ++ ++ /* DMA1 Descriptor0 */ ++ if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) /* TVE mode */ ++ dma1_desc0->next_desc = (unsigned int)virt_to_phys(dma1_desc1); ++ else /* Normal TFT LCD */ ++ dma1_desc0->next_desc = (unsigned int)virt_to_phys(dma1_desc0); ++ ++ dma1_desc0->databuf = virt_to_phys((void *)lcd_frame1); ++ dma1_desc0->frame_id = (unsigned int)0x0000da10; /* DMA1'0 */ ++ ++ /* DMA1 Descriptor1 */ ++ if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* TVE mode */ ++ dma1_desc1->next_desc = (unsigned int)virt_to_phys(dma1_desc0); ++ dma1_desc1->frame_id = (unsigned int)0x0000da11; /* DMA1'1 */ ++ } ++ ++ REG_LCD_DA1 = virt_to_phys(dma1_desc0); /* set Dma-chan1's Descripter Addrress */ ++ dma_cache_wback_inv((unsigned int)(dma_desc_base), (DMA_DESC_NUM)*sizeof(struct jz4750_lcd_dma_desc)); ++ ++#if 0 ++ /* Palette Descriptor */ ++ if ( lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD ) ++// dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); ++ dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd1); ++ else ++ dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc0); ++ dma0_desc_palette->databuf = (unsigned int)virt_to_phys((void *)lcd_palette); ++ dma0_desc_palette->frame_id = (unsigned int)0xaaaaaaaa; ++ dma0_desc_palette->cmd = LCD_CMD_PAL | pal_size; /* Palette Descriptor */ ++ ++ /* Dummy Command Descriptor, cmd_num is 0 */ ++ dma0_desc_cmd->next_desc = (unsigned int)virt_to_phys(dma0_desc0); ++ dma0_desc_cmd->databuf = (unsigned int)virt_to_phys((void *)lcd_cmdbuf); ++ dma0_desc_cmd->frame_id = (unsigned int)0x0da0cad0; /* dma0's cmd0 */ ++ dma0_desc_cmd->cmd = LCD_CMD_CMD | 3; /* dummy command */ ++ dma0_desc_cmd->offsize = 0; /* dummy command */ ++ dma0_desc_cmd->page_width = 0; /* dummy command */ ++ dma0_desc_cmd->cmd_num = 3; ++ ++//--------------------------------- ++ dma0_desc_cmd1->next_desc = (unsigned int)virt_to_phys(dma0_desc0); ++ dma0_desc_cmd1->databuf = 0; ++ dma0_desc_cmd1->frame_id = (unsigned int)0x0da0cad1; /* dma0's cmd0 */ ++ dma0_desc_cmd1->cmd = LCD_CMD_CMD | 0; /* dummy command */ ++ dma0_desc_cmd1->cmd_num = 0; ++ dma0_desc_cmd1->offsize = 0; /* dummy command */ ++ dma0_desc_cmd1->page_width = 0; /* dummy command */ ++//----------------------------------- ++ /* DMA0 Descriptor0 */ ++ if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) /* TVE mode */ ++ dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc1); ++ else{ /* Normal TFT LCD */ ++ if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup?? */ ++// dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_palette); //tft ++ dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); // smart lcd ++ else if ( lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD ) ++ dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd1); ++// dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); ++ else ++ dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc0); ++ } ++ ++ dma0_desc0->databuf = virt_to_phys((void *)lcd_frame0); ++ dma0_desc0->frame_id = (unsigned int)0x0000da00; /* DMA0'0 */ ++ ++ /* DMA0 Descriptor1 */ ++ if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* TVE mode */ ++ if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup?? */ ++ dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc_palette); ++ ++ else if ( lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD ) ++ dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); ++ else ++ dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc0); ++ dma0_desc1->frame_id = (unsigned int)0x0000da01; /* DMA0'1 */ ++ } ++ ++ /* DMA1 Descriptor0 */ ++ if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) /* TVE mode */ ++ dma1_desc0->next_desc = (unsigned int)virt_to_phys(dma1_desc1); ++ else /* Normal TFT LCD */ ++ dma1_desc0->next_desc = (unsigned int)virt_to_phys(dma1_desc0); ++ ++ dma1_desc0->databuf = virt_to_phys((void *)lcd_frame1); ++ dma1_desc0->frame_id = (unsigned int)0x0000da10; /* DMA1'0 */ ++ ++ /* DMA1 Descriptor1 */ ++ if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* TVE mode */ ++ dma1_desc1->next_desc = (unsigned int)virt_to_phys(dma1_desc0); ++ dma1_desc1->frame_id = (unsigned int)0x0000da11; /* DMA1'1 */ ++ } ++ ++ if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup?? */ ++ REG_LCD_DA0 = virt_to_phys(dma0_desc_palette); ++ else ++// REG_LCD_DA0 = virt_to_phys(dma0_desc_cmd); //smart lcd ++ REG_LCD_DA0 = virt_to_phys(dma0_desc0); //tft ++ REG_LCD_DA1 = virt_to_phys(dma1_desc0); /* set Dma-chan1's Descripter Addrress */ ++ dma_cache_wback_inv((unsigned int)(dma_desc_base), (DMA_DESC_NUM)*sizeof(struct jz4750_lcd_dma_desc)); ++#endif ++} ++ ++static void jz4750fb_set_panel_mode( struct jz4750lcd_info * lcd_info ) ++{ ++ struct jz4750lcd_panel_t *panel = &lcd_info->panel; ++ ++ /* set bpp */ ++ lcd_info->panel.ctrl &= ~LCD_CTRL_BPP_MASK; ++ if ( lcd_info->osd.fg0.bpp == 1 ) ++ lcd_info->panel.ctrl |= LCD_CTRL_BPP_1; ++ else if ( lcd_info->osd.fg0.bpp == 2 ) ++ lcd_info->panel.ctrl |= LCD_CTRL_BPP_2; ++ else if ( lcd_info->osd.fg0.bpp == 4 ) ++ lcd_info->panel.ctrl |= LCD_CTRL_BPP_4; ++ else if ( lcd_info->osd.fg0.bpp == 8 ) ++ lcd_info->panel.ctrl |= LCD_CTRL_BPP_8; ++ else if ( lcd_info->osd.fg0.bpp == 15 ) ++ lcd_info->panel.ctrl |= LCD_CTRL_BPP_16 | LCD_CTRL_RGB555; ++ else if ( lcd_info->osd.fg0.bpp == 16 ) ++ lcd_info->panel.ctrl |= LCD_CTRL_BPP_16 | LCD_CTRL_RGB565; ++ else if ( lcd_info->osd.fg0.bpp > 16 && lcd_info->osd.fg0.bpp < 32+1 ) { ++ lcd_info->osd.fg0.bpp = 32; ++ lcd_info->panel.ctrl |= LCD_CTRL_BPP_18_24; ++ } ++ else { ++ printk("The BPP %d is not supported\n", lcd_info->osd.fg0.bpp); ++ lcd_info->osd.fg0.bpp = 32; ++ lcd_info->panel.ctrl |= LCD_CTRL_BPP_18_24; ++ } ++ ++ lcd_info->panel.cfg |= LCD_CFG_NEWDES; /* use 8words descriptor always */ ++ ++ REG_LCD_CTRL = lcd_info->panel.ctrl; /* LCDC Controll Register */ ++ REG_LCD_CFG = lcd_info->panel.cfg; /* LCDC Configure Register */ ++ REG_SLCD_CFG = lcd_info->panel.slcd_cfg; /* Smart LCD Configure Register */ ++ ++ if ( lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD ) /* enable Smart LCD DMA */ ++ REG_SLCD_CTRL = SLCD_CTRL_DMA_EN; ++ ++ switch ( lcd_info->panel.cfg & LCD_CFG_MODE_MASK ) { ++ case LCD_CFG_MODE_GENERIC_TFT: ++ case LCD_CFG_MODE_INTER_CCIR656: ++ case LCD_CFG_MODE_NONINTER_CCIR656: ++ case LCD_CFG_MODE_SLCD: ++ default: /* only support TFT16 TFT32, not support STN and Special TFT by now(10-06-2008)*/ ++ REG_LCD_VAT = (((panel->blw + panel->w + panel->elw + panel->hsw)) << 16) | (panel->vsw + panel->bfw + panel->h + panel->efw); ++ REG_LCD_DAH = ((panel->hsw + panel->blw) << 16) | (panel->hsw + panel->blw + panel->w); ++ REG_LCD_DAV = ((panel->vsw + panel->bfw) << 16) | (panel->vsw + panel->bfw + panel->h); ++ REG_LCD_HSYNC = (0 << 16) | panel->hsw; ++ REG_LCD_VSYNC = (0 << 16) | panel->vsw; ++ break; ++ } ++} ++ ++ ++static void jz4750fb_set_osd_mode( struct jz4750lcd_info * lcd_info ) ++{ ++ dprintk("%s, %d\n", __FILE__, __LINE__ ); ++ lcd_info->osd.osd_ctrl &= ~(LCD_OSDCTRL_OSDBPP_MASK); ++ if ( lcd_info->osd.fg1.bpp == 15 ) ++ lcd_info->osd.osd_ctrl |= LCD_OSDCTRL_OSDBPP_15_16|LCD_OSDCTRL_RGB555; ++ else if ( lcd_info->osd.fg1.bpp == 16 ) ++ lcd_info->osd.osd_ctrl |= LCD_OSDCTRL_OSDBPP_15_16|LCD_OSDCTRL_RGB565; ++ else { ++ lcd_info->osd.fg1.bpp = 32; ++ lcd_info->osd.osd_ctrl |= LCD_OSDCTRL_OSDBPP_18_24; ++ } ++ ++ REG_LCD_OSDC = lcd_info->osd.osd_cfg; /* F0, F1, alpha, */ ++ ++ REG_LCD_OSDCTRL = lcd_info->osd.osd_ctrl; /* IPUEN, bpp */ ++ REG_LCD_RGBC = lcd_info->osd.rgb_ctrl; ++ REG_LCD_BGC = lcd_info->osd.bgcolor; ++ REG_LCD_KEY0 = lcd_info->osd.colorkey0; ++ REG_LCD_KEY1 = lcd_info->osd.colorkey1; ++ REG_LCD_ALPHA = lcd_info->osd.alpha; ++ REG_LCD_IPUR = lcd_info->osd.ipu_restart; ++} ++ ++static void jz4750fb_foreground_resize( struct jz4750lcd_info * lcd_info ) ++{ ++ int fg0_line_size, fg0_frm_size, fg1_line_size, fg1_frm_size; ++ /* ++ * NOTE: ++ * Foreground change sequence: ++ * 1. Change Position Registers -> LCD_OSDCTL.Change; ++ * 2. LCD_OSDCTRL.Change -> descripter->Size ++ * Foreground, only one of the following can be change at one time: ++ * 1. F0 size; ++ * 2. F0 position ++ * 3. F1 size ++ * 4. F1 position ++ */ ++ ++ /* ++ * The rules of f0, f1's position: ++ * f0.x + f0.w <= panel.w; ++ * f0.y + f0.h <= panel.h; ++ * ++ * When output is LCD panel, fg.y and fg.h can be odd number or even number. ++ * When output is TVE, as the TVE has odd frame and even frame, ++ * to simplified operation, fg.y and fg.h should be even number always. ++ * ++ */ ++ ++ /* Foreground 0 */ ++ if ( lcd_info->osd.fg0.x >= lcd_info->panel.w ) ++ lcd_info->osd.fg0.x = lcd_info->panel.w - 1; ++ if ( lcd_info->osd.fg0.y >= lcd_info->panel.h ) ++ lcd_info->osd.fg0.y = lcd_info->panel.h - 1; ++ if ( lcd_info->osd.fg0.x + lcd_info->osd.fg0.w > lcd_info->panel.w ) ++ lcd_info->osd.fg0.w = lcd_info->panel.w - lcd_info->osd.fg0.x; ++ if ( lcd_info->osd.fg0.y + lcd_info->osd.fg0.h > lcd_info->panel.h ) ++ lcd_info->osd.fg0.h = lcd_info->panel.h - lcd_info->osd.fg0.y; ++ /* Foreground 1 */ ++ /* Case TVE ??? TVE 720x573 or 720x480*/ ++ if ( lcd_info->osd.fg1.x >= lcd_info->panel.w ) ++ lcd_info->osd.fg1.x = lcd_info->panel.w - 1; ++ if ( lcd_info->osd.fg1.y >= lcd_info->panel.h ) ++ lcd_info->osd.fg1.y = lcd_info->panel.h - 1; ++ if ( lcd_info->osd.fg1.x + lcd_info->osd.fg1.w > lcd_info->panel.w ) ++ lcd_info->osd.fg1.w = lcd_info->panel.w - lcd_info->osd.fg1.x; ++ if ( lcd_info->osd.fg1.y + lcd_info->osd.fg1.h > lcd_info->panel.h ) ++ lcd_info->osd.fg1.h = lcd_info->panel.h - lcd_info->osd.fg1.y; ++ ++// fg0_line_size = lcd_info->osd.fg0.w*((lcd_info->osd.fg0.bpp+7)/8); ++ fg0_line_size = (lcd_info->osd.fg0.w*(lcd_info->osd.fg0.bpp)/8); ++ fg0_line_size = ((fg0_line_size+3)>>2)<<2; /* word aligned */ ++ fg0_frm_size = fg0_line_size * lcd_info->osd.fg0.h; ++ ++ fg1_line_size = lcd_info->osd.fg1.w*((lcd_info->osd.fg1.bpp+7)/8); ++ fg1_line_size = ((fg1_line_size+3)>>2)<<2; /* word aligned */ ++ fg1_frm_size = fg1_line_size * lcd_info->osd.fg1.h; ++ ++ if ( lcd_info->osd.fg_change ) { ++ if ( lcd_info->osd.fg_change & FG0_CHANGE_POSITION ) { /* F1 change position */ ++ REG_LCD_XYP0 = lcd_info->osd.fg0.y << 16 | lcd_info->osd.fg0.x; ++ } ++ if ( lcd_info->osd.fg_change & FG1_CHANGE_POSITION ) { /* F1 change position */ ++ REG_LCD_XYP1 = lcd_info->osd.fg1.y << 16 | lcd_info->osd.fg1.x; ++ } ++ ++ /* set change */ ++ if ( !(lcd_info->osd.osd_ctrl & LCD_OSDCTRL_IPU) && ++ (lcd_info->osd.fg_change != FG_CHANGE_ALL) ) ++ REG_LCD_OSDCTRL |= LCD_OSDCTRL_CHANGES; ++ ++ /* wait change ready??? */ ++// while ( REG_LCD_OSDS & LCD_OSDS_READY ) /* fix in the future, Wolfgang, 06-20-2008 */ ++ print_dbg("wait LCD_OSDS_READY\n"); ++ ++ if ( lcd_info->osd.fg_change & FG0_CHANGE_SIZE ) { /* change FG0 size */ ++ if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* output to TV */ ++ dma0_desc0->cmd = dma0_desc1->cmd = (fg0_frm_size/4)/2; ++ dma0_desc0->offsize = dma0_desc1->offsize ++ = fg0_line_size/4; ++ dma0_desc0->page_width = dma0_desc1->page_width ++ = fg0_line_size/4; ++ dma0_desc1->databuf = virt_to_phys((void *)(lcd_frame0 + fg0_line_size)); ++ } ++ else { ++ dma0_desc0->cmd = dma0_desc1->cmd = fg0_frm_size/4; ++ dma0_desc0->offsize = dma0_desc1->offsize =0; ++ dma0_desc0->page_width = dma0_desc1->page_width = 0; ++ } ++ ++ dma0_desc0->desc_size = dma0_desc1->desc_size ++ = lcd_info->osd.fg0.h << 16 | lcd_info->osd.fg0.w; ++ REG_LCD_SIZE0 = (lcd_info->osd.fg0.h<<16)|lcd_info->osd.fg0.w; ++ ++ } ++ ++ if ( lcd_info->osd.fg_change & FG1_CHANGE_SIZE ) { /* change FG1 size*/ ++ if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* output to TV */ ++ dma1_desc0->cmd = dma1_desc1->cmd = (fg1_frm_size/4)/2; ++ dma1_desc0->offsize = dma1_desc1->offsize = fg1_line_size/4; ++ dma1_desc0->page_width = dma1_desc1->page_width = fg1_line_size/4; ++ dma1_desc1->databuf = virt_to_phys((void *)(lcd_frame1 + fg1_line_size)); ++ } ++ else { ++ dma1_desc0->cmd = dma1_desc1->cmd = fg1_frm_size/4; ++ dma1_desc0->offsize = dma1_desc1->offsize = 0; ++ dma1_desc0->page_width = dma1_desc1->page_width = 0; ++ } ++ ++ dma1_desc0->desc_size = dma1_desc1->desc_size ++ = lcd_info->osd.fg1.h << 16 | lcd_info->osd.fg1.w; ++ REG_LCD_SIZE1 = lcd_info->osd.fg1.h << 16|lcd_info->osd.fg1.w; ++ } ++ ++ dma_cache_wback((unsigned int)(dma_desc_base), (DMA_DESC_NUM)*sizeof(struct jz4750_lcd_dma_desc)); ++ lcd_info->osd.fg_change = FG_NOCHANGE; /* clear change flag */ ++ } ++} ++ ++static void jz4750fb_change_clock( struct jz4750lcd_info * lcd_info ) ++{ ++#if defined(CONFIG_JZ4750_FUWA) || defined(CONFIG_JZ4750_APUS) /* FPGA test, pixdiv */ ++#if 0 ++ REG_LCD_REV = 0x00000004; ++ printk("Fuwa test, pixclk divide REG_LCD_REV=0x%08x\n", REG_LCD_REV); ++ printk("Fuwa test, pixclk %d\n", JZ_EXTAL/(((REG_LCD_REV&0xFF)+1)*2)); ++#endif ++ unsigned int val = 0; ++ unsigned int pclk; ++ /* Timing setting */ ++ __cpm_stop_lcd(); ++ ++ val = lcd_info->panel.fclk; /* frame clk */ ++ ++ if ( (lcd_info->panel.cfg & LCD_CFG_MODE_MASK) != LCD_CFG_MODE_SERIAL_TFT) { ++ pclk = val * (lcd_info->panel.w + lcd_info->panel.hsw + lcd_info->panel.elw + lcd_info->panel.blw) * (lcd_info->panel.h + lcd_info->panel.vsw + lcd_info->panel.efw + lcd_info->panel.bfw); /* Pixclk */ ++ } ++ else { ++ /* serial mode: Hsync period = 3*Width_Pixel */ ++ pclk = val * (lcd_info->panel.w*3 + lcd_info->panel.hsw + lcd_info->panel.elw + lcd_info->panel.blw) * (lcd_info->panel.h + lcd_info->panel.vsw + lcd_info->panel.efw + lcd_info->panel.bfw); /* Pixclk */ ++ } ++ ++ /********* In TVE mode PCLK = 27MHz ***********/ ++ if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* LCDC output to TVE */ ++ REG_CPM_LPCDR |= CPM_LPCDR_LTCS; ++ val = 11; /* PLLCLK = 324 */ ++ __cpm_set_pixdiv(val); ++ ++ dprintk("REG_CPM_LPCDR = 0x%08x\n", REG_CPM_LPCDR); ++ ++ val = pclk * 3 ; /* LCDClock > 2.5*Pixclock */ ++ ++ val =(__cpm_get_pllout()) / val; ++ if ( val > 0x1f ) { ++ printk("lcd clock divide is too large, set it to 0x1f\n"); ++ val = 0x1f; ++ } ++ __cpm_set_ldiv( val ); ++ REG_CPM_LPCDR |= CPM_LPCDR_LTCS; ++ REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */ ++ ++ } ++ else { /* LCDC output to LCD panel */ ++// REG_CPM_LPCDR = 0; ++ val = __cpm_get_pllout2() / pclk; /* pclk */ ++ val--; ++ dprintk("ratio: val = %d\n", val); ++ if ( val > 0x7ff ) { ++ printk("pixel clock divid is too large, set it to 0x7ff\n"); ++ val = 0x7ff; ++ } ++ ++ __cpm_set_pixdiv(val); ++ ++ dprintk("REG_CPM_LPCDR = 0x%08x\n", REG_CPM_LPCDR); ++ val = pclk * 3 ; /* LCDClock > 2.5*Pixclock */ ++ val =__cpm_get_pllout2() / val; ++ if ( val > 0x1f ) { ++ printk("lcd clock divide is too large, set it to 0x1f\n"); ++ val = 0x1f; ++ } ++ __cpm_set_ldiv( val ); ++ REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */ ++ ++ } ++ ++ dprintk("REG_CPM_LPCDR=0x%08x\n", REG_CPM_LPCDR); ++ dprintk("REG_CPM_CPCCR=0x%08x\n", REG_CPM_CPCCR); ++ ++ jz_clocks.pixclk = __cpm_get_pixclk(); ++ jz_clocks.lcdclk = __cpm_get_lcdclk(); ++ printk("LCDC: PixClock:%d LcdClock:%d\n", ++ jz_clocks.pixclk, jz_clocks.lcdclk); ++ ++ __cpm_start_lcd(); ++ udelay(1000); ++#else ++ ++#error "Set lcd clock first, Not support your chipset now!!!" ++ /* ++ * set lcd device clock and lcd pixel clock. ++ * what about TVE mode??? ++ * ++ */ ++#endif ++ ++} ++ ++/* ++ * jz4750fb_set_mode(), set osd configure, resize foreground ++ * ++ */ ++static void jz4750fb_set_mode( struct jz4750lcd_info * lcd_info ) ++{ ++ struct lcd_cfb_info *cfb = jz4750fb_info; ++ ++ jz4750fb_set_osd_mode(lcd_info); ++ jz4750fb_foreground_resize(lcd_info); ++ jz4750fb_set_var(&cfb->fb.var, -1, &cfb->fb); ++} ++ ++/* ++ * jz4750fb_deep_set_mode, ++ * ++ */ ++static void jz4750fb_deep_set_mode( struct jz4750lcd_info * lcd_info ) ++{ ++ /* configurate sequence: ++ * 1. disable lcdc. ++ * 2. init frame descriptor. ++ * 3. set panel mode ++ * 4. set osd mode ++ * 5. start lcd clock in CPM ++ * 6. enable lcdc. ++ */ ++ ++ __lcd_clr_ena(); /* Quick Disable */ ++ lcd_info->osd.fg_change = FG_CHANGE_ALL; /* change FG0, FG1 size, postion??? */ ++ jz4750fb_descriptor_init(lcd_info); ++ jz4750fb_set_panel_mode(lcd_info); ++ jz4750fb_set_mode(lcd_info); ++ jz4750fb_change_clock(lcd_info); ++ __lcd_set_ena(); /* enable lcdc */ ++} ++ ++ ++static irqreturn_t jz4750fb_interrupt_handler(int irq, void *dev_id) ++{ ++ unsigned int state; ++ static int irqcnt=0; ++ ++ state = REG_LCD_STATE; ++ dprintk("In the lcd interrupt handler, state=0x%x\n", state); ++ ++ if (state & LCD_STATE_EOF) /* End of frame */ ++ REG_LCD_STATE = state & ~LCD_STATE_EOF; ++ ++ if (state & LCD_STATE_IFU0) { ++ printk("%s, InFiFo0 underrun\n", __FUNCTION__); ++ REG_LCD_STATE = state & ~LCD_STATE_IFU0; ++ } ++ ++ if (state & LCD_STATE_IFU1) { ++ printk("%s, InFiFo1 underrun\n", __FUNCTION__); ++ REG_LCD_STATE = state & ~LCD_STATE_IFU1; ++ } ++ ++ if (state & LCD_STATE_OFU) { /* Out fifo underrun */ ++ REG_LCD_STATE = state & ~LCD_STATE_OFU; ++ if ( irqcnt++ > 100 ) { ++ __lcd_disable_ofu_intr(); ++ printk("disable Out FiFo underrun irq.\n"); ++ } ++ printk("%s, Out FiFo underrun.\n", __FUNCTION__); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++#ifdef CONFIG_PM ++ ++/* ++ * Suspend the LCDC. ++ */ ++static int jzfb_suspend(void) ++{ ++ __lcd_clr_ena(); /* Quick Disable */ ++ __lcd_display_off(); ++ __cpm_stop_lcd(); ++ ++ return 0; ++} ++ ++/* ++ * Resume the LCDC. ++ */ ++static int jzfb_resume(void) ++{ ++ __cpm_start_lcd(); ++ __gpio_set_pin(GPIO_DISP_OFF_N); ++ __lcd_special_on(); ++ __lcd_set_ena(); ++ mdelay(200); ++ __lcd_set_backlight_level(80); ++ ++ return 0; ++} ++ ++/* ++ * Power management hook. Note that we won't be called from IRQ context, ++ * unlike the blank functions above, so we may sleep. ++ */ ++static int jzlcd_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) ++{ ++ int ret; ++ struct lcd_cfb_info *cfb = pm_dev->data; ++ ++ if (!cfb) return -EINVAL; ++ ++ switch (req) { ++ case PM_SUSPEND: ++ ret = jzfb_suspend(); ++ break; ++ ++ case PM_RESUME: ++ ret = jzfb_resume(); ++ break; ++ ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++#else ++#define jzfb_suspend NULL ++#define jzfb_resume NULL ++#endif /* CONFIG_PM */ ++ ++/* The following routine is only for test */ ++ ++#ifdef DEBUG ++static void test_gpio(int gpio_num, int delay) { ++ __gpio_as_output(gpio_num); ++ while(1) { ++ __gpio_set_pin(gpio_num); ++ udelay(delay); ++ __gpio_clear_pin(gpio_num); ++ udelay(delay); ++ } ++} ++static void display_v_color_bar(int w, int h, int bpp) { ++ int i, j, wpl, data = 0; ++ int *ptr; ++ ptr = (int *)lcd_frame0; ++// ptr = (int *)lcd_frame1; ++ wpl = w*bpp/32; ++ if (!(bpp > 8)) ++ switch(bpp){ ++ case 1: ++ for (j = 0;j < h; j++) ++ for (i = 0;i < wpl; i++) { ++ *ptr++ = 0x00ff00ff; ++ } ++ break; ++ case 2: ++ for (j = 0;j < h; j++) ++ for (i = 0;i < wpl; i++) { ++ data = (i%4)*0x55555555; ++ *ptr++ = data; ++ } ++ break; ++ case 4: ++ for (j = 0;j < h; j++) ++ for (i = 0;i < wpl; i++) { ++ data = (i%16)*0x11111111; ++ *ptr++ = data; ++ } ++ break; ++ case 8: ++ for (j = 0;j < h; j++) ++ for (i = 0;i < wpl; i+=2) { ++ data = (i%(256))*0x01010101; ++ *ptr++ = data; ++ *ptr++ = data; ++ } ++ break; ++ } ++ else { ++ switch(bpp) { ++ case 16: ++ for (j = 0;j < h; j++) ++ for (i = 0;i < wpl; i++) { ++ if((i/4)%8==0) ++ *ptr++ = 0xffffffff; ++ else if ((i/4)%8==1) ++ *ptr++ = 0xf800f800; ++ else if ((i/4)%8==2) ++ *ptr++ = 0xffe0ffe0; ++ else if ((i/4)%8==3) ++ *ptr++ = 0x07e007e0; ++ else if ((i/4)%8==4) ++ *ptr++ = 0x07ff07ff; ++ else if ((i/4)%8==5) ++ *ptr++ = 0x001f001f; ++ else if ((i/4)%8==6) ++ *ptr++ = 0xf81ff81f; ++ else if ((i/4)%8==7) ++ *ptr++ = 0x00000000; ++ } ++ break; ++ case 18: ++ case 24: ++ case 32: ++ default: ++#if 1 ++ for (j = 0;j < h; j++) ++ for (i = 0;i < wpl; i++) { ++ if((i/8)%8==7) ++ *ptr++ = 0xffffff; ++ else if ((i/8)%8==1) ++ *ptr++ = 0xff0000; ++ else if ((i/8)%8==2) ++ *ptr++ = 0xffff00; ++ else if ((i/8)%8==3) ++ *ptr++ = 0x00ff00; ++ else if ((i/8)%8==4) ++ *ptr++ = 0x00ffff; ++ else if ((i/8)%8==5) ++ *ptr++ = 0x0000ff; ++ else if ((i/8)%8==6) ++ *ptr++ = 0xff00ff; ++ else if ((i/8)%8==0) ++ *ptr++ = 0x000000; ++ } ++#else ++ for (j = 0;j < h; j++) ++ for (i = 0;i < wpl; i++) { ++ if((i/8)%8==7) ++ *ptr++ = 0x00ff0000; ++ else if ((i/8)%8==1) ++ *ptr++ = 0xffff0000; ++ else if ((i/8)%8==2) ++ *ptr++ = 0x20ff0000; ++ else if ((i/8)%8==3) ++ *ptr++ = 0x40ff0000; ++ else if ((i/8)%8==4) ++ *ptr++ = 0x60ff0000; ++ else if ((i/8)%8==5) ++ *ptr++ = 0x80ff0000; ++ else if ((i/8)%8==6) ++ *ptr++ = 0xa0ff0000; ++ else if ((i/8)%8==0) ++ *ptr++ = 0xc0ff0000; ++ } ++#endif ++ break; ++ } ++ } ++} ++static void display_h_color_bar(int w, int h, int bpp) { ++ int i, data = 0; ++ int *ptr; ++ int wpl; //word_per_line ++ ptr = (int *)lcd_frame0; ++// ptr = (int *)lcd_frame1; ++ wpl = w*bpp/32; ++ if (!(bpp > 8)) ++ for (i = 0;i < wpl*h;i++) { ++ switch(bpp){ ++ case 1: ++ if(i%(wpl*8)==0) ++ data = ((i/(wpl*8))%2)*0xffffffff; ++ *ptr++ = data; ++ break; ++ case 2: ++ if(i%(wpl*8)==0) ++ data = ((i/(wpl*8))%4)*0x55555555; ++ *ptr++ = data; ++ break; ++ case 4: ++ if(i%(wpl*8)==0) ++ data = ((i/(wpl*8))%16)*0x11111111; ++ *ptr++ = data; ++ break; ++ case 8: ++ if(i%(wpl*8)==0) ++ data = ((i/(wpl*8))%256)*0x01010101; ++ *ptr++ = data; ++ break; ++ } ++ } ++ else { ++ ++ switch(bpp) { ++ case 15: ++ case 16: ++ for (i = 0;i < wpl*h;i++) { ++ if (((i/(wpl*8)) % 8) == 0) ++ *ptr++ = 0xffffffff; ++ else if (((i/(wpl*8)) % 8) == 1) ++ *ptr++ = 0xf800f800; ++ else if (((i/(wpl*8)) % 8) == 2) ++ *ptr++ = 0xffe0ffe0; ++ else if (((i/(wpl*8)) % 8) == 3) ++ *ptr++ = 0x07e007e0; ++ else if (((i/(wpl*8)) % 8) == 4) ++ *ptr++ = 0x07ff07ff; ++ else if (((i/(wpl*8)) % 8) == 5) ++ *ptr++ = 0x001f001f; ++ else if (((i/(wpl*8)) % 8) == 6) ++ *ptr++ = 0xf81ff81f; ++ else if (((i/(wpl*8)) % 8) == 7) ++ *ptr++ = 0x00000000; ++ } ++ break; ++ case 18: ++ case 24: ++ case 32: ++ default: ++ for (i = 0;i < wpl*h;i++) { ++ if (((i/(wpl*8)) % 8) == 7) ++ *ptr++ = 0xffffff; ++ else if (((i/(wpl*8)) % 8) == 2) ++ *ptr++ = 0xff0000; ++ else if (((i/(wpl*8)) % 8) == 4) ++ *ptr++ = 0xffff00; ++ else if (((i/(wpl*8)) % 8) == 6) ++ *ptr++ = 0x00ff00; ++ else if (((i/(wpl*8)) % 8) == 1) ++ *ptr++ = 0x00ffff; ++ else if (((i/(wpl*8)) % 8) == 3) ++ *ptr++ = 0x0000ff; ++ else if (((i/(wpl*8)) % 8) == 5) ++ *ptr++ = 0x000000; ++ else if (((i/(wpl*8)) % 8) == 0) ++ *ptr++ = 0xff00ff; ++ } ++ break; ++ } ++ ++ } ++ ++} ++#endif ++ ++static int __init jz4750fb_init(void) ++{ ++ struct lcd_cfb_info *cfb; ++ int err = 0; ++ ++ /* gpio init __gpio_as_lcd */ ++ if (jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_TFT_16BIT) ++ __gpio_as_lcd_16bit(); ++ else if (jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_TFT_24BIT) ++ __gpio_as_lcd_24bit(); ++ else ++ __gpio_as_lcd_18bit(); ++ /* In special mode, we only need init special pin, ++ * as general lcd pin has init in uboot */ ++#if defined(CONFIG_SOC_JZ4750) ++ switch (jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_MASK) { ++ case LCD_CFG_MODE_SPECIAL_TFT_1: ++ case LCD_CFG_MODE_SPECIAL_TFT_2: ++ case LCD_CFG_MODE_SPECIAL_TFT_3: ++ __gpio_as_lcd_special(); ++ break; ++ default: ++ ; ++ } ++#endif ++ if ( jz4750_lcd_info->osd.fg0.bpp > 16 && ++ jz4750_lcd_info->osd.fg0.bpp < 32 ) { ++ jz4750_lcd_info->osd.fg0.bpp = 32; ++ } ++ ++ switch ( jz4750_lcd_info->osd.fg1.bpp ) { ++ case 15: ++ case 16: ++ break; ++ case 17 ... 32: ++ jz4750_lcd_info->osd.fg1.bpp = 32; ++ break; ++ default: ++ printk("jz4750fb fg1 not support bpp(%d), force to 32bpp\n", ++ jz4750_lcd_info->osd.fg1.bpp); ++ jz4750_lcd_info->osd.fg1.bpp = 32; ++ } ++ __lcd_clr_dis(); ++ __lcd_clr_ena(); ++ ++ /* Configure SLCD module for setting smart lcd control registers */ ++#if defined(CONFIG_FB_JZ4750_SLCD) ++ __lcd_as_smart_lcd(); ++ __slcd_disable_dma(); ++ __init_slcd_bus(); /* Note: modify this depend on you lcd */ ++ ++#endif ++ /* init clk */ ++ jz4750fb_change_clock(jz4750_lcd_info); ++ __lcd_display_pin_init(); ++ __lcd_slcd_special_on(); ++ ++ cfb = jz4750fb_alloc_fb_info(); ++ if (!cfb) ++ goto failed; ++ ++ err = jz4750fb_map_smem(cfb); ++ if (err) ++ goto failed; ++ ++ jz4750fb_deep_set_mode( jz4750_lcd_info ); ++ ++ err = register_framebuffer(&cfb->fb); ++ if (err < 0) { ++ dprintk("jzfb_init(): register framebuffer err.\n"); ++ goto failed; ++ } ++ printk("fb%d: %s frame buffer device, using %dK of video memory\n", ++ cfb->fb.node, cfb->fb.fix.id, cfb->fb.fix.smem_len>>10); ++ ++ if (request_irq(IRQ_LCD, jz4750fb_interrupt_handler, IRQF_DISABLED, ++ "lcd", 0)) { ++ err = -EBUSY; ++ goto failed; ++ } ++ ++#ifdef CONFIG_PM ++ /* ++ * Note that the console registers this as well, but we want to ++ * power down the display prior to sleeping. ++ */ ++ cfb->pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, jzlcd_pm_callback); ++ if (cfb->pm) ++ cfb->pm->data = cfb; ++#endif ++ ++ __lcd_set_ena(); /* enalbe LCD Controller */ ++ __lcd_display_on(); ++ ++#ifdef DEBUG ++ display_h_color_bar(jz4750_lcd_info->osd.fg0.w, jz4750_lcd_info->osd.fg0.h, jz4750_lcd_info->osd.fg0.bpp); ++#endif ++ print_lcdc_registers(); ++ return 0; ++ ++failed: ++ print_dbg(); ++ jz4750fb_unmap_smem(cfb); ++ jz4750fb_free_fb_info(cfb); ++ ++ return err; ++} ++ ++#if 0 ++static int jzfb_remove(struct device *dev) ++{ ++ struct lcd_cfb_info *cfb = dev_get_drvdata(dev); ++ jzfb_unmap_smem(cfb); ++ jzfb_free_fb_info(cfb); ++ return 0; ++} ++#endif ++ ++#if 0 ++static struct device_driver jzfb_driver = { ++ .name = "jz-lcd", ++ .bus = &platform_bus_type, ++ .probe = jzfb_probe, ++ .remove = jzfb_remove, ++ .suspend = jzfb_suspend, ++ .resume = jzfb_resume, ++}; ++#endif ++ ++static void __exit jz4750fb_cleanup(void) ++{ ++ //driver_unregister(&jzfb_driver); ++ //jzfb_remove(); ++} ++ ++module_init(jz4750fb_init); ++module_exit(jz4750fb_cleanup); +diff -urN linux-2.6.24.7.old/drivers/video/jz4750_lcd.h linux-2.6.24.7/drivers/video/jz4750_lcd.h +--- linux-2.6.24.7.old/drivers/video/jz4750_lcd.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/video/jz4750_lcd.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,756 @@ ++/* ++ * linux/drivers/video/jz4750_lcd.h -- Ingenic Jz4750 On-Chip LCD frame buffer device ++ * ++ * Copyright (C) 2005-2008, Ingenic Semiconductor Inc. ++ * ++ * 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 __JZ4750_LCD_H__ ++#define __JZ4750_LCD_H__ ++ ++//#include ++ ++ ++#define NR_PALETTE 256 ++#define PALETTE_SIZE (NR_PALETTE*2) ++ ++/* use new descriptor(8 words) */ ++struct jz4750_lcd_dma_desc { ++ unsigned int next_desc; /* LCDDAx */ ++ unsigned int databuf; /* LCDSAx */ ++ unsigned int frame_id; /* LCDFIDx */ ++ unsigned int cmd; /* LCDCMDx */ ++ unsigned int offsize; /* Stride Offsize(in word) */ ++ unsigned int page_width; /* Stride Pagewidth(in word) */ ++ unsigned int cmd_num; /* Command Number(for SLCD) */ ++ unsigned int desc_size; /* Foreground Size */ ++}; ++ ++struct jz4750lcd_panel_t { ++ unsigned int cfg; /* panel mode and pin usage etc. */ ++ unsigned int slcd_cfg; /* Smart lcd configurations */ ++ unsigned int ctrl; /* lcd controll register */ ++ unsigned int w; /* Panel Width(in pixel) */ ++ unsigned int h; /* Panel Height(in line) */ ++ unsigned int fclk; /* frame clk */ ++ unsigned int hsw; /* hsync width, in pclk */ ++ unsigned int vsw; /* vsync width, in line count */ ++ unsigned int elw; /* end of line, in pclk */ ++ unsigned int blw; /* begin of line, in pclk */ ++ unsigned int efw; /* end of frame, in line count */ ++ unsigned int bfw; /* begin of frame, in line count */ ++}; ++ ++ ++struct jz4750lcd_fg_t { ++ int bpp; /* foreground bpp */ ++ int x; /* foreground start position x */ ++ int y; /* foreground start position y */ ++ int w; /* foreground width */ ++ int h; /* foreground height */ ++}; ++ ++struct jz4750lcd_osd_t { ++ unsigned int osd_cfg; /* OSDEN, ALHPAEN, F0EN, F1EN, etc */ ++ unsigned int osd_ctrl; /* IPUEN, OSDBPP, etc */ ++ unsigned int rgb_ctrl; /* RGB Dummy, RGB sequence, RGB to YUV */ ++ unsigned int bgcolor; /* background color(RGB888) */ ++ unsigned int colorkey0; /* foreground0's Colorkey enable, Colorkey value */ ++ unsigned int colorkey1; /* foreground1's Colorkey enable, Colorkey value */ ++ unsigned int alpha; /* ALPHAEN, alpha value */ ++ unsigned int ipu_restart; /* IPU Restart enable, ipu restart interval time */ ++ ++#define FG_NOCHANGE 0x0000 ++#define FG0_CHANGE_SIZE 0x0001 ++#define FG0_CHANGE_POSITION 0x0002 ++#define FG1_CHANGE_SIZE 0x0010 ++#define FG1_CHANGE_POSITION 0x0020 ++#define FG_CHANGE_ALL ( FG0_CHANGE_SIZE | FG0_CHANGE_POSITION | \ ++ FG1_CHANGE_SIZE | FG1_CHANGE_POSITION ) ++ int fg_change; ++ struct jz4750lcd_fg_t fg0; /* foreground 0 */ ++ struct jz4750lcd_fg_t fg1; /* foreground 1 */ ++}; ++ ++struct jz4750lcd_info { ++ struct jz4750lcd_panel_t panel; ++ struct jz4750lcd_osd_t osd; ++}; ++ ++ ++/* Jz LCDFB supported I/O controls. */ ++#define FBIOSETBACKLIGHT 0x4688 /* set back light level */ ++#define FBIODISPON 0x4689 /* display on */ ++#define FBIODISPOFF 0x468a /* display off */ ++#define FBIORESET 0x468b /* lcd reset */ ++#define FBIOPRINT_REG 0x468c /* print lcd registers(debug) */ ++#define FBIOROTATE 0x46a0 /* rotated fb */ ++#define FBIOGETBUFADDRS 0x46a1 /* get buffers addresses */ ++#define FBIO_GET_MODE 0x46a2 /* get lcd info */ ++#define FBIO_SET_MODE 0x46a3 /* set osd mode */ ++#define FBIO_DEEP_SET_MODE 0x46a4 /* set panel and osd mode */ ++#define FBIO_MODE_SWITCH 0x46a5 /* switch mode between LCD and TVE */ ++#define FBIO_GET_TVE_MODE 0x46a6 /* get tve info */ ++#define FBIO_SET_TVE_MODE 0x46a7 /* set tve mode */ ++ ++/* ++ * LCD panel specific definition ++ */ ++/* AUO */ ++#if defined(CONFIG_JZ4750_LCD_AUO_A043FL01V2) ++#if defined(CONFIG_JZ4750_APUS) /* board pavo */ ++ #define SPEN (32*3+29) /*LCD_CS*/ ++ #define SPCK (32*3+26) /*LCD_SCL*/ ++ #define SPDA (32*3+27) /*LCD_SDA*/ ++ #define LCD_RET (32*4+25) /*LCD_DISP_N use for lcd reset*/ ++#elif defined(CONFIG_JZ4750_FUWA) /* board pavo */ ++ #define SPEN (32*3+29) /*LCD_CS*/ ++ #define SPCK (32*3+26) /*LCD_SCL*/ ++ #define SPDA (32*3+27) /*LCD_SDA*/ ++ #define LCD_RET (32*5+2) /*LCD_DISP_N use for lcd reset*/ ++#else ++#error "driver/video/Jzlcd.h, please define SPI pins on your board." ++#endif ++ ++#define __spi_write_reg(reg, val) \ ++ do { \ ++ unsigned char no; \ ++ unsigned short value; \ ++ unsigned char a=0; \ ++ unsigned char b=0; \ ++ __gpio_as_output(SPEN); /* use SPDA */ \ ++ __gpio_as_output(SPCK); /* use SPCK */ \ ++ __gpio_as_output(SPDA); /* use SPDA */ \ ++ a=reg; \ ++ b=val; \ ++ __gpio_set_pin(SPEN); \ ++ __gpio_clear_pin(SPCK); \ ++ udelay(50); \ ++ __gpio_clear_pin(SPDA); \ ++ __gpio_clear_pin(SPEN); \ ++ udelay(50); \ ++ value=((a<<8)|(b&0xFF)); \ ++ for(no=0;no<16;no++) \ ++ { \ ++ if((value&0x8000)==0x8000){ \ ++ __gpio_set_pin(SPDA);} \ ++ else{ \ ++ __gpio_clear_pin(SPDA); } \ ++ udelay(50); \ ++ __gpio_set_pin(SPCK); \ ++ value=(value<<1); \ ++ udelay(50); \ ++ __gpio_clear_pin(SPCK); \ ++ } \ ++ __gpio_set_pin(SPEN); \ ++ udelay(400); \ ++ } while (0) ++#define __spi_read_reg(reg,val) \ ++ do{ \ ++ unsigned char no; \ ++ unsigned short value; \ ++ __gpio_as_output(SPEN); /* use SPDA */ \ ++ __gpio_as_output(SPCK); /* use SPCK */ \ ++ __gpio_as_output(SPDA); /* use SPDA */ \ ++ value = ((reg << 0) | (1 << 7)); \ ++ val = 0; \ ++ __gpio_as_output(SPDA); \ ++ __gpio_set_pin(SPEN); \ ++ __gpio_clear_pin(SPCK); \ ++ udelay(50); \ ++ __gpio_clear_pin(SPDA); \ ++ __gpio_clear_pin(SPEN); \ ++ udelay(50); \ ++ for (no = 0; no < 16; no++ ) { \ ++ udelay(50); \ ++ if(no < 8) \ ++ { \ ++ if (value & 0x80) /* send data */ \ ++ __gpio_set_pin(SPDA); \ ++ else \ ++ __gpio_clear_pin(SPDA); \ ++ udelay(50); \ ++ __gpio_set_pin(SPCK); \ ++ value = (value << 1); \ ++ udelay(50); \ ++ __gpio_clear_pin(SPCK); \ ++ if(no == 7) \ ++ __gpio_as_input(SPDA); \ ++ } \ ++ else \ ++ { \ ++ udelay(100); \ ++ __gpio_set_pin(SPCK); \ ++ udelay(50); \ ++ val = (val << 1); \ ++ val |= __gpio_get_pin(SPDA); \ ++ __gpio_clear_pin(SPCK); \ ++ } \ ++ } \ ++ __gpio_as_output(SPDA); \ ++ __gpio_set_pin(SPEN); \ ++ udelay(400); \ ++ } while(0) ++ ++#define __lcd_special_pin_init() \ ++ do { \ ++ __gpio_as_output(SPEN); /* use SPDA */ \ ++ __gpio_as_output(SPCK); /* use SPCK */ \ ++ __gpio_as_output(SPDA); /* use SPDA */ \ ++ __gpio_as_output(LCD_RET); \ ++ udelay(50); \ ++ __gpio_clear_pin(LCD_RET); \ ++ udelay(100); \ ++ __gpio_set_pin(LCD_RET); \ ++ } while (0) ++#define __lcd_special_on() \ ++ do { \ ++ udelay(50); \ ++ __gpio_clear_pin(LCD_RET); \ ++ udelay(100); \ ++ __gpio_set_pin(LCD_RET); \ ++} while (0) ++ ++ #define __lcd_special_off() \ ++ do { \ ++ __gpio_clear_pin(LCD_RET); \ ++ } while (0) ++ ++#endif /* CONFIG_JZLCD_AUO_A030FL01_V1 */ ++ ++/* TRULY_TFTG320240DTSW */ ++#if defined(CONFIG_JZ4750_LCD_TRULY_TFTG320240DTSW_16BIT) || defined(CONFIG_JZ4750_LCD_TRULY_TFTG320240DTSW_18BIT) ++ ++#if defined(CONFIG_JZ4750_FUWA) ++#define LCD_RESET_PIN (32*3+25)// LCD_REV, GPD25 ++#else ++#error "Define LCD_RESET_PIN on your board" ++#endif ++ ++#define __lcd_special_on() \ ++do { \ ++ __gpio_as_output(32*3+30);\ ++ __gpio_clear_pin(32*3+30);\ ++ __gpio_as_output(LCD_RESET_PIN); \ ++ __gpio_set_pin(LCD_RESET_PIN); \ ++ udelay(100); \ ++ __gpio_clear_pin(LCD_RESET_PIN); \ ++ udelay(100); \ ++ __gpio_set_pin(LCD_RESET_PIN); \ ++} while (0) ++ ++#endif /* CONFIG_JZ4750_LCD_TRULY_TFTG320240DTSW */ ++ ++// Wolfgang 2008.02.23 ++#if defined(CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DELTA) || defined(CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DUMMY) ++ ++#if defined(CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DELTA) ++#define PANEL_MODE 0x02 /* RGB Delta */ ++#elif defined(CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DUMMY) ++#define PANEL_MODE 0x00 /* RGB Dummy */ ++#endif ++ ++#if defined(CONFIG_JZ4750_FUWA) /* board FuWa */ ++ #define SPEN (32*3+16) //LCD_D16 - GPD16 ++ #define SPCK (32*3+17) //LCD_D17 - GPD17 ++ #define SPDA (32*3+21) //LCD_DE - GPD21 ++ #define LCD_RET (32*3+25) //LCD_REV - GPD25 //use for lcd reset ++#else ++#error "please define SPI pins on your board." ++#endif ++ ++ #define __spi_write_reg1(reg, val) \ ++ do { \ ++ unsigned char no;\ ++ unsigned short value;\ ++ unsigned char a=0;\ ++ unsigned char b=0;\ ++ a=reg;\ ++ b=val;\ ++ __gpio_set_pin(SPEN);\ ++ udelay(100);\ ++ __gpio_clear_pin(SPCK);\ ++ __gpio_clear_pin(SPDA);\ ++ __gpio_clear_pin(SPEN);\ ++ udelay(25);\ ++ value=((a<<8)|(b&0xFF));\ ++ for(no=0;no<16;no++)\ ++ {\ ++ __gpio_clear_pin(SPCK);\ ++ if((value&0x8000)==0x8000)\ ++ __gpio_set_pin(SPDA);\ ++ else\ ++ __gpio_clear_pin(SPDA);\ ++ udelay(25);\ ++ __gpio_set_pin(SPCK);\ ++ value=(value<<1); \ ++ udelay(25);\ ++ }\ ++ __gpio_clear_pin(SPCK);\ ++ __gpio_set_pin(SPEN);\ ++ udelay(100);\ ++ } while (0) ++ ++ #define __spi_write_reg(reg, val) \ ++ do {\ ++ __spi_write_reg1((reg<<2), val); \ ++ udelay(100); \ ++ }while(0) ++ ++ #define __lcd_special_pin_init() \ ++ do { \ ++ __gpio_as_output(SPEN); /* use SPDA */\ ++ __gpio_as_output(SPCK); /* use SPCK */\ ++ __gpio_as_output(SPDA); /* use SPDA */\ ++ __gpio_as_output(SPDA); /* use reset */\ ++ __gpio_as_output(LCD_RET); /* use reset */\ ++ __gpio_set_pin(LCD_RET);\ ++ mdelay(15);\ ++ __gpio_clear_pin(LCD_RET);\ ++ mdelay(15);\ ++ __gpio_set_pin(LCD_RET);\ ++ } while (0) ++ ++ #define __lcd_special_on() \ ++ do { \ ++ mdelay(10); \ ++ __spi_write_reg(0x00, 0x10); \ ++ __spi_write_reg(0x01, 0xB1); \ ++ __spi_write_reg(0x00, 0x10); \ ++ __spi_write_reg(0x01, 0xB1); \ ++ __spi_write_reg(0x02, PANEL_MODE); /* RGBD MODE */ \ ++ __spi_write_reg(0x03, 0x01); /* Noninterlace*/ \ ++ mdelay(10); \ ++ } while (0) ++ ++ #define __lcd_special_off() \ ++ do { \ ++ } while (0) ++ ++#endif /* CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DELTA */ ++ ++ ++#if defined(CONFIG_JZ4750_LCD_FOXCONN_PT035TN01) || defined(CONFIG_JZ4750_LCD_INNOLUX_PT035TN01_SERIAL) ++ ++#if defined(CONFIG_JZ4750_LCD_FOXCONN_PT035TN01) /* board FUWA */ ++#define MODE 0xcd /* 24bit parellel RGB */ ++#endif ++#if defined(CONFIG_JZ4750_LCD_INNOLUX_PT035TN01_SERIAL) ++#define MODE 0xc9 /* 8bit serial RGB */ ++#endif ++ ++#if defined(CONFIG_JZ4750_FUWA) /* board FuWa */ ++#if 0 ++ #define SPEN (32*5+7) //LCD_SPL GPF7 ++ #define SPCK (32*5+6) //LCD_CLS GPF6 ++ #define SPDA (32*5+5) //LCD_PS GPF5 ++ #define LCD_RET (32*5+4) //LCD_REV GPF4 //use for lcd reset ++#endif ++ #define SPEN (32*3+29) /*LCD_CS*/ ++ #define SPCK (32*3+26) /*LCD_SCL*/ ++ #define SPDA (32*3+27) /*LCD_SDA*/ ++ #define LCD_RET (32*4+25) /*LCD_DISP_N use for lcd reset*/ ++#else ++#error "driver/video/Jzlcd.h, please define SPI pins on your board." ++#endif ++ ++ #define __spi_write_reg1(reg, val) \ ++ do { \ ++ unsigned char no;\ ++ unsigned short value;\ ++ unsigned char a=0;\ ++ unsigned char b=0;\ ++ a=reg;\ ++ b=val;\ ++ __gpio_set_pin(SPEN);\ ++ __gpio_set_pin(SPCK);\ ++ __gpio_clear_pin(SPDA);\ ++ __gpio_clear_pin(SPEN);\ ++ udelay(25);\ ++ value=((a<<8)|(b&0xFF));\ ++ for(no=0;no<16;no++)\ ++ {\ ++ __gpio_clear_pin(SPCK);\ ++ if((value&0x8000)==0x8000)\ ++ __gpio_set_pin(SPDA);\ ++ else\ ++ __gpio_clear_pin(SPDA);\ ++ udelay(25);\ ++ __gpio_set_pin(SPCK);\ ++ value=(value<<1); \ ++ udelay(25);\ ++ }\ ++ __gpio_set_pin(SPEN);\ ++ udelay(100);\ ++ } while (0) ++ ++ #define __spi_write_reg(reg, val) \ ++ do {\ ++ __spi_write_reg1((reg<<2|2), val); \ ++ udelay(100); \ ++ }while(0) ++ ++ #define __lcd_special_pin_init() \ ++ do { \ ++ __gpio_as_output(SPEN); /* use SPDA */\ ++ __gpio_as_output(SPCK); /* use SPCK */\ ++ __gpio_as_output(SPDA); /* use SPDA */\ ++ __gpio_as_output(LCD_RET);\ ++ udelay(50);\ ++ __gpio_clear_pin(LCD_RET);\ ++ mdelay(150);\ ++ __gpio_set_pin(LCD_RET);\ ++ } while (0) ++ ++ #define __lcd_special_on() \ ++ do { \ ++ udelay(50);\ ++ __gpio_clear_pin(LCD_RET);\ ++ mdelay(150);\ ++ __gpio_set_pin(LCD_RET);\ ++ mdelay(10);\ ++ __spi_write_reg(0x00, 0x03); \ ++ __spi_write_reg(0x01, 0x40); \ ++ __spi_write_reg(0x02, 0x11); \ ++ __spi_write_reg(0x03, MODE); /* mode */ \ ++ __spi_write_reg(0x04, 0x32); \ ++ __spi_write_reg(0x05, 0x0e); \ ++ __spi_write_reg(0x07, 0x03); \ ++ __spi_write_reg(0x08, 0x08); \ ++ __spi_write_reg(0x09, 0x32); \ ++ __spi_write_reg(0x0A, 0x88); \ ++ __spi_write_reg(0x0B, 0xc6); \ ++ __spi_write_reg(0x0C, 0x20); \ ++ __spi_write_reg(0x0D, 0x20); \ ++ } while (0) //reg 0x0a is control the display direction:DB0->horizontal level DB1->vertical level ++ ++/* __spi_write_reg(0x02, 0x03); \ ++ __spi_write_reg(0x06, 0x40); \ ++ __spi_write_reg(0x0a, 0x11); \ ++ __spi_write_reg(0x0e, 0xcd); \ ++ __spi_write_reg(0x12, 0x32); \ ++ __spi_write_reg(0x16, 0x0e); \ ++ __spi_write_reg(0x1e, 0x03); \ ++ __spi_write_reg(0x22, 0x08); \ ++ __spi_write_reg(0x26, 0x40); \ ++ __spi_write_reg(0x2a, 0x88); \ ++ __spi_write_reg(0x2e, 0x88); \ ++ __spi_write_reg(0x32, 0x20); \ ++ __spi_write_reg(0x36, 0x20); \ ++*/ ++// } while (0) //reg 0x0a is control the display direction:DB0->horizontal level DB1->vertical level ++ ++ #define __lcd_special_off() \ ++ do { \ ++ __spi_write_reg(0x00, 0x03); \ ++ } while (0) ++ ++#endif /* CONFIG_JZ4750_LCD_FOXCONN_PT035TN01 or CONFIG_JZ4750_LCD_INNOLUX_PT035TN01_SERIAL */ ++ ++#if defined(CONFIG_JZ4750_LCD_TRULY_TFT_GG1P0319LTSW_W) ++static inline void CmdWrite(unsigned int cmd) ++{ ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); /* wait slcd ready */ ++ udelay(30); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | cmd; ++} ++ ++static inline void DataWrite(unsigned int data) ++{ ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); /* wait slcd ready */ ++// udelay(30); ++ REG_SLCD_DATA = SLCD_DATA_RS_DATA | data; ++} ++ ++ ++static inline void delay(long delay_time) ++{ ++ long cnt; ++ ++// delay_time *= (384/8); ++ delay_time *= (43/8); ++ ++ for (cnt=0;cnt SETP 3 ++ CmdWrite(0x0000); ++ CmdWrite(0x01A0); ++ CmdWrite(0x3B01); ++ ++ CmdWrite(0x2809); ++ delay(1000); ++ CmdWrite(0x1900); ++ delay(1000); ++ CmdWrite(0x2110); ++ delay(1000); ++ CmdWrite(0x1805); ++ delay(1000); ++ CmdWrite(0x1E01); ++ delay(1000); ++ CmdWrite(0x1847); ++ delay(1000); ++ CmdWrite(0x1867); ++ delay(1000); ++ CmdWrite(0x18F7); ++ delay(1000); ++ CmdWrite(0x2100); ++ delay(1000); ++ CmdWrite(0x2809); ++ delay(1000); ++ CmdWrite(0x1A05); ++ delay(1000); ++ CmdWrite(0x19E8); ++ delay(1000); ++ CmdWrite(0x1F64); ++ delay(1000); ++ CmdWrite(0x2045); ++ delay(1000); ++ CmdWrite(0x1E81); ++ delay(1000); ++ CmdWrite(0x1B09); ++ delay(1000); ++ CmdWrite(0x0020); ++ delay(1000); ++ CmdWrite(0x0120); ++ delay(1000); ++ ++ CmdWrite(0x3B01); ++ delay(1000); ++ ++ /* Set Window(239,319), Set Cursor(239,319) */ ++ CmdWrite(0x0510); ++ CmdWrite(0x01C0); ++ CmdWrite(0x4500); ++ CmdWrite(0x46EF); ++ CmdWrite(0x4800); ++ CmdWrite(0x4700); ++ CmdWrite(0x4A3F); ++ CmdWrite(0x4901); ++ CmdWrite(0x42EF); ++ CmdWrite(0x443F); ++ CmdWrite(0x4301); ++ ++} ++ ++#if defined(CONFIG_JZ4750_FUWA) ++//#define PIN_CS_N (32*2+xx) /* a low voltage */ ++#define PIN_RD_N (32*3+21) /* LCD_DE: GP D21, a high voltage */ ++#define PIN_RESET_N (32*3+25) /* LCD_REV GP D25 */ ++#else ++#error "Define special lcd pins for your platform." ++#endif ++ ++#define __lcd_slcd_pin_init() \ ++ do { \ ++ __gpio_as_output(PIN_RD_N); /* RD#: LCD_REV */ \ ++ __gpio_as_output(PIN_RESET_N); /* RESET#: LCD_SPL */ \ ++ __gpio_set_pin(PIN_RD_N); /*set read signal high */ \ ++ __gpio_set_pin(PIN_RESET_N); \ ++ mdelay(100); \ ++ __gpio_clear_pin(PIN_RESET_N); \ ++ mdelay(100); \ ++ __gpio_set_pin(PIN_RESET_N); \ ++ /* Configure SLCD module */ \ ++ REG_LCD_CTRL &= ~(LCD_CTRL_ENA|LCD_CTRL_DIS); /* disable lcdc */ \ ++ REG_LCD_CFG = LCD_CFG_LCDPIN_SLCD | 0x0D; /* LCM */ \ ++ REG_SLCD_CTRL &= ~SLCD_CTRL_DMA_EN; /* disable slcd dma */ \ ++ REG_SLCD_CFG = SLCD_CFG_DWIDTH_16BIT | SLCD_CFG_CWIDTH_16BIT | SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING | SLCD_CFG_TYPE_PARALLEL; \ ++ REG_LCD_REV = 0x04; /* lcd clock??? */ \ ++ printk("Fuwa test, pixclk divide REG_LCD_REV=0x%08x\n", REG_LCD_REV); \ ++}while (0) ++ ++#define __lcd_slcd_special_on() \ ++ do { \ ++ __lcd_slcd_pin_init(); \ ++ SlcdInit(); \ ++ REG_SLCD_CTRL |= SLCD_CTRL_DMA_EN; /* slcdc dma enable */ \ ++ } while (0) ++ ++#endif /* #if CONFIG_JZ4750_LCD_TRULY_TFT_GG1P0319LTSW_W */ ++ ++#ifndef __lcd_special_pin_init ++#define __lcd_special_pin_init() ++#endif ++#ifndef __lcd_special_on ++#define __lcd_special_on() ++#endif ++#ifndef __lcd_special_off ++#define __lcd_special_off() ++#endif ++ ++ ++/* ++ * Platform specific definition ++ */ ++#if defined(CONFIG_SOC_JZ4750) ++ ++#if defined(CONFIG_JZ4750_APUS) /* board apus */ ++#define __lcd_display_pin_init() \ ++do { \ ++ __gpio_as_output(GPIO_LCD_VCC_EN_N); \ ++ __lcd_special_pin_init(); \ ++} while (0) ++#define __lcd_display_on() \ ++do { \ ++ __gpio_clear_pin(GPIO_LCD_VCC_EN_N); \ ++ __lcd_special_on(); \ ++ __lcd_set_backlight_level(80); \ ++} while (0) ++ ++#define __lcd_display_off() \ ++do { \ ++ __lcd_close_backlight(); \ ++ __lcd_special_off(); \ ++} while (0) ++ ++#else /* other boards */ ++ ++#define __lcd_display_pin_init() \ ++do { \ ++ __lcd_special_pin_init(); \ ++} while (0) ++#define __lcd_display_on() \ ++do { \ ++ __lcd_special_on(); \ ++ __lcd_set_backlight_level(80); \ ++} while (0) ++ ++#define __lcd_display_off() \ ++do { \ ++ __lcd_close_backlight(); \ ++ __lcd_special_off(); \ ++} while (0) ++#endif /* APUS */ ++#endif /* CONFIG_SOC_JZ4750 */ ++ ++ ++/***************************************************************************** ++ * LCD display pin dummy macros ++ *****************************************************************************/ ++ ++#ifndef __lcd_display_pin_init ++#define __lcd_display_pin_init() ++#endif ++#ifndef __lcd_slcd_special_on ++#define __lcd_slcd_special_on() ++#endif ++#ifndef __lcd_display_on ++#define __lcd_display_on() ++#endif ++#ifndef __lcd_display_off ++#define __lcd_display_off() ++#endif ++#ifndef __lcd_set_backlight_level ++#define __lcd_set_backlight_level(n) ++#endif ++ ++#endif /* __JZ4750_LCD_H__ */ +diff -urN linux-2.6.24.7.old/drivers/video/jz4750_tve.c linux-2.6.24.7/drivers/video/jz4750_tve.c +--- linux-2.6.24.7.old/drivers/video/jz4750_tve.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/video/jz4750_tve.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,104 @@ ++ ++/* ++ * linux/drivers/video/jz4750_tve.c -- Ingenic Jz4750 TVE Controller operation ++ * interface. ++ * Copyright (C) 2005-2008, Ingenic Semiconductor Inc. ++ * Author: Wolfgang Wang, ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++#include ++#include "jz4750_tve.h" ++ ++struct jz4750tve_info jz4750_tve_info_PAL = { ++ .ctrl = (4 << TVE_CTRL_YCDLY_BIT) | TVE_CTRL_SYNCT | TVE_CTRL_PAL | TVE_CTRL_SWRST, /* PAL, SVIDEO */ ++ .frcfg = (23 << TVE_FRCFG_L1ST_BIT) | (625 << TVE_FRCFG_NLINE_BIT), ++ .slcfg1 = (800<ctrl = (jz4750_tve_info->ctrl | TVE_CTRL_DAPD) & ( ~( TVE_CTRL_DAPD1 | TVE_CTRL_DAPD2)); ++ jz4750_tve_info->ctrl &= ~TVE_CTRL_SWRST; ++ REG_TVE_CTRL = jz4750_tve_info->ctrl; ++} ++ ++/* turn off TVE, turn off DACn... */ ++void jz4750tve_disable_tve(void) ++{ ++ jz4750_tve_info->ctrl &= ~TVE_CTRL_DAPD;/* DACn disabled??? */ ++ jz4750_tve_info->ctrl |= TVE_CTRL_SWRST;/* DACn disabled??? */ ++ REG_TVE_CTRL = jz4750_tve_info->ctrl; ++} ++ ++void jz4750tve_set_tve_mode( struct jz4750tve_info *tve ) ++{ ++ REG_TVE_CTRL = tve->ctrl; ++ REG_TVE_FRCFG = tve->frcfg; ++ REG_TVE_SLCFG1 = tve->slcfg1; ++ REG_TVE_SLCFG2 = tve->slcfg2; ++ REG_TVE_SLCFG3 = tve->slcfg3; ++ REG_TVE_LTCFG1 = tve->ltcfg1; ++ REG_TVE_LTCFG2 = tve->ltcfg2; ++ REG_TVE_CFREQ = tve->cfreq; ++ REG_TVE_CPHASE = tve->cphase; ++ REG_TVE_CBCRCFG = tve->cbcrcfg; ++ REG_TVE_WSSCR = tve->wsscr; ++ REG_TVE_WSSCFG1 = tve->wsscfg1; ++ REG_TVE_WSSCFG2 = tve->wsscfg2; ++ REG_TVE_WSSCFG3 = tve->wsscfg3; ++} ++ ++void jz4750tve_init( int tve_mode ) ++{ ++ switch ( tve_mode ) { ++ case PANEL_MODE_TVE_PAL: ++ jz4750_tve_info = &jz4750_tve_info_PAL; ++ break; ++ case PANEL_MODE_TVE_NTSC: ++ jz4750_tve_info = &jz4750_tve_info_NTSC; ++ break; ++ } ++ ++ jz4750tve_set_tve_mode( jz4750_tve_info ); ++// jz4750tve_enable_tve(); ++} +diff -urN linux-2.6.24.7.old/drivers/video/jz4750_tve.h linux-2.6.24.7/drivers/video/jz4750_tve.h +--- linux-2.6.24.7.old/drivers/video/jz4750_tve.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/video/jz4750_tve.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,45 @@ ++#ifndef __JZ4750_TVE_H__ ++#define __JZ4750_TVE_H__ ++ ++ ++#define PANEL_MODE_LCD_PANEL 0 ++#define PANEL_MODE_TVE_PAL 1 ++#define PANEL_MODE_TVE_NTSC 2 ++ ++/* TV parameter */ ++#define TVE_WIDTH_PAL 720 ++#define TVE_HEIGHT_PAL 573 ++#define TVE_FREQ_PAL 50 ++#define TVE_WIDTH_NTSC 720 ++#define TVE_HEIGHT_NTSC 482 ++#define TVE_FREQ_NTSC 60 ++ ++ ++/* Structure for TVE */ ++struct jz4750tve_info { ++ unsigned int ctrl; ++ unsigned int frcfg; ++ unsigned int slcfg1; ++ unsigned int slcfg2; ++ unsigned int slcfg3; ++ unsigned int ltcfg1; ++ unsigned int ltcfg2; ++ unsigned int cfreq; ++ unsigned int cphase; ++ unsigned int cbcrcfg; ++ unsigned int wsscr; ++ unsigned int wsscfg1; ++ unsigned int wsscfg2; ++ unsigned int wsscfg3; ++}; ++ ++extern struct jz4750tve_info *jz4750_tve_info; ++ ++extern void jz4750tve_enable_tve(void); ++extern void jz4750tve_disable_tve(void); ++ ++extern void jz4750tve_set_tve_mode( struct jz4750tve_info *tve ); ++extern void jz4750tve_init( int tve_mode ); ++ ++ ++#endif /* __JZ4750_TVE_H__ */ +diff -urN linux-2.6.24.7.old/drivers/video/jz_kgm_spfd5420a.h linux-2.6.24.7/drivers/video/jz_kgm_spfd5420a.h +--- linux-2.6.24.7.old/drivers/video/jz_kgm_spfd5420a.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/video/jz_kgm_spfd5420a.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,382 @@ ++/* Set registers of smart lcd acording to the following routines ++ * Note: BUS width and CMD width and register value width ++ * This example: BUS is 8, 9, 16 or 18-bit; CMD and DATA is 16-bit ++ * Configure SLCD module to initialize smart lcd registers ++ ++ switch (bus) { ++ case 8: ++ REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_8_x2 ++ | SLCD_CFG_CWIDTH_8BIT | SLCD_CFG_CS_ACTIVE_LOW ++ | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING ++ | SLCD_CFG_TYPE_PARALLEL; ++ __gpio_as_slcd_8bit(); ++ break; ++ case 9: ++ REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_8_x2 ++ | SLCD_CFG_CWIDTH_8BIT | SLCD_CFG_CS_ACTIVE_LOW ++ | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING ++ | SLCD_CFG_TYPE_PARALLEL; ++ __gpio_as_slcd_9bit(); ++ break; ++ case 16: ++ REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_16 ++ | SLCD_CFG_CWIDTH_16BIT | SLCD_CFG_CS_ACTIVE_LOW ++ | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING ++ | SLCD_CFG_TYPE_PARALLEL; ++ __gpio_as_slcd_16bit(); ++ break; ++ case 18: ++ REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_18 ++ | SLCD_CFG_CWIDTH_18BIT | SLCD_CFG_CS_ACTIVE_LOW ++ | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING ++ | SLCD_CFG_TYPE_PARALLEL; ++ __gpio_as_slcd_18bit(); ++ break; ++ } ++ ++ static void Mcupanel_RegSet(unsigned int cmd, unsigned int data) ++ { ++ switch (bus) { ++ case 8: ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_DATA | (data&0xffff); ++ break; ++ case 9: ++ data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); ++ data = ((data << 6) & 0xfc0000) | ((data << 4) & 0xfc00) | ((data << 2) & 0xfc); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_DATA | data; ++ break; ++ case 16: ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | (cmd&0xffff); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_DATA | (data&0xffff); ++ break; ++ case 18: ++ cmd = ((cmd & 0xff) << 1) | ((cmd & 0xff00) << 2); ++ data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | cmd; ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_DATA | ((data<<6)&0xfc0000)|((data<<4)&0xfc00) | ((data<<2)&0xfc); ++ break; ++ default: ++ printk("Don't support %d bit Bus\n", jzfb.bus ); ++ break; ++ } ++ } ++ ++ static void Mcupanel_Command(unsigned int cmd) { ++ switch (bus) { ++ case 8: ++ case 9: ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); ++ break; ++ case 16: ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | (cmd&0xffff); ++ break; ++ case 18: ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) << 2) | ((cmd&0xff) << 1); ++ break; ++ default: ++ printk("Don't support %d bit Bus\n", jzfb.bus ); ++ break; ++ } ++ } ++ ++ *Display---------------------------------------- ++ Note: BUS and BPP, send data to gram data register to display ++ BUS: 8, 9, 16 or 18-bit; BPP: 8, 16, 18-bit ++ switch (bus) { ++ case 8: ++ switch (bpp) { ++ case 8: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; ++ break; ++ case 15: ++ case 16: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; ++ break; ++ case 17 ... 32: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; ++ break; ++ default: ++ printk("The BPP %d is not supported\n", jzfb.bpp); ++ break; ++ } ++ break; ++ case 9: ++ switch (bpp) { ++ case 8: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; ++ break; ++ case 15 ... 16: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; ++ break; ++ case 17 ... 32: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_9_x2; ++ break; ++ default: ++ printk("The BPP %d is not supported\n", jzfb.bpp); ++ break; ++ } ++ break; ++ case 16: ++ switch (bpp) { ++ case 8: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; ++ break; ++ case 15 ... 16: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_16; ++ break; ++ case 17 ... 32: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; ++ break; ++ default: ++ printk("The BPP %d is not supported\n", jzfb.bpp); ++ break; ++ } ++ break; ++ case 18: ++ switch (bpp) { ++ case 8: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; ++ break; ++ case 15: ++ case 16: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_16; ++ break; ++ case 17 ... 32: ++ REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; ++ REG_SLCD_CFG |= SLCD_CFG_DWIDTH_18; ++ break; ++ default: ++ printk("The BPP %d is not supported\n", jzfb.bpp); ++ break; ++ } ++ break; ++ default: ++ printk("Error: The BUS %d is not supported\n", jzfb.bus); ++ break; ++ } ++ dprintk("SLCD_CFG=0x%x\n", REG_SLCD_CFG); ++} ++ ************************************************************************************************/ ++ ++#ifndef __JZ_KGM_SPF5420A_H__ ++#define __JZ_KGM_SPF5420A_H__ ++ ++#include ++ ++#if defined(CONFIG_JZ4750_SLCD_KGM701A3_TFT_SPFD5420A) ++#define WR_GRAM_CMD 0x0202 ++ ++#if defined(CONFIG_JZ4750_FUWA) ++#define PIN_CS_N (32*3+24) // Chip select //GPD24; ++#define PIN_RESET_N (32*5+6) /* LCD_REV GPF6 */ ++#else ++#error "Define special lcd pins for your platform." ++#endif ++ ++/* Sent a command with data (18-bit bus, 16-bit index, 16-bit register value) */ ++static void Mcupanel_RegSet(unsigned int cmd, unsigned int data) ++{ ++ cmd = ((cmd & 0xff) << 1) | ((cmd & 0xff00) << 2); ++ data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); ++ data = ((data<<6)&0xfc0000)|((data<<4)&0xfc00) | ((data<<2)&0xfc); ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | cmd; ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_DATA | data; ++} ++ ++/* Sent a command without data (18-bit bus, 16-bit index) */ ++static void Mcupanel_Command(unsigned int cmd) { ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); ++ REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) << 2) | ((cmd&0xff) << 1); ++} ++ ++/* Set the start address of screen, for example (0, 0) */ ++void Mcupanel_SetAddr(u32 x, u32 y) //u32 ++{ ++ Mcupanel_RegSet(0x200,x) ; ++ udelay(1); ++ Mcupanel_RegSet(0x201,y) ; ++ udelay(1); ++ Mcupanel_Command(0x202); ++ ++} ++ ++#undef __lcd_special_pin_init ++#define __lcd_special_pin_init() \ ++do { \ ++ __gpio_as_output(PIN_CS_N); \ ++ __gpio_as_output(PIN_RESET_N); \ ++ __gpio_clear_pin(PIN_CS_N); /* Clear CS */ \ ++ mdelay(100); \ ++ __gpio_set_pin(PIN_RESET_N); \ ++ mdelay(10); \ ++ __gpio_clear_pin(PIN_RESET_N); \ ++ mdelay(10); \ ++ __gpio_set_pin(PIN_RESET_N); \ ++ mdelay(100); \ ++} while(0) ++ ++ ++#define GAMMA() \ ++do { \ ++ Mcupanel_RegSet(0x0300,0x0101); \ ++ Mcupanel_RegSet(0x0301,0x0b27); \ ++ Mcupanel_RegSet(0x0302,0x132a); \ ++ Mcupanel_RegSet(0x0303,0x2a13); \ ++ Mcupanel_RegSet(0x0304,0x270b); \ ++ Mcupanel_RegSet(0x0305,0x0101); \ ++ Mcupanel_RegSet(0x0306,0x1205); \ ++ Mcupanel_RegSet(0x0307,0x0512); \ ++ Mcupanel_RegSet(0x0308,0x0005); \ ++ Mcupanel_RegSet(0x0309,0x0003); \ ++ Mcupanel_RegSet(0x030a,0x0f04); \ ++ Mcupanel_RegSet(0x030b,0x0f00); \ ++ Mcupanel_RegSet(0x030c,0x000f); \ ++ Mcupanel_RegSet(0x030d,0x040f); \ ++ Mcupanel_RegSet(0x030e,0x0300); \ ++ Mcupanel_RegSet(0x030f,0x0500); \ ++ /*** secorrect gamma2 ***/ \ ++ Mcupanel_RegSet(0x0400,0x3500); \ ++ Mcupanel_RegSet(0x0401,0x0001); \ ++ Mcupanel_RegSet(0x0404,0x0000); \ ++ Mcupanel_RegSet(0x0500,0x0000); \ ++ Mcupanel_RegSet(0x0501,0x0000); \ ++ Mcupanel_RegSet(0x0502,0x0000); \ ++ Mcupanel_RegSet(0x0503,0x0000); \ ++ Mcupanel_RegSet(0x0504,0x0000); \ ++ Mcupanel_RegSet(0x0505,0x0000); \ ++ Mcupanel_RegSet(0x0600,0x0000); \ ++ Mcupanel_RegSet(0x0606,0x0000); \ ++ Mcupanel_RegSet(0x06f0,0x0000); \ ++ Mcupanel_RegSet(0x07f0,0x5420); \ ++ Mcupanel_RegSet(0x07f3,0x288a); \ ++ Mcupanel_RegSet(0x07f4,0x0022); \ ++ Mcupanel_RegSet(0x07f5,0x0001); \ ++ Mcupanel_RegSet(0x07f0,0x0000); \ ++} while(0) ++ ++#define SlcdInit() \ ++do { \ ++ __gpio_set_pin(PIN_RESET_N); \ ++ mdelay(10); \ ++ __gpio_clear_pin(PIN_RESET_N); \ ++ mdelay(10); \ ++ __gpio_set_pin(PIN_RESET_N); \ ++ mdelay(100); \ ++ Mcupanel_RegSet(0x0600, 0x0001); /*soft reset*/ \ ++ mdelay(10); \ ++ Mcupanel_RegSet(0x0600, 0x0000); /*soft reset*/ \ ++ mdelay(10); \ ++ Mcupanel_RegSet(0x0606,0x0000); \ ++ udelay(10); \ ++ Mcupanel_RegSet(0x0007,0x0001); \ ++ udelay(10); \ ++ Mcupanel_RegSet(0x0110,0x0001); \ ++ udelay(10); \ ++ Mcupanel_RegSet(0x0100,0x17b0); \ ++ Mcupanel_RegSet(0x0101,0x0147); \ ++ Mcupanel_RegSet(0x0102,0x019d); \ ++ Mcupanel_RegSet(0x0103,0x8600); \ ++ Mcupanel_RegSet(0x0281,0x0010); \ ++ udelay(10); \ ++ Mcupanel_RegSet(0x0102,0x01bd); \ ++ udelay(10); \ ++ /************initial************/\ ++ Mcupanel_RegSet(0x0000,0x0000); \ ++ Mcupanel_RegSet(0x0001,0x0000); \ ++ Mcupanel_RegSet(0x0002,0x0400); \ ++ Mcupanel_RegSet(0x0003,0x12b8); /*up:0x1288 down:0x12B8 left:0x1290 right:0x12A0*/ \ ++ Mcupanel_RegSet(0x0006,0x0000); \ ++ Mcupanel_RegSet(0x0008,0x0503); \ ++ Mcupanel_RegSet(0x0009,0x0001); \ ++ Mcupanel_RegSet(0x000b,0x0010); \ ++ Mcupanel_RegSet(0x000c,0x0000); \ ++ Mcupanel_RegSet(0x000f,0x0000); \ ++ Mcupanel_RegSet(0x0007,0x0001); \ ++ Mcupanel_RegSet(0x0010,0x0010); \ ++ Mcupanel_RegSet(0x0011,0x0202); \ ++ Mcupanel_RegSet(0x0012,0x0300); \ ++ Mcupanel_RegSet(0x0020,0x021e); \ ++ Mcupanel_RegSet(0x0021,0x0202); \ ++ Mcupanel_RegSet(0x0022,0x0100); \ ++ Mcupanel_RegSet(0x0090,0x0000); \ ++ Mcupanel_RegSet(0x0092,0x0000); \ ++ Mcupanel_RegSet(0x0100,0x16b0); \ ++ Mcupanel_RegSet(0x0101,0x0147); \ ++ Mcupanel_RegSet(0x0102,0x01bd); \ ++ Mcupanel_RegSet(0x0103,0x2c00); \ ++ Mcupanel_RegSet(0x0107,0x0000); \ ++ Mcupanel_RegSet(0x0110,0x0001); \ ++ Mcupanel_RegSet(0x0210,0x0000); \ ++ Mcupanel_RegSet(0x0211,0x00ef); \ ++ Mcupanel_RegSet(0x0212,0x0000); \ ++ Mcupanel_RegSet(0x0213,0x018f); \ ++ Mcupanel_RegSet(0x0280,0x0000); \ ++ Mcupanel_RegSet(0x0281,0x0001); \ ++ Mcupanel_RegSet(0x0282,0x0000); \ ++ GAMMA(); \ ++ Mcupanel_RegSet(0x0007,0x0173); \ ++ Mcupanel_Command(0x0202); /*Write Data to GRAM */ \ ++ udelay(10);\ ++ Mcupanel_SetAddr(0,0);\ ++ udelay(100);\ ++} while(0) ++ ++/*---- LCD Initial ----*/ ++#undef __lcd_slcd_pin_init ++#define __lcd_slcd_pin_init() \ ++ do { \ ++ __lcd_special_pin_init(); \ ++}while (0) ++ ++#undef __lcd_slcd_special_on ++#define __lcd_slcd_special_on() \ ++ do { \ ++ __lcd_slcd_pin_init(); \ ++ SlcdInit(); \ ++ REG_SLCD_CTRL |= SLCD_CTRL_DMA_EN; /* slcdc dma enable */ \ ++ } while (0) ++ ++#define __init_slcd_bus()\ ++do{\ ++ __slcd_set_data_18bit();\ ++ __slcd_set_cmd_18bit();\ ++ __slcd_set_cs_low();\ ++ __slcd_set_rs_low();\ ++ __slcd_set_clk_falling();\ ++ __slcd_set_parallel_type();\ ++}while(0) ++#endif /* #if CONFIG_JZ4750_SLCD_KGM701A3_TFT_SPFD5420A */ ++ ++#endif /* __JZ_KGM_SPF5420A_H__ */ +diff -urN linux-2.6.24.7.old/drivers/video/jz_toppoly_td043mgeb1.h linux-2.6.24.7/drivers/video/jz_toppoly_td043mgeb1.h +--- linux-2.6.24.7.old/drivers/video/jz_toppoly_td043mgeb1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/video/jz_toppoly_td043mgeb1.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,264 @@ ++ ++#ifndef __JZ_KGM_TOPPOLY_TD043MGEB1_H__ ++#define __JZ_KGM_TOPPOLY_TD043MGEB1_H__ ++ ++#include ++ ++#if defined(CONFIG_JZ4750_LCD_TOPPOLY_TD043MGEB1) ++#if defined(CONFIG_JZ4750_APUS) /* board FuWa */ ++ #define SPEN (32*3+29) /*LCD_CS*/ ++ #define SPCK (32*3+26) /*LCD_SCL*/ ++ #define SPDA (32*3+27) /*LCD_SDA*/ ++ #define LCD_RET (32*4+23) /*LCD_DISP_N use for lcd reset*/ ++ #define LCD_STBY (32*4+25) /*LCD_STBY, use for lcd standby*/ ++#else ++#error "driver/video/Jzlcd.h, please define SPI pins on your board." ++#endif ++ ++#define __spi_write_reg(reg, val) \ ++ do { \ ++ unsigned char no; \ ++ unsigned short value; \ ++ unsigned char a=0; \ ++ unsigned char b=0; \ ++ __gpio_as_output(SPEN); /* use SPDA */ \ ++ __gpio_as_output(SPCK); /* use SPCK */ \ ++ __gpio_as_output(SPDA); /* use SPDA */ \ ++ a=reg; \ ++ b=val; \ ++ __gpio_set_pin(SPEN); \ ++ __gpio_clear_pin(SPCK); \ ++ udelay(500); \ ++ __gpio_clear_pin(SPDA); \ ++ __gpio_clear_pin(SPEN); \ ++ udelay(500); \ ++ value=((a<<8)|(b&0xFF)); \ ++ for(no=0;no<16;no++) \ ++ { \ ++ if((value&0x8000)==0x8000){ \ ++ __gpio_set_pin(SPDA);} \ ++ else{ \ ++ __gpio_clear_pin(SPDA); } \ ++ udelay(500); \ ++ __gpio_set_pin(SPCK); \ ++ value=(value<<1); \ ++ udelay(500); \ ++ __gpio_clear_pin(SPCK); \ ++ } \ ++ __gpio_set_pin(SPEN); \ ++ udelay(4000); \ ++ } while (0) ++#define __spi_read_reg(reg,val) \ ++ do{ \ ++ unsigned char no; \ ++ unsigned short value; \ ++ __gpio_as_output(SPEN); /* use SPDA */ \ ++ __gpio_as_output(SPCK); /* use SPCK */ \ ++ __gpio_as_output(SPDA); /* use SPDA */ \ ++ value = ((reg << 0) | (1 << 7)); \ ++ val = 0; \ ++ __gpio_as_output(SPDA); \ ++ __gpio_set_pin(SPEN); \ ++ __gpio_clear_pin(SPCK); \ ++ udelay(50); \ ++ __gpio_clear_pin(SPDA); \ ++ __gpio_clear_pin(SPEN); \ ++ udelay(50); \ ++ for (no = 0; no < 16; no++ ) { \ ++ udelay(50); \ ++ if(no < 8) \ ++ { \ ++ if (value & 0x80) /* send data */ \ ++ __gpio_set_pin(SPDA); \ ++ else \ ++ __gpio_clear_pin(SPDA); \ ++ udelay(50); \ ++ __gpio_set_pin(SPCK); \ ++ value = (value << 1); \ ++ udelay(50); \ ++ __gpio_clear_pin(SPCK); \ ++ if(no == 7) \ ++ __gpio_as_input(SPDA); \ ++ } \ ++ else \ ++ { \ ++ udelay(100); \ ++ __gpio_set_pin(SPCK); \ ++ udelay(50); \ ++ val = (val << 1); \ ++ val |= __gpio_get_pin(SPDA); \ ++ __gpio_clear_pin(SPCK); \ ++ } \ ++ } \ ++ __gpio_as_output(SPDA); \ ++ __gpio_set_pin(SPEN); \ ++ udelay(400); \ ++ } while(0) ++ ++#define __lcd_special_pin_init() \ ++ do { \ ++ __gpio_as_output(SPEN); /* use SPDA */ \ ++ __gpio_as_output(SPCK); /* use SPCK */ \ ++ __gpio_as_output(SPDA); /* use SPDA */ \ ++ __gpio_as_output(LCD_STBY); \ ++ __gpio_as_output(LCD_RET); \ ++ udelay(500); \ ++ __gpio_clear_pin(LCD_RET); \ ++ udelay(1000); \ ++ __gpio_set_pin(LCD_RET); \ ++ udelay(1000); \ ++ __gpio_set_pin(LCD_STBY); \ ++ udelay(1000); \ ++ } while (0) ++#define __lcd_special_on() \ ++ do { \ ++} while (0) ++ ++ #define __lcd_special_off() \ ++ do { \ ++ __gpio_clear_pin(LCD_RET); \ ++ } while (0) ++ ++#endif /* CONFIG_JZLCD_AUO_A030FL01_V1 */ ++ ++#endif /* __JZ_KGM_TOPPOLY_TD043MGEB1_H__ */ ++/* 2.2 ++ __spi_write_reg(0x02, 0x07 ); \ ++ __spi_write_reg(0x03, 0x5f); \ ++ __spi_write_reg(0x04, 0x17); \ ++ __spi_write_reg(0x05, 0x20); \ ++ __spi_write_reg(0x06, 0x08); \ ++ __spi_write_reg(0x07, 0x26); \ ++ __spi_write_reg(0x08, 0x13); \ ++ __spi_write_reg(0x09, 0x33); \ ++ __spi_write_reg(0x0a, 0x20); \ ++ __spi_write_reg(0x0b, 0x20); \ ++ __spi_write_reg(0x0c, 0x20); \ ++ __spi_write_reg(0x0d, 0x20); \ ++ __spi_write_reg(0x0e, 0x10); \ ++ __spi_write_reg(0x0f, 0x10); \ ++ __spi_write_reg(0x10, 0x10); \ ++ __spi_write_reg(0x11, 0x15); \ ++ __spi_write_reg(0x12, 0xaa); \ ++ __spi_write_reg(0x13, 0xff); \ ++ __spi_write_reg(0x14, 0x86); \ ++ __spi_write_reg(0x15, 0x8e); \ ++ __spi_write_reg(0x16, 0xd6); \ ++ __spi_write_reg(0x17, 0xfe); \ ++ __spi_write_reg(0x18, 0x28); \ ++ __spi_write_reg(0x19, 0x52); \ ++ __spi_write_reg(0x1a, 0x7c); \ ++ __spi_write_reg(0x1b, 0xe9); \ ++ __spi_write_reg(0x1c, 0x42); \ ++ __spi_write_reg(0x1d, 0x88); \ ++ __spi_write_reg(0x1e, 0xb8); \ ++ __spi_write_reg(0x1f, 0xff); \ ++ __spi_write_reg(0x20, 0xf0); \ ++ __spi_write_reg(0x21, 0xf0); \ ++ __spi_write_reg(0x22, 0x07); \ ++*/ ++/* 3.1 ++ __spi_write_reg(0x02, 0x07); \ ++ __spi_write_reg(0x03, 0x5f); \ ++ __spi_write_reg(0x04, 0x17); \ ++ __spi_write_reg(0x05, 0x20); \ ++ __spi_write_reg(0x06, 0x08); \ ++ __spi_write_reg(0x07, 0x20); \ ++ __spi_write_reg(0x08, 0x20); \ ++ __spi_write_reg(0x09, 0x20); \ ++ __spi_write_reg(0x0a, 0x20); \ ++ __spi_write_reg(0x0b, 0x20); \ ++ __spi_write_reg(0x0c, 0x20); \ ++ __spi_write_reg(0x0d, 0x22); \ ++ __spi_write_reg(0x0e, 0x10); \ ++ __spi_write_reg(0x0f, 0x10); \ ++ __spi_write_reg(0x10, 0x10); \ ++ __spi_write_reg(0x11, 0x15); \ ++ __spi_write_reg(0x12, 0x6a); \ ++ __spi_write_reg(0x13, 0xff); \ ++ __spi_write_reg(0x14, 0x86); \ ++ __spi_write_reg(0x15, 0x7c); \ ++ __spi_write_reg(0x16, 0xc2); \ ++ __spi_write_reg(0x17, 0xd1); \ ++ __spi_write_reg(0x18, 0xf5); \ ++ __spi_write_reg(0x19, 0x25); \ ++ __spi_write_reg(0x1a, 0x4a); \ ++ __spi_write_reg(0x1b, 0xbf); \ ++ __spi_write_reg(0x1c, 0x15); \ ++ __spi_write_reg(0x1d, 0x6a); \ ++ __spi_write_reg(0x1e, 0xa4); \ ++ __spi_write_reg(0x1f, 0xff); \ ++ __spi_write_reg(0x20, 0xf0); \ ++ __spi_write_reg(0x21, 0xf0); \ ++ __spi_write_reg(0x22, 0x08); \ ++ */ ++ /* 2.5 ++ __spi_write_reg(0x02, 0x07); \ ++ __spi_write_reg(0x03, 0x5f); \ ++ __spi_write_reg(0x04, 0x17); \ ++ __spi_write_reg(0x05, 0x20); \ ++ __spi_write_reg(0x06, 0x08); \ ++ __spi_write_reg(0x07, 0x20); \ ++ __spi_write_reg(0x08, 0x20); \ ++ __spi_write_reg(0x09, 0x20); \ ++ __spi_write_reg(0x0a, 0x20); \ ++ __spi_write_reg(0x0b, 0x20); \ ++ __spi_write_reg(0x0c, 0x20); \ ++ __spi_write_reg(0x0d, 0x22); \ ++ __spi_write_reg(0x0e, 0x10); \ ++ __spi_write_reg(0x0f, 0x10); \ ++ __spi_write_reg(0x10, 0x10); \ ++ __spi_write_reg(0x11, 0x15); \ ++ __spi_write_reg(0x12, 0xaa); \ ++ __spi_write_reg(0x13, 0xff); \ ++ __spi_write_reg(0x14, 0x86); \ ++ __spi_write_reg(0x15, 0x89); \ ++ __spi_write_reg(0x16, 0xc6); \ ++ __spi_write_reg(0x17, 0xea); \ ++ __spi_write_reg(0x18, 0x0c); \ ++ __spi_write_reg(0x19, 0x33); \ ++ __spi_write_reg(0x1a, 0x5e); \ ++ __spi_write_reg(0x1b, 0xd0); \ ++ __spi_write_reg(0x1c, 0x33); \ ++ __spi_write_reg(0x1d, 0x7e); \ ++ __spi_write_reg(0x1e, 0xb3); \ ++ __spi_write_reg(0x1f, 0xff); \ ++ __spi_write_reg(0x20, 0xf0); \ ++ __spi_write_reg(0x21, 0xf0); \ ++ __spi_write_reg(0x22, 0x08); \ ++*/ ++/* ++ __spi_write_reg(0x02, 0x07); \ ++ __spi_write_reg(0x03, 0x5f); \ ++ __spi_write_reg(0x04, 0x17); \ ++ __spi_write_reg(0x05, 0x20); \ ++ __spi_write_reg(0x06, 0x08); \ ++ __spi_write_reg(0x07, 0x20); \ ++ __spi_write_reg(0x08, 0x20); \ ++ __spi_write_reg(0x09, 0x20); \ ++ __spi_write_reg(0x0a, 0x20); \ ++ __spi_write_reg(0x0b, 0x20); \ ++ __spi_write_reg(0x0c, 0x20); \ ++ __spi_write_reg(0x0d, 0x22); \ ++ __spi_write_reg(0x0e, 0x10); \ ++ __spi_write_reg(0x0f, 0x10); \ ++ __spi_write_reg(0x10, 0x10); \ ++ __spi_write_reg(0x11, 0x15); \ ++ __spi_write_reg(0x12, 0xaa); \ ++ __spi_write_reg(0x13, 0xff); \ ++ __spi_write_reg(0x14, 0x86); \ ++ __spi_write_reg(0x15, 0x84); \ ++ __spi_write_reg(0x16, 0xc3); \ ++ __spi_write_reg(0x17, 0xd8); \ ++ __spi_write_reg(0x18, 0x01); \ ++ __spi_write_reg(0x19, 0x28); \ ++ __spi_write_reg(0x1a, 0x53); \ ++ __spi_write_reg(0x1b, 0xc5); \ ++ __spi_write_reg(0x1c, 0x26); \ ++ __spi_write_reg(0x1d, 0x74); \ ++ __spi_write_reg(0x1e, 0xae); \ ++ __spi_write_reg(0x1f, 0xff); \ ++ __spi_write_reg(0x20, 0xf0); \ ++ __spi_write_reg(0x21, 0xf0); \ ++ __spi_write_reg(0x22, 0x08); \ ++ */ +diff -urN linux-2.6.24.7.old/drivers/video/jzlcd.c linux-2.6.24.7/drivers/video/jzlcd.c +--- linux-2.6.24.7.old/drivers/video/jzlcd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/video/jzlcd.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1571 @@ ++/* ++ * linux/drivers/video/jzlcd.c -- Ingenic On-Chip LCD frame buffer device ++ * ++ * Copyright (C) 2005-2007, Ingenic Semiconductor Inc. ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "console/fbcon.h" ++ ++#include "jzlcd.h" ++ ++#undef DEBUG ++//#define DEBUG ++#ifdef DEBUG ++#define dprintk(x...) printk(x) ++#else ++#define dprintk(x...) ++#endif ++ ++#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) ++#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) ++#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) ++#ifdef DEBUG ++#define print_dbg(f, arg...) printk("dbg::" __FILE__ ",LINE(%d): " f "\n", __LINE__, ## arg) ++#else ++#define print_dbg(f, arg...) do {} while (0) ++#endif ++ ++struct lcd_cfb_info { ++ struct fb_info fb; ++ struct display_switch *dispsw; ++ signed int currcon; ++ int func_use_count; ++ ++ struct { ++ u16 red, green, blue; ++ } palette[NR_PALETTE]; ++#ifdef CONFIG_PM ++ struct pm_dev *pm; ++#endif ++#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) ++ struct task_struct *rotate_daemon_thread; ++#endif ++}; ++ ++static struct lcd_cfb_info *jzlcd_info; ++ ++struct jzfb_info { ++ unsigned int cfg; /* panel mode and pin usage etc. */ ++ unsigned int w; ++ unsigned int h; ++ unsigned int bpp; /* bit per pixel */ ++ unsigned int fclk; /* frame clk */ ++ unsigned int hsw; /* hsync width, in pclk */ ++ unsigned int vsw; /* vsync width, in line count */ ++ unsigned int elw; /* end of line, in pclk */ ++ unsigned int blw; /* begin of line, in pclk */ ++ unsigned int efw; /* end of frame, in line count */ ++ unsigned int bfw; /* begin of frame, in line count */ ++}; ++ ++static struct jzfb_info jzfb = { ++#if defined(CONFIG_JZLCD_SHARP_LQ035Q7) ++ MODE_TFT_SHARP | PCLK_N | VSYNC_N, ++ 240, 320, 16, 60, 1, 2, 1, 2, 0, 6 ++#endif ++#if defined(CONFIG_JZLCD_SAMSUNG_LTS350Q1) ++ MODE_TFT_SAMSUNG | PCLK_N, ++ 240, 320, 16, 60, 1, 2, (254-240), 0, 7, 0 ++#endif ++#if defined(CONFIG_JZLCD_SAMSUNG_LTV350QVF04) ++ MODE_TFT_GEN | HSYNC_N | VSYNC_N, ++ 320, 240, 16, 70, 19, 4, 20, 14, 18, 6 ++#endif ++#if defined(CONFIG_JZLCD_SAMSUNG_LTP400WQF01) ++ MODE_TFT_GEN | HSYNC_N | VSYNC_N, ++ 480, 272, 16, 60, 41, 10, 2, 2, 2, 2 ++#endif ++ ++#if defined(CONFIG_JZLCD_SAMSUNG_LTP400WQF02) ++ /* MODE_TFT_18BIT: JZ4740@ version */ ++ MODE_TFT_GEN | MODE_TFT_18BIT | HSYNC_N | VSYNC_N, ++ 480, 272, 32, 60, 41, 10, 2, 2, 2, 2 ++#endif ++#if defined(CONFIG_JZLCD_TRULY_TFTG320240DTSW) ++ MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N, ++ 320, 240, 16, 85, 30, 3, 38, 20, 11, 8 ++#endif ++#if defined(CONFIG_JZLCD_TRULY_TFTG320240DTSW_SERIAL) ++ MODE_8BIT_SERIAL_TFT | HSYNC_N | VSYNC_N | PCLK_N, ++ /* serial mode 280 lines, parallel mode 240 lines */ ++ 320, 280, 32, 60, (30*3), 3, (20*3), (38*3), 46, 23 ++#endif ++#if defined(CONFIG_JZLCD_AUO_A030FL01_V1) ++ MODE_TFT_GEN | MODE_TFT_18BIT | HSYNC_N | VSYNC_N, ++ 480, 272, 32, 60, 39, 10, 8, 4, 4, 2 ++#endif ++#if defined(CONFIG_JZLCD_TRULY_TFTG240320UTSW_63W_E) ++ MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N | DE_N, ++ 320, 240, 16, 60, 3, 3, 3, 3, 3, 85 /* 320x240 */ ++#endif ++#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) && defined(CONFIG_JZ4740_PAVO) ++ MODE_TFT_GEN | HSYNC_N | VSYNC_N | MODE_TFT_18BIT | PCLK_N, ++// 320, 240, 18, 110, 1, 1, 10, 50, 10, 13 ++ 320, 240, 18, 80, 1, 1, 10, 50, 10, 13 ++#endif ++#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) && !(defined(CONFIG_JZ4740_PAVO)) ++ MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N, ++ 320, 240, 16, 110, 1, 1, 10, 50, 10, 13 ++#endif ++#if defined(CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL) ++ MODE_8BIT_SERIAL_TFT | PCLK_N | HSYNC_N | VSYNC_N, ++ 320, 240, 32, 60, 1, 1, 10, 50, 10, 13 ++#endif ++#if defined(CONFIG_JZLCD_HYNIX_HT10X21) ++ MODE_TFT_GEN | PCLK_N, ++ 1024, 768, 16, 45, 1, 1, 75, 0, 3, 0 ++#endif ++#if defined(CONFIG_JZLCD_TOSHIBA_LTM084P363) ++ MODE_TFT_GEN | PCLK_N, ++ 800, 600, 16, 50, 1, 2, 199, 0, 2, 0 ++#endif ++#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42) ++ MODE_TFT_SHARP | PCLK_N, ++ 800, 600, 16, 40, 1, 1, 255, 0, 34, 0 ++#endif ++#if defined(CONFIG_JZLCD_CSTN_800x600) ++ MODE_STN_COLOR_DUAL | STN_DAT_PIN8, ++ 800, 600, 16, 30, 8, 1, 0, 0, 0, 0 ++#endif ++#if defined(CONFIG_JZLCD_CSTN_320x240) ++ MODE_STN_COLOR_SINGLE | STN_DAT_PIN8, ++ 320, 240, 16, 120, 8, 1, 8, 0, 0, 0 ++#endif ++#if defined(CONFIG_JZLCD_MSTN_640x480) ++ MODE_STN_MONO_DUAL | STN_DAT_PIN4, ++ 640, 480, 8, 110, 4, 1, 4, 0, 0, 0 ++#endif ++#if defined(CONFIG_JZLCD_MSTN_320x240) ++ MODE_STN_MONO_SINGLE | STN_DAT_PIN4, ++ 320, 240, 8, 110, 4, 1, 4, 0, 0, 0 ++#endif ++#if defined(CONFIG_JZLCD_MSTN_480x320) ++ MODE_STN_MONO_SINGLE | STN_DAT_PIN8 ++#if defined(CONFIG_JZLCD_MSTN_INVERSE) ++ | DATA_INVERSE ++#endif ++ , 480, 320, 8, 65, 8, 1, 8, 0, 0, 0 ++#endif ++ ++#if defined(CONFIG_JZLCD_MSTN_240x128) ++ MODE_STN_MONO_SINGLE | STN_DAT_PIN1 ++#if defined(CONFIG_JZLCD_MSTN_INVERSE) ++ | DATA_INVERSE ++#endif ++ , 240, 128, 8, 100, 1, 1, 1, 0, 0, 0 ++#endif ++}; ++ ++static struct lcd_desc *lcd_desc_base; ++static struct lcd_desc *lcd_palette_desc; ++static struct lcd_desc *lcd_frame_desc0; ++static struct lcd_desc *lcd_frame_desc1; ++ ++static unsigned char *lcd_palette; ++static unsigned char *lcd_frame[CONFIG_JZLCD_FRAMEBUFFER_MAX]; ++struct jz_lcd_buffer_addrs_t jz_lcd_buffer_addrs; ++//extern struct display fb_display[MAX_NR_CONSOLES]; ++#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) ++static unsigned char *lcd_frame_user_fb; ++/* default rotate angle */ ++static volatile int rotate_angle = CONFIG_JZLCD_FRAMEBUFFER_DEFAULT_ROTATE_ANGLE; ++#endif ++ ++#ifdef DEBUG ++static void print_regs(void) /* debug */ ++{ ++ printk("REG_LCD_CFG:\t0x%8.8x\n", REG_LCD_CFG); ++ printk("REG_LCD_VSYNC:\t0x%8.8x\n", REG_LCD_VSYNC); ++ printk("REG_LCD_HSYNC:\t0x%8.8x\n", REG_LCD_HSYNC); ++ printk("REG_LCD_VAT:\t0x%8.8x\n", REG_LCD_VAT); ++ printk("REG_LCD_DAH:\t0x%8.8x\n", REG_LCD_DAH); ++ printk("REG_LCD_DAV:\t0x%8.8x\n", REG_LCD_DAV); ++ printk("REG_LCD_PS:\t0x%8.8x\n", REG_LCD_PS); ++ printk("REG_LCD_CLS:\t0x%8.8x\n", REG_LCD_CLS); ++ printk("REG_LCD_SPL:\t0x%8.8x\n", REG_LCD_SPL); ++ printk("REG_LCD_REV:\t0x%8.8x\n", REG_LCD_REV); ++ printk("REG_LCD_CTRL:\t0x%8.8x\n", REG_LCD_CTRL); ++ printk("REG_LCD_STATE:\t0x%8.8x\n", REG_LCD_STATE); ++ printk("REG_LCD_IID:\t0x%8.8x\n", REG_LCD_IID); ++ printk("REG_LCD_DA0:\t0x%8.8x\n", REG_LCD_DA0); ++ printk("REG_LCD_SA0:\t0x%8.8x\n", REG_LCD_SA0); ++ printk("REG_LCD_FID0:\t0x%8.8x\n", REG_LCD_FID0); ++ printk("REG_LCD_CMD0:\t0x%8.8x\n", REG_LCD_CMD0); ++ ++ printk("==================================\n"); ++ printk("REG_LCD_VSYNC:\t%d:%d\n", REG_LCD_VSYNC>>16, REG_LCD_VSYNC&0xfff); ++ printk("REG_LCD_HSYNC:\t%d:%d\n", REG_LCD_HSYNC>>16, REG_LCD_HSYNC&0xfff); ++ printk("REG_LCD_VAT:\t%d:%d\n", REG_LCD_VAT>>16, REG_LCD_VAT&0xfff); ++ printk("REG_LCD_DAH:\t%d:%d\n", REG_LCD_DAH>>16, REG_LCD_DAH&0xfff); ++ printk("REG_LCD_DAV:\t%d:%d\n", REG_LCD_DAV>>16, REG_LCD_DAV&0xfff); ++ printk("==================================\n"); ++ ++} ++#else ++#define print_regs() ++#endif ++ ++#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) ++static int jzfb_rotate_daemon_thread(void *info) ++{ ++ int i,j; ++ struct fb_info *fb = &jzlcd_info->fb; ++ ++ while (!kthread_should_stop()) { ++#if (CONFIG_JZLCD_FRAMEBUFFER_BPP == 8) ++ unsigned char *plcd_frame = (unsigned char *)lcd_frame[0]; ++ unsigned char *pfb = (unsigned char *) (fb->screen_base); ++#elif (CONFIG_JZLCD_FRAMEBUFFER_BPP == 16) ++ unsigned short *plcd_frame = (unsigned short *)lcd_frame[0]; ++ unsigned short *pfb = (unsigned short *) (fb->screen_base); ++#elif (CONFIG_JZLCD_FRAMEBUFFER_BPP == 32) ++ unsigned int *plcd_frame = (unsigned int *)lcd_frame[0]; ++ unsigned int *pfb = (unsigned int *) (fb->screen_base); ++#else ++#error "ERROR, rotate not support this bpp." ++#endif ++ switch ( rotate_angle ) { ++ case FB_ROTATE_UR: ++ printk("%s, Warning, this shouldn't reache\n", __FUNCTION__); ++ ssleep(1); ++ break; ++ case FB_ROTATE_UD: /* cost about 30ms, can be accelrated by dma in the future */ ++ plcd_frame += jzfb.w*jzfb.h -1; ++ for (i=0;ivar.height+1; i++) { ++ for (j=1; j < fb->var.width+1; j++) ++ plcd_frame[j*fb->var.height-i] = *pfb++; ++ } ++ msleep(100); /* sleep 100ms */ ++ break; ++ case FB_ROTATE_CCW: /* cost about 80ms */ ++ for (i=0;ivar.height;i++) { ++ for ( j=fb->var.width-1;j>=0;j--) ++ plcd_frame[j*fb->var.height+i] = *pfb++; ++ } ++ msleep(100); /* sleep 100ms */ ++ break; ++ default: /* FB_ROTATE_UR */ ++ dprintk("Unknown rotate(%d) type\n", rotate_angle); ++ ssleep(1); ++ } ++ ++ dma_cache_wback_inv((unsigned int)(lcd_frame_user_fb), fb->fix.smem_len); ++ } ++ return 0; ++} ++/* ++ * rotate param angle: ++ * 0: FB_ROTATE_UR, 0'C ++ * 1: FB_ROTATE_CW, 90'C ++ * 2: FB_ROTATE_UD, 180'C ++ * 3: FB_ROTATE_CCW, 270'C ++ */ ++static int jzfb_rotate_change( int angle ) ++{ ++ struct fb_info *fb = &jzlcd_info->fb; ++ ++ /* clear frame buffer */ ++ memset((void*)lcd_frame_user_fb, 0x00, fb->fix.smem_len); ++ switch ( angle ) { ++ case FB_ROTATE_UR: ++ fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.w; ++ fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.h; ++ /* change lcd controller's data buffer to lcd_frame_user_fb*/ ++ lcd_frame_desc0->databuf = virt_to_phys((void *)lcd_frame_user_fb); ++ if ( rotate_angle != FB_ROTATE_UR ) ++ kthread_stop(jzlcd_info->rotate_daemon_thread); ++ rotate_angle = angle; ++ break; ++ case FB_ROTATE_UD: ++ case FB_ROTATE_CW: ++ case FB_ROTATE_CCW: ++ if ( angle == FB_ROTATE_UD ) { ++ fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.w; ++ fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.h; ++ } ++ else { /* CW, CCW */ ++ fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.h; ++ fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.w; ++ } ++ /* change lcd controller's data buffer to lcd_frame[0]*/ ++ lcd_frame_desc0->databuf = virt_to_phys((void *)lcd_frame[0]); ++ if ( rotate_angle == FB_ROTATE_UR || \ ++ jzlcd_info->rotate_daemon_thread == NULL) ++ jzlcd_info->rotate_daemon_thread = kthread_run( jzfb_rotate_daemon_thread, jzlcd_info, "%s", "jzlcd-rotate-daemon"); /* start rotate daemon */ ++ rotate_angle = angle; ++ break; ++ default: ++ printk("Invalid angle(%d)\n", (unsigned int)angle); ++ } ++ fb->fix.line_length = fb->var.xres * CONFIG_JZLCD_FRAMEBUFFER_BPP/8; ++ dma_cache_wback_inv((unsigned int)(lcd_frame_desc0), sizeof(struct lcd_desc)); ++ return 0; ++} ++ ++void jzfb_fb_rotate(struct fb_info *fbi, int angle) ++{ ++ jzfb_rotate_change( angle/90 ); ++} ++#endif /* #if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) */ ++ ++static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) ++{ ++ chan &= 0xffff; ++ chan >>= 16 - bf->length; ++ return chan << bf->offset; ++} ++ ++static int jzfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, ++ u_int transp, struct fb_info *info) ++{ ++ struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; ++ unsigned short *ptr, ctmp; ++ ++// print_dbg("regno:%d,RGBt:(%d,%d,%d,%d)\t", regno, red, green, blue, transp); ++ if (regno >= NR_PALETTE) ++ return 1; ++ ++ cfb->palette[regno].red = red ; ++ cfb->palette[regno].green = green; ++ cfb->palette[regno].blue = blue; ++ if (cfb->fb.var.bits_per_pixel <= 16) { ++ red >>= 8; ++ green >>= 8; ++ blue >>= 8; ++ ++ red &= 0xff; ++ green &= 0xff; ++ blue &= 0xff; ++ } ++ switch (cfb->fb.var.bits_per_pixel) { ++ case 1: ++ case 2: ++ case 4: ++ case 8: ++ if (((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_SINGLE) || ++ ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) { ++ ctmp = (77L * red + 150L * green + 29L * blue) >> 8; ++ ctmp = ((ctmp >> 3) << 11) | ((ctmp >> 2) << 5) | ++ (ctmp >> 3); ++ } else { ++ /* RGB 565 */ ++ if (((red >> 3) == 0) && ((red >> 2) != 0)) ++ red = 1 << 3; ++ if (((blue >> 3) == 0) && ((blue >> 2) != 0)) ++ blue = 1 << 3; ++ ctmp = ((red >> 3) << 11) ++ | ((green >> 2) << 5) | (blue >> 3); ++ } ++ ++ ptr = (unsigned short *)lcd_palette; ++ ptr = (unsigned short *)(((u32)ptr)|0xa0000000); ++ ptr[regno] = ctmp; ++ ++ break; ++ ++ case 15: ++ if (regno < 16) ++ ((u32 *)cfb->fb.pseudo_palette)[regno] = ++ ((red >> 3) << 10) | ++ ((green >> 3) << 5) | ++ (blue >> 3); ++ break; ++ case 16: ++ if (regno < 16) { ++ ((u32 *)cfb->fb.pseudo_palette)[regno] = ++ ((red >> 3) << 11) | ++ ((green >> 2) << 5) | ++ (blue >> 3); ++ } ++ break; ++ case 18: ++ case 24: ++ case 32: ++ if (regno < 16) ++ ((u32 *)cfb->fb.pseudo_palette)[regno] = ++ (red << 16) | ++ (green << 8) | ++ (blue << 0); ++ ++/* if (regno < 16) { ++ unsigned val; ++ val = chan_to_field(red, &cfb->fb.var.red); ++ val |= chan_to_field(green, &cfb->fb.var.green); ++ val |= chan_to_field(blue, &cfb->fb.var.blue); ++ ((u32 *)cfb->fb.pseudo_palette)[regno] = val; ++ } ++*/ ++ ++ break; ++ } ++ return 0; ++} ++ ++ ++static int jzfb_ioctl (struct fb_info *fb, unsigned int cmd, unsigned long arg ) ++{ ++ int ret = 0; ++ void __user *argp = (void __user *)arg; ++ ++ switch (cmd) { ++ case FBIOSETBACKLIGHT: ++ __lcd_set_backlight_level(arg); /* We support 8 levels here. */ ++ break; ++ case FBIODISPON: ++ __lcd_display_on(); ++ break; ++ case FBIODISPOFF: ++ __lcd_display_off(); ++ break; ++ case FBIOPRINT_REGS: ++ print_regs(); ++ break; ++ case FBIOGETBUFADDRS: ++ if ( copy_to_user(argp, &jz_lcd_buffer_addrs, ++ sizeof(struct jz_lcd_buffer_addrs_t)) ) ++ return -EFAULT; ++ break; ++#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) ++ case FBIOROTATE: ++ ret = jzfb_rotate_change(arg); ++ break; ++#endif /* defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) */ ++ default: ++ printk("Warn: Command(%x) not support\n", cmd); ++ ret = -1; ++ break; ++ } ++ return ret; ++ ++} ++ ++/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */ ++static int jzfb_mmap(struct fb_info *info, struct vm_area_struct *vma) ++{ ++ struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; ++ unsigned long start; ++ unsigned long off; ++ u32 len; ++ ++ off = vma->vm_pgoff << PAGE_SHIFT; ++ //fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); ++ ++ /* frame buffer memory */ ++ start = cfb->fb.fix.smem_start; ++ len = PAGE_ALIGN((start & ~PAGE_MASK) + cfb->fb.fix.smem_len); ++ start &= PAGE_MASK; ++ ++ if ((vma->vm_end - vma->vm_start + off) > len) ++ return -EINVAL; ++ off += start; ++ ++ vma->vm_pgoff = off >> PAGE_SHIFT; ++ vma->vm_flags |= VM_IO; ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* Uncacheable */ ++ ++#if 1 ++ pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; ++ pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; /* Uncacheable */ ++// pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; /* Write-Through */ ++#endif ++ ++ if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, ++ vma->vm_end - vma->vm_start, ++ vma->vm_page_prot)) { ++ return -EAGAIN; ++ } ++ return 0; ++} ++ ++/* checks var and eventually tweaks it to something supported, ++ * DO NOT MODIFY PAR */ ++static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) ++{ ++ print_dbg("jzfb_check_var"); ++ return 0; ++} ++ ++ ++/* ++ * set the video mode according to info->var ++ */ ++static int jzfb_set_par(struct fb_info *info) ++{ ++ print_dbg("jzfb_set_par"); ++ return 0; ++} ++ ++ ++/* ++ * (Un)Blank the display. ++ * Fix me: should we use VESA value? ++ */ ++static int jzfb_blank(int blank_mode, struct fb_info *info) ++{ ++ ++ dprintk("fb_blank %d %p", blank_mode, info); ++ ++ switch (blank_mode) { ++ ++ case FB_BLANK_UNBLANK: ++ //case FB_BLANK_NORMAL: ++ /* Turn on panel */ ++ __lcd_set_ena(); ++ __lcd_display_on(); ++ break; ++ ++ case FB_BLANK_NORMAL: ++ case FB_BLANK_VSYNC_SUSPEND: ++ case FB_BLANK_HSYNC_SUSPEND: ++ case FB_BLANK_POWERDOWN: ++#if 0 ++ /* Turn off panel */ ++ __lcd_set_dis(); ++ __lcd_display_off(); ++#endif ++ break; ++ default: ++ break; ++ ++ } ++ return 0; ++} ++ ++/* ++ * pan display ++ */ ++static int jzfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) ++{ ++ struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; ++ int dy; ++ ++ if (!var || !cfb) { ++ return -EINVAL; ++ } ++ ++ if (var->xoffset - cfb->fb.var.xoffset) { ++ /* No support for X panning for now! */ ++ return -EINVAL; ++ } ++ ++ dy = var->yoffset - cfb->fb.var.yoffset; ++ print_dbg("var.yoffset: %d", dy); ++ if (dy) { ++ ++ print_dbg("Panning screen of %d lines", dy); ++ ++ lcd_frame_desc0->databuf += (cfb->fb.fix.line_length * dy); ++ /* TODO: Wait for current frame to finished */ ++ } ++ ++ return 0; ++} ++ ++ ++/* use default function cfb_fillrect, cfb_copyarea, cfb_imageblit */ ++static struct fb_ops jzfb_ops = { ++ .owner = THIS_MODULE, ++ .fb_setcolreg = jzfb_setcolreg, ++ .fb_check_var = jzfb_check_var, ++ .fb_set_par = jzfb_set_par, ++ .fb_blank = jzfb_blank, ++ .fb_pan_display = jzfb_pan_display, ++ .fb_fillrect = cfb_fillrect, ++ .fb_copyarea = cfb_copyarea, ++ .fb_imageblit = cfb_imageblit, ++ .fb_mmap = jzfb_mmap, ++ .fb_ioctl = jzfb_ioctl, ++#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) ++ .fb_rotate = jzfb_fb_rotate, ++#endif ++}; ++ ++static int jzfb_set_var(struct fb_var_screeninfo *var, int con, ++ struct fb_info *info) ++{ ++ struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; ++ int chgvar = 0; ++ ++ var->height = jzfb.h ; ++ var->width = jzfb.w ; ++ var->bits_per_pixel = jzfb.bpp; ++ ++ var->vmode = FB_VMODE_NONINTERLACED; ++ var->activate = cfb->fb.var.activate; ++ var->xres = var->width; ++ var->yres = var->height; ++ var->xres_virtual = var->width; ++ var->yres_virtual = var->height; ++ var->xoffset = 0; ++ var->yoffset = 0; ++ var->pixclock = 0; ++ var->left_margin = 0; ++ var->right_margin = 0; ++ var->upper_margin = 0; ++ var->lower_margin = 0; ++ var->hsync_len = 0; ++ var->vsync_len = 0; ++ var->sync = 0; ++ var->activate &= ~FB_ACTIVATE_TEST; ++ ++ /* ++ * CONUPDATE and SMOOTH_XPAN are equal. However, ++ * SMOOTH_XPAN is only used internally by fbcon. ++ */ ++ if (var->vmode & FB_VMODE_CONUPDATE) { ++ var->vmode |= FB_VMODE_YWRAP; ++ var->xoffset = cfb->fb.var.xoffset; ++ var->yoffset = cfb->fb.var.yoffset; ++ } ++ ++ if (var->activate & FB_ACTIVATE_TEST) ++ return 0; ++ ++ if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) ++ return -EINVAL; ++ ++ if (cfb->fb.var.xres != var->xres) ++ chgvar = 1; ++ if (cfb->fb.var.yres != var->yres) ++ chgvar = 1; ++ if (cfb->fb.var.xres_virtual != var->xres_virtual) ++ chgvar = 1; ++ if (cfb->fb.var.yres_virtual != var->yres_virtual) ++ chgvar = 1; ++ if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel) ++ chgvar = 1; ++ ++ var->red.msb_right = 0; ++ var->green.msb_right = 0; ++ var->blue.msb_right = 0; ++ ++ switch(var->bits_per_pixel){ ++ case 1: /* Mono */ ++ cfb->fb.fix.visual = FB_VISUAL_MONO01; ++ cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; ++ break; ++ case 2: /* Mono */ ++ var->red.offset = 0; ++ var->red.length = 2; ++ var->green.offset = 0; ++ var->green.length = 2; ++ var->blue.offset = 0; ++ var->blue.length = 2; ++ ++ cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; ++ cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; ++ break; ++ case 4: /* PSEUDOCOLOUR*/ ++ var->red.offset = 0; ++ var->red.length = 4; ++ var->green.offset = 0; ++ var->green.length = 4; ++ var->blue.offset = 0; ++ var->blue.length = 4; ++ ++ cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; ++ cfb->fb.fix.line_length = var->xres / 2; ++ break; ++ case 8: /* PSEUDOCOLOUR, 256 */ ++ var->red.offset = 0; ++ var->red.length = 8; ++ var->green.offset = 0; ++ var->green.length = 8; ++ var->blue.offset = 0; ++ var->blue.length = 8; ++ ++ cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; ++ cfb->fb.fix.line_length = var->xres ; ++ break; ++ case 15: /* DIRECTCOLOUR, 32k */ ++ var->bits_per_pixel = 15; ++ var->red.offset = 10; ++ var->red.length = 5; ++ var->green.offset = 5; ++ var->green.length = 5; ++ var->blue.offset = 0; ++ var->blue.length = 5; ++ ++ cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR; ++ cfb->fb.fix.line_length = var->xres_virtual * 2; ++ break; ++ case 16: /* DIRECTCOLOUR, 64k */ ++ var->bits_per_pixel = 16; ++ var->red.offset = 11; ++ var->red.length = 5; ++ var->green.offset = 5; ++ var->green.length = 6; ++ var->blue.offset = 0; ++ var->blue.length = 5; ++ ++ cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; ++ cfb->fb.fix.line_length = var->xres_virtual * 2; ++ break; ++ case 18: ++ case 24: ++ case 32: ++ /* DIRECTCOLOUR, 16M */ ++ var->bits_per_pixel = 32; ++ ++ var->red.offset = 16; ++ var->red.length = 8; ++ var->green.offset = 8; ++ var->green.length = 8; ++ var->blue.offset = 0; ++ var->blue.length = 8; ++ var->transp.offset = 24; ++ var->transp.length = 8; ++ ++ cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; ++ cfb->fb.fix.line_length = var->xres_virtual * 4; ++ break; ++ ++ default: /* in theory this should never happen */ ++ printk(KERN_WARNING "%s: don't support for %dbpp\n", ++ cfb->fb.fix.id, var->bits_per_pixel); ++ break; ++ } ++ ++ cfb->fb.var = *var; ++ cfb->fb.var.activate &= ~FB_ACTIVATE_ALL; ++ ++ /* ++ * Update the old var. The fbcon drivers still use this. ++ * Once they are using cfb->fb.var, this can be dropped. ++ * --rmk ++ */ ++ //display->var = cfb->fb.var; ++ /* ++ * If we are setting all the virtual consoles, also set the ++ * defaults used to create new consoles. ++ */ ++ fb_set_cmap(&cfb->fb.cmap, &cfb->fb); ++ dprintk("jzfb_set_var: after fb_set_cmap...\n"); ++ ++ return 0; ++} ++ ++static struct lcd_cfb_info * jzfb_alloc_fb_info(void) ++{ ++ struct lcd_cfb_info *cfb; ++ ++ cfb = kmalloc(sizeof(struct lcd_cfb_info) + sizeof(u32) * 16, GFP_KERNEL); ++ ++ if (!cfb) ++ return NULL; ++ ++ jzlcd_info = cfb; ++ ++ memset(cfb, 0, sizeof(struct lcd_cfb_info) ); ++ ++ cfb->currcon = -1; ++ ++ ++ strcpy(cfb->fb.fix.id, "jz-lcd"); ++ cfb->fb.fix.type = FB_TYPE_PACKED_PIXELS; ++ cfb->fb.fix.type_aux = 0; ++ cfb->fb.fix.xpanstep = 1; ++ cfb->fb.fix.ypanstep = 1; ++ cfb->fb.fix.ywrapstep = 0; ++ cfb->fb.fix.accel = FB_ACCEL_NONE; ++ ++ cfb->fb.var.nonstd = 0; ++ cfb->fb.var.activate = FB_ACTIVATE_NOW; ++ cfb->fb.var.height = -1; ++ cfb->fb.var.width = -1; ++ cfb->fb.var.accel_flags = FB_ACCELF_TEXT; ++ ++ cfb->fb.fbops = &jzfb_ops; ++ cfb->fb.flags = FBINFO_FLAG_DEFAULT; ++ ++ cfb->fb.pseudo_palette = (void *)(cfb + 1); ++ ++ switch (jzfb.bpp) { ++ case 1: ++ fb_alloc_cmap(&cfb->fb.cmap, 4, 0); ++ break; ++ case 2: ++ fb_alloc_cmap(&cfb->fb.cmap, 8, 0); ++ break; ++ case 4: ++ fb_alloc_cmap(&cfb->fb.cmap, 32, 0); ++ break; ++ case 8: ++ ++ default: ++ fb_alloc_cmap(&cfb->fb.cmap, 256, 0); ++ break; ++ } ++ dprintk("fb_alloc_cmap,fb.cmap.len:%d....\n", cfb->fb.cmap.len); ++ ++ return cfb; ++} ++ ++/* ++ * Map screen memory ++ */ ++static int jzfb_map_smem(struct lcd_cfb_info *cfb) ++{ ++ struct page * map = NULL; ++ unsigned char *tmp; ++ unsigned int page_shift, needroom, t; ++#if defined(CONFIG_SOC_JZ4740) ++ if (jzfb.bpp == 18 || jzfb.bpp == 24) ++ t = 32; ++ else ++ t = jzfb.bpp; ++#else ++ if (jzfb.bpp == 15) ++ t = 16; ++ else ++ t = jzfb.bpp; ++#endif ++ ++ needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h; ++ for (page_shift = 0; page_shift < 12; page_shift++) ++ if ((PAGE_SIZE << page_shift) >= needroom) ++ break; ++ ++ /* lcd_palette room total 4KB: ++ * 0 -- 512: lcd palette ++ * 1024 -- [1024+16*3]: lcd descripters ++ * [1024+16*3] -- 4096: reserved ++ */ ++ lcd_palette = (unsigned char *)__get_free_pages(GFP_KERNEL, 0); ++ if ((!lcd_palette)) ++ return -ENOMEM; ++ ++ memset((void *)lcd_palette, 0, PAGE_SIZE); ++ map = virt_to_page(lcd_palette); ++ set_bit(PG_reserved, &map->flags); ++ lcd_desc_base = (struct lcd_desc *)(lcd_palette + 1024); ++ ++ jz_lcd_buffer_addrs.fb_num = CONFIG_JZLCD_FRAMEBUFFER_MAX; ++ printk("jzlcd use %d framebuffer:\n", CONFIG_JZLCD_FRAMEBUFFER_MAX); ++ /* alloc frame buffer space */ ++ for ( t = 0; t < CONFIG_JZLCD_FRAMEBUFFER_MAX; t++ ) { ++ lcd_frame[t] = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); ++ if ((!lcd_frame[t])) { ++ printk("no mem for fb[%d]\n", t); ++ return -ENOMEM; ++ } ++// memset((void *)lcd_frame[t], 0, PAGE_SIZE << page_shift); ++ for (tmp=(unsigned char *)lcd_frame[t]; ++ tmp < lcd_frame[t] + (PAGE_SIZE << page_shift); ++ tmp += PAGE_SIZE) { ++ map = virt_to_page(tmp); ++ set_bit(PG_reserved, &map->flags); ++ } ++ jz_lcd_buffer_addrs.fb_phys_addr[t] = virt_to_phys((void *)lcd_frame[t]); ++ printk("jzlcd fb[%d] phys addr =0x%08x\n", ++ t, jz_lcd_buffer_addrs.fb_phys_addr[t]); ++ } ++#if !defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) ++ cfb->fb.fix.smem_start = virt_to_phys((void *)lcd_frame[0]); ++ cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift); ++ cfb->fb.screen_base = ++ (unsigned char *)(((unsigned int)lcd_frame[0] & 0x1fffffff) | 0xa0000000); ++#else /* Framebuffer rotate */ ++ lcd_frame_user_fb = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); ++ if ((!lcd_frame_user_fb)) { ++ printk("no mem for fb[%d]\n", t); ++ return -ENOMEM; ++ } ++ memset((void *)lcd_frame_user_fb, 0, PAGE_SIZE << page_shift); ++ for (tmp=(unsigned char *)lcd_frame_user_fb; ++ tmp < lcd_frame_user_fb + (PAGE_SIZE << page_shift); ++ tmp += PAGE_SIZE) { ++ map = virt_to_page(tmp); ++ set_bit(PG_reserved, &map->flags); ++ } ++ ++ printk("Rotate userfb phys addr =0x%08x\n", ++ (unsigned int)virt_to_phys((void *)lcd_frame_user_fb)); ++ cfb->fb.fix.smem_start = virt_to_phys((void *)lcd_frame_user_fb); ++ cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift); ++ cfb->fb.screen_base = (unsigned char *)(((unsigned int)lcd_frame_user_fb & 0x1fffffff) | 0xa0000000); ++ ++#endif /* #if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) */ ++ if (!cfb->fb.screen_base) { ++ printk("%s: unable to map screen memory\n", cfb->fb.fix.id); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void jzfb_free_fb_info(struct lcd_cfb_info *cfb) ++{ ++ if (cfb) { ++ fb_alloc_cmap(&cfb->fb.cmap, 0, 0); ++ kfree(cfb); ++ } ++} ++ ++static void jzfb_unmap_smem(struct lcd_cfb_info *cfb) ++{ ++ struct page * map = NULL; ++ unsigned char *tmp; ++ unsigned int page_shift, needroom, t; ++#if defined(CONFIG_SOC_JZ4740) ++ if (jzfb.bpp == 18 || jzfb.bpp == 24) ++ t = 32; ++ else ++ t = jzfb.bpp; ++#else ++ if (jzfb.bpp == 15) ++ t = 16; ++ else ++ t = jzfb.bpp; ++#endif ++ needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h; ++ for (page_shift = 0; page_shift < 12; page_shift++) ++ if ((PAGE_SIZE << page_shift) >= needroom) ++ break; ++ ++ if (cfb && cfb->fb.screen_base) { ++ iounmap(cfb->fb.screen_base); ++ cfb->fb.screen_base = NULL; ++ release_mem_region(cfb->fb.fix.smem_start, ++ cfb->fb.fix.smem_len); ++ } ++ ++ if (lcd_palette) { ++ map = virt_to_page(lcd_palette); ++ clear_bit(PG_reserved, &map->flags); ++ free_pages((int)lcd_palette, 0); ++ } ++ ++ for ( t=0; t < CONFIG_JZLCD_FRAMEBUFFER_MAX; t++ ) { ++ if (lcd_frame[t]) { ++ for (tmp=(unsigned char *)lcd_frame[t]; ++ tmp < lcd_frame[t] + (PAGE_SIZE << page_shift); ++ tmp += PAGE_SIZE) { ++ map = virt_to_page(tmp); ++ clear_bit(PG_reserved, &map->flags); ++ } ++ free_pages((int)lcd_frame[t], page_shift); ++ } ++ } ++#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) ++ if (lcd_frame_user_fb) { ++ for (tmp=(unsigned char *)lcd_frame_user_fb; ++ tmp < lcd_frame_user_fb + (PAGE_SIZE << page_shift); ++ tmp += PAGE_SIZE) { ++ map = virt_to_page(tmp); ++ clear_bit(PG_reserved, &map->flags); ++ } ++ free_pages((int)lcd_frame_user_fb, page_shift); ++ } ++ ++#endif ++} ++ ++static void lcd_descriptor_init(void) ++{ ++ int i; ++ unsigned int pal_size; ++ unsigned int frm_size, ln_size; ++ unsigned char dual_panel = 0; ++ ++ i = jzfb.bpp; ++#if defined(CONFIG_SOC_JZ4740) ++ if (i == 18 || i == 24) ++ i = 32; ++#else ++ if (i == 15) ++ i = 16; ++#endif ++ frm_size = (jzfb.w*jzfb.h*i)>>3; ++ ln_size = (jzfb.w*i)>>3; ++ ++ if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || ++ ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) { ++ dual_panel = 1; ++ frm_size >>= 1; ++ } ++ ++ frm_size = frm_size / 4; ++ ln_size = ln_size / 4; ++ ++ switch (jzfb.bpp) { ++ case 1: ++ pal_size = 4; ++ break; ++ case 2: ++ pal_size = 8; ++ break; ++ case 4: ++ pal_size = 32; ++ break; ++ case 8: ++ default: ++ pal_size = 512; ++ } ++ ++ pal_size /= 4; ++ ++ lcd_frame_desc0 = lcd_desc_base + 0; ++ lcd_frame_desc1 = lcd_desc_base + 1; ++ lcd_palette_desc = lcd_desc_base + 2; ++ ++ jz_lcd_buffer_addrs.lcd_desc_phys_addr = (unsigned int)virt_to_phys(lcd_frame_desc0); ++ ++ /* Palette Descriptor */ ++ lcd_palette_desc->next_desc = (int)virt_to_phys(lcd_frame_desc0); ++ lcd_palette_desc->databuf = (int)virt_to_phys((void *)lcd_palette); ++ lcd_palette_desc->frame_id = (unsigned int)0xdeadbeaf; ++ lcd_palette_desc->cmd = pal_size|LCD_CMD_PAL; /* Palette Descriptor */ ++ ++ /* Frame Descriptor 0 */ ++ if (jzfb.bpp <= 8) ++ lcd_frame_desc0->next_desc = (int)virt_to_phys(lcd_palette_desc); ++ else ++ lcd_frame_desc0->next_desc = (int)virt_to_phys(lcd_frame_desc0); ++ lcd_frame_desc0->databuf = virt_to_phys((void *)lcd_frame[0]); ++ lcd_frame_desc0->frame_id = (unsigned int)0xbeafbeaf; ++ lcd_frame_desc0->cmd = LCD_CMD_SOFINT | LCD_CMD_EOFINT | frm_size; ++ dma_cache_wback_inv((unsigned int)(lcd_palette_desc),0x10); ++ dma_cache_wback_inv((unsigned int)(lcd_frame_desc0),0x10); ++ ++ if (!(dual_panel)) ++ return; ++ ++ /* Frame Descriptor 1 */ ++ lcd_frame_desc1->next_desc = (int)virt_to_phys(lcd_frame_desc1); ++ lcd_frame_desc1->databuf = virt_to_phys((void *)(lcd_frame[0] + frm_size * 4)); ++ lcd_frame_desc1->frame_id = (unsigned int)0xdeaddead; ++ lcd_frame_desc1->cmd = LCD_CMD_SOFINT | LCD_CMD_EOFINT | frm_size; ++ dma_cache_wback_inv((unsigned int)(lcd_frame_desc1),0x10); ++} ++ ++static int lcd_hw_init(void) ++{ ++ unsigned int val = 0; ++ unsigned int pclk; ++ unsigned int stnH; ++ int ret = 0; ++ ++ /* Setting Control register */ ++ switch (jzfb.bpp) { ++ case 1: ++ val |= LCD_CTRL_BPP_1; ++ break; ++ case 2: ++ val |= LCD_CTRL_BPP_2; ++ break; ++ case 4: ++ val |= LCD_CTRL_BPP_4; ++ break; ++ case 8: ++ val |= LCD_CTRL_BPP_8; ++ break; ++ case 15: ++ val |= LCD_CTRL_RGB555; ++ case 16: ++ val |= LCD_CTRL_BPP_16; ++ break; ++#if defined(CONFIG_SOC_JZ4740) ++ case 17 ... 32: ++ val |= LCD_CTRL_BPP_18_24; /* target is 4bytes/pixel */ ++ break; ++#endif ++ default: ++ printk("The BPP %d is not supported\n", jzfb.bpp); ++ val |= LCD_CTRL_BPP_16; ++ break; ++ } ++ ++ switch (jzfb.cfg & MODE_MASK) { ++ case MODE_STN_MONO_DUAL: ++ case MODE_STN_COLOR_DUAL: ++ case MODE_STN_MONO_SINGLE: ++ case MODE_STN_COLOR_SINGLE: ++ switch (jzfb.bpp) { ++ case 1: ++ case 2: ++ val |= LCD_CTRL_FRC_2; ++ break; ++ case 4: ++ val |= LCD_CTRL_FRC_4; ++ break; ++ case 8: ++ default: ++ val |= LCD_CTRL_FRC_16; ++ break; ++ } ++ break; ++ } ++ ++ val |= LCD_CTRL_BST_16; /* Burst Length is 16WORD=64Byte */ ++ ++ switch (jzfb.cfg & MODE_MASK) { ++ case MODE_STN_MONO_DUAL: ++ case MODE_STN_COLOR_DUAL: ++ case MODE_STN_MONO_SINGLE: ++ case MODE_STN_COLOR_SINGLE: ++ switch (jzfb.cfg & STN_DAT_PINMASK) { ++#define align2(n) (n)=((((n)+1)>>1)<<1) ++#define align4(n) (n)=((((n)+3)>>2)<<2) ++#define align8(n) (n)=((((n)+7)>>3)<<3) ++ case STN_DAT_PIN1: ++ /* Do not adjust the hori-param value. */ ++ break; ++ case STN_DAT_PIN2: ++ align2(jzfb.hsw); ++ align2(jzfb.elw); ++ align2(jzfb.blw); ++ break; ++ case STN_DAT_PIN4: ++ align4(jzfb.hsw); ++ align4(jzfb.elw); ++ align4(jzfb.blw); ++ break; ++ case STN_DAT_PIN8: ++ align8(jzfb.hsw); ++ align8(jzfb.elw); ++ align8(jzfb.blw); ++ break; ++ } ++ break; ++ } ++ ++ val |= 1 << 26; /* Output FIFO underrun protection */ ++ REG_LCD_CTRL = val; ++ ++ switch (jzfb.cfg & MODE_MASK) { ++ case MODE_STN_MONO_DUAL: ++ case MODE_STN_COLOR_DUAL: ++ case MODE_STN_MONO_SINGLE: ++ case MODE_STN_COLOR_SINGLE: ++ if (((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL) || ++ ((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL)) ++ stnH = jzfb.h >> 1; ++ else ++ stnH = jzfb.h; ++ ++ REG_LCD_VSYNC = (0 << 16) | jzfb.vsw; ++ REG_LCD_HSYNC = ((jzfb.blw+jzfb.w) << 16) | (jzfb.blw+jzfb.w+jzfb.hsw); ++ ++ /* Screen setting */ ++ REG_LCD_VAT = ((jzfb.blw + jzfb.w + jzfb.hsw + jzfb.elw) << 16) | (stnH + jzfb.vsw + jzfb.bfw + jzfb.efw); ++ REG_LCD_DAH = (jzfb.blw << 16) | (jzfb.blw + jzfb.w); ++ REG_LCD_DAV = (0 << 16) | (stnH); ++ ++ /* AC BIAs signal */ ++ REG_LCD_PS = (0 << 16) | (stnH+jzfb.vsw+jzfb.efw+jzfb.bfw); ++ ++ break; ++ ++ case MODE_TFT_GEN: ++ case MODE_TFT_SHARP: ++ case MODE_TFT_CASIO: ++ case MODE_TFT_SAMSUNG: ++ case MODE_8BIT_SERIAL_TFT: ++ case MODE_TFT_18BIT: ++ REG_LCD_VSYNC = (0 << 16) | jzfb.vsw; ++#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42) ++ REG_LCD_DAV = (0 << 16) | ( jzfb.h ); ++#else ++ REG_LCD_DAV = ((jzfb.vsw + jzfb.bfw) << 16) | (jzfb.vsw + jzfb.bfw + jzfb.h); ++#endif /*#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42)*/ ++ REG_LCD_VAT = (((jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw)) << 16) | (jzfb.vsw + jzfb.bfw + jzfb.h + jzfb.efw); ++ REG_LCD_HSYNC = (0 << 16) | jzfb.hsw; ++ REG_LCD_DAH = ((jzfb.hsw + jzfb.blw) << 16) | (jzfb.hsw + jzfb.blw + jzfb.w); ++ break; ++ } ++ ++ switch (jzfb.cfg & MODE_MASK) { ++ case MODE_TFT_SAMSUNG: ++ { ++ unsigned int total, tp_s, tp_e, ckv_s, ckv_e; ++ unsigned int rev_s, rev_e, inv_s, inv_e; ++ total = jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw; ++ tp_s = jzfb.blw + jzfb.w + 1; ++ tp_e = tp_s + 1; ++ ckv_s = tp_s - jz_clocks.pixclk/(1000000000/4100); ++ ckv_e = tp_s + total; ++ rev_s = tp_s - 11; /* -11.5 clk */ ++ rev_e = rev_s + total; ++ inv_s = tp_s; ++ inv_e = inv_s + total; ++ REG_LCD_CLS = (tp_s << 16) | tp_e; ++ REG_LCD_PS = (ckv_s << 16) | ckv_e; ++ REG_LCD_SPL = (rev_s << 16) | rev_e; ++ REG_LCD_REV = (inv_s << 16) | inv_e; ++ jzfb.cfg |= STFT_REVHI | STFT_SPLHI; ++ break; ++ } ++ case MODE_TFT_SHARP: ++ { ++ unsigned int total, cls_s, cls_e, ps_s, ps_e; ++ unsigned int spl_s, spl_e, rev_s, rev_e; ++ total = jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw; ++#if !defined(CONFIG_JZLCD_INNOLUX_AT080TN42) ++ spl_s = 1; ++ spl_e = spl_s + 1; ++ cls_s = 0; ++ cls_e = total - 60; /* > 4us (pclk = 80ns) */ ++ ps_s = cls_s; ++ ps_e = cls_e; ++ rev_s = total - 40; /* > 3us (pclk = 80ns) */ ++ rev_e = rev_s + total; ++ jzfb.cfg |= STFT_PSHI; ++#else /*#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42)*/ ++ spl_s = total - 5; /* LD */ ++ spl_e = total - 3; ++ cls_s = 32; /* CKV */ ++ cls_e = 145; ++ ps_s = 0; /* OEV */ ++ ps_e = 45; ++ rev_s = 0; /* POL */ ++ rev_e = 0; ++#endif /*#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42)*/ ++ REG_LCD_SPL = (spl_s << 16) | spl_e; ++ REG_LCD_CLS = (cls_s << 16) | cls_e; ++ REG_LCD_PS = (ps_s << 16) | ps_e; ++ REG_LCD_REV = (rev_s << 16) | rev_e; ++ break; ++ } ++ case MODE_TFT_CASIO: ++ break; ++ } ++ ++ /* Configure the LCD panel */ ++ REG_LCD_CFG = jzfb.cfg; ++ ++ /* Timing setting */ ++ __cpm_stop_lcd(); ++ ++ val = jzfb.fclk; /* frame clk */ ++ ++ if ( (jzfb.cfg & MODE_MASK) != MODE_8BIT_SERIAL_TFT) { ++ pclk = val * (jzfb.w + jzfb.hsw + jzfb.elw + jzfb.blw) * ++ (jzfb.h + jzfb.vsw + jzfb.efw + jzfb.bfw); /* Pixclk */ ++ } ++ else { ++ /* serial mode: Hsync period = 3*Width_Pixel */ ++ pclk = val * (jzfb.w*3 + jzfb.hsw + jzfb.elw + jzfb.blw) * ++ (jzfb.h + jzfb.vsw + jzfb.efw + jzfb.bfw); /* Pixclk */ ++ } ++ ++ if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_SINGLE) || ++ ((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL)) ++ pclk = (pclk * 3); ++ ++ if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_SINGLE) || ++ ((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || ++ ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_SINGLE) || ++ ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) ++ pclk = pclk >> ((jzfb.cfg & STN_DAT_PINMASK) >> 4); ++ ++ if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || ++ ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) ++ pclk >>= 1; ++#if defined(CONFIG_SOC_JZ4730) ++ val = __cpm_get_pllout() / pclk; ++ REG_CPM_CFCR2 = val - 1; ++ val = __cpm_get_pllout() / (pclk * 4); ++ val = __cpm_divisor_encode(val); ++ __cpm_set_lcdclk_div(val); ++ REG_CPM_CFCR |= CPM_CFCR_UPE; ++#elif defined(CONFIG_SOC_JZ4740) ++ val = ( __cpm_get_pllout2()) / pclk; ++ val--; ++ if ( val > 0x3ff ) { ++ printk("pixel clock divid is too large, set it to 0x3ff\n"); ++ val = 0x3ff; ++ } ++ __cpm_set_pixdiv(val); ++ ++ val = pclk * 3 ; /* LCDClock > 2.5*Pixclock */ ++ val =__cpm_get_pllout() / val; ++ if ( val > 0x1f ) { ++ printk("lcd clock divide is too large, set it to 0x1f\n"); ++ val = 0x1f; ++ } ++ __cpm_set_ldiv( val ); ++ REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */ ++ ++#else ++ printk("drivers/video/Jzlcd.c, CONFIG_MIPS, please set chip type.\n"); ++#endif /*#ifdef CONFIG_MIPS_JZ4730 */ ++ ++ jz_clocks.pixclk = __cpm_get_pixclk(); ++ jz_clocks.lcdclk = __cpm_get_lcdclk(); ++ printk("LCDC: PixClock:%d LcdClock:%d\n", ++ jz_clocks.pixclk, jz_clocks.lcdclk); ++ ++ __cpm_start_lcd(); ++ udelay(1000); ++ return ret; ++} ++ ++static irqreturn_t lcd_interrupt_handler(int irq, void *dev_id) ++{ ++ unsigned int state; ++ ++ state = REG_LCD_STATE; ++ ++ if (state & LCD_STATE_EOF) /* End of frame */ ++ REG_LCD_STATE = state & ~LCD_STATE_EOF; ++ ++ if (state & LCD_STATE_IFU0) { ++ dprintk("InFiFo0 underrun\n"); ++ REG_LCD_STATE = state & ~LCD_STATE_IFU0; ++ } ++ ++ if (state & LCD_STATE_OFU) { /* Out fifo underrun */ ++ REG_LCD_STATE = state & ~LCD_STATE_OFU; ++ dprintk("Out FiFo underrun.\n"); ++ } ++ return IRQ_HANDLED; ++} ++ ++#ifdef CONFIG_PM ++ ++/* ++ * Suspend the LCDC. ++ */ ++static int jzfb_suspend(void) ++{ ++ __lcd_clr_ena(); /* Quick Disable */ ++ __lcd_display_off(); ++ __cpm_stop_lcd(); ++ ++ return 0; ++} ++ ++/* ++ * Resume the LCDC. ++ */ ++#ifdef CONFIG_SOC_JZ4730 ++static int jzfb_resume(void) ++{ ++ __cpm_start_lcd(); ++ ++ __lcd_display_pin_init(); ++ ++ __lcd_display_on(); ++ ++ lcd_hw_init(); ++ ++ if (jzfb.bpp <= 8) ++ REG_LCD_DA0 = virt_to_phys(lcd_palette_desc); ++ else ++ REG_LCD_DA0 = virt_to_phys(lcd_frame_desc0); ++ ++ if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || ++ ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) ++ REG_LCD_DA1 = virt_to_phys(lcd_frame_desc1); ++ ++ __lcd_set_ena(); ++ return 0; ++} ++ ++#else ++/* ++ * Resume the LCDC. ++ */ ++static int jzfb_resume(void) ++{ ++ __cpm_start_lcd(); ++ __gpio_set_pin(GPIO_DISP_OFF_N); ++ __lcd_special_on(); ++ __lcd_set_ena(); ++ mdelay(200); ++ __lcd_set_backlight_level(80); ++ ++ return 0; ++} ++#endif /* CONFIG_MIPS_JZ4730 */ ++ ++/* ++ * Power management hook. Note that we won't be called from IRQ context, ++ * unlike the blank functions above, so we may sleep. ++ */ ++static int jzlcd_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) ++{ ++ int ret; ++ struct lcd_cfb_info *cfb = pm_dev->data; ++ ++ if (!cfb) return -EINVAL; ++ ++ switch (req) { ++ case PM_SUSPEND: ++ ret = jzfb_suspend(); ++ break; ++ ++ case PM_RESUME: ++ ret = jzfb_resume(); ++ break; ++ ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++#else ++#define jzfb_suspend NULL ++#define jzfb_resume NULL ++#endif /* CONFIG_PM */ ++ ++static int __init jzfb_init(void) ++{ ++ struct lcd_cfb_info *cfb; ++ int err = 0; ++ ++ /* In special mode, we only need init special pin, ++ * as general lcd pin has init in uboot */ ++#if defined(CONFIG_SOC_JZ4740) || defined(CONFIG_SOC_JZ4750) ++ switch (jzfb.cfg & MODE_MASK) { ++ case LCD_CFG_MODE_SPECIAL_TFT_1: ++ case LCD_CFG_MODE_SPECIAL_TFT_2: ++ case LCD_CFG_MODE_SPECIAL_TFT_3: ++ __gpio_as_lcd_special(); ++ break; ++ default: ++ ; ++ } ++#endif ++ __lcd_display_pin_init(); ++ ++ cfb = jzfb_alloc_fb_info(); ++ if (!cfb) ++ goto failed; ++ ++ err = jzfb_map_smem(cfb); ++ if (err) ++ goto failed; ++ ++ jzfb_set_var(&cfb->fb.var, -1, &cfb->fb); ++ ++ lcd_descriptor_init(); ++ ++ err = lcd_hw_init(); ++ if (err) ++ goto failed; ++ ++ if (jzfb.bpp <= 8) ++ REG_LCD_DA0 = virt_to_phys(lcd_palette_desc); ++ else ++ REG_LCD_DA0 = virt_to_phys(lcd_frame_desc0); ++ ++ if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || ++ ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) ++ REG_LCD_DA1 = virt_to_phys(lcd_frame_desc1); ++ ++ __lcd_set_ena(); ++ ++ if (request_irq(IRQ_LCD, lcd_interrupt_handler, IRQF_DISABLED, ++ "lcd", 0)) { ++ err = -EBUSY; ++ goto failed; ++ } ++ ++ __lcd_enable_ofu_intr(); /* enable OutFifo underrun */ ++// __lcd_enable_ifu0_intr(); /* needn't enable InFifo underrun */ ++ ++#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) ++ jzfb_rotate_change(rotate_angle); ++ /* sleep n??? */ ++#endif ++ err = register_framebuffer(&cfb->fb); ++ if (err < 0) { ++ dprintk("jzfb_init(): register framebuffer err.\n"); ++ goto failed; ++ } ++ ++ printk("fb%d: %s frame buffer device, using %dK of video memory\n", ++ cfb->fb.node, cfb->fb.fix.id, cfb->fb.fix.smem_len>>10); ++ ++#ifdef CONFIG_PM ++ /* ++ * Note that the console registers this as well, but we want to ++ * power down the display prior to sleeping. ++ */ ++ cfb->pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, jzlcd_pm_callback); ++ if (cfb->pm) ++ cfb->pm->data = cfb; ++#endif ++ ++ __lcd_display_on(); ++ ++ return 0; ++ ++failed: ++ jzfb_unmap_smem(cfb); ++ jzfb_free_fb_info(cfb); ++ ++ return err; ++} ++ ++#if 0 ++static int jzfb_remove(struct device *dev) ++{ ++ struct lcd_cfb_info *cfb = dev_get_drvdata(dev); ++ jzfb_unmap_smem(cfb); ++ jzfb_free_fb_info(cfb); ++ return 0; ++} ++#endif ++ ++#if 0 ++static struct device_driver jzfb_driver = { ++ .name = "jz-lcd", ++ .bus = &platform_bus_type, ++ .probe = jzfb_probe, ++ .remove = jzfb_remove, ++ .suspend = jzfb_suspend, ++ .resume = jzfb_resume, ++}; ++#endif ++ ++static void __exit jzfb_cleanup(void) ++{ ++#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) ++ kthread_stop(jzlcd_info->rotate_daemon_thread); ++#endif ++// driver_unregister(&jzfb_driver); ++// jzfb_remove(); ++} ++ ++module_init(jzfb_init); ++module_exit(jzfb_cleanup); ++ ++MODULE_DESCRIPTION("JzSOC LCD Controller driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/drivers/video/jzlcd.h linux-2.6.24.7/drivers/video/jzlcd.h +--- linux-2.6.24.7.old/drivers/video/jzlcd.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/video/jzlcd.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,791 @@ ++/* ++ * linux/drivers/video/jzlcd.h -- Ingenic On-Chip LCD frame buffer device ++ * ++ * Copyright (C) 2005-2007, Ingenic Semiconductor Inc. ++ * ++ * 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 __JZLCD_H__ ++#define __JZLCD_H__ ++ ++#include ++ ++#define NR_PALETTE 256 ++ ++struct lcd_desc{ ++ unsigned int next_desc; /* LCDDAx */ ++ unsigned int databuf; /* LCDSAx */ ++ unsigned int frame_id; /* LCDFIDx */ ++ unsigned int cmd; /* LCDCMDx */ ++}; ++ ++#define MODE_MASK 0x0f ++#define MODE_TFT_GEN 0x00 ++#define MODE_TFT_SHARP 0x01 ++#define MODE_TFT_CASIO 0x02 ++#define MODE_TFT_SAMSUNG 0x03 ++#define MODE_CCIR656_NONINT 0x04 ++#define MODE_CCIR656_INT 0x05 ++#define MODE_STN_COLOR_SINGLE 0x08 ++#define MODE_STN_MONO_SINGLE 0x09 ++#define MODE_STN_COLOR_DUAL 0x0a ++#define MODE_STN_MONO_DUAL 0x0b ++#define MODE_8BIT_SERIAL_TFT 0x0c ++ ++#define MODE_TFT_18BIT (1<<7) ++ ++#define STN_DAT_PIN1 (0x00 << 4) ++#define STN_DAT_PIN2 (0x01 << 4) ++#define STN_DAT_PIN4 (0x02 << 4) ++#define STN_DAT_PIN8 (0x03 << 4) ++#define STN_DAT_PINMASK STN_DAT_PIN8 ++ ++#define STFT_PSHI (1 << 15) ++#define STFT_CLSHI (1 << 14) ++#define STFT_SPLHI (1 << 13) ++#define STFT_REVHI (1 << 12) ++ ++#define SYNC_MASTER (0 << 16) ++#define SYNC_SLAVE (1 << 16) ++ ++#define DE_P (0 << 9) ++#define DE_N (1 << 9) ++ ++#define PCLK_P (0 << 10) ++#define PCLK_N (1 << 10) ++ ++#define HSYNC_P (0 << 11) ++#define HSYNC_N (1 << 11) ++ ++#define VSYNC_P (0 << 8) ++#define VSYNC_N (1 << 8) ++ ++#define DATA_NORMAL (0 << 17) ++#define DATA_INVERSE (1 << 17) ++ ++ ++/* Jz LCDFB supported I/O controls. */ ++#define FBIOSETBACKLIGHT 0x4688 ++#define FBIODISPON 0x4689 ++#define FBIODISPOFF 0x468a ++#define FBIORESET 0x468b ++#define FBIOPRINT_REGS 0x468c ++#define FBIOGETBUFADDRS 0x468d ++#define FBIOROTATE 0x46a0 /* rotated fb */ ++ ++struct jz_lcd_buffer_addrs_t { ++ int fb_num; ++ unsigned int lcd_desc_phys_addr; ++ unsigned int fb_phys_addr[CONFIG_JZLCD_FRAMEBUFFER_MAX]; ++}; ++ ++ ++/* ++ * LCD panel specific definition ++ */ ++#if defined(CONFIG_JZLCD_TRULY_TFTG320240DTSW) ++ ++#if defined(CONFIG_JZ4730_PMP) ++#define LCD_RESET_PIN 63 ++#endif ++ ++#define __lcd_special_on() \ ++do { \ ++ __gpio_set_pin(LCD_RESET_PIN); \ ++ __gpio_as_output(LCD_RESET_PIN); \ ++ __gpio_clear_pin(LCD_RESET_PIN); \ ++ udelay(100); \ ++ __gpio_set_pin(LCD_RESET_PIN); \ ++} while (0) ++ ++#endif /* CONFIG_JZLCD_TRULY_TFTG320240DTSW */ ++ ++#if defined(CONFIG_JZLCD_SAMSUNG_LTV350QVF04) ++ ++#if defined(CONFIG_JZ4730_FPRINT) ++#define PortSDI 60 ++#define PortSCL 61 ++#define PortCS 62 ++#define PortRST 63 ++#define PortSht 64 ++#endif ++ ++#if defined(CONFIG_JZ4730_GPS) ++#define PortSDI 74 ++#define PortSCL 72 ++#define PortCS 73 ++#define PortRST 60 ++#define PortSht 59 ++#endif ++ ++#ifndef PortSDI ++#define PortSDI 0 ++#endif ++#ifndef PortSCL ++#define PortSCL 0 ++#endif ++#ifndef PortCS ++#define PortCS 0 ++#endif ++#ifndef PortRST ++#define PortRST 0 ++#endif ++#ifndef PortSht ++#define PortSht 0 ++#endif ++ ++#define __lcd_special_pin_init() \ ++do { \ ++ __gpio_as_output(PortSDI); /* SDI */\ ++ __gpio_as_output(PortSCL); /* SCL */ \ ++ __gpio_as_output(PortCS); /* CS */ \ ++ __gpio_as_output(PortRST); /* Reset */ \ ++ __gpio_as_output(PortSht); /* Shut Down # */ \ ++ __gpio_set_pin(PortCS); \ ++ __gpio_set_pin(PortSCL); \ ++ __gpio_set_pin(PortSDI); \ ++} while (0) ++ ++#define __spi_out(val) \ ++do { \ ++ int __i__; \ ++ unsigned int _t_ = (val); \ ++ __gpio_clear_pin(PortCS); \ ++ udelay(25); \ ++ for (__i__ = 0; __i__ < 24; __i__++ ) { \ ++ __gpio_clear_pin(PortSCL); \ ++ if (_t_ & 0x800000) \ ++ __gpio_set_pin(PortSDI); \ ++ else \ ++ __gpio_clear_pin(PortSDI); \ ++ _t_ <<= 1; \ ++ udelay(25); \ ++ __gpio_set_pin(PortSCL); \ ++ udelay(25); \ ++ } \ ++ __gpio_set_pin(PortCS); \ ++ udelay(25); \ ++ __gpio_set_pin(PortSDI); \ ++ udelay(25); \ ++ __gpio_set_pin(PortSCL); \ ++} while (0) ++ ++#define __spi_id_op_data(rs, rw, val) \ ++ __spi_out((0x1d<<18)|((rs)<<17)|((rw)<<16)|(val)) ++ ++#define __spi_write_reg(reg, val) \ ++do { \ ++ __spi_id_op_data(0, 0, (reg)); \ ++ __spi_id_op_data(1, 0, (val)); \ ++} while (0) ++ ++#define __lcd_special_on() \ ++do { \ ++ __gpio_set_pin(PortSht); \ ++ __gpio_clear_pin(PortRST); \ ++ mdelay(10); \ ++ __gpio_set_pin(PortRST); \ ++ mdelay(1); \ ++ __spi_write_reg(0x09, 0); \ ++ mdelay(10); \ ++ __spi_write_reg(0x09, 0x4000); \ ++ __spi_write_reg(0x0a, 0x2000); \ ++ mdelay(40); \ ++ __spi_write_reg(0x09, 0x4055); \ ++ mdelay(50); \ ++ __spi_write_reg(0x01, 0x409d); \ ++ __spi_write_reg(0x02, 0x0204); \ ++ __spi_write_reg(0x03, 0x0100); \ ++ __spi_write_reg(0x04, 0x3000); \ ++ __spi_write_reg(0x05, 0x4003); \ ++ __spi_write_reg(0x06, 0x000a); \ ++ __spi_write_reg(0x07, 0x0021); \ ++ __spi_write_reg(0x08, 0x0c00); \ ++ __spi_write_reg(0x10, 0x0103); \ ++ __spi_write_reg(0x11, 0x0301); \ ++ __spi_write_reg(0x12, 0x1f0f); \ ++ __spi_write_reg(0x13, 0x1f0f); \ ++ __spi_write_reg(0x14, 0x0707); \ ++ __spi_write_reg(0x15, 0x0307); \ ++ __spi_write_reg(0x16, 0x0707); \ ++ __spi_write_reg(0x17, 0x0000); \ ++ __spi_write_reg(0x18, 0x0004); \ ++ __spi_write_reg(0x19, 0x0000); \ ++ mdelay(60); \ ++ __spi_write_reg(0x09, 0x4a55); \ ++ __spi_write_reg(0x05, 0x5003); \ ++} while (0) ++ ++#define __lcd_special_off() \ ++do { \ ++ __spi_write_reg(0x09, 0x4055); \ ++ __spi_write_reg(0x05, 0x4003); \ ++ __spi_write_reg(0x0a, 0x0000); \ ++ mdelay(10); \ ++ __spi_write_reg(0x09, 0x4000); \ ++ __gpio_clear_pin(PortSht); \ ++} while (0) ++ ++#endif /* CONFIG_JZLCD_SAMSUNG_LTV350QVF04 */ ++ ++#if defined(CONFIG_JZLCD_AUO_A030FL01_V1) ++#if defined(CONFIG_JZ4740_PAVO) /* board pavo */ ++ #define SPEN (32*1+18) /*LCD_CS*/ ++ #define SPCK (32*1+17) /*LCD_SCL*/ ++ #define SPDA (32*2+12) /*LCD_SDA*/ ++ #define LCD_RET (32*2+23) /*use for lcd reset*/ ++#elif defined(CONFIG_JZ4740_LYRA) /* board lyra */ ++ #define SPEN (32*3+19) //LCD_CS ++ #define SPCK (32*3+18) //LCD_SCL ++ #define SPDA (32*3+20) //LCD_SDA ++ #define LCD_RET (32*3+31) //use for lcd reset ++#else ++#error "driver/video/Jzlcd.h, please define SPI pins on your board." ++#endif ++ ++#define __spi_write_reg(reg, val) \ ++ do { \ ++ unsigned char no; \ ++ unsigned short value; \ ++ unsigned char a=0; \ ++ unsigned char b=0; \ ++ __gpio_as_output(SPEN); /* use SPDA */ \ ++ __gpio_as_output(SPCK); /* use SPCK */ \ ++ __gpio_as_output(SPDA); /* use SPDA */ \ ++ a=reg; \ ++ b=val; \ ++ __gpio_set_pin(SPEN); \ ++ __gpio_clear_pin(SPCK); \ ++ udelay(50); \ ++ __gpio_clear_pin(SPDA); \ ++ __gpio_clear_pin(SPEN); \ ++ udelay(50); \ ++ value=((a<<8)|(b&0xFF)); \ ++ for(no=0;no<16;no++) \ ++ { \ ++ if((value&0x8000)==0x8000){ \ ++ __gpio_set_pin(SPDA);} \ ++ else{ \ ++ __gpio_clear_pin(SPDA); } \ ++ udelay(400); \ ++ __gpio_set_pin(SPCK); \ ++ value=(value<<1); \ ++ udelay(50); \ ++ __gpio_clear_pin(SPCK); \ ++ } \ ++ __gpio_set_pin(SPEN); \ ++ udelay(400); \ ++ } while (0) ++#define __spi_read_reg(reg,val) \ ++ do{ \ ++ unsigned char no; \ ++ unsigned short value; \ ++ __gpio_as_output(SPEN); /* use SPDA */ \ ++ __gpio_as_output(SPCK); /* use SPCK */ \ ++ __gpio_as_output(SPDA); /* use SPDA */ \ ++ value = ((reg << 0) | (1 << 7)); \ ++ val = 0; \ ++ __gpio_as_output(SPDA); \ ++ __gpio_set_pin(SPEN); \ ++ __gpio_set_pin(SPCK); \ ++ udelay(1); \ ++ __gpio_clear_pin(SPDA); \ ++ __gpio_clear_pin(SPEN); \ ++ udelay(1); \ ++ for (no = 0; no < 16; no++ ) { \ ++ __gpio_clear_pin(SPCK); \ ++ udelay(1); \ ++ if(no < 8) \ ++ { \ ++ if (value & 0x80) /* send data */ \ ++ __gpio_set_pin(SPDA); \ ++ else \ ++ __gpio_clear_pin(SPDA); \ ++ value = (value << 1); \ ++ udelay(1); \ ++ __gpio_set_pin(SPCK); \ ++ udelay(1); \ ++ } \ ++ else \ ++ { \ ++ __gpio_as_input(SPDA); \ ++ udelay(1); \ ++ __gpio_set_pin(SPCK); \ ++ udelay(1); \ ++ val = (val << 1); \ ++ val |= __gpio_get_pin(SPDA); \ ++ udelay(1); \ ++ } \ ++ udelay(400); \ ++ } \ ++ __gpio_as_output(SPDA); \ ++ __gpio_set_pin(SPEN); \ ++ udelay(400); \ ++ } while(0) ++ ++#define __lcd_special_pin_init() \ ++ do { \ ++ __gpio_as_output(SPEN); /* use SPDA */ \ ++ __gpio_as_output(SPCK); /* use SPCK */ \ ++ __gpio_as_output(SPDA); /* use SPDA */ \ ++ __gpio_as_output(LCD_RET); \ ++ udelay(50); \ ++ __gpio_clear_pin(LCD_RET); \ ++ udelay(100); \ ++ __gpio_set_pin(LCD_RET); \ ++ } while (0) ++#define __lcd_special_on() \ ++ do { \ ++ udelay(50); \ ++ __gpio_clear_pin(LCD_RET); \ ++ udelay(100); \ ++ __gpio_set_pin(LCD_RET); \ ++ __spi_write_reg(0x0D, 0x44); \ ++ __spi_write_reg(0x0D, 0x4D); \ ++ __spi_write_reg(0x0B, 0x06); \ ++ __spi_write_reg(0x40, 0xC0); \ ++ __spi_write_reg(0x42, 0x43); \ ++ __spi_write_reg(0x44, 0x28); \ ++ __spi_write_reg(0x0D, 0x4F); \ ++} while (0) ++ ++ #define __lcd_special_off() \ ++ do { \ ++ __spi_write_reg(0x04, 0x4C); \ ++ } while (0) ++ ++#endif /* CONFIG_JZLCD_AUO_A030FL01_V1 */ ++ ++//#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) ++#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) || defined(CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL) ++ ++#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) /* board pmp */ ++#define MODE 0xcd /* 24bit parellel RGB */ ++#endif ++#if defined(CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL) ++#define MODE 0xc9 /* 8bit serial RGB */ ++#endif ++ ++#if defined(CONFIG_JZ4730_PMP) ++ #define SPEN 60 //LCD_SPL ++ #define SPCK 61 //LCD_CLS ++ #define SPDA 62 //LCD_PS ++ #define LCD_RET 63 //LCD_REV //use for lcd reset ++#elif defined(CONFIG_JZ4740_LEO) /* board leo */ ++ #define SPEN (32*1+18) //LCD_SPL ++ #define SPCK (32*1+17) //LCD_CLS ++ #define SPDA (32*2+22) //LCD_PS ++ #define LCD_RET (32*2+23) //LCD_REV //use for lcd reset ++#elif defined(CONFIG_JZ4740_PAVO) /* board pavo */ ++ #define SPEN (32*1+18) //LCD_SPL ++ #define SPCK (32*1+17) //LCD_CLS ++ #define SPDA (32*2+12) //LCD_D12 ++ #define LCD_RET (32*2+23) //LCD_REV, GPC23 ++#if 0 /*old driver*/ ++ #define SPEN (32*1+18) //LCD_SPL ++ #define SPCK (32*1+17) //LCD_CLS ++ #define SPDA (32*2+12) //LCD_D12 ++ #define LCD_RET (32*3+27) //PWM4 //use for lcd reset ++#endif ++#else ++#error "driver/video/Jzlcd.h, please define SPI pins on your board." ++#endif ++ ++ #define __spi_write_reg1(reg, val) \ ++ do { \ ++ unsigned char no;\ ++ unsigned short value;\ ++ unsigned char a=0;\ ++ unsigned char b=0;\ ++ a=reg;\ ++ b=val;\ ++ __gpio_set_pin(SPEN);\ ++ __gpio_set_pin(SPCK);\ ++ __gpio_clear_pin(SPDA);\ ++ __gpio_clear_pin(SPEN);\ ++ udelay(25);\ ++ value=((a<<8)|(b&0xFF));\ ++ for(no=0;no<16;no++)\ ++ {\ ++ __gpio_clear_pin(SPCK);\ ++ if((value&0x8000)==0x8000)\ ++ __gpio_set_pin(SPDA);\ ++ else\ ++ __gpio_clear_pin(SPDA);\ ++ udelay(25);\ ++ __gpio_set_pin(SPCK);\ ++ value=(value<<1); \ ++ udelay(25);\ ++ }\ ++ __gpio_set_pin(SPEN);\ ++ udelay(100);\ ++ } while (0) ++ ++ #define __spi_write_reg(reg, val) \ ++ do {\ ++ __spi_write_reg1((reg<<2|2), val); \ ++ udelay(100); \ ++ }while(0) ++ ++ #define __lcd_special_pin_init() \ ++ do { \ ++ __gpio_as_output(SPEN); /* use SPDA */\ ++ __gpio_as_output(SPCK); /* use SPCK */\ ++ __gpio_as_output(SPDA); /* use SPDA */\ ++ __gpio_as_output(LCD_RET);\ ++ udelay(50);\ ++ __gpio_clear_pin(LCD_RET);\ ++ mdelay(150);\ ++ __gpio_set_pin(LCD_RET);\ ++ } while (0) ++ ++ #define __lcd_special_on() \ ++ do { \ ++ udelay(50);\ ++ __gpio_clear_pin(LCD_RET);\ ++ mdelay(150);\ ++ __gpio_set_pin(LCD_RET);\ ++ mdelay(10);\ ++ __spi_write_reg(0x00, 0x03); \ ++ __spi_write_reg(0x01, 0x40); \ ++ __spi_write_reg(0x02, 0x11); \ ++ __spi_write_reg(0x03, MODE); /* mode */ \ ++ __spi_write_reg(0x04, 0x32); \ ++ __spi_write_reg(0x05, 0x0e); \ ++ __spi_write_reg(0x07, 0x03); \ ++ __spi_write_reg(0x08, 0x08); \ ++ __spi_write_reg(0x09, 0x32); \ ++ __spi_write_reg(0x0A, 0x88); \ ++ __spi_write_reg(0x0B, 0xc6); \ ++ __spi_write_reg(0x0C, 0x20); \ ++ __spi_write_reg(0x0D, 0x20); \ ++ } while (0) //reg 0x0a is control the display direction:DB0->horizontal level DB1->vertical level ++ ++/* __spi_write_reg(0x02, 0x03); \ ++ __spi_write_reg(0x06, 0x40); \ ++ __spi_write_reg(0x0a, 0x11); \ ++ __spi_write_reg(0x0e, 0xcd); \ ++ __spi_write_reg(0x12, 0x32); \ ++ __spi_write_reg(0x16, 0x0e); \ ++ __spi_write_reg(0x1e, 0x03); \ ++ __spi_write_reg(0x22, 0x08); \ ++ __spi_write_reg(0x26, 0x40); \ ++ __spi_write_reg(0x2a, 0x88); \ ++ __spi_write_reg(0x2e, 0x88); \ ++ __spi_write_reg(0x32, 0x20); \ ++ __spi_write_reg(0x36, 0x20); \ ++*/ ++// } while (0) //reg 0x0a is control the display direction:DB0->horizontal level DB1->vertical level ++ ++ #define __lcd_special_off() \ ++ do { \ ++ __spi_write_reg(0x00, 0x03); \ ++ } while (0) ++ ++#endif /* CONFIG_JZLCD_FOXCONN_PT035TN01 or CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL */ ++ ++ ++#ifndef __lcd_special_pin_init ++#define __lcd_special_pin_init() ++#endif ++#ifndef __lcd_special_on ++#define __lcd_special_on() ++#endif ++#ifndef __lcd_special_off ++#define __lcd_special_off() ++#endif ++ ++ ++/* ++ * Platform specific definition ++ */ ++ ++#if defined(CONFIG_JZ4730_GPS) ++ ++#define __lcd_set_backlight_level(n) \ ++do { \ ++ ; \ ++} while (0) ++ ++#define __lcd_display_pin_init() \ ++do { \ ++ __lcd_special_pin_init(); \ ++ __gpio_as_output(94); /* PWM0 pin */ \ ++ __gpio_as_output(95); /* PWM1 pin */ \ ++} while (0) ++ ++#define __lcd_display_on() \ ++do { \ ++ __lcd_special_on(); \ ++ __gpio_set_pin(94); /* PWM0 pin */ \ ++ __gpio_set_pin(95); /* PWM1 pin */ \ ++ __lcd_set_backlight_level(8); \ ++} while (0) ++ ++#define __lcd_display_off() \ ++do { \ ++ __lcd_special_off(); \ ++} while (0) ++ ++#endif /* CONFIG_JZ4730_GPS */ ++ ++#if defined(CONFIG_JZ4730_FPRINT) ++ ++#define __lcd_set_backlight_level(n) \ ++do { \ ++ REG_PWM_DUT(0) = n; \ ++ REG_PWM_PER(0) = 7; \ ++ REG_PWM_CTR(0) = 0x81; \ ++} while (0) ++ ++#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) ++ ++#define __lcd_display_pin_init() \ ++do { \ ++ __lcd_special_pin_init();\ ++ __gpio_as_pwm();\ ++ __lcd_set_backlight_level(8);\ ++} while (0) ++ ++#define __lcd_display_on() \ ++do { \ ++ __lcd_set_backlight_level(8); \ ++ __lcd_special_on();\ ++} while (0) ++ ++#define __lcd_display_off() \ ++do { \ ++ __lcd_set_backlight_level(0); \ ++ __lcd_special_off();\ ++} while (0) ++ ++#else ++ ++#define __lcd_display_pin_init() \ ++do { \ ++ __gpio_as_output(GPIO_DISP_OFF_N); \ ++ __gpio_as_pwm(); \ ++ __lcd_set_backlight_level(8); \ ++} while (0) ++ ++#define __lcd_display_on() \ ++do { \ ++ __lcd_set_backlight_level(8); \ ++ __gpio_set_pin(GPIO_DISP_OFF_N); \ ++} while (0) ++ ++#define __lcd_display_off() \ ++do { \ ++ __lcd_set_backlight_level(0); \ ++ __gpio_clear_pin(GPIO_DISP_OFF_N); \ ++} while (0) ++#endif ++ ++#endif /* CONFIG_JZ4730_FPRINT */ ++ ++#if defined(CONFIG_JZ4730_LIBRA) ++ ++#define __lcd_set_backlight_level(n) \ ++do { \ ++} while (0) ++ ++#define __lcd_display_pin_init() \ ++do { \ ++ __lcd_special_pin_init(); \ ++ __gpio_clear_pin(100); \ ++ __gpio_as_output(100); \ ++ __gpio_as_output(94); \ ++ __gpio_as_output(95); \ ++ __lcd_set_backlight_level(8); \ ++} while (0) ++ ++#define __lcd_display_on() \ ++do { \ ++ __lcd_special_on(); \ ++ __gpio_set_pin(100); \ ++ __gpio_set_pin(94); \ ++ __gpio_set_pin(95); \ ++} while (0) ++ ++#define __lcd_display_off() \ ++do { \ ++ __lcd_special_off(); \ ++ __gpio_clear_pin(100); \ ++ __gpio_clear_pin(94); \ ++ __gpio_clear_pin(95); \ ++} while (0) ++ ++#endif /* CONFIG_JZ4730_LIBRA */ ++ ++#if defined(CONFIG_JZ4730_PMP) ++ ++#define __lcd_set_backlight_level(n) \ ++do { \ ++ REG_PWM_DUT(0) = n; \ ++ REG_PWM_PER(0) = 7; \ ++ REG_PWM_CTR(0) = 0x81; \ ++} while (0) ++ ++#define __lcd_display_pin_init() \ ++do { \ ++ __gpio_as_output(GPIO_DISP_OFF_N); \ ++ __gpio_as_pwm(); \ ++ __lcd_set_backlight_level(10); \ ++ __lcd_special_pin_init(); \ ++} while (0) ++ ++#define __lcd_display_on() \ ++do { \ ++ __lcd_special_on(); \ ++ __lcd_set_backlight_level(8); \ ++ __gpio_set_pin(GPIO_DISP_OFF_N); \ ++} while (0) ++ ++#define __lcd_display_off() \ ++do { \ ++ __lcd_special_off(); \ ++ __lcd_set_backlight_level(0); \ ++ __gpio_clear_pin(GPIO_DISP_OFF_N); \ ++} while (0) ++ ++#endif /* CONFIG_JZ4730_PMP */ ++ ++/*#if defined(CONFIG_JZ4740_LEO) || defined(CONFIG_JZ4740_PAVO)*/ ++#if defined(CONFIG_SOC_JZ4740) ++#if defined(CONFIG_JZ4740_PAVO) || defined(CONFIG_JZ4740_LYRA) ++#define GPIO_PWM 123 /* GP_D27 */ ++#define PWM_CHN 4 /* pwm channel */ ++#define PWM_FULL 101 ++/* 100 level: 0,1,...,100 */ ++#define __lcd_set_backlight_level(n)\ ++do { \ ++__gpio_as_output(32*3+27); \ ++__gpio_set_pin(32*3+27); \ ++} while (0) ++ ++#define __lcd_close_backlight() \ ++do { \ ++__gpio_as_output(GPIO_PWM); \ ++__gpio_clear_pin(GPIO_PWM); \ ++} while (0) ++ ++#elif defined(CONFIG_JZ4720_VIRGO) ++#define GPIO_PWM 119 /* GP_D23 */ ++#define PWM_CHN 0 /* pwm channel */ ++#define PWM_FULL 101 ++/* 100 level: 0,1,...,100 */ ++/*#define __lcd_set_backlight_level(n) \ ++do { \ ++ __gpio_as_pwm(0); \ ++ __tcu_disable_pwm_output(PWM_CHN); \ ++ __tcu_stop_counter(PWM_CHN); \ ++ __tcu_init_pwm_output_high(PWM_CHN); \ ++ __tcu_set_pwm_output_shutdown_abrupt(PWM_CHN); \ ++ __tcu_select_clk_div1(PWM_CHN); \ ++ __tcu_mask_full_match_irq(PWM_CHN); \ ++ __tcu_mask_half_match_irq(PWM_CHN); \ ++ __tcu_set_count(PWM_CHN,0); \ ++ __tcu_set_full_data(PWM_CHN,__cpm_get_extalclk()/1000); \ ++ __tcu_set_half_data(PWM_CHN,__cpm_get_extalclk()/1000*n/100); \ ++ __tcu_enable_pwm_output(PWM_CHN); \ ++ __tcu_select_extalclk(PWM_CHN); \ ++ __tcu_start_counter(PWM_CHN); \ ++} while (0) ++*/ ++ ++#define __lcd_set_backlight_level(n) \ ++do { \ ++ __gpio_as_output(GPIO_PWM); \ ++ __gpio_set_pin(GPIO_PWM); \ ++} while (0) ++ ++#define __lcd_close_backlight() \ ++do { \ ++__gpio_as_output(GPIO_PWM); \ ++__gpio_clear_pin(GPIO_PWM); \ ++} while (0) ++ ++#else ++#define __lcd_set_backlight_level(n) ++#define __lcd_close_backlight() ++ ++#endif /* #if defined(CONFIG_MIPS_JZ4740_PAVO) */ ++ ++#define __lcd_display_pin_init() \ ++do { \ ++ __gpio_as_output(GPIO_DISP_OFF_N); \ ++ __cpm_start_tcu(); \ ++ __lcd_special_pin_init(); \ ++} while (0) ++/* __lcd_set_backlight_level(100); \*/ ++#define __lcd_display_on() \ ++do { \ ++ __gpio_set_pin(GPIO_DISP_OFF_N); \ ++ __lcd_special_on(); \ ++ __lcd_set_backlight_level(80); \ ++} while (0) ++ ++#define __lcd_display_off() \ ++do { \ ++ __lcd_special_off(); \ ++ __lcd_close_backlight(); \ ++ __gpio_clear_pin(GPIO_DISP_OFF_N); \ ++} while (0) ++ ++#endif /* CONFIG_MIPS_JZ4740_LEO */ ++ ++#if defined(CONFIG_JZLCD_MSTN_240x128) ++ ++#if 0 /* The final version does not use software emulation of VCOM. */ ++ ++#define GPIO_VSYNC 59 ++#define GPIO_VCOM 90 ++ ++#define REG_VCOM REG_GPIO_GPDR((GPIO_VCOM>>5)) ++#define VCOM_BIT (1 << (GPIO_VCOM & 0x1f)) ++static unsigned int vcom_static; ++static void vsync_irq(int irq, void *dev_id, struct pt_regs *reg) ++{ ++ vcom_static = REG_VCOM; ++ vcom_static ^= VCOM_BIT; ++ REG_VCOM = vcom_static; ++} ++ ++#define __lcd_display_pin_init() \ ++ __gpio_as_irq_rise_edge(GPIO_VSYNC); \ ++ __gpio_as_output(GPIO_VCOM); \ ++ { \ ++ static int inited = 0; \ ++ if (!inited) { \ ++ inited = 1; \ ++ if (request_irq(IRQ_GPIO_0 + GPIO_VSYNC, vsync_irq, SA_INTERRUPT, \ ++ "vsync", 0)) { \ ++ err = -EBUSY; \ ++ goto failed; \ ++ }}} ++ ++#endif ++ ++/* We uses AC BIAs pin to generate VCOM signal, so above code should be removed. ++ */ ++#endif ++/***************************************************************************** ++ * LCD display pin dummy macros ++ *****************************************************************************/ ++#ifndef __lcd_display_pin_init ++#define __lcd_display_pin_init() ++#endif ++#ifndef __lcd_display_on ++#define __lcd_display_on() ++#endif ++#ifndef __lcd_display_off ++#define __lcd_display_off() ++#endif ++#ifndef __lcd_set_backlight_level ++#define __lcd_set_backlight_level(n) ++#endif ++ ++#endif /* __JZLCD_H__ */ +diff -urN linux-2.6.24.7.old/drivers/watchdog/Kconfig linux-2.6.24.7/drivers/watchdog/Kconfig +--- linux-2.6.24.7.old/drivers/watchdog/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/watchdog/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -45,6 +45,15 @@ + + # Architecture Independent + ++config JZ_WDT ++ bool 'JzSoC On-Chip watchdog' ++ help ++ Watchdog timer embedded into JZSOC chips. This will reboot your ++ system when the timeout is reached. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called jz_wdt. ++ + config SOFT_WATCHDOG + tristate "Software watchdog" + help +diff -urN linux-2.6.24.7.old/drivers/watchdog/Makefile linux-2.6.24.7/drivers/watchdog/Makefile +--- linux-2.6.24.7.old/drivers/watchdog/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/drivers/watchdog/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -10,6 +10,9 @@ + # that also fails then you can fall back to the software watchdog + # to give you some cover. + ++# JZ-watchdog timer ++obj-$(CONFIG_JZ_WDT) += jz_wdt.o ++ + # ISA-based Watchdog Cards + obj-$(CONFIG_PCWATCHDOG) += pcwd.o + obj-$(CONFIG_MIXCOMWD) += mixcomwd.o +diff -urN linux-2.6.24.7.old/drivers/watchdog/jz_wdt.c linux-2.6.24.7/drivers/watchdog/jz_wdt.c +--- linux-2.6.24.7.old/drivers/watchdog/jz_wdt.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/drivers/watchdog/jz_wdt.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,203 @@ ++/* ++ * linux/drivers/char/jz_wdt.c ++ * ++ * Watchdog driver for the Ingenic JzSOC ++ * ++ * Author: Wei Jianli ++ * ++ * 2005 (c) Ingenic Semiconductor. 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 ++//#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define TIMER_MARGIN 60 /* (secs) Default is 1 minute */ ++ ++static unsigned int timer_margin = TIMER_MARGIN; /* in seconds */ ++static unsigned int timer_rate; ++static unsigned int pre_margin; ++static unsigned long jz_wdt_users = 0; ++ ++#ifdef MODULE ++MODULE_PARM(timer_margin, "i"); ++#endif ++ ++static void ++jz_wdt_ping(void) ++{ ++ printk("jz_wdt_ping\n"); ++ /* reload counter with (new) margin */ ++#ifdef CONFIG_SOC_JZ4730 ++ pre_margin = 0xffffffff - timer_rate * timer_margin; ++ __wdt_set_count(pre_margin); ++#endif ++#ifdef CONFIG_SOC_JZ4740 ++ pre_margin = timer_rate * timer_margin; ++ __wdt_set_count(0); ++ __wdt_set_data(pre_margin); ++#endif ++} ++ ++/* ++ * Allow only one person to hold it open ++ */ ++ ++static int ++jz_wdt_open(struct inode *inode, struct file *file) ++{ ++ if (test_and_set_bit(1, &jz_wdt_users)) ++ return -EBUSY; ++ ++ printk("jz_wdt_open\n"); ++#ifdef CONFIG_SOC_JZ4730 ++ if (REG_CPM_OCR & CPM_OCR_EXT_RTC_CLK) ++ timer_rate = 32768; ++ else ++ timer_rate = JZ_EXTAL/128; ++#endif ++ ++#ifdef CONFIG_SOC_JZ4740 ++ /* Initialize the wdt clocks */ ++ __wdt_select_rtcclk(); ++ __wdt_select_clk_div1024(); ++ __tcu_start_wdt_clock(); ++ timer_rate = 32; /* 32768 / 1024 */ ++#endif ++ ++ jz_wdt_ping(); ++ __wdt_start(); ++ ++ return 0; ++} ++ ++static int ++jz_wdt_release(struct inode *inode, struct file *file) ++{ ++ /* ++ * Shut off the timer. ++ * Lock it in if it's a module and we defined ...NOWAYOUT ++ */ ++ jz_wdt_ping(); ++#ifndef CONFIG_WATCHDOG_NOWAYOUT ++ /* SW can't stop wdt once it was started */ ++#endif ++ jz_wdt_users = 0; ++ return 0; ++} ++ ++static ssize_t ++jz_wdt_write(struct file *file, const char *data, size_t len, loff_t * ppos) ++{ ++ /* Can't seek (pwrite) on this device */ ++ if (ppos != &file->f_pos) ++ return -ESPIPE; ++ ++ printk("jz_wdt_write\n"); ++ ++ /* Refresh counter */ ++ if (len) { ++ jz_wdt_ping(); ++ return 1; ++ } ++ return 0; ++} ++ ++static int ++jz_wdt_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ int new_margin; ++ static struct watchdog_info ident = { ++ .identity = "JzSOC Watchdog", ++ .options = WDIOF_SETTIMEOUT, ++ .firmware_version = 0, ++ }; ++ ++ switch (cmd) { ++ default: ++ return -ENOIOCTLCMD; ++ case WDIOC_GETSUPPORT: ++ return copy_to_user((struct watchdog_info *) arg, &ident, ++ sizeof (ident)); ++ case WDIOC_GETSTATUS: ++ return put_user(0, (int *) arg); ++ case WDIOC_GETBOOTSTATUS: ++#ifdef CONFIG_SOC_JZ4730 ++ return put_user(REG_CPM_RSTR, (int *) arg); ++#endif ++#ifdef CONFIG_SOC_JZ4740 ++ return put_user(REG_RTC_HWRSR, (int *) arg); ++#endif ++ case WDIOC_KEEPALIVE: ++ jz_wdt_ping(); ++ return 0; ++ case WDIOC_SETTIMEOUT: ++ if (get_user(new_margin, (int *) arg)) ++ return -EFAULT; ++ if (new_margin < 1) ++ return -EINVAL; ++ timer_margin = new_margin; ++ jz_wdt_ping(); ++ /* Fall */ ++ case WDIOC_GETTIMEOUT: ++ return put_user(timer_margin, (int *) arg); ++ } ++} ++ ++static struct file_operations jz_wdt_fops = { ++ .owner = THIS_MODULE, ++ .write = jz_wdt_write, ++ .ioctl = jz_wdt_ioctl, ++ .open = jz_wdt_open, ++ .release = jz_wdt_release, ++}; ++ ++static struct miscdevice jz_wdt_miscdev = { ++ .minor = WATCHDOG_MINOR, ++ .name = "jz_wdt", ++ .fops = &jz_wdt_fops ++}; ++ ++static int __init ++jz_wdt_init(void) ++{ ++ int ret; ++ ++ ret = misc_register(&jz_wdt_miscdev); ++ ++ if (ret) ++ return ret; ++ ++ printk("JzSOC Watchdog Timer: timer margin %d sec\n", timer_margin); ++ ++ return 0; ++} ++ ++static void __exit ++jz_wdt_exit(void) ++{ ++ misc_deregister(&jz_wdt_miscdev); ++} ++ ++module_init(jz_wdt_init); ++module_exit(jz_wdt_exit); ++ ++MODULE_AUTHOR("Wei Jianli"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/fs/Kconfig linux-2.6.24.7/fs/Kconfig +--- linux-2.6.24.7.old/fs/Kconfig 2009-04-12 18:05:07.000000000 +0200 ++++ linux-2.6.24.7/fs/Kconfig 2009-04-12 18:27:23.000000000 +0200 +@@ -1389,6 +1389,9 @@ + + endchoice + ++# UBIFS File system configuration ++source "fs/ubifs/Kconfig" ++ + config CRAMFS + tristate "Compressed ROM file system support (cramfs)" + depends on BLOCK +diff -urN linux-2.6.24.7.old/fs/Kconfig.orig linux-2.6.24.7/fs/Kconfig.orig +--- linux-2.6.24.7.old/fs/Kconfig.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/Kconfig.orig 2009-04-12 18:01:55.000000000 +0200 +@@ -0,0 +1,2202 @@ ++# ++# File system configuration ++# ++ ++menu "File systems" ++ ++if BLOCK ++ ++config EXT2_FS ++ tristate "Second extended fs support" ++ help ++ Ext2 is a standard Linux file system for hard disks. ++ ++ To compile this file system support as a module, choose M here: the ++ module will be called ext2. ++ ++ If unsure, say Y. ++ ++config EXT2_FS_XATTR ++ bool "Ext2 extended attributes" ++ depends on EXT2_FS ++ help ++ Extended attributes are name:value pairs associated with inodes by ++ the kernel or by users (see the attr(5) manual page, or visit ++ for details). ++ ++ If unsure, say N. ++ ++config EXT2_FS_POSIX_ACL ++ bool "Ext2 POSIX Access Control Lists" ++ depends on EXT2_FS_XATTR ++ select FS_POSIX_ACL ++ help ++ Posix Access Control Lists (ACLs) support permissions for users and ++ groups beyond the owner/group/world scheme. ++ ++ To learn more about Access Control Lists, visit the Posix ACLs for ++ Linux website . ++ ++ If you don't know what Access Control Lists are, say N ++ ++config EXT2_FS_SECURITY ++ bool "Ext2 Security Labels" ++ depends on EXT2_FS_XATTR ++ help ++ Security labels support alternative access control models ++ implemented by security modules like SELinux. This option ++ enables an extended attribute handler for file security ++ labels in the ext2 filesystem. ++ ++ If you are not using a security module that requires using ++ extended attributes for file security labels, say N. ++ ++config EXT2_FS_XIP ++ bool "Ext2 execute in place support" ++ depends on EXT2_FS && MMU ++ help ++ Execute in place can be used on memory-backed block devices. If you ++ enable this option, you can select to mount block devices which are ++ capable of this feature without using the page cache. ++ ++ If you do not use a block device that is capable of using this, ++ or if unsure, say N. ++ ++config FS_XIP ++# execute in place ++ bool ++ depends on EXT2_FS_XIP ++ default y ++ ++config EXT3_FS ++ tristate "Ext3 journalling file system support" ++ select JBD ++ help ++ This is the journalling version of the Second extended file system ++ (often called ext3), the de facto standard Linux file system ++ (method to organize files on a storage device) for hard disks. ++ ++ The journalling code included in this driver means you do not have ++ to run e2fsck (file system checker) on your file systems after a ++ crash. The journal keeps track of any changes that were being made ++ at the time the system crashed, and can ensure that your file system ++ is consistent without the need for a lengthy check. ++ ++ Other than adding the journal to the file system, the on-disk format ++ of ext3 is identical to ext2. It is possible to freely switch ++ between using the ext3 driver and the ext2 driver, as long as the ++ file system has been cleanly unmounted, or e2fsck is run on the file ++ system. ++ ++ To add a journal on an existing ext2 file system or change the ++ behavior of ext3 file systems, you can use the tune2fs utility ("man ++ tune2fs"). To modify attributes of files and directories on ext3 ++ file systems, use chattr ("man chattr"). You need to be using ++ e2fsprogs version 1.20 or later in order to create ext3 journals ++ (available at ). ++ ++ To compile this file system support as a module, choose M here: the ++ module will be called ext3. ++ ++config EXT3_FS_XATTR ++ bool "Ext3 extended attributes" ++ depends on EXT3_FS ++ default y ++ help ++ Extended attributes are name:value pairs associated with inodes by ++ the kernel or by users (see the attr(5) manual page, or visit ++ for details). ++ ++ If unsure, say N. ++ ++ You need this for POSIX ACL support on ext3. ++ ++config EXT3_FS_POSIX_ACL ++ bool "Ext3 POSIX Access Control Lists" ++ depends on EXT3_FS_XATTR ++ select FS_POSIX_ACL ++ help ++ Posix Access Control Lists (ACLs) support permissions for users and ++ groups beyond the owner/group/world scheme. ++ ++ To learn more about Access Control Lists, visit the Posix ACLs for ++ Linux website . ++ ++ If you don't know what Access Control Lists are, say N ++ ++config EXT3_FS_SECURITY ++ bool "Ext3 Security Labels" ++ depends on EXT3_FS_XATTR ++ help ++ Security labels support alternative access control models ++ implemented by security modules like SELinux. This option ++ enables an extended attribute handler for file security ++ labels in the ext3 filesystem. ++ ++ If you are not using a security module that requires using ++ extended attributes for file security labels, say N. ++ ++config EXT4DEV_FS ++ tristate "Ext4dev/ext4 extended fs support development (EXPERIMENTAL)" ++ depends on EXPERIMENTAL ++ select JBD2 ++ select CRC16 ++ help ++ Ext4dev is a predecessor filesystem of the next generation ++ extended fs ext4, based on ext3 filesystem code. It will be ++ renamed ext4 fs later, once ext4dev is mature and stabilized. ++ ++ Unlike the change from ext2 filesystem to ext3 filesystem, ++ the on-disk format of ext4dev is not the same as ext3 any more: ++ it is based on extent maps and it supports 48-bit physical block ++ numbers. These combined on-disk format changes will allow ++ ext4dev/ext4 to handle more than 16 TB filesystem volumes -- ++ a hard limit that ext3 cannot overcome without changing the ++ on-disk format. ++ ++ Other than extent maps and 48-bit block numbers, ext4dev also is ++ likely to have other new features such as persistent preallocation, ++ high resolution time stamps, and larger file support etc. These ++ features will be added to ext4dev gradually. ++ ++ To compile this file system support as a module, choose M here. The ++ module will be called ext4dev. ++ ++ If unsure, say N. ++ ++config EXT4DEV_FS_XATTR ++ bool "Ext4dev extended attributes" ++ depends on EXT4DEV_FS ++ default y ++ help ++ Extended attributes are name:value pairs associated with inodes by ++ the kernel or by users (see the attr(5) manual page, or visit ++ for details). ++ ++ If unsure, say N. ++ ++ You need this for POSIX ACL support on ext4dev/ext4. ++ ++config EXT4DEV_FS_POSIX_ACL ++ bool "Ext4dev POSIX Access Control Lists" ++ depends on EXT4DEV_FS_XATTR ++ select FS_POSIX_ACL ++ help ++ POSIX Access Control Lists (ACLs) support permissions for users and ++ groups beyond the owner/group/world scheme. ++ ++ To learn more about Access Control Lists, visit the POSIX ACLs for ++ Linux website . ++ ++ If you don't know what Access Control Lists are, say N ++ ++config EXT4DEV_FS_SECURITY ++ bool "Ext4dev Security Labels" ++ depends on EXT4DEV_FS_XATTR ++ help ++ Security labels support alternative access control models ++ implemented by security modules like SELinux. This option ++ enables an extended attribute handler for file security ++ labels in the ext4dev/ext4 filesystem. ++ ++ If you are not using a security module that requires using ++ extended attributes for file security labels, say N. ++ ++config JBD ++ tristate ++ help ++ This is a generic journalling layer for block devices. It is ++ currently used by the ext3 and OCFS2 file systems, but it could ++ also be used to add journal support to other file systems or block ++ devices such as RAID or LVM. ++ ++ If you are using the ext3 or OCFS2 file systems, you need to ++ say Y here. If you are not using ext3 OCFS2 then you will probably ++ want to say N. ++ ++ To compile this device as a module, choose M here: the module will be ++ called jbd. If you are compiling ext3 or OCFS2 into the kernel, ++ you cannot compile this code as a module. ++ ++config JBD_DEBUG ++ bool "JBD (ext3) debugging support" ++ depends on JBD && DEBUG_FS ++ help ++ If you are using the ext3 journaled file system (or potentially any ++ other file system/device using JBD), this option allows you to ++ enable debugging output while the system is running, in order to ++ help track down any problems you are having. By default the ++ debugging output will be turned off. ++ ++ If you select Y here, then you will be able to turn on debugging ++ with "echo N > /sys/kernel/debug/jbd/jbd-debug", where N is a ++ number between 1 and 5, the higher the number, the more debugging ++ output is generated. To turn debugging off again, do ++ "echo 0 > /sys/kernel/debug/jbd/jbd-debug". ++ ++config JBD2 ++ tristate ++ help ++ This is a generic journaling layer for block devices that support ++ both 32-bit and 64-bit block numbers. It is currently used by ++ the ext4dev/ext4 filesystem, but it could also be used to add ++ journal support to other file systems or block devices such ++ as RAID or LVM. ++ ++ If you are using ext4dev/ext4, you need to say Y here. If you are not ++ using ext4dev/ext4 then you will probably want to say N. ++ ++ To compile this device as a module, choose M here. The module will be ++ called jbd2. If you are compiling ext4dev/ext4 into the kernel, ++ you cannot compile this code as a module. ++ ++config JBD2_DEBUG ++ bool "JBD2 (ext4dev/ext4) debugging support" ++ depends on JBD2 && DEBUG_FS ++ help ++ If you are using the ext4dev/ext4 journaled file system (or ++ potentially any other filesystem/device using JBD2), this option ++ allows you to enable debugging output while the system is running, ++ in order to help track down any problems you are having. ++ By default, the debugging output will be turned off. ++ ++ If you select Y here, then you will be able to turn on debugging ++ with "echo N > /sys/kernel/debug/jbd2/jbd2-debug", where N is a ++ number between 1 and 5. The higher the number, the more debugging ++ output is generated. To turn debugging off again, do ++ "echo 0 > /sys/kernel/debug/jbd2/jbd2-debug". ++ ++config FS_MBCACHE ++# Meta block cache for Extended Attributes (ext2/ext3/ext4) ++ tristate ++ depends on EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4DEV_FS_XATTR ++ default y if EXT2_FS=y || EXT3_FS=y || EXT4DEV_FS=y ++ default m if EXT2_FS=m || EXT3_FS=m || EXT4DEV_FS=m ++ ++config REISERFS_FS ++ tristate "Reiserfs support" ++ help ++ Stores not just filenames but the files themselves in a balanced ++ tree. Uses journalling. ++ ++ Balanced trees are more efficient than traditional file system ++ architectural foundations. ++ ++ In general, ReiserFS is as fast as ext2, but is very efficient with ++ large directories and small files. Additional patches are needed ++ for NFS and quotas, please see for links. ++ ++ It is more easily extended to have features currently found in ++ database and keyword search systems than block allocation based file ++ systems are. The next version will be so extended, and will support ++ plugins consistent with our motto ``It takes more than a license to ++ make source code open.'' ++ ++ Read to learn more about reiserfs. ++ ++ Sponsored by Threshold Networks, Emusic.com, and Bigstorage.com. ++ ++ If you like it, you can pay us to add new features to it that you ++ need, buy a support contract, or pay us to port it to another OS. ++ ++config REISERFS_CHECK ++ bool "Enable reiserfs debug mode" ++ depends on REISERFS_FS ++ help ++ If you set this to Y, then ReiserFS will perform every check it can ++ possibly imagine of its internal consistency throughout its ++ operation. It will also go substantially slower. More than once we ++ have forgotten that this was on, and then gone despondent over the ++ latest benchmarks.:-) Use of this option allows our team to go all ++ out in checking for consistency when debugging without fear of its ++ effect on end users. If you are on the verge of sending in a bug ++ report, say Y and you might get a useful error message. Almost ++ everyone should say N. ++ ++config REISERFS_PROC_INFO ++ bool "Stats in /proc/fs/reiserfs" ++ depends on REISERFS_FS && PROC_FS ++ help ++ Create under /proc/fs/reiserfs a hierarchy of files, displaying ++ various ReiserFS statistics and internal data at the expense of ++ making your kernel or module slightly larger (+8 KB). This also ++ increases the amount of kernel memory required for each mount. ++ Almost everyone but ReiserFS developers and people fine-tuning ++ reiserfs or tracing problems should say N. ++ ++config REISERFS_FS_XATTR ++ bool "ReiserFS extended attributes" ++ depends on REISERFS_FS ++ help ++ Extended attributes are name:value pairs associated with inodes by ++ the kernel or by users (see the attr(5) manual page, or visit ++ for details). ++ ++ If unsure, say N. ++ ++config REISERFS_FS_POSIX_ACL ++ bool "ReiserFS POSIX Access Control Lists" ++ depends on REISERFS_FS_XATTR ++ select FS_POSIX_ACL ++ help ++ Posix Access Control Lists (ACLs) support permissions for users and ++ groups beyond the owner/group/world scheme. ++ ++ To learn more about Access Control Lists, visit the Posix ACLs for ++ Linux website . ++ ++ If you don't know what Access Control Lists are, say N ++ ++config REISERFS_FS_SECURITY ++ bool "ReiserFS Security Labels" ++ depends on REISERFS_FS_XATTR ++ help ++ Security labels support alternative access control models ++ implemented by security modules like SELinux. This option ++ enables an extended attribute handler for file security ++ labels in the ReiserFS filesystem. ++ ++ If you are not using a security module that requires using ++ extended attributes for file security labels, say N. ++ ++config JFS_FS ++ tristate "JFS filesystem support" ++ select NLS ++ help ++ This is a port of IBM's Journaled Filesystem . More information is ++ available in the file . ++ ++ If you do not intend to use the JFS filesystem, say N. ++ ++config JFS_POSIX_ACL ++ bool "JFS POSIX Access Control Lists" ++ depends on JFS_FS ++ select FS_POSIX_ACL ++ help ++ Posix Access Control Lists (ACLs) support permissions for users and ++ groups beyond the owner/group/world scheme. ++ ++ To learn more about Access Control Lists, visit the Posix ACLs for ++ Linux website . ++ ++ If you don't know what Access Control Lists are, say N ++ ++config JFS_SECURITY ++ bool "JFS Security Labels" ++ depends on JFS_FS ++ help ++ Security labels support alternative access control models ++ implemented by security modules like SELinux. This option ++ enables an extended attribute handler for file security ++ labels in the jfs filesystem. ++ ++ If you are not using a security module that requires using ++ extended attributes for file security labels, say N. ++ ++config JFS_DEBUG ++ bool "JFS debugging" ++ depends on JFS_FS ++ help ++ If you are experiencing any problems with the JFS filesystem, say ++ Y here. This will result in additional debugging messages to be ++ written to the system log. Under normal circumstances, this ++ results in very little overhead. ++ ++config JFS_STATISTICS ++ bool "JFS statistics" ++ depends on JFS_FS ++ help ++ Enabling this option will cause statistics from the JFS file system ++ to be made available to the user in the /proc/fs/jfs/ directory. ++ ++config FS_POSIX_ACL ++# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs) ++# ++# NOTE: you can implement Posix ACLs without these helpers (XFS does). ++# Never use this symbol for ifdefs. ++# ++ bool ++ default n ++ ++source "fs/xfs/Kconfig" ++source "fs/gfs2/Kconfig" ++source "fs/yaffs2/Kconfig" ++ ++config OCFS2_FS ++ tristate "OCFS2 file system support" ++ depends on NET && SYSFS ++ select CONFIGFS_FS ++ select JBD ++ select CRC32 ++ help ++ OCFS2 is a general purpose extent based shared disk cluster file ++ system with many similarities to ext3. It supports 64 bit inode ++ numbers, and has automatically extending metadata groups which may ++ also make it attractive for non-clustered use. ++ ++ You'll want to install the ocfs2-tools package in order to at least ++ get "mount.ocfs2". ++ ++ Project web page: http://oss.oracle.com/projects/ocfs2 ++ Tools web page: http://oss.oracle.com/projects/ocfs2-tools ++ OCFS2 mailing lists: http://oss.oracle.com/projects/ocfs2/mailman/ ++ ++ Note: Features which OCFS2 does not support yet: ++ - extended attributes ++ - quotas ++ - cluster aware flock ++ - Directory change notification (F_NOTIFY) ++ - Distributed Caching (F_SETLEASE/F_GETLEASE/break_lease) ++ - POSIX ACLs ++ - readpages / writepages (not user visible) ++ ++config OCFS2_DEBUG_MASKLOG ++ bool "OCFS2 logging support" ++ depends on OCFS2_FS ++ default y ++ help ++ The ocfs2 filesystem has an extensive logging system. The system ++ allows selection of events to log via files in /sys/o2cb/logmask/. ++ This option will enlarge your kernel, but it allows debugging of ++ ocfs2 filesystem issues. ++ ++config OCFS2_DEBUG_FS ++ bool "OCFS2 expensive checks" ++ depends on OCFS2_FS ++ default n ++ help ++ This option will enable expensive consistency checks. Enable ++ this option for debugging only as it is likely to decrease ++ performance of the filesystem. ++ ++config MINI_FO ++ tristate "Mini fanout overlay filesystem" ++ ++config MINIX_FS ++ tristate "Minix fs support" ++ help ++ Minix is a simple operating system used in many classes about OS's. ++ The minix file system (method to organize files on a hard disk ++ partition or a floppy disk) was the original file system for Linux, ++ but has been superseded by the second extended file system ext2fs. ++ You don't want to use the minix file system on your hard disk ++ because of certain built-in restrictions, but it is sometimes found ++ on older Linux floppy disks. This option will enlarge your kernel ++ by about 28 KB. If unsure, say N. ++ ++ To compile this file system support as a module, choose M here: the ++ module will be called minix. Note that the file system of your root ++ partition (the one containing the directory /) cannot be compiled as ++ a module. ++ ++config ROMFS_FS ++ tristate "ROM file system support" ++ ---help--- ++ This is a very small read-only file system mainly intended for ++ initial ram disks of installation disks, but it could be used for ++ other read-only media as well. Read ++ for details. ++ ++ To compile this file system support as a module, choose M here: the ++ module will be called romfs. Note that the file system of your ++ root partition (the one containing the directory /) cannot be a ++ module. ++ ++ If you don't know whether you need it, then you don't need it: ++ answer N. ++ ++endif ++ ++config INOTIFY ++ bool "Inotify file change notification support" ++ default y ++ ---help--- ++ Say Y here to enable inotify support. Inotify is a file change ++ notification system and a replacement for dnotify. Inotify fixes ++ numerous shortcomings in dnotify and introduces several new features ++ including multiple file events, one-shot support, and unmount ++ notification. ++ ++ For more information, see ++ ++ If unsure, say Y. ++ ++config INOTIFY_USER ++ bool "Inotify support for userspace" ++ depends on INOTIFY ++ default y ++ ---help--- ++ Say Y here to enable inotify support for userspace, including the ++ associated system calls. Inotify allows monitoring of both files and ++ directories via a single open fd. Events are read from the file ++ descriptor, which is also select()- and poll()-able. ++ ++ For more information, see ++ ++ If unsure, say Y. ++ ++config QUOTA ++ bool "Quota support" ++ help ++ If you say Y here, you will be able to set per user limits for disk ++ usage (also called disk quotas). Currently, it works for the ++ ext2, ext3, and reiserfs file system. ext3 also supports journalled ++ quotas for which you don't need to run quotacheck(8) after an unclean ++ shutdown. ++ For further details, read the Quota mini-HOWTO, available from ++ , or the documentation provided ++ with the quota tools. Probably the quota support is only useful for ++ multi user systems. If unsure, say N. ++ ++config QUOTA_NETLINK_INTERFACE ++ bool "Report quota messages through netlink interface" ++ depends on QUOTA && NET ++ help ++ If you say Y here, quota warnings (about exceeding softlimit, reaching ++ hardlimit, etc.) will be reported through netlink interface. If unsure, ++ say Y. ++ ++config PRINT_QUOTA_WARNING ++ bool "Print quota warnings to console (OBSOLETE)" ++ depends on QUOTA ++ default y ++ help ++ If you say Y here, quota warnings (about exceeding softlimit, reaching ++ hardlimit, etc.) will be printed to the process' controlling terminal. ++ Note that this behavior is currently deprecated and may go away in ++ future. Please use notification via netlink socket instead. ++ ++config QFMT_V1 ++ tristate "Old quota format support" ++ depends on QUOTA ++ help ++ This quota format was (is) used by kernels earlier than 2.4.22. If ++ you have quota working and you don't want to convert to new quota ++ format say Y here. ++ ++config QFMT_V2 ++ tristate "Quota format v2 support" ++ depends on QUOTA ++ help ++ This quota format allows using quotas with 32-bit UIDs/GIDs. If you ++ need this functionality say Y here. ++ ++config QUOTACTL ++ bool ++ depends on XFS_QUOTA || QUOTA ++ default y ++ ++config DNOTIFY ++ bool "Dnotify support" ++ default y ++ help ++ Dnotify is a directory-based per-fd file change notification system ++ that uses signals to communicate events to user-space. There exist ++ superior alternatives, but some applications may still rely on ++ dnotify. ++ ++ If unsure, say Y. ++ ++config AUTOFS_FS ++ tristate "Kernel automounter support" ++ help ++ The automounter is a tool to automatically mount remote file systems ++ on demand. This implementation is partially kernel-based to reduce ++ overhead in the already-mounted case; this is unlike the BSD ++ automounter (amd), which is a pure user space daemon. ++ ++ To use the automounter you need the user-space tools from the autofs ++ package; you can find the location in . ++ You also want to answer Y to "NFS file system support", below. ++ ++ If you want to use the newer version of the automounter with more ++ features, say N here and say Y to "Kernel automounter v4 support", ++ below. ++ ++ To compile this support as a module, choose M here: the module will be ++ called autofs. ++ ++ If you are not a part of a fairly large, distributed network, you ++ probably do not need an automounter, and can say N here. ++ ++config AUTOFS4_FS ++ tristate "Kernel automounter version 4 support (also supports v3)" ++ help ++ The automounter is a tool to automatically mount remote file systems ++ on demand. This implementation is partially kernel-based to reduce ++ overhead in the already-mounted case; this is unlike the BSD ++ automounter (amd), which is a pure user space daemon. ++ ++ To use the automounter you need the user-space tools from ++ ; you also ++ want to answer Y to "NFS file system support", below. ++ ++ To compile this support as a module, choose M here: the module will be ++ called autofs4. You will need to add "alias autofs autofs4" to your ++ modules configuration file. ++ ++ If you are not a part of a fairly large, distributed network or ++ don't have a laptop which needs to dynamically reconfigure to the ++ local network, you probably do not need an automounter, and can say ++ N here. ++ ++config FUSE_FS ++ tristate "Filesystem in Userspace support" ++ help ++ With FUSE it is possible to implement a fully functional filesystem ++ in a userspace program. ++ ++ There's also companion library: libfuse. This library along with ++ utilities is available from the FUSE homepage: ++ ++ ++ See for more information. ++ See for needed library/utility version. ++ ++ If you want to develop a userspace FS, or if you want to use ++ a filesystem based on FUSE, answer Y or M. ++ ++config GENERIC_ACL ++ bool ++ select FS_POSIX_ACL ++ ++if BLOCK ++menu "CD-ROM/DVD Filesystems" ++ ++config ISO9660_FS ++ tristate "ISO 9660 CDROM file system support" ++ help ++ This is the standard file system used on CD-ROMs. It was previously ++ known as "High Sierra File System" and is called "hsfs" on other ++ Unix systems. The so-called Rock-Ridge extensions which allow for ++ long Unix filenames and symbolic links are also supported by this ++ driver. If you have a CD-ROM drive and want to do more with it than ++ just listen to audio CDs and watch its LEDs, say Y (and read ++ and the CD-ROM-HOWTO, ++ available from ), thereby ++ enlarging your kernel by about 27 KB; otherwise say N. ++ ++ To compile this file system support as a module, choose M here: the ++ module will be called isofs. ++ ++config JOLIET ++ bool "Microsoft Joliet CDROM extensions" ++ depends on ISO9660_FS ++ select NLS ++ help ++ Joliet is a Microsoft extension for the ISO 9660 CD-ROM file system ++ which allows for long filenames in unicode format (unicode is the ++ new 16 bit character code, successor to ASCII, which encodes the ++ characters of almost all languages of the world; see ++ for more information). Say Y here if you ++ want to be able to read Joliet CD-ROMs under Linux. ++ ++config ZISOFS ++ bool "Transparent decompression extension" ++ depends on ISO9660_FS ++ select ZLIB_INFLATE ++ help ++ This is a Linux-specific extension to RockRidge which lets you store ++ data in compressed form on a CD-ROM and have it transparently ++ decompressed when the CD-ROM is accessed. See ++ for the tools ++ necessary to create such a filesystem. Say Y here if you want to be ++ able to read such compressed CD-ROMs. ++ ++config UDF_FS ++ tristate "UDF file system support" ++ help ++ This is the new file system used on some CD-ROMs and DVDs. Say Y if ++ you intend to mount DVD discs or CDRW's written in packet mode, or ++ if written to by other UDF utilities, such as DirectCD. ++ Please read . ++ ++ To compile this file system support as a module, choose M here: the ++ module will be called udf. ++ ++ If unsure, say N. ++ ++config UDF_NLS ++ bool ++ default y ++ depends on (UDF_FS=m && NLS) || (UDF_FS=y && NLS=y) ++ ++endmenu ++endif ++ ++if BLOCK ++menu "DOS/FAT/NT Filesystems" ++ ++config FAT_FS ++ tristate ++ select NLS ++ help ++ If you want to use one of the FAT-based file systems (the MS-DOS and ++ VFAT (Windows 95) file systems), then you must say Y or M here ++ to include FAT support. You will then be able to mount partitions or ++ diskettes with FAT-based file systems and transparently access the ++ files on them, i.e. MSDOS files will look and behave just like all ++ other Unix files. ++ ++ This FAT support is not a file system in itself, it only provides ++ the foundation for the other file systems. You will have to say Y or ++ M to at least one of "MSDOS fs support" or "VFAT fs support" in ++ order to make use of it. ++ ++ Another way to read and write MSDOS floppies and hard drive ++ partitions from within Linux (but not transparently) is with the ++ mtools ("man mtools") program suite. You don't need to say Y here in ++ order to do that. ++ ++ If you need to move large files on floppies between a DOS and a ++ Linux box, say Y here, mount the floppy under Linux with an MSDOS ++ file system and use GNU tar's M option. GNU tar is a program ++ available for Unix and DOS ("man tar" or "info tar"). ++ ++ The FAT support will enlarge your kernel by about 37 KB. If unsure, ++ say Y. ++ ++ To compile this as a module, choose M here: the module will be called ++ fat. Note that if you compile the FAT support as a module, you ++ cannot compile any of the FAT-based file systems into the kernel ++ -- they will have to be modules as well. ++ ++config MSDOS_FS ++ tristate "MSDOS fs support" ++ select FAT_FS ++ help ++ This allows you to mount MSDOS partitions of your hard drive (unless ++ they are compressed; to access compressed MSDOS partitions under ++ Linux, you can either use the DOS emulator DOSEMU, described in the ++ DOSEMU-HOWTO, available from ++ , or try dmsdosfs in ++ . If you ++ intend to use dosemu with a non-compressed MSDOS partition, say Y ++ here) and MSDOS floppies. This means that file access becomes ++ transparent, i.e. the MSDOS files look and behave just like all ++ other Unix files. ++ ++ If you have Windows 95 or Windows NT installed on your MSDOS ++ partitions, you should use the VFAT file system (say Y to "VFAT fs ++ support" below), or you will not be able to see the long filenames ++ generated by Windows 95 / Windows NT. ++ ++ This option will enlarge your kernel by about 7 KB. If unsure, ++ answer Y. This will only work if you said Y to "DOS FAT fs support" ++ as well. To compile this as a module, choose M here: the module will ++ be called msdos. ++ ++config VFAT_FS ++ tristate "VFAT (Windows-95) fs support" ++ select FAT_FS ++ help ++ This option provides support for normal Windows file systems with ++ long filenames. That includes non-compressed FAT-based file systems ++ used by Windows 95, Windows 98, Windows NT 4.0, and the Unix ++ programs from the mtools package. ++ ++ The VFAT support enlarges your kernel by about 10 KB and it only ++ works if you said Y to the "DOS FAT fs support" above. Please read ++ the file for details. If ++ unsure, say Y. ++ ++ To compile this as a module, choose M here: the module will be called ++ vfat. ++ ++config FAT_DEFAULT_CODEPAGE ++ int "Default codepage for FAT" ++ depends on MSDOS_FS || VFAT_FS ++ default 437 ++ help ++ This option should be set to the codepage of your FAT filesystems. ++ It can be overridden with the "codepage" mount option. ++ See for more information. ++ ++config FAT_DEFAULT_IOCHARSET ++ string "Default iocharset for FAT" ++ depends on VFAT_FS ++ default "iso8859-1" ++ help ++ Set this to the default input/output character set you'd ++ like FAT to use. It should probably match the character set ++ that most of your FAT filesystems use, and can be overridden ++ with the "iocharset" mount option for FAT filesystems. ++ Note that "utf8" is not recommended for FAT filesystems. ++ If unsure, you shouldn't set "utf8" here. ++ See for more information. ++ ++config NTFS_FS ++ tristate "NTFS file system support" ++ select NLS ++ help ++ NTFS is the file system of Microsoft Windows NT, 2000, XP and 2003. ++ ++ Saying Y or M here enables read support. There is partial, but ++ safe, write support available. For write support you must also ++ say Y to "NTFS write support" below. ++ ++ There are also a number of user-space tools available, called ++ ntfsprogs. These include ntfsundelete and ntfsresize, that work ++ without NTFS support enabled in the kernel. ++ ++ This is a rewrite from scratch of Linux NTFS support and replaced ++ the old NTFS code starting with Linux 2.5.11. A backport to ++ the Linux 2.4 kernel series is separately available as a patch ++ from the project web site. ++ ++ For more information see ++ and . ++ ++ To compile this file system support as a module, choose M here: the ++ module will be called ntfs. ++ ++ If you are not using Windows NT, 2000, XP or 2003 in addition to ++ Linux on your computer it is safe to say N. ++ ++config NTFS_DEBUG ++ bool "NTFS debugging support" ++ depends on NTFS_FS ++ help ++ If you are experiencing any problems with the NTFS file system, say ++ Y here. This will result in additional consistency checks to be ++ performed by the driver as well as additional debugging messages to ++ be written to the system log. Note that debugging messages are ++ disabled by default. To enable them, supply the option debug_msgs=1 ++ at the kernel command line when booting the kernel or as an option ++ to insmod when loading the ntfs module. Once the driver is active, ++ you can enable debugging messages by doing (as root): ++ echo 1 > /proc/sys/fs/ntfs-debug ++ Replacing the "1" with "0" would disable debug messages. ++ ++ If you leave debugging messages disabled, this results in little ++ overhead, but enabling debug messages results in very significant ++ slowdown of the system. ++ ++ When reporting bugs, please try to have available a full dump of ++ debugging messages while the misbehaviour was occurring. ++ ++config NTFS_RW ++ bool "NTFS write support" ++ depends on NTFS_FS ++ help ++ This enables the partial, but safe, write support in the NTFS driver. ++ ++ The only supported operation is overwriting existing files, without ++ changing the file length. No file or directory creation, deletion or ++ renaming is possible. Note only non-resident files can be written to ++ so you may find that some very small files (<500 bytes or so) cannot ++ be written to. ++ ++ While we cannot guarantee that it will not damage any data, we have ++ so far not received a single report where the driver would have ++ damaged someones data so we assume it is perfectly safe to use. ++ ++ Note: While write support is safe in this version (a rewrite from ++ scratch of the NTFS support), it should be noted that the old NTFS ++ write support, included in Linux 2.5.10 and before (since 1997), ++ is not safe. ++ ++ This is currently useful with TopologiLinux. TopologiLinux is run ++ on top of any DOS/Microsoft Windows system without partitioning your ++ hard disk. Unlike other Linux distributions TopologiLinux does not ++ need its own partition. For more information see ++ ++ ++ It is perfectly safe to say N here. ++ ++endmenu ++endif ++ ++menu "Pseudo filesystems" ++ ++config PROC_FS ++ bool "/proc file system support" if EMBEDDED ++ default y ++ help ++ This is a virtual file system providing information about the status ++ of the system. "Virtual" means that it doesn't take up any space on ++ your hard disk: the files are created on the fly by the kernel when ++ you try to access them. Also, you cannot read the files with older ++ version of the program less: you need to use more or cat. ++ ++ It's totally cool; for example, "cat /proc/interrupts" gives ++ information about what the different IRQs are used for at the moment ++ (there is a small number of Interrupt ReQuest lines in your computer ++ that are used by the attached devices to gain the CPU's attention -- ++ often a source of trouble if two devices are mistakenly configured ++ to use the same IRQ). The program procinfo to display some ++ information about your system gathered from the /proc file system. ++ ++ Before you can use the /proc file system, it has to be mounted, ++ meaning it has to be given a location in the directory hierarchy. ++ That location should be /proc. A command such as "mount -t proc proc ++ /proc" or the equivalent line in /etc/fstab does the job. ++ ++ The /proc file system is explained in the file ++ and on the proc(5) manpage ++ ("man 5 proc"). ++ ++ This option will enlarge your kernel by about 67 KB. Several ++ programs depend on this, so everyone should say Y here. ++ ++config PROC_KCORE ++ bool "/proc/kcore support" if !ARM ++ depends on PROC_FS && MMU ++ ++config PROC_VMCORE ++ bool "/proc/vmcore support (EXPERIMENTAL)" ++ depends on PROC_FS && EXPERIMENTAL && CRASH_DUMP ++ default y ++ help ++ Exports the dump image of crashed kernel in ELF format. ++ ++config PROC_SYSCTL ++ bool "Sysctl support (/proc/sys)" if EMBEDDED ++ depends on PROC_FS ++ select SYSCTL ++ default y ++ ---help--- ++ The sysctl interface provides a means of dynamically changing ++ certain kernel parameters and variables on the fly without requiring ++ a recompile of the kernel or reboot of the system. The primary ++ interface is through /proc/sys. If you say Y here a tree of ++ modifiable sysctl entries will be generated beneath the ++ /proc/sys directory. They are explained in the files ++ in . Note that enabling this ++ option will enlarge the kernel by at least 8 KB. ++ ++ As it is generally a good thing, you should say Y here unless ++ building a kernel for install/rescue disks or your system is very ++ limited in memory. ++ ++config SYSFS ++ bool "sysfs file system support" if EMBEDDED ++ default y ++ help ++ The sysfs filesystem is a virtual filesystem that the kernel uses to ++ export internal kernel objects, their attributes, and their ++ relationships to one another. ++ ++ Users can use sysfs to ascertain useful information about the running ++ kernel, such as the devices the kernel has discovered on each bus and ++ which driver each is bound to. sysfs can also be used to tune devices ++ and other kernel subsystems. ++ ++ Some system agents rely on the information in sysfs to operate. ++ /sbin/hotplug uses device and object attributes in sysfs to assist in ++ delegating policy decisions, like persistently naming devices. ++ ++ sysfs is currently used by the block subsystem to mount the root ++ partition. If sysfs is disabled you must specify the boot device on ++ the kernel boot command line via its major and minor numbers. For ++ example, "root=03:01" for /dev/hda1. ++ ++ Designers of embedded systems may wish to say N here to conserve space. ++ ++config TMPFS ++ bool "Virtual memory file system support (former shm fs)" ++ help ++ Tmpfs is a file system which keeps all files in virtual memory. ++ ++ Everything in tmpfs is temporary in the sense that no files will be ++ created on your hard drive. The files live in memory and swap ++ space. If you unmount a tmpfs instance, everything stored therein is ++ lost. ++ ++ See for details. ++ ++config TMPFS_POSIX_ACL ++ bool "Tmpfs POSIX Access Control Lists" ++ depends on TMPFS ++ select GENERIC_ACL ++ help ++ POSIX Access Control Lists (ACLs) support permissions for users and ++ groups beyond the owner/group/world scheme. ++ ++ To learn more about Access Control Lists, visit the POSIX ACLs for ++ Linux website . ++ ++ If you don't know what Access Control Lists are, say N. ++ ++config HUGETLBFS ++ bool "HugeTLB file system support" ++ depends on X86 || IA64 || PPC64 || SPARC64 || (SUPERH && MMU) || BROKEN ++ help ++ hugetlbfs is a filesystem backing for HugeTLB pages, based on ++ ramfs. For architectures that support it, say Y here and read ++ for details. ++ ++ If unsure, say N. ++ ++config HUGETLB_PAGE ++ def_bool HUGETLBFS ++ ++config CONFIGFS_FS ++ tristate "Userspace-driven configuration filesystem (EXPERIMENTAL)" ++ depends on SYSFS && EXPERIMENTAL ++ help ++ configfs is a ram-based filesystem that provides the converse ++ of sysfs's functionality. Where sysfs is a filesystem-based ++ view of kernel objects, configfs is a filesystem-based manager ++ of kernel objects, or config_items. ++ ++ Both sysfs and configfs can and should exist together on the ++ same system. One is not a replacement for the other. ++ ++endmenu ++ ++menu "Miscellaneous filesystems" ++ ++config ADFS_FS ++ tristate "ADFS file system support (EXPERIMENTAL)" ++ depends on BLOCK && EXPERIMENTAL ++ help ++ The Acorn Disc Filing System is the standard file system of the ++ RiscOS operating system which runs on Acorn's ARM-based Risc PC ++ systems and the Acorn Archimedes range of machines. If you say Y ++ here, Linux will be able to read from ADFS partitions on hard drives ++ and from ADFS-formatted floppy discs. If you also want to be able to ++ write to those devices, say Y to "ADFS write support" below. ++ ++ The ADFS partition should be the first partition (i.e., ++ /dev/[hs]d?1) on each of your drives. Please read the file ++ for further details. ++ ++ To compile this code as a module, choose M here: the module will be ++ called adfs. ++ ++ If unsure, say N. ++ ++config ADFS_FS_RW ++ bool "ADFS write support (DANGEROUS)" ++ depends on ADFS_FS ++ help ++ If you say Y here, you will be able to write to ADFS partitions on ++ hard drives and ADFS-formatted floppy disks. This is experimental ++ codes, so if you're unsure, say N. ++ ++config AFFS_FS ++ tristate "Amiga FFS file system support (EXPERIMENTAL)" ++ depends on BLOCK && EXPERIMENTAL ++ help ++ The Fast File System (FFS) is the common file system used on hard ++ disks by Amiga(tm) systems since AmigaOS Version 1.3 (34.20). Say Y ++ if you want to be able to read and write files from and to an Amiga ++ FFS partition on your hard drive. Amiga floppies however cannot be ++ read with this driver due to an incompatibility of the floppy ++ controller used in an Amiga and the standard floppy controller in ++ PCs and workstations. Read ++ and . ++ ++ With this driver you can also mount disk files used by Bernd ++ Schmidt's Un*X Amiga Emulator ++ (). ++ If you want to do this, you will also need to say Y or M to "Loop ++ device support", above. ++ ++ To compile this file system support as a module, choose M here: the ++ module will be called affs. If unsure, say N. ++ ++config ECRYPT_FS ++ tristate "eCrypt filesystem layer support (EXPERIMENTAL)" ++ depends on EXPERIMENTAL && KEYS && CRYPTO && NET ++ help ++ Encrypted filesystem that operates on the VFS layer. See ++ to learn more about ++ eCryptfs. Userspace components are required and can be ++ obtained from . ++ ++ To compile this file system support as a module, choose M here: the ++ module will be called ecryptfs. ++ ++config HFS_FS ++ tristate "Apple Macintosh file system support (EXPERIMENTAL)" ++ depends on BLOCK && EXPERIMENTAL ++ select NLS ++ help ++ If you say Y here, you will be able to mount Macintosh-formatted ++ floppy disks and hard drive partitions with full read-write access. ++ Please read to learn about ++ the available mount options. ++ ++ To compile this file system support as a module, choose M here: the ++ module will be called hfs. ++ ++config HFSPLUS_FS ++ tristate "Apple Extended HFS file system support" ++ depends on BLOCK ++ select NLS ++ select NLS_UTF8 ++ help ++ If you say Y here, you will be able to mount extended format ++ Macintosh-formatted hard drive partitions with full read-write access. ++ ++ This file system is often called HFS+ and was introduced with ++ MacOS 8. It includes all Mac specific filesystem data such as ++ data forks and creator codes, but it also has several UNIX ++ style features such as file ownership and permissions. ++ ++config BEFS_FS ++ tristate "BeOS file system (BeFS) support (read only) (EXPERIMENTAL)" ++ depends on BLOCK && EXPERIMENTAL ++ select NLS ++ help ++ The BeOS File System (BeFS) is the native file system of Be, Inc's ++ BeOS. Notable features include support for arbitrary attributes ++ on files and directories, and database-like indices on selected ++ attributes. (Also note that this driver doesn't make those features ++ available at this time). It is a 64 bit filesystem, so it supports ++ extremely large volumes and files. ++ ++ If you use this filesystem, you should also say Y to at least one ++ of the NLS (native language support) options below. ++ ++ If you don't know what this is about, say N. ++ ++ To compile this as a module, choose M here: the module will be ++ called befs. ++ ++config BEFS_DEBUG ++ bool "Debug BeFS" ++ depends on BEFS_FS ++ help ++ If you say Y here, you can use the 'debug' mount option to enable ++ debugging output from the driver. ++ ++config BFS_FS ++ tristate "BFS file system support (EXPERIMENTAL)" ++ depends on BLOCK && EXPERIMENTAL ++ help ++ Boot File System (BFS) is a file system used under SCO UnixWare to ++ allow the bootloader access to the kernel image and other important ++ files during the boot process. It is usually mounted under /stand ++ and corresponds to the slice marked as "STAND" in the UnixWare ++ partition. You should say Y if you want to read or write the files ++ on your /stand slice from within Linux. You then also need to say Y ++ to "UnixWare slices support", below. More information about the BFS ++ file system is contained in the file ++ . ++ ++ If you don't know what this is about, say N. ++ ++ To compile this as a module, choose M here: the module will be called ++ bfs. Note that the file system of your root partition (the one ++ containing the directory /) cannot be compiled as a module. ++ ++ ++ ++config EFS_FS ++ tristate "EFS file system support (read only) (EXPERIMENTAL)" ++ depends on BLOCK && EXPERIMENTAL ++ help ++ EFS is an older file system used for non-ISO9660 CD-ROMs and hard ++ disk partitions by SGI's IRIX operating system (IRIX 6.0 and newer ++ uses the XFS file system for hard disk partitions however). ++ ++ This implementation only offers read-only access. If you don't know ++ what all this is about, it's safe to say N. For more information ++ about EFS see its home page at . ++ ++ To compile the EFS file system support as a module, choose M here: the ++ module will be called efs. ++ ++config JFFS2_FS ++ tristate "Journalling Flash File System v2 (JFFS2) support" ++ select CRC32 ++ depends on MTD ++ help ++ JFFS2 is the second generation of the Journalling Flash File System ++ for use on diskless embedded devices. It provides improved wear ++ levelling, compression and support for hard links. You cannot use ++ this on normal block devices, only on 'MTD' devices. ++ ++ Further information on the design and implementation of JFFS2 is ++ available at . ++ ++config JFFS2_FS_DEBUG ++ int "JFFS2 debugging verbosity (0 = quiet, 2 = noisy)" ++ depends on JFFS2_FS ++ default "0" ++ help ++ This controls the amount of debugging messages produced by the JFFS2 ++ code. Set it to zero for use in production systems. For evaluation, ++ testing and debugging, it's advisable to set it to one. This will ++ enable a few assertions and will print debugging messages at the ++ KERN_DEBUG loglevel, where they won't normally be visible. Level 2 ++ is unlikely to be useful - it enables extra debugging in certain ++ areas which at one point needed debugging, but when the bugs were ++ located and fixed, the detailed messages were relegated to level 2. ++ ++ If reporting bugs, please try to have available a full dump of the ++ messages at debug level 1 while the misbehaviour was occurring. ++ ++config JFFS2_FS_WRITEBUFFER ++ bool "JFFS2 write-buffering support" ++ depends on JFFS2_FS ++ default y ++ help ++ This enables the write-buffering support in JFFS2. ++ ++ This functionality is required to support JFFS2 on the following ++ types of flash devices: ++ - NAND flash ++ - NOR flash with transparent ECC ++ - DataFlash ++ ++config JFFS2_FS_WBUF_VERIFY ++ bool "Verify JFFS2 write-buffer reads" ++ depends on JFFS2_FS_WRITEBUFFER ++ default n ++ help ++ This causes JFFS2 to read back every page written through the ++ write-buffer, and check for errors. ++ ++config JFFS2_SUMMARY ++ bool "JFFS2 summary support (EXPERIMENTAL)" ++ depends on JFFS2_FS && EXPERIMENTAL ++ default n ++ help ++ This feature makes it possible to use summary information ++ for faster filesystem mount. ++ ++ The summary information can be inserted into a filesystem image ++ by the utility 'sumtool'. ++ ++ If unsure, say 'N'. ++ ++config JFFS2_FS_XATTR ++ bool "JFFS2 XATTR support (EXPERIMENTAL)" ++ depends on JFFS2_FS && EXPERIMENTAL ++ default n ++ help ++ Extended attributes are name:value pairs associated with inodes by ++ the kernel or by users (see the attr(5) manual page, or visit ++ for details). ++ ++ If unsure, say N. ++ ++config JFFS2_FS_POSIX_ACL ++ bool "JFFS2 POSIX Access Control Lists" ++ depends on JFFS2_FS_XATTR ++ default y ++ select FS_POSIX_ACL ++ help ++ Posix Access Control Lists (ACLs) support permissions for users and ++ groups beyond the owner/group/world scheme. ++ ++ To learn more about Access Control Lists, visit the Posix ACLs for ++ Linux website . ++ ++ If you don't know what Access Control Lists are, say N ++ ++config JFFS2_FS_SECURITY ++ bool "JFFS2 Security Labels" ++ depends on JFFS2_FS_XATTR ++ default y ++ help ++ Security labels support alternative access control models ++ implemented by security modules like SELinux. This option ++ enables an extended attribute handler for file security ++ labels in the jffs2 filesystem. ++ ++ If you are not using a security module that requires using ++ extended attributes for file security labels, say N. ++ ++config JFFS2_COMPRESSION_OPTIONS ++ bool "Advanced compression options for JFFS2" ++ depends on JFFS2_FS ++ default n ++ help ++ Enabling this option allows you to explicitly choose which ++ compression modules, if any, are enabled in JFFS2. Removing ++ compressors can mean you cannot read existing file systems, ++ and enabling experimental compressors can mean that you ++ write a file system which cannot be read by a standard kernel. ++ ++ If unsure, you should _definitely_ say 'N'. ++ ++config JFFS2_ZLIB ++ bool "JFFS2 ZLIB compression support" if JFFS2_COMPRESSION_OPTIONS ++ select ZLIB_INFLATE ++ select ZLIB_DEFLATE ++ depends on JFFS2_FS ++ default y ++ help ++ Zlib is designed to be a free, general-purpose, legally unencumbered, ++ lossless data-compression library for use on virtually any computer ++ hardware and operating system. See for ++ further information. ++ ++ Say 'Y' if unsure. ++ ++config JFFS2_LZO ++ bool "JFFS2 LZO compression support" if JFFS2_COMPRESSION_OPTIONS ++ select LZO_COMPRESS ++ select LZO_DECOMPRESS ++ depends on JFFS2_FS ++ default n ++ help ++ minilzo-based compression. Generally works better than Zlib. ++ ++ This feature was added in July, 2007. Say 'N' if you need ++ compatibility with older bootloaders or kernels. ++ ++config JFFS2_RTIME ++ bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS ++ depends on JFFS2_FS ++ default y ++ help ++ Rtime does manage to recompress already-compressed data. Say 'Y' if unsure. ++ ++config JFFS2_RUBIN ++ bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS ++ depends on JFFS2_FS ++ default n ++ help ++ RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure. ++ ++choice ++ prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS ++ default JFFS2_CMODE_PRIORITY ++ depends on JFFS2_FS ++ help ++ You can set here the default compression mode of JFFS2 from ++ the available compression modes. Don't touch if unsure. ++ ++config JFFS2_CMODE_NONE ++ bool "no compression" ++ help ++ Uses no compression. ++ ++config JFFS2_CMODE_PRIORITY ++ bool "priority" ++ help ++ Tries the compressors in a predefined order and chooses the first ++ successful one. ++ ++config JFFS2_CMODE_SIZE ++ bool "size (EXPERIMENTAL)" ++ help ++ Tries all compressors and chooses the one which has the smallest ++ result. ++ ++config JFFS2_CMODE_FAVOURLZO ++ bool "Favour LZO" ++ help ++ Tries all compressors and chooses the one which has the smallest ++ result but gives some preference to LZO (which has faster ++ decompression) at the expense of size. ++ ++endchoice ++ ++config CRAMFS ++ tristate "Compressed ROM file system support (cramfs)" ++ depends on BLOCK ++ select ZLIB_INFLATE ++ help ++ Saying Y here includes support for CramFs (Compressed ROM File ++ System). CramFs is designed to be a simple, small, and compressed ++ file system for ROM based embedded systems. CramFs is read-only, ++ limited to 256MB file systems (with 16MB files), and doesn't support ++ 16/32 bits uid/gid, hard links and timestamps. ++ ++ See and ++ for further information. ++ ++ To compile this as a module, choose M here: the module will be called ++ cramfs. Note that the root file system (the one containing the ++ directory /) cannot be compiled as a module. ++ ++ If unsure, say N. ++ ++config SQUASHFS ++ tristate "SquashFS 3.0 - Squashed file system support" ++ select ZLIB_INFLATE ++ help ++ Saying Y here includes support for SquashFS 3.0 (a Compressed Read-Only File ++ System). Squashfs is a highly compressed read-only filesystem for Linux. ++ It uses zlib compression to compress both files, inodes and directories. ++ Inodes in the system are very small and all blocks are packed to minimise ++ data overhead. Block sizes greater than 4K are supported up to a maximum of 64K. ++ SquashFS 3.0 supports 64 bit filesystems and files (larger than 4GB), full ++ uid/gid information, hard links and timestamps. ++ ++ Squashfs is intended for general read-only filesystem use, for archival ++ use (i.e. in cases where a .tar.gz file may be used), and in embedded ++ systems where low overhead is needed. Further information and filesystem tools ++ are available from http://squashfs.sourceforge.net. ++ ++ If you want to compile this as a module ( = code which can be ++ inserted in and removed from the running kernel whenever you want), ++ say M here and read . The module ++ will be called squashfs. Note that the root file system (the one ++ containing the directory /) cannot be compiled as a module. ++ ++ If unsure, say N. ++ ++config SQUASHFS_EMBEDDED ++ ++ bool "Additional options for memory-constrained systems" ++ depends on SQUASHFS ++ default n ++ help ++ Saying Y here allows you to specify cache sizes and how Squashfs ++ allocates memory. This is only intended for memory constrained ++ systems. ++ ++ If unsure, say N. ++ ++config SQUASHFS_FRAGMENT_CACHE_SIZE ++ int "Number of fragments cached" if SQUASHFS_EMBEDDED ++ depends on SQUASHFS ++ default "3" ++ help ++ By default SquashFS caches the last 3 fragments read from ++ the filesystem. Increasing this amount may mean SquashFS ++ has to re-read fragments less often from disk, at the expense ++ of extra system memory. Decreasing this amount will mean ++ SquashFS uses less memory at the expense of extra reads from disk. ++ ++ Note there must be at least one cached fragment. Anything ++ much more than three will probably not make much difference. ++ ++config SQUASHFS_VMALLOC ++ bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED ++ depends on SQUASHFS ++ default n ++ help ++ By default SquashFS uses kmalloc to obtain fragment cache memory. ++ Kmalloc memory is the standard kernel allocator, but it can fail ++ on memory constrained systems. Because of the way Vmalloc works, ++ Vmalloc can succeed when kmalloc fails. Specifying this option ++ will make SquashFS always use Vmalloc to allocate the ++ fragment cache memory. ++ ++ If unsure, say N. ++ ++config VXFS_FS ++ tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" ++ depends on BLOCK ++ help ++ FreeVxFS is a file system driver that support the VERITAS VxFS(TM) ++ file system format. VERITAS VxFS(TM) is the standard file system ++ of SCO UnixWare (and possibly others) and optionally available ++ for Sunsoft Solaris, HP-UX and many other operating systems. ++ Currently only readonly access is supported. ++ ++ NOTE: the file system type as used by mount(1), mount(2) and ++ fstab(5) is 'vxfs' as it describes the file system format, not ++ the actual driver. ++ ++ To compile this as a module, choose M here: the module will be ++ called freevxfs. If unsure, say N. ++ ++ ++config HPFS_FS ++ tristate "OS/2 HPFS file system support" ++ depends on BLOCK ++ help ++ OS/2 is IBM's operating system for PC's, the same as Warp, and HPFS ++ is the file system used for organizing files on OS/2 hard disk ++ partitions. Say Y if you want to be able to read files from and ++ write files to an OS/2 HPFS partition on your hard drive. OS/2 ++ floppies however are in regular MSDOS format, so you don't need this ++ option in order to be able to read them. Read ++ . ++ ++ To compile this file system support as a module, choose M here: the ++ module will be called hpfs. If unsure, say N. ++ ++ ++ ++config QNX4FS_FS ++ tristate "QNX4 file system support (read only)" ++ depends on BLOCK ++ help ++ This is the file system used by the real-time operating systems ++ QNX 4 and QNX 6 (the latter is also called QNX RTP). ++ Further information is available at . ++ Say Y if you intend to mount QNX hard disks or floppies. ++ Unless you say Y to "QNX4FS read-write support" below, you will ++ only be able to read these file systems. ++ ++ To compile this file system support as a module, choose M here: the ++ module will be called qnx4. ++ ++ If you don't know whether you need it, then you don't need it: ++ answer N. ++ ++config QNX4FS_RW ++ bool "QNX4FS write support (DANGEROUS)" ++ depends on QNX4FS_FS && EXPERIMENTAL && BROKEN ++ help ++ Say Y if you want to test write support for QNX4 file systems. ++ ++ It's currently broken, so for now: ++ answer N. ++ ++ ++ ++config SYSV_FS ++ tristate "System V/Xenix/V7/Coherent file system support" ++ depends on BLOCK ++ help ++ SCO, Xenix and Coherent are commercial Unix systems for Intel ++ machines, and Version 7 was used on the DEC PDP-11. Saying Y ++ here would allow you to read from their floppies and hard disk ++ partitions. ++ ++ If you have floppies or hard disk partitions like that, it is likely ++ that they contain binaries from those other Unix systems; in order ++ to run these binaries, you will want to install linux-abi which is ++ a set of kernel modules that lets you run SCO, Xenix, Wyse, ++ UnixWare, Dell Unix and System V programs under Linux. It is ++ available via FTP (user: ftp) from ++ ). ++ NOTE: that will work only for binaries from Intel-based systems; ++ PDP ones will have to wait until somebody ports Linux to -11 ;-) ++ ++ If you only intend to mount files from some other Unix over the ++ network using NFS, you don't need the System V file system support ++ (but you need NFS file system support obviously). ++ ++ Note that this option is generally not needed for floppies, since a ++ good portable way to transport files and directories between unixes ++ (and even other operating systems) is given by the tar program ("man ++ tar" or preferably "info tar"). Note also that this option has ++ nothing whatsoever to do with the option "System V IPC". Read about ++ the System V file system in ++ . ++ Saying Y here will enlarge your kernel by about 27 KB. ++ ++ To compile this as a module, choose M here: the module will be called ++ sysv. ++ ++ If you haven't heard about all of this before, it's safe to say N. ++ ++ ++ ++config UFS_FS ++ tristate "UFS file system support (read only)" ++ depends on BLOCK ++ help ++ BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD, ++ OpenBSD and NeXTstep) use a file system called UFS. Some System V ++ Unixes can create and mount hard disk partitions and diskettes using ++ this file system as well. Saying Y here will allow you to read from ++ these partitions; if you also want to write to them, say Y to the ++ experimental "UFS file system write support", below. Please read the ++ file for more information. ++ ++ The recently released UFS2 variant (used in FreeBSD 5.x) is ++ READ-ONLY supported. ++ ++ If you only intend to mount files from some other Unix over the ++ network using NFS, you don't need the UFS file system support (but ++ you need NFS file system support obviously). ++ ++ Note that this option is generally not needed for floppies, since a ++ good portable way to transport files and directories between unixes ++ (and even other operating systems) is given by the tar program ("man ++ tar" or preferably "info tar"). ++ ++ When accessing NeXTstep files, you may need to convert them from the ++ NeXT character set to the Latin1 character set; use the program ++ recode ("info recode") for this purpose. ++ ++ To compile the UFS file system support as a module, choose M here: the ++ module will be called ufs. ++ ++ If you haven't heard about all of this before, it's safe to say N. ++ ++config UFS_FS_WRITE ++ bool "UFS file system write support (DANGEROUS)" ++ depends on UFS_FS && EXPERIMENTAL ++ help ++ Say Y here if you want to try writing to UFS partitions. This is ++ experimental, so you should back up your UFS partitions beforehand. ++ ++config UFS_DEBUG ++ bool "UFS debugging" ++ depends on UFS_FS ++ help ++ If you are experiencing any problems with the UFS filesystem, say ++ Y here. This will result in _many_ additional debugging messages to be ++ written to the system log. ++ ++endmenu ++ ++menuconfig NETWORK_FILESYSTEMS ++ bool "Network File Systems" ++ default y ++ depends on NET ++ ---help--- ++ Say Y here to get to see options for network filesystems and ++ filesystem-related networking code, such as NFS daemon and ++ RPCSEC security modules. ++ This option alone does not add any kernel code. ++ ++ If you say N, all options in this submenu will be skipped and ++ disabled; if unsure, say Y here. ++ ++if NETWORK_FILESYSTEMS ++ ++config NFS_FS ++ tristate "NFS file system support" ++ depends on INET ++ select LOCKD ++ select SUNRPC ++ select NFS_ACL_SUPPORT if NFS_V3_ACL ++ help ++ If you are connected to some other (usually local) Unix computer ++ (using SLIP, PLIP, PPP or Ethernet) and want to mount files residing ++ on that computer (the NFS server) using the Network File Sharing ++ protocol, say Y. "Mounting files" means that the client can access ++ the files with usual UNIX commands as if they were sitting on the ++ client's hard disk. For this to work, the server must run the ++ programs nfsd and mountd (but does not need to have NFS file system ++ support enabled in its kernel). NFS is explained in the Network ++ Administrator's Guide, available from ++ , on its man page: "man ++ nfs", and in the NFS-HOWTO. ++ ++ A superior but less widely used alternative to NFS is provided by ++ the Coda file system; see "Coda file system support" below. ++ ++ If you say Y here, you should have said Y to TCP/IP networking also. ++ This option would enlarge your kernel by about 27 KB. ++ ++ To compile this file system support as a module, choose M here: the ++ module will be called nfs. ++ ++ If you are configuring a diskless machine which will mount its root ++ file system over NFS at boot time, say Y here and to "Kernel ++ level IP autoconfiguration" above and to "Root file system on NFS" ++ below. You cannot compile this driver as a module in this case. ++ There are two packages designed for booting diskless machines over ++ the net: netboot, available from ++ , and Etherboot, ++ available from . ++ ++ If you don't know what all this is about, say N. ++ ++config NFS_V3 ++ bool "Provide NFSv3 client support" ++ depends on NFS_FS ++ help ++ Say Y here if you want your NFS client to be able to speak version ++ 3 of the NFS protocol. ++ ++ If unsure, say Y. ++ ++config NFS_V3_ACL ++ bool "Provide client support for the NFSv3 ACL protocol extension" ++ depends on NFS_V3 ++ help ++ Implement the NFSv3 ACL protocol extension for manipulating POSIX ++ Access Control Lists. The server should also be compiled with ++ the NFSv3 ACL protocol extension; see the CONFIG_NFSD_V3_ACL option. ++ ++ If unsure, say N. ++ ++config NFS_V4 ++ bool "Provide NFSv4 client support (EXPERIMENTAL)" ++ depends on NFS_FS && EXPERIMENTAL ++ select RPCSEC_GSS_KRB5 ++ help ++ Say Y here if you want your NFS client to be able to speak the newer ++ version 4 of the NFS protocol. ++ ++ Note: Requires auxiliary userspace daemons which may be found on ++ http://www.citi.umich.edu/projects/nfsv4/ ++ ++ If unsure, say N. ++ ++config NFS_DIRECTIO ++ bool "Allow direct I/O on NFS files" ++ depends on NFS_FS ++ help ++ This option enables applications to perform uncached I/O on files ++ in NFS file systems using the O_DIRECT open() flag. When O_DIRECT ++ is set for a file, its data is not cached in the system's page ++ cache. Data is moved to and from user-level application buffers ++ directly. Unlike local disk-based file systems, NFS O_DIRECT has ++ no alignment restrictions. ++ ++ Unless your program is designed to use O_DIRECT properly, you are ++ much better off allowing the NFS client to manage data caching for ++ you. Misusing O_DIRECT can cause poor server performance or network ++ storms. This kernel build option defaults OFF to avoid exposing ++ system administrators unwittingly to a potentially hazardous ++ feature. ++ ++ For more details on NFS O_DIRECT, see fs/nfs/direct.c. ++ ++ If unsure, say N. This reduces the size of the NFS client, and ++ causes open() to return EINVAL if a file residing in NFS is ++ opened with the O_DIRECT flag. ++ ++config NFSD ++ tristate "NFS server support" ++ depends on INET ++ select LOCKD ++ select SUNRPC ++ select EXPORTFS ++ select NFSD_V2_ACL if NFSD_V3_ACL ++ select NFS_ACL_SUPPORT if NFSD_V2_ACL ++ select NFSD_TCP if NFSD_V4 ++ select CRYPTO_MD5 if NFSD_V4 ++ select CRYPTO if NFSD_V4 ++ select FS_POSIX_ACL if NFSD_V4 ++ help ++ If you want your Linux box to act as an NFS *server*, so that other ++ computers on your local network which support NFS can access certain ++ directories on your box transparently, you have two options: you can ++ use the self-contained user space program nfsd, in which case you ++ should say N here, or you can say Y and use the kernel based NFS ++ server. The advantage of the kernel based solution is that it is ++ faster. ++ ++ In either case, you will need support software; the respective ++ locations are given in the file in the ++ NFS section. ++ ++ If you say Y here, you will get support for version 2 of the NFS ++ protocol (NFSv2). If you also want NFSv3, say Y to the next question ++ as well. ++ ++ Please read the NFS-HOWTO, available from ++ . ++ ++ To compile the NFS server support as a module, choose M here: the ++ module will be called nfsd. If unsure, say N. ++ ++config NFSD_V2_ACL ++ bool ++ depends on NFSD ++ ++config NFSD_V3 ++ bool "Provide NFSv3 server support" ++ depends on NFSD ++ help ++ If you would like to include the NFSv3 server as well as the NFSv2 ++ server, say Y here. If unsure, say Y. ++ ++config NFSD_V3_ACL ++ bool "Provide server support for the NFSv3 ACL protocol extension" ++ depends on NFSD_V3 ++ help ++ Implement the NFSv3 ACL protocol extension for manipulating POSIX ++ Access Control Lists on exported file systems. NFS clients should ++ be compiled with the NFSv3 ACL protocol extension; see the ++ CONFIG_NFS_V3_ACL option. If unsure, say N. ++ ++config NFSD_V4 ++ bool "Provide NFSv4 server support (EXPERIMENTAL)" ++ depends on NFSD && NFSD_V3 && EXPERIMENTAL ++ select RPCSEC_GSS_KRB5 ++ help ++ If you would like to include the NFSv4 server as well as the NFSv2 ++ and NFSv3 servers, say Y here. This feature is experimental, and ++ should only be used if you are interested in helping to test NFSv4. ++ If unsure, say N. ++ ++config NFSD_TCP ++ bool "Provide NFS server over TCP support" ++ depends on NFSD ++ default y ++ help ++ If you want your NFS server to support TCP connections, say Y here. ++ TCP connections usually perform better than the default UDP when ++ the network is lossy or congested. If unsure, say Y. ++ ++config ROOT_NFS ++ bool "Root file system on NFS" ++ depends on NFS_FS=y && IP_PNP ++ help ++ If you want your Linux box to mount its whole root file system (the ++ one containing the directory /) from some other computer over the ++ net via NFS (presumably because your box doesn't have a hard disk), ++ say Y. Read for details. It is ++ likely that in this case, you also want to say Y to "Kernel level IP ++ autoconfiguration" so that your box can discover its network address ++ at boot time. ++ ++ Most people say N here. ++ ++config LOCKD ++ tristate ++ ++config LOCKD_V4 ++ bool ++ depends on NFSD_V3 || NFS_V3 ++ default y ++ ++config EXPORTFS ++ tristate ++ ++config NFS_ACL_SUPPORT ++ tristate ++ select FS_POSIX_ACL ++ ++config NFS_COMMON ++ bool ++ depends on NFSD || NFS_FS ++ default y ++ ++config SUNRPC ++ tristate ++ ++config SUNRPC_GSS ++ tristate ++ ++config SUNRPC_XPRT_RDMA ++ tristate "RDMA transport for sunrpc (EXPERIMENTAL)" ++ depends on SUNRPC && INFINIBAND && EXPERIMENTAL ++ default m ++ help ++ Adds a client RPC transport for supporting kernel NFS over RDMA ++ mounts, including Infiniband and iWARP. Experimental. ++ ++config SUNRPC_BIND34 ++ bool "Support for rpcbind versions 3 & 4 (EXPERIMENTAL)" ++ depends on SUNRPC && EXPERIMENTAL ++ help ++ Provides kernel support for querying rpcbind servers via versions 3 ++ and 4 of the rpcbind protocol. The kernel automatically falls back ++ to version 2 if a remote rpcbind service does not support versions ++ 3 or 4. ++ ++ If unsure, say N to get traditional behavior (version 2 rpcbind ++ requests only). ++ ++config RPCSEC_GSS_KRB5 ++ tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)" ++ depends on SUNRPC && EXPERIMENTAL ++ select SUNRPC_GSS ++ select CRYPTO ++ select CRYPTO_MD5 ++ select CRYPTO_DES ++ select CRYPTO_CBC ++ help ++ Provides for secure RPC calls by means of a gss-api ++ mechanism based on Kerberos V5. This is required for ++ NFSv4. ++ ++ Note: Requires an auxiliary userspace daemon which may be found on ++ http://www.citi.umich.edu/projects/nfsv4/ ++ ++ If unsure, say N. ++ ++config RPCSEC_GSS_SPKM3 ++ tristate "Secure RPC: SPKM3 mechanism (EXPERIMENTAL)" ++ depends on SUNRPC && EXPERIMENTAL ++ select SUNRPC_GSS ++ select CRYPTO ++ select CRYPTO_MD5 ++ select CRYPTO_DES ++ select CRYPTO_CAST5 ++ select CRYPTO_CBC ++ help ++ Provides for secure RPC calls by means of a gss-api ++ mechanism based on the SPKM3 public-key mechanism. ++ ++ Note: Requires an auxiliary userspace daemon which may be found on ++ http://www.citi.umich.edu/projects/nfsv4/ ++ ++ If unsure, say N. ++ ++config SMB_FS ++ tristate "SMB file system support (to mount Windows shares etc.)" ++ depends on INET ++ select NLS ++ help ++ SMB (Server Message Block) is the protocol Windows for Workgroups ++ (WfW), Windows 95/98, Windows NT and OS/2 Lan Manager use to share ++ files and printers over local networks. Saying Y here allows you to ++ mount their file systems (often called "shares" in this context) and ++ access them just like any other Unix directory. Currently, this ++ works only if the Windows machines use TCP/IP as the underlying ++ transport protocol, and not NetBEUI. For details, read ++ and the SMB-HOWTO, ++ available from . ++ ++ Note: if you just want your box to act as an SMB *server* and make ++ files and printing services available to Windows clients (which need ++ to have a TCP/IP stack), you don't need to say Y here; you can use ++ the program SAMBA (available from ) ++ for that. ++ ++ General information about how to connect Linux, Windows machines and ++ Macs is on the WWW at . ++ ++ To compile the SMB support as a module, choose M here: the module will ++ be called smbfs. Most people say N, however. ++ ++config SMB_NLS_DEFAULT ++ bool "Use a default NLS" ++ depends on SMB_FS ++ help ++ Enabling this will make smbfs use nls translations by default. You ++ need to specify the local charset (CONFIG_NLS_DEFAULT) in the nls ++ settings and you need to give the default nls for the SMB server as ++ CONFIG_SMB_NLS_REMOTE. ++ ++ The nls settings can be changed at mount time, if your smbmount ++ supports that, using the codepage and iocharset parameters. ++ ++ smbmount from samba 2.2.0 or later supports this. ++ ++config SMB_NLS_REMOTE ++ string "Default Remote NLS Option" ++ depends on SMB_NLS_DEFAULT ++ default "cp437" ++ help ++ This setting allows you to specify a default value for which ++ codepage the server uses. If this field is left blank no ++ translations will be done by default. The local codepage/charset ++ default to CONFIG_NLS_DEFAULT. ++ ++ The nls settings can be changed at mount time, if your smbmount ++ supports that, using the codepage and iocharset parameters. ++ ++ smbmount from samba 2.2.0 or later supports this. ++ ++config CIFS ++ tristate "CIFS support (advanced network filesystem for Samba, Window and other CIFS compliant servers)" ++ depends on INET ++ select NLS ++ help ++ This is the client VFS module for the Common Internet File System ++ (CIFS) protocol which is the successor to the Server Message Block ++ (SMB) protocol, the native file sharing mechanism for most early ++ PC operating systems. The CIFS protocol is fully supported by ++ file servers such as Windows 2000 (including Windows 2003, NT 4 ++ and Windows XP) as well by Samba (which provides excellent CIFS ++ server support for Linux and many other operating systems). Limited ++ support for OS/2 and Windows ME and similar servers is provided as well. ++ ++ The intent of the cifs module is to provide an advanced ++ network file system client for mounting to CIFS compliant servers, ++ including support for dfs (hierarchical name space), secure per-user ++ session establishment, safe distributed caching (oplock), optional ++ packet signing, Unicode and other internationalization improvements. ++ If you need to mount to Samba or Windows from this machine, say Y. ++ ++config CIFS_STATS ++ bool "CIFS statistics" ++ depends on CIFS ++ help ++ Enabling this option will cause statistics for each server share ++ mounted by the cifs client to be displayed in /proc/fs/cifs/Stats ++ ++config CIFS_STATS2 ++ bool "Extended statistics" ++ depends on CIFS_STATS ++ help ++ Enabling this option will allow more detailed statistics on SMB ++ request timing to be displayed in /proc/fs/cifs/DebugData and also ++ allow optional logging of slow responses to dmesg (depending on the ++ value of /proc/fs/cifs/cifsFYI, see fs/cifs/README for more details). ++ These additional statistics may have a minor effect on performance ++ and memory utilization. ++ ++ Unless you are a developer or are doing network performance analysis ++ or tuning, say N. ++ ++config CIFS_WEAK_PW_HASH ++ bool "Support legacy servers which use weaker LANMAN security" ++ depends on CIFS ++ help ++ Modern CIFS servers including Samba and most Windows versions ++ (since 1997) support stronger NTLM (and even NTLMv2 and Kerberos) ++ security mechanisms. These hash the password more securely ++ than the mechanisms used in the older LANMAN version of the ++ SMB protocol needed to establish sessions with old SMB servers. ++ ++ Enabling this option allows the cifs module to mount to older ++ LANMAN based servers such as OS/2 and Windows 95, but such ++ mounts may be less secure than mounts using NTLM or more recent ++ security mechanisms if you are on a public network. Unless you ++ have a need to access old SMB servers (and are on a private ++ network) you probably want to say N. Even if this support ++ is enabled in the kernel build, they will not be used ++ automatically. At runtime LANMAN mounts are disabled but ++ can be set to required (or optional) either in ++ /proc/fs/cifs (see fs/cifs/README for more detail) or via an ++ option on the mount command. This support is disabled by ++ default in order to reduce the possibility of a downgrade ++ attack. ++ ++ If unsure, say N. ++ ++config CIFS_XATTR ++ bool "CIFS extended attributes" ++ depends on CIFS ++ help ++ Extended attributes are name:value pairs associated with inodes by ++ the kernel or by users (see the attr(5) manual page, or visit ++ for details). CIFS maps the name of ++ extended attributes beginning with the user namespace prefix ++ to SMB/CIFS EAs. EAs are stored on Windows servers without the ++ user namespace prefix, but their names are seen by Linux cifs clients ++ prefaced by the user namespace prefix. The system namespace ++ (used by some filesystems to store ACLs) is not supported at ++ this time. ++ ++ If unsure, say N. ++ ++config CIFS_POSIX ++ bool "CIFS POSIX Extensions" ++ depends on CIFS_XATTR ++ help ++ Enabling this option will cause the cifs client to attempt to ++ negotiate a newer dialect with servers, such as Samba 3.0.5 ++ or later, that optionally can handle more POSIX like (rather ++ than Windows like) file behavior. It also enables ++ support for POSIX ACLs (getfacl and setfacl) to servers ++ (such as Samba 3.10 and later) which can negotiate ++ CIFS POSIX ACL support. If unsure, say N. ++ ++config CIFS_DEBUG2 ++ bool "Enable additional CIFS debugging routines" ++ depends on CIFS ++ help ++ Enabling this option adds a few more debugging routines ++ to the cifs code which slightly increases the size of ++ the cifs module and can cause additional logging of debug ++ messages in some error paths, slowing performance. This ++ option can be turned off unless you are debugging ++ cifs problems. If unsure, say N. ++ ++config CIFS_EXPERIMENTAL ++ bool "CIFS Experimental Features (EXPERIMENTAL)" ++ depends on CIFS && EXPERIMENTAL ++ help ++ Enables cifs features under testing. These features are ++ experimental and currently include DFS support and directory ++ change notification ie fcntl(F_DNOTIFY), as well as the upcall ++ mechanism which will be used for Kerberos session negotiation ++ and uid remapping. Some of these features also may depend on ++ setting a value of 1 to the pseudo-file /proc/fs/cifs/Experimental ++ (which is disabled by default). See the file fs/cifs/README ++ for more details. If unsure, say N. ++ ++config CIFS_UPCALL ++ bool "Kerberos/SPNEGO advanced session setup (EXPERIMENTAL)" ++ depends on CIFS_EXPERIMENTAL ++ depends on KEYS ++ help ++ Enables an upcall mechanism for CIFS which will be used to contact ++ userspace helper utilities to provide SPNEGO packaged Kerberos ++ tickets which are needed to mount to certain secure servers ++ (for which more secure Kerberos authentication is required). If ++ unsure, say N. ++ ++config NCP_FS ++ tristate "NCP file system support (to mount NetWare volumes)" ++ depends on IPX!=n || INET ++ help ++ NCP (NetWare Core Protocol) is a protocol that runs over IPX and is ++ used by Novell NetWare clients to talk to file servers. It is to ++ IPX what NFS is to TCP/IP, if that helps. Saying Y here allows you ++ to mount NetWare file server volumes and to access them just like ++ any other Unix directory. For details, please read the file ++ in the kernel source and ++ the IPX-HOWTO from . ++ ++ You do not have to say Y here if you want your Linux box to act as a ++ file *server* for Novell NetWare clients. ++ ++ General information about how to connect Linux, Windows machines and ++ Macs is on the WWW at . ++ ++ To compile this as a module, choose M here: the module will be called ++ ncpfs. Say N unless you are connected to a Novell network. ++ ++source "fs/ncpfs/Kconfig" ++ ++config CODA_FS ++ tristate "Coda file system support (advanced network fs)" ++ depends on INET ++ help ++ Coda is an advanced network file system, similar to NFS in that it ++ enables you to mount file systems of a remote server and access them ++ with regular Unix commands as if they were sitting on your hard ++ disk. Coda has several advantages over NFS: support for ++ disconnected operation (e.g. for laptops), read/write server ++ replication, security model for authentication and encryption, ++ persistent client caches and write back caching. ++ ++ If you say Y here, your Linux box will be able to act as a Coda ++ *client*. You will need user level code as well, both for the ++ client and server. Servers are currently user level, i.e. they need ++ no kernel support. Please read ++ and check out the Coda ++ home page . ++ ++ To compile the coda client support as a module, choose M here: the ++ module will be called coda. ++ ++config CODA_FS_OLD_API ++ bool "Use 96-bit Coda file identifiers" ++ depends on CODA_FS ++ help ++ A new kernel-userspace API had to be introduced for Coda v6.0 ++ to support larger 128-bit file identifiers as needed by the ++ new realms implementation. ++ ++ However this new API is not backward compatible with older ++ clients. If you really need to run the old Coda userspace ++ cache manager then say Y. ++ ++ For most cases you probably want to say N. ++ ++config AFS_FS ++ tristate "Andrew File System support (AFS) (EXPERIMENTAL)" ++ depends on INET && EXPERIMENTAL ++ select AF_RXRPC ++ help ++ If you say Y here, you will get an experimental Andrew File System ++ driver. It currently only supports unsecured read-only AFS access. ++ ++ See for more information. ++ ++ If unsure, say N. ++ ++config AFS_DEBUG ++ bool "AFS dynamic debugging" ++ depends on AFS_FS ++ help ++ Say Y here to make runtime controllable debugging messages appear. ++ ++ See for more information. ++ ++ If unsure, say N. ++ ++config 9P_FS ++ tristate "Plan 9 Resource Sharing Support (9P2000) (Experimental)" ++ depends on INET && NET_9P && EXPERIMENTAL ++ help ++ If you say Y here, you will get experimental support for ++ Plan 9 resource sharing via the 9P2000 protocol. ++ ++ See for more information. ++ ++ If unsure, say N. ++ ++endif # NETWORK_FILESYSTEMS ++ ++if BLOCK ++menu "Partition Types" ++ ++source "fs/partitions/Kconfig" ++ ++endmenu ++endif ++ ++source "fs/nls/Kconfig" ++source "fs/dlm/Kconfig" ++ ++endmenu ++ +diff -urN linux-2.6.24.7.old/fs/fs-writeback.c linux-2.6.24.7/fs/fs-writeback.c +--- linux-2.6.24.7.old/fs/fs-writeback.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/fs/fs-writeback.c 2009-04-12 18:13:57.000000000 +0200 +@@ -386,8 +386,6 @@ + * WB_SYNC_HOLD is a hack for sys_sync(): reattach the inode to sb->s_dirty so + * that it can be located for waiting on in __writeback_single_inode(). + * +- * Called under inode_lock. +- * + * If `bdi' is non-zero then we're being asked to writeback a specific queue. + * This function assumes that the blockdev superblock's inodes are backed by + * a variety of queues, so all inodes are searched. For other superblocks, +@@ -403,11 +401,12 @@ + * on the writer throttling path, and we get decent balancing between many + * throttled threads: we don't want them all piling up on inode_sync_wait. + */ +-static void +-sync_sb_inodes(struct super_block *sb, struct writeback_control *wbc) ++void generic_sync_sb_inodes(struct super_block *sb, ++ struct writeback_control *wbc) + { + const unsigned long start = jiffies; /* livelock avoidance */ + ++ spin_lock(&inode_lock); + if (!wbc->for_kupdate || list_empty(&sb->s_io)) + queue_io(sb, wbc->older_than_this); + +@@ -482,8 +481,16 @@ + if (wbc->nr_to_write <= 0) + break; + } ++ spin_unlock(&inode_lock); + return; /* Leave any unwritten inodes on s_io */ + } ++EXPORT_SYMBOL_GPL(generic_sync_sb_inodes); ++ ++static void sync_sb_inodes(struct super_block *sb, ++ struct writeback_control *wbc) ++{ ++ generic_sync_sb_inodes(sb, wbc); ++} + + /* + * Start writeback of dirty pagecache data against all unlocked inodes. +@@ -524,11 +531,8 @@ + * be unmounted by the time it is released. + */ + if (down_read_trylock(&sb->s_umount)) { +- if (sb->s_root) { +- spin_lock(&inode_lock); ++ if (sb->s_root) + sync_sb_inodes(sb, wbc); +- spin_unlock(&inode_lock); +- } + up_read(&sb->s_umount); + } + spin_lock(&sb_lock); +@@ -566,9 +570,7 @@ + (inodes_stat.nr_inodes - inodes_stat.nr_unused) + + nr_dirty + nr_unstable; + wbc.nr_to_write += wbc.nr_to_write / 2; /* Bit more for luck */ +- spin_lock(&inode_lock); + sync_sb_inodes(sb, &wbc); +- spin_unlock(&inode_lock); + } + + /* +diff -urN linux-2.6.24.7.old/fs/ubifs/Kconfig linux-2.6.24.7/fs/ubifs/Kconfig +--- linux-2.6.24.7.old/fs/ubifs/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,71 @@ ++config UBIFS_FS ++ tristate "UBIFS file system support" ++ select CRC16 ++ select CRC32 ++ depends on MTD_UBI ++ help ++ UBIFS is a file system for flash devices which works on top of UBI. ++ ++config UBIFS_FS_XATTR ++ bool "Extended attributes support" ++ depends on UBIFS_FS ++ help ++ This option enables support of extended attributes. ++ ++config UBIFS_FS_ADVANCED_COMPR ++ bool "Advanced compression options" ++ depends on UBIFS_FS ++ help ++ This option allows to explicitly choose which compressions, if any, ++ are enabled in UBIFS. Removing compressors means inbility to read ++ existing file systems. ++ ++ If unsure, say 'N'. ++ ++config UBIFS_FS_LZO ++ bool "LZO compression support" if UBIFS_FS_ADVANCED_COMPR ++ select CRYPTO ++ select CRYPTO_LZO ++ depends on UBIFS_FS ++ default y ++ help ++ LZO compressor is generally faster then zlib but compresses worse. ++ Say 'Y' if unsure. ++ ++config UBIFS_FS_ZLIB ++ bool "ZLIB compression support" if UBIFS_FS_ADVANCED_COMPR ++ select CRYPTO ++ select CRYPTO_DEFLATE ++ depends on UBIFS_FS ++ default y ++ help ++ Zlib copresses better then LZO but it is slower. Say 'Y' if unsure. ++ ++# Debugging-related stuff ++config UBIFS_FS_DEBUG ++ bool "Enable debugging" ++ depends on UBIFS_FS ++ select DEBUG_FS ++ select KALLSYMS_ALL ++ help ++ This option enables UBIFS debugging. ++ ++config UBIFS_FS_DEBUG_MSG_LVL ++ int "Default message level (0 = no extra messages, 3 = lots)" ++ depends on UBIFS_FS_DEBUG ++ default "0" ++ help ++ This controls the amount of debugging messages produced by UBIFS. ++ If reporting bugs, please try to have available a full dump of the ++ messages at level 1 while the misbehaviour was occurring. Level 2 ++ may become necessary if level 1 messages were not enough to find the ++ bug. Generally Level 3 should be avoided. ++ ++config UBIFS_FS_DEBUG_CHKS ++ bool "Enable extra checks" ++ depends on UBIFS_FS_DEBUG ++ help ++ If extra checks are enabled UBIFS will check the consistency of its ++ internal data structures during operation. However, UBIFS performance ++ is dramatically slower when this option is selected especially if the ++ file system is large. +diff -urN linux-2.6.24.7.old/fs/ubifs/Kconfig.debug linux-2.6.24.7/fs/ubifs/Kconfig.debug +--- linux-2.6.24.7.old/fs/ubifs/Kconfig.debug 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/Kconfig.debug 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,173 @@ ++# UBIFS debugging configuration options, part of fs/ubifs/Kconfig ++ ++config UBIFS_FS_DEBUG ++ bool "Enable debugging" ++ default n ++ depends on UBIFS_FS ++ select DEBUG_FS ++ select KALLSYMS_ALL ++ help ++ This option enables UBIFS debugging. ++ ++menu "Debugging messages" ++ depends on UBIFS_FS_DEBUG ++ ++config UBIFS_FS_DEBUG_MSG_GEN ++ bool "General messages" ++ default n ++ help ++ This option enables general debugging messages. ++ ++config UBIFS_FS_DEBUG_MSG_JRN ++ bool "Journal messages" ++ default n ++ help ++ This option enables detailed journal debugging messages. ++ ++config UBIFS_FS_DEBUG_MSG_CMT ++ bool "Commit messages" ++ default n ++ help ++ This option enables detailed journal commit debugging messages. ++ ++config UBIFS_FS_DEBUG_MSG_BUDG ++ bool "Budgeting messages" ++ default n ++ help ++ This option enables detailed budgeting debugging messages. ++ ++config UBIFS_FS_DEBUG_MSG_LOG ++ bool "Log messages" ++ default n ++ help ++ This option enables detailed journal log debugging messages. ++ ++config UBIFS_FS_DEBUG_MSG_TNC ++ bool "Tree Node Cache (TNC) messages" ++ default n ++ help ++ This option enables detailed TNC debugging messages. ++ ++config UBIFS_FS_DEBUG_MSG_LP ++ bool "LEB properties (lprops) messages" ++ default n ++ help ++ This option enables detailed lprops debugging messages. ++ ++config UBIFS_FS_DEBUG_MSG_FIND ++ bool "LEB search messages" ++ default n ++ help ++ This option enables detailed LEB search debugging messages. ++ ++config UBIFS_FS_DEBUG_MSG_MNT ++ bool "Mount messages" ++ default n ++ help ++ This option enables detailed mount debugging messages, including ++ recovery messages. ++ ++config UBIFS_FS_DEBUG_MSG_IO ++ bool "Input/output messages" ++ default n ++ help ++ This option enables detailed I/O debugging messages. ++ ++config UBIFS_FS_DEBUG_MSG_GC ++ bool "Garbage collection messages" ++ default n ++ help ++ This option enables detailed garbage collection debugging messages. ++ ++config UBIFS_FS_DEBUG_MSG_SCAN ++ bool "Scan messages" ++ default n ++ help ++ This option enables detailed scan debugging messages. ++ ++endmenu ++ ++menu "Extra self-checks" ++ depends on UBIFS_FS_DEBUG ++ ++config UBIFS_FS_DEBUG_CHK_MEMPRESS ++ bool "Create memory pressure" ++ default n ++ depends on UBIFS_FS_DEBUG ++ help ++ This option causes kernel memory pressure in order to make TNC shrinker ++ run. ++ ++config UBIFS_FS_DEBUG_CHK_LPROPS ++ bool "Check LEB properties (lprops)" ++ default n ++ depends on UBIFS_FS_DEBUG ++ help ++ This option enables a function which runs during journal commit and ++ checks that the dirty and free space is correct for every LEB. Note, ++ this option makes UBIFS scan whole media before each commit which is ++ very slow. ++ ++config UBIFS_FS_DEBUG_CHK_TNC ++ bool "Check Tree Node Cache (TNC)" ++ default n ++ depends on UBIFS_FS_DEBUG ++ help ++ This option enables a function which runs after every ++ TNC insert / delete and checks that the TNC nodes are correct. ++ ++config UBIFS_FS_DEBUG_CHK_ORPH ++ bool "Check orphan area" ++ default n ++ depends on UBIFS_FS_DEBUG ++ help ++ This option enables a function which runs during journal commit and ++ checks that the orphan area is correct. ++ ++config UBIFS_FS_DEBUG_CHK_IDX_SZ ++ bool "Check indexing tree size" ++ default n ++ depends on UBIFS_FS_DEBUG ++ help ++ This option enables checking of the znode size accounting variables. ++ ++config UBIFS_FS_DEBUG_CHK_OLD_IDX ++ bool "Check old indexing tree" ++ default n ++ depends on UBIFS_FS_DEBUG ++ help ++ This option enables checking of the old indexing tree which must be ++ intact to allow recovery in the event of an unclean unmount. ++ ++config UBIFS_FS_DEBUG_CHK_OTHER ++ bool "Other checks" ++ default n ++ depends on UBIFS_FS_DEBUG ++ help ++ This option enables different checks which are light-weight and do not ++ affect file-system performance too much. ++ ++endmenu ++ ++config UBIFS_FS_DEBUG_FORCE_IN_THE_GAPS ++ bool "Force in-the-gaps commit method" ++ default n ++ depends on UBIFS_FS_DEBUG ++ help ++ This option makes UBIFS use the in-the-gap commit method much more ++ often than it is normally used (normally it is used only as fall-back ++ method when there is no space to do the "normal" commit method). It ++ is useful to run tests with this option enabled from time to time ++ because it may reveal UBIFS bugs which would otherwise be really ++ difficult to hit. ++ ++config UBIFS_FS_DEBUG_TEST_RCVRY ++ bool "Simulate random device removal (recovery testing)" ++ default n ++ depends on UBIFS_FS_DEBUG ++ help ++ This option provides the ability to test recovery from unclean ++ unmounts. It causes UBIFS to simulate device removal. At a some ++ random point UBIFS will switch to "failure mode" after which all I/O ++ operations will fail. UBIFS can then be unmounted and mounted again ++ at which point "failure mode" is switched off and recovery ensues. +diff -urN linux-2.6.24.7.old/fs/ubifs/Makefile linux-2.6.24.7/fs/ubifs/Makefile +--- linux-2.6.24.7.old/fs/ubifs/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,9 @@ ++obj-$(CONFIG_UBIFS_FS) += ubifs.o ++ ++ubifs-y += shrinker.o journal.o file.o dir.o super.o sb.o io.o ++ubifs-y += tnc.o master.o scan.o replay.o log.o commit.o gc.o orphan.o ++ubifs-y += budget.o find.o tnc_commit.o compress.o lpt.o lprops.o ++ubifs-y += recovery.o ioctl.o compat.o lpt_commit.o tnc_misc.o ++ ++ubifs-$(CONFIG_UBIFS_FS_DEBUG) += debug.o ++ubifs-$(CONFIG_UBIFS_FS_XATTR) += xattr.o +diff -urN linux-2.6.24.7.old/fs/ubifs/budget.c linux-2.6.24.7/fs/ubifs/budget.c +--- linux-2.6.24.7.old/fs/ubifs/budget.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/budget.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,871 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy (Битюцкий Ðртём) ++ */ ++ ++/* ++ * This file implements the budgeting unit which is responsible for UBIFS space ++ * management. ++ * ++ * Factors such as compression, wasted space at the ends of LEBs, space in other ++ * journal heads, the effect of updates on the index, and so on, make it ++ * impossible to accurately predict the amount of space needed. Consequently ++ * approximations are used. ++ */ ++ ++#include "ubifs.h" ++#include ++#include ++ ++/* ++ * When pessimistic budget calculations say that there is no enough space, ++ * UBIFS starts writing back dirty inodes and pages, doing garbage collection, ++ * or committing. The below constants define maximum number of times UBIFS ++ * repeats the operations. ++ */ ++#define MAX_SHRINK_RETRIES 8 ++#define MAX_GC_RETRIES 4 ++#define MAX_CMT_RETRIES 2 ++#define MAX_NOSPC_RETRIES 1 ++ ++/* ++ * The below constant defines amount of dirty pages which should be written ++ * back at when trying to shrink the liability. ++ */ ++#define NR_TO_WRITE 16 ++ ++/** ++ * struct retries_info - information about re-tries while making free space. ++ * @prev_liability: previous liability ++ * @shrink_cnt: how many times the liability was shrinked ++ * @shrink_retries: count of liability shrink re-tries (increased when ++ * liability does not shrink) ++ * @try_gc: GC should be tried first ++ * @gc_retries: how many times GC was run ++ * @cmt_retries: how many times commit has been done ++ * @nospc_retries: how many times GC returned %-ENOSPC ++ * ++ * Since we consider budgeting to be the fast-path, and this structure has to ++ * be allocated on stack and zeroed out, we make it smaller using bit-fields. ++ */ ++struct retries_info { ++ long long prev_liability; ++ unsigned int shrink_cnt; ++ unsigned int shrink_retries:5; ++ unsigned int try_gc:1; ++ unsigned int gc_retries:4; ++ unsigned int cmt_retries:3; ++ unsigned int nospc_retries:1; ++}; ++ ++/* TODO: remove compatibility stuff as late as possible */ ++#ifndef UBIFS_COMPAT_USE_OLD_PREPARE_WRITE ++/** ++ * shrink_liability - write-back some dirty pages/inodes. ++ * @c: UBIFS file-system description object ++ * @nr_to_write: how many dirty pages to write-back ++ * ++ * This function shrinks UBIFS liability by means of writing back some amount ++ * of dirty inodes and their pages. Returns the amount of pages which were ++ * written back. The returned value does not include dirty inodes which were ++ * synchronized. ++ * ++ * Note, this function synchronizes even VFS inodes which are locked ++ * (@i_mutex) by the caller of the budgeting function, because write-back does ++ * not touch @i_mutex. ++ */ ++static int shrink_liability(struct ubifs_info *c, int nr_to_write) ++{ ++ struct writeback_control wbc = { ++ .sync_mode = WB_SYNC_NONE, ++ .range_end = LLONG_MAX, ++ .nr_to_write = nr_to_write, ++ }; ++ ++ generic_sync_sb_inodes(c->vfs_sb, &wbc); ++ dbg_budg("%ld pages were written back", nr_to_write - wbc.nr_to_write); ++ return nr_to_write - wbc.nr_to_write; ++} ++ ++ ++/** ++ * run_gc - run garbage collector. ++ * @c: UBIFS file-system description object ++ * ++ * This function runs garbage collector to make some more free space. Returns ++ * zero if a free LEB has been produced, %-EAGAIN if commit is required, and a ++ * negative error code in case of failure. ++ */ ++static int run_gc(struct ubifs_info *c) ++{ ++ int err, lnum; ++ ++ /* Make some free space by garbage-collecting dirty space */ ++ down_read(&c->commit_sem); ++ lnum = ubifs_garbage_collect(c, 1); ++ up_read(&c->commit_sem); ++ if (lnum < 0) ++ return lnum; ++ ++ /* GC freed one LEB, return it to lprops */ ++ dbg_budg("GC freed LEB %d", lnum); ++ err = ubifs_return_leb(c, lnum); ++ if (err) ++ return err; ++ ++ return 0; ++} ++ ++/** ++ * make_free_space - make more free space on the file-system. ++ * @c: UBIFS file-system description object ++ * @ri: information about previous invocations of this function ++ * ++ * This function is called when an operation cannot be budgeted because there ++ * is supposedly no free space. But in most cases there is some free space: ++ * o budgeting is pessimistic, so it always budgets more then it is actually ++ * needed, so shrinking the liability is one way to make free space - the ++ * cached data will take less space then it was budgeted for; ++ * o GC may turn some dark space into free space (budgeting treats dark space ++ * as not available); ++ * o commit may free some LEB, i.e., turn freeable LEBs into free LEBs. ++ * ++ * So this function tries to do the above. Returns %-EAGAIN if some free space ++ * was presumably made and the caller has to re-try budgeting the operation. ++ * Returns %-ENOSPC if it couldn't do more free space, and other negative error ++ * codes on failures. ++ */ ++static int make_free_space(struct ubifs_info *c, struct retries_info *ri) ++{ ++ int err; ++ ++ /* ++ * If we have some dirty pages and inodes (liability), try to write ++ * them back unless this was tried too many times without effect ++ * already. ++ */ ++ if (ri->shrink_retries < MAX_SHRINK_RETRIES && !ri->try_gc) { ++ long long liability; ++ ++ spin_lock(&c->space_lock); ++ liability = c->budg_idx_growth + c->budg_data_growth + ++ c->budg_dd_growth; ++ spin_unlock(&c->space_lock); ++ ++ if (ri->prev_liability >= liability) { ++ /* Liability does not shrink, next time try GC then */ ++ ri->shrink_retries += 1; ++ if (ri->gc_retries < MAX_GC_RETRIES) ++ ri->try_gc = 1; ++ dbg_budg("liability did not shrink: retries %d of %d", ++ ri->shrink_retries, MAX_SHRINK_RETRIES); ++ } ++ ++ dbg_budg("force write-back (count %d)", ri->shrink_cnt); ++ shrink_liability(c, NR_TO_WRITE + ri->shrink_cnt); ++ ++ ri->prev_liability = liability; ++ ri->shrink_cnt += 1; ++ return -EAGAIN; ++ } ++ ++ /* ++ * Try to run garbage collector unless it was already tried too many ++ * times. ++ */ ++ if (ri->gc_retries < MAX_GC_RETRIES) { ++ ri->gc_retries += 1; ++ dbg_budg("run GC, retries %d of %d", ++ ri->gc_retries, MAX_GC_RETRIES); ++ ++ ri->try_gc = 0; ++ err = run_gc(c); ++ if (!err) ++ return -EAGAIN; ++ ++ if (err == -EAGAIN) { ++ dbg_budg("GC asked to commit"); ++ err = ubifs_run_commit(c); ++ if (err) ++ return err; ++ return -EAGAIN; ++ } ++ ++ if (err != -ENOSPC) ++ return err; ++ ++ /* ++ * GC could not make any progress. If this is the first time, ++ * then it makes sense to try to commit, because it might make ++ * some dirty space. ++ */ ++ dbg_budg("GC returned -ENOSPC, retries %d", ++ ri->nospc_retries); ++ if (ri->nospc_retries >= MAX_NOSPC_RETRIES) ++ return err; ++ ri->nospc_retries += 1; ++ } ++ ++ /* Neither GC nor write-back helped, try to commit */ ++ if (ri->cmt_retries < MAX_CMT_RETRIES) { ++ ri->cmt_retries += 1; ++ dbg_budg("run commit, retries %d of %d", ++ ri->cmt_retries, MAX_CMT_RETRIES); ++ err = ubifs_run_commit(c); ++ if (err) ++ return err; ++ return -EAGAIN; ++ } ++ ++ return -ENOSPC; ++} ++#endif /* UBIFS_COMPAT_USE_OLD_PREPARE_WRITE */ ++ ++/** ++ * ubifs_calc_min_idx_lebs - calculate amount of eraseblocks for the index. ++ * @c: UBIFS file-system description object ++ * ++ * This function calculates and returns the number of eraseblocks which should ++ * be kept for index usage. ++ */ ++int ubifs_calc_min_idx_lebs(struct ubifs_info *c) ++{ ++ int ret; ++ uint64_t idx_size; ++ ++ idx_size = c->old_idx_sz + c->budg_idx_growth + c->budg_uncommitted_idx; ++ ++ /* And make sure we have twice the index size of space reserved */ ++ idx_size <<= 1; ++ ++ /* ++ * We do not maintain 'old_idx_size' as 'old_idx_lebs'/'old_idx_bytes' ++ * pair, nor similarly the two variables for the new index size, so we ++ * have to do this costly 64-bit division on fast-path. ++ */ ++ if (do_div(idx_size, c->leb_size - c->max_idx_node_sz)) ++ ret = idx_size + 1; ++ else ++ ret = idx_size; ++ /* ++ * The index head is not available for the in-the-gaps method, so add an ++ * extra LEB to compensate. ++ */ ++ ret += 1; ++ /* ++ * At present the index needs at least 2 LEBs: one for the index head ++ * and one for in-the-gaps method (which currently does not cater for ++ * the index head and so excludes it from consideration). ++ */ ++ if (ret < 2) ++ ret = 2; ++ return ret; ++} ++ ++/** ++ * ubifs_calc_available - calculate available FS space. ++ * @c: UBIFS file-system description object ++ * ++ * This function calculates and returns amount of FS space available for use. ++ */ ++long long ubifs_calc_available(const struct ubifs_info *c) ++{ ++ long long available, subtract_lebs; ++ ++ /* ++ * Force the amount available to the total size reported if the used ++ * space is zero. ++ */ ++ if (c->lst.total_used <= UBIFS_INO_NODE_SZ && ++ c->budg_data_growth + c->budg_dd_growth == 0) { ++ /* Do the same calculation as for c->block_cnt */ ++ available = c->main_lebs - 2; ++ available *= c->leb_size - c->dark_wm; ++ return available; ++ } ++ ++ available = c->main_bytes - c->lst.total_used; ++ ++ /* ++ * Now 'available' contains theoretically available flash space ++ * assuming there is no index, so we have to subtract the space which ++ * is reserved for the index. ++ */ ++ subtract_lebs = c->min_idx_lebs; ++ ++ /* Take into account that GC reserves one LEB for its own needs */ ++ subtract_lebs += 1; ++ ++ /* ++ * The GC journal head LEB is not really accessible. And since ++ * different write types go to different heads, we may count only on ++ * one head's space. ++ */ ++ subtract_lebs += c->jhead_cnt - 1; ++ ++ /* We also reserve one LEB for deletions, which bypass budgeting */ ++ subtract_lebs += 1; ++ ++ available -= subtract_lebs * c->leb_size; ++ ++ /* Subtract the dead space which is not available for use */ ++ available -= c->lst.total_dead; ++ ++ /* ++ * Subtract dark space, which might or might not be usable - it depends ++ * on the data which we have on the media and which will be written. If ++ * this is a lot of uncompressed or not-compressible data, the dark ++ * space cannot be used. ++ */ ++ available -= c->lst.total_dark; ++ ++ /* ++ * However, there is more dark space. The index may be bigger than ++ * min_idx_lebs. Those extra LEBs are assumed to be available, but ++ * their dark space is not included in total_dark, so it is subtracted ++ * here. ++ */ ++ if (c->lst.idx_lebs > c->min_idx_lebs) { ++ subtract_lebs = c->lst.idx_lebs - c->min_idx_lebs; ++ available -= subtract_lebs * c->dark_wm; ++ } ++ ++ return available; ++} ++ ++/** ++ * rp_can_write - check whether the user is allowed to write. ++ * @c: UBIFS file-system description object ++ * @avail: available space on FS ++ * ++ * UBIFS has so-called "reserved pool" which is flash space reserved ++ * for the superuser and for uses whose UID/GID is recorded in UBIFS superblock. ++ * This function checks whether current user is allowed to write ++ * to the file-system - it returns %1 if there is plenty of space or the user ++ * is eligible to use the reserved pool and %0 otherwise. ++ */ ++static int rp_can_write(struct ubifs_info *c, long long avail) ++{ ++ if (avail > c->rp_size || current->fsuid == c->rp_uid || ++ capable(CAP_SYS_RESOURCE) || ++ (c->rp_gid != 0 && in_group_p(c->rp_gid))) ++ return 1; ++ ++ return 0; ++} ++ ++/** ++ * do_budget_space - reserve flash space for index and data growth. ++ * @c: UBIFS file-system description object ++ * ++ * This function makes sure UBIFS has enough free eraseblocks for index growth ++ * and data. ++ * ++ * When budgeting index space, UBIFS reserves twice as more LEBs as the index ++ * would take if it was consolidated and written to the flash. This guarantees ++ * that the "in-the-gaps" commit method always succeeds and UBIFS will always ++ * be able to commit dirty index. So this function basically adds amount of ++ * budgeted index space to the size of the current index, multiplies this by 2, ++ * and makes sure this does not exceed the amount of free eraseblocks. ++ * ++ * Notes about @c->min_idx_lebs and @c->lst.idx_lebs variables: ++ * o @c->lst.idx_lebs is the number of LEBs the index currently uses. It might ++ * be large, because UBIFS does not do any index consolidation as long as ++ * there is free space. IOW, the index may take a lot of LEBs, but the LEBs ++ * will contain a lot of dirt. ++ * o @c->min_idx_lebs is the the index presumably takes. IOW, the index may be ++ * consolidated to take up to @c->min_idx_lebs LEBs. ++ * ++ * This function returns zero in case of success, and %-ENOSPC in case of ++ * failure. ++ */ ++static int do_budget_space(struct ubifs_info *c) ++{ ++ long long outstanding, available; ++ int lebs, rsvd_idx_lebs, min_idx_lebs; ++ ++ /* First budget index space */ ++ min_idx_lebs = ubifs_calc_min_idx_lebs(c); ++ ++ /* Now 'min_idx_lebs' contains number of LEBs to reserve */ ++ if (min_idx_lebs > c->lst.idx_lebs) ++ rsvd_idx_lebs = min_idx_lebs - c->lst.idx_lebs; ++ else ++ rsvd_idx_lebs = 0; ++ ++ /* ++ * The number of LEBs that are available to be used by the index is: ++ * ++ * @c->lst.empty_lebs + @c->freeable_cnt + @c->idx_gc_cnt - ++ * @c->lst.taken_empty_lebs ++ * ++ * @empty_lebs are available because they are empty. @freeable_cnt are ++ * available because they contain only free and dirty space and the ++ * index allocation always occurs after wbufs are synch'ed. ++ * @idx_gc_cnt are available because they are index LEBs that have been ++ * garbage collected (including trivial GC) and are awaiting the commit ++ * before they can be unmapped - note that the in-the-gaps method will ++ * grab these if it needs them. @taken_empty_lebs are empty_lebs that ++ * have already been allocated for some purpose (also includes those ++ * LEBs on the @idx_gc list). ++ * ++ * Note, @taken_empty_lebs may temporarily be higher by one because of ++ * the way we serialize LEB allocations and budgeting. See a comment in ++ * 'ubifs_find_free_space()'. ++ */ ++ lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt - ++ c->lst.taken_empty_lebs; ++ if (unlikely(rsvd_idx_lebs > lebs)) { ++ dbg_budg("out of indexing space: min_idx_lebs %d (old %d), " ++ "rsvd_idx_lebs %d", min_idx_lebs, c->min_idx_lebs, ++ rsvd_idx_lebs); ++ return -ENOSPC; ++ } ++ ++ available = ubifs_calc_available(c); ++ outstanding = c->budg_data_growth + c->budg_dd_growth; ++ ++ if (unlikely(available < outstanding)) { ++ dbg_budg("out of data space: available %lld, outstanding %lld", ++ available, outstanding); ++ return -ENOSPC; ++ } ++ ++ if (!rp_can_write(c, available - outstanding)) ++ return -ENOSPC; ++ ++ c->min_idx_lebs = min_idx_lebs; ++ return 0; ++} ++ ++/** ++ * calc_idx_growth - calculate approximate index growth from budgeting request. ++ * @c: UBIFS file-system description object ++ * @req: budgeting request ++ * ++ * For now we assume each new node adds one znode. But this is rather poor ++ * approximation, though. ++ */ ++static int calc_idx_growth(const struct ubifs_info *c, ++ const struct ubifs_budget_req *req) ++{ ++ int znodes; ++ ++ znodes = req->new_ino + (req->new_page << UBIFS_BLOCKS_PER_PAGE_SHIFT) + ++ req->new_dent; ++ return znodes * c->max_idx_node_sz; ++} ++ ++/** ++ * calc_data_growth - calculate approximate amount of new data from budgeting ++ * request. ++ * @c: UBIFS file-system description object ++ * @req: budgeting request ++ */ ++static int calc_data_growth(const struct ubifs_info *c, ++ const struct ubifs_budget_req *req) ++{ ++ int data_growth; ++ ++ data_growth = req->new_ino ? c->inode_budget : 0; ++ if (req->new_page) ++ data_growth += c->page_budget; ++ if (req->new_dent) ++ data_growth += c->dent_budget; ++ data_growth += req->new_ino_d; ++ ++ return data_growth; ++} ++ ++/** ++ * calc_dd_growth - calculate approximate amount of data which makes other data ++ * dirty from budgeting request. ++ * @c: UBIFS file-system description object ++ * @req: budgeting request ++ */ ++static int calc_dd_growth(const struct ubifs_info *c, ++ const struct ubifs_budget_req *req) ++{ ++ int dd_growth; ++ ++ dd_growth = req->dirtied_page ? c->page_budget : 0; ++ ++ if (req->dirtied_ino) ++ dd_growth += c->inode_budget << (req->dirtied_ino - 1); ++ if (req->mod_dent) ++ dd_growth += c->dent_budget; ++ dd_growth += req->dirtied_ino_d; ++ ++ return dd_growth; ++} ++ ++/** ++ * ubifs_budget_space - ensure there is enough space to complete an operation. ++ * @c: UBIFS file-system description object ++ * @req: budget request ++ * ++ * This function allocates budget for an operation. It uses pessimistic ++ * approximation of how much flash space the operation needs. The goal of this ++ * function is to make sure UBIFS always has flash space to flush all dirty ++ * pages, dirty inodes, and dirty znodes (liability). This function may force ++ * commit, garbage-collection or write-back. Returns zero in case of success, ++ * %-ENOSPC if there is no free space and other negative error codes in case of ++ * failures. ++ */ ++int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req) ++{ ++ int uninitialized_var(cmt_retries), uninitialized_var(wb_retries); ++ int err, idx_growth, data_growth, dd_growth; ++ struct retries_info ri; ++ ++ data_growth = calc_data_growth(c, req); ++ dd_growth = calc_dd_growth(c, req); ++ if (!data_growth && !dd_growth) ++ return 0; ++ idx_growth = calc_idx_growth(c, req); ++ memset(&ri, 0, sizeof(struct retries_info)); ++ ++again: ++ spin_lock(&c->space_lock); ++ ubifs_assert(c->budg_idx_growth >= 0); ++ ubifs_assert(c->budg_data_growth >= 0); ++ ubifs_assert(c->budg_dd_growth >= 0); ++ ++ c->budg_idx_growth += idx_growth; ++ c->budg_data_growth += data_growth; ++ c->budg_dd_growth += dd_growth; ++ ++ err = do_budget_space(c); ++ if (unlikely(err)) { ++ /* Restore the old values */ ++ c->budg_idx_growth -= idx_growth; ++ c->budg_data_growth -= data_growth; ++ c->budg_dd_growth -= dd_growth; ++ spin_unlock(&c->space_lock); ++ ++ goto make_space; ++ } ++ ++ req->idx_growth = idx_growth; ++ req->data_growth = data_growth; ++ req->dd_growth = dd_growth; ++ spin_unlock(&c->space_lock); ++ ++ return 0; ++ ++make_space: ++/* TODO: remove compatibility stuff as late as possible */ ++#ifdef UBIFS_COMPAT_USE_OLD_PREPARE_WRITE ++ err = ubifs_make_free_space(c, &ri, req->locked_pg); ++#else ++ err = make_free_space(c, &ri); ++#endif ++ ++ if (err == -EAGAIN) { ++ dbg_budg("try again"); ++ cond_resched(); ++ goto again; ++ } else if (err == -ENOSPC) ++ dbg_budg("FS is full, -ENOSPC"); ++ else ++ ubifs_err("cannot budget space, error %d", err); ++ ++ return err; ++} ++ ++/** ++ * ubifs_release_budget - release budgeted free space. ++ * @c: UBIFS file-system description object ++ * @req: budget request ++ * ++ * This function releases the space budgeted by 'ubifs_budget_space()'. Note, ++ * since the index changes (which were budgeted for in @req->idx_growth) will ++ * only be written to the media on commit, this function moves the index budget ++ * from @c->budg_idx_growth to @c->budg_uncommitted_idx. The latter will be ++ * zeroed by the commit operation. ++ */ ++void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req) ++{ ++ if (!req->data_growth && !req->dd_growth) ++ return; ++ ++ if (req->idx_growth == -1) ++ req->idx_growth = calc_idx_growth(c, req); ++ ++ spin_lock(&c->space_lock); ++ c->budg_idx_growth -= req->idx_growth; ++ c->budg_uncommitted_idx += req->idx_growth; ++ c->budg_data_growth -= req->data_growth; ++ c->budg_dd_growth -= req->dd_growth; ++ c->min_idx_lebs = ubifs_calc_min_idx_lebs(c); ++ ++ ubifs_assert(c->budg_idx_growth >= 0); ++ ubifs_assert(c->budg_data_growth >= 0); ++ ubifs_assert(c->min_idx_lebs < c->main_lebs); ++ spin_unlock(&c->space_lock); ++} ++ ++/** ++ * ubifs_convert_page_budget - convert budget of a new page. ++ * @c: UBIFS file-system description object ++ * ++ * This function converts budget which was allocated for a new page of data to ++ * the budget of changing an existing page of data. The latter is not larger ++ * then the former, so this function only does simple re-calculation and does ++ * not involve any write-back. ++ */ ++void ubifs_convert_page_budget(struct ubifs_info *c) ++{ ++ spin_lock(&c->space_lock); ++ /* Release the index growth reservation */ ++ c->budg_idx_growth -= c->max_idx_node_sz << UBIFS_BLOCKS_PER_PAGE_SHIFT; ++ /* Release the data growth reservation */ ++ c->budg_data_growth -= c->page_budget; ++ /* Increase the dirty data growth reservation instead */ ++ c->budg_dd_growth += c->page_budget; ++ /* And re-calculate the indexing space reservation */ ++ c->min_idx_lebs = ubifs_calc_min_idx_lebs(c); ++ spin_unlock(&c->space_lock); ++} ++ ++/** ++ * ubifs_budget_inode_op - budget an operation on inode. ++ * @c: UBIFS file-system description object ++ * @inode: VFS inode which will be made dirty by the operation ++ * @req: budget request of the operation ++ * ++ * This function is called to get budget for an operation which changes an ++ * inode. The inode may be in dirty or clean state. The former means there is ++ * no need to allocate the budget as it has already been allocated before. The ++ * latter means that the inode change budget has to be allocated. ++ * ++ * The caller has to pass the inode which is going to be changed. This function ++ * acquires budget the for as described in @req plus the budget for changing ++ * the inode dirty, if needed. Returns zero in case of success, %-ENOSPC if ++ * there is no more flash space, and other negative error codes in case of ++ * failure. ++ * ++ * Note, upon exit, this function leaves the inode locked, and the ++ * 'ubifs_release_ino_dirty()' or 'ubifs_release_ino_clean()' function has to ++ * be called to unlock it. ++ */ ++int ubifs_budget_inode_op(struct ubifs_info *c, struct inode *inode, ++ struct ubifs_budget_req *req) ++{ ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ int err, old = req->dirtied_ino; ++ ++ ubifs_assert(req->dirtied_ino <= 3); ++ ubifs_assert(req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 3); ++ ++again: ++ /* ++ * If the inode is clean, it will be dirtied by this operation and we ++ * have to budget for this. ++ */ ++ req->dirtied_ino += !ui->dirty; ++ if (req->dirtied_ino > old) ++ req->dirtied_ino_d += ui->data_len; ++ ++ /* ++ * Note, if the budget request does not actually request anything ++ * (i.e., @req contains only zeroes), 'ubifs_budget_space()' will ++ * return almost straight away. ++ */ ++ err = ubifs_budget_space(c, req); ++ if (unlikely(err)) ++ return err; ++ ++ mutex_lock(&ui->budg_mutex); ++ ++ if (req->dirtied_ino != old + !ui->dirty) { ++ /* The inode has probably been written back meanwhile */ ++ ubifs_release_budget(c, req); ++ mutex_unlock(&ui->budg_mutex); ++ req->dirtied_ino = old; ++ req->dirtied_ino_d -= ui->data_len; ++ goto again; ++ } ++ ++ UBIFS_DBG(ui->budgeted = 1); ++ return 0; ++} ++ ++/** ++ * ubifs_release_ino_dirty - release budget of a "dirtying" operation. ++ * @c: UBIFS file-system description object ++ * @inode: VFS inode the operation worked on ++ * @req: budget to release ++ * ++ * This function has to be called at the end of VFS operations which acquired ++ * budget via 'ubifs_budget_inode_op()'. It assumes that the inode has been ++ * marked as dirty and will be synchronized later by write-back, so it does not ++ * release the budget of the inode. ++ * ++ * Note, this function also avoids releasing page budgets which are released ++ * separately. ++ */ ++void ubifs_release_ino_dirty(struct ubifs_info *c, struct inode *inode, ++ struct ubifs_budget_req *req) ++{ ++ ubifs_assert(req->dirtied_ino <= 4); ++ ubifs_assert(req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4); ++ ubifs_assert(req->idx_growth >= 0); ++ ubifs_assert(req->data_growth >= 0); ++ ubifs_assert(req->dd_growth >= 0); ++ ++ if (req->dirtied_ino) { ++ req->dd_growth -= c->inode_budget; ++ req->dd_growth -= req->dirtied_ino_d; ++ } ++ ++ if (req->dirtied_page) { ++ req->dd_growth -= c->page_budget; ++ ubifs_assert(req->new_page == 0); ++ } else if (req->new_page) { ++ req->idx_growth -= ++ c->max_idx_node_sz << UBIFS_BLOCKS_PER_PAGE_SHIFT; ++ req->data_growth -= c->page_budget; ++ ubifs_assert(req->dirtied_page == 0); ++ } ++ ++ ubifs_assert(req->dd_growth >= 0); ++ ubifs_release_budget(c, req); ++ mutex_unlock(&ubifs_inode(inode)->budg_mutex); ++} ++ ++/** ++ * ubifs_cancel_ino_op - cancel budget of an operation on inode. ++ * @c: UBIFS file-system description object ++ * @inode: VFS inode the operation worked on ++ * @req: budget to release ++ * ++ * This function has to be called if the operation failed and whole budget has ++ * to be released, including the budget for inode which would had been ++ * dirtied. It is important not to mark the inode dirty before calling this ++ * function. ++ */ ++void ubifs_cancel_ino_op(struct ubifs_info *c, struct inode *inode, ++ struct ubifs_budget_req *req) ++{ ++ ubifs_assert(req->dirtied_ino <= 4); ++ ubifs_assert(req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4); ++ ubifs_assert(req->idx_growth >= 0); ++ ubifs_assert(req->data_growth >= 0); ++ ubifs_assert(req->dd_growth >= 0); ++ ++ ubifs_release_budget(c, req); ++ mutex_unlock(&ubifs_inode(inode)->budg_mutex); ++} ++ ++/** ++ * ubifs_release_ino_clean - release budget of a "cleaning" operation. ++ * @c: UBIFS file-system description object ++ * @inode: VFS inode the operation worked on ++ * @req: budget to release ++ * ++ * This function has to be called at the end of VFS operations which acquired ++ * budget via 'ubifs_budget_inode_op()'. It assumed the operation synchronized ++ * the inode, so it marks the inode clean, unlocks it and releases whole budget. ++ * ++ * Note, this function also avoids releasing page budgets which are released ++ * separately. ++ */ ++void ubifs_release_ino_clean(struct ubifs_info *c, struct inode *inode, ++ struct ubifs_budget_req *req) ++{ ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ ++ ubifs_assert(req->dirtied_ino <= 4); ++ ubifs_assert(req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4); ++ ubifs_assert(req->idx_growth >= 0); ++ ubifs_assert(req->data_growth >= 0); ++ ubifs_assert(req->dd_growth >= 0); ++ ubifs_assert(!req->dirtied_page); ++ ubifs_assert(!req->new_page); ++ UBIFS_DBG(ui->budgeted = 0); ++ ++ ubifs_release_budget(c, req); ++ if (ui->dirty) { ++ ui->dirty = 0; ++ /* ++ * Note, VFS still treats the inode as dirty and ++ * 'ubifs_write_inode()' will be called, but it'll do nothing ++ * because @ui->dirty is %0. ++ */ ++ atomic_long_dec(&c->dirty_ino_cnt); ++ } ++ mutex_unlock(&ubifs_inode(inode)->budg_mutex); ++} ++ ++/** ++ * ubifs_release_new_page_budget - release budget of a new page. ++ * @c: UBIFS file-system description object ++ * ++ * This is a helper function which releases budget corresponding to the budget ++ * of one new page of data. ++ */ ++void ubifs_release_new_page_budget(struct ubifs_info *c) ++{ ++ struct ubifs_budget_req req = { .new_page = 1, ++ .idx_growth = -1, ++ .data_growth = c->page_budget }; ++ ++ ubifs_release_budget(c, &req); ++} ++ ++/** ++ * ubifs_budg_get_free_space - return amount of free space. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns amount of free space on the file-system. ++ */ ++long long ubifs_budg_get_free_space(struct ubifs_info *c) ++{ ++ int min_idx_lebs, rsvd_idx_lebs; ++ long long available, outstanding, free; ++ ++ /* Do exactly the same calculations as in 'do_budget_space()' */ ++ spin_lock(&c->space_lock); ++ min_idx_lebs = ubifs_calc_min_idx_lebs(c); ++ ++ if (min_idx_lebs > c->lst.idx_lebs) ++ rsvd_idx_lebs = min_idx_lebs - c->lst.idx_lebs; ++ else ++ rsvd_idx_lebs = 0; ++ ++ if (rsvd_idx_lebs > c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt ++ - c->lst.taken_empty_lebs) { ++ spin_unlock(&c->space_lock); ++ return 0; ++ } ++ ++ c->min_idx_lebs = min_idx_lebs; ++ available = ubifs_calc_available(c); ++ outstanding = c->budg_data_growth + c->budg_dd_growth; ++ spin_unlock(&c->space_lock); ++ ++ if (available > outstanding) ++ free = ubifs_reported_space(c, available - outstanding); ++ else ++ free = 0; ++ ++ return free; ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/build.c linux-2.6.24.7/fs/ubifs/build.c +--- linux-2.6.24.7.old/fs/ubifs/build.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/build.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1423 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This file implements UBIFS initialization, mount and un-mount. Some ++ * initialization stuff which is rather large and complex is placed at ++ * corresponding subsystems, but most of it is here. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ubifs.h" ++ ++/* Slab cache for UBIFS inodes */ ++struct kmem_cache *ubifs_inode_slab; ++ ++/* TODO: remove compatibility stuff as late as possible */ ++#ifndef UBIFS_COMPAT_NO_SHRINKER ++/* UBIFS TNC shrinker description */ ++static struct shrinker ubifs_shrinker_info = { ++ .shrink = ubifs_shrinker, ++ .seeks = DEFAULT_SEEKS, ++}; ++#endif ++ ++/** ++ * init_constants_early - initialize UBIFS constants. ++ * @c: UBIFS file-system description object ++ * ++ * This function initialize UBIFS constants which do not need the superblock to ++ * be read. It also checks that the UBI volume satisfies basic UBIFS ++ * requirements. Returns zero in case of success and a negative error code in ++ * case of failure. ++ */ ++static int init_constants_early(struct ubifs_info *c) ++{ ++ if (c->vi.corrupted) { ++ ubifs_warn("UBI volume is corrupted - read-only mode"); ++ c->ro_media = 1; ++ } ++ ++ if (c->di.ro_mode) { ++ ubifs_msg("read-only UBI device"); ++ c->ro_media = 1; ++ } ++ ++ if (c->vi.vol_type == UBI_STATIC_VOLUME) { ++ ubifs_msg("static UBI volume - read-only mode"); ++ c->ro_media = 1; ++ } ++ ++ c->leb_cnt = c->vi.size; ++ c->leb_size = c->vi.usable_leb_size; ++ c->half_leb_size = c->leb_size / 2; ++ c->min_io_size = c->di.min_io_size; ++ c->min_io_shift = fls(c->min_io_size) - 1; ++ ++ if (c->leb_size < UBIFS_MIN_LEB_SZ) { ++ ubifs_err("too small LEBs (%d bytes), min. is %d bytes", ++ c->leb_size, UBIFS_MIN_LEB_SZ); ++ return -EINVAL; ++ } ++ ++ if (c->leb_cnt < UBIFS_MIN_LEB_CNT) { ++ ubifs_err("too few LEBs (%d), min. is %d", ++ c->leb_cnt, UBIFS_MIN_LEB_CNT); ++ return -EINVAL; ++ } ++ ++ if (!is_power_of_2(c->min_io_size)) { ++ ubifs_err("bad min. I/O size %d", c->min_io_size); ++ return -EINVAL; ++ } ++ ++ /* ++ * UBIFS aligns all node to 8-byte boundary, so to make function in ++ * io.c simpler, assume minimum I/O unit size to be 8 bytes if it is ++ * less than 8. ++ */ ++ if (c->min_io_size < 8) { ++ c->min_io_size = 8; ++ c->min_io_shift = 3; ++ } ++ ++ c->ref_node_alsz = ALIGN(UBIFS_REF_NODE_SZ, c->min_io_size); ++ c->mst_node_alsz = ALIGN(UBIFS_MST_NODE_SZ, c->min_io_size); ++ ++ /* ++ * Initialize node length ranges which are mostly needed for node ++ * length validation. ++ */ ++ c->ranges[UBIFS_PAD_NODE].len = UBIFS_PAD_NODE_SZ; ++ c->ranges[UBIFS_SB_NODE].len = UBIFS_SB_NODE_SZ; ++ c->ranges[UBIFS_MST_NODE].len = UBIFS_MST_NODE_SZ; ++ c->ranges[UBIFS_REF_NODE].len = UBIFS_REF_NODE_SZ; ++ c->ranges[UBIFS_TRUN_NODE].len = UBIFS_TRUN_NODE_SZ; ++ c->ranges[UBIFS_CS_NODE].len = UBIFS_CS_NODE_SZ; ++ ++ c->ranges[UBIFS_INO_NODE].min_len = UBIFS_INO_NODE_SZ; ++ c->ranges[UBIFS_INO_NODE].max_len = UBIFS_MAX_INO_NODE_SZ; ++ c->ranges[UBIFS_ORPH_NODE].min_len = ++ UBIFS_ORPH_NODE_SZ + sizeof(__le64); ++ c->ranges[UBIFS_ORPH_NODE].max_len = c->leb_size; ++ c->ranges[UBIFS_DENT_NODE].min_len = UBIFS_DENT_NODE_SZ; ++ c->ranges[UBIFS_DENT_NODE].max_len = UBIFS_MAX_DENT_NODE_SZ; ++ c->ranges[UBIFS_XENT_NODE].min_len = UBIFS_XENT_NODE_SZ; ++ c->ranges[UBIFS_XENT_NODE].max_len = UBIFS_MAX_XENT_NODE_SZ; ++ c->ranges[UBIFS_DATA_NODE].min_len = UBIFS_DATA_NODE_SZ; ++ c->ranges[UBIFS_DATA_NODE].max_len = UBIFS_MAX_DATA_NODE_SZ; ++ /* ++ * Minimum indexing node size is amended later when superblock is ++ * read and the key length is known. ++ */ ++ c->ranges[UBIFS_IDX_NODE].min_len = UBIFS_IDX_NODE_SZ + UBIFS_BRANCH_SZ; ++ /* ++ * Maximum indexing node size is amended later when superblock is ++ * read and the fanout is known. ++ */ ++ c->ranges[UBIFS_IDX_NODE].max_len = INT_MAX; ++ ++ /* ++ * Initialize dead and dark LEB space watermarks. ++ * ++ * Dead space is the space which cannot be used. Its watermark is ++ * equivalent to min. I/O unit or minimum node size if it is greater ++ * then min. I/O unit. ++ * ++ * Dark space is the space which might be used, or might not, depending ++ * on which node should be written to the LEB. Its watermark is ++ * equivalent to maximum UBIFS node size. ++ */ ++ c->dead_wm = ALIGN(MIN_WRITE_SZ, c->min_io_size); ++ c->dark_wm = ALIGN(UBIFS_MAX_NODE_SZ, c->min_io_size); ++ ++ return 0; ++} ++ ++/** ++ * bud_wbuf_callback - bud LEB write-buffer synchronization call-back. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB the write-buffer was synchronized to ++ * @free: how many free bytes left in this LEB ++ * @pad: how many bytes were padded ++ * ++ * This is a callback function which is called by the I/O unit when the ++ * write-buffer is synchronized. We need this to correctly maintain space ++ * accounting in bud logical eraseblocks. This function returns zero in case of ++ * success and a negative error code in case of failure. ++ * ++ * This function actually belongs to the journal, but we keep it here because ++ * we want to keep it static. ++ */ ++static int bud_wbuf_callback(struct ubifs_info *c, int lnum, int free, int pad) ++{ ++ return ubifs_update_one_lp(c, lnum, free, pad, 0, 0); ++} ++ ++/* ++ * init_constants_late - initialize UBIFS constants. ++ * @c: UBIFS file-system description object ++ * ++ * This is a helper function which initializes various UBIFS constants after ++ * the superblock has been read. It also checks various UBIFS parameters and ++ * makes sure they are all right. Returns zero in case of success and a ++ * negative error code in case of failure. ++ */ ++static int init_constants_late(struct ubifs_info *c) ++{ ++ int tmp, err; ++ uint64_t tmp64; ++ ++ c->main_bytes = c->main_lebs * c->leb_size; ++ ++ c->max_znode_sz = sizeof(struct ubifs_znode) + ++ c->fanout * sizeof(struct ubifs_zbranch); ++ ++ tmp = ubifs_idx_node_sz(c, 1); ++ c->ranges[UBIFS_IDX_NODE].min_len = tmp; ++ c->min_idx_node_sz = ALIGN(tmp, 8); ++ ++ tmp = ubifs_idx_node_sz(c, c->fanout); ++ c->ranges[UBIFS_IDX_NODE].max_len = tmp; ++ c->max_idx_node_sz = ALIGN(tmp, 8); ++ ++ /* Make sure LEB size is large enough to fit full commit */ ++ tmp = UBIFS_CS_NODE_SZ + UBIFS_REF_NODE_SZ * c->jhead_cnt; ++ tmp = ALIGN(tmp, c->min_io_size); ++ if (tmp > c->leb_size) { ++ dbg_err("too small LEB size %d, at least %d needed", ++ c->leb_size, tmp); ++ return -EINVAL; ++ } ++ ++ /* ++ * Make sure that the log is large enough to fit reference nodes for ++ * all buds plus one reserved LEB. ++ */ ++ tmp64 = c->max_bud_bytes; ++ tmp = do_div(tmp64, c->leb_size); ++ c->max_bud_cnt = tmp64 + !!tmp; ++ tmp = (c->ref_node_alsz * c->max_bud_cnt + c->leb_size - 1); ++ tmp /= c->leb_size; ++ tmp += 1; ++ if (c->log_lebs < tmp) { ++ dbg_err("too small log %d LEBs, required min. %d LEBs", ++ c->log_lebs, tmp); ++ return -EINVAL; ++ } ++ ++ /* ++ * When budgeting we assume worst-case scenarios when the pages are not ++ * be compressed and direntries are of the maximum size. ++ * ++ * Note, data, which may be stored in inodes is budgeted separately, so ++ * it is not included into 'c->inode_budget'. ++ * ++ * c->page_budget is PAGE_CACHE_SIZE + UBIFS_CH_SZ * blocks_per_page ++ */ ++ c->page_budget = PAGE_CACHE_SIZE + UBIFS_CH_SZ; ++ c->inode_budget = UBIFS_INO_NODE_SZ; ++ c->dent_budget = UBIFS_MAX_DENT_NODE_SZ; ++ ++ /* ++ * When the amount of flash space used by buds becomes ++ * 'c->max_bud_bytes', UBIFS just blocks all writers and starts commit. ++ * The writers are unblocked when the commit is finished. To avoid ++ * writers to be blocked UBIFS initiates background commit in advance, ++ * when number of bud bytes becomes above the limit defined below. ++ */ ++ c->bg_bud_bytes = (c->max_bud_bytes * 13) >> 4; ++ ++ /* ++ * Ensure minimum journal size. All the bytes in the journal heads are ++ * considered to be used, when calculating the current journal usage. ++ * Consequently, if the journal is too small, UBIFS will treat it as ++ * always full. ++ */ ++ tmp64 = (uint64_t)(c->jhead_cnt + 1) * c->leb_size + 1; ++ if (c->bg_bud_bytes < tmp64) ++ c->bg_bud_bytes = tmp64; ++ if (c->max_bud_bytes < tmp64 + c->leb_size) ++ c->max_bud_bytes = tmp64 + c->leb_size; ++ ++ err = ubifs_calc_lpt_geom(c); ++ if (err) ++ return err; ++ ++ c->min_idx_lebs = ubifs_calc_min_idx_lebs(c); ++ ++ /* ++ * Calculate total amount of FS blocks. This number is not used ++ * internally because it does not make much sense for UBIFS, but it is ++ * necessary to report something for the 'statfs()' call. ++ * ++ * Subtract the LEB reserved for GC and the LEB which is reserved for ++ * deletions. ++ * ++ * Review 'ubifs_calc_available()' if changing this calculation. ++ */ ++ tmp64 = c->main_lebs - 2; ++ tmp64 *= c->leb_size - c->dark_wm; ++ tmp64 = ubifs_reported_space(c, tmp64); ++ c->block_cnt = tmp64 >> UBIFS_BLOCK_SHIFT; ++ ++ return 0; ++} ++ ++/** ++ * care_about_gc_lnum - take care about reserved GC LEB. ++ * @c: UBIFS file-system description object ++ * ++ * This function ensures that the LEB reserved for garbage collection is ++ * unmapped and is marked as "taken" in lprops. We also have to set free space ++ * to LEB size and dirty space to zero, because lprops may contain out-of-date ++ * information if the file-system was un-mounted before it has been committed. ++ * This function returns zero in case of success and a negative error code in ++ * case of failure. ++ */ ++static int care_about_gc_lnum(struct ubifs_info *c) ++{ ++ int err; ++ ++ if (c->gc_lnum == -1) { ++ ubifs_err("no LEB for GC"); ++ return -EINVAL; ++ } ++ ++ err = ubifs_leb_unmap(c, c->gc_lnum); ++ if (err) ++ return err; ++ ++ /* And we have to tell lprops that this LEB is taken */ ++ err = ubifs_change_one_lp(c, c->gc_lnum, c->leb_size, 0, ++ LPROPS_TAKEN, 0, 0); ++ return err; ++} ++ ++/** ++ * alloc_wbufs - allocate write-buffers. ++ * @c: UBIFS file-system description object ++ * ++ * This helper function allocates and initializes UBIFS write-buffers. Returns ++ * zero in case of success and %-ENOMEM in case of failure. ++ */ ++static int alloc_wbufs(struct ubifs_info *c) ++{ ++ int i, err; ++ ++ c->jheads = kzalloc(c->jhead_cnt * sizeof(struct ubifs_jhead), ++ GFP_KERNEL); ++ if (!c->jheads) ++ return -ENOMEM; ++ ++ /* Initialize journal heads */ ++ for (i = 0; i < c->jhead_cnt; i++) { ++ INIT_LIST_HEAD(&c->jheads[i].buds_list); ++ err = ubifs_wbuf_init(c, &c->jheads[i].wbuf); ++ if (err) ++ return err; ++ ++ c->jheads[i].wbuf.sync_callback = &bud_wbuf_callback; ++ c->jheads[i].wbuf.jhead = i; ++ } ++ ++ c->jheads[BASEHD].wbuf.dtype = UBI_SHORTTERM; ++ /* ++ * Garbage Collector head likely contains long-term data and ++ * does not need to be synchronized by timer. ++ */ ++ c->jheads[GCHD].wbuf.dtype = UBI_LONGTERM; ++ c->jheads[GCHD].wbuf.timeout = 0; ++ ++ sprintf(c->bgt_name, "%s%d_%d", SYNCER_BG_NAME, ++ c->vi.ubi_num, c->vi.vol_id); ++ ++ return 0; ++} ++ ++/** ++ * free_wbufs - free write-buffers. ++ * @c: UBIFS file-system description object ++ */ ++static void free_wbufs(struct ubifs_info *c) ++{ ++ int i; ++ ++ if (c->jheads) { ++ for (i = 0; i < c->jhead_cnt; i++) { ++ kfree(c->jheads[i].wbuf.buf); ++ kfree(c->jheads[i].wbuf.inodes); ++ } ++ kfree(c->jheads); ++ c->jheads = NULL; ++ } ++} ++ ++/** ++ * free_orphans - free orphans. ++ * @c: UBIFS file-system description object ++ */ ++static void free_orphans(struct ubifs_info *c) ++{ ++ struct ubifs_orphan *orph; ++ ++ while (c->orph_dnext) { ++ orph = c->orph_dnext; ++ c->orph_dnext = orph->dnext; ++ list_del(&orph->list); ++ kfree(orph); ++ } ++ ++ while (!list_empty(&c->orph_list)) { ++ orph = list_entry(c->orph_list.next, struct ubifs_orphan, list); ++ list_del(&orph->list); ++ kfree(orph); ++ dbg_err("orphan list not empty at unmount"); ++ } ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(c->orph_buf); ++#else ++ vfree(c->orph_buf); ++#endif ++ c->orph_buf = NULL; ++} ++ ++/** ++ * free_buds - free per-bud objects. ++ * @c: UBIFS file-system description object ++ */ ++static void free_buds(struct ubifs_info *c) ++{ ++ struct rb_node *this = c->buds.rb_node; ++ struct ubifs_bud *bud; ++ ++ while (this) { ++ if (this->rb_left) ++ this = this->rb_left; ++ else if (this->rb_right) ++ this = this->rb_right; ++ else { ++ bud = rb_entry(this, struct ubifs_bud, rb); ++ this = rb_parent(this); ++ if (this) { ++ if (this->rb_left == &bud->rb) ++ this->rb_left = NULL; ++ else ++ this->rb_right = NULL; ++ } ++ kfree(bud); ++ } ++ } ++} ++ ++/** ++ * check_volume_empty - check if the UBI volume is empty. ++ * @c: UBIFS file-system description object ++ * ++ * This function checks if the UBIFS volume is empty by looking if its LEBs are ++ * mapped or not. The result of checking is stored in the @c->empty variable. ++ * Returns zero in case of success and a negative error code in case of ++ * failure. ++ */ ++static int check_volume_empty(struct ubifs_info *c) ++{ ++ int lnum, err; ++ ++ c->empty = 1; ++ for (lnum = 0; lnum < c->leb_cnt; lnum++) { ++ err = ubi_is_mapped(c->ubi, lnum); ++ if (unlikely(err < 0)) ++ return err; ++ if (err == 1) { ++ c->empty = 0; ++ break; ++ } ++ ++ cond_resched(); ++ } ++ ++ return 0; ++} ++ ++/** ++ * mount_ubifs - mount UBIFS file-system. ++ * @c: UBIFS file-system description object ++ * ++ * This function mounts UBIFS file system. Returns zero in case of success and ++ * a negative error code in case of failure. ++ * ++ * Note, the function does not de-allocate resources it it fails half way ++ * through, and the caller has to do this instead. ++ */ ++static int mount_ubifs(struct ubifs_info *c) ++{ ++ struct super_block *sb = c->vfs_sb; ++ int err, mounted_read_only = (sb->s_flags & MS_RDONLY); ++ unsigned long long x; ++ size_t sz; ++ ++ err = init_constants_early(c); ++ if (err) ++ return err; ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ c->dbg_buf = kmalloc(c->leb_size, GFP_KERNEL); ++#else ++ c->dbg_buf = vmalloc(c->leb_size); ++#endif ++ if (!c->dbg_buf) ++ return -ENOMEM; ++#endif ++ ++ err = check_volume_empty(c); ++ if (err) ++ return err; ++ ++ if (c->empty && (mounted_read_only || c->ro_media)) { ++ /* ++ * This UBI volume is empty, and read-only, or the file system ++ * is mounted read-only - we cannot format it. ++ */ ++ ubifs_err("can't format empty UBI volume: read-only %s", ++ c->ro_media ? "UBI volume" : "mount"); ++ return -EROFS; ++ } ++ ++ if (c->ro_media && !mounted_read_only) { ++ ubifs_err("cannot mount read-write - read-only media"); ++ return -EROFS; ++ } ++ ++ /* ++ * The requirement for the buffer is that it should fit indexing B-tree ++ * height amount of integers. We assume the height if the TNC tree will ++ * never exceed 64. ++ */ ++ c->bottom_up_buf = kmalloc(BOTTOM_UP_HEIGHT * sizeof(int), GFP_KERNEL); ++ if (!c->bottom_up_buf) ++ return -ENOMEM; ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ c->sbuf = kmalloc(c->leb_size, GFP_KERNEL); ++#else ++ c->sbuf = vmalloc(c->leb_size); ++#endif ++ if (!c->sbuf) ++ return -ENOMEM; ++ ++ if (!mounted_read_only) { ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ c->ileb_buf = kmalloc(c->leb_size, GFP_KERNEL); ++#else ++ c->ileb_buf = vmalloc(c->leb_size); ++#endif ++ if (!c->ileb_buf) ++ return -ENOMEM; ++ } ++ ++ err = ubifs_read_superblock(c); ++ if (err) ++ return err; ++ ++ /* ++ * Make sure the compressor which is set as the default on in the ++ * superblock was actually compiled in. ++ */ ++ if (!ubifs_compr_present(c->default_compr)) { ++ ubifs_warn("'%s' compressor is set by superblock, but not " ++ "compiled in", ubifs_compr_name(c->default_compr)); ++ c->default_compr = UBIFS_COMPR_NONE; ++ } ++ ++ dbg_failure_mode_registration(c); ++ ++ err = init_constants_late(c); ++ if (err) ++ return err; ++ ++ sz = ALIGN(c->max_idx_node_sz, c->min_io_size); ++ sz = ALIGN(sz + c->max_idx_node_sz, c->min_io_size); ++ c->cbuf = kmalloc(sz, GFP_NOFS); ++ if (!c->cbuf) ++ return -ENOMEM; ++ ++ if (!mounted_read_only) { ++ err = alloc_wbufs(c); ++ if (err) ++ return err; ++ ++ /* Create background thread */ ++ c->bgt = kthread_create(ubifs_bg_thread, c, c->bgt_name); ++ if (!c->bgt) ++ c->bgt = ERR_PTR(-EINVAL); ++ if (IS_ERR(c->bgt)) { ++ err = PTR_ERR(c->bgt); ++ c->bgt = NULL; ++ ubifs_err("cannot spawn \"%s\", error %d", ++ c->bgt_name, err); ++ return err; ++ } ++ } ++ ++ err = ubifs_read_master(c); ++ if (err) ++ return err; ++ ++ if ((c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY)) != 0) { ++ ubifs_msg("recovery needed"); ++ c->need_recovery = 1; ++ if (!mounted_read_only) { ++ err = ubifs_recover_inl_heads(c, c->sbuf); ++ if (err) ++ return err; ++ } ++ } else if (!mounted_read_only) { ++ /* ++ * Set the "dirty" flag so that if we reboot uncleanly we ++ * will notice this immediately on the next mount. ++ */ ++ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY); ++ err = ubifs_write_master(c); ++ if (err) ++ return err; ++ } ++ ++ err = ubifs_lpt_init(c, 1, !mounted_read_only); ++ if (err) ++ return err; ++ ++ err = dbg_check_idx_size(c, c->old_idx_sz); ++ if (err) ++ return err; ++ ++ err = ubifs_replay_journal(c); ++ if (err) ++ return err; ++ ++ if (!mounted_read_only) { ++ int lnum; ++ ++ if (c->need_recovery) ++ err = ubifs_recover_gc_lnum(c); ++ else ++ err = care_about_gc_lnum(c); ++ if (err) ++ return err; ++ err = ubifs_mount_orphans(c, c->need_recovery); ++ if (err) ++ return err; ++ ++ /* Check for enough log space */ ++ lnum = c->lhead_lnum + 1; ++ if (lnum >= UBIFS_LOG_LNUM + c->log_lebs) ++ lnum = UBIFS_LOG_LNUM; ++ if (lnum == c->ltail_lnum) { ++ err = ubifs_consolidate_log(c); ++ if (err) ++ return err; ++ } ++ ++ /* Check for enough free space */ ++ if (ubifs_calc_available(c) <= 0) { ++ ubifs_err("insufficient available space"); ++ return -EINVAL; ++ } ++ ++ err = dbg_check_lprops(c); ++ if (err) ++ return err; ++ } ++ ++ if (c->need_recovery) { ++ err = ubifs_recover_size(c); ++ if (err) ++ return err; ++ } ++ ++ spin_lock(&ubifs_infos_lock); ++ list_add_tail(&c->infos_list, &ubifs_infos); ++ spin_unlock(&ubifs_infos_lock); ++ ++ if (c->need_recovery) { ++ if (mounted_read_only) ++ ubifs_msg("recovery deferred"); ++ else { ++ c->need_recovery = 0; ++ ubifs_msg("recovery completed"); ++ } ++ } ++ ++ ubifs_msg("mounted UBI device %d, volume %d", c->vi.ubi_num, ++ c->vi.vol_id); ++ if (mounted_read_only) ++ ubifs_msg("mounted read-only"); ++ ubifs_msg("minimal I/O unit size: %d bytes", c->min_io_size); ++ ubifs_msg("logical eraseblock size: %d bytes (%d KiB)", ++ c->leb_size, c->leb_size / 1024); ++ x = (unsigned long long)c->main_lebs * c->leb_size; ++ ubifs_msg("file system size: %lld bytes (%lld KiB, %lld MiB, " ++ "%d LEBs)", x, x >> 10, x >> 20, c->main_lebs); ++ x = (unsigned long long)c->log_lebs * c->leb_size + c->max_bud_bytes; ++ ubifs_msg("journal size: %lld bytes (%lld KiB, %lld MiB, " ++ "%d LEBs)", x, x >> 10, x >> 20, ++ c->log_lebs + c->max_bud_cnt); ++ ubifs_msg("data journal heads: %d", ++ c->jhead_cnt - NONDATA_JHEADS_CNT); ++ ubifs_msg("default compressor: %s", ++ ubifs_compr_name(c->default_compr)); ++ ++ dbg_msg("compiled on: " __DATE__ " at " __TIME__); ++ dbg_msg("fast unmount: %d", c->fast_unmount); ++ dbg_msg("big_lpt %d", c->big_lpt); ++ dbg_msg("log LEBs: %d (%d - %d)", ++ c->log_lebs, UBIFS_LOG_LNUM, c->log_last); ++ dbg_msg("LPT area LEBs: %d (%d - %d)", ++ c->lpt_lebs, c->lpt_first, c->lpt_last); ++ dbg_msg("orphan area LEBs: %d (%d - %d)", ++ c->orph_lebs, c->orph_first, c->orph_last); ++ dbg_msg("main area LEBs: %d (%d - %d)", ++ c->main_lebs, c->main_first, c->leb_cnt - 1); ++ dbg_msg("index LEBs: %d", c->lst.idx_lebs); ++ dbg_msg("total index bytes: %lld (%lld KiB, %lld MiB)", ++ c->old_idx_sz, c->old_idx_sz >> 10, c->old_idx_sz >> 20); ++ dbg_msg("key hash type: %d", c->key_hash_type); ++ dbg_msg("tree fanout: %d", c->fanout); ++ dbg_msg("reserved GC LEB: %d", c->gc_lnum); ++ dbg_msg("first main LEB: %d", c->main_first); ++ dbg_msg("dead watermark: %d", c->dead_wm); ++ dbg_msg("dark watermark: %d", c->dark_wm); ++ x = c->main_lebs * c->dark_wm; ++ dbg_msg("max. dark space: %lld (%lld KiB, %lld MiB)", ++ x, x >> 10, x >> 20); ++ dbg_msg("maximum bud bytes: %lld (%lld KiB, %lld MiB)", ++ c->max_bud_bytes, c->max_bud_bytes >> 10, ++ c->max_bud_bytes >> 20); ++ dbg_msg("BG commit bud bytes: %lld (%lld KiB, %lld MiB)", ++ c->bg_bud_bytes, c->bg_bud_bytes >> 10, ++ c->bg_bud_bytes >> 20); ++ dbg_msg("current bud bytes %lld (%lld KiB, %lld MiB)", ++ c->bud_bytes, c->bud_bytes >> 10, c->bud_bytes >> 20); ++ dbg_msg("max. seq. number: %llu", c->max_sqnum); ++ dbg_msg("commit number: %llu", c->cmt_no); ++ ++ return 0; ++} ++ ++/** ++ * ubifs_umount - un-mount UBIFS file-system. ++ * @c: UBIFS file-system description object ++ * ++ * Note, this function is called to free allocated resourced when un-mounting, ++ * as well as free resources when an error occurred while we were half way ++ * through mounting (error path cleanup function). So it has to make sure the ++ * resource was actually allocated before freeing it. ++ */ ++void ubifs_umount(struct ubifs_info *c) ++{ ++ dbg_gen("un-mounting UBI device %d, volume %d", c->vi.ubi_num, ++ c->vi.vol_id); ++ ++ ubifs_destroy_size_tree(c); ++ ++ if (c->bgt) ++ kthread_stop(c->bgt); ++ ++ free_buds(c); ++ ubifs_destroy_idx_gc(c); ++ ubifs_tnc_close(c); ++ ++ free_wbufs(c); ++ free_orphans(c); ++ ubifs_lpt_free(c, 0); ++ ++ while (!list_empty(&c->unclean_leb_list)) { ++ struct ubifs_unclean_leb *ucleb; ++ ++ ucleb = list_entry(c->unclean_leb_list.next, ++ struct ubifs_unclean_leb, list); ++ list_del(&ucleb->list); ++ kfree(ucleb); ++ } ++ ++ while (!list_empty(&c->old_buds)) { ++ struct ubifs_bud *bud; ++ ++ bud = list_entry(c->old_buds.next, struct ubifs_bud, list); ++ list_del(&bud->list); ++ kfree(bud); ++ } ++ ++ kfree(c->rcvrd_mst_node); ++ kfree(c->mst_node); ++ kfree(c->bottom_up_buf); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(c->sbuf); ++ UBIFS_DBG(kfree(c->dbg_buf)); ++ kfree(c->ileb_buf); ++#else ++ vfree(c->sbuf); ++ UBIFS_DBG(vfree(c->dbg_buf)); ++ vfree(c->ileb_buf); ++#endif ++ dbg_failure_mode_deregistration(c); ++} ++ ++/** ++ * ubifs_remount_rw - re-mount in read-write mode. ++ * @c: UBIFS file-system description object ++ * ++ * UBIFS avoids allocating many unnecessary resources when mounted in read-only ++ * mode. This function allocates the needed resources and re-mounts UBIFS in ++ * read-write mode. ++ */ ++int ubifs_remount_rw(struct ubifs_info *c) ++{ ++ int err, lnum; ++ ++ if (c->ro_media) ++ return -EINVAL; ++ ++ mutex_lock(&c->umount_mutex); ++ c->remounting_rw = 1; ++ ++ /* Check for enough free space */ ++ if (ubifs_calc_available(c) <= 0) { ++ ubifs_err("insufficient available space"); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (c->old_leb_cnt != c->leb_cnt) { ++ struct ubifs_sb_node *sup; ++ ++ sup = ubifs_read_sb_node(c); ++ if (IS_ERR(sup)) { ++ err = PTR_ERR(sup); ++ goto out; ++ } ++ sup->leb_cnt = cpu_to_le32(c->leb_cnt); ++ err = ubifs_write_sb_node(c, sup); ++ if (err) ++ goto out; ++ } ++ ++ if (c->need_recovery) { ++ ubifs_msg("completing deferred recovery"); ++ err = ubifs_write_rcvrd_mst_node(c); ++ if (err) ++ goto out; ++ err = ubifs_recover_size(c); ++ if (err) ++ goto out; ++ err = ubifs_clean_lebs(c, c->sbuf); ++ if (err) ++ goto out; ++ err = ubifs_recover_inl_heads(c, c->sbuf); ++ if (err) ++ goto out; ++ } ++ ++ if (!(c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY))) { ++ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY); ++ err = ubifs_write_master(c); ++ if (err) ++ goto out; ++ } ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ c->ileb_buf = kmalloc(c->leb_size, GFP_KERNEL); ++#else ++ c->ileb_buf = vmalloc(c->leb_size); ++#endif ++ if (!c->ileb_buf) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ err = ubifs_lpt_init(c, 0, 1); ++ if (err) ++ goto out; ++ ++ err = alloc_wbufs(c); ++ if (err) ++ goto out; ++ ++ ubifs_create_buds_lists(c); ++ ++ /* Create background thread */ ++ c->bgt = kthread_create(ubifs_bg_thread, c, c->bgt_name); ++ if (!c->bgt) ++ c->bgt = ERR_PTR(-EINVAL); ++ if (IS_ERR(c->bgt)) { ++ err = PTR_ERR(c->bgt); ++ c->bgt = NULL; ++ ubifs_err("cannot spawn \"%s\", error %d", ++ c->bgt_name, err); ++ return err; ++ } ++ ++ if (c->need_recovery) ++ err = ubifs_recover_gc_lnum(c); ++ else ++ err = care_about_gc_lnum(c); ++ if (err) ++ goto out; ++ ++ err = ubifs_mount_orphans(c, c->need_recovery); ++ if (err) ++ goto out; ++ /* Check for enough log space */ ++ lnum = c->lhead_lnum + 1; ++ if (lnum >= UBIFS_LOG_LNUM + c->log_lebs) ++ lnum = UBIFS_LOG_LNUM; ++ if (lnum == c->ltail_lnum) { ++ err = ubifs_consolidate_log(c); ++ if (err) ++ goto out; ++ } ++ ++ if (c->need_recovery) { ++ c->need_recovery = 0; ++ ubifs_msg("deferred recovery completed"); ++ } ++ ++ dbg_gen("re-mounted read-write"); ++ c->vfs_sb->s_flags &= ~MS_RDONLY; ++ c->remounting_rw = 0; ++ mutex_unlock(&c->umount_mutex); ++ return 0; ++ ++out: ++ free_orphans(c); ++ if (c->bgt) { ++ kthread_stop(c->bgt); ++ c->bgt = NULL; ++ } ++ free_wbufs(c); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(c->ileb_buf); ++#else ++ vfree(c->ileb_buf); ++#endif ++ c->ileb_buf = NULL; ++ ubifs_lpt_free(c, 1); ++ c->remounting_rw = 0; ++ mutex_unlock(&c->umount_mutex); ++ return err; ++} ++ ++/** ++ * commit_on_unmount - commit the journal when un-mounting. ++ * @c: UBIFS file-system description object ++ * ++ * This function is called during un-mounting and it commits the journal unless ++ * the "fast unmount" mode is enabled. It also avoids committing the journal if ++ * it contains too few data. ++ * ++ * Sometimes recovery requires the journal to be committed at least once, and ++ * this function takes care about this. ++ */ ++static void commit_on_unmount(struct ubifs_info *c) ++{ ++ if (!c->fast_unmount) { ++ long long bud_bytes; ++ ++ spin_lock(&c->buds_lock); ++ bud_bytes = c->bud_bytes; ++ spin_unlock(&c->buds_lock); ++ if (bud_bytes > c->leb_size) ++ ubifs_run_commit(c); ++ } ++ ++ if (c->recovery_needs_commit) ++ ubifs_recovery_commit(c); ++} ++ ++/** ++ * ubifs_remount_ro - re-mount in read-only mode. ++ * @c: UBIFS file-system description object ++ * ++ * We rely on VFS to have stopped writing. Possibly the background thread could ++ * be running a commit, however kthread_stop will wait in that case. ++ */ ++void ubifs_remount_ro(struct ubifs_info *c) ++{ ++ int i, err; ++ ++ ubifs_assert(!c->need_recovery); ++ ++ commit_on_unmount(c); ++ ++ mutex_lock(&c->umount_mutex); ++ if (c->bgt) { ++ kthread_stop(c->bgt); ++ c->bgt = NULL; ++ } ++ ++ for (i = 0; i < c->jhead_cnt; i++) { ++ ubifs_wbuf_sync(&c->jheads[i].wbuf); ++ del_timer_sync(&c->jheads[i].wbuf.timer); ++ } ++ ++ if (!c->ro_media) { ++ c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY); ++ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS); ++ c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum); ++ err = ubifs_write_master(c); ++ if (err) ++ ubifs_ro_mode(c, err); ++ } ++ ++ ubifs_destroy_idx_gc(c); ++ free_wbufs(c); ++ free_orphans(c); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(c->ileb_buf); ++#else ++ vfree(c->ileb_buf); ++#endif ++ c->ileb_buf = NULL; ++ ubifs_lpt_free(c, 1); ++ mutex_unlock(&c->umount_mutex); ++} ++ ++/** ++ * open_ubi - parse UBI device name string and open the UBI device. ++ * @c: UBIFS file-system description object ++ * @name: UBI volume name ++ * @mode: UBI volume open mode ++ * ++ * There are several ways to specify UBI volumes when mounting UBIFS: ++ * o ubiX_Y - UBI device number X, volume Y; ++ * o ubiY - UBI device number 0, volume Y; ++ * o ubiX:NAME - mount UBI device X, volume with name NAME; ++ * o ubi:NAME - mount UBI device 0, volume with name NAME. ++ * ++ * Alternative '!' separator may be used instead of ':' (because some shells ++ * like busybox may interpret ':' as an NFS host name separator). This function ++ * returns zero in case of success and a negative error code in case of ++ * failure. ++ */ ++static int open_ubi(struct ubifs_info *c, const char *name, int mode) ++{ ++ int dev, vol; ++ char *endptr; ++ ++ if (name[0] != 'u' || name[1] != 'b' || name[2] != 'i') ++ return -EINVAL; ++ ++ if ((name[3] == ':' || name[3] == '!') && name[4] != '\0') { ++ /* ubi:NAME method */ ++ c->ubi = ubi_open_volume_nm(0, name + 4, mode); ++ if (IS_ERR(c->ubi)) ++ return PTR_ERR(c->ubi); ++ } else if (isdigit(name[3])) { ++ dev = simple_strtoul(name + 3, &endptr, 0); ++ if (*endptr == '\0') { ++ /* ubiY method */ ++ c->ubi = ubi_open_volume(0, dev, mode); ++ if (IS_ERR(c->ubi)) ++ return PTR_ERR(c->ubi); ++ } else if (*endptr == '_' && isdigit(endptr[1])) { ++ /* ubiX_Y method */ ++ vol = simple_strtoul(endptr + 1, &endptr, 0); ++ if (*endptr != '\0') ++ return -EINVAL; ++ c->ubi = ubi_open_volume(dev, vol, mode); ++ if (IS_ERR(c->ubi)) ++ return PTR_ERR(c->ubi); ++ } else if ((*endptr == ':' || *endptr == '!') && ++ endptr[1] != '\0') { ++ /* ubiX:NAME method */ ++ c->ubi = ubi_open_volume_nm(dev, ++endptr, mode); ++ if (IS_ERR(c->ubi)) ++ return PTR_ERR(c->ubi); ++ } ++ } ++ ++ if (!c->ubi) ++ return -EINVAL; ++ ++ ubi_get_volume_info(c->ubi, &c->vi); ++ ubi_get_device_info(c->vi.ubi_num, &c->di); ++ return 0; ++} ++ ++static int sb_test(struct super_block *sb, void *data) ++{ ++ dev_t *dev = data; ++ ++ return sb->s_dev == *dev; ++} ++ ++static int sb_set(struct super_block *sb, void *data) ++{ ++ return 0; ++} ++ ++/* ++ * UBIFS mount options. ++ * ++ * Opt_fast_unmount: do not run a journal commit before un-mounting ++ * Opt_norm_unmount: run a journal commit before un-mounting ++ * Opt_err: just end of array marker ++ */ ++enum { ++ Opt_fast_unmount, ++ Opt_norm_unmount, ++ Opt_err, ++}; ++ ++static match_table_t tokens = { ++ {Opt_fast_unmount, "fast_unmount"}, ++ {Opt_norm_unmount, "norm_unmount"}, ++ {Opt_err, NULL}, ++}; ++ ++/** ++ * ubifs_parse_options - parse mount parameters. ++ * @c: UBIFS file-system description object ++ * @options: parameters to parse ++ * @is_remount: non-zero if this is FS re-mount ++ * ++ * This function parses UBIFS mount options and returns zero in case success ++ * and a negative error code in case of failure. ++ */ ++int ubifs_parse_options(struct ubifs_info *c, char *options, int is_remount) ++{ ++ char *p; ++ substring_t args[MAX_OPT_ARGS]; ++ ++ if (!options) ++ return 0; ++ ++ while ((p = strsep(&options, ",")) != NULL) { ++ int token; ++ ++ if (!*p) ++ continue; ++ ++ token = match_token(p, tokens, args); ++ switch (token) { ++ case Opt_fast_unmount: ++ c->mount_opts.unmount_mode = 2; ++ c->fast_unmount = 1; ++ break; ++ case Opt_norm_unmount: ++ c->mount_opts.unmount_mode = 1; ++ c->fast_unmount = 0; ++ break; ++ default: ++ ubifs_err("unrecognized mount option \"%s\" " ++ "or missing value", p); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int ubifs_get_sb(struct file_system_type *fs_type, int flags, ++ const char *name, void *data, struct vfsmount *mnt) ++{ ++ int err; ++ struct super_block *sb; ++ struct ubifs_info *c; ++ struct inode *root; ++ ++ dbg_gen("name %s, flags %#x", name, flags); ++ ++ c = kzalloc(sizeof(struct ubifs_info), GFP_KERNEL); ++ if (!c) ++ return -ENOMEM; ++ ++ spin_lock_init(&c->cnt_lock); ++ spin_lock_init(&c->cs_lock); ++ spin_lock_init(&c->buds_lock); ++ spin_lock_init(&c->space_lock); ++ spin_lock_init(&c->orphan_lock); ++ init_rwsem(&c->commit_sem); ++ mutex_init(&c->lp_mutex); ++ mutex_init(&c->tnc_mutex); ++ mutex_init(&c->log_mutex); ++ mutex_init(&c->mst_mutex); ++ mutex_init(&c->umount_mutex); ++ init_waitqueue_head(&c->cmt_wq); ++ c->buds = RB_ROOT; ++ c->old_idx = RB_ROOT; ++ c->size_tree = RB_ROOT; ++ c->orph_tree = RB_ROOT; ++ INIT_LIST_HEAD(&c->infos_list); ++ INIT_LIST_HEAD(&c->idx_gc); ++ INIT_LIST_HEAD(&c->replay_list); ++ INIT_LIST_HEAD(&c->replay_buds); ++ INIT_LIST_HEAD(&c->uncat_list); ++ INIT_LIST_HEAD(&c->empty_list); ++ INIT_LIST_HEAD(&c->freeable_list); ++ INIT_LIST_HEAD(&c->frdi_idx_list); ++ INIT_LIST_HEAD(&c->unclean_leb_list); ++ INIT_LIST_HEAD(&c->old_buds); ++ INIT_LIST_HEAD(&c->orph_list); ++ INIT_LIST_HEAD(&c->orph_new); ++ ++ c->highest_inum = UBIFS_FIRST_INO; ++ get_random_bytes(&c->vfs_gen, sizeof(int)); ++ c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM; ++ ++ err = ubifs_parse_options(c, data, 0); ++ if (err) ++ goto out_free; ++ ++ /* ++ * Get UBI device number and volume ID. Mount it read-only so far ++ * because this might be a new mount point, and UBI allows only one ++ * read-write user at a time. ++ */ ++ err = open_ubi(c, name, UBI_READONLY); ++ if (err) { ++ ubifs_err("cannot open \"%s\", error %d", name, err); ++ goto out_free; ++ } ++ ++ dbg_gen("opened ubi%d_%d", c->vi.ubi_num, c->vi.vol_id); ++ ++ sb = sget(fs_type, &sb_test, &sb_set, &c->vi.cdev); ++ if (IS_ERR(sb)) { ++ err = PTR_ERR(sb); ++ goto out_close; ++ } ++ ++ if (sb->s_root) { ++ /* A new mount point for already mounted UBIFS */ ++ dbg_gen("this ubi volume is already mounted"); ++ err = simple_set_mnt(mnt, sb); ++ goto out_close; ++ } ++ ++ /* Re-open the UBI device in read-write mode */ ++ ubi_close_volume(c->ubi); ++ c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READWRITE); ++ if (IS_ERR(c->ubi)) { ++ err = PTR_ERR(c->ubi); ++ goto out_free; ++ } ++ ++ c->vfs_sb = sb; ++ sb->s_fs_info = c; ++ sb->s_magic = UBIFS_SUPER_MAGIC; ++ sb->s_blocksize = UBIFS_BLOCK_SIZE; ++ sb->s_blocksize_bits = UBIFS_BLOCK_SHIFT; ++ sb->s_dev = c->vi.cdev; ++ sb->s_maxbytes = c->max_inode_sz = ++ min_t(uint64_t, MAX_LFS_FILESIZE, UBIFS_MAX_INODE_SZ); ++ sb->s_op = &ubifs_super_operations; ++ sb->s_flags = flags; ++ ++ mutex_lock(&c->umount_mutex); ++ err = mount_ubifs(c); ++ if (err) { ++ ubifs_assert(err < 0); ++ goto out_umount; ++ } ++ ++ /* Read the root inode */ ++ root = ubifs_iget(sb, UBIFS_ROOT_INO); ++ if (IS_ERR(root)) { ++ err = PTR_ERR(root); ++ goto out_umount; ++ } ++ ++ sb->s_root = d_alloc_root(root); ++ if (!sb->s_root) ++ goto out_iput; ++ ++ mutex_unlock(&c->umount_mutex); ++ ++ /* We do not support atime */ ++ sb->s_flags |= MS_ACTIVE | MS_NOATIME; ++ return simple_set_mnt(mnt, sb); ++ ++out_iput: ++ iput(root); ++out_umount: ++ spin_lock(&ubifs_infos_lock); ++ if (c->infos_list.next) ++ list_del(&c->infos_list); ++ spin_unlock(&ubifs_infos_lock); ++ ubifs_umount(c); ++ mutex_unlock(&c->umount_mutex); ++ up_write(&sb->s_umount); ++ sb->s_root = NULL; ++ deactivate_super(sb); ++out_close: ++ ubi_close_volume(c->ubi); ++out_free: ++ kfree(c); ++ return err; ++} ++ ++static void ubifs_kill_sb(struct super_block *sb) ++{ ++ struct ubifs_info *c = sb->s_fs_info; ++ ++ if (sb->s_root != NULL && !(sb->s_flags & MS_RDONLY)) ++ commit_on_unmount(c); ++ /* The un-mount routine is actually done in put_super() */ ++ generic_shutdown_super(sb); ++} ++ ++static struct file_system_type ubifs_fs_type = { ++ .name = "ubifs", ++ .owner = THIS_MODULE, ++ .get_sb = ubifs_get_sb, ++ .kill_sb = ubifs_kill_sb ++}; ++ ++/* ++ * Inode slab cache constructor. ++ * ++ * TODO: remove backward compatibility as late as possible ++ */ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)) ++static void inode_slab_ctor(void *obj, struct kmem_cache *cachep, ++ unsigned long flags) ++#else ++static void inode_slab_ctor(struct kmem_cache *cachep, void *obj) ++#endif ++{ ++ struct ubifs_inode *inode = obj; ++ inode_init_once(&inode->vfs_inode); ++} ++ ++static int __init ubifs_init(void) ++{ ++ int err; ++ ++ BUILD_BUG_ON(sizeof(struct ubifs_ch) != 24); ++ ++ /* Make sure node sizes are 8-byte aligned */ ++ BUILD_BUG_ON(UBIFS_CH_SZ & 7); ++ BUILD_BUG_ON(UBIFS_INO_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_DENT_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_XENT_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_DATA_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_TRUN_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_SB_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_MST_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_REF_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_CS_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_ORPH_NODE_SZ & 7); ++ ++ BUILD_BUG_ON(UBIFS_MAX_DENT_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_MAX_XENT_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_MAX_DATA_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_MAX_INO_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_MAX_NODE_SZ & 7); ++ BUILD_BUG_ON(MIN_WRITE_SZ & 7); ++ ++ /* Check min. node size */ ++ BUILD_BUG_ON(UBIFS_INO_NODE_SZ < MIN_WRITE_SZ); ++ BUILD_BUG_ON(UBIFS_DENT_NODE_SZ < MIN_WRITE_SZ); ++ BUILD_BUG_ON(UBIFS_XENT_NODE_SZ < MIN_WRITE_SZ); ++ BUILD_BUG_ON(UBIFS_TRUN_NODE_SZ < MIN_WRITE_SZ); ++ ++ BUILD_BUG_ON(UBIFS_MAX_DENT_NODE_SZ > UBIFS_MAX_NODE_SZ); ++ BUILD_BUG_ON(UBIFS_MAX_XENT_NODE_SZ > UBIFS_MAX_NODE_SZ); ++ BUILD_BUG_ON(UBIFS_MAX_DATA_NODE_SZ > UBIFS_MAX_NODE_SZ); ++ BUILD_BUG_ON(UBIFS_MAX_INO_NODE_SZ > UBIFS_MAX_NODE_SZ); ++ ++ /* We do not support multiple pages per block ATM */ ++ BUILD_BUG_ON(UBIFS_BLOCK_SIZE != PAGE_CACHE_SIZE); ++ ++ /* Defined node sizes */ ++ BUILD_BUG_ON(UBIFS_SB_NODE_SZ != 4096); ++ BUILD_BUG_ON(UBIFS_MST_NODE_SZ != 512); ++ BUILD_BUG_ON(UBIFS_INO_NODE_SZ != 160); ++ BUILD_BUG_ON(UBIFS_REF_NODE_SZ != 64); ++ ++ err = bdi_init(&ubifs_backing_dev_info); ++ if (err) ++ return err; ++ ++ err = register_filesystem(&ubifs_fs_type); ++ if (err) { ++ ubifs_err("cannot register file system, error %d", err); ++ goto out; ++ } ++ ++ err = -ENOMEM; ++ ubifs_inode_slab = kmem_cache_create("ubifs_inode_slab", ++ sizeof(struct ubifs_inode), 0, ++ SLAB_MEM_SPREAD | SLAB_RECLAIM_ACCOUNT, ++ &inode_slab_ctor UBIFSCOMPATNULL); ++ if (!ubifs_inode_slab) ++ goto out_reg; ++ ++ register_shrinker(&ubifs_shrinker_info); ++ dbg_mempressure_init(); ++ ++ err = ubifs_compressors_init(); ++ if (err) ++ goto out_compr; ++ ++ return 0; ++ ++out_compr: ++ dbg_mempressure_exit(); ++ unregister_shrinker(&ubifs_shrinker_info); ++ kmem_cache_destroy(ubifs_inode_slab); ++out_reg: ++ unregister_filesystem(&ubifs_fs_type); ++out: ++ bdi_destroy(&ubifs_backing_dev_info); ++ return err; ++} ++/* late_initcall to let compressors initialize first */ ++late_initcall(ubifs_init); ++ ++static void __exit ubifs_exit(void) ++{ ++ ubifs_assert(list_empty(&ubifs_infos)); ++ ubifs_assert(atomic_long_read(&ubifs_clean_zn_cnt) == 0); ++ ++ ubifs_compressors_exit(); ++ dbg_mempressure_exit(); ++ unregister_shrinker(&ubifs_shrinker_info); ++ kmem_cache_destroy(ubifs_inode_slab); ++ unregister_filesystem(&ubifs_fs_type); ++ bdi_destroy(&ubifs_backing_dev_info); ++ dbg_leak_report(); ++} ++module_exit(ubifs_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(__stringify(UBIFS_VERSION)); ++MODULE_AUTHOR("Artem Bityutskiy, Adrian Hunter"); ++MODULE_DESCRIPTION("UBIFS - UBI File System"); +diff -urN linux-2.6.24.7.old/fs/ubifs/commit.c linux-2.6.24.7/fs/ubifs/commit.c +--- linux-2.6.24.7.old/fs/ubifs/commit.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/commit.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,718 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy (Битюцкий Ðртём) ++ */ ++ ++/* ++ * This file implements functions that manage the running of the commit process. ++ * Each affected module has its own functions to accomplish their part in the ++ * commit and those functions are called here. ++ * ++ * The commit is the process whereby all updates to the index and LEB properties ++ * are written out together and the journal becomes empty. This keeps the ++ * file system consistent - at all times the state can be recreated by reading ++ * the index and LEB properties and then replaying the journal. ++ * ++ * The commit is split into two parts named "commit start" and "commit end". ++ * During commit start, the commit process has exclusive access to the journal ++ * by holding the commit semaphore down for writing. As few I/O operations as ++ * possible are performed during commit start, instead the nodes that are to be ++ * written are merely identified. During commit end, the commit semaphore is no ++ * longer held and the journal is again in operation, allowing users to continue ++ * to use the file system while the bulk of the commit I/O is performed. The ++ * purpose of this two-step approach is to prevent the commit from causing any ++ * latency blips. Note that in any case, the commit does not prevent lookups ++ * (as permitted by the TNC mutex), or access to VFS data structures e.g. page ++ * cache. ++ */ ++ ++#include ++#include ++#include "ubifs.h" ++ ++/** ++ * do_commit - commit the journal. ++ * @c: UBIFS file-system description object ++ * ++ * This function implements UBIFS commit. It has to be called with commit lock ++ * locked. Returns zero in case of success and a negative error code in case of ++ * failure. ++ */ ++static int do_commit(struct ubifs_info *c) ++{ ++ int err, new_ltail_lnum, old_ltail_lnum, i; ++ struct ubifs_zbranch zroot; ++ struct ubifs_lp_stats lst; ++ ++ dbg_cmt("start"); ++ if (c->ro_media) { ++ err = -EROFS; ++ goto out_up; ++ } ++ ++ c->recovery_needs_commit = 0; ++ ++ /* Sync all write buffers (necessary for recovery) */ ++ for (i = 0; i < c->jhead_cnt; i++) { ++ err = ubifs_wbuf_sync(&c->jheads[i].wbuf); ++ if (err) ++ goto out_up; ++ } ++ ++ err = ubifs_gc_start_commit(c); ++ if (err) ++ goto out_up; ++ err = dbg_check_lprops(c); ++ if (err) ++ goto out_up; ++ err = ubifs_log_start_commit(c, &new_ltail_lnum); ++ if (err) ++ goto out_up; ++ err = ubifs_tnc_start_commit(c, &zroot); ++ if (err) ++ goto out_up; ++ err = ubifs_lpt_start_commit(c); ++ if (err) ++ goto out_up; ++ err = ubifs_orphan_start_commit(c); ++ if (err) ++ goto out_up; ++ ++ ubifs_get_lp_stats(c, &lst); ++ ++ up_write(&c->commit_sem); ++ ++ err = ubifs_tnc_end_commit(c); ++ if (err) ++ goto out; ++ err = ubifs_lpt_end_commit(c); ++ if (err) ++ goto out; ++ err = ubifs_orphan_end_commit(c); ++ if (err) ++ goto out; ++ old_ltail_lnum = c->ltail_lnum; ++ err = ubifs_log_end_commit(c, new_ltail_lnum); ++ if (err) ++ goto out; ++ err = dbg_check_old_index(c, &zroot); ++ if (err) ++ goto out; ++ ++ mutex_lock(&c->mst_mutex); ++ c->mst_node->cmt_no = cpu_to_le64(++c->cmt_no); ++ c->mst_node->log_lnum = cpu_to_le32(new_ltail_lnum); ++ c->mst_node->root_lnum = cpu_to_le32(zroot.lnum); ++ c->mst_node->root_offs = cpu_to_le32(zroot.offs); ++ c->mst_node->root_len = cpu_to_le32(zroot.len); ++ c->mst_node->ihead_lnum = cpu_to_le32(c->ihead_lnum); ++ c->mst_node->ihead_offs = cpu_to_le32(c->ihead_offs); ++ c->mst_node->index_size = cpu_to_le64(c->old_idx_sz); ++ c->mst_node->lpt_lnum = cpu_to_le32(c->lpt_lnum); ++ c->mst_node->lpt_offs = cpu_to_le32(c->lpt_offs); ++ c->mst_node->nhead_lnum = cpu_to_le32(c->nhead_lnum); ++ c->mst_node->nhead_offs = cpu_to_le32(c->nhead_offs); ++ c->mst_node->ltab_lnum = cpu_to_le32(c->ltab_lnum); ++ c->mst_node->ltab_offs = cpu_to_le32(c->ltab_offs); ++ c->mst_node->lsave_lnum = cpu_to_le32(c->lsave_lnum); ++ c->mst_node->lsave_offs = cpu_to_le32(c->lsave_offs); ++ c->mst_node->lscan_lnum = cpu_to_le32(c->lscan_lnum); ++ c->mst_node->empty_lebs = cpu_to_le32(lst.empty_lebs); ++ c->mst_node->idx_lebs = cpu_to_le32(lst.idx_lebs); ++ c->mst_node->total_free = cpu_to_le64(lst.total_free); ++ c->mst_node->total_dirty = cpu_to_le64(lst.total_dirty); ++ c->mst_node->total_used = cpu_to_le64(lst.total_used); ++ c->mst_node->total_dead = cpu_to_le64(lst.total_dead); ++ c->mst_node->total_dark = cpu_to_le64(lst.total_dark); ++ if (c->no_orphs) ++ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS); ++ else ++ c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_NO_ORPHS); ++ err = ubifs_write_master(c); ++ mutex_unlock(&c->mst_mutex); ++ if (err) ++ goto out; ++ ++ err = ubifs_log_post_commit(c, old_ltail_lnum); ++ if (err) ++ goto out; ++ err = ubifs_gc_end_commit(c); ++ if (err) ++ goto out; ++ err = ubifs_lpt_post_commit(c); ++ if (err) ++ goto out; ++ ++ spin_lock(&c->cs_lock); ++ c->cmt_state = COMMIT_RESTING; ++ wake_up(&c->cmt_wq); ++ dbg_cmt("commit end"); ++ spin_unlock(&c->cs_lock); ++ ++ return 0; ++ ++out_up: ++ up_write(&c->commit_sem); ++out: ++ ubifs_err("commit failed, error %d", err); ++ spin_lock(&c->cs_lock); ++ c->cmt_state = COMMIT_BROKEN; ++ wake_up(&c->cmt_wq); ++ spin_unlock(&c->cs_lock); ++ ubifs_ro_mode(c, err); ++ return err; ++} ++ ++/** ++ * run_bg_commit - run background commit if it is needed. ++ * @c: UBIFS file-system description object ++ * ++ * This function runs background commit if it is needed. Returns zero in case ++ * of success and a negative error code in case of failure. ++ */ ++static int run_bg_commit(struct ubifs_info *c) ++{ ++ spin_lock(&c->cs_lock); ++ /* ++ * Run background commit only if background commit was requested or if ++ * commit is required. ++ */ ++ if (c->cmt_state != COMMIT_BACKGROUND && ++ c->cmt_state != COMMIT_REQUIRED) ++ goto out; ++ spin_unlock(&c->cs_lock); ++ ++ down_write(&c->commit_sem); ++ spin_lock(&c->cs_lock); ++ if (c->cmt_state == COMMIT_REQUIRED) ++ c->cmt_state = COMMIT_RUNNING_REQUIRED; ++ else if (c->cmt_state == COMMIT_BACKGROUND) ++ c->cmt_state = COMMIT_RUNNING_BACKGROUND; ++ else ++ goto out_cmt_unlock; ++ spin_unlock(&c->cs_lock); ++ ++ return do_commit(c); ++ ++out_cmt_unlock: ++ up_write(&c->commit_sem); ++out: ++ spin_unlock(&c->cs_lock); ++ return 0; ++} ++ ++/** ++ * ubifs_bg_thread - UBIFS background thread function. ++ * @info: points to the file-system description object ++ * ++ * This function implements various file-system background activities: ++ * o when a write-buffer timer expires it synchronizes the appropriate ++ * write-buffer; ++ * o when the journal is about to be full, it starts in-advance commit. ++ * ++ * Note, other stuff like background garbage collection may be added here in ++ * future. ++ */ ++int ubifs_bg_thread(void *info) ++{ ++ int err; ++ struct ubifs_info *c = info; ++ ++ ubifs_msg("background thread \"%s\" started, PID %d", ++ c->bgt_name, current->pid); ++ set_freezable(); ++ ++ while (1) { ++ if (kthread_should_stop()) ++ break; ++ ++ if (try_to_freeze()) ++ continue; ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ /* Check if there is something to do */ ++ if (!c->need_bgt) { ++ /* ++ * Nothing prevents us from going sleep now and ++ * be never woken up and block the task which ++ * could wait in 'kthread_stop()' forever. ++ */ ++ if (kthread_should_stop()) ++ break; ++ schedule(); ++ continue; ++ } else ++ __set_current_state(TASK_RUNNING); ++ ++ c->need_bgt = 0; ++ err = ubifs_bg_wbufs_sync(c); ++ if (err) ++ ubifs_ro_mode(c, err); ++ ++ run_bg_commit(c); ++ cond_resched(); ++ } ++ ++ dbg_msg("background thread \"%s\" stops", c->bgt_name); ++ return 0; ++} ++ ++/** ++ * ubifs_commit_required - set commit state to "required". ++ * @c: UBIFS file-system description object ++ * ++ * This function is called if a commit is required but cannot be done from the ++ * calling function, so it is just flagged instead. ++ */ ++void ubifs_commit_required(struct ubifs_info *c) ++{ ++ spin_lock(&c->cs_lock); ++ switch (c->cmt_state) { ++ case COMMIT_RESTING: ++ case COMMIT_BACKGROUND: ++ dbg_cmt("old: %s, new: %s", dbg_cstate(c->cmt_state), ++ dbg_cstate(COMMIT_REQUIRED)); ++ c->cmt_state = COMMIT_REQUIRED; ++ break; ++ case COMMIT_RUNNING_BACKGROUND: ++ dbg_cmt("old: %s, new: %s", dbg_cstate(c->cmt_state), ++ dbg_cstate(COMMIT_RUNNING_REQUIRED)); ++ c->cmt_state = COMMIT_RUNNING_REQUIRED; ++ break; ++ case COMMIT_REQUIRED: ++ case COMMIT_RUNNING_REQUIRED: ++ case COMMIT_BROKEN: ++ break; ++ } ++ spin_unlock(&c->cs_lock); ++} ++ ++/** ++ * ubifs_request_bg_commit - notify the background thread to do a commit. ++ * @c: UBIFS file-system description object ++ * ++ * This function is called if the journal is full enough to make a commit ++ * worthwhile, so background thread is kicked to start it. ++ */ ++void ubifs_request_bg_commit(struct ubifs_info *c) ++{ ++ spin_lock(&c->cs_lock); ++ if (c->cmt_state == COMMIT_RESTING) { ++ dbg_cmt("old: %s, new: %s", dbg_cstate(c->cmt_state), ++ dbg_cstate(COMMIT_BACKGROUND)); ++ c->cmt_state = COMMIT_BACKGROUND; ++ spin_unlock(&c->cs_lock); ++ ubifs_wake_up_bgt(c); ++ } else ++ spin_unlock(&c->cs_lock); ++} ++ ++/** ++ * wait_for_commit - wait for commit. ++ * @c: UBIFS file-system description object ++ * ++ * This function sleeps until the commit operation is no longer running. ++ */ ++static int wait_for_commit(struct ubifs_info *c) ++{ ++ dbg_cmt("pid %d goes sleep", current->pid); ++ ++ /* ++ * The following sleeps if the condition is false, and will be woken ++ * when the commit ends. It is possible, although very unlikely, that we ++ * will wake up and see the subsequent commit running, rather than the ++ * one we were waiting for, and go back to sleep. However, we will be ++ * woken again, so there is no danger of sleeping forever. ++ */ ++ wait_event(c->cmt_wq, c->cmt_state != COMMIT_RUNNING_BACKGROUND && ++ c->cmt_state != COMMIT_RUNNING_REQUIRED); ++ dbg_cmt("commit finished, pid %d woke up", current->pid); ++ return 0; ++} ++ ++/** ++ * ubifs_run_commit - run or wait for commit. ++ * @c: UBIFS file-system description object ++ * ++ * This function runs commit and returns zero in case of success and a negative ++ * error code in case of failure. ++ */ ++int ubifs_run_commit(struct ubifs_info *c) ++{ ++ int err = 0; ++ ++ spin_lock(&c->cs_lock); ++ if (c->cmt_state == COMMIT_BROKEN) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (c->cmt_state == COMMIT_RUNNING_BACKGROUND) ++ /* ++ * We set the commit state to 'running required' to indicate ++ * that we want it to complete as quickly as possible. ++ */ ++ c->cmt_state = COMMIT_RUNNING_REQUIRED; ++ ++ if (c->cmt_state == COMMIT_RUNNING_REQUIRED) { ++ spin_unlock(&c->cs_lock); ++ return wait_for_commit(c); ++ } ++ spin_unlock(&c->cs_lock); ++ ++ /* Ok, the commit is indeed needed */ ++ ++ down_write(&c->commit_sem); ++ spin_lock(&c->cs_lock); ++ /* ++ * Since we unlocked 'c->cs_lock', the state may have changed, so ++ * re-check it. ++ */ ++ if (c->cmt_state == COMMIT_BROKEN) { ++ err = -EINVAL; ++ goto out_cmt_unlock; ++ } ++ ++ if (c->cmt_state == COMMIT_RUNNING_BACKGROUND) ++ c->cmt_state = COMMIT_RUNNING_REQUIRED; ++ ++ if (c->cmt_state == COMMIT_RUNNING_REQUIRED) { ++ up_write(&c->commit_sem); ++ spin_unlock(&c->cs_lock); ++ return wait_for_commit(c); ++ } ++ c->cmt_state = COMMIT_RUNNING_REQUIRED; ++ spin_unlock(&c->cs_lock); ++ ++ err = do_commit(c); ++ return err; ++ ++out_cmt_unlock: ++ up_write(&c->commit_sem); ++out: ++ spin_unlock(&c->cs_lock); ++ return err; ++} ++ ++/** ++ * ubifs_recovery_commit - if needed, ensure a commit has run since recovery. ++ * @c: UBIFS file-system description object ++ * ++ * This function ensures that a commit has been run since recovery and before ++ * unmounting cleanly. Errors are ignored because in that case a subsequent ++ * unmount will not be clean. ++ * ++ * The recovery needs a commit when it updates TNC directly without there being ++ * a corresponding record of the change in the journal. In that case, if UBIFS ++ * were to unmount cleanly without having run a commit, the TNC changes would ++ * be lost. ++ */ ++void ubifs_recovery_commit(struct ubifs_info *c) ++{ ++ spin_lock(&c->cs_lock); ++ if (!c->recovery_needs_commit || ++ c->cmt_state == COMMIT_BROKEN || ++ c->cmt_state == COMMIT_RUNNING_BACKGROUND || ++ c->cmt_state == COMMIT_RUNNING_REQUIRED) { ++ spin_unlock(&c->cs_lock); ++ return; ++ } ++ spin_unlock(&c->cs_lock); ++ down_write(&c->commit_sem); ++ spin_lock(&c->cs_lock); ++ if (!c->recovery_needs_commit || ++ c->cmt_state == COMMIT_BROKEN || ++ c->cmt_state == COMMIT_RUNNING_BACKGROUND || ++ c->cmt_state == COMMIT_RUNNING_REQUIRED) { ++ spin_unlock(&c->cs_lock); ++ up_write(&c->commit_sem); ++ return; ++ } ++ c->cmt_state = COMMIT_RUNNING_REQUIRED; ++ spin_unlock(&c->cs_lock); ++ do_commit(c); ++} ++ ++/** ++ * ubifs_gc_should_commit - determine if it is time for GC to run commit. ++ * @c: UBIFS file-system description object ++ * ++ * This function is called by garbage collection to determine if commit should ++ * be run. If commit state is @COMMIT_BACKGROUND, which means that the journal ++ * is full enough to start commit, this function returns true. It is not ++ * absolutely necessary to commit yet, but it feels like this should be better ++ * then to keep doing GC. This function returns %1 if GC has to initiate commit ++ * and %0 if not. ++ */ ++int ubifs_gc_should_commit(struct ubifs_info *c) ++{ ++ int ret = 0; ++ ++ spin_lock(&c->cs_lock); ++ if (c->cmt_state == COMMIT_BACKGROUND) { ++ dbg_cmt("commit required now"); ++ c->cmt_state = COMMIT_REQUIRED; ++ } else ++ dbg_cmt("commit not requested"); ++ if (c->cmt_state == COMMIT_REQUIRED) ++ ret = 1; ++ spin_unlock(&c->cs_lock); ++ return ret; ++} ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ ++/** ++ * struct idx_node - hold index nodes during index tree traversal. ++ * @list: list ++ * @iip: index in parent (slot number of this indexing node in the parent ++ * indexing node) ++ * @upper_key: all keys in this indexing node have to be less or equivalent to ++ * this key ++ * @idx: index node (8-byte aligned because all node structures must be 8-byte ++ * aligned) ++ */ ++struct idx_node { ++ struct list_head list; ++ int iip; ++ union ubifs_key upper_key; ++ struct ubifs_idx_node idx __attribute__((aligned(8))); ++}; ++ ++/** ++ * dbg_old_index_check_init - get information for the next old index check. ++ * @c: UBIFS file-system description object ++ * @zroot: root of the index ++ * ++ * This function records information about the index that will be needed for the ++ * next old index check i.e. 'dbg_check_old_index()'. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int dbg_old_index_check_init(struct ubifs_info *c, struct ubifs_zbranch *zroot) ++{ ++ struct ubifs_idx_node *idx; ++ int lnum, offs, len, err = 0; ++ ++ c->old_zroot = *zroot; ++ ++ lnum = c->old_zroot.lnum; ++ offs = c->old_zroot.offs; ++ len = c->old_zroot.len; ++ ++ idx = kmalloc(c->max_idx_node_sz, GFP_NOFS); ++ if (!idx) ++ return -ENOMEM; ++ ++ err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs); ++ if (err) ++ goto out; ++ ++ c->old_zroot_level = le16_to_cpu(idx->level); ++ c->old_zroot_sqnum = le64_to_cpu(idx->ch.sqnum); ++out: ++ kfree(idx); ++ return err; ++} ++ ++/** ++ * dbg_check_old_index - check the old copy of the index. ++ * @c: UBIFS file-system description object ++ * @zroot: root of the new index ++ * ++ * In order to be able to recover from an unclean unmount, a complete copy of ++ * the index must exist on flash. This is the "old" index. The commit process ++ * must write the "new" index to flash without overwriting or destroying any ++ * part of the old index. This function is run at commit end in order to check ++ * that the old index does indeed exist completely intact. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot) ++{ ++ int lnum, offs, len, err = 0, uninitialized_var(last_level), child_cnt; ++ int first = 1, iip; ++ union ubifs_key lower_key, upper_key, l_key, u_key; ++ unsigned long long uninitialized_var(last_sqnum); ++ struct ubifs_idx_node *idx; ++ struct list_head list; ++ struct idx_node *i; ++ size_t sz; ++ ++ if (!(ubifs_chk_flags & UBIFS_CHK_OLD_IDX)) ++ goto out; ++ ++ INIT_LIST_HEAD(&list); ++ ++ sz = sizeof(struct idx_node) + ubifs_idx_node_sz(c, c->fanout) - ++ UBIFS_IDX_NODE_SZ; ++ ++ /* Start at the old zroot */ ++ lnum = c->old_zroot.lnum; ++ offs = c->old_zroot.offs; ++ len = c->old_zroot.len; ++ iip = 0; ++ ++ /* ++ * Traverse the index tree preorder depth-first i.e. do a node and then ++ * its subtrees from left to right. ++ */ ++ while (1) { ++ struct ubifs_branch *br; ++ ++ /* Get the next index node */ ++ i = kmalloc(sz, GFP_NOFS); ++ if (!i) { ++ err = -ENOMEM; ++ goto out_free; ++ } ++ i->iip = iip; ++ /* Keep the index nodes on our path in a linked list */ ++ list_add_tail(&i->list, &list); ++ /* Read the index node */ ++ idx = &i->idx; ++ err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs); ++ if (err) ++ goto out_free; ++ /* Validate index node */ ++ child_cnt = le16_to_cpu(idx->child_cnt); ++ if (child_cnt < 1 || child_cnt > c->fanout) { ++ err = 1; ++ goto out_dump; ++ } ++ if (first) { ++ first = 0; ++ /* Check root level and sqnum */ ++ if (le16_to_cpu(idx->level) != c->old_zroot_level) { ++ err = 2; ++ goto out_dump; ++ } ++ if (le64_to_cpu(idx->ch.sqnum) != c->old_zroot_sqnum) { ++ err = 3; ++ goto out_dump; ++ } ++ /* Set last values as though root had a parent */ ++ last_level = le16_to_cpu(idx->level) + 1; ++ last_sqnum = le64_to_cpu(idx->ch.sqnum) + 1; ++ key_read(c, ubifs_idx_key(c, idx), &lower_key); ++ highest_ino_key(c, &upper_key, INUM_WATERMARK); ++ } ++ key_copy(c, &upper_key, &i->upper_key); ++ if (le16_to_cpu(idx->level) != last_level - 1) { ++ err = 3; ++ goto out_dump; ++ } ++ /* ++ * The index is always written bottom up hence a child's sqnum ++ * is always less than the parents. ++ */ ++ if (le64_to_cpu(idx->ch.sqnum) >= last_sqnum) { ++ err = 4; ++ goto out_dump; ++ } ++ /* Check key range */ ++ key_read(c, ubifs_idx_key(c, idx), &l_key); ++ br = ubifs_idx_branch(c, idx, child_cnt - 1); ++ key_read(c, &br->key, &u_key); ++ if (keys_cmp(c, &lower_key, &l_key) > 0) { ++ err = 5; ++ goto out_dump; ++ } ++ if (keys_cmp(c, &upper_key, &u_key) < 0) { ++ err = 6; ++ goto out_dump; ++ } ++ if (keys_cmp(c, &upper_key, &u_key) == 0) ++ if (!is_hash_key(c, &u_key)) { ++ err = 7; ++ goto out_dump; ++ } ++ /* Go to next index node */ ++ if (le16_to_cpu(idx->level) == 0) { ++ /* At the bottom, so go up until can go right */ ++ while (1) { ++ /* Drop the bottom of the list */ ++ list_del(&i->list); ++ kfree(i); ++ /* No more list means we are done */ ++ if (list_empty(&list)) ++ goto out; ++ /* Look at the new bottom */ ++ i = list_entry(list.prev, struct idx_node, ++ list); ++ idx = &i->idx; ++ /* Can we go right */ ++ if (iip + 1 < le16_to_cpu(idx->child_cnt)) { ++ iip = iip + 1; ++ break; ++ } else ++ /* Nope, so go up again */ ++ iip = i->iip; ++ } ++ } else ++ /* Go down left */ ++ iip = 0; ++ /* ++ * We have the parent in 'idx' and now we set up for reading the ++ * child pointed to by slot 'iip'. ++ */ ++ last_level = le16_to_cpu(idx->level); ++ last_sqnum = le64_to_cpu(idx->ch.sqnum); ++ br = ubifs_idx_branch(c, idx, iip); ++ lnum = le32_to_cpu(br->lnum); ++ offs = le32_to_cpu(br->offs); ++ len = le32_to_cpu(br->len); ++ key_read(c, &br->key, &lower_key); ++ if (iip + 1 < le16_to_cpu(idx->child_cnt)) { ++ br = ubifs_idx_branch(c, idx, iip + 1); ++ key_read(c, &br->key, &upper_key); ++ } else ++ key_copy(c, &i->upper_key, &upper_key); ++ } ++out: ++ err = dbg_old_index_check_init(c, zroot); ++ if (err) ++ goto out_free; ++ ++ return 0; ++ ++out_dump: ++ dbg_err("dumping index node (iip=%d)", i->iip); ++ dbg_dump_node(c, idx); ++ list_del(&i->list); ++ kfree(i); ++ if (!list_empty(&list)) { ++ i = list_entry(list.prev, struct idx_node, list); ++ dbg_err("dumping parent index node"); ++ dbg_dump_node(c, &i->idx); ++ } ++out_free: ++ while (!list_empty(&list)) { ++ i = list_entry(list.next, struct idx_node, list); ++ list_del(&i->list); ++ kfree(i); ++ } ++ ubifs_err("failed, error %d", err); ++ if (err > 0) ++ err = -EINVAL; ++ return err; ++} ++ ++#endif /* CONFIG_UBIFS_FS_DEBUG */ +diff -urN linux-2.6.24.7.old/fs/ubifs/compat.c linux-2.6.24.7/fs/ubifs/compat.c +--- linux-2.6.24.7.old/fs/ubifs/compat.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/compat.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,480 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy ++ * Adrian Hunter ++ */ ++ ++#include "ubifs.h" ++#include ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)) ++#define BYTES_PER_LINE 32 ++ ++/** ++ * ubifs_hexdump - dump a buffer. ++ * @ptr: the buffer to dump ++ * @size: buffer size which must be multiple of 4 bytes ++ */ ++void ubifs_hexdump(const void *ptr, int size) ++{ ++ int i, k = 0, rows, columns; ++ const uint8_t *p = ptr; ++ ++ rows = size / BYTES_PER_LINE + size % BYTES_PER_LINE; ++ for (i = 0; i < rows; i++) { ++ int j; ++ ++ cond_resched(); ++ columns = min(size - k, BYTES_PER_LINE) / 4; ++ if (columns == 0) ++ break; ++ printk(KERN_DEBUG "%5d: ", i * BYTES_PER_LINE); ++ for (j = 0; j < columns; j++) { ++ int n, N; ++ ++ N = size - k > 4 ? 4 : size - k; ++ for (n = 0; n < N; n++) ++ printk("%02x", p[k++]); ++ printk(" "); ++ } ++ printk("\n"); ++ } ++} ++#endif /* LINUX_VERSION_CODE < 2.6.23 */ ++ ++#ifdef UBIFS_COMPAT_USE_OLD_IGET ++struct inode *ubifs_iget(struct super_block *sb, unsigned long inum) ++{ ++ struct inode *inode; ++ ++ inode = iget(sb, inum); ++ if (!inode) { ++ make_bad_inode(inode); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return inode; ++} ++ ++int validate_inode(struct ubifs_info *c, const struct inode *inode); ++ ++void ubifs_read_inode(struct inode *inode) ++{ ++ int err; ++ union ubifs_key key; ++ struct ubifs_ino_node *ino; ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ ++ dbg_gen("inode %lu", inode->i_ino); ++ ubifs_assert(inode->i_state & I_LOCK); ++ ++ ino = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS); ++ if (!ino) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ ino_key_init(c, &key, inode->i_ino); ++ ++ err = ubifs_tnc_lookup(c, &key, ino); ++ if (err) ++ goto out_ino; ++ ++ inode->i_flags |= (S_NOCMTIME | S_NOATIME); ++ inode->i_nlink = le32_to_cpu(ino->nlink); ++ inode->i_uid = le32_to_cpu(ino->uid); ++ inode->i_gid = le32_to_cpu(ino->gid); ++ inode->i_atime.tv_sec = (int64_t)le64_to_cpu(ino->atime_sec); ++ inode->i_atime.tv_nsec = le32_to_cpu(ino->atime_nsec); ++ inode->i_mtime.tv_sec = (int64_t)le64_to_cpu(ino->mtime_sec); ++ inode->i_mtime.tv_nsec = le32_to_cpu(ino->mtime_nsec); ++ inode->i_ctime.tv_sec = (int64_t)le64_to_cpu(ino->ctime_sec); ++ inode->i_ctime.tv_nsec = le32_to_cpu(ino->ctime_nsec); ++ inode->i_mode = le32_to_cpu(ino->mode); ++ inode->i_size = le64_to_cpu(ino->size); ++ ++ ui->data_len = le32_to_cpu(ino->data_len); ++ ui->flags = le32_to_cpu(ino->flags); ++ ui->compr_type = le16_to_cpu(ino->compr_type); ++ ui->creat_sqnum = le64_to_cpu(ino->creat_sqnum); ++ ui->xattr_cnt = le32_to_cpu(ino->xattr_cnt); ++ ui->xattr_size = le64_to_cpu(ino->xattr_size); ++ ui->xattr_names = le32_to_cpu(ino->xattr_names); ++ ++ err = validate_inode(c, inode); ++ if (err) ++ goto out_invalid; ++ ++ switch (inode->i_mode & S_IFMT) { ++ case S_IFREG: ++ inode->i_mapping->a_ops = &ubifs_file_address_operations; ++ inode->i_op = &ubifs_file_inode_operations; ++ inode->i_fop = &ubifs_file_operations; ++ if (ui->data_len != 0) { ++ err = 10; ++ goto out_invalid; ++ } ++ break; ++ case S_IFDIR: ++ inode->i_op = &ubifs_dir_inode_operations; ++ inode->i_fop = &ubifs_dir_operations; ++ if (ui->data_len != 0) { ++ err = 11; ++ goto out_invalid; ++ } ++ break; ++ case S_IFLNK: ++ inode->i_op = &ubifs_symlink_inode_operations; ++ if (ui->data_len <= 0 || ui->data_len > UBIFS_MAX_INO_DATA) { ++ err = 12; ++ goto out_invalid; ++ } ++ ui->data = kmalloc(ui->data_len + 1, GFP_KERNEL); ++ if (!ui->data) { ++ err = -ENOMEM; ++ goto out_ino; ++ } ++ memcpy(ui->data, ino->data, ui->data_len); ++ ((char *)ui->data)[ui->data_len] = '\0'; ++ break; ++ case S_IFBLK: ++ case S_IFCHR: ++ { ++ dev_t rdev; ++ union ubifs_dev_desc *dev; ++ ++ ui->data = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS); ++ if (!ui->data) { ++ err = -ENOMEM; ++ goto out_ino; ++ } ++ ++ dev = (union ubifs_dev_desc *)ino->data; ++ if (ui->data_len == sizeof(dev->new)) { ++ rdev = new_decode_dev(__le32_to_cpu(dev->new)); ++ } else if (ui->data_len == sizeof(dev->huge)) { ++ rdev = huge_decode_dev(__le64_to_cpu(dev->huge)); ++ } else { ++ err = 13; ++ goto out_invalid; ++ } ++ inode->i_op = &ubifs_file_inode_operations; ++ init_special_inode(inode, inode->i_mode, rdev); ++ break; ++ } ++ case S_IFSOCK: ++ case S_IFIFO: ++ inode->i_op = &ubifs_file_inode_operations; ++ init_special_inode(inode, inode->i_mode, 0); ++ if (ui->data_len != 0) { ++ err = 14; ++ goto out_invalid; ++ } ++ break; ++ default: ++ err = 15; ++ goto out_invalid; ++ } ++ ++ ubifs_set_inode_flags(inode); ++ kfree(ino); ++ return; ++ ++out_invalid: ++ ubifs_err("inode %lu validation failed, error %d", inode->i_ino, err); ++ dbg_dump_inode(c, inode); ++ dbg_dump_node(c, ino); ++ err = -EINVAL; ++out_ino: ++ kfree(ino); ++out: ++ ubifs_err("failed to read inode %lu, error %d", inode->i_ino, err); ++ make_bad_inode(inode); ++ return; ++} ++#endif /* UBIFS_COMPAT_USE_OLD_IGET */ ++ ++#ifdef UBIFS_COMPAT_USE_OLD_PREPARE_WRITE ++int ubifs_prepare_write(struct file *file, struct page *page, unsigned from, ++ unsigned to) ++{ ++ struct inode *inode = page->mapping->host; ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; ++ struct ubifs_budget_req req; ++ int err; ++ ++ ubifs_assert(PageLocked(page)); ++ ubifs_assert(mutex_is_locked(&inode->i_mutex)); ++ ubifs_assert(!(inode->i_sb->s_flags & MS_RDONLY)); ++ ++ if (c->ro_media) ++ return -EINVAL; ++ ++ if (!PageUptodate(page)) { ++ /* ++ * The page is not loaded from the flash and has to be loaded ++ * unless we are writing all of it. ++ */ ++ if (from == 0 && to == PAGE_CACHE_SIZE) ++ /* ++ * Set the PG_checked flag to make the further code ++ * allocate full budget, because we do not know whether ++ * the page exists on the flash media or not. ++ */ ++ SetPageChecked(page); ++ else { ++ err = do_readpage(page); ++ if (err) ++ return err; ++ } ++ ++ SetPageUptodate(page); ++ ClearPageError(page); ++ } ++ ++ memset(&req, 0, sizeof(struct ubifs_budget_req)); ++ if (!PagePrivate(page)) { ++ /* ++ * If the PG_Checked flag is set, the page corresponds to a ++ * hole or to a place beyond the inode. In this case we have to ++ * budget for a new page, otherwise for a dirtied page. ++ */ ++ if (PageChecked(page)) ++ req.new_page = 1; ++ else ++ req.dirtied_page = 1; ++ } else ++ req.locked_pg = 1; ++ ++ if (pos > inode->i_size) ++ /* ++ * We are writing beyond the file which means we are going to ++ * change inode size and make the inode dirty. And in turn, ++ * this means we have to budget for making the inode dirty. ++ * ++ * Note, if the inode is already dirty, ++ * 'ubifs_budget_inode_op()' will not allocate any budget, ++ * but will just lock the @budg_mutex of the inode to prevent ++ * it from becoming clean before we have changed its size, ++ * which is going to happen in 'ubifs_write_end()'. ++ */ ++ err = ubifs_budget_inode_op(c, inode, &req); ++ else ++ /* ++ * The inode is not going to be marked as dirty by this write ++ * operation, do do not budget for this. ++ */ ++ err = ubifs_budget_space(c, &req); ++ ++ return err; ++} ++ ++int ubifs_commit_write(struct file *file, struct page *page, unsigned from, ++ unsigned to) ++{ ++ struct inode *inode = page->mapping->host; ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; ++ ++ dbg_gen("ino %lu, pg %lu, offs %lld-%lld (in pg: %u-%u, %u bytes) " ++ "flags %#lx", inode->i_ino, page->index, pos - to + from, ++ pos, from, to, to - from, page->flags); ++ ubifs_assert(PageUptodate(page)); ++ ubifs_assert(mutex_is_locked(&inode->i_mutex)); ++ ++ if (!PagePrivate(page)) { ++ SetPagePrivate(page); ++ atomic_long_inc(&c->dirty_pg_cnt); ++ __set_page_dirty_nobuffers(page); ++ } ++ ++ if (pos > inode->i_size) { ++ i_size_write(inode, pos); ++ ++ /* ++ * Note, we do not set 'I_DIRTY_PAGES' (which means that the ++ * inode has dirty pages), this has been done in ++ * '__set_page_dirty_nobuffers()'. ++ */ ++ mark_inode_dirty_sync(inode); ++ ++ /* ++ * The inode has been marked dirty, unlock it. This is a bit ++ * hacky because normally we would have to call ++ * 'ubifs_release_ino_dirty()'. But we know there is nothing ++ * to release because page's budget will be released ++ * in 'ubifs_write_inode()', so just unlock the inode here for ++ * optimization. ++ */ ++ mutex_unlock(&ui->budg_mutex); ++ } ++ ++ return 0; ++} ++ ++#include ++ ++#define MAX_SHRINK_RETRIES 8 ++#define MAX_GC_RETRIES 4 ++#define MAX_CMT_RETRIES 2 ++#define MAX_NOSPC_RETRIES 1 ++#define NR_TO_WRITE 16 ++ ++struct retries_info { ++ long long prev_liability; ++ unsigned int shrink_cnt; ++ unsigned int shrink_retries:5; ++ unsigned int try_gc:1; ++ unsigned int gc_retries:4; ++ unsigned int cmt_retries:3; ++ unsigned int nospc_retries:1; ++}; ++ ++static int shrink_liability(struct ubifs_info *c, int nr_to_write, ++ int locked_pg) ++{ ++ struct writeback_control wbc = { ++ .sync_mode = WB_SYNC_NONE, ++ .range_end = LLONG_MAX, ++ .nr_to_write = nr_to_write, ++ .skip_locked_pages = locked_pg, ++ }; ++ ++ writeback_inodes_sb(c->vfs_sb, &wbc); ++ dbg_budg("%ld pages were written back", nr_to_write - wbc.nr_to_write); ++ return nr_to_write - wbc.nr_to_write; ++} ++ ++static int run_gc(struct ubifs_info *c) ++{ ++ int err, lnum; ++ ++ /* Make some free space by garbage-collecting dirty space */ ++ down_read(&c->commit_sem); ++ lnum = ubifs_garbage_collect(c, 1); ++ up_read(&c->commit_sem); ++ if (lnum < 0) ++ return lnum; ++ ++ /* GC freed one LEB, return it to lprops */ ++ dbg_budg("GC freed LEB %d", lnum); ++ err = ubifs_return_leb(c, lnum); ++ if (err) ++ return err; ++ ++ return 0; ++} ++ ++int ubifs_make_free_space(struct ubifs_info *c, struct retries_info *ri, ++ int locked_pg) ++{ ++ int err; ++ ++ /* ++ * If we have some dirty pages and inodes (liability), try to write ++ * them back unless this was tried too many times without effect ++ * already. ++ */ ++ if (ri->shrink_retries < MAX_SHRINK_RETRIES && !ri->try_gc) { ++ long long liability; ++ ++ spin_lock(&c->space_lock); ++ liability = c->budg_idx_growth + c->budg_data_growth + ++ c->budg_dd_growth; ++ spin_unlock(&c->space_lock); ++ ++ if (ri->prev_liability >= liability) { ++ /* Liability does not shrink, next time try GC then */ ++ ri->shrink_retries += 1; ++ if (ri->gc_retries < MAX_GC_RETRIES) ++ ri->try_gc = 1; ++ dbg_budg("liability did not shrink: retries %d of %d", ++ ri->shrink_retries, MAX_SHRINK_RETRIES); ++ } ++ ++ dbg_budg("force write-back (count %d)", ri->shrink_cnt); ++ shrink_liability(c, NR_TO_WRITE + ri->shrink_cnt, locked_pg); ++ ++ ri->prev_liability = liability; ++ ri->shrink_cnt += 1; ++ return -EAGAIN; ++ } ++ ++ /* ++ * Try to run garbage collector unless it was already tried too many ++ * times. ++ */ ++ if (ri->gc_retries < MAX_GC_RETRIES) { ++ ri->gc_retries += 1; ++ dbg_budg("run GC, retries %d of %d", ++ ri->gc_retries, MAX_GC_RETRIES); ++ ++ ri->try_gc = 0; ++ err = run_gc(c); ++ if (!err) ++ return -EAGAIN; ++ ++ if (err == -EAGAIN) { ++ dbg_budg("GC asked to commit"); ++ err = ubifs_run_commit(c); ++ if (err) ++ return err; ++ return -EAGAIN; ++ } ++ ++ if (err != -ENOSPC) ++ return err; ++ ++ /* ++ * GC could not make any progress. If this is the first time, ++ * then it makes sense to try to commit, because it might make ++ * some dirty space. ++ */ ++ dbg_budg("GC returned -ENOSPC, retries %d", ++ ri->nospc_retries); ++ if (ri->nospc_retries >= MAX_NOSPC_RETRIES) ++ return err; ++ ri->nospc_retries += 1; ++ } ++ ++ /* Neither GC nor write-back helped, try to commit */ ++ if (ri->cmt_retries < MAX_CMT_RETRIES) { ++ ri->cmt_retries += 1; ++ dbg_budg("run commit, retries %d of %d", ++ ri->cmt_retries, MAX_CMT_RETRIES); ++ err = ubifs_run_commit(c); ++ if (err) ++ return err; ++ return -EAGAIN; ++ } ++ ++ return -ENOSPC; ++} ++ ++#endif /* UBIFS_COMPAT_USE_OLD_PREPARE_WRITE */ ++ ++#ifdef UBIFS_COMPAT_NO_SHARED_MMAP ++int ubifs_file_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ return generic_file_readonly_mmap(file, vma); ++} ++#endif /* UBIFS_COMPAT_NO_SHARED_MMAP */ +diff -urN linux-2.6.24.7.old/fs/ubifs/compat.h linux-2.6.24.7/fs/ubifs/compat.h +--- linux-2.6.24.7.old/fs/ubifs/compat.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/compat.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,88 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Author: Artem Bityutskiy ++ * Adrian Hunter ++ */ ++ ++#ifndef __UBIFS_COMPAT_H__ ++#define __UBIFS_COMPAT_H__ ++ ++#include ++ ++struct inode; ++struct file; ++struct page; ++struct ubifs_info; ++struct retries_info; ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)) ++/* iget() does not exist since 2.6.25 */ ++#define UBIFS_COMPAT_USE_OLD_IGET ++void ubifs_read_inode(struct inode *inode); ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)) ++/* ++ * We have write_begin() write_end() instead of prepare_write(), commit_write() ++ * since 2.6.24. ++ */ ++#define UBIFS_COMPAT_USE_OLD_PREPARE_WRITE ++#define do_readpage(page) ubifs_do_readpage(page) ++int ubifs_do_readpage(struct page *page); ++int ubifs_prepare_write(struct file *file, struct page *page, unsigned from, ++ unsigned to); ++int ubifs_commit_write(struct file *file, struct page *page, unsigned from, ++ unsigned to); ++int ubifs_make_free_space(struct ubifs_info *c, struct retries_info *ri, ++ int locked_pg); ++#define bdi_init(x) 0 ++#define bdi_destroy(x) ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)) ++#define UBIFS_COMPAT_NO_SHRINKER ++#define register_shrinker(x) ++#define unregister_shrinker(x) ++#define dbg_mempressure_init() ++#define dbg_mempressure_exit() ++#define set_freezable() ++#define is_owner_or_cap(inode) \ ++ ((current->fsuid == (inode)->i_uid) || capable(CAP_FOWNER)) ++/* This is to hide slab cache interface changes - destructor was dropped */ ++#define UBIFSCOMPATNULL ,NULL ++#else ++#define UBIFSCOMPATNULL ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)) ++#define uninitialized_var(x) x = x ++/* print_hex_dump() did not exist in kernel prior to 2.6.22 */ ++#define print_hex_dump(a, b, c, f, e, buf, len, g) ubifs_hexdump(buf, len) ++void ubifs_hexdump(const void *ptr, int size); ++/* ++ * Older kernel have no '->page_mkwrite()' call-back in ++ * 'struct vm_operations_struct', so we just do not support writeble mmaps. The ++ * support can be added if someone needs it badly, though. ++ */ ++int ubifs_file_mmap(struct file *file, struct vm_area_struct *vma); ++#define UBIFS_COMPAT_NO_SHARED_MMAP ++#endif ++ ++ ++#endif /* !__UBIFS_COMPAT_H__ */ +diff -urN linux-2.6.24.7.old/fs/ubifs/compress.c linux-2.6.24.7/fs/ubifs/compress.c +--- linux-2.6.24.7.old/fs/ubifs/compress.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/compress.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,253 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * Copyright (C) 2006, 2007 University of Szeged, Hungary ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy (Битюцкий Ðртём) ++ * Zoltan Sogor ++ */ ++ ++/* ++ * This file provides a single place to access to compression and ++ * decompression. ++ */ ++ ++#include ++#include "ubifs.h" ++ ++/* Fake description object for the "none" compressor */ ++static struct ubifs_compressor none_compr = { ++ .compr_type = UBIFS_COMPR_NONE, ++ .name = "no compression", ++ .capi_name = "", ++}; ++ ++#ifdef CONFIG_UBIFS_FS_LZO ++static DEFINE_MUTEX(lzo_mutex); ++ ++static struct ubifs_compressor lzo_compr = { ++ .compr_type = UBIFS_COMPR_LZO, ++ .comp_mutex = &lzo_mutex, ++ .name = "LZO", ++ .capi_name = "lzo", ++}; ++#else ++static struct ubifs_compressor lzo_compr = { ++ .compr_type = UBIFS_COMPR_LZO, ++ .name = "LZO", ++}; ++#endif ++ ++#ifdef CONFIG_UBIFS_FS_ZLIB ++static DEFINE_MUTEX(deflate_mutex); ++static DEFINE_MUTEX(inflate_mutex); ++ ++static struct ubifs_compressor zlib_compr = { ++ .compr_type = UBIFS_COMPR_ZLIB, ++ .comp_mutex = &deflate_mutex, ++ .decomp_mutex = &inflate_mutex, ++ .name = "zlib", ++ .capi_name = "deflate", ++}; ++#else ++static struct ubifs_compressor zlib_compr = { ++ .compr_type = UBIFS_COMPR_ZLIB, ++ .name = "zlib", ++}; ++#endif ++ ++/* All UBIFS compressors */ ++struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT]; ++ ++/** ++ * ubifs_compress - compress data. ++ * @in_buf: data to compress ++ * @in_len: length of the data to compress ++ * @out_buf: output buffer where compressed data should be stored ++ * @out_len: output buffer length is returned here ++ * @compr_type: type of compression to use on enter, actually used compression ++ * type on exit ++ * ++ * This function compresses input buffer @in_buf of length @in_len and stores ++ * the result in the output buffer @out_buf and the resulting length in ++ * @out_len. If the input buffer does not compress, it is just copied to the ++ * @out_buf. The same happens if @compr_type is %UBIFS_COMPR_NONE or if ++ * compression error occurred. ++ * ++ * Note, if the input buffer was not compressed, it is copied to the output ++ * buffer and %UBIFS_COMPR_NONE is returned in @compr_type. ++ * ++ * This functions returns %0 on success or a negative error code on failure. ++ */ ++void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len, ++ int *compr_type) ++{ ++ int err; ++ struct ubifs_compressor *compr = ubifs_compressors[*compr_type]; ++ ++ if (*compr_type == UBIFS_COMPR_NONE) ++ goto no_compr; ++ ++ /* If the input data is small, do not even try to compress it */ ++ if (in_len < UBIFS_MIN_COMPR_LEN) ++ goto no_compr; ++ ++ if (compr->comp_mutex) ++ mutex_lock(compr->comp_mutex); ++ err = crypto_comp_compress(compr->cc, in_buf, in_len, out_buf, ++ out_len); ++ if (compr->comp_mutex) ++ mutex_unlock(compr->comp_mutex); ++ if (unlikely(err)) { ++ ubifs_warn("cannot compress %d bytes, compressor %s, " ++ "error %d, leave data uncompressed", ++ in_len, compr->name, err); ++ goto no_compr; ++ } ++ ++ /* ++ * Presently, we just require that compression results in less data, ++ * rather than any defined minimum compression ratio or amount. ++ */ ++ if (ALIGN(*out_len, 8) >= ALIGN(in_len, 8)) ++ goto no_compr; ++ ++ return; ++ ++no_compr: ++ memcpy(out_buf, in_buf, in_len); ++ *out_len = in_len; ++ *compr_type = UBIFS_COMPR_NONE; ++} ++ ++/** ++ * ubifs_decompress - decompress data. ++ * @in_buf: data to decompress ++ * @in_len: length of the data to decompress ++ * @out_buf: output buffer where decompressed data should ++ * @out_len: output length is returned here ++ * @compr_type: type of compression ++ * ++ * This function decompresses data from buffer @in_buf into buffer @out_buf. ++ * The length of the uncompressed data is returned in @out_len. This functions ++ * returns %0 on success or a negative error code on failure. ++ */ ++int ubifs_decompress(const void *in_buf, int in_len, void *out_buf, ++ int *out_len, int compr_type) ++{ ++ int err; ++ struct ubifs_compressor *compr; ++ ++ if (unlikely(compr_type < 0 || compr_type >= UBIFS_COMPR_TYPES_CNT)) { ++ ubifs_err("invalid compression type %d", compr_type); ++ return -EINVAL; ++ } ++ ++ compr = ubifs_compressors[compr_type]; ++ ++ if (unlikely(!compr->capi_name)) { ++ ubifs_err("%s compression is not compiled in", compr->name); ++ return -EINVAL; ++ } ++ ++ if (compr_type == UBIFS_COMPR_NONE) { ++ memcpy(out_buf, in_buf, in_len); ++ *out_len = in_len; ++ return 0; ++ } ++ ++ if (compr->decomp_mutex) ++ mutex_lock(compr->decomp_mutex); ++ err = crypto_comp_decompress(compr->cc, in_buf, in_len, out_buf, ++ out_len); ++ if (compr->decomp_mutex) ++ mutex_unlock(compr->decomp_mutex); ++ if (err) ++ ubifs_err("cannot decompress %d bytes, compressor %s, " ++ "error %d", in_len, compr->name, err); ++ ++ return err; ++} ++ ++/** ++ * compr_init - initialize a compressor. ++ * @compr: compressor description object ++ * ++ * This function initializes the requested compressor and returns zero in case ++ * of success or a negative error code in case of failure. ++ */ ++static int __init compr_init(struct ubifs_compressor *compr) ++{ ++ if (compr->capi_name) { ++ compr->cc = crypto_alloc_comp(compr->capi_name, 0, 0); ++ if (IS_ERR(compr->cc)) { ++ ubifs_err("cannot initialize compressor %s, error %ld", ++ compr->name, PTR_ERR(compr->cc)); ++ return PTR_ERR(compr->cc); ++ } ++ } ++ ++ ubifs_compressors[compr->compr_type] = compr; ++ return 0; ++} ++ ++/** ++ * compr_exit - de-initialize a compressor. ++ * @compr: compressor description object ++ */ ++static void compr_exit(struct ubifs_compressor *compr) ++{ ++ if (compr->capi_name) ++ crypto_free_comp(compr->cc); ++ return; ++} ++ ++/** ++ * ubifs_compressors_init - initialize UBIFS compressors. ++ * ++ * This function initializes the compressor which were compiled in. Returns ++ * zero in case of success and a negative error code in case of failure. ++ */ ++int __init ubifs_compressors_init(void) ++{ ++ int err; ++ ++ err = compr_init(&lzo_compr); ++ if (err) ++ return err; ++ ++ err = compr_init(&zlib_compr); ++ if (err) ++ goto out_lzo; ++ ++ ubifs_compressors[UBIFS_COMPR_NONE] = &none_compr; ++ return 0; ++ ++out_lzo: ++ compr_exit(&lzo_compr); ++ return err; ++} ++ ++/** ++ * ubifs_compressors_exit - de-initialize UBIFS compressors. ++ */ ++void __exit ubifs_compressors_exit(void) ++{ ++ compr_exit(&lzo_compr); ++ compr_exit(&zlib_compr); ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/debug.c linux-2.6.24.7/fs/ubifs/debug.c +--- linux-2.6.24.7.old/fs/ubifs/debug.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/debug.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1517 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This file implements most of the debugging stuff which is compiled in only ++ * when it is enabled. But some debugging check functions are implemented in ++ * corresponding subsystem, just because they are closely related and utilize ++ * various local functions of those subsystems. ++ */ ++ ++#define UBIFS_DBG_PRESERVE_UBI ++ ++#include "ubifs.h" ++#include ++#include ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ ++DEFINE_SPINLOCK(dbg_lock); ++ ++static char dbg_key_buf0[128]; ++static char dbg_key_buf1[128]; ++ ++unsigned int ubifs_msg_flags = UBIFS_MSG_FLAGS_DEFAULT; ++unsigned int ubifs_chk_flags = UBIFS_CHK_FLAGS_DEFAULT; ++unsigned int ubifs_tst_flags; ++ ++module_param_named(debug_msgs, ubifs_msg_flags, uint, S_IRUGO | S_IWUSR); ++module_param_named(debug_chks, ubifs_chk_flags, uint, S_IRUGO | S_IWUSR); ++module_param_named(debug_tsts, ubifs_tst_flags, uint, S_IRUGO | S_IWUSR); ++ ++MODULE_PARM_DESC(debug_msgs, "Debug message type flags"); ++MODULE_PARM_DESC(debug_chks, "Debug check flags"); ++MODULE_PARM_DESC(debug_tsts, "Debug special test flags"); ++ ++static const char *get_key_fmt(int fmt) ++{ ++ switch (fmt) { ++ case UBIFS_SIMPLE_KEY_FMT: ++ return "simple"; ++ default: ++ return "unknown/invalid format"; ++ } ++} ++ ++static const char *get_key_hash(int hash) ++{ ++ switch (hash) { ++ case UBIFS_KEY_HASH_R5: ++ return "R5"; ++ case UBIFS_KEY_HASH_TEST: ++ return "test"; ++ default: ++ return "unknown/invalid name hash"; ++ } ++} ++ ++static const char *get_key_type(int type) ++{ ++ switch (type) { ++ case UBIFS_INO_KEY: ++ return "inode"; ++ case UBIFS_DENT_KEY: ++ return "direntry"; ++ case UBIFS_XENT_KEY: ++ return "xentry"; ++ case UBIFS_DATA_KEY: ++ return "data"; ++ case UBIFS_TRUN_KEY: ++ return "truncate"; ++ default: ++ return "unknown/invalid key"; ++ } ++} ++ ++static void sprintf_key(const struct ubifs_info *c, const union ubifs_key *key, ++ char *buffer) ++{ ++ char *p = buffer; ++ int type = key_type(c, key); ++ ++ if (c->key_fmt == UBIFS_SIMPLE_KEY_FMT) { ++ switch (type) { ++ case UBIFS_INO_KEY: ++ sprintf(p, "(%lu, %s)", key_ino(c, key), ++ get_key_type(type)); ++ break; ++ case UBIFS_DENT_KEY: ++ case UBIFS_XENT_KEY: ++ sprintf(p, "(%lu, %s, %#08x)", key_ino(c, key), ++ get_key_type(type), key_hash(c, key)); ++ break; ++ case UBIFS_DATA_KEY: ++ sprintf(p, "(%lu, %s, %u)", key_ino(c, key), ++ get_key_type(type), key_block(c, key)); ++ break; ++ case UBIFS_TRUN_KEY: ++ sprintf(p, "(%lu, %s)", ++ key_ino(c, key), get_key_type(type)); ++ break; ++ default: ++ sprintf(p, "(bad key type: %#08x, %#08x)", ++ key->u32[0], key->u32[1]); ++ } ++ } else ++ sprintf(p, "bad key format %d", c->key_fmt); ++} ++ ++const char *dbg_key_str0(const struct ubifs_info *c, const union ubifs_key *key) ++{ ++ /* dbg_lock must be held */ ++ sprintf_key(c, key, dbg_key_buf0); ++ return dbg_key_buf0; ++} ++ ++const char *dbg_key_str1(const struct ubifs_info *c, const union ubifs_key *key) ++{ ++ /* dbg_lock must be held */ ++ sprintf_key(c, key, dbg_key_buf1); ++ return dbg_key_buf1; ++} ++ ++const char *dbg_ntype(int type) ++{ ++ switch (type) { ++ case UBIFS_PAD_NODE: ++ return "padding node"; ++ case UBIFS_SB_NODE: ++ return "superblock node"; ++ case UBIFS_MST_NODE: ++ return "master node"; ++ case UBIFS_REF_NODE: ++ return "reference node"; ++ case UBIFS_INO_NODE: ++ return "inode node"; ++ case UBIFS_DENT_NODE: ++ return "direntry node"; ++ case UBIFS_XENT_NODE: ++ return "xentry node"; ++ case UBIFS_DATA_NODE: ++ return "data node"; ++ case UBIFS_TRUN_NODE: ++ return "truncate node"; ++ case UBIFS_IDX_NODE: ++ return "indexing node"; ++ case UBIFS_CS_NODE: ++ return "commit start node"; ++ case UBIFS_ORPH_NODE: ++ return "orphan node"; ++ default: ++ return "unknown node"; ++ } ++} ++ ++static const char *dbg_gtype(int type) ++{ ++ switch (type) { ++ case UBIFS_NO_NODE_GROUP: ++ return "no node group"; ++ case UBIFS_IN_NODE_GROUP: ++ return "in node group"; ++ case UBIFS_LAST_OF_NODE_GROUP: ++ return "last of node group"; ++ default: ++ return "unknown"; ++ } ++} ++ ++const char *dbg_cstate(int cmt_state) ++{ ++ switch (cmt_state) { ++ case COMMIT_RESTING: ++ return "commit resting"; ++ case COMMIT_BACKGROUND: ++ return "background commit requested"; ++ case COMMIT_REQUIRED: ++ return "commit required"; ++ case COMMIT_RUNNING_BACKGROUND: ++ return "BACKGROUND commit running"; ++ case COMMIT_RUNNING_REQUIRED: ++ return "commit running and required"; ++ case COMMIT_BROKEN: ++ return "broken commit"; ++ default: ++ return "unknown commit state"; ++ } ++} ++ ++static void dump_ch(const struct ubifs_ch *ch) ++{ ++ printk(KERN_DEBUG "\tmagic %#x\n", le32_to_cpu(ch->magic)); ++ printk(KERN_DEBUG "\tcrc %#x\n", le32_to_cpu(ch->crc)); ++ printk(KERN_DEBUG "\tnode_type %d (%s)\n", ch->node_type, ++ dbg_ntype(ch->node_type)); ++ printk(KERN_DEBUG "\tgroup_type %d (%s)\n", ch->group_type, ++ dbg_gtype(ch->group_type)); ++ printk(KERN_DEBUG "\tsqnum %llu\n", ++ (unsigned long long)le64_to_cpu(ch->sqnum)); ++ printk(KERN_DEBUG "\tlen %u\n", le32_to_cpu(ch->len)); ++} ++ ++void dbg_dump_inode(const struct ubifs_info *c, const struct inode *inode) ++{ ++ const struct ubifs_inode *ui = ubifs_inode(inode); ++ ++ printk(KERN_DEBUG "inode %lu\n", inode->i_ino); ++ printk(KERN_DEBUG "size %llu\n", ++ (unsigned long long)i_size_read(inode)); ++ printk(KERN_DEBUG "nlink %u\n", inode->i_nlink); ++ printk(KERN_DEBUG "uid %u\n", (unsigned int)inode->i_uid); ++ printk(KERN_DEBUG "gid %u\n", (unsigned int)inode->i_gid); ++ printk(KERN_DEBUG "atime %u.%u\n", ++ (unsigned int)inode->i_atime.tv_sec, ++ (unsigned int)inode->i_atime.tv_nsec); ++ printk(KERN_DEBUG "mtime %u.%u\n", ++ (unsigned int)inode->i_mtime.tv_sec, ++ (unsigned int)inode->i_mtime.tv_nsec); ++ printk(KERN_DEBUG "ctime %u.%u\n", ++ (unsigned int)inode->i_ctime.tv_sec, ++ (unsigned int)inode->i_ctime.tv_nsec); ++ printk(KERN_DEBUG "creat_sqnum %llu\n", ui->creat_sqnum); ++ printk(KERN_DEBUG "xattr_size %lld\n", ui->xattr_size); ++ printk(KERN_DEBUG "xattr_cnt %d\n", ui->xattr_cnt); ++ printk(KERN_DEBUG "xattr_names %d\n", ui->xattr_names); ++ printk(KERN_DEBUG "dirty %u\n", ui->dirty); ++ printk(KERN_DEBUG "xattr %u\n", ui->xattr); ++ printk(KERN_DEBUG "flags %d\n", ui->flags); ++ printk(KERN_DEBUG "compr_type %d\n", ui->compr_type); ++ printk(KERN_DEBUG "data_len %d\n", ui->data_len); ++} ++ ++void dbg_dump_node(const struct ubifs_info *c, const void *node) ++{ ++ int i, n; ++ union ubifs_key key; ++ const struct ubifs_ch *ch = node; ++ ++ if (dbg_failure_mode) ++ return; ++ ++ /* If the magic is incorrect, just hexdump the first bytes */ ++ if (le32_to_cpu(ch->magic) != UBIFS_NODE_MAGIC) { ++ printk(KERN_DEBUG "Not a node, first %zu bytes:", UBIFS_CH_SZ); ++ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, ++ (void *)node, UBIFS_CH_SZ, 1); ++ return; ++ } ++ ++ spin_lock(&dbg_lock); ++ dump_ch(node); ++ ++ switch (ch->node_type) { ++ case UBIFS_PAD_NODE: ++ { ++ const struct ubifs_pad_node *pad = node; ++ ++ printk(KERN_DEBUG "\tpad_len %u\n", ++ le32_to_cpu(pad->pad_len)); ++ break; ++ } ++ case UBIFS_SB_NODE: ++ { ++ const struct ubifs_sb_node *sup = node; ++ unsigned int sup_flags = le32_to_cpu(sup->flags); ++ ++ printk(KERN_DEBUG "\tkey_hash %d (%s)\n", ++ (int)sup->key_hash, get_key_hash(sup->key_hash)); ++ printk(KERN_DEBUG "\tkey_fmt %d (%s)\n", ++ (int)sup->key_fmt, get_key_fmt(sup->key_fmt)); ++ printk(KERN_DEBUG "\tflags %#x\n", sup_flags); ++ printk(KERN_DEBUG "\t big_lpt %u\n", ++ !!(sup_flags & UBIFS_FLG_BIGLPT)); ++ printk(KERN_DEBUG "\tmin_io_size %u\n", ++ le32_to_cpu(sup->min_io_size)); ++ printk(KERN_DEBUG "\tleb_size %u\n", ++ le32_to_cpu(sup->leb_size)); ++ printk(KERN_DEBUG "\tleb_cnt %u\n", ++ le32_to_cpu(sup->leb_cnt)); ++ printk(KERN_DEBUG "\tmax_leb_cnt %u\n", ++ le32_to_cpu(sup->max_leb_cnt)); ++ printk(KERN_DEBUG "\tmax_bud_bytes %llu\n", ++ (unsigned long long)le64_to_cpu(sup->max_bud_bytes)); ++ printk(KERN_DEBUG "\tlog_lebs %u\n", ++ le32_to_cpu(sup->log_lebs)); ++ printk(KERN_DEBUG "\tlpt_lebs %u\n", ++ le32_to_cpu(sup->lpt_lebs)); ++ printk(KERN_DEBUG "\torph_lebs %u\n", ++ le32_to_cpu(sup->orph_lebs)); ++ printk(KERN_DEBUG "\tjhead_cnt %u\n", ++ le32_to_cpu(sup->jhead_cnt)); ++ printk(KERN_DEBUG "\tfanout %u\n", ++ le32_to_cpu(sup->fanout)); ++ printk(KERN_DEBUG "\tlsave_cnt %u\n", ++ le32_to_cpu(sup->lsave_cnt)); ++ printk(KERN_DEBUG "\tdefault_compr %u\n", ++ (int)le16_to_cpu(sup->default_compr)); ++ printk(KERN_DEBUG "\trp_size %llu\n", ++ (unsigned long long)le64_to_cpu(sup->rp_size)); ++ printk(KERN_DEBUG "\trp_uid %u\n", ++ le32_to_cpu(sup->rp_uid)); ++ printk(KERN_DEBUG "\trp_gid %u\n", ++ le32_to_cpu(sup->rp_gid)); ++ printk(KERN_DEBUG "\tfmt_version %u\n", ++ le32_to_cpu(sup->fmt_version)); ++ printk(KERN_DEBUG "\ttime_gran %u\n", ++ le32_to_cpu(sup->time_gran)); ++ break; ++ } ++ case UBIFS_MST_NODE: ++ { ++ const struct ubifs_mst_node *mst = node; ++ ++ printk(KERN_DEBUG "\thighest_inum %llu\n", ++ (unsigned long long)le64_to_cpu(mst->highest_inum)); ++ printk(KERN_DEBUG "\tcommit number %llu\n", ++ (unsigned long long)le64_to_cpu(mst->cmt_no)); ++ printk(KERN_DEBUG "\tflags %#x\n", ++ le32_to_cpu(mst->flags)); ++ printk(KERN_DEBUG "\tlog_lnum %u\n", ++ le32_to_cpu(mst->log_lnum)); ++ printk(KERN_DEBUG "\troot_lnum %u\n", ++ le32_to_cpu(mst->root_lnum)); ++ printk(KERN_DEBUG "\troot_offs %u\n", ++ le32_to_cpu(mst->root_offs)); ++ printk(KERN_DEBUG "\troot_len %u\n", ++ le32_to_cpu(mst->root_len)); ++ printk(KERN_DEBUG "\tgc_lnum %u\n", ++ le32_to_cpu(mst->gc_lnum)); ++ printk(KERN_DEBUG "\tihead_lnum %u\n", ++ le32_to_cpu(mst->ihead_lnum)); ++ printk(KERN_DEBUG "\tihead_offs %u\n", ++ le32_to_cpu(mst->ihead_offs)); ++ printk(KERN_DEBUG "\tindex_size %u\n", ++ le32_to_cpu(mst->index_size)); ++ printk(KERN_DEBUG "\tlpt_lnum %u\n", ++ le32_to_cpu(mst->lpt_lnum)); ++ printk(KERN_DEBUG "\tlpt_offs %u\n", ++ le32_to_cpu(mst->lpt_offs)); ++ printk(KERN_DEBUG "\tnhead_lnum %u\n", ++ le32_to_cpu(mst->nhead_lnum)); ++ printk(KERN_DEBUG "\tnhead_offs %u\n", ++ le32_to_cpu(mst->nhead_offs)); ++ printk(KERN_DEBUG "\tltab_lnum %u\n", ++ le32_to_cpu(mst->ltab_lnum)); ++ printk(KERN_DEBUG "\tltab_offs %u\n", ++ le32_to_cpu(mst->ltab_offs)); ++ printk(KERN_DEBUG "\tlsave_lnum %u\n", ++ le32_to_cpu(mst->lsave_lnum)); ++ printk(KERN_DEBUG "\tlsave_offs %u\n", ++ le32_to_cpu(mst->lsave_offs)); ++ printk(KERN_DEBUG "\tlscan_lnum %u\n", ++ le32_to_cpu(mst->lscan_lnum)); ++ printk(KERN_DEBUG "\tleb_cnt %u\n", ++ le32_to_cpu(mst->leb_cnt)); ++ printk(KERN_DEBUG "\tempty_lebs %u\n", ++ le32_to_cpu(mst->empty_lebs)); ++ printk(KERN_DEBUG "\tidx_lebs %u\n", ++ le32_to_cpu(mst->idx_lebs)); ++ printk(KERN_DEBUG "\ttotal_free %llu\n", ++ (unsigned long long)le64_to_cpu(mst->total_free)); ++ printk(KERN_DEBUG "\ttotal_dirty %llu\n", ++ (unsigned long long)le64_to_cpu(mst->total_dirty)); ++ printk(KERN_DEBUG "\ttotal_used %llu\n", ++ (unsigned long long)le64_to_cpu(mst->total_used)); ++ printk(KERN_DEBUG "\ttotal_dead %llu\n", ++ (unsigned long long)le64_to_cpu(mst->total_dead)); ++ printk(KERN_DEBUG "\ttotal_dark %llu\n", ++ (unsigned long long)le64_to_cpu(mst->total_dark)); ++ break; ++ } ++ case UBIFS_REF_NODE: ++ { ++ const struct ubifs_ref_node *ref = node; ++ ++ printk(KERN_DEBUG "\tlnum %u\n", ++ le32_to_cpu(ref->lnum)); ++ printk(KERN_DEBUG "\toffs %u\n", ++ le32_to_cpu(ref->offs)); ++ printk(KERN_DEBUG "\tjhead %u\n", ++ le32_to_cpu(ref->jhead)); ++ break; ++ } ++ case UBIFS_INO_NODE: ++ { ++ const struct ubifs_ino_node *ino = node; ++ ++ key_read(c, &ino->key, &key); ++ printk(KERN_DEBUG "\tkey %s\n", DBGKEY(&key)); ++ printk(KERN_DEBUG "\tsize %llu\n", ++ (unsigned long long)le64_to_cpu(ino->size)); ++ printk(KERN_DEBUG "\tnlink %u\n", ++ le32_to_cpu(ino->nlink)); ++ printk(KERN_DEBUG "\tatime %lld.%u\n", ++ (long long)le64_to_cpu(ino->atime_sec), ++ le32_to_cpu(ino->atime_nsec)); ++ printk(KERN_DEBUG "\tmtime %lld.%u\n", ++ (long long)le64_to_cpu(ino->mtime_sec), ++ le32_to_cpu(ino->mtime_nsec)); ++ printk(KERN_DEBUG "\tctime %lld.%u\n", ++ (long long)le64_to_cpu(ino->ctime_sec), ++ le32_to_cpu(ino->ctime_nsec)); ++ printk(KERN_DEBUG "\tuid %u\n", ++ le32_to_cpu(ino->uid)); ++ printk(KERN_DEBUG "\tgid %u\n", ++ le32_to_cpu(ino->gid)); ++ printk(KERN_DEBUG "\tmode %u\n", ++ le32_to_cpu(ino->mode)); ++ printk(KERN_DEBUG "\tflags %#x\n", ++ le32_to_cpu(ino->flags)); ++ printk(KERN_DEBUG "\txattr_cnt %u\n", ++ le32_to_cpu(ino->xattr_cnt)); ++ printk(KERN_DEBUG "\txattr_size %llu\n", ++ (unsigned long long)le64_to_cpu(ino->xattr_size)); ++ printk(KERN_DEBUG "\txattr_names %u\n", ++ le32_to_cpu(ino->xattr_names)); ++ printk(KERN_DEBUG "\tcompr_type %#x\n", ++ (int)le16_to_cpu(ino->compr_type)); ++ printk(KERN_DEBUG "\tdata len %u\n", ++ le32_to_cpu(ino->data_len)); ++ break; ++ } ++ case UBIFS_DENT_NODE: ++ case UBIFS_XENT_NODE: ++ { ++ const struct ubifs_dent_node *dent = node; ++ int nlen = le16_to_cpu(dent->nlen); ++ ++ key_read(c, &dent->key, &key); ++ printk(KERN_DEBUG "\tkey %s\n", DBGKEY(&key)); ++ printk(KERN_DEBUG "\tinum %llu\n", ++ (unsigned long long)le64_to_cpu(dent->inum)); ++ printk(KERN_DEBUG "\ttype %d\n", (int)dent->type); ++ printk(KERN_DEBUG "\tnlen %d\n", nlen); ++ printk(KERN_DEBUG "\tname "); ++ ++ if (nlen > UBIFS_MAX_NLEN) { ++ nlen = UBIFS_MAX_NLEN; ++ printk(KERN_DEBUG "\tWarning! Node is corrupted\n"); ++ } ++ ++ for (i = 0; i < nlen && dent->name[i]; i++) ++ printk("%c", dent->name[i]); ++ printk("\n"); ++ ++ break; ++ } ++ case UBIFS_DATA_NODE: ++ { ++ const struct ubifs_data_node *dn = node; ++ int dlen = le32_to_cpu(ch->len) - UBIFS_DATA_NODE_SZ; ++ ++ key_read(c, &dn->key, &key); ++ printk(KERN_DEBUG "\tkey %s\n", DBGKEY(&key)); ++ printk(KERN_DEBUG "\tsize %u\n", ++ le32_to_cpu(dn->size)); ++ printk(KERN_DEBUG "\tcompr_typ %d\n", ++ (int)le16_to_cpu(dn->compr_type)); ++ printk(KERN_DEBUG "\tdata size %d\n", ++ dlen); ++ printk(KERN_DEBUG "\tdata:\n"); ++ print_hex_dump(KERN_DEBUG, "\t", DUMP_PREFIX_OFFSET, 32, 1, ++ (void *)&dn->data, dlen, 0); ++ break; ++ } ++ case UBIFS_TRUN_NODE: ++ { ++ const struct ubifs_trun_node *trun = node; ++ ++ key_read(c, &trun->key, &key); ++ printk(KERN_DEBUG "\tkey %s\n", DBGKEY(&key)); ++ printk(KERN_DEBUG "\told_size %llu\n", ++ (unsigned long long)le64_to_cpu(trun->old_size)); ++ printk(KERN_DEBUG "\tnew_size %llu\n", ++ (unsigned long long)le64_to_cpu(trun->new_size)); ++ break; ++ } ++ case UBIFS_IDX_NODE: ++ { ++ const struct ubifs_idx_node *idx = node; ++ ++ n = le16_to_cpu(idx->child_cnt); ++ printk(KERN_DEBUG "\tchild_cnt %d\n", n); ++ printk(KERN_DEBUG "\tlevel %d\n", ++ (int)le16_to_cpu(idx->level)); ++ printk(KERN_DEBUG "\tBranches:\n"); ++ ++ for (i = 0; i < n && i < c->fanout - 1; i++) { ++ const struct ubifs_branch *br; ++ ++ br = ubifs_idx_branch(c, idx, i); ++ key_read(c, &br->key, &key); ++ printk(KERN_DEBUG "\t%d: LEB %d:%d len %d key %s\n", ++ i, le32_to_cpu(br->lnum), le32_to_cpu(br->offs), ++ le32_to_cpu(br->len), DBGKEY(&key)); ++ } ++ break; ++ } ++ case UBIFS_CS_NODE: ++ break; ++ case UBIFS_ORPH_NODE: ++ { ++ const struct ubifs_orph_node *orph = node; ++ ++ printk(KERN_DEBUG "\tcommit number %llu\n", ++ (unsigned long long) ++ le64_to_cpu(orph->cmt_no) & LLONG_MAX); ++ printk(KERN_DEBUG "\tlast node flag %llu\n", ++ (unsigned long long)(le64_to_cpu(orph->cmt_no)) >> 63); ++ n = (le32_to_cpu(ch->len) - UBIFS_ORPH_NODE_SZ) >> 3; ++ printk(KERN_DEBUG "\t%d orphan inode numbers:\n", n); ++ for (i = 0; i < n; i++) ++ printk(KERN_DEBUG "\t ino %llu\n", ++ le64_to_cpu(orph->inos[i])); ++ break; ++ } ++ default: ++ printk(KERN_DEBUG "node type %d was not recognized\n", ++ (int)ch->node_type); ++ } ++ spin_unlock(&dbg_lock); ++} ++ ++void dbg_dump_budget_req(const struct ubifs_budget_req *req) ++{ ++ spin_lock(&dbg_lock); ++ printk(KERN_DEBUG "Budgeting request: new_ino %d, dirtied_ino %d\n", ++ req->new_ino, req->dirtied_ino); ++ printk(KERN_DEBUG "\tnew_ino_d %d, dirtied_ino_d %d\n", ++ req->new_ino_d, req->dirtied_ino_d); ++ printk(KERN_DEBUG "\tnew_page %d, dirtied_page %d\n", ++ req->new_page, req->dirtied_page); ++ printk(KERN_DEBUG "\tnew_dent %d, mod_dent %d\n", ++ req->new_dent, req->mod_dent); ++/* TODO: remove compatibility stuff as late as possible */ ++#ifdef UBIFS_COMPAT_USE_OLD_PREPARE_WRITE ++ printk(KERN_DEBUG "\tlocked_pg %d idx_growth %d\n", ++ req->locked_pg, req->idx_growth); ++#else ++ printk(KERN_DEBUG "\tidx_growth %d\n", req->idx_growth); ++#endif ++ printk(KERN_DEBUG "\tdata_growth %d dd_growth %d\n", ++ req->data_growth, req->dd_growth); ++ spin_unlock(&dbg_lock); ++} ++ ++void dbg_dump_lstats(const struct ubifs_lp_stats *lst) ++{ ++ spin_lock(&dbg_lock); ++ printk(KERN_DEBUG "Lprops statistics: empty_lebs %d, idx_lebs %d\n", ++ lst->empty_lebs, lst->idx_lebs); ++ printk(KERN_DEBUG "\ttaken_empty_lebs %d, total_free %lld, " ++ "total_dirty %lld\n", lst->taken_empty_lebs, lst->total_free, ++ lst->total_dirty); ++ printk(KERN_DEBUG "\ttotal_used %lld, total_dark %lld, " ++ "total_dead %lld\n", lst->total_used, lst->total_dark, ++ lst->total_dead); ++ spin_unlock(&dbg_lock); ++} ++ ++void dbg_dump_budg(struct ubifs_info *c) ++{ ++ int i; ++ struct rb_node *rb; ++ struct ubifs_bud *bud; ++ struct ubifs_gced_idx_leb *idx_gc; ++ ++ spin_lock(&dbg_lock); ++ printk(KERN_DEBUG "Budgeting info: budg_data_growth %lld, " ++ "budg_dd_growth %lld, budg_idx_growth %lld\n", ++ c->budg_data_growth, c->budg_dd_growth, c->budg_idx_growth); ++ printk(KERN_DEBUG "\tdata budget sum %lld, total budget sum %lld, " ++ "freeable_cnt %d\n", c->budg_data_growth + c->budg_dd_growth, ++ c->budg_data_growth + c->budg_dd_growth + c->budg_idx_growth, ++ c->freeable_cnt); ++ printk(KERN_DEBUG "\tmin_idx_lebs %d, old_idx_sz %lld, " ++ "calc_idx_sz %lld, idx_gc_cnt %d\n", c->min_idx_lebs, ++ c->old_idx_sz, c->calc_idx_sz, c->idx_gc_cnt); ++ printk(KERN_DEBUG "\tdirty_pg_cnt %ld, dirty_ino_cnt %ld, " ++ "dirty_zn_cnt %ld, clean_zn_cnt %ld\n", ++ atomic_long_read(&c->dirty_pg_cnt), ++ atomic_long_read(&c->dirty_ino_cnt), ++ atomic_long_read(&c->dirty_zn_cnt), ++ atomic_long_read(&c->clean_zn_cnt)); ++ printk(KERN_DEBUG "\tdark_wm %d, dead_wm %d, max_idx_node_sz %d\n", ++ c->dark_wm, c->dead_wm, c->max_idx_node_sz); ++ printk(KERN_DEBUG "\tgc_lnum %d, ihead_lnum %d\n", ++ c->gc_lnum, c->ihead_lnum); ++ for (i = 0; i < c->jhead_cnt; i++) ++ printk(KERN_DEBUG "\tjhead %d\t LEB %d\n", ++ c->jheads[i].wbuf.jhead, c->jheads[i].wbuf.lnum); ++ for (rb = rb_first(&c->buds); rb; rb = rb_next(rb)) { ++ bud = rb_entry(rb, struct ubifs_bud, rb); ++ printk(KERN_DEBUG "\tbud LEB %d\n", bud->lnum); ++ } ++ list_for_each_entry(bud, &c->old_buds, list) ++ printk(KERN_DEBUG "\told bud LEB %d\n", bud->lnum); ++ list_for_each_entry(idx_gc, &c->idx_gc, list) ++ printk(KERN_DEBUG "\tGC'ed idx LEB %d unmap %d\n", ++ idx_gc->lnum, idx_gc->unmap); ++ printk(KERN_DEBUG "\tcommit state %d\n", c->cmt_state); ++ spin_unlock(&dbg_lock); ++} ++ ++void dbg_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp) ++{ ++ printk(KERN_DEBUG "LEB %d lprops: free %d, dirty %d (used %d), " ++ "flags %#x\n", lp->lnum, lp->free, lp->dirty, ++ c->leb_size - lp->free - lp->dirty, lp->flags); ++} ++ ++void dbg_dump_lprops(struct ubifs_info *c) ++{ ++ int lnum, err; ++ struct ubifs_lprops lp; ++ struct ubifs_lp_stats lst; ++ ++ printk(KERN_DEBUG "Dumping LEB properties\n"); ++ ubifs_get_lp_stats(c, &lst); ++ dbg_dump_lstats(&lst); ++ ++ for (lnum = c->main_first; lnum < c->leb_cnt; lnum++) { ++ err = ubifs_read_one_lp(c, lnum, &lp); ++ if (err) ++ ubifs_err("cannot read lprops for LEB %d", lnum); ++ ++ dbg_dump_lprop(c, &lp); ++ } ++} ++ ++void dbg_dump_leb(const struct ubifs_info *c, int lnum) ++{ ++ struct ubifs_scan_leb *sleb; ++ struct ubifs_scan_node *snod; ++ ++ if (dbg_failure_mode) ++ return; ++ ++ printk(KERN_DEBUG "Dumping LEB %d\n", lnum); ++ ++ sleb = ubifs_scan(c, lnum, 0, c->dbg_buf); ++ if (IS_ERR(sleb)) { ++ ubifs_err("scan error %d", (int)PTR_ERR(sleb)); ++ return; ++ } ++ ++ printk(KERN_DEBUG "LEB %d has %d nodes ending at %d\n", lnum, ++ sleb->nodes_cnt, sleb->endpt); ++ ++ list_for_each_entry(snod, &sleb->nodes, list) { ++ cond_resched(); ++ printk(KERN_DEBUG "Dumping node at LEB %d:%d len %d\n", lnum, ++ snod->offs, snod->len); ++ dbg_dump_node(c, snod->node); ++ } ++ ++ ubifs_scan_destroy(sleb); ++ return; ++} ++ ++void dbg_dump_znode(const struct ubifs_info *c, ++ const struct ubifs_znode *znode) ++{ ++ int n; ++ const struct ubifs_zbranch *zbr; ++ ++ spin_lock(&dbg_lock); ++ if (znode->parent) ++ zbr = &znode->parent->zbranch[znode->iip]; ++ else ++ zbr = &c->zroot; ++ ++ printk(KERN_DEBUG "znode %p, LEB %d:%d len %d parent %p iip %d level %d" ++ " child_cnt %d flags %lx\n", znode, zbr->lnum, zbr->offs, ++ zbr->len, znode->parent, znode->iip, znode->level, ++ znode->child_cnt, znode->flags); ++ ++ if (znode->child_cnt <= 0 || znode->child_cnt > c->fanout) { ++ spin_unlock(&dbg_lock); ++ return; ++ } ++ ++ printk(KERN_DEBUG "zbranches:\n"); ++ for (n = 0; n < znode->child_cnt; n++) { ++ cond_resched(); ++ ++ zbr = &znode->zbranch[n]; ++ if (znode->level > 0) ++ printk(KERN_DEBUG "\t%d: znode %p LEB %d:%d len %d key " ++ "%s\n", n, zbr->znode, zbr->lnum, ++ zbr->offs, zbr->len, ++ DBGKEY(&zbr->key)); ++ else ++ printk(KERN_DEBUG "\t%d: LNC %p LEB %d:%d len %d key " ++ "%s\n", n, zbr->znode, zbr->lnum, ++ zbr->offs, zbr->len, ++ DBGKEY(&zbr->key)); ++ } ++ spin_unlock(&dbg_lock); ++} ++ ++void dbg_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat) ++{ ++ int i; ++ ++ printk(KERN_DEBUG "Dumping heap cat %d (%d elements)\n", ++ cat, heap->cnt); ++ for (i = 0; i < heap->cnt; i++) { ++ struct ubifs_lprops *lprops = heap->arr[i]; ++ ++ printk(KERN_DEBUG "\t%d. LEB %d hpos %d free %d dirty %d " ++ "flags %d\n", i, lprops->lnum, lprops->hpos, ++ lprops->free, lprops->dirty, lprops->flags); ++ } ++} ++ ++void dbg_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, ++ struct ubifs_nnode *parent, int iip) ++{ ++ int i; ++ ++ printk(KERN_DEBUG "Dumping pnode:\n"); ++ printk(KERN_DEBUG "\taddress %zx parent %zx cnext %zx\n", ++ (size_t)pnode, (size_t)parent, (size_t)pnode->cnext); ++ printk(KERN_DEBUG "\tflags %lu iip %d level %d num %d\n", ++ pnode->flags, iip, pnode->level, pnode->num); ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ struct ubifs_lprops *lp = &pnode->lprops[i]; ++ ++ printk(KERN_DEBUG "\t%d: free %d dirty %d flags %d lnum %d\n", ++ i, lp->free, lp->dirty, lp->flags, lp->lnum); ++ } ++} ++ ++void dbg_dump_tnc(struct ubifs_info *c) ++{ ++ struct ubifs_znode *znode; ++ int level; ++ ++ printk(KERN_DEBUG "\n"); ++ printk(KERN_DEBUG "Dumping the TNC tree\n"); ++ znode = ubifs_tnc_levelorder_next(c->zroot.znode, NULL); ++ level = znode->level; ++ printk(KERN_DEBUG "== Level %d ==\n", level); ++ while (znode) { ++ if (level != znode->level) { ++ level = znode->level; ++ printk(KERN_DEBUG "== Level %d ==\n", level); ++ } ++ dbg_dump_znode(c, znode); ++ znode = ubifs_tnc_levelorder_next(c->zroot.znode, znode); ++ } ++ ++ printk(KERN_DEBUG "\n"); ++} ++ ++/* ++ * dbg_check_dir - check directory inode size. ++ * @c: UBIFS file-system description object ++ * @dir: the directory to calculate size for ++ * @size: the result is returned here ++ * ++ * This function makes sure that directory size is correct. Returns zero ++ * in case of success and a negative error code in case of failure. ++ * ++ * Note, it is good idea to make sure the @dir->i_mutex is locked before ++ * calling this function. ++ */ ++int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir) ++{ ++ union ubifs_key key; ++ struct ubifs_dent_node *dent, *pdent = NULL; ++ struct qstr nm = { .name = NULL }; ++ loff_t size = UBIFS_INO_NODE_SZ; ++ ++ if (!(ubifs_chk_flags & UBIFS_CHK_GEN)) ++ return 0; ++ ++ if (!S_ISDIR(dir->i_mode)) ++ return 0; ++ ++ lowest_dent_key(c, &key, dir->i_ino); ++ while (1) { ++ int err; ++ ++ dent = ubifs_tnc_next_ent(c, &key, &nm); ++ if (IS_ERR(dent)) { ++ err = PTR_ERR(dent); ++ if (err == -ENOENT) ++ break; ++ return err; ++ } ++ ++ nm.name = dent->name; ++ nm.len = le16_to_cpu(dent->nlen); ++ size += CALC_DENT_SIZE(nm.len); ++ kfree(pdent); ++ pdent = dent; ++ key_read(c, &dent->key, &key); ++ } ++ ++ kfree(pdent); ++ ++ if (i_size_read(dir) != size) { ++ ubifs_err("bad directory dir %lu size %llu, " ++ "calculated %llu", dir->i_ino, ++ (unsigned long long)i_size_read(dir), ++ (unsigned long long)size); ++ dump_stack(); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * dbg_check_key_order - make sure that colliding keys are properly ordered. ++ * @c: UBIFS file-system description object ++ * @zbr1: first zbranch ++ * @zbr1: following zbranch ++ * ++ * In UBIFS indexing B-tree colliding keys has to be sorted in binary order of ++ * names of the direntries/xentries which are referred by the keys. This ++ * function reads direntries/xentries referred by @zbr1 and @zbr2 and makes ++ * sure the name of direntry/xentry referred by @zbr1 is less than ++ * direntry/xentry referred by @zbr2. Returns zero if this is true, %1 if not, ++ * and a negative error code in case of failure. ++ */ ++static int dbg_check_key_order(struct ubifs_info *c, struct ubifs_zbranch *zbr1, ++ struct ubifs_zbranch *zbr2) ++{ ++ int err, nlen1, nlen2, cmp; ++ struct ubifs_dent_node *dent1, *dent2; ++ union ubifs_key key; ++ ++ ubifs_assert(!keys_cmp(c, &zbr1->key, &zbr2->key)); ++ dent1 = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS); ++ if (!dent1) ++ return -ENOMEM; ++ dent2 = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS); ++ if (!dent2) { ++ err = -ENOMEM; ++ goto out_free; ++ } ++ ++ err = dbg_read_leaf_nolock(c, zbr1, dent1); ++ if (err) ++ goto out_free; ++ err = ubifs_validate_entry(c, dent1); ++ if (err) ++ goto out_free; ++ ++ err = dbg_read_leaf_nolock(c, zbr2, dent2); ++ if (err) ++ goto out_free; ++ err = ubifs_validate_entry(c, dent2); ++ if (err) ++ goto out_free; ++ ++ /* Make sure node keys are the same as in zbranch */ ++ err = 1; ++ key_read(c, &dent1->key, &key); ++ if (keys_cmp(c, &zbr1->key, &key)) { ++ dbg_err("1st entry at %d:%d has key %s", zbr1->lnum, ++ zbr1->offs, DBGKEY(&key)); ++ dbg_err("but it should have key %s according to tnc", ++ DBGKEY(&zbr1->key)); ++ dbg_dump_node(c, dent1); ++ goto out_free; ++ } ++ ++ key_read(c, &dent2->key, &key); ++ if (keys_cmp(c, &zbr2->key, &key)) { ++ dbg_err("2nd entry at %d:%d has key %s", zbr1->lnum, ++ zbr1->offs, DBGKEY(&key)); ++ dbg_err("but it should have key %s according to tnc", ++ DBGKEY(&zbr2->key)); ++ dbg_dump_node(c, dent2); ++ goto out_free; ++ } ++ ++ nlen1 = le16_to_cpu(dent1->nlen); ++ nlen2 = le16_to_cpu(dent2->nlen); ++ ++ cmp = memcmp(dent1->name, dent2->name, min_t(int, nlen1, nlen2)); ++ if (cmp < 0 || (cmp == 0 && nlen1 < nlen2)) { ++ err = 0; ++ goto out_free; ++ } ++ if (cmp == 0 && nlen1 == nlen2) ++ dbg_err("2 xent/dent nodes with the same name"); ++ else ++ dbg_err("bad order of colliding key %s", ++ DBGKEY(&key)); ++ ++ dbg_msg("first node at %d:%d\n", zbr1->lnum, zbr1->offs); ++ dbg_dump_node(c, dent1); ++ dbg_msg("second node at %d:%d\n", zbr2->lnum, zbr2->offs); ++ dbg_dump_node(c, dent2); ++ ++out_free: ++ kfree(dent2); ++ kfree(dent1); ++ return err; ++} ++ ++/** ++ * dbg_check_znode - check if znode is all right. ++ * @c: UBIFS file-system description object ++ * @zbr: zbranch which points to this znode ++ * ++ * This function makes sure that znode referred to by @zbr is all right. ++ * Returns zero if it is, and %-EINVAL if it is not. ++ */ ++static int dbg_check_znode(struct ubifs_info *c, struct ubifs_zbranch *zbr) ++{ ++ struct ubifs_znode *znode = zbr->znode; ++ struct ubifs_znode *zp = znode->parent; ++ int n, err, cmp; ++ ++ if (znode->child_cnt <= 0 || znode->child_cnt > c->fanout) { ++ err = 1; ++ goto out; ++ } ++ if (znode->level < 0) { ++ err = 2; ++ goto out; ++ } ++ if (znode->iip < 0 || znode->iip >= c->fanout) { ++ err = 3; ++ goto out; ++ } ++ ++ if (zbr->len == 0) ++ /* Only dirty zbranch may have no on-flash nodes */ ++ if (!ubifs_zn_dirty(znode)) { ++ err = 4; ++ goto out; ++ } ++ ++ if (ubifs_zn_dirty(znode)) { ++ /* ++ * If znode is dirty, its parent has to be dirty as well. The ++ * order of the operation is important, so we have to have ++ * memory barriers. ++ */ ++ smp_mb(); ++ if (zp && !ubifs_zn_dirty(zp)) { ++ /* ++ * The dirty flag is atomic and is cleared outside the ++ * TNC mutex, so znode's dirty flag may now have ++ * been cleared. The child is always cleared before the ++ * parent, so we just need to check again. ++ */ ++ smp_mb(); ++ if (ubifs_zn_dirty(znode)) { ++ err = 5; ++ goto out; ++ } ++ } ++ } ++ ++ if (zp) { ++ const union ubifs_key *min, *max; ++ ++ if (znode->level != zp->level - 1) { ++ err = 6; ++ goto out; ++ } ++ ++ /* Make sure the 'parent' pointer in our znode is correct */ ++ err = ubifs_search_zbranch(c, zp, &zbr->key, &n); ++ if (!err) { ++ /* This zbranch does not exist in the parent */ ++ err = 7; ++ goto out; ++ } ++ ++ if (znode->iip >= zp->child_cnt) { ++ err = 8; ++ goto out; ++ } ++ ++ if (znode->iip != n) { ++ /* This may happen only in case of collisions */ ++ if (keys_cmp(c, &zp->zbranch[n].key, ++ &zp->zbranch[znode->iip].key)) { ++ err = 9; ++ goto out; ++ } ++ n = znode->iip; ++ } ++ ++ /* ++ * Make sure that the first key in our znode is greater than or ++ * equal to the key in the pointing zbranch. ++ */ ++ min = &zbr->key; ++ cmp = keys_cmp(c, min, &znode->zbranch[0].key); ++ if (cmp == 1) { ++ err = 10; ++ goto out; ++ } ++ ++ if (n + 1 < zp->child_cnt) { ++ max = &zp->zbranch[n + 1].key; ++ ++ /* ++ * Make sure the last key in our znode is less or ++ * equivalent than the the key in zbranch which goes ++ * after our pointing zbranch. ++ */ ++ cmp = keys_cmp(c, max, ++ &znode->zbranch[znode->child_cnt - 1].key); ++ if (cmp == -1) { ++ err = 11; ++ goto out; ++ } ++ } ++ } else { ++ /* This may only be root znode */ ++ if (zbr != &c->zroot) { ++ err = 12; ++ goto out; ++ } ++ } ++ ++ /* ++ * Make sure that next key is greater or equivalent then the previous ++ * one. ++ */ ++ for (n = 1; n < znode->child_cnt; n++) { ++ cmp = keys_cmp(c, &znode->zbranch[n - 1].key, ++ &znode->zbranch[n].key); ++ if (cmp > 0) { ++ err = 13; ++ goto out; ++ } ++ if (cmp == 0) { ++ /* This can only be keys with colliding hash */ ++ if (!is_hash_key(c, &znode->zbranch[n].key)) { ++ err = 14; ++ goto out; ++ } ++ ++ if (znode->level != 0 || c->replaying) ++ continue; ++ ++ /* ++ * Colliding keys should follow binary order of ++ * corresponding xentry/dentry names. ++ */ ++ err = dbg_check_key_order(c, &znode->zbranch[n - 1], ++ &znode->zbranch[n]); ++ if (err < 0) ++ return err; ++ if (err) { ++ err = 15; ++ goto out; ++ } ++ } ++ } ++ ++ for (n = 0; n < znode->child_cnt; n++) { ++ if (!znode->zbranch[n].znode && ++ (znode->zbranch[n].lnum == 0 || ++ znode->zbranch[n].len == 0)) { ++ err = 16; ++ goto out; ++ } ++ ++ if (znode->zbranch[n].lnum != 0 && ++ znode->zbranch[n].len == 0) { ++ err = 17; ++ goto out; ++ } ++ ++ if (znode->zbranch[n].lnum == 0 && ++ znode->zbranch[n].len != 0) { ++ err = 18; ++ goto out; ++ } ++ ++ if (znode->zbranch[n].lnum == 0 && ++ znode->zbranch[n].offs != 0) { ++ err = 19; ++ goto out; ++ } ++ ++ if (znode->level != 0 && znode->zbranch[n].znode) ++ if (znode->zbranch[n].znode->parent != znode) { ++ err = 20; ++ goto out; ++ } ++ } ++ ++ return 0; ++ ++out: ++ ubifs_err("failed, error %d", err); ++ ubifs_msg("dump of the znode"); ++ dbg_dump_znode(c, znode); ++ if (zp) { ++ ubifs_msg("dump of the parent znode"); ++ dbg_dump_znode(c, zp); ++ } ++ dump_stack(); ++ return -EINVAL; ++} ++ ++/** ++ * dbg_check_tnc - check TNC tree. ++ * @c: UBIFS file-system description object ++ * @extra: do extra checks that are possible at start commit ++ * ++ * This function traverses whole TNC tree and checks every znode. Returns zero ++ * if everything is all right and %-EINVAL if something is wrong with TNC. ++ */ ++int dbg_check_tnc(struct ubifs_info *c, int extra) ++{ ++ struct ubifs_znode *znode; ++ long clean_cnt = 0, dirty_cnt = 0; ++ int err, last; ++ ++ if (!(ubifs_chk_flags & UBIFS_CHK_TNC)) ++ return 0; ++ ++ ubifs_assert(mutex_is_locked(&c->tnc_mutex)); ++ if (!c->zroot.znode) ++ return 0; ++ ++ znode = ubifs_tnc_postorder_first(c->zroot.znode); ++ while (1) { ++ struct ubifs_znode *prev; ++ struct ubifs_zbranch *zbr; ++ ++ if (!znode->parent) ++ zbr = &c->zroot; ++ else ++ zbr = &znode->parent->zbranch[znode->iip]; ++ ++ err = dbg_check_znode(c, zbr); ++ if (err) ++ return err; ++ ++ if (extra) { ++ if (ubifs_zn_dirty(znode)) ++ dirty_cnt += 1; ++ else ++ clean_cnt += 1; ++ } ++ ++ prev = znode; ++ znode = ubifs_tnc_postorder_next(znode); ++ if (!znode) ++ break; ++ ++ /* ++ * If the last key of this znode is equivalent to the first key ++ * of the next znode (collision), then check order of the keys. ++ */ ++ last = prev->child_cnt - 1; ++ if (prev->level == 0 && znode->level == 0 && !c->replaying && ++ !keys_cmp(c, &prev->zbranch[last].key, ++ &znode->zbranch[0].key)) { ++ err = dbg_check_key_order(c, &prev->zbranch[last], ++ &znode->zbranch[0]); ++ if (err < 0) ++ return err; ++ if (err) { ++ ubifs_msg("first znode"); ++ dbg_dump_znode(c, prev); ++ ubifs_msg("second znode"); ++ dbg_dump_znode(c, znode); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ if (extra) { ++ if (clean_cnt != atomic_long_read(&c->clean_zn_cnt)) { ++ ubifs_err("incorrect clean_zn_cnt %ld, calculated %ld", ++ atomic_long_read(&c->clean_zn_cnt), ++ clean_cnt); ++ return -EINVAL; ++ } ++ if (dirty_cnt != atomic_long_read(&c->dirty_zn_cnt)) { ++ ubifs_err("incorrect dirty_zn_cnt %ld, calculated %ld", ++ atomic_long_read(&c->dirty_zn_cnt), ++ dirty_cnt); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int dbg_add_size(struct ubifs_info *c, struct ubifs_znode *znode, ++ void *priv) ++{ ++ long long *idx_size = priv; ++ int add; ++ ++ add = ubifs_idx_node_sz(c, znode->child_cnt); ++ add = ALIGN(add, 8); ++ *idx_size += add; ++ return 0; ++} ++ ++int dbg_check_idx_size(struct ubifs_info *c, long long idx_size) ++{ ++ int err; ++ long long calc = 0; ++ ++ if (!(ubifs_chk_flags & UBIFS_CHK_IDX_SZ)) ++ return 0; ++ ++ err = dbg_walk_index(c, NULL, dbg_add_size, &calc); ++ if (err) { ++ ubifs_err("error %d while walking the index", err); ++ return err; ++ } ++ ++ if (calc != idx_size) { ++ ubifs_err("index size check failed"); ++ ubifs_err("calculated size is %lld, should be %lld", ++ calc, idx_size); ++ dump_stack(); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int invocation_cnt; ++ ++int dbg_force_in_the_gaps(void) ++{ ++ if (!dbg_force_in_the_gaps_enabled) ++ return 0; ++ /* Force in-the-gaps every 8th commit */ ++ return !((invocation_cnt++) & 0x7); ++} ++ ++#endif /* CONFIG_UBIFS_FS_DEBUG */ ++ ++/* Failure mode for recovery testing */ ++ ++#define chance(n, d) (simple_rand() <= (n) * 32768LL / (d)) ++ ++struct failure_mode_info { ++ struct list_head list; ++ struct ubifs_info *c; ++}; ++ ++static LIST_HEAD(fmi_list); ++static DEFINE_SPINLOCK(fmi_lock); ++ ++static unsigned int next; ++ ++static int simple_rand(void) ++{ ++ if (next == 0) ++ next = current->pid; ++ next = next * 1103515245 + 12345; ++ return (next >> 16) & 32767; ++} ++ ++void dbg_failure_mode_registration(struct ubifs_info *c) ++{ ++ struct failure_mode_info *fmi; ++ ++ fmi = kmalloc(sizeof(struct failure_mode_info), GFP_NOFS); ++ if (!fmi) { ++ dbg_err("Failed to register failure mode - no memory"); ++ return; ++ } ++ fmi->c = c; ++ spin_lock(&fmi_lock); ++ list_add_tail(&fmi->list, &fmi_list); ++ spin_unlock(&fmi_lock); ++} ++ ++void dbg_failure_mode_deregistration(struct ubifs_info *c) ++{ ++ struct failure_mode_info *fmi, *tmp; ++ ++ spin_lock(&fmi_lock); ++ list_for_each_entry_safe(fmi, tmp, &fmi_list, list) ++ if (fmi->c == c) { ++ list_del(&fmi->list); ++ kfree(fmi); ++ } ++ spin_unlock(&fmi_lock); ++} ++ ++static struct ubifs_info *dbg_find_info(struct ubi_volume_desc *desc) ++{ ++ struct failure_mode_info *fmi; ++ ++ spin_lock(&fmi_lock); ++ list_for_each_entry(fmi, &fmi_list, list) ++ if (fmi->c->ubi == desc) { ++ struct ubifs_info *c = fmi->c; ++ ++ spin_unlock(&fmi_lock); ++ return c; ++ } ++ spin_unlock(&fmi_lock); ++ return NULL; ++} ++ ++static int in_failure_mode(struct ubi_volume_desc *desc) ++{ ++ struct ubifs_info *c = dbg_find_info(desc); ++ ++ if (c) ++ return c->failure_mode; ++ return 0; ++} ++ ++static int do_fail(struct ubi_volume_desc *desc, int lnum, int write) ++{ ++ struct ubifs_info *c = dbg_find_info(desc); ++ ++ if (!c || !dbg_failure_mode) ++ return 0; ++ if (c->failure_mode) ++ return 1; ++ if (!c->fail_cnt) { ++ /* First call - decide delay to failure */ ++ if (chance(1, 2)) { ++ unsigned int delay = 1 << (simple_rand() >> 11); ++ ++ if (chance(1, 2)) { ++ c->fail_delay = 1; ++ c->fail_timeout = jiffies + ++ msecs_to_jiffies(delay); ++ dbg_rcvry("failing after %ums", delay); ++ } else { ++ c->fail_delay = 2; ++ c->fail_cnt_max = delay; ++ dbg_rcvry("failing after %u calls", delay); ++ } ++ } ++ c->fail_cnt += 1; ++ } ++ /* Determine if failure delay has expired */ ++ if (c->fail_delay == 1) { ++ if (time_before(jiffies, c->fail_timeout)) ++ return 0; ++ } else if (c->fail_delay == 2) ++ if (c->fail_cnt++ < c->fail_cnt_max) ++ return 0; ++ if (lnum == UBIFS_SB_LNUM) { ++ if (write) { ++ if (chance(1, 2)) ++ return 0; ++ } else if (chance(19, 20)) ++ return 0; ++ dbg_rcvry("failing in super block LEB %d", lnum); ++ } else if (lnum == UBIFS_MST_LNUM || lnum == UBIFS_MST_LNUM + 1) { ++ if (chance(19, 20)) ++ return 0; ++ dbg_rcvry("failing in master LEB %d", lnum); ++ } else if (lnum >= UBIFS_LOG_LNUM && lnum <= c->log_last) { ++ if (write) { ++ if (chance(99, 100)) ++ return 0; ++ } else if (chance(399, 400)) ++ return 0; ++ dbg_rcvry("failing in log LEB %d", lnum); ++ } else if (lnum >= c->lpt_first && lnum <= c->lpt_last) { ++ if (write) { ++ if (chance(7, 8)) ++ return 0; ++ } else if (chance(19, 20)) ++ return 0; ++ dbg_rcvry("failing in LPT LEB %d", lnum); ++ } else if (lnum >= c->orph_first && lnum <= c->orph_last) { ++ if (write) { ++ if (chance(1, 2)) ++ return 0; ++ } else if (chance(9, 10)) ++ return 0; ++ dbg_rcvry("failing in orphan LEB %d", lnum); ++ } else if (lnum == c->ihead_lnum) { ++ if (chance(99, 100)) ++ return 0; ++ dbg_rcvry("failing in index head LEB %d", lnum); ++ } else if (c->jheads && lnum == c->jheads[GCHD].wbuf.lnum) { ++ if (chance(9, 10)) ++ return 0; ++ dbg_rcvry("failing in GC head LEB %d", lnum); ++ } else if (write && !RB_EMPTY_ROOT(&c->buds) && ++ !ubifs_search_bud(c, lnum)) { ++ if (chance(19, 20)) ++ return 0; ++ dbg_rcvry("failing in non-bud LEB %d", lnum); ++ } else if (c->cmt_state == COMMIT_RUNNING_BACKGROUND || ++ c->cmt_state == COMMIT_RUNNING_REQUIRED) { ++ if (chance(999, 1000)) ++ return 0; ++ dbg_rcvry("failing in bud LEB %d commit running", lnum); ++ } else { ++ if (chance(9999, 10000)) ++ return 0; ++ dbg_rcvry("failing in bud LEB %d commit not running", lnum); ++ } ++ ubifs_err("*** SETTING FAILURE MODE ON (LEB %d) ***", lnum); ++ c->failure_mode = 1; ++ dump_stack(); ++ return 1; ++} ++ ++static void cut_data(const void *buf, int len) ++{ ++ int flen, i; ++ unsigned char *p = (void *)buf; ++ ++ flen = (len * (long long)simple_rand()) >> 15; ++ for (i = flen; i < len; i++) ++ p[i] = 0xff; ++} ++ ++int dbg_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, ++ int len, int check) ++{ ++ if (in_failure_mode(desc)) ++ return -EIO; ++ return ubi_leb_read(desc, lnum, buf, offset, len, check); ++} ++ ++int dbg_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, ++ int offset, int len, int dtype) ++{ ++ int err; ++ ++ if (in_failure_mode(desc)) ++ return -EIO; ++ if (do_fail(desc, lnum, 1)) ++ cut_data(buf, len); ++ err = ubi_leb_write(desc, lnum, buf, offset, len, dtype); ++ if (err) ++ return err; ++ if (in_failure_mode(desc)) ++ return -EIO; ++ return 0; ++} ++ ++int dbg_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, ++ int len, int dtype) ++{ ++ int err; ++ ++ if (do_fail(desc, lnum, 1)) ++ return -EIO; ++ err = ubi_leb_change(desc, lnum, buf, len, dtype); ++ if (err) ++ return err; ++ if (do_fail(desc, lnum, 1)) ++ return -EIO; ++ return 0; ++} ++ ++int dbg_leb_erase(struct ubi_volume_desc *desc, int lnum) ++{ ++ int err; ++ ++ if (do_fail(desc, lnum, 0)) ++ return -EIO; ++ err = ubi_leb_erase(desc, lnum); ++ if (err) ++ return err; ++ if (do_fail(desc, lnum, 0)) ++ return -EIO; ++ return 0; ++} ++ ++int dbg_leb_unmap(struct ubi_volume_desc *desc, int lnum) ++{ ++ int err; ++ ++ if (do_fail(desc, lnum, 0)) ++ return -EIO; ++ err = ubi_leb_unmap(desc, lnum); ++ if (err) ++ return err; ++ if (do_fail(desc, lnum, 0)) ++ return -EIO; ++ return 0; ++} ++ ++int dbg_is_mapped(struct ubi_volume_desc *desc, int lnum) ++{ ++ if (in_failure_mode(desc)) ++ return -EIO; ++ return ubi_is_mapped(desc, lnum); ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/debug.h linux-2.6.24.7/fs/ubifs/debug.h +--- linux-2.6.24.7.old/fs/ubifs/debug.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/debug.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,392 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++#ifndef __UBIFS_DEBUG_H__ ++#define __UBIFS_DEBUG_H__ ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ ++#define UBIFS_DBG(op) op ++ ++#define ubifs_assert(expr) do { \ ++ if (unlikely(!(expr))) { \ ++ printk(KERN_CRIT "UBIFS assert failed in %s at %u (pid %d)\n", \ ++ __func__, __LINE__, current->pid); \ ++ dbg_dump_stack(); \ ++ } \ ++} while (0) ++ ++#define ubifs_assert_cmt_locked(c) do { \ ++ if (unlikely(down_write_trylock(&(c)->commit_sem))) { \ ++ up_write(&(c)->commit_sem); \ ++ printk(KERN_CRIT "commit lock is not locked!\n"); \ ++ ubifs_assert(0); \ ++ } \ ++} while (0) ++ ++#define dbg_dump_stack() do { \ ++ if (!dbg_failure_mode) \ ++ dump_stack(); \ ++} while (0) ++ ++/* Generic debugging messages */ ++#define dbg_msg(fmt, ...) do { \ ++ spin_lock(&dbg_lock); \ ++ printk(KERN_DEBUG "UBIFS DBG (pid %d): %s: " fmt "\n", current->pid, \ ++ __func__, ##__VA_ARGS__); \ ++ spin_unlock(&dbg_lock); \ ++} while (0) ++ ++#define dbg_do_msg(typ, fmt, ...) do { \ ++ if (ubifs_msg_flags & typ) \ ++ dbg_msg(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define dbg_err(fmt, ...) do { \ ++ spin_lock(&dbg_lock); \ ++ ubifs_err(fmt, ##__VA_ARGS__); \ ++ spin_unlock(&dbg_lock); \ ++} while (0) ++ ++const char *dbg_key_str0(const struct ubifs_info *c, ++ const union ubifs_key *key); ++const char *dbg_key_str1(const struct ubifs_info *c, ++ const union ubifs_key *key); ++ ++/* ++ * DBGKEY macros require dbg_lock to be held, which it is in the dbg message ++ * macros. ++ */ ++#define DBGKEY(key) dbg_key_str0(c, (key)) ++#define DBGKEY1(key) dbg_key_str1(c, (key)) ++ ++/* General messages */ ++#define dbg_gen(fmt, ...) dbg_do_msg(UBIFS_MSG_GEN, fmt, ##__VA_ARGS__) ++ ++/* Additional journal messages */ ++#define dbg_jnl(fmt, ...) dbg_do_msg(UBIFS_MSG_JNL, fmt, ##__VA_ARGS__) ++ ++/* Additional TNC messages */ ++#define dbg_tnc(fmt, ...) dbg_do_msg(UBIFS_MSG_TNC, fmt, ##__VA_ARGS__) ++ ++/* Additional lprops messages */ ++#define dbg_lp(fmt, ...) dbg_do_msg(UBIFS_MSG_LP, fmt, ##__VA_ARGS__) ++ ++/* Additional LEB find messages */ ++#define dbg_find(fmt, ...) dbg_do_msg(UBIFS_MSG_FIND, fmt, ##__VA_ARGS__) ++ ++/* Additional mount messages */ ++#define dbg_mnt(fmt, ...) dbg_do_msg(UBIFS_MSG_MNT, fmt, ##__VA_ARGS__) ++ ++/* Additional I/O messages */ ++#define dbg_io(fmt, ...) dbg_do_msg(UBIFS_MSG_IO, fmt, ##__VA_ARGS__) ++ ++/* Additional commit messages */ ++#define dbg_cmt(fmt, ...) dbg_do_msg(UBIFS_MSG_CMT, fmt, ##__VA_ARGS__) ++ ++/* Additional budgeting messages */ ++#define dbg_budg(fmt, ...) dbg_do_msg(UBIFS_MSG_BUDG, fmt, ##__VA_ARGS__) ++ ++/* Additional log messages */ ++#define dbg_log(fmt, ...) dbg_do_msg(UBIFS_MSG_LOG, fmt, ##__VA_ARGS__) ++ ++/* Additional gc messages */ ++#define dbg_gc(fmt, ...) dbg_do_msg(UBIFS_MSG_GC, fmt, ##__VA_ARGS__) ++ ++/* Additional scan messages */ ++#define dbg_scan(fmt, ...) dbg_do_msg(UBIFS_MSG_SCAN, fmt, ##__VA_ARGS__) ++ ++/* Additional recovery messages */ ++#define dbg_rcvry(fmt, ...) dbg_do_msg(UBIFS_MSG_RCVRY, fmt, ##__VA_ARGS__) ++ ++/* ++ * Debugging message type flags (must match msg_type_names in debug.c). ++ * ++ * UBIFS_MSG_GEN: general messages ++ * UBIFS_MSG_JNL: journal messages ++ * UBIFS_MSG_MNT: mount messages ++ * UBIFS_MSG_CMT: commit messages ++ * UBIFS_MSG_FIND: LEB find messages ++ * UBIFS_MSG_BUDG: budgeting messages ++ * UBIFS_MSG_GC: garbage collection messages ++ * UBIFS_MSG_TNC: TNC messages ++ * UBIFS_MSG_LP: lprops messages ++ * UBIFS_MSG_IO: I/O messages ++ * UBIFS_MSG_LOG: log messages ++ * UBIFS_MSG_SCAN: scan messages ++ * UBIFS_MSG_RCVRY: recovery messages ++ */ ++enum { ++ UBIFS_MSG_GEN = 0x1, ++ UBIFS_MSG_JNL = 0x2, ++ UBIFS_MSG_MNT = 0x4, ++ UBIFS_MSG_CMT = 0x8, ++ UBIFS_MSG_FIND = 0x10, ++ UBIFS_MSG_BUDG = 0x20, ++ UBIFS_MSG_GC = 0x40, ++ UBIFS_MSG_TNC = 0x80, ++ UBIFS_MSG_LP = 0x100, ++ UBIFS_MSG_IO = 0x200, ++ UBIFS_MSG_LOG = 0x400, ++ UBIFS_MSG_SCAN = 0x800, ++ UBIFS_MSG_RCVRY = 0x1000, ++}; ++ ++/* Debugging message type flags for each default debug message level */ ++#define UBIFS_MSG_LVL_0 0 ++#define UBIFS_MSG_LVL_1 0x1 ++#define UBIFS_MSG_LVL_2 0x7f ++#define UBIFS_MSG_LVL_3 0xffff ++ ++/* ++ * Debugging check flags (must match chk_names in debug.c). ++ * ++ * UBIFS_CHK_GEN: general checks ++ * UBIFS_CHK_TNC: check TNC ++ * UBIFS_CHK_IDX_SZ: check index size ++ * UBIFS_CHK_ORPH: check orphans ++ * UBIFS_CHK_OLD_IDX: check the old index ++ * UBIFS_CHK_LPROPS: check lprops ++ */ ++enum { ++ UBIFS_CHK_GEN = 0x1, ++ UBIFS_CHK_TNC = 0x2, ++ UBIFS_CHK_IDX_SZ = 0x4, ++ UBIFS_CHK_ORPH = 0x8, ++ UBIFS_CHK_OLD_IDX = 0x10, ++ UBIFS_CHK_LPROPS = 0x20, ++}; ++ ++/* ++ * Special testing flags (must match tst_names in debug.c). ++ * ++ * UBIFS_TST_FORCE_IN_THE_GAPS: force the use of in-the-gaps method ++ * UBIFS_TST_RCVRY: failure mode for recovery testing ++ */ ++enum { ++ UBIFS_TST_FORCE_IN_THE_GAPS = 0x2, ++ UBIFS_TST_RCVRY = 0x4, ++}; ++ ++#if CONFIG_UBIFS_FS_DEBUG_MSG_LVL == 1 ++#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_1 ++#elif CONFIG_UBIFS_FS_DEBUG_MSG_LVL == 2 ++#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_2 ++#elif CONFIG_UBIFS_FS_DEBUG_MSG_LVL == 3 ++#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_3 ++#else ++#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_0 ++#endif ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG_CHKS ++#define UBIFS_CHK_FLAGS_DEFAULT 0xffffffff ++#else ++#define UBIFS_CHK_FLAGS_DEFAULT 0 ++#endif ++ ++extern spinlock_t dbg_lock; ++ ++extern unsigned int ubifs_msg_flags; ++extern unsigned int ubifs_chk_flags; ++extern unsigned int ubifs_tst_flags; ++ ++/* Dump functions */ ++ ++const char *dbg_ntype(int type); ++const char *dbg_cstate(int cmt_state); ++const char *dbg_get_key_dump(const struct ubifs_info *c, ++ const union ubifs_key *key); ++void dbg_dump_inode(const struct ubifs_info *c, const struct inode *inode); ++void dbg_dump_node(const struct ubifs_info *c, const void *node); ++void dbg_dump_budget_req(const struct ubifs_budget_req *req); ++void dbg_dump_lstats(const struct ubifs_lp_stats *lst); ++void dbg_dump_budg(struct ubifs_info *c); ++void dbg_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp); ++void dbg_dump_lprops(struct ubifs_info *c); ++void dbg_dump_leb(const struct ubifs_info *c, int lnum); ++void dbg_dump_znode(const struct ubifs_info *c, ++ const struct ubifs_znode *znode); ++void dbg_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat); ++void dbg_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, ++ struct ubifs_nnode *parent, int iip); ++void dbg_dump_tnc(struct ubifs_info *c); ++ ++/* Checking helper functions */ ++ ++typedef int (*dbg_leaf_callback)(struct ubifs_info *c, ++ struct ubifs_zbranch *zbr, void *priv); ++typedef int (*dbg_znode_callback)(struct ubifs_info *c, ++ struct ubifs_znode *znode, void *priv); ++ ++int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb, ++ dbg_znode_callback znode_cb, void *priv); ++int dbg_read_leaf_nolock(struct ubifs_info *c, struct ubifs_zbranch *zbr, ++ void *node); ++ ++/* Checking functions */ ++ ++int dbg_check_lprops(struct ubifs_info *c); ++ ++int dbg_old_index_check_init(struct ubifs_info *c, struct ubifs_zbranch *zroot); ++int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot); ++ ++int dbg_check_cats(struct ubifs_info *c); ++ ++int dbg_check_ltab(struct ubifs_info *c); ++ ++int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir); ++ ++int dbg_check_tnc(struct ubifs_info *c, int extra); ++ ++int dbg_check_idx_size(struct ubifs_info *c, long long idx_size); ++ ++void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat, ++ int add_pos); ++ ++int dbg_check_lprops(struct ubifs_info *c); ++int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode, ++ int row, int col); ++ ++/* Force the use of in-the-gaps method for testing */ ++ ++#define dbg_force_in_the_gaps_enabled \ ++ (ubifs_tst_flags & UBIFS_TST_FORCE_IN_THE_GAPS) ++ ++int dbg_force_in_the_gaps(void); ++ ++/* Failure mode for recovery testing */ ++ ++#define dbg_failure_mode (ubifs_tst_flags & UBIFS_TST_RCVRY) ++ ++void dbg_failure_mode_registration(struct ubifs_info *c); ++void dbg_failure_mode_deregistration(struct ubifs_info *c); ++ ++#ifndef UBIFS_DBG_PRESERVE_UBI ++ ++#define ubi_leb_read dbg_leb_read ++#define ubi_leb_write dbg_leb_write ++#define ubi_leb_change dbg_leb_change ++#define ubi_leb_erase dbg_leb_erase ++#define ubi_leb_unmap dbg_leb_unmap ++#define ubi_is_mapped dbg_is_mapped ++ ++#endif ++ ++int dbg_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, ++ int len, int check); ++int dbg_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, ++ int offset, int len, int dtype); ++int dbg_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, ++ int len, int dtype); ++int dbg_leb_erase(struct ubi_volume_desc *desc, int lnum); ++int dbg_leb_unmap(struct ubi_volume_desc *desc, int lnum); ++int dbg_is_mapped(struct ubi_volume_desc *desc, int lnum); ++ ++static inline int dbg_read(struct ubi_volume_desc *desc, int lnum, char *buf, ++ int offset, int len) ++{ ++ return dbg_leb_read(desc, lnum, buf, offset, len, 0); ++} ++ ++static inline int dbg_write(struct ubi_volume_desc *desc, int lnum, ++ const void *buf, int offset, int len) ++{ ++ return dbg_leb_write(desc, lnum, buf, offset, len, UBI_UNKNOWN); ++} ++ ++static inline int dbg_change(struct ubi_volume_desc *desc, int lnum, ++ const void *buf, int len) ++{ ++ return dbg_leb_change(desc, lnum, buf, len, UBI_UNKNOWN); ++} ++ ++#else /* !CONFIG_UBIFS_FS_DEBUG */ ++ ++#define UBIFS_DBG(op) ++#define ubifs_assert(expr) ({}) ++#define ubifs_assert_cmt_locked(c) ++#define dbg_dump_stack() ++#define dbg_err(fmt, ...) ({}) ++#define dbg_msg(fmt, ...) ({}) ++#define dbg_key(c, key, fmt, ...) ({}) ++ ++#define dbg_gen(fmt, ...) ({}) ++#define dbg_jnl(fmt, ...) ({}) ++#define dbg_tnc(fmt, ...) ({}) ++#define dbg_lp(fmt, ...) ({}) ++#define dbg_find(fmt, ...) ({}) ++#define dbg_mnt(fmt, ...) ({}) ++#define dbg_io(fmt, ...) ({}) ++#define dbg_cmt(fmt, ...) ({}) ++#define dbg_budg(fmt, ...) ({}) ++#define dbg_log(fmt, ...) ({}) ++#define dbg_gc(fmt, ...) ({}) ++#define dbg_scan(fmt, ...) ({}) ++#define dbg_rcvry(fmt, ...) ({}) ++ ++#define dbg_ntype(type) "" ++#define dbg_cstate(cmt_state) "" ++#define dbg_get_key_dump(c, key) ({}) ++#define dbg_dump_inode(c, inode) ({}) ++#define dbg_dump_node(c, node) ({}) ++#define dbg_dump_budget_req(req) ({}) ++#define dbg_dump_lstats(lst) ({}) ++#define dbg_dump_budg(c) ({}) ++#define dbg_dump_lprop(c, lp) ({}) ++#define dbg_dump_lprops(c) ({}) ++#define dbg_dump_leb(c, lnum) ({}) ++#define dbg_dump_znode(c, znode) ({}) ++#define dbg_dump_heap(c, heap, cat) ({}) ++#define dbg_dump_pnode(c, pnode, parent, iip) ({}) ++#define dbg_dump_tnc(c) ({}) ++ ++#define dbg_walk_index(c, leaf_cb, znode_cb, priv) 0 ++#define dbg_read_leaf_nolock(c, zbr, node) 0 ++ ++#define dbg_old_index_check_init(c, zroot) 0 ++#define dbg_check_old_index(c, zroot) 0 ++ ++#define dbg_check_cats(c) 0 ++ ++#define dbg_check_ltab(c) 0 ++ ++#define dbg_check_dir_size(c, dir) 0 ++ ++#define dbg_check_tnc(c, x) 0 ++ ++#define dbg_check_idx_size(c, idx_size) 0 ++ ++#define dbg_check_heap(c, heap, cat, add_pos) ({}) ++ ++#define dbg_check_lprops(c) 0 ++#define dbg_check_lpt_nodes(c, cnode, row, col) 0 ++ ++#define dbg_force_in_the_gaps_enabled 0 ++#define dbg_force_in_the_gaps() 0 ++ ++#define dbg_failure_mode 0 ++#define dbg_failure_mode_registration(c) ({}) ++#define dbg_failure_mode_deregistration(c) ({}) ++ ++#endif /* !CONFIG_UBIFS_FS_DEBUG */ ++ ++#endif /* !__UBIFS_DEBUG_H__ */ +diff -urN linux-2.6.24.7.old/fs/ubifs/dir.c linux-2.6.24.7/fs/ubifs/dir.c +--- linux-2.6.24.7.old/fs/ubifs/dir.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/dir.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1017 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * Copyright (C) 2006, 2007 University of Szeged, Hungary ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ * Zoltan Sogor ++ */ ++ ++/* ++ * This file implements directory operations. ++ * ++ * All FS operations in this file allocate budget before writing anything to the ++ * media. If they fail to allocate it, the error is returned. The only ++ * exceptions are 'ubifs_unlink()' and 'ubifs_rmdir()' which keep working even ++ * if they unable to allocate the budget, because deletion %-ENOSPC failure is ++ * not what users are usually ready to get. UBIFS budgeting subsystem has some ++ * space reserved for these purposes. ++ * ++ * All operations in this file change the parent inode, e.g., 'ubifs_link()' ++ * changes ctime and nlink of the parent inode. The parent inode is written to ++ * the media straight away - it is not marked as dirty and there is no ++ * write-back for it. This was done to simplify file-system recovery which ++ * would otherwise be very difficult to do. So instead of marking the parent ++ * inode dirty, the operations mark it clean. ++ */ ++ ++#include "ubifs.h" ++ ++/* ++ * Provide backing_dev_info in order to disable readahead. For UBIFS, I/O is ++ * not deferred, it is done immediately in readpage, which means the user would ++ * have to wait not just for their own I/O but the readahead I/O as well i.e. ++ * completely pointless. ++ */ ++struct backing_dev_info ubifs_backing_dev_info = { ++ .ra_pages = 0, /* Set to zero to disable readahead */ ++ .state = 0, ++ .capabilities = BDI_CAP_MAP_COPY, ++ .unplug_io_fn = default_unplug_io_fn, ++}; ++ ++/** ++ * inherit_flags - inherit flags of the parent inode. ++ * @dir: parent inode ++ * @mode: new inode mode flags ++ * ++ * This is a helper function for 'ubifs_new_inode()' which inherits flag of the ++ * parent directory inode @dir. UBIFS inodes inherit the following flags: ++ * o %UBIFS_COMPR_FL, which is useful to switch compression on/of on ++ * sub-directory basis; ++ * o %UBIFS_SYNC_FL - useful for the same reasons; ++ * o %UBIFS_DIRSYNC_FL - similar, but relevant only to directories. ++ * ++ * This function returns the inherited flags. ++ */ ++static int inherit_flags(const struct inode *dir, int mode) ++{ ++ int flags; ++ const struct ubifs_inode *ui = ubifs_inode(dir); ++ ++ if (!S_ISDIR(dir->i_mode)) ++ /* ++ * The parent is not a directory, which means that an extended ++ * attribute inode is being created. No flags. ++ */ ++ return 0; ++ ++ flags = ui->flags & (UBIFS_COMPR_FL | UBIFS_SYNC_FL | UBIFS_DIRSYNC_FL); ++ if (!S_ISDIR(mode)) ++ /* The "DIRSYNC" flag only applies to directories */ ++ flags &= ~UBIFS_DIRSYNC_FL; ++ ++ return flags; ++} ++ ++/** ++ * ubifs_new_inode - allocate new UBIFS inode object. ++ * @c: UBIFS file-system description object ++ * @dir: parent directory inode ++ * @mode: inode mode flags ++ * ++ * This function finds an unused inode number, allocates new inode and ++ * initializes it. Returns new inode in case of success and an error code in ++ * case of failure. ++ */ ++struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir, ++ int mode) ++{ ++ struct inode *inode; ++ struct ubifs_inode *ui; ++ ++ inode = new_inode(c->vfs_sb); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ ++ /* ++ * Set 'S_NOCMTIME' to prevent VFS form updating [mc]time of inodes and ++ * marking them dirty in file write path (see 'file_update_time()'). ++ * UBIFS has to fully control "clean <-> dirty" transitions of inodes ++ * to make budgeting work. ++ */ ++ inode->i_flags |= (S_NOCMTIME); ++ ++ inode->i_uid = current->fsuid; ++ if (dir->i_mode & S_ISGID) { ++ inode->i_gid = dir->i_gid; ++ if (S_ISDIR(mode)) ++ mode |= S_ISGID; ++ } else ++ inode->i_gid = current->fsgid; ++ inode->i_mode = mode; ++ inode->i_mtime = inode->i_atime = inode->i_ctime = ++ ubifs_current_time(inode); ++ inode->i_mapping->nrpages = 0; ++ /* Disable readahead */ ++ inode->i_mapping->backing_dev_info = &ubifs_backing_dev_info; ++ ++ switch (mode & S_IFMT) { ++ case S_IFREG: ++ inode->i_mapping->a_ops = &ubifs_file_address_operations; ++ inode->i_op = &ubifs_file_inode_operations; ++ inode->i_fop = &ubifs_file_operations; ++ break; ++ case S_IFDIR: ++ inode->i_op = &ubifs_dir_inode_operations; ++ inode->i_fop = &ubifs_dir_operations; ++ inode->i_size = UBIFS_INO_NODE_SZ; ++ break; ++ case S_IFLNK: ++ inode->i_op = &ubifs_symlink_inode_operations; ++ break; ++ case S_IFSOCK: ++ case S_IFIFO: ++ case S_IFBLK: ++ case S_IFCHR: ++ inode->i_op = &ubifs_file_inode_operations; ++ break; ++ default: ++ BUG(); ++ } ++ ++ ui = ubifs_inode(inode); ++ ui->flags = inherit_flags(dir, mode); ++ ubifs_set_inode_flags(inode); ++ ++ if (S_ISREG(mode)) ++ ui->compr_type = c->default_compr; ++ else ++ ui->compr_type = UBIFS_COMPR_NONE; ++ ++ spin_lock(&c->cnt_lock); ++ /* Inode number overflow is currently not supported */ ++ if (c->highest_inum >= INUM_WARN_WATERMARK) { ++ if (c->highest_inum >= INUM_WATERMARK) { ++ spin_unlock(&c->cnt_lock); ++ ubifs_err("out of inode numbers"); ++ make_bad_inode(inode); ++ iput(inode); ++ return ERR_PTR(-EINVAL); ++ } ++ ubifs_warn("running out of inode numbers (current %lu, max %d)", ++ c->highest_inum, INUM_WATERMARK); ++ } ++ ++ inode->i_ino = ++c->highest_inum; ++ inode->i_generation = ++c->vfs_gen; ++ /* ++ * The creation sequence number remains with this inode for its ++ * lifetime. All nodes for this inode have a greater sequence number, ++ * and so it is possible to distinguish obsolete nodes belonging to a ++ * previous incarnation of the same inode number - for example, for the ++ * purpose of rebuilding the index. ++ */ ++ ui->creat_sqnum = ++c->max_sqnum; ++ spin_unlock(&c->cnt_lock); ++ ++ return inode; ++} ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ ++static int dbg_check_name(struct ubifs_dent_node *dent, struct qstr *nm) ++{ ++ if (!(ubifs_chk_flags & UBIFS_CHK_GEN)) ++ return 0; ++ if (le16_to_cpu(dent->nlen) != nm->len) ++ return -EINVAL; ++ if (memcmp(dent->name, nm->name, nm->len)) ++ return -EINVAL; ++ return 0; ++} ++ ++#else ++ ++#define dbg_check_name(dent, nm) 0 ++ ++#endif ++ ++static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ int err; ++ union ubifs_key key; ++ struct inode *inode = NULL; ++ struct ubifs_dent_node *dent; ++ struct ubifs_info *c = dir->i_sb->s_fs_info; ++ ++ dbg_gen("'%.*s' in dir ino %lu", ++ dentry->d_name.len, dentry->d_name.name, dir->i_ino); ++ ++ if (dentry->d_name.len > UBIFS_MAX_NLEN) ++ return ERR_PTR(-ENAMETOOLONG); ++ ++ dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS); ++ if (!dent) ++ return ERR_PTR(-ENOMEM); ++ ++ dent_key_init(c, &key, dir->i_ino, &dentry->d_name); ++ ++ err = ubifs_tnc_lookup_nm(c, &key, dent, &dentry->d_name); ++ if (err) { ++ if (err == -ENOENT) { ++ dbg_gen("not found"); ++ goto done; ++ } ++ goto out; ++ } ++ ++ if (dbg_check_name(dent, &dentry->d_name)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ inode = ubifs_iget(dir->i_sb, le64_to_cpu(dent->inum)); ++ if (IS_ERR(inode)) { ++ /* ++ * This should not happen. Probably the file-system needs ++ * checking. ++ */ ++ err = PTR_ERR(inode); ++ ubifs_err("dead directory entry '%.*s', error %d", ++ dentry->d_name.len, dentry->d_name.name, err); ++ ubifs_ro_mode(c, err); ++ goto out; ++ } ++ ++done: ++ kfree(dent); ++ /* ++ * Note, d_splice_alias() would be required instead if we supported ++ * NFS. ++ */ ++ d_add(dentry, inode); ++ return NULL; ++ ++out: ++ kfree(dent); ++ return ERR_PTR(err); ++} ++ ++static int ubifs_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *nd) ++{ ++ struct inode *inode; ++ struct ubifs_info *c = dir->i_sb->s_fs_info; ++ struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1 }; ++ int err, sz_change = CALC_DENT_SIZE(dentry->d_name.len); ++ ++ dbg_gen("dent '%.*s', mode %#x in dir ino %lu", ++ dentry->d_name.len, dentry->d_name.name, mode, dir->i_ino); ++ ++ inode = ubifs_new_inode(c, dir, mode); ++ if (IS_ERR(inode)) ++ return PTR_ERR(inode); ++ ++ err = ubifs_budget_inode_op(c, dir, &req); ++ if (err) ++ goto out; ++ ++ dir->i_size += sz_change; ++ ++ err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, ++ IS_DIRSYNC(dir), 0); ++ if (err) ++ goto out_budg; ++ ++ insert_inode_hash(inode); ++ d_instantiate(dentry, inode); ++ ubifs_release_ino_clean(c, dir, &req); ++ return 0; ++ ++out_budg: ++ dir->i_size -= sz_change; ++ ubifs_cancel_ino_op(c, dir, &req); ++ ubifs_err("cannot create regular file, error %d", err); ++out: ++ make_bad_inode(inode); ++ iput(inode); ++ return err; ++} ++ ++/** ++ * vfs_dent_type - get VFS directory entry type. ++ * @type: UBIFS directory entry type ++ * ++ * This function converts UBIFS directory entry type into VFS directory entry ++ * type. ++ */ ++static unsigned int vfs_dent_type(uint8_t type) ++{ ++ switch (type) { ++ case UBIFS_ITYPE_REG: ++ return DT_REG; ++ case UBIFS_ITYPE_DIR: ++ return DT_DIR; ++ case UBIFS_ITYPE_LNK: ++ return DT_LNK; ++ case UBIFS_ITYPE_BLK: ++ return DT_BLK; ++ case UBIFS_ITYPE_CHR: ++ return DT_CHR; ++ case UBIFS_ITYPE_FIFO: ++ return DT_FIFO; ++ case UBIFS_ITYPE_SOCK: ++ return DT_SOCK; ++ default: ++ BUG(); ++ } ++ return 0; ++} ++ ++/* ++ * The classical Unix view for directory is that it is a linear array of ++ * (name, inode number) entries. Linux/VFS assumes this model as well. ++ * Particularly, 'readdir()' call wants us to return a directory entry offset ++ * which later may be used to continue 'readdir()'ing the directory or to ++ * 'seek()' to that specific direntry. Obviously UBIFS does not really fit this ++ * model because directory entries are identified by keys, which may collide. ++ * ++ * UBIFS uses directory entry hash value for directory offsets, so ++ * 'seekdir()'/'telldir()' may not always work because of possible key ++ * collisions. But UBIFS guarantees that consecutive 'readdir()' calls work ++ * properly by means of saving full directory entry name in the private field ++ * of the file description object. ++ * ++ * This means that UBIFS cannot support NFS which requires full ++ * 'seekdir()'/'telldir()' support. ++ */ ++static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) ++{ ++ int err, over = 0; ++ struct qstr nm; ++ union ubifs_key key; ++ struct ubifs_dent_node *dent; ++ struct inode *dir = file->f_path.dentry->d_inode; ++ struct ubifs_info *c = dir->i_sb->s_fs_info; ++ ++ dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos); ++ ++ if (file->f_pos > UBIFS_S_KEY_HASH_MASK || file->f_pos == 2) ++ /* ++ * The directory was seek'ed to a senseless position or there ++ * are no more entries. ++ */ ++ return 0; ++ ++ /* File positions 0 and 1 correspond to "." and ".." */ ++ if (file->f_pos == 0) { ++ ubifs_assert(!file->private_data); ++ over = filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR); ++ if (over) ++ return 0; ++ file->f_pos = 1; ++ } ++ ++ if (file->f_pos == 1) { ++ ubifs_assert(!file->private_data); ++ over = filldir(dirent, "..", 2, 1, ++ parent_ino(file->f_path.dentry), DT_DIR); ++ if (over) ++ return 0; ++ ++ /* Find the first entry in TNC and save it */ ++ lowest_dent_key(c, &key, dir->i_ino); ++ nm.name = NULL; ++ dent = ubifs_tnc_next_ent(c, &key, &nm); ++ if (IS_ERR(dent)) { ++ err = PTR_ERR(dent); ++ goto out; ++ } ++ ++ file->f_pos = key_hash_flash(c, &dent->key); ++ file->private_data = dent; ++ } ++ ++ dent = file->private_data; ++ if (!dent) { ++ /* ++ * The directory was seek'ed to and is now readdir'ed. ++ * Find the entry corresponding to @file->f_pos or the ++ * closest one. ++ */ ++ dent_key_init_hash(c, &key, dir->i_ino, file->f_pos); ++ nm.name = NULL; ++ dent = ubifs_tnc_next_ent(c, &key, &nm); ++ if (IS_ERR(dent)) { ++ err = PTR_ERR(dent); ++ goto out; ++ } ++ file->f_pos = key_hash_flash(c, &dent->key); ++ file->private_data = dent; ++ } ++ ++ while (1) { ++ dbg_gen("feed '%s', ino %llu, new f_pos %#x", ++ dent->name, le64_to_cpu(dent->inum), ++ key_hash_flash(c, &dent->key)); ++ ubifs_assert(dent->ch.sqnum > ubifs_inode(dir)->creat_sqnum); ++ ++ nm.len = le16_to_cpu(dent->nlen); ++ over = filldir(dirent, dent->name, nm.len, file->f_pos, ++ le64_to_cpu(dent->inum), ++ vfs_dent_type(dent->type)); ++ if (over) ++ return 0; ++ ++ /* Switch to the next entry */ ++ key_read(c, &dent->key, &key); ++ nm.name = dent->name; ++ dent = ubifs_tnc_next_ent(c, &key, &nm); ++ if (IS_ERR(dent)) { ++ err = PTR_ERR(dent); ++ goto out; ++ } ++ ++ kfree(file->private_data); ++ file->f_pos = key_hash_flash(c, &dent->key); ++ file->private_data = dent; ++ cond_resched(); ++ } ++ ++out: ++ if (err != -ENOENT) { ++ ubifs_err("cannot find next direntry, error %d", err); ++ return err; ++ } ++ ++ kfree(file->private_data); ++ file->private_data = NULL; ++ file->f_pos = 2; ++ return 0; ++} ++ ++/* If a directory is seeked, we have to free saved readdir() state */ ++loff_t ubifs_dir_llseek(struct file *file, loff_t offset, int origin) ++{ ++ kfree(file->private_data); ++ file->private_data = NULL; ++ return generic_file_llseek(file, offset, origin); ++} ++ ++/* Free saved readdir() state when the directory is closed */ ++static int ubifs_dir_release(struct inode *dir, struct file *file) ++{ ++ kfree(file->private_data); ++ file->private_data = NULL; ++ return 0; ++} ++ ++static int ubifs_link(struct dentry *old_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ struct ubifs_info *c = dir->i_sb->s_fs_info; ++ struct inode *inode = old_dentry->d_inode; ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ struct ubifs_budget_req req = { .new_dent = 1, .dirtied_ino = 1, ++ .dirtied_ino_d = ui->data_len }; ++ int err, sz_change = CALC_DENT_SIZE(dentry->d_name.len); ++ ++ dbg_gen("dent '%.*s' to ino %lu (nlink %d) in dir ino %lu", ++ dentry->d_name.len, dentry->d_name.name, inode->i_ino, ++ inode->i_nlink, dir->i_ino); ++ ++ err = ubifs_budget_inode_op(c, dir, &req); ++ if (err) ++ return err; ++ ++ inc_nlink(inode); ++ dir->i_size += sz_change; ++ inode->i_ctime = dir->i_mtime = dir->i_ctime = ++ ubifs_current_time(inode); ++ ++ err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, ++ IS_DIRSYNC(dir), 0); ++ if (err) ++ goto out_budg; ++ ++ atomic_inc(&inode->i_count); ++ d_instantiate(dentry, inode); ++ ubifs_release_ino_clean(c, dir, &req); ++ return 0; ++ ++out_budg: ++ dir->i_size -= sz_change; ++ ubifs_cancel_ino_op(c, dir, &req); ++ drop_nlink(inode); ++ iput(inode); ++ return err; ++} ++ ++static int ubifs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ struct ubifs_info *c = dir->i_sb->s_fs_info; ++ struct inode *inode = dentry->d_inode; ++ struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 1 }; ++ int sz_change = CALC_DENT_SIZE(dentry->d_name.len); ++ int err, budgeted = 1; ++ ++ dbg_gen("dent '%.*s' from ino %lu (nlink %d) in dir ino %lu", ++ dentry->d_name.len, dentry->d_name.name, inode->i_ino, ++ inode->i_nlink, dir->i_ino); ++ ++ err = ubifs_budget_inode_op(c, dir, &req); ++ if (err) { ++ if (err != -ENOSPC) ++ return err; ++ err = 0; ++ budgeted = 0; ++ } ++ ++ dir->i_size -= sz_change; ++ dir->i_mtime = dir->i_ctime = ubifs_current_time(dir); ++ ++ inode->i_ctime = dir->i_ctime; ++ drop_nlink(inode); ++ ++ err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 1, ++ IS_DIRSYNC(dir), 0); ++ if (err) ++ goto out_budg; ++ ++ if (budgeted) ++ ubifs_release_ino_clean(c, dir, &req); ++ ++ return 0; ++ ++out_budg: ++ dir->i_size += sz_change; ++ inc_nlink(inode); ++ if (budgeted) ++ ubifs_cancel_ino_op(c, dir, &req); ++ return err; ++} ++ ++/** ++ * check_dir_empty - check if a directory is empty or not. ++ * @c: UBIFS file-system description object ++ * @dir: VFS inode object of the directory to check ++ * ++ * This function checks if directory @dir is empty. Returns zero if the ++ * directory is empty, %-ENOTEMPTY if it is not, and other negative error codes ++ * in case of of errors. ++ */ ++static int check_dir_empty(struct ubifs_info *c, struct inode *dir) ++{ ++ struct qstr nm = { .name = NULL }; ++ struct ubifs_dent_node *dent; ++ union ubifs_key key; ++ int err; ++ ++ lowest_dent_key(c, &key, dir->i_ino); ++ dent = ubifs_tnc_next_ent(c, &key, &nm); ++ if (IS_ERR(dent)) { ++ err = PTR_ERR(dent); ++ if (err == -ENOENT) ++ err = 0; ++ } else { ++ kfree(dent); ++ err = -ENOTEMPTY; ++ } ++ ++ return err; ++} ++ ++static int ubifs_rmdir(struct inode *dir, struct dentry *dentry) ++{ ++ struct ubifs_info *c = dir->i_sb->s_fs_info; ++ struct inode *inode = dentry->d_inode; ++ struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 1 }; ++ int sz_change = CALC_DENT_SIZE(dentry->d_name.len); ++ int err, budgeted = 0; ++ ++ dbg_gen("directory '%.*s', ino %lu in dir ino %lu", dentry->d_name.len, ++ dentry->d_name.name, inode->i_ino, dir->i_ino); ++ ++ err = check_dir_empty(c, dentry->d_inode); ++ if (err) ++ return err; ++ ++ budgeted = 1; ++ err = ubifs_budget_inode_op(c, dir, &req); ++ if (err) { ++ if (err != -ENOSPC) ++ return err; ++ budgeted = 0; ++ } ++ ++ dir->i_size -= sz_change; ++ dir->i_mtime = dir->i_ctime = ubifs_current_time(dir); ++ drop_nlink(dir); ++ ++ inode->i_size = 0; ++ inode->i_ctime = dir->i_ctime; ++ clear_nlink(inode); ++ ++ err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 1, ++ IS_DIRSYNC(dir), 0); ++ if (err) ++ goto out_budg; ++ ++ if (budgeted) ++ ubifs_release_ino_clean(c, dir, &req); ++ ++ return 0; ++ ++out_budg: ++ dir->i_size += sz_change; ++ inc_nlink(dir); ++ inc_nlink(inode); ++ inc_nlink(inode); ++ if (budgeted) ++ ubifs_cancel_ino_op(c, dir, &req); ++ return err; ++} ++ ++static int ubifs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++{ ++ struct inode *inode; ++ struct ubifs_info *c = dir->i_sb->s_fs_info; ++ struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1 }; ++ int err, sz_change = CALC_DENT_SIZE(dentry->d_name.len); ++ ++ dbg_gen("dent '%.*s', mode %#x in dir ino %lu", ++ dentry->d_name.len, dentry->d_name.name, mode, dir->i_ino); ++ ++ err = ubifs_budget_inode_op(c, dir, &req); ++ if (err) ++ return err; ++ ++ inode = ubifs_new_inode(c, dir, S_IFDIR | mode); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ goto out_budg; ++ } ++ ++ insert_inode_hash(inode); ++ inc_nlink(inode); ++ ++ dir->i_mtime = dir->i_ctime = ubifs_current_time(dir); ++ dir->i_size += sz_change; ++ inc_nlink(dir); ++ ++ err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, ++ IS_DIRSYNC(dir), 0); ++ if (err) { ++ ubifs_err("cannot create directory, error %d", err); ++ goto out_inode; ++ } ++ ++ d_instantiate(dentry, inode); ++ ubifs_release_ino_clean(c, dir, &req); ++ return 0; ++ ++out_inode: ++ dir->i_size -= sz_change; ++ drop_nlink(dir); ++ make_bad_inode(inode); ++ iput(inode); ++out_budg: ++ ubifs_cancel_ino_op(c, dir, &req); ++ return err; ++} ++ ++static int ubifs_mknod(struct inode *dir, struct dentry *dentry, ++ int mode, dev_t rdev) ++{ ++ struct inode *inode; ++ struct ubifs_info *c = dir->i_sb->s_fs_info; ++ struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1 }; ++ union ubifs_dev_desc *dev = NULL; ++ int sz_change = CALC_DENT_SIZE(dentry->d_name.len); ++ int err, devlen = 0; ++ ++ dbg_gen("dent '%.*s' in dir ino %lu", ++ dentry->d_name.len, dentry->d_name.name, dir->i_ino); ++ ++ if (!new_valid_dev(rdev)) ++ return -EINVAL; ++ ++ if (S_ISBLK(mode) || S_ISCHR(mode)) { ++ dev = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS); ++ if (!dev) ++ return -ENOMEM; ++ devlen = ubifs_encode_dev(dev, rdev); ++ } ++ ++ err = ubifs_budget_inode_op(c, dir, &req); ++ if (err) { ++ kfree(dev); ++ return err; ++ } ++ ++ inode = ubifs_new_inode(c, dir, mode); ++ if (IS_ERR(inode)) { ++ kfree(dev); ++ err = PTR_ERR(inode); ++ goto out_budg; ++ } ++ ++ init_special_inode(inode, inode->i_mode, rdev); ++ ++ inode->i_size = devlen; ++ ubifs_inode(inode)->data = dev; ++ ubifs_inode(inode)->data_len = devlen; ++ ++ dir->i_size += sz_change; ++ ++ err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, ++ IS_DIRSYNC(dir), 0); ++ if (err) ++ goto out_inode; ++ ++ insert_inode_hash(inode); ++ d_instantiate(dentry, inode); ++ ubifs_release_ino_clean(c, dir, &req); ++ return 0; ++ ++out_inode: ++ dir->i_size -= sz_change; ++ make_bad_inode(inode); ++ iput(inode); ++out_budg: ++ ubifs_cancel_ino_op(c, dir, &req); ++ return err; ++} ++ ++static int ubifs_symlink(struct inode *dir, struct dentry *dentry, ++ const char *symname) ++{ ++ struct inode *inode; ++ struct ubifs_inode *ui; ++ struct ubifs_info *c = dir->i_sb->s_fs_info; ++ int err, len = strlen(symname); ++ int sz_change = CALC_DENT_SIZE(dentry->d_name.len); ++ struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1, ++ .new_ino_d = len }; ++ ++ dbg_gen("dent '%.*s', target '%s' in dir ino %lu", dentry->d_name.len, ++ dentry->d_name.name, symname, dir->i_ino); ++ ++ if (len > UBIFS_MAX_INO_DATA) ++ return -ENAMETOOLONG; ++ ++ err = ubifs_budget_inode_op(c, dir, &req); ++ if (err) ++ return err; ++ ++ inode = ubifs_new_inode(c, dir, S_IFLNK | S_IRWXUGO); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ goto out_budg; ++ } ++ ++ ui = ubifs_inode(inode); ++ ui->data = kmalloc(len + 1, GFP_NOFS); ++ if (!ui->data) { ++ err = -ENOMEM; ++ goto out_inode; ++ } ++ ++ memcpy(ui->data, symname, len); ++ ((char *)ui->data)[len] = '\0'; ++ /* ++ * The terminating zero byte is not written to the flash media and it ++ * is put just to make later in-memory string processing simpler. Thus, ++ * data length is @len, not @len + %1. ++ */ ++ ui->data_len = len; ++ inode->i_size = len; ++ ++ dir->i_size += sz_change; ++ ++ err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, ++ IS_DIRSYNC(dir), 0); ++ if (err) ++ goto out_dir; ++ ++ insert_inode_hash(inode); ++ d_instantiate(dentry, inode); ++ ubifs_release_ino_clean(c, dir, &req); ++ return 0; ++ ++out_dir: ++ dir->i_size -= sz_change; ++out_inode: ++ make_bad_inode(inode); ++ iput(inode); ++out_budg: ++ ubifs_cancel_ino_op(c, dir, &req); ++ return err; ++} ++ ++static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry) ++{ ++ struct ubifs_info *c = old_dir->i_sb->s_fs_info; ++ struct inode *old_inode = old_dentry->d_inode; ++ struct inode *new_inode = new_dentry->d_inode; ++ int err, move = (new_dir != old_dir); ++ int is_dir = S_ISDIR(old_inode->i_mode); ++ int unlink = !!new_inode; ++ int dirsync = (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)); ++ int new_sz = CALC_DENT_SIZE(new_dentry->d_name.len); ++ int old_sz = CALC_DENT_SIZE(old_dentry->d_name.len); ++ struct ubifs_budget_req req = { .new_dent = 1, .mod_dent = 1 }; ++ struct timespec time = ubifs_current_time(old_dir); ++ ++ dbg_gen("dent '%.*s' ino %lu in dir ino %lu to dent '%.*s' in " ++ "dir ino %lu", old_dentry->d_name.len, old_dentry->d_name.name, ++ old_inode->i_ino, old_dir->i_ino, new_dentry->d_name.len, ++ new_dentry->d_name.name, new_dir->i_ino); ++ ++ if (unlink && is_dir) { ++ err = check_dir_empty(c, new_inode); ++ if (err) ++ return err; ++ } ++ ++ if (move) { ++ req.dirtied_ino = 1; ++ if (unlink) { ++ req.dirtied_ino += 2; ++ req.dirtied_ino_d = ubifs_inode(new_inode)->data_len; ++ } ++ } ++ ++ /* ++ * Note, rename may write @new_dir inode if the directory entry is ++ * moved there. And if the @new_dir is dirty, we do not bother to make ++ * it clean. It could be done, but requires extra coding which does not ++ * seem to be really worth it. ++ */ ++ err = ubifs_budget_inode_op(c, old_dir, &req); ++ if (err) ++ return err; ++ ++ /* ++ * Like most other Unix systems, set the ctime for inodes on a ++ * rename. ++ */ ++ old_inode->i_ctime = time; ++ ++ /* ++ * If we moved a directory to another parent directory, decrement ++ * 'i_nlink' of the old parent. Also, update 'i_size' of the old parent ++ * as well as its [mc]time. ++ */ ++ if (is_dir && move) ++ drop_nlink(old_dir); ++ old_dir->i_size -= old_sz; ++ old_dir->i_mtime = old_dir->i_ctime = time; ++ new_dir->i_mtime = new_dir->i_ctime = time; ++ ++ /* ++ * If we moved a directory object to new directory, parent's 'i_nlink' ++ * should be adjusted. ++ */ ++ if (move && is_dir) ++ inc_nlink(new_dir); ++ ++ /* ++ * And finally, if we unlinked a direntry which happened to have the ++ * same name as the moved direntry, we have to decrement 'i_nlink' of ++ * the unlinked inode and change its ctime. ++ */ ++ if (unlink) { ++ /* ++ * Directories cannot have hard-links, so if this is a ++ * directory, decrement its 'i_nlink' twice because an empty ++ * directory has 'i_nlink' 2. ++ */ ++ if (is_dir) ++ drop_nlink(new_inode); ++ new_inode->i_ctime = time; ++ drop_nlink(new_inode); ++ } else ++ new_dir->i_size += new_sz; ++ ++ err = ubifs_jnl_rename(c, old_dir, old_dentry, new_dir, new_dentry, ++ dirsync); ++ if (err) ++ goto out_inode; ++ ++ ubifs_release_ino_clean(c, old_dir, &req); ++ return 0; ++ ++out_inode: ++ if (unlink) { ++ if (is_dir) ++ inc_nlink(new_inode); ++ inc_nlink(new_inode); ++ } else ++ new_dir->i_size -= new_sz; ++ old_dir->i_size += old_sz; ++ if (is_dir && move) { ++ drop_nlink(new_dir); ++ inc_nlink(old_dir); ++ } ++ ubifs_cancel_ino_op(c, old_dir, &req); ++ return err; ++} ++ ++int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry, ++ struct kstat *stat) ++{ ++ struct inode *inode = dentry->d_inode; ++ loff_t size; ++ ++ stat->dev = inode->i_sb->s_dev; ++ stat->ino = inode->i_ino; ++ stat->mode = inode->i_mode; ++ stat->nlink = inode->i_nlink; ++ stat->uid = inode->i_uid; ++ stat->gid = inode->i_gid; ++ stat->rdev = inode->i_rdev; ++ stat->atime = inode->i_atime; ++ stat->mtime = inode->i_mtime; ++ stat->ctime = inode->i_ctime; ++ stat->blksize = UBIFS_BLOCK_SIZE; ++ stat->size = i_size_read(inode); ++ ++ spin_lock(&inode->i_lock); ++ size = ubifs_inode(inode)->xattr_size; ++ spin_unlock(&inode->i_lock); ++ ++ /* ++ * Unfortunately, the 'stat()' system call was designed for block ++ * device based file systems, and it is not appropriate for UBIFS, ++ * because UBIFS does not have notion of "block". For example, it is ++ * difficult to tell how many block a directory takes - it actually ++ * takes less than 300 bytes, but we have to round it to block size, ++ * which introduces large mistake. This makes utilities like 'du' to ++ * report completely senseless numbers. This is the reason why UBIFS ++ * goes the same way as JFFS2 - it reports zero blocks for everything ++ * but regular files, which makes more sense than reporting completely ++ * wrong sizes. ++ */ ++ if (S_ISREG(inode->i_mode)) ++ size += stat->size; ++ ++ size = ALIGN(size, UBIFS_BLOCK_SIZE); ++ /* ++ * Note, user-space expects 512-byte blocks count irrespectively of what ++ * was reported in @stat->size. ++ */ ++ stat->blocks = size >> 9; ++ ++ return 0; ++} ++ ++struct inode_operations ubifs_dir_inode_operations = { ++ .lookup = ubifs_lookup, ++ .create = ubifs_create, ++ .link = ubifs_link, ++ .symlink = ubifs_symlink, ++ .unlink = ubifs_unlink, ++ .mkdir = ubifs_mkdir, ++ .rmdir = ubifs_rmdir, ++ .mknod = ubifs_mknod, ++ .rename = ubifs_rename, ++ .setattr = ubifs_setattr, ++ .getattr = ubifs_getattr, ++#ifdef CONFIG_UBIFS_FS_XATTR ++ .setxattr = ubifs_setxattr, ++ .getxattr = ubifs_getxattr, ++ .listxattr = ubifs_listxattr, ++ .removexattr = ubifs_removexattr, ++#endif ++}; ++ ++struct file_operations ubifs_dir_operations = { ++ .llseek = ubifs_dir_llseek, ++ .release = ubifs_dir_release, ++ .read = generic_read_dir, ++ .readdir = ubifs_readdir, ++ .fsync = ubifs_fsync, ++ .unlocked_ioctl = ubifs_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = ubifs_compat_ioctl, ++#endif ++}; +diff -urN linux-2.6.24.7.old/fs/ubifs/file.c linux-2.6.24.7/fs/ubifs/file.c +--- linux-2.6.24.7.old/fs/ubifs/file.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/file.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,983 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This file implements VFS file and inode operations of regular files, device ++ * nodes and symlinks as well as address space operations. ++ * ++ * UBIFS uses 2 page flags: PG_private and PG_checked. PG_private is set if the ++ * page is dirty and is used for budgeting purposes - dirty pages should not be ++ * budgeted. The PG_checked flag is set if full budgeting is required for the ++ * page e.g., when it corresponds to a file hole or it is just beyond the file ++ * size. The budgeting is done in 'ubifs_write_begin()', because it is OK to ++ * fail in this function, and the budget is released in 'ubifs_write_end()'. So ++ * the PG_private and PG_checked flags carry the information about how the page ++ * was budgeted, to make it possible to release the budget properly. ++ * ++ * A thing to keep in mind: inode's 'i_mutex' is locked in most VFS operations ++ * we implement. However, this is not true for '->writepage()', which might be ++ * called with 'i_mutex' unlocked. For example, when pdflush is performing ++ * write-back, it calls 'writepage()' with unlocked 'i_mutex', although the ++ * inode has 'I_LOCK' flag in this case. At "normal" work-paths 'i_mutex' is ++ * locked in '->writepage', e.g. in "sys_write -> alloc_pages -> direct reclaim ++ * path'. So, in '->writepage()' we are only guaranteed that the page is ++ * locked. ++ * ++ * Similarly, 'i_mutex' does not have to be locked in readpage(), e.g., ++ * readahead path does not have it locked ("sys_read -> generic_file_aio_read ++ * -> ondemand_readahead -> readpage"). In case of readahead, 'I_LOCK' flag is ++ * not set as well. ++ * ++ * This, for example means that there might be 2 concurrent '->writepage()' ++ * calls for the same inode, but different inode dirty pages. ++ */ ++ ++#include "ubifs.h" ++#include ++ ++static int read_block(struct inode *inode, void *addr, unsigned int block, ++ struct ubifs_data_node *dn) ++{ ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ int err, len, out_len; ++ union ubifs_key key; ++ unsigned int dlen; ++ ++ data_key_init(c, &key, inode->i_ino, block); ++ err = ubifs_tnc_lookup(c, &key, dn); ++ if (err) { ++ if (err == -ENOENT) ++ /* Not found, so it must be a hole */ ++ memset(addr, 0, UBIFS_BLOCK_SIZE); ++ return err; ++ } ++ ++ ubifs_assert(dn->ch.sqnum > ubifs_inode(inode)->creat_sqnum); ++ ++ len = le32_to_cpu(dn->size); ++ if (len <= 0 || len > UBIFS_BLOCK_SIZE) ++ goto dump; ++ ++ dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ; ++ out_len = UBIFS_BLOCK_SIZE; ++ err = ubifs_decompress(&dn->data, dlen, addr, &out_len, ++ le16_to_cpu(dn->compr_type)); ++ if (err || len != out_len) ++ goto dump; ++ ++ /* ++ * Data length can be less than a full block, even for blocks that are ++ * not the last in the file (e.g., as a result of making a hole and ++ * appending data). Ensure that the remainder is zeroed out. ++ */ ++ if (len < UBIFS_BLOCK_SIZE) ++ memset(addr + len, 0, UBIFS_BLOCK_SIZE - len); ++ ++ return 0; ++ ++dump: ++ ubifs_err("bad data node (block %u, inode %lu)", ++ block, inode->i_ino); ++ dbg_dump_node(c, dn); ++ return -EINVAL; ++} ++ ++/* TODO: remove compatibility stuff as late as possible */ ++#ifdef UBIFS_COMPAT_USE_OLD_PREPARE_WRITE ++int ubifs_do_readpage(struct page *page) ++#else ++static int do_readpage(struct page *page) ++#endif ++{ ++ void *addr; ++ int err = 0, i; ++ unsigned int block, beyond; ++ struct ubifs_data_node *dn; ++ struct inode *inode = page->mapping->host; ++ loff_t i_size = i_size_read(inode); ++ ++ dbg_gen("ino %lu, pg %lu, i_size %lld, flags %#lx", ++ inode->i_ino, page->index, i_size, page->flags); ++ ubifs_assert(!PageChecked(page)); ++ ubifs_assert(!PagePrivate(page)); ++ ++ addr = kmap(page); ++ ++ block = page->index << UBIFS_BLOCKS_PER_PAGE_SHIFT; ++ beyond = (i_size + UBIFS_BLOCK_SIZE - 1) >> UBIFS_BLOCK_SHIFT; ++ if (block >= beyond) { ++ /* Reading beyond inode */ ++ SetPageChecked(page); ++ memset(addr, 0, PAGE_CACHE_SIZE); ++ goto out; ++ } ++ ++ dn = kmalloc(UBIFS_MAX_DATA_NODE_SZ, GFP_NOFS); ++ if (!dn) { ++ err = -ENOMEM; ++ goto error; ++ } ++ ++ i = 0; ++ while (1) { ++ int ret; ++ ++ if (block >= beyond) { ++ /* Reading beyond inode */ ++ err = -ENOENT; ++ memset(addr, 0, UBIFS_BLOCK_SIZE); ++ } else { ++ ret = read_block(inode, addr, block, dn); ++ if (ret) { ++ err = ret; ++ if (err != -ENOENT) ++ break; ++ } ++ } ++ if (++i >= UBIFS_BLOCKS_PER_PAGE) ++ break; ++ block += 1; ++ addr += UBIFS_BLOCK_SIZE; ++ } ++ if (err) { ++ if (err == -ENOENT) { ++ /* Not found, so it must be a hole */ ++ SetPageChecked(page); ++ dbg_gen("hole"); ++ goto out_free; ++ } ++ ubifs_err("cannot read page %lu of inode %lu, error %d", ++ page->index, inode->i_ino, err); ++ goto error; ++ } ++ ++out_free: ++ kfree(dn); ++out: ++ SetPageUptodate(page); ++ ClearPageError(page); ++ flush_dcache_page(page); ++ kunmap(page); ++ return 0; ++ ++error: ++ kfree(dn); ++ ClearPageUptodate(page); ++ SetPageError(page); ++ flush_dcache_page(page); ++ kunmap(page); ++ return err; ++} ++ ++/* TODO: remove compatibility stuff as late as possible */ ++#ifndef UBIFS_COMPAT_USE_OLD_PREPARE_WRITE ++ ++static int ubifs_write_begin(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata) ++{ ++ struct inode *inode = mapping->host; ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ pgoff_t index = pos >> PAGE_CACHE_SHIFT; ++ struct ubifs_budget_req req = { .new_page = 1 }; ++ loff_t i_size = i_size_read(inode); ++ int uninitialized_var(err); ++ struct page *page; ++ ++ ubifs_assert(!(inode->i_sb->s_flags & MS_RDONLY)); ++ ++ if (unlikely(c->ro_media)) ++ return -EROFS; ++ ++ /* ++ * We are about to have a page of data written and we have to budget for ++ * this. The very important point here is that we have to budget before ++ * locking the page, because budgeting may force write-back, which ++ * would wait on locked pages and deadlock if we had the page locked. ++ * ++ * At this point we do not know anything about the page of data we are ++ * going to change, so assume the biggest budget (i.e., assume that ++ * this is a new page of data and it does not override an older page of ++ * data in the inode). Later the budget will be amended if this is not ++ * true. ++ */ ++ if (pos + len > i_size) ++ /* ++ * We are writing beyond the file which means we are going to ++ * change inode size and make the inode dirty. And in turn, ++ * this means we have to budget for making the inode dirty. ++ * ++ * Note, if the inode is already dirty, ++ * 'ubifs_budget_inode_op()' will not allocate any budget, ++ * but will just lock the @budg_mutex of the inode to prevent ++ * it from becoming clean before we have changed its size, ++ * which is going to happen in 'ubifs_write_end()'. ++ */ ++ err = ubifs_budget_inode_op(c, inode, &req); ++ else ++ /* ++ * The inode is not going to be marked as dirty by this write ++ * operation, do not budget for this. ++ */ ++ err = ubifs_budget_space(c, &req); ++ if (unlikely(err)) ++ return err; ++ ++ page = __grab_cache_page(mapping, index); ++ if (unlikely(!page)) { ++ err = -ENOMEM; ++ goto out_release; ++ } ++ ++ if (!PageUptodate(page)) { ++ /* ++ * The page is not loaded from the flash and has to be loaded ++ * unless we are writing all of it. ++ */ ++ if (!(pos & PAGE_CACHE_MASK) && len == PAGE_CACHE_SIZE) ++ /* ++ * Set the PG_checked flag to make the further code ++ * assume the page is new. ++ */ ++ SetPageChecked(page); ++ else { ++ err = do_readpage(page); ++ if (err) ++ goto out_unlock; ++ } ++ ++ SetPageUptodate(page); ++ ClearPageError(page); ++ } ++ ++ if (PagePrivate(page)) ++ /* ++ * The page is dirty, which means it was budgeted twice: ++ * o first time the budget was allocated by the task which ++ * made the page dirty and set the PG_private flag; ++ * o and then we budgeted for it for the second time at the ++ * very beginning of this function. ++ * ++ * So what we have to do is to release the page budget we ++ * allocated. ++ * ++ * Note, the page write operation may change the inode length, ++ * which makes it dirty and means the budget should be ++ * allocated. This was done above in the "pos + len > i_size" ++ * case. If this was done, we do not free the the inode budget, ++ * because we cannot as we are really going to mark it dirty in ++ * the 'ubifs_write_end()' function. ++ */ ++ ubifs_release_new_page_budget(c); ++ else if (!PageChecked(page)) ++ /* ++ * The page is not new, which means we are changing the page ++ * which already exists on the media. This means that changing ++ * the page does not make the amount of indexing information ++ * larger, and this part of the budget which we have already ++ * acquired may be released. ++ */ ++ ubifs_convert_page_budget(c); ++ ++ *pagep = page; ++ return 0; ++ ++out_unlock: ++ unlock_page(page); ++ page_cache_release(page); ++out_release: ++ if (pos + len > i_size) ++ ubifs_cancel_ino_op(c, inode, &req); ++ else ++ ubifs_release_budget(c, &req); ++ return err; ++} ++ ++static int ubifs_write_end(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *page, void *fsdata) ++{ ++ struct inode *inode = mapping->host; ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ loff_t i_size = i_size_read(inode); ++ ++ dbg_gen("ino %lu, pos %llu, pg %lu, len %u, copied %d, i_size %lld", ++ inode->i_ino, pos, page->index, len, copied, i_size); ++ ++ if (unlikely(copied < len && len == PAGE_CACHE_SIZE)) { ++ /* ++ * VFS copied less data to the page that it intended and ++ * declared in its '->write_begin()' call via the @len ++ * argument. If the page was not up-to-date, and @len was ++ * @PAGE_CACHE_SIZE, the 'ubifs_write_begin()' function did ++ * not load it from the media (for optimization reasons). This ++ * means that part of the page contains garbage. So read the ++ * page now. ++ */ ++ dbg_gen("copied %d instead of %d, read page and repeat", ++ copied, len); ++ ++ if (pos + len > i_size) ++ /* See a comment below about this hacky unlock */ ++ mutex_unlock(&ui->budg_mutex); ++ ++ copied = do_readpage(page); ++ ++ /* ++ * Return 0 to force VFS to repeat the whole operation, or the ++ * error code if 'do_readpage()' failed. ++ */ ++ goto out; ++ } ++ ++ if (!PagePrivate(page)) { ++ SetPagePrivate(page); ++ atomic_long_inc(&c->dirty_pg_cnt); ++ __set_page_dirty_nobuffers(page); ++ } ++ ++ if (pos + len > i_size) { ++ i_size_write(inode, pos + len); ++ ++ /* ++ * Note, we do not set @I_DIRTY_PAGES (which means that the ++ * inode has dirty pages), this has been done in ++ * '__set_page_dirty_nobuffers()'. ++ */ ++ mark_inode_dirty_sync(inode); ++ ++ /* ++ * The inode has been marked dirty, unlock it. This is a bit ++ * hacky because normally we would have to call ++ * 'ubifs_release_ino_dirty()'. But we know there is nothing ++ * to release because page's budget will be released in ++ * 'ubifs_write_page()' and inode's budget will be released in ++ * 'ubifs_write_inode()', so just unlock the inode here for ++ * optimization. ++ */ ++ mutex_unlock(&ui->budg_mutex); ++ } ++ ++out: ++ unlock_page(page); ++ page_cache_release(page); ++ return copied; ++} ++ ++#endif /* UBIFS_COMPAT_USE_OLD_PREPARE_WRITE */ ++ ++static int ubifs_readpage(struct file *file, struct page *page) ++{ ++ do_readpage(page); ++ unlock_page(page); ++ return 0; ++} ++ ++/** ++ * release_existing_page_budget - release budget of an existing page. ++ * @c: UBIFS file-system description object ++ * ++ * This is a helper function which releases budget corresponding to the budget ++ * of changing one one page of data which already exists on the flash media. ++ * ++ * This function was not moved to "budget.c" because there is only one user. ++ */ ++static void release_existing_page_budget(struct ubifs_info *c) ++{ ++ struct ubifs_budget_req req = { .dd_growth = c->page_budget}; ++ ++ ubifs_release_budget(c, &req); ++} ++ ++static int do_writepage(struct page *page, int len) ++{ ++ int err = 0, i, blen; ++ unsigned int block; ++ void *addr; ++ union ubifs_key key; ++ struct inode *inode = page->mapping->host; ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ ++ /* Update radix tree tags */ ++ set_page_writeback(page); ++ ++ addr = kmap(page); ++ ++ block = page->index << UBIFS_BLOCKS_PER_PAGE_SHIFT; ++ i = 0; ++ while (len) { ++ blen = min_t(int, len, UBIFS_BLOCK_SIZE); ++ data_key_init(c, &key, inode->i_ino, block); ++ err = ubifs_jnl_write_data(c, inode, &key, addr, blen); ++ if (err) ++ break; ++ if (++i >= UBIFS_BLOCKS_PER_PAGE) ++ break; ++ block += 1; ++ addr += blen; ++ len -= blen; ++ } ++ if (err) { ++ SetPageError(page); ++ ubifs_err("cannot write page %lu of inode %lu, error %d", ++ page->index, inode->i_ino, err); ++ ubifs_ro_mode(c, err); ++ } ++ ++ ubifs_assert(PagePrivate(page)); ++ if (PageChecked(page)) ++ ubifs_release_new_page_budget(c); ++ else ++ release_existing_page_budget(c); ++ ++ atomic_long_dec(&c->dirty_pg_cnt); ++ ClearPagePrivate(page); ++ ClearPageChecked(page); ++ ++ kunmap(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ ++ return err; ++} ++ ++static int ubifs_writepage(struct page *page, struct writeback_control *wbc) ++{ ++ struct inode *inode = page->mapping->host; ++ loff_t i_size = i_size_read(inode); ++ pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT; ++ int len; ++ void *kaddr; ++ ++ dbg_gen("ino %lu, pg %lu, pg flags %#lx", ++ inode->i_ino, page->index, page->flags); ++ ubifs_assert(PagePrivate(page)); ++ ++ /* Is the page fully inside i_size? */ ++ if (page->index < end_index) ++ return do_writepage(page, PAGE_CACHE_SIZE); ++ ++ /* Is the page fully outside i_size? (truncate in progress) */ ++ len = i_size & (PAGE_CACHE_SIZE - 1); ++ if (page->index >= end_index + 1 || !len) { ++ unlock_page(page); ++ return 0; ++ } ++ ++ /* ++ * The page straddles i_size. It must be zeroed out on each and every ++ * writepage invocation because it may be mmapped. "A file is mapped ++ * in multiples of the page size. For a file that is not a multiple of ++ * the page size, the remaining memory is zeroed when mapped, and ++ * writes to that region are not written out to the file." ++ */ ++ kaddr = kmap_atomic(page, KM_USER0); ++ memset(kaddr + len, 0, PAGE_CACHE_SIZE - len); ++ flush_dcache_page(page); ++ kunmap_atomic(kaddr, KM_USER0); ++ ++ return do_writepage(page, len); ++} ++ ++static int ubifs_trunc(struct inode *inode, loff_t new_size) ++{ ++ loff_t old_size; ++ int err; ++ ++ dbg_gen("ino %lu, size %lld -> %lld", ++ inode->i_ino, inode->i_size, new_size); ++ old_size = inode->i_size; ++ ++ err = vmtruncate(inode, new_size); ++ if (err) ++ return err; ++ ++ if (new_size < old_size) { ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ int offset = new_size & (UBIFS_BLOCK_SIZE - 1); ++ ++ if (offset) { ++ pgoff_t index = new_size >> PAGE_CACHE_SHIFT; ++ struct page *page; ++ ++ page = find_lock_page(inode->i_mapping, index); ++ if (page) { ++ if (PageDirty(page)) { ++ ubifs_assert(PagePrivate(page)); ++ ++ clear_page_dirty_for_io(page); ++ if (UBIFS_BLOCKS_PER_PAGE_SHIFT) ++ offset = new_size & ++ (PAGE_CACHE_SIZE - 1); ++ err = do_writepage(page, offset); ++ page_cache_release(page); ++ if (err) ++ return err; ++ /* ++ * We could now tell ubifs_jnl_truncate ++ * not to read the last block. ++ */ ++ } else { ++ /* ++ * We could 'kmap()' the page and ++ * pass the data to ubifs_jnl_truncate ++ * to save it from having to read it. ++ */ ++ unlock_page(page); ++ page_cache_release(page); ++ } ++ } ++ } ++ err = ubifs_jnl_truncate(c, inode->i_ino, old_size, new_size); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++int ubifs_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ unsigned int ia_valid = attr->ia_valid; ++ struct inode *inode = dentry->d_inode; ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ struct ubifs_budget_req req; ++ int truncation, err = 0; ++ ++ dbg_gen("ino %lu, ia_valid %#x", inode->i_ino, ia_valid); ++ err = inode_change_ok(inode, attr); ++ if (err) ++ return err; ++ ++ memset(&req, 0, sizeof(struct ubifs_budget_req)); ++ ++ /* ++ * If this is truncation, and we do not truncate on a block boundary, ++ * budget for changing one data block, because the last block will be ++ * re-written. ++ */ ++ truncation = (ia_valid & ATTR_SIZE) && attr->ia_size != inode->i_size; ++ if (truncation && attr->ia_size < inode->i_size && ++ (attr->ia_size & (UBIFS_BLOCK_SIZE - 1))) ++ req.dirtied_page = 1; ++ ++ err = ubifs_budget_inode_op(c, inode, &req); ++ if (err) ++ return err; ++ ++ if (truncation) { ++ err = ubifs_trunc(inode, attr->ia_size); ++ if (err) { ++ ubifs_cancel_ino_op(c, inode, &req); ++ return err; ++ } ++ ++ inode->i_mtime = inode->i_ctime = ubifs_current_time(inode); ++ } ++ ++ if (ia_valid & ATTR_UID) ++ inode->i_uid = attr->ia_uid; ++ if (ia_valid & ATTR_GID) ++ inode->i_gid = attr->ia_gid; ++ if (ia_valid & ATTR_ATIME) ++ inode->i_atime = timespec_trunc(attr->ia_atime, ++ inode->i_sb->s_time_gran); ++ if (ia_valid & ATTR_MTIME) ++ inode->i_mtime = timespec_trunc(attr->ia_mtime, ++ inode->i_sb->s_time_gran); ++ if (ia_valid & ATTR_CTIME) ++ inode->i_ctime = timespec_trunc(attr->ia_ctime, ++ inode->i_sb->s_time_gran); ++ if (ia_valid & ATTR_MODE) { ++ umode_t mode = attr->ia_mode; ++ ++ if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) ++ mode &= ~S_ISGID; ++ inode->i_mode = mode; ++ } ++ ++ mark_inode_dirty_sync(inode); ++ ubifs_release_ino_dirty(c, inode, &req); ++ ++ if (req.dirtied_page) { ++ /* ++ * Truncation code does not make the reenacted page dirty, it ++ * just changes it on journal level, so we have to release page ++ * change budget. ++ */ ++ memset(&req, 0, sizeof(struct ubifs_budget_req)); ++ req.dd_growth = c->page_budget; ++ ubifs_release_budget(c, &req); ++ } ++ ++ if (IS_SYNC(inode)) ++ err = write_inode_now(inode, 1); ++ ++ return err; ++} ++ ++static void ubifs_invalidatepage(struct page *page, unsigned long offset) ++{ ++ struct inode *inode = page->mapping->host; ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ struct ubifs_budget_req req; ++ ++ ubifs_assert(PagePrivate(page)); ++ if (offset) ++ /* Partial page remains dirty */ ++ return; ++ ++ memset(&req, 0, sizeof(struct ubifs_budget_req)); ++ if (PageChecked(page)) { ++ req.new_page = 1; ++ req.idx_growth = -1; ++ req.data_growth = c->page_budget; ++ } else ++ req.dd_growth = c->page_budget; ++ ubifs_release_budget(c, &req); ++ ++ atomic_long_dec(&c->dirty_pg_cnt); ++ ClearPagePrivate(page); ++ ClearPageChecked(page); ++} ++ ++static void *ubifs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ struct ubifs_inode *ui = ubifs_inode(dentry->d_inode); ++ ++ nd_set_link(nd, ui->data); ++ return NULL; ++} ++ ++int ubifs_fsync(struct file *file, struct dentry *dentry, int datasync) ++{ ++ struct inode *inode = dentry->d_inode; ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ int err; ++ ++ dbg_gen("syncing inode %lu", inode->i_ino); ++ ++ /* Synchronize the inode and dirty pages */ ++ err = write_inode_now(inode, 1); ++ if (err) ++ return err; ++ ++ /* ++ * Some data related to this inode may still sit in a write-buffer. ++ * Flush them. ++ */ ++ err = ubifs_sync_wbufs_by_inodes(c, &inode, 1); ++ if (err) ++ return err; ++ ++ return 0; ++} ++ ++/** ++ * mctime_update_needed - check if mtime or ctime update is needed. ++ * @inode: the inode to do the check for ++ * @now: current time ++ * ++ * This helper function checks if the inode mtime/ctime should be updated or ++ * not. If current values of the time-stamps are within the UBIFS inode time ++ * granularity, they are not updated. This is an optimization. ++ */ ++static inline int mctime_update_needed(struct inode *inode, ++ struct timespec *now) ++{ ++ if (!timespec_equal(&inode->i_mtime, now) || ++ !timespec_equal(&inode->i_ctime, now)) ++ return 1; ++ return 0; ++} ++ ++/** ++ * update_ctime - update mtime and ctime of an inode. ++ * @c: UBIFS file-system description object ++ * @inode: inode to update ++ * ++ * This function updates mtime and ctime of the inode if it is not equivalent to ++ * current time. Returns zero in case of success and a negative error code in ++ * case of failure. ++ */ ++static int update_mctime(struct ubifs_info *c, struct inode *inode) ++{ ++ struct timespec now = ubifs_current_time(inode); ++ ++ if (mctime_update_needed(inode, &now)) { ++ struct ubifs_budget_req req; ++ int err; ++ ++ memset(&req, 0, sizeof(struct ubifs_budget_req)); ++ err = ubifs_budget_inode_op(c, inode, &req); ++ if (err) ++ return err; ++ ++ inode->i_mtime = inode->i_ctime = now; ++ mark_inode_dirty_sync(inode); ++ mutex_unlock(&ubifs_inode(inode)->budg_mutex); ++ } ++ ++ return 0; ++} ++ ++static ssize_t ubifs_write(struct file *file, const char __user *buf, ++ size_t len, loff_t *ppos) ++{ ++ int err; ++ ssize_t ret; ++ struct inode *inode = file->f_mapping->host; ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ ++ err = update_mctime(c, inode); ++ if (err) ++ return err; ++ ++ ret = do_sync_write(file, buf, len, ppos); ++ if (ret < 0) ++ return ret; ++ ++ if (ret > 0 && IS_SYNC(inode)) { ++ err = ubifs_sync_wbufs_by_inodes(c, &inode, 1); ++ if (err) ++ return err; ++ } ++ ++ return ret; ++} ++ ++static ssize_t ubifs_aio_write(struct kiocb *iocb, const struct iovec *iov, ++ unsigned long nr_segs, loff_t pos) ++{ ++ int err; ++ ssize_t ret; ++ struct inode *inode = iocb->ki_filp->f_mapping->host; ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ ++ err = update_mctime(c, inode); ++ if (err) ++ return err; ++ ++ ret = generic_file_aio_write(iocb, iov, nr_segs, pos); ++ if (ret < 0) ++ return ret; ++ ++ if (ret > 0 && IS_SYNC(inode)) { ++ err = ubifs_sync_wbufs_by_inodes(c, &inode, 1); ++ if (err) ++ return err; ++ } ++ ++ return ret; ++} ++ ++static int ubifs_set_page_dirty(struct page *page) ++{ ++ int ret; ++ ++ ret = __set_page_dirty_nobuffers(page); ++ /* ++ * An attempt to dirty a page without budgeting for it - should not ++ * happen. ++ */ ++ ubifs_assert(ret == 0); ++ return ret; ++} ++ ++static int ubifs_releasepage(struct page *page, gfp_t unused_gfp_flags) ++{ ++ /* ++ * An attempt to release a dirty page without budgeting for it - should ++ * not happen. ++ */ ++ if (PageWriteback(page)) ++ return 0; ++ ubifs_assert(PagePrivate(page)); ++ ubifs_assert(0); ++ ClearPagePrivate(page); ++ ClearPageChecked(page); ++ return 1; ++} ++ ++#ifndef UBIFS_COMPAT_NO_SHARED_MMAP ++ ++/* ++ * mmap()d file has taken write protection fault and is being made ++ * writable. UBIFS must ensure page is budgeted for. ++ */ ++static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page) ++{ ++ struct inode *inode = vma->vm_file->f_path.dentry->d_inode; ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ struct timespec now = ubifs_current_time(inode); ++ struct ubifs_budget_req req = { .new_page = 1 }; ++ int err, update_time; ++ ++ dbg_gen("ino %lu, pg %lu, i_size %lld", inode->i_ino, page->index, ++ i_size_read(inode)); ++ ubifs_assert(!(inode->i_sb->s_flags & MS_RDONLY)); ++ ++ if (unlikely(c->ro_media)) ++ return -EROFS; ++ ++ /* ++ * We have not locked @page so far so we may budget for changing the ++ * page. Note, we cannot do this after we locked the page, because ++ * budgeting may cause write-back which would cause deadlock. ++ * ++ * At the moment we do not know whether the page is dirty or not, so we ++ * assume that it is not and budget for a new page. We could look at ++ * the @PG_private flag and figure this out, but we may race with write ++ * back and the page state may change by the time we lock it, so this ++ * would need additional care. We do not bother with this at the ++ * moment, although it might be good idea to do. Instead, we allocate ++ * budget for a new page and amend it later on if the page was in fact ++ * dirty. ++ * ++ * The budgeting-related logic of this function is similar to what we ++ * do in 'ubifs_write_begin()' and 'ubifs_write_end()'. Glance there ++ * for more comments. ++ */ ++ if (mctime_update_needed(inode, &now)) { ++ /* ++ * We have to change inode time stamp which requires extra ++ * budgeting. ++ */ ++ update_time = 1; ++ err = ubifs_budget_inode_op(c, inode, &req); ++ } else { ++ update_time = 0; ++ err = ubifs_budget_space(c, &req); ++ } ++ if (unlikely(err)) { ++ if (err == -ENOSPC) ++ ubifs_warn("out of space for mmapped file " ++ "(inode number %lu)", inode->i_ino); ++ return err; ++ } ++ ++ lock_page(page); ++ if (unlikely(page->mapping != inode->i_mapping || ++ page_offset(page) > i_size_read(inode))) { ++ /* Page got truncated out from underneath us */ ++ err = -EINVAL; ++ goto out_unlock; ++ } ++ ++ if (PagePrivate(page)) ++ ubifs_release_new_page_budget(c); ++ else { ++ if (!PageChecked(page)) ++ ubifs_convert_page_budget(c); ++ SetPagePrivate(page); ++ atomic_long_inc(&c->dirty_pg_cnt); ++ __set_page_dirty_nobuffers(page); ++ } ++ ++ if (update_time) { ++ inode->i_mtime = inode->i_ctime = now; ++ mark_inode_dirty_sync(inode); ++ mutex_unlock(&ubifs_inode(inode)->budg_mutex); ++ } ++ ++ unlock_page(page); ++ return 0; ++ ++out_unlock: ++ unlock_page(page); ++ if (update_time) ++ ubifs_cancel_ino_op(c, inode, &req); ++ else ++ ubifs_release_budget(c, &req); ++ return err; ++} ++ ++struct vm_operations_struct ubifs_file_vm_ops = { ++#if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,22)) ++ .nopage = filemap_nopage, ++#else ++ .fault = filemap_fault, ++#endif ++ .page_mkwrite = ubifs_vm_page_mkwrite, ++}; ++ ++static int ubifs_file_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int err; ++ ++ /* 'generic_file_mmap()' takes care of NOMMU case */ ++ err = generic_file_mmap(file, vma); ++ if (err) ++ return err; ++ vma->vm_ops = &ubifs_file_vm_ops; ++ return 0; ++} ++#endif ++ ++struct address_space_operations ubifs_file_address_operations = { ++ .readpage = ubifs_readpage, ++ .writepage = ubifs_writepage, ++/* TODO: remove compatibility stuff as late as possible */ ++#ifdef UBIFS_COMPAT_USE_OLD_PREPARE_WRITE ++ .prepare_write = ubifs_prepare_write, ++ .commit_write = ubifs_commit_write, ++#else ++ .write_begin = ubifs_write_begin, ++ .write_end = ubifs_write_end, ++#endif ++ .invalidatepage = ubifs_invalidatepage, ++ .set_page_dirty = ubifs_set_page_dirty, ++ .releasepage = ubifs_releasepage, ++}; ++ ++struct inode_operations ubifs_file_inode_operations = { ++ .setattr = ubifs_setattr, ++ .getattr = ubifs_getattr, ++#ifdef CONFIG_UBIFS_FS_XATTR ++ .setxattr = ubifs_setxattr, ++ .getxattr = ubifs_getxattr, ++ .listxattr = ubifs_listxattr, ++ .removexattr = ubifs_removexattr, ++#endif ++}; ++ ++struct inode_operations ubifs_symlink_inode_operations = { ++ .readlink = generic_readlink, ++ .follow_link = ubifs_follow_link, ++ .setattr = ubifs_setattr, ++ .getattr = ubifs_getattr, ++}; ++ ++struct file_operations ubifs_file_operations = { ++ .llseek = generic_file_llseek, ++ .read = do_sync_read, ++ .write = ubifs_write, ++ .aio_read = generic_file_aio_read, ++ .aio_write = ubifs_aio_write, ++ .mmap = ubifs_file_mmap, ++ .fsync = ubifs_fsync, ++ .unlocked_ioctl = ubifs_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = ubifs_compat_ioctl, ++#endif ++}; +diff -urN linux-2.6.24.7.old/fs/ubifs/find.c linux-2.6.24.7/fs/ubifs/find.c +--- linux-2.6.24.7.old/fs/ubifs/find.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/find.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,971 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This file contains functions for finding LEBs for various purposes e.g. ++ * garbage collection. In general, lprops category heaps and lists are used ++ * for fast access, falling back on scanning the LPT as a last resort. ++ */ ++ ++#include ++#include "ubifs.h" ++ ++/** ++ * struct scan_data - data provided to scan callback functions ++ * @min_space: minimum number of bytes for which to scan ++ * @pick_free: whether it is OK to scan for empty LEBs ++ * @lnum: LEB number found is returned here ++ * @exclude_index: whether to exclude index LEBs ++ */ ++struct scan_data { ++ int min_space; ++ int pick_free; ++ int lnum; ++ int exclude_index; ++}; ++ ++/** ++ * valuable - determine whether LEB properties are valuable. ++ * @c: the UBIFS file-system description object ++ * @lprops: LEB properties ++ * ++ * This function return %1 if the LEB properties should be added to the LEB ++ * properties tree in memory. Otherwise %0 is returned. ++ */ ++static int valuable(struct ubifs_info *c, const struct ubifs_lprops *lprops) ++{ ++ int n, cat = lprops->flags & LPROPS_CAT_MASK; ++ struct ubifs_lpt_heap *heap; ++ ++ switch (cat) { ++ case LPROPS_DIRTY: ++ case LPROPS_DIRTY_IDX: ++ case LPROPS_FREE: ++ heap = &c->lpt_heap[cat - 1]; ++ if (heap->cnt < heap->max_cnt) ++ return 1; ++ if (lprops->free + lprops->dirty >= c->dark_wm) ++ return 1; ++ return 0; ++ case LPROPS_EMPTY: ++ n = c->lst.empty_lebs + c->freeable_cnt - ++ c->lst.taken_empty_lebs; ++ if (n < c->lsave_cnt) ++ return 1; ++ return 0; ++ case LPROPS_FREEABLE: ++ return 1; ++ case LPROPS_FRDI_IDX: ++ return 1; ++ } ++ return 0; ++} ++ ++/** ++ * scan_for_dirty_cb - dirty space scan callback. ++ * @c: the UBIFS file-system description object ++ * @lprops: LEB properties to scan ++ * @in_tree: whether the LEB properties are in main memory ++ * @data: information passed to and from the caller of the scan ++ * ++ * This function returns a code that indicates whether the scan should continue ++ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree ++ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop ++ * (%LPT_SCAN_STOP). ++ */ ++static int scan_for_dirty_cb(struct ubifs_info *c, ++ const struct ubifs_lprops *lprops, int in_tree, ++ struct scan_data *data) ++{ ++ int ret = LPT_SCAN_CONTINUE; ++ ++ /* Exclude LEBs that are currently in use */ ++ if (lprops->flags & LPROPS_TAKEN) ++ return LPT_SCAN_CONTINUE; ++ /* Determine whether to add these LEB properties to the tree */ ++ if (!in_tree && valuable(c, lprops)) ++ ret |= LPT_SCAN_ADD; ++ /* Exclude LEBs with too little space */ ++ if (lprops->free + lprops->dirty < data->min_space) ++ return ret; ++ /* If specified, exclude index LEBs */ ++ if (data->exclude_index && lprops->flags & LPROPS_INDEX) ++ return ret; ++ /* If specified, exclude empty or freeable LEBs */ ++ if (!data->pick_free && lprops->free + lprops->dirty == c->leb_size) ++ return ret; ++ /* Exclude LEBs with too little dirty space (unless it is empty) */ ++ if (lprops->dirty < c->dead_wm && lprops->free != c->leb_size) ++ return ret; ++ /* Finally we found space */ ++ data->lnum = lprops->lnum; ++ return LPT_SCAN_ADD | LPT_SCAN_STOP; ++} ++ ++/** ++ * scan_for_dirty - find a data LEB with free space. ++ * @c: the UBIFS file-system description object ++ * @min_space: minimum amount free plus dirty space the returned LEB has to ++ * have ++ * @pick_free: if it is OK to return a free or freeable LEB ++ * @exclude_index: whether to exclude index LEBs ++ * ++ * This function returns a pointer to the LEB properties found or a negative ++ * error code. ++ */ ++static const struct ubifs_lprops *scan_for_dirty(struct ubifs_info *c, ++ int min_space, int pick_free, ++ int exclude_index) ++{ ++ const struct ubifs_lprops *lprops; ++ struct ubifs_lpt_heap *heap; ++ struct scan_data data; ++ int err, i; ++ ++ /* There may be an LEB with enough dirty space on the free heap */ ++ heap = &c->lpt_heap[LPROPS_FREE - 1]; ++ for (i = 0; i < heap->cnt; i++) { ++ lprops = heap->arr[i]; ++ if (lprops->free + lprops->dirty < min_space) ++ continue; ++ if (lprops->dirty < c->dead_wm) ++ continue; ++ return lprops; ++ } ++ /* ++ * A LEB may have fallen off of the bottom of the dirty heap, and ended ++ * up as uncategorized even though it has enough dirty space for us now, ++ * so check the uncategorized list. N.B. neither empty nor freeable LEBs ++ * can end up as uncategorized because they are kept on lists not ++ * finite-sized heaps. ++ */ ++ list_for_each_entry(lprops, &c->uncat_list, list) { ++ if (lprops->flags & LPROPS_TAKEN) ++ continue; ++ if (lprops->free + lprops->dirty < min_space) ++ continue; ++ if (exclude_index && (lprops->flags & LPROPS_INDEX)) ++ continue; ++ if (lprops->dirty < c->dead_wm) ++ continue; ++ return lprops; ++ } ++ /* We have looked everywhere in main memory, now scan the flash */ ++ if (c->pnodes_have >= c->pnode_cnt) ++ /* All pnodes are in memory, so skip scan */ ++ return ERR_PTR(-ENOSPC); ++ data.min_space = min_space; ++ data.pick_free = pick_free; ++ data.lnum = -1; ++ data.exclude_index = exclude_index; ++ err = ubifs_lpt_scan_nolock(c, -1, c->lscan_lnum, ++ (ubifs_lpt_scan_callback)scan_for_dirty_cb, ++ &data); ++ if (err) ++ return ERR_PTR(err); ++ ubifs_assert(data.lnum >= c->main_first && data.lnum < c->leb_cnt); ++ c->lscan_lnum = data.lnum; ++ lprops = ubifs_lpt_lookup_dirty(c, data.lnum); ++ if (IS_ERR(lprops)) ++ return lprops; ++ ubifs_assert(lprops->lnum == data.lnum); ++ ubifs_assert(lprops->free + lprops->dirty >= min_space); ++ ubifs_assert(lprops->dirty >= c->dead_wm); ++ ubifs_assert(!(lprops->flags & LPROPS_TAKEN)); ++ ubifs_assert(!(lprops->flags & LPROPS_INDEX)); ++ return lprops; ++} ++ ++/** ++ * ubifs_find_dirty_leb - find a dirty LEB for the Garbage Collector. ++ * @c: the UBIFS file-system description object ++ * @ret_lp: LEB properties are returned here on exit ++ * @min_space: minimum amount free plus dirty space the returned LEB has to ++ * have ++ * @pick_free: controls whether it is OK to pick empty or index LEBs ++ * ++ * This function tries to find a dirty logical eraseblock which has at least ++ * @min_space free and dirty space. It prefers to take an LEB from the dirty or ++ * dirty index heap, and it falls-back to LPT scanning if the heaps are empty ++ * or do not have an LEB which satisfies the @min_space criteria. ++ * ++ * Note: ++ * o LEBs which have less than dead watermark of dirty space are never picked ++ * by this function; ++ * ++ * Returns zero and the LEB properties of ++ * found dirty LEB in case of success, %-ENOSPC if no dirty LEB was found and a ++ * negative error code in case of other failures. The returned LEB is marked as ++ * "taken". ++ * ++ * The additional @pick_free argument controls if this function has to return a ++ * free or freeable LEB if one is present. For example, GC must to set it to %1, ++ * when called from the journal space reservation function, because the ++ * appearance of free space may coincide with the loss of enough dirty space ++ * for GC to succeed anyway. ++ * ++ * In contrast, if the Garbage Collector is called from budgeting, it should ++ * just make free space, not return LEBs which are already free or freeable. ++ * ++ * In addition @pick_free is set to %2 by the recovery process in order to ++ * recover gc_lnum in which case an index LEB must not be returned. ++ */ ++int ubifs_find_dirty_leb(struct ubifs_info *c, struct ubifs_lprops *ret_lp, ++ int min_space, int pick_free) ++{ ++ int err = 0, sum, exclude_index = pick_free == 2 ? 1 : 0; ++ const struct ubifs_lprops *lp = NULL, *idx_lp = NULL; ++ struct ubifs_lpt_heap *heap, *idx_heap; ++ ++ ubifs_get_lprops(c); ++ ++ if (pick_free) { ++ int lebs, rsvd_idx_lebs = 0; ++ ++ spin_lock(&c->space_lock); ++ lebs = c->lst.empty_lebs; ++ lebs += c->freeable_cnt - c->lst.taken_empty_lebs; ++ ++ /* ++ * Note, the index may consume more LEBs than have been reserved ++ * for it. It is OK because it might be consolidated by GC. ++ * But if the index takes fewer LEBs than it is reserved for it, ++ * this function must avoid picking those reserved LEBs. ++ */ ++ if (c->min_idx_lebs >= c->lst.idx_lebs) { ++ rsvd_idx_lebs = c->min_idx_lebs - c->lst.idx_lebs; ++ exclude_index = 1; ++ } ++ spin_unlock(&c->space_lock); ++ ++ /* Check if there are enough free LEBs for the index */ ++ if (rsvd_idx_lebs < lebs) { ++ /* OK, try to find an empty LEB */ ++ lp = ubifs_fast_find_empty(c); ++ if (lp) ++ goto found; ++ ++ /* Or a freeable LEB */ ++ lp = ubifs_fast_find_freeable(c); ++ if (lp) ++ goto found; ++ } else ++ /* ++ * We cannot pick free/freeable LEBs in the below code. ++ */ ++ pick_free = 0; ++ } else { ++ spin_lock(&c->space_lock); ++ exclude_index = (c->min_idx_lebs >= c->lst.idx_lebs); ++ spin_unlock(&c->space_lock); ++ } ++ ++ /* Look on the dirty and dirty index heaps */ ++ heap = &c->lpt_heap[LPROPS_DIRTY - 1]; ++ idx_heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1]; ++ ++ if (idx_heap->cnt && !exclude_index) { ++ idx_lp = idx_heap->arr[0]; ++ sum = idx_lp->free + idx_lp->dirty; ++ /* ++ * Since we reserve twice as more space for the index than it ++ * actually takes, it does not make sense to pick indexing LEBs ++ * with less than half LEB of dirty space. ++ */ ++ if (sum < min_space || sum < c->half_leb_size) ++ idx_lp = NULL; ++ } ++ ++ if (heap->cnt) { ++ lp = heap->arr[0]; ++ if (lp->dirty + lp->free < min_space) ++ lp = NULL; ++ } ++ ++ /* Pick the LEB with most space */ ++ if (idx_lp && lp) { ++ if (idx_lp->free + idx_lp->dirty >= lp->free + lp->dirty) ++ lp = idx_lp; ++ } else if (idx_lp && !lp) ++ lp = idx_lp; ++ ++ if (lp) { ++ ubifs_assert(lp->dirty >= c->dead_wm); ++ goto found; ++ } ++ ++ /* Did not find a dirty LEB on the dirty heaps, have to scan */ ++ dbg_find("scanning LPT for a dirty LEB"); ++ lp = scan_for_dirty(c, min_space, pick_free, exclude_index); ++ if (IS_ERR(lp)) { ++ err = PTR_ERR(lp); ++ goto out; ++ } ++ ubifs_assert(lp->dirty >= c->dead_wm); ++ ++found: ++ dbg_find("found LEB %d, free %d, dirty %d, flags %#x", ++ lp->lnum, lp->free, lp->dirty, lp->flags); ++ ++ lp = ubifs_change_lp(c, lp, -1, -1, lp->flags | LPROPS_TAKEN, 0); ++ if (IS_ERR(lp)) { ++ err = PTR_ERR(lp); ++ goto out; ++ } ++ ++ memcpy(ret_lp, lp, sizeof(struct ubifs_lprops)); ++ ++out: ++ ubifs_release_lprops(c); ++ return err; ++} ++ ++/** ++ * scan_for_free_cb - free space scan callback. ++ * @c: the UBIFS file-system description object ++ * @lprops: LEB properties to scan ++ * @in_tree: whether the LEB properties are in main memory ++ * @data: information passed to and from the caller of the scan ++ * ++ * This function returns a code that indicates whether the scan should continue ++ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree ++ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop ++ * (%LPT_SCAN_STOP). ++ */ ++static int scan_for_free_cb(struct ubifs_info *c, ++ const struct ubifs_lprops *lprops, int in_tree, ++ struct scan_data *data) ++{ ++ int ret = LPT_SCAN_CONTINUE; ++ ++ /* Exclude LEBs that are currently in use */ ++ if (lprops->flags & LPROPS_TAKEN) ++ return LPT_SCAN_CONTINUE; ++ /* Determine whether to add these LEB properties to the tree */ ++ if (!in_tree && valuable(c, lprops)) ++ ret |= LPT_SCAN_ADD; ++ /* Exclude index LEBs */ ++ if (lprops->flags & LPROPS_INDEX) ++ return ret; ++ /* Exclude LEBs with too little space */ ++ if (lprops->free < data->min_space) ++ return ret; ++ /* If specified, exclude empty LEBs */ ++ if (!data->pick_free && lprops->free == c->leb_size) ++ return ret; ++ /* ++ * LEBs that have only free and dirty space must not be allocated ++ * because they may have been unmapped already or they may have data ++ * that is obsolete only because of nodes that are still sitting in a ++ * wbuf. ++ */ ++ if (lprops->free + lprops->dirty == c->leb_size && lprops->dirty > 0) ++ return ret; ++ /* Finally we found space */ ++ data->lnum = lprops->lnum; ++ return LPT_SCAN_ADD | LPT_SCAN_STOP; ++} ++ ++/** ++ * do_find_free_space - find a data LEB with free space. ++ * @c: the UBIFS file-system description object ++ * @min_space: minimum amount of free space required ++ * @pick_free: whether it is OK to scan for empty LEBs ++ * @squeeze: whether to try to find space in a non-empty LEB first ++ * ++ * This function returns a pointer to the LEB properties found or a negative ++ * error code. ++ */ ++static ++const struct ubifs_lprops *do_find_free_space(struct ubifs_info *c, ++ int min_space, int pick_free, ++ int squeeze) ++{ ++ const struct ubifs_lprops *lprops; ++ struct ubifs_lpt_heap *heap; ++ struct scan_data data; ++ int err, i; ++ ++ if (squeeze) { ++ lprops = ubifs_fast_find_free(c); ++ if (lprops && lprops->free >= min_space) ++ return lprops; ++ } ++ if (pick_free) { ++ lprops = ubifs_fast_find_empty(c); ++ if (lprops) ++ return lprops; ++ } ++ if (!squeeze) { ++ lprops = ubifs_fast_find_free(c); ++ if (lprops && lprops->free >= min_space) ++ return lprops; ++ } ++ /* There may be an LEB with enough free space on the dirty heap */ ++ heap = &c->lpt_heap[LPROPS_DIRTY - 1]; ++ for (i = 0; i < heap->cnt; i++) { ++ lprops = heap->arr[i]; ++ if (lprops->free >= min_space) ++ return lprops; ++ } ++ /* ++ * A LEB may have fallen off of the bottom of the free heap, and ended ++ * up as uncategorized even though it has enough free space for us now, ++ * so check the uncategorized list. N.B. neither empty nor freeable LEBs ++ * can end up as uncategorized because they are kept on lists not ++ * finite-sized heaps. ++ */ ++ list_for_each_entry(lprops, &c->uncat_list, list) { ++ if (lprops->flags & LPROPS_TAKEN) ++ continue; ++ if (lprops->flags & LPROPS_INDEX) ++ continue; ++ if (lprops->free >= min_space) ++ return lprops; ++ } ++ /* We have looked everywhere in main memory, now scan the flash */ ++ if (c->pnodes_have >= c->pnode_cnt) ++ /* All pnodes are in memory, so skip scan */ ++ return ERR_PTR(-ENOSPC); ++ data.min_space = min_space; ++ data.pick_free = pick_free; ++ data.lnum = -1; ++ err = ubifs_lpt_scan_nolock(c, -1, c->lscan_lnum, ++ (ubifs_lpt_scan_callback)scan_for_free_cb, ++ &data); ++ if (err) ++ return ERR_PTR(err); ++ ubifs_assert(data.lnum >= c->main_first && data.lnum < c->leb_cnt); ++ c->lscan_lnum = data.lnum; ++ lprops = ubifs_lpt_lookup_dirty(c, data.lnum); ++ if (IS_ERR(lprops)) ++ return lprops; ++ ubifs_assert(lprops->lnum == data.lnum); ++ ubifs_assert(lprops->free >= min_space); ++ ubifs_assert(!(lprops->flags & LPROPS_TAKEN)); ++ ubifs_assert(!(lprops->flags & LPROPS_INDEX)); ++ return lprops; ++} ++ ++/** ++ * ubifs_find_free_space - find a data LEB with free space. ++ * @c: the UBIFS file-system description object ++ * @min_space: minimum amount of required free space ++ * @free: contains amount of free space in the LEB on exit ++ * @squeeze: whether to try to find space in a non-empty LEB first ++ * ++ * This function looks for an LEB with at least @min_space bytes of free space. ++ * It tries to find an empty LEB if possible. If no empty LEBs are available, ++ * this function searches for a non-empty data LEB. The returned LEB is marked ++ * as "taken". ++ * ++ * This function returns found LEB number in case of success, %-ENOSPC if it ++ * failed to find a LEB with @min_space bytes of free space and other a negative ++ * error codes in case of failure. ++ */ ++int ubifs_find_free_space(struct ubifs_info *c, int min_space, int *free, ++ int squeeze) ++{ ++ const struct ubifs_lprops *lprops; ++ int lebs, rsvd_idx_lebs, pick_free = 0, err, lnum, flags; ++ ++ dbg_find("min_space %d", min_space); ++ ubifs_assert(min_space > 0 && min_space <= c->dark_wm); ++ ++ ubifs_get_lprops(c); ++ ++ /* Check if there are enough empty LEBs for commit */ ++ spin_lock(&c->space_lock); ++ if (c->min_idx_lebs > c->lst.idx_lebs) ++ rsvd_idx_lebs = c->min_idx_lebs - c->lst.idx_lebs; ++ else ++ rsvd_idx_lebs = 0; ++ lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt - ++ c->lst.taken_empty_lebs; ++ ubifs_assert(lebs + c->lst.idx_lebs >= c->min_idx_lebs); ++ if (rsvd_idx_lebs < lebs) ++ /* ++ * OK to allocate an empty LEB, but we still don't want to go ++ * looking for one if there aren't any. ++ */ ++ if (c->lst.empty_lebs - c->lst.taken_empty_lebs > 0) { ++ pick_free = 1; ++ /* ++ * Because we release the space lock, we must account ++ * for this allocation here. After the LEB properties ++ * flags have been updated, we subtract one. Note, the ++ * result of this is that lprops also decreases ++ * @taken_empty_lebs in 'ubifs_change_lp()', so it is ++ * off by one for a short period of time which may ++ * introduce a small disturbance to budgeting ++ * calculations, but this is harmless because at the ++ * worst case this would make the budgeting subsystem ++ * be more pessimistic than needed. ++ * ++ * Fundamentally, this is about serialization of the ++ * budgeting and lprops subsystems. We could make the ++ * @space_lock a mutex and avoid dropping it before ++ * calling 'ubifs_change_lp()', but mutex is more ++ * heavy-weight, and we want budgeting to be as fast as ++ * possible. ++ */ ++ c->lst.taken_empty_lebs += 1; ++ } ++ spin_unlock(&c->space_lock); ++ ++ lprops = do_find_free_space(c, min_space, pick_free, squeeze); ++ if (IS_ERR(lprops)) { ++ err = PTR_ERR(lprops); ++ goto out; ++ } ++ ++ lnum = lprops->lnum; ++ flags = lprops->flags | LPROPS_TAKEN; ++ ++ lprops = ubifs_change_lp(c, lprops, -1, -1, flags, 0); ++ if (IS_ERR(lprops)) { ++ err = PTR_ERR(lprops); ++ goto out; ++ } ++ ++ if (pick_free) { ++ spin_lock(&c->space_lock); ++ c->lst.taken_empty_lebs -= 1; ++ spin_unlock(&c->space_lock); ++ } ++ ++ *free = lprops->free; ++ ubifs_release_lprops(c); ++ ++ if (*free == c->leb_size) { ++ /* ++ * Ensure that empty LEBs have been unmapped. They may not have ++ * been, for example, because of an unclean unmount. Also ++ * LEBs that were freeable LEBs (free + dirty == leb_size) will ++ * not have been unmapped. ++ */ ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ return err; ++ } ++ ++ dbg_find("found LEB %d, free %d", lnum, *free); ++ ubifs_assert(*free >= min_space); ++ return lnum; ++ ++out: ++ if (pick_free) { ++ spin_lock(&c->space_lock); ++ c->lst.taken_empty_lebs -= 1; ++ spin_unlock(&c->space_lock); ++ } ++ ubifs_release_lprops(c); ++ return err; ++} ++ ++/** ++ * scan_for_idx_cb - callback used by the scan for a free LEB for the index. ++ * @c: the UBIFS file-system description object ++ * @lprops: LEB properties to scan ++ * @in_tree: whether the LEB properties are in main memory ++ * @data: information passed to and from the caller of the scan ++ * ++ * This function returns a code that indicates whether the scan should continue ++ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree ++ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop ++ * (%LPT_SCAN_STOP). ++ */ ++static int scan_for_idx_cb(struct ubifs_info *c, ++ const struct ubifs_lprops *lprops, int in_tree, ++ struct scan_data *data) ++{ ++ int ret = LPT_SCAN_CONTINUE; ++ ++ /* Exclude LEBs that are currently in use */ ++ if (lprops->flags & LPROPS_TAKEN) ++ return LPT_SCAN_CONTINUE; ++ /* Determine whether to add these LEB properties to the tree */ ++ if (!in_tree && valuable(c, lprops)) ++ ret |= LPT_SCAN_ADD; ++ /* Exclude index LEBS */ ++ if (lprops->flags & LPROPS_INDEX) ++ return ret; ++ /* Exclude LEBs that cannot be made empty */ ++ if (lprops->free + lprops->dirty != c->leb_size) ++ return ret; ++ /* ++ * We are allocating for the index so it is safe to allocate LEBs with ++ * only free and dirty space, because write buffers are sync'd at commit ++ * start. ++ */ ++ data->lnum = lprops->lnum; ++ return LPT_SCAN_ADD | LPT_SCAN_STOP; ++} ++ ++/** ++ * scan_for_leb_for_idx - scan for a free LEB for the index. ++ * @c: the UBIFS file-system description object ++ */ ++static const struct ubifs_lprops *scan_for_leb_for_idx(struct ubifs_info *c) ++{ ++ struct ubifs_lprops *lprops; ++ struct scan_data data; ++ int err; ++ ++ data.lnum = -1; ++ err = ubifs_lpt_scan_nolock(c, -1, c->lscan_lnum, ++ (ubifs_lpt_scan_callback)scan_for_idx_cb, ++ &data); ++ if (err) ++ return ERR_PTR(err); ++ ubifs_assert(data.lnum >= c->main_first && data.lnum < c->leb_cnt); ++ c->lscan_lnum = data.lnum; ++ lprops = ubifs_lpt_lookup_dirty(c, data.lnum); ++ if (IS_ERR(lprops)) ++ return lprops; ++ ubifs_assert(lprops->lnum == data.lnum); ++ ubifs_assert(lprops->free + lprops->dirty == c->leb_size); ++ ubifs_assert(!(lprops->flags & LPROPS_TAKEN)); ++ ubifs_assert(!(lprops->flags & LPROPS_INDEX)); ++ return lprops; ++} ++ ++/** ++ * ubifs_find_free_leb_for_idx - find a free LEB for the index. ++ * @c: the UBIFS file-system description object ++ * ++ * This function looks for a free LEB and returns that LEB number. The returned ++ * LEB is marked as "taken", "index". ++ * ++ * Only empty LEBs are allocated. This is for two reasons. First, the commit ++ * calculates the number of LEBs to allocate based on the assumption that they ++ * will be empty. Secondly, free space at the end of an index LEB is not ++ * guaranteed to be empty because it may have been used by the in-the-gaps ++ * method prior to an unclean unmount. ++ * ++ * If no LEB is found %-ENOSPC is returned. For other failures another negative ++ * error code is returned. ++ */ ++int ubifs_find_free_leb_for_idx(struct ubifs_info *c) ++{ ++ const struct ubifs_lprops *lprops; ++ int lnum = -1, err, flags; ++ ++ ubifs_get_lprops(c); ++ ++ lprops = ubifs_fast_find_empty(c); ++ if (!lprops) { ++ lprops = ubifs_fast_find_freeable(c); ++ if (!lprops) { ++ ubifs_assert(c->freeable_cnt == 0); ++ if (c->lst.empty_lebs - c->lst.taken_empty_lebs > 0) { ++ lprops = scan_for_leb_for_idx(c); ++ if (IS_ERR(lprops)) { ++ err = PTR_ERR(lprops); ++ goto out; ++ } ++ } ++ } ++ } ++ ++ if (!lprops) { ++ err = -ENOSPC; ++ goto out; ++ } ++ ++ lnum = lprops->lnum; ++ ++ dbg_find("found LEB %d, free %d, dirty %d, flags %#x", ++ lnum, lprops->free, lprops->dirty, lprops->flags); ++ ++ flags = lprops->flags | LPROPS_TAKEN | LPROPS_INDEX; ++ lprops = ubifs_change_lp(c, lprops, c->leb_size, 0, flags, 0); ++ if (IS_ERR(lprops)) { ++ err = PTR_ERR(lprops); ++ goto out; ++ } ++ ++ ubifs_release_lprops(c); ++ ++ /* ++ * Ensure that empty LEBs have been unmapped. They may not have been, ++ * for example, because of an unclean unmount. Also LEBs that were ++ * freeable LEBs (free + dirty == leb_size) will not have been unmapped. ++ */ ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) { ++ ubifs_change_one_lp(c, lnum, -1, -1, 0, ++ LPROPS_TAKEN | LPROPS_INDEX, 0); ++ return err; ++ } ++ ++ return lnum; ++ ++out: ++ ubifs_release_lprops(c); ++ return err; ++} ++ ++static int cmp_dirty_idx(const struct ubifs_lprops **a, ++ const struct ubifs_lprops **b) ++{ ++ const struct ubifs_lprops *lpa = *a; ++ const struct ubifs_lprops *lpb = *b; ++ ++ return lpa->dirty + lpa->free - lpb->dirty - lpb->free; ++} ++ ++static void swap_dirty_idx(struct ubifs_lprops **a, struct ubifs_lprops **b, ++ int size) ++{ ++ struct ubifs_lprops *t = *a; ++ ++ *a = *b; ++ *b = t; ++} ++ ++/** ++ * ubifs_save_dirty_idx_lnums - save an array of the most dirty index LEB nos. ++ * @c: the UBIFS file-system description object ++ * ++ * This function is called each commit to create an array of LEB numbers of ++ * dirty index LEBs sorted in order of dirty and free space. This is used by ++ * the in-the-gaps method of TNC commit. ++ */ ++int ubifs_save_dirty_idx_lnums(struct ubifs_info *c) ++{ ++ int i; ++ ++ ubifs_get_lprops(c); ++ /* Copy the LPROPS_DIRTY_IDX heap */ ++ c->dirty_idx.cnt = c->lpt_heap[LPROPS_DIRTY_IDX - 1].cnt; ++ memcpy(c->dirty_idx.arr, c->lpt_heap[LPROPS_DIRTY_IDX - 1].arr, ++ sizeof(void *) * c->dirty_idx.cnt); ++ /* Sort it so that the dirtiest is now at the end */ ++ sort(c->dirty_idx.arr, c->dirty_idx.cnt, sizeof(void *), ++ (int (*)(const void *, const void *))cmp_dirty_idx, ++ (void (*)(void *, void *, int))swap_dirty_idx); ++ dbg_find("found %d dirty index LEBs", c->dirty_idx.cnt); ++ if (c->dirty_idx.cnt) ++ dbg_find("dirtiest index LEB is %d with dirty %d and free %d", ++ c->dirty_idx.arr[c->dirty_idx.cnt - 1]->lnum, ++ c->dirty_idx.arr[c->dirty_idx.cnt - 1]->dirty, ++ c->dirty_idx.arr[c->dirty_idx.cnt - 1]->free); ++ /* Replace the lprops pointers with LEB numbers */ ++ for (i = 0; i < c->dirty_idx.cnt; i++) ++ c->dirty_idx.arr[i] = (void *)(size_t)c->dirty_idx.arr[i]->lnum; ++ ubifs_release_lprops(c); ++ return 0; ++} ++ ++/** ++ * scan_dirty_idx_cb - callback used by the scan for a dirty index LEB. ++ * @c: the UBIFS file-system description object ++ * @lprops: LEB properties to scan ++ * @in_tree: whether the LEB properties are in main memory ++ * @data: information passed to and from the caller of the scan ++ * ++ * This function returns a code that indicates whether the scan should continue ++ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree ++ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop ++ * (%LPT_SCAN_STOP). ++ */ ++static int scan_dirty_idx_cb(struct ubifs_info *c, ++ const struct ubifs_lprops *lprops, int in_tree, ++ struct scan_data *data) ++{ ++ int ret = LPT_SCAN_CONTINUE; ++ ++ /* Exclude LEBs that are currently in use */ ++ if (lprops->flags & LPROPS_TAKEN) ++ return LPT_SCAN_CONTINUE; ++ /* Determine whether to add these LEB properties to the tree */ ++ if (!in_tree && valuable(c, lprops)) ++ ret |= LPT_SCAN_ADD; ++ /* Exclude non-index LEBs */ ++ if (!(lprops->flags & LPROPS_INDEX)) ++ return ret; ++ /* Exclude LEBs with too little space */ ++ if (lprops->free + lprops->dirty < c->min_idx_node_sz) ++ return ret; ++ /* Finally we found space */ ++ data->lnum = lprops->lnum; ++ return LPT_SCAN_ADD | LPT_SCAN_STOP; ++} ++ ++/** ++ * find_dirty_idx_leb - find a dirty index LEB. ++ * @c: the UBIFS file-system description object ++ * ++ * This function returns LEB number upon success and a negative error code upon ++ * failure. In particular, -ENOSPC is returned if a dirty index LEB is not ++ * found. ++ * ++ * Note that this function scans the entire LPT but it is called very rarely. ++ */ ++static int find_dirty_idx_leb(struct ubifs_info *c) ++{ ++ const struct ubifs_lprops *lprops; ++ struct ubifs_lpt_heap *heap; ++ struct scan_data data; ++ int err, i, ret; ++ ++ /* Check all structures in memory first */ ++ data.lnum = -1; ++ heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1]; ++ for (i = 0; i < heap->cnt; i++) { ++ lprops = heap->arr[i]; ++ ret = scan_dirty_idx_cb(c, lprops, 1, &data); ++ if (ret & LPT_SCAN_STOP) ++ goto found; ++ } ++ list_for_each_entry(lprops, &c->frdi_idx_list, list) { ++ ret = scan_dirty_idx_cb(c, lprops, 1, &data); ++ if (ret & LPT_SCAN_STOP) ++ goto found; ++ } ++ list_for_each_entry(lprops, &c->uncat_list, list) { ++ ret = scan_dirty_idx_cb(c, lprops, 1, &data); ++ if (ret & LPT_SCAN_STOP) ++ goto found; ++ } ++ if (c->pnodes_have >= c->pnode_cnt) ++ /* All pnodes are in memory, so skip scan */ ++ return -ENOSPC; ++ err = ubifs_lpt_scan_nolock(c, -1, c->lscan_lnum, ++ (ubifs_lpt_scan_callback)scan_dirty_idx_cb, ++ &data); ++ if (err) ++ return err; ++found: ++ ubifs_assert(data.lnum >= c->main_first && data.lnum < c->leb_cnt); ++ c->lscan_lnum = data.lnum; ++ lprops = ubifs_lpt_lookup_dirty(c, data.lnum); ++ if (IS_ERR(lprops)) ++ return PTR_ERR(lprops); ++ ubifs_assert(lprops->lnum == data.lnum); ++ ubifs_assert(lprops->free + lprops->dirty >= c->min_idx_node_sz); ++ ubifs_assert(!(lprops->flags & LPROPS_TAKEN)); ++ ubifs_assert((lprops->flags & LPROPS_INDEX)); ++ ++ dbg_find("found dirty LEB %d, free %d, dirty %d, flags %#x", ++ lprops->lnum, lprops->free, lprops->dirty, lprops->flags); ++ ++ lprops = ubifs_change_lp(c, lprops, -1, -1, ++ lprops->flags | LPROPS_TAKEN, 0); ++ if (IS_ERR(lprops)) ++ return PTR_ERR(lprops); ++ ++ return lprops->lnum; ++} ++ ++/** ++ * get_idx_gc_leb - try to get a LEB number from trivial GC. ++ * @c: the UBIFS file-system description object ++ */ ++static int get_idx_gc_leb(struct ubifs_info *c) ++{ ++ const struct ubifs_lprops *lp; ++ int err, lnum; ++ ++ err = ubifs_get_idx_gc_leb(c); ++ if (err < 0) ++ return err; ++ lnum = err; ++ /* ++ * The LEB was due to be unmapped after the commit but ++ * it is needed now for this commit. ++ */ ++ lp = ubifs_lpt_lookup_dirty(c, lnum); ++ if (unlikely(IS_ERR(lp))) ++ return PTR_ERR(lp); ++ lp = ubifs_change_lp(c, lp, -1, -1, lp->flags | LPROPS_INDEX, -1); ++ if (unlikely(IS_ERR(lp))) ++ return PTR_ERR(lp); ++ dbg_find("LEB %d, dirty %d and free %d flags %#x", ++ lp->lnum, lp->dirty, lp->free, lp->flags); ++ return lnum; ++} ++ ++/** ++ * find_dirtiest_idx_leb - find dirtiest index LEB from dirtiest array. ++ * @c: the UBIFS file-system description object ++ */ ++static int find_dirtiest_idx_leb(struct ubifs_info *c) ++{ ++ const struct ubifs_lprops *lp; ++ int lnum; ++ ++ while (1) { ++ if (!c->dirty_idx.cnt) ++ return -ENOSPC; ++ /* The lprops pointers were replaced by LEB numbers */ ++ lnum = (size_t)c->dirty_idx.arr[--c->dirty_idx.cnt]; ++ lp = ubifs_lpt_lookup(c, lnum); ++ if (IS_ERR(lp)) ++ return PTR_ERR(lp); ++ if ((lp->flags & LPROPS_TAKEN) || !(lp->flags & LPROPS_INDEX)) ++ continue; ++ lp = ubifs_change_lp(c, lp, -1, -1, ++ lp->flags | LPROPS_TAKEN, 0); ++ if (IS_ERR(lp)) ++ return PTR_ERR(lp); ++ break; ++ } ++ dbg_find("LEB %d, dirty %d and free %d flags %#x", lp->lnum, lp->dirty, ++ lp->free, lp->flags); ++ ubifs_assert(lp->flags | LPROPS_TAKEN); ++ ubifs_assert(lp->flags | LPROPS_INDEX); ++ return lnum; ++} ++ ++/** ++ * ubifs_find_dirty_idx_leb - try to find dirtiest index LEB as at last commit. ++ * @c: the UBIFS file-system description object ++ * ++ * This function attempts to find an untaken index LEB with the most free and ++ * dirty space that can be used without overwriting index nodes that were in the ++ * last index committed. ++ */ ++int ubifs_find_dirty_idx_leb(struct ubifs_info *c) ++{ ++ int err; ++ ++ ubifs_get_lprops(c); ++ ++ /* ++ * We made an array of the dirtiest index LEB numbers as at the start of ++ * last commit. Try that array first. ++ */ ++ err = find_dirtiest_idx_leb(c); ++ ++ /* Next try scanning the entire LPT */ ++ if (err == -ENOSPC) ++ err = find_dirty_idx_leb(c); ++ ++ /* Finally take any index LEBs awaiting trivial GC */ ++ if (err == -ENOSPC) ++ err = get_idx_gc_leb(c); ++ ++ ubifs_release_lprops(c); ++ return err; ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/gc.c linux-2.6.24.7/fs/ubifs/gc.c +--- linux-2.6.24.7.old/fs/ubifs/gc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/gc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,762 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy (Битюцкий Ðртём) ++ */ ++ ++/* ++ * This file implements garbage collection. The procedure for garbage collection ++ * is different depending on whether a LEB as an index LEB (contains index ++ * nodes) or not. For non-index LEBs, garbage collection finds a LEB which ++ * contains a lot of dirty space (obsolete nodes), and copies the non-obsolete ++ * nodes to the journal, at which point the garbage-collected LEB is free to be ++ * reused. For index LEBs, garbage collection marks the non-obsolete index nodes ++ * dirty in the TNC, and after the next commit, the garbage-collected LEB is ++ * to be reused. Garbage collection will cause the number of dirty index nodes ++ * to grow, however sufficient space is reserved for the index to ensure the ++ * commit will never run out of space. ++ */ ++ ++#include ++#include "ubifs.h" ++ ++/* ++ * GC tries to optimize the way it fit nodes to available space, and it sorts ++ * nodes a little. The below constants are watermarks which define "large", ++ * "medium", and "small" nodes. ++ */ ++#define MEDIUM_NODE_WM (UBIFS_BLOCK_SIZE / 4) ++#define SMALL_NODE_WM UBIFS_MAX_DENT_NODE_SZ ++ ++/* ++ * GC may need to move more then one LEB to make progress. The below constants ++ * define "soft" and "hard" limits on the number of LEBs the garbage collector ++ * may move. ++ */ ++#define SOFT_LEBS_LIMIT 4 ++#define HARD_LEBS_LIMIT 32 ++ ++/** ++ * switch_gc_head - switch the garbage collection journal head. ++ * @c: UBIFS file-system description object ++ * @buf: buffer to write ++ * @len: length of the buffer to write ++ * @lnum: LEB number written is returned here ++ * @offs: offset written is returned here ++ * ++ * This function switch the GC head to the next LEB which is reserved in ++ * @c->gc_lnum. Returns %0 in case of success, %-EAGAIN if commit is required, ++ * and other negative error code in case of failures. ++ */ ++static int switch_gc_head(struct ubifs_info *c) ++{ ++ int err, gc_lnum = c->gc_lnum; ++ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf; ++ ++ ubifs_assert(gc_lnum != -1); ++ dbg_gc("switch GC head from LEB %d:%d to LEB %d (waste %d bytes)", ++ wbuf->lnum, wbuf->offs + wbuf->used, gc_lnum, ++ c->leb_size - wbuf->offs - wbuf->used); ++ ++ err = ubifs_wbuf_sync_nolock(wbuf); ++ if (err) ++ return err; ++ ++ /* ++ * The GC write-buffer was synchronized, we may safely unmap ++ * 'c->gc_lnum'. ++ */ ++ err = ubifs_leb_unmap(c, gc_lnum); ++ if (err) ++ return err; ++ ++ err = ubifs_add_bud_to_log(c, GCHD, gc_lnum, 0); ++ if (err) ++ return err; ++ ++ c->gc_lnum = -1; ++ err = ubifs_wbuf_seek_nolock(wbuf, gc_lnum, 0, UBI_LONGTERM); ++ return err; ++} ++ ++/** ++ * move_nodes - move nodes. ++ * @c: UBIFS file-system description object ++ * @sleb: describes nodes to move ++ * ++ * This function moves valid nodes from data LEB described by @sleb to the GC ++ * journal head. The obsolete nodes are dropped. ++ * ++ * When moving nodes we have to deal with classical bin-packing problem: the ++ * space in the current GC journal head LEB and in @c->gc_lnum are the "bins", ++ * where the nodes in the @sleb->nodes list are the elements which should be ++ * fit optimally to the bins. This function uses the "first fit decreasing" ++ * strategy, although it does not really sort the nodes but just split them on ++ * 3 classes - large, medium, and small, so they are roughly sorted. ++ * ++ * This function returns zero in case of success, %-EAGAIN if commit is ++ * required, and other negative error codes in case of other failures. ++ */ ++static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb) ++{ ++ struct ubifs_scan_node *snod, *tmp; ++ struct list_head large, medium, small; ++ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf; ++ int avail, err, min = INT_MAX; ++ ++ INIT_LIST_HEAD(&large); ++ INIT_LIST_HEAD(&medium); ++ INIT_LIST_HEAD(&small); ++ ++ list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) { ++ struct list_head *lst; ++ ++ ubifs_assert(snod->type != UBIFS_IDX_NODE); ++ ubifs_assert(snod->type != UBIFS_REF_NODE); ++ ubifs_assert(snod->type != UBIFS_CS_NODE); ++ ++ err = ubifs_tnc_has_node(c, &snod->key, 0, sleb->lnum, ++ snod->offs, 0); ++ if (err < 0) ++ goto out; ++ ++ lst = &snod->list; ++ list_del(lst); ++ if (!err) { ++ /* The node is obsolete, remove it from the list */ ++ kfree(snod); ++ continue; ++ } ++ ++ /* ++ * Sort the list of nodes so that large nodes go first, and ++ * small nodes go last. ++ */ ++ if (snod->len > MEDIUM_NODE_WM) ++ list_add(lst, &large); ++ else if (snod->len > SMALL_NODE_WM) ++ list_add(lst, &medium); ++ else ++ list_add(lst, &small); ++ ++ /* And find the smallest node */ ++ if (snod->len < min) ++ min = snod->len; ++ } ++ ++ /* ++ * Join the tree lists so that we'd have one roughly sorted list ++ * ('large' will be the head of the joined list). ++ */ ++ list_splice(&medium, large.prev); ++ list_splice(&small, large.prev); ++ ++ if (wbuf->lnum == -1) { ++ /* ++ * The GC journal head is not set, because it is the first GC ++ * invocation since mount. ++ */ ++ err = switch_gc_head(c); ++ if (err) ++ goto out; ++ } ++ ++ /* Write nodes to their new location. Use the first-fit strategy */ ++ while (1) { ++ avail = c->leb_size - wbuf->offs - wbuf->used; ++ list_for_each_entry_safe(snod, tmp, &large, list) { ++ int new_lnum, new_offs; ++ ++ if (avail < min) ++ break; ++ ++ if (snod->len > avail) ++ /* This node does not fit */ ++ continue; ++ ++ cond_resched(); ++ ++ new_lnum = wbuf->lnum; ++ new_offs = wbuf->offs + wbuf->used; ++ err = ubifs_wbuf_write_nolock(wbuf, snod->node, ++ snod->len); ++ ++ err = ubifs_tnc_replace(c, &snod->key, sleb->lnum, ++ snod->offs, new_lnum, new_offs, ++ snod->len); ++ if (err) ++ goto out; ++ ++ avail = c->leb_size - wbuf->offs - wbuf->used; ++ list_del(&snod->list); ++ kfree(snod); ++ } ++ ++ if (list_empty(&large)) ++ break; ++ ++ /* ++ * Waste the rest of the space in the LEB and switch to the ++ * next LEB. ++ */ ++ err = switch_gc_head(c); ++ if (err) ++ goto out; ++ } ++ ++ return 0; ++ ++out: ++ list_for_each_entry_safe(snod, tmp, &large, list) { ++ list_del(&snod->list); ++ kfree(snod); ++ } ++ return err; ++} ++ ++/** ++ * gc_sync_wbufs - sync write-buffers for GC. ++ * @c: UBIFS file-system description object ++ * ++ * We must guarantee that obsoleting nodes are on flash. Unfortunately they may ++ * be in a write-buffer instead. That is, a node could be written to a ++ * write-buffer, obsoleting another node in a LEB that is GC'd. If that LEB is ++ * erased before the write-buffer is sync'd and then there is an unclean ++ * unmount, then an existing node is lost. To avoid this, we sync all ++ * write-buffers. ++ * ++ * This function returns %0 on success or a negative error code on failure. ++ */ ++static int gc_sync_wbufs(struct ubifs_info *c) ++{ ++ int err, i; ++ ++ for (i = 0; i < c->jhead_cnt; i++) { ++ if (i == GCHD) ++ continue; ++ err = ubifs_wbuf_sync(&c->jheads[i].wbuf); ++ if (err) ++ return err; ++ } ++ return 0; ++} ++ ++/** ++ * ubifs_garbage_collect_leb - garbage-collect a logical eraseblock. ++ * @c: UBIFS file-system description object ++ * @lp: describes the LEB to garbage collect ++ * ++ * This function garbage-collects an LEB and returns one of the @LEB_FREED, ++ * @LEB_RETAINED, etc positive codes in case of success, %-EAGAIN if commit is ++ * required, and other negative error codes in case of failures. ++ */ ++int ubifs_garbage_collect_leb(struct ubifs_info *c, struct ubifs_lprops *lp) ++{ ++ struct ubifs_scan_leb *sleb; ++ struct ubifs_scan_node *snod; ++ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf; ++ int err = 0, lnum = lp->lnum; ++ ++ ubifs_assert(c->gc_lnum != -1 || wbuf->offs + wbuf->used == 0 || ++ c->need_recovery); ++ ubifs_assert(c->gc_lnum != lnum); ++ ubifs_assert(wbuf->lnum != lnum); ++ ++ /* ++ * We scan the entire LEB even though we only really need to scan up to ++ * (c->leb_size - lp->free). ++ */ ++ sleb = ubifs_scan(c, lnum, 0, c->sbuf); ++ if (IS_ERR(sleb)) ++ return PTR_ERR(sleb); ++ ++ ubifs_assert(!list_empty(&sleb->nodes)); ++ snod = list_entry(sleb->nodes.next, struct ubifs_scan_node, list); ++ ++ if (snod->type == UBIFS_IDX_NODE) { ++ struct ubifs_gced_idx_leb *idx_gc; ++ ++ dbg_gc("indexing LEB %d (free %d, dirty %d)", ++ lnum, lp->free, lp->dirty); ++ list_for_each_entry(snod, &sleb->nodes, list) { ++ struct ubifs_idx_node *idx = snod->node; ++ int level = le16_to_cpu(idx->level); ++ ++ ubifs_assert(snod->type == UBIFS_IDX_NODE); ++ key_read(c, ubifs_idx_key(c, idx), &snod->key); ++ err = ubifs_dirty_idx_node(c, &snod->key, level, lnum, ++ snod->offs); ++ if (err) ++ goto out; ++ } ++ ++ idx_gc = kmalloc(sizeof(struct ubifs_gced_idx_leb), GFP_NOFS); ++ if (!idx_gc) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ idx_gc->lnum = lnum; ++ idx_gc->unmap = 0; ++ list_add(&idx_gc->list, &c->idx_gc); ++ ++ /* ++ * Don't release the LEB until after the next commit, because ++ * it may contain date which is needed for recovery. So ++ * although we freed this LEB, it will become usable only after ++ * the commit. ++ */ ++ err = ubifs_change_one_lp(c, lnum, c->leb_size, 0, 0, ++ LPROPS_INDEX, 1); ++ if (err) ++ goto out; ++ err = LEB_FREED_IDX; ++ } else { ++ dbg_gc("data LEB %d (free %d, dirty %d)", ++ lnum, lp->free, lp->dirty); ++ ++ err = move_nodes(c, sleb); ++ if (err) ++ goto out; ++ ++ err = gc_sync_wbufs(c); ++ if (err) ++ goto out; ++ ++ err = ubifs_change_one_lp(c, lnum, c->leb_size, 0, 0, 0, 0); ++ if (err) ++ goto out; ++ ++ if (c->gc_lnum == -1) { ++ c->gc_lnum = lnum; ++ err = LEB_RETAINED; ++ } else { ++ err = ubifs_wbuf_sync_nolock(wbuf); ++ if (err) ++ goto out; ++ ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ goto out; ++ ++ err = LEB_FREED; ++ } ++ } ++ ++out: ++ ubifs_scan_destroy(sleb); ++ return err; ++} ++ ++/** ++ * ubifs_garbage_collect - UBIFS garbage collector. ++ * @c: UBIFS file-system description object ++ * @anyway: do GC even if there are free LEBs ++ * ++ * This function does out-of-place garbage collection. The return codes are: ++ * o positive LEB number if the LEB has been freed and may be used; ++ * o %-EAGAIN if the caller has to run commit; ++ * o %-ENOSPC if GC failed to make any progress; ++ * o other negative error codes in case of other errors. ++ * ++ * Garbage collector writes data to the journal when GC'ing data LEBs, and just ++ * marking indexing nodes dirty when GC'ing indexing LEBs. Thus, at some point ++ * commit may be required. But commit cannot be run from inside GC, because the ++ * caller might be holding the commit lock, so %-EAGAIN is returned instead; ++ * And this error code means that the caller has to run commit, and re-run GC ++ * if there is still no free space. ++ * ++ * There are many reasons why this function may return %-EAGAIN: ++ * o the log is full and there is no space to write an LEB reference for ++ * @c->gc_lnum; ++ * o the journal is too large and exceeds size limitations; ++ * o GC moved indexing LEBs, but they can be used only after the commit; ++ * o the shrinker fails to find clean znodes to free and requests the commit; ++ * o etc. ++ * ++ * Note, if the file-system is close to be full, this function may return ++ * %-EAGAIN infinitely, so the caller has to limit amount of re-invocations of ++ * the function. E.g., this happens if the limits on the journal size are too ++ * tough and GC writes too much to the journal before an LEB is freed. This ++ * might also mean that the journal is too large, and the TNC becomes to big, ++ * so that the shrinker is constantly called, finds not clean znodes to free, ++ * and requests commit. Well, this may also happen if the journal is all right, ++ * but another kernel process consumes too much memory. Anyway, infinite ++ * %-EAGAIN may happen, but in some extreme/misconfiguration cases. ++ */ ++int ubifs_garbage_collect(struct ubifs_info *c, int anyway) ++{ ++ int i, err, ret, min_space = c->dead_wm; ++ struct ubifs_lprops lp; ++ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf; ++ ++ ubifs_assert_cmt_locked(c); ++ ++ if (ubifs_gc_should_commit(c)) ++ return -EAGAIN; ++ ++ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); ++ /* We expect the write-buffer to be empty on entry */ ++ ubifs_assert(!wbuf->used); ++ ++ for (i = 0; ; i++) { ++ int space_before = c->leb_size - wbuf->offs - wbuf->used; ++ int space_after; ++ ++ cond_resched(); ++ ++ /* Give the commit an opportunity to run */ ++ if (ubifs_gc_should_commit(c)) { ++ ret = -EAGAIN; ++ break; ++ } ++ ++ if (i > SOFT_LEBS_LIMIT && !list_empty(&c->idx_gc)) { ++ /* ++ * We've done enough iterations. Indexing LEBs were ++ * moved and will be available after the commit. ++ */ ++ dbg_gc("soft limit, some index LEBs GC'ed, -EAGAIN"); ++ ubifs_commit_required(c); ++ ret = -EAGAIN; ++ break; ++ } ++ ++ if (i > HARD_LEBS_LIMIT) { ++ /* ++ * We've moved too many LEBs and have not made ++ * progress, give up. ++ */ ++ dbg_gc("hard limit, -ENOSPC"); ++ ret = -ENOSPC; ++ break; ++ } ++ ++ /* ++ * Empty and freeable LEBs can turn up while we waited for ++ * the wbuf lock, or while we have been running GC. In that ++ * case, we should just return one of those instead of ++ * continuing to GC dirty LEBs. Hence we request ++ * 'ubifs_find_dirty_leb()' to return an empty LEB if it can. ++ */ ++ ret = ubifs_find_dirty_leb(c, &lp, min_space, anyway ? 0 : 1); ++ if (ret) { ++ if (ret == -ENOSPC) ++ dbg_gc("no more dirty LEBs"); ++ break; ++ } ++ ++ dbg_gc("found LEB %d: free %d, dirty %d, sum %d " ++ "(min. space %d)", lp.lnum, lp.free, lp.dirty, ++ lp.free + lp.dirty, min_space); ++ ++ if (lp.free + lp.dirty == c->leb_size) { ++ /* An empty LEB was returned */ ++ dbg_gc("LEB %d is free, return it", lp.lnum); ++ /* ++ * ubifs_find_dirty_leb() doesn't return freeable index ++ * LEBs. ++ */ ++ ubifs_assert(!(lp.flags & LPROPS_INDEX)); ++ if (lp.free != c->leb_size) { ++ /* ++ * Write buffers must be sync'd before ++ * unmapping freeable LEBs, because one of them ++ * may contain data which obsoletes something ++ * in 'lp.pnum'. ++ */ ++ ret = gc_sync_wbufs(c); ++ if (ret) ++ goto out; ++ ret = ubifs_change_one_lp(c, lp.lnum, ++ c->leb_size, 0, 0, 0, ++ 0); ++ if (ret) ++ goto out; ++ } ++ ret = ubifs_leb_unmap(c, lp.lnum); ++ if (ret) ++ goto out; ++ ret = lp.lnum; ++ break; ++ } ++ ++ space_before = c->leb_size - wbuf->offs - wbuf->used; ++ if (wbuf->lnum == -1) ++ space_before = 0; ++ ++ ret = ubifs_garbage_collect_leb(c, &lp); ++ if (ret < 0) { ++ if (ret == -EAGAIN || ret == -ENOSPC) { ++ /* ++ * These codes are not errors, so we have to ++ * return the LEB to lprops. But if the ++ * 'ubifs_return_leb()' function fails, its ++ * failure code is propagated to the caller ++ * instead of the original '-EAGAIN' or ++ * '-ENOSPC'. ++ */ ++ err = ubifs_return_leb(c, lp.lnum); ++ if (err) ++ ret = err; ++ break; ++ } ++ goto out; ++ } ++ ++ if (ret == LEB_FREED) { ++ /* An LEB has been freed and is ready for use */ ++ dbg_gc("LEB %d freed, return", lp.lnum); ++ ret = lp.lnum; ++ break; ++ } ++ ++ if (ret == LEB_FREED_IDX) { ++ /* ++ * This was an indexing LEB and it cannot be ++ * immediately used. And instead of requesting the ++ * commit straight away, we try to garbage collect some ++ * more. ++ */ ++ dbg_gc("indexing LEB %d freed, continue", lp.lnum); ++ continue; ++ } ++ ++ ubifs_assert(ret == LEB_RETAINED); ++ space_after = c->leb_size - wbuf->offs - wbuf->used; ++ dbg_gc("LEB %d retained, freed %d bytes", lp.lnum, ++ space_after - space_before); ++ ++ if (space_after > space_before) { ++ /* GC makes progress, keep working */ ++ min_space >>= 1; ++ if (min_space < c->dead_wm) ++ min_space = c->dead_wm; ++ continue; ++ } ++ ++ dbg_gc("did not make progress"); ++ ++ /* ++ * GC moved an LEB bud have not done any progress. This means ++ * that the previous GC head LEB contained too few free space ++ * and the LEB which was GC'ed contained only large nodes which ++ * did not fit that space. ++ * ++ * We can do 2 things: ++ * 1. pick another LEB in a hope it'll contain a small node ++ * which will fit the space we have at the end of current GC ++ * head LEB, but there is no guarantee, so we try this out ++ * unless we have already been working for too long; ++ * 2. request an LEB with more dirty space, which will force ++ * 'ubifs_find_dirty_leb()' to start scanning the lprops ++ * table, instead of just picking one from the heap ++ * (previously it already picked the dirtiest LEB). ++ */ ++ if (i < SOFT_LEBS_LIMIT) { ++ dbg_gc("try again"); ++ continue; ++ } ++ ++ min_space <<= 1; ++ if (min_space > c->dark_wm) ++ min_space = c->dark_wm; ++ dbg_gc("set min. space to %d", min_space); ++ } ++ ++ if (ret == -ENOSPC && !list_empty(&c->idx_gc)) { ++ dbg_gc("no space, some index LEBs GC'ed, -EAGAIN"); ++ ubifs_commit_required(c); ++ ret = -EAGAIN; ++ } ++ ++ err = ubifs_wbuf_sync_nolock(wbuf); ++ if (!err) ++ err = ubifs_leb_unmap(c, c->gc_lnum); ++ if (err) ++ ret = err; ++ mutex_unlock(&wbuf->io_mutex); ++ return ret; ++ ++out: ++ ubifs_assert(ret < 0); ++ ubifs_assert(ret != -ENOSPC && ret != -EAGAIN); ++ ubifs_wbuf_sync_nolock(wbuf); ++ mutex_unlock(&wbuf->io_mutex); ++ ubifs_return_leb(c, lp.lnum); ++ return ret; ++} ++ ++/** ++ * ubifs_gc_start_commit - garbage collection at start of commit. ++ * @c: UBIFS file-system description object ++ * ++ * If a LEB has only dirty and free space, then we may safely unmap it and make ++ * it free. Note, we cannot do this with indexing LEBs because dirty space may ++ * correspond index nodes that are required for recovery. In that case, the ++ * LEB cannot be unmapped until after the next commit. ++ * ++ * This function returns %0 upon success and a negative error code upon failure. ++ */ ++int ubifs_gc_start_commit(struct ubifs_info *c) ++{ ++ struct ubifs_gced_idx_leb *idx_gc; ++ const struct ubifs_lprops *lp; ++ int err = 0, flags; ++ ++ ubifs_get_lprops(c); ++ ++ /* ++ * Unmap (non-index) freeable LEBs. Note that recovery requires that all ++ * wbufs are sync'd before this, which is done in 'do_commit()'. ++ */ ++ while (1) { ++ lp = ubifs_fast_find_freeable(c); ++ if (unlikely(IS_ERR(lp))) { ++ err = PTR_ERR(lp); ++ goto out; ++ } ++ if (!lp) ++ break; ++ ubifs_assert(!(lp->flags & LPROPS_TAKEN)); ++ ubifs_assert(!(lp->flags & LPROPS_INDEX)); ++ err = ubifs_leb_unmap(c, lp->lnum); ++ if (err) ++ goto out; ++ lp = ubifs_change_lp(c, lp, c->leb_size, 0, lp->flags, 0); ++ if (unlikely(IS_ERR(lp))) { ++ err = PTR_ERR(lp); ++ goto out; ++ } ++ ubifs_assert(!(lp->flags & LPROPS_TAKEN)); ++ ubifs_assert(!(lp->flags & LPROPS_INDEX)); ++ } ++ ++ /* Mark GC'd index LEBs OK to unmap after this commit finishes */ ++ list_for_each_entry(idx_gc, &c->idx_gc, list) ++ idx_gc->unmap = 1; ++ ++ /* Record index freeable LEBs for unmapping after commit */ ++ while (1) { ++ lp = ubifs_fast_find_frdi_idx(c); ++ if (unlikely(IS_ERR(lp))) { ++ err = PTR_ERR(lp); ++ goto out; ++ } ++ if (!lp) ++ break; ++ idx_gc = kmalloc(sizeof(struct ubifs_gced_idx_leb), GFP_NOFS); ++ if (!idx_gc) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ubifs_assert(!(lp->flags & LPROPS_TAKEN)); ++ ubifs_assert(lp->flags & LPROPS_INDEX); ++ /* Don't release the LEB until after the next commit */ ++ flags = (lp->flags | LPROPS_TAKEN) ^ LPROPS_INDEX; ++ lp = ubifs_change_lp(c, lp, c->leb_size, 0, flags, 1); ++ if (unlikely(IS_ERR(lp))) { ++ err = PTR_ERR(lp); ++ kfree(idx_gc); ++ goto out; ++ } ++ ubifs_assert(lp->flags & LPROPS_TAKEN); ++ ubifs_assert(!(lp->flags & LPROPS_INDEX)); ++ idx_gc->lnum = lp->lnum; ++ idx_gc->unmap = 1; ++ list_add(&idx_gc->list, &c->idx_gc); ++ } ++out: ++ ubifs_release_lprops(c); ++ return err; ++} ++ ++/** ++ * ubifs_gc_end_commit - garbage collection at end of commit. ++ * @c: UBIFS file-system description object ++ * ++ * This function completes out-of-place garbage collection of index LEBs. ++ */ ++int ubifs_gc_end_commit(struct ubifs_info *c) ++{ ++ struct ubifs_gced_idx_leb *idx_gc, *tmp; ++ struct ubifs_wbuf *wbuf; ++ int err = 0; ++ ++ wbuf = &c->jheads[GCHD].wbuf; ++ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); ++ list_for_each_entry_safe(idx_gc, tmp, &c->idx_gc, list) ++ if (idx_gc->unmap) { ++ dbg_gc("LEB %d", idx_gc->lnum); ++ err = ubifs_leb_unmap(c, idx_gc->lnum); ++ if (err) ++ goto out; ++ err = ubifs_change_one_lp(c, idx_gc->lnum, -1, -1, 0, ++ LPROPS_TAKEN, -1); ++ if (err) ++ goto out; ++ list_del(&idx_gc->list); ++ kfree(idx_gc); ++ } ++out: ++ mutex_unlock(&wbuf->io_mutex); ++ return err; ++} ++ ++/** ++ * ubifs_destroy_idx_gc - destroy idx_gc list. ++ * @c: UBIFS file-system description object ++ * ++ * This function destroys the idx_gc list. It is called when unmounting or ++ * remounting read-only so locks are not needed. ++ */ ++void ubifs_destroy_idx_gc(struct ubifs_info *c) ++{ ++ while (!list_empty(&c->idx_gc)) { ++ struct ubifs_gced_idx_leb *idx_gc; ++ ++ idx_gc = list_entry(c->idx_gc.next, struct ubifs_gced_idx_leb, ++ list); ++ c->idx_gc_cnt -= 1; ++ list_del(&idx_gc->list); ++ kfree(idx_gc); ++ } ++ ++} ++ ++/** ++ * ubifs_get_idx_gc_leb - get a LEB from GC'd index LEB list. ++ * @c: UBIFS file-system description object ++ * ++ * Called during start commit so locks are not needed. ++ */ ++int ubifs_get_idx_gc_leb(struct ubifs_info *c) ++{ ++ struct ubifs_gced_idx_leb *idx_gc; ++ int lnum; ++ ++ if (list_empty(&c->idx_gc)) ++ return -ENOSPC; ++ idx_gc = list_entry(c->idx_gc.next, struct ubifs_gced_idx_leb, list); ++ lnum = idx_gc->lnum; ++ /* c->idx_gc_cnt is updated by the caller when lprops are updated */ ++ list_del(&idx_gc->list); ++ kfree(idx_gc); ++ return lnum; ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/io.c linux-2.6.24.7/fs/ubifs/io.c +--- linux-2.6.24.7.old/fs/ubifs/io.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/io.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,921 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * Copyright (C) 2006, 2007 University of Szeged, Hungary ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ * Zoltan Sogor ++ */ ++ ++/* ++ * This file implements UBIFS I/O subsystem which provides various I/O-related ++ * helper functions (reading/writing/checking/validating nodes) and implements ++ * write-buffering support. Write buffers help to save space which otherwise ++ * would have been wasted for padding to the nearest minimal I/O unit boundary. ++ * Instead, data first goes to the write-buffer and is flushed when the ++ * buffer is full or when it is not used for some time (by timer). This is ++ * similarto the mechanism is used by JFFS2. ++ * ++ * Write-buffers are defined by 'struct ubifs_wbuf' objects and protected by ++ * mutexes defined inside these objects. Since sometimes upper-level code ++ * has to lock the write-buffer (e.g. journal space reservation code), many ++ * functions related to write-buffers have "nolock" suffix which means that the ++ * caller has to lock the write-buffer before calling this function. ++ * ++ * UBIFS stores nodes at 64 bit-aligned addresses. If the node length is not ++ * aligned, UBIFS starts the next node from the aligned address, and the padded ++ * bytes may contain any rubbish. In other words, UBIFS does not put padding ++ * bytes in those small gaps. Common headers of nodes store real node lengths, ++ * not aligned lengths. Indexing nodes also store real lengths in branches. ++ * ++ * UBIFS uses padding when it pads to the next min. I/O unit. In this case it ++ * uses padding nodes or padding bytes, if the padding node does not fit. ++ * ++ * All UBIFS nodes are protected by CRC checksums and UBIFS checks all nodes ++ * every time they are read from the flash media. ++ */ ++ ++#include ++#include "ubifs.h" ++ ++/** ++ * ubifs_check_node - check node. ++ * @c: UBIFS file-system description object ++ * @buf: node to check ++ * @lnum: logical eraseblock number ++ * @offs: offset within the logical eraseblock ++ * @quiet: print no messages ++ * ++ * This function checks node magic number and CRC checksum. This function also ++ * validates node length to prevent UBIFS from becoming crazy when an attacker ++ * feeds it a file-system image with incorrect nodes. For example, too large ++ * node length in the common header could cause UBIFS to read memory outside of ++ * allocated buffer when checking the CRC checksum. ++ * ++ * This function returns zero in case of success %-EUCLEAN in case of bad CRC ++ * or magic. ++ */ ++int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum, ++ int offs, int quiet) ++{ ++ int err = -EINVAL, type, node_len; ++ uint32_t crc, node_crc, magic; ++ const struct ubifs_ch *ch = buf; ++ ++ ubifs_assert(lnum >= 0 && lnum < c->leb_cnt && offs >= 0); ++ ubifs_assert(!(offs & 7) && offs < c->leb_size); ++ ++ magic = le32_to_cpu(ch->magic); ++ if (magic != UBIFS_NODE_MAGIC) { ++ if (!quiet) ++ ubifs_err("bad magic %#08x, expected %#08x", ++ magic, UBIFS_NODE_MAGIC); ++ err = -EUCLEAN; ++ goto out; ++ } ++ ++ type = ch->node_type; ++ if (type < 0 || type >= UBIFS_NODE_TYPES_CNT) { ++ if (!quiet) ++ ubifs_err("bad node type %d", type); ++ goto out; ++ } ++ ++ node_len = le32_to_cpu(ch->len); ++ if (node_len + offs > c->leb_size) ++ goto out_len; ++ ++ if (c->ranges[type].max_len == 0) { ++ if (node_len != c->ranges[type].len) ++ goto out_len; ++ } else if (node_len < c->ranges[type].min_len || ++ node_len > c->ranges[type].max_len) ++ goto out_len; ++ ++ crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8); ++ node_crc = le32_to_cpu(ch->crc); ++ if (crc != node_crc) { ++ if (!quiet) ++ ubifs_err("bad CRC: calculated %#08x, read %#08x", ++ crc, node_crc); ++ err = -EUCLEAN; ++ goto out; ++ } ++ ++ return 0; ++ ++out_len: ++ if (!quiet) ++ ubifs_err("bad node length %d", node_len); ++out: ++ if (!quiet) { ++ ubifs_err("bad node at LEB %d:%d", lnum, offs); ++ dbg_dump_node(c, buf); ++ dbg_dump_stack(); ++ } ++ return err; ++} ++ ++/** ++ * ubifs_pad - pad flash space. ++ * @c: UBIFS file-system description object ++ * @buf: buffer to put padding to ++ * @pad: how many bytes to pad ++ * ++ * The flash media obliges us to write only in chunks of %c->min_io_size and ++ * when we have to write less data we add padding node to the write-buffer and ++ * pad it to the next minimal I/O unit's boundary. Padding nodes help when the ++ * media is being scanned. If the amount of wasted space is not enough to fit a ++ * padding node which takes %UBIFS_PAD_NODE_SZ bytes, we write padding bytes ++ * pattern (%UBIFS_PADDING_BYTE). ++ * ++ * Padding nodes are also used to fill gaps when the "commit-in-gaps" method is ++ * used. ++ */ ++void ubifs_pad(const struct ubifs_info *c, void *buf, int pad) ++{ ++ uint32_t crc; ++ ++ ubifs_assert(pad >= 0 && !(pad & 7)); ++ ++ if (pad >= UBIFS_PAD_NODE_SZ) { ++ struct ubifs_ch *ch = buf; ++ struct ubifs_pad_node *pad_node = buf; ++ ++ ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); ++ ch->node_type = UBIFS_PAD_NODE; ++ ch->group_type = UBIFS_NO_NODE_GROUP; ++ ch->padding[0] = ch->padding[1] = 0; ++ ch->sqnum = 0; ++ ch->len = cpu_to_le32(UBIFS_PAD_NODE_SZ); ++ pad -= UBIFS_PAD_NODE_SZ; ++ pad_node->pad_len = cpu_to_le32(pad); ++ crc = crc32(UBIFS_CRC32_INIT, buf + 8, UBIFS_PAD_NODE_SZ - 8); ++ ch->crc = cpu_to_le32(crc); ++ memset(buf + UBIFS_PAD_NODE_SZ, 0, pad); ++ } else if (pad > 0) ++ /* Too little space, padding node won't fit */ ++ memset(buf, UBIFS_PADDING_BYTE, pad); ++} ++ ++/** ++ * next_sqnum - get next sequence number. ++ * @c: UBIFS file-system description object ++ */ ++static unsigned long long next_sqnum(struct ubifs_info *c) ++{ ++ unsigned long long sqnum; ++ ++ spin_lock(&c->cnt_lock); ++ sqnum = ++c->max_sqnum; ++ spin_unlock(&c->cnt_lock); ++ ++ if (unlikely(sqnum >= SQNUM_WARN_WATERMARK)) { ++ if (sqnum >= SQNUM_WATERMARK) { ++ ubifs_err("sequence number overflow %llu, end of life", ++ sqnum); ++ ubifs_ro_mode(c, -EINVAL); ++ } ++ ubifs_warn("running out of sequence numbers, end of life soon"); ++ } ++ ++ return sqnum; ++} ++ ++/** ++ * ubifs_prepare_node - prepare node to be written to flash. ++ * @c: UBIFS file-system description object ++ * @node: the node to pad ++ * @len: node length ++ * @pad: if the buffer has to be padded ++ * ++ * This function prepares node at @node to be written to the media - it ++ * calculates node CRC, fills the common header, and adds proper padding up to ++ * the next minimum I/O unit if @pad is not zero. ++ */ ++void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad) ++{ ++ uint32_t crc; ++ struct ubifs_ch *ch = node; ++ unsigned long long sqnum = next_sqnum(c); ++ ++ ubifs_assert(len >= UBIFS_CH_SZ); ++ ++ ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); ++ ch->len = cpu_to_le32(len); ++ ch->group_type = UBIFS_NO_NODE_GROUP; ++ ch->sqnum = cpu_to_le64(sqnum); ++ ch->padding[0] = ch->padding[1] = 0; ++ crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8); ++ ch->crc = cpu_to_le32(crc); ++ ++ if (pad) { ++ len = ALIGN(len, 8); ++ pad = ALIGN(len, c->min_io_size) - len; ++ ubifs_pad(c, node + len, pad); ++ } ++} ++ ++/** ++ * ubifs_prep_grp_node - prepare node of a group to be written to flash. ++ * @c: UBIFS file-system description object ++ * @node: the node to pad ++ * @len: node length ++ * @last: indicates the last node of the group ++ * ++ * This function prepares node at @node to be written to the media - it ++ * calculates node CRC and fills the common header. ++ */ ++void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last) ++{ ++ uint32_t crc; ++ struct ubifs_ch *ch = node; ++ unsigned long long sqnum = next_sqnum(c); ++ ++ ubifs_assert(len >= UBIFS_CH_SZ); ++ ++ ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); ++ ch->len = cpu_to_le32(len); ++ if (last) ++ ch->group_type = UBIFS_LAST_OF_NODE_GROUP; ++ else ++ ch->group_type = UBIFS_IN_NODE_GROUP; ++ ch->sqnum = cpu_to_le64(sqnum); ++ ch->padding[0] = ch->padding[1] = 0; ++ crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8); ++ ch->crc = cpu_to_le32(crc); ++} ++ ++/** ++ * wbuf_timer_callback - write-buffer timer callback function. ++ * @data: timer data (write-buffer descriptor) ++ * ++ * This function is called when the write-buffer timer expires. ++ */ ++static void wbuf_timer_callback_nolock(unsigned long data) ++{ ++ struct ubifs_wbuf *wbuf = (struct ubifs_wbuf *)data; ++ ++ wbuf->need_sync = 1; ++ wbuf->c->need_wbuf_sync = 1; ++ ubifs_wake_up_bgt(wbuf->c); ++} ++ ++/** ++ * new_wbuf_timer - start new write-buffer timer. ++ * @wbuf: write-buffer descriptor ++ */ ++static void new_wbuf_timer_nolock(struct ubifs_wbuf *wbuf) ++{ ++ ubifs_assert(!timer_pending(&wbuf->timer)); ++ ++ if (!wbuf->timeout) ++ return; ++ ++ wbuf->timer.expires = jiffies + wbuf->timeout; ++ add_timer(&wbuf->timer); ++} ++ ++/** ++ * cancel_wbuf_timer - cancel write-buffer timer. ++ * @wbuf: write-buffer descriptor ++ */ ++static void cancel_wbuf_timer_nolock(struct ubifs_wbuf *wbuf) ++{ ++ /* ++ * If the syncer is waiting for the lock (from the background thread's ++ * context) and another task is changing write-buffer then the syncing ++ * should be canceled. ++ */ ++ wbuf->need_sync = 0; ++ del_timer(&wbuf->timer); ++} ++ ++/** ++ * ubifs_wbuf_sync_nolock - synchronize write-buffer. ++ * @wbuf: write-buffer to synchronize ++ * ++ * This function synchronizes write-buffer @buf and returns zero in case of ++ * success or a negative error code in case of failure. ++ */ ++int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf) ++{ ++ struct ubifs_info *c = wbuf->c; ++ int err, dirt; ++ ++ cancel_wbuf_timer_nolock(wbuf); ++ if (!wbuf->used || wbuf->lnum == -1) ++ /* Write-buffer is empty or not seeked */ ++ return 0; ++ ++ dbg_io("LEB %d:%d, %d bytes", ++ wbuf->lnum, wbuf->offs, wbuf->used); ++ ubifs_assert(!(c->vfs_sb->s_flags & MS_RDONLY)); ++ ubifs_assert(!(wbuf->avail & 7)); ++ ubifs_assert(wbuf->offs + c->min_io_size <= c->leb_size); ++ ++ if (c->ro_media) ++ return -EROFS; ++ ++ ubifs_pad(c, wbuf->buf + wbuf->used, wbuf->avail); ++ err = ubi_leb_write(c->ubi, wbuf->lnum, wbuf->buf, wbuf->offs, ++ c->min_io_size, wbuf->dtype); ++ if (err) { ++ ubifs_err("cannot write %d bytes to LEB %d:%d", ++ c->min_io_size, wbuf->lnum, wbuf->offs); ++ dbg_dump_stack(); ++ return err; ++ } ++ ++ dirt = wbuf->avail; ++ ++ spin_lock(&wbuf->lock); ++ wbuf->offs += c->min_io_size; ++ wbuf->avail = c->min_io_size; ++ wbuf->used = 0; ++ wbuf->next_ino = 0; ++ spin_unlock(&wbuf->lock); ++ ++ if (wbuf->sync_callback) ++ err = wbuf->sync_callback(c, wbuf->lnum, ++ c->leb_size - wbuf->offs, dirt); ++ return err; ++} ++ ++/** ++ * ubifs_wbuf_seek_nolock - seek write-buffer. ++ * @wbuf: write-buffer ++ * @lnum: logical eraseblock number to seek to ++ * @offs: logical eraseblock offset to seek to ++ * @dtype: data type ++ * ++ * This function targets the write buffer to logical eraseblock @lnum:@offs. ++ * The write-buffer is synchronized if it is not empty. Returns zero in case of ++ * success and a negative error code in case of failure. ++ */ ++int ubifs_wbuf_seek_nolock(struct ubifs_wbuf *wbuf, int lnum, int offs, ++ int dtype) ++{ ++ const struct ubifs_info *c = wbuf->c; ++ ++ dbg_io("LEB %d:%d", lnum, offs); ++ ubifs_assert(lnum >= 0 && lnum < c->leb_cnt); ++ ubifs_assert(offs >= 0 && offs <= c->leb_size); ++ ubifs_assert(offs % c->min_io_size == 0 && !(offs & 7)); ++ ubifs_assert(lnum != wbuf->lnum); ++ ++ if (wbuf->used > 0) { ++ int err = ubifs_wbuf_sync_nolock(wbuf); ++ ++ if (err) ++ return err; ++ } ++ ++ spin_lock(&wbuf->lock); ++ wbuf->lnum = lnum; ++ wbuf->offs = offs; ++ wbuf->avail = c->min_io_size; ++ wbuf->used = 0; ++ spin_unlock(&wbuf->lock); ++ wbuf->dtype = dtype; ++ ++ return 0; ++} ++ ++/** ++ * ubifs_bg_wbufs_sync - synchronize write-buffers. ++ * @c: UBIFS file-system description object ++ * ++ * This function is called by background thread to synchronize write-buffers. ++ * Returns zero in case of success and a negative error code in case of ++ * failure. ++ */ ++int ubifs_bg_wbufs_sync(struct ubifs_info *c) ++{ ++ int err, i; ++ ++ if (!c->need_wbuf_sync) ++ return 0; ++ c->need_wbuf_sync = 0; ++ ++ if (c->ro_media) { ++ err = -EROFS; ++ goto out_timers; ++ } ++ ++ dbg_io("synchronize"); ++ for (i = 0; i < c->jhead_cnt; i++) { ++ struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf; ++ ++ cond_resched(); ++ ++ /* ++ * If the mutex is locked then wbuf is being changed, so ++ * synchronization is not necessary. ++ */ ++ if (mutex_is_locked(&wbuf->io_mutex)) ++ continue; ++ ++ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); ++ if (!wbuf->need_sync) { ++ mutex_unlock(&wbuf->io_mutex); ++ continue; ++ } ++ ++ err = ubifs_wbuf_sync_nolock(wbuf); ++ mutex_unlock(&wbuf->io_mutex); ++ if (err) { ++ ubifs_err("cannot sync write-buffer, error %d", err); ++ ubifs_ro_mode(c, err); ++ goto out_timers; ++ } ++ } ++ ++ return 0; ++ ++out_timers: ++ /* Cancel all timers to prevent repeated errors */ ++ for (i = 0; i < c->jhead_cnt; i++) { ++ struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf; ++ ++ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); ++ cancel_wbuf_timer_nolock(wbuf); ++ mutex_unlock(&wbuf->io_mutex); ++ } ++ return err; ++} ++ ++/** ++ * ubifs_wbuf_write_nolock - write data to flash via write-buffer. ++ * @wbuf: write-buffer ++ * @buf: node to write ++ * @len: node length ++ * ++ * This function writes data to flash via write-buffer @wbuf. This means that ++ * the last piece of the node won't reach the flash media immediately if it ++ * does not take whole minimal I/O unit. Instead, the node will sit in RAM ++ * until the write-buffer is synchronized (e.g., by timer). ++ * ++ * This function returns zero in case of success and a negative error code in ++ * case of failure. If the node cannot be written because there is no more ++ * space in this logical eraseblock, %-ENOSPC is returned. ++ */ ++int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len) ++{ ++ struct ubifs_info *c = wbuf->c; ++ int err, written, n, aligned_len = ALIGN(len, 8), offs; ++ ++ dbg_io("%d bytes (%s) to wbuf at LEB %d:%d", len, ++ dbg_ntype(((struct ubifs_ch *)buf)->node_type), wbuf->lnum, ++ wbuf->offs + wbuf->used); ++ ubifs_assert(len > 0 && wbuf->lnum >= 0 && wbuf->lnum < c->leb_cnt); ++ ubifs_assert(wbuf->offs >= 0 && wbuf->offs % c->min_io_size == 0); ++ ubifs_assert(!(wbuf->offs & 7) && wbuf->offs <= c->leb_size); ++ ubifs_assert(wbuf->avail > 0 && wbuf->avail <= c->min_io_size); ++ ubifs_assert(mutex_is_locked(&wbuf->io_mutex)); ++ ++ if (c->leb_size - wbuf->offs - wbuf->used < aligned_len) { ++ err = -ENOSPC; ++ goto out; ++ } ++ ++ cancel_wbuf_timer_nolock(wbuf); ++ ++ if (c->ro_media) ++ return -EROFS; ++ ++ if (aligned_len <= wbuf->avail) { ++ /* ++ * The node is not very large and fits entirely within ++ * write-buffer. ++ */ ++ memcpy(wbuf->buf + wbuf->used, buf, len); ++ ++ if (aligned_len == wbuf->avail) { ++ dbg_io("flush wbuf to LEB %d:%d", wbuf->lnum, ++ wbuf->offs); ++ err = ubi_leb_write(c->ubi, wbuf->lnum, wbuf->buf, ++ wbuf->offs, c->min_io_size, ++ wbuf->dtype); ++ if (err) ++ goto out; ++ ++ spin_lock(&wbuf->lock); ++ wbuf->offs += c->min_io_size; ++ wbuf->avail = c->min_io_size; ++ wbuf->used = 0; ++ wbuf->next_ino = 0; ++ spin_unlock(&wbuf->lock); ++ } else { ++ spin_lock(&wbuf->lock); ++ wbuf->avail -= aligned_len; ++ wbuf->used += aligned_len; ++ spin_unlock(&wbuf->lock); ++ } ++ ++ goto exit; ++ } ++ ++ /* ++ * The node is large enough and does not fit entirely within current ++ * minimal I/O unit. We have to fill and flush write-buffer and switch ++ * to the next min. I/O unit. ++ */ ++ dbg_io("flush wbuf to LEB %d:%d", wbuf->lnum, wbuf->offs); ++ memcpy(wbuf->buf + wbuf->used, buf, wbuf->avail); ++ err = ubi_leb_write(c->ubi, wbuf->lnum, wbuf->buf, wbuf->offs, ++ c->min_io_size, wbuf->dtype); ++ if (err) ++ goto out; ++ ++ offs = wbuf->offs + c->min_io_size; ++ len -= wbuf->avail; ++ aligned_len -= wbuf->avail; ++ written = wbuf->avail; ++ ++ /* ++ * The remaining data may take more whole min. I/O units, so write the ++ * remains multiple to min. I/O unit size directly to the flash media. ++ * We align node length to 8-byte boundary because we anyway flash wbuf ++ * if the remaining space is less than 8 bytes. ++ */ ++ n = aligned_len >> c->min_io_shift; ++ if (n) { ++ n <<= c->min_io_shift; ++ dbg_io("write %d bytes to LEB %d:%d", n, wbuf->lnum, offs); ++ err = ubi_leb_write(c->ubi, wbuf->lnum, buf + written, offs, n, ++ wbuf->dtype); ++ if (err) ++ goto out; ++ offs += n; ++ aligned_len -= n; ++ len -= n; ++ written += n; ++ } ++ ++ spin_lock(&wbuf->lock); ++ if (aligned_len) ++ /* ++ * And now we have what's left and what does not take whole ++ * min. I/O unit, so write it to the write-buffer and we are ++ * done. ++ */ ++ memcpy(wbuf->buf, buf + written, len); ++ ++ wbuf->offs = offs; ++ wbuf->used = aligned_len; ++ wbuf->avail = c->min_io_size - aligned_len; ++ wbuf->next_ino = 0; ++ spin_unlock(&wbuf->lock); ++ ++exit: ++ if (wbuf->sync_callback) { ++ int free = c->leb_size - wbuf->offs - wbuf->used; ++ ++ err = wbuf->sync_callback(c, wbuf->lnum, free, 0); ++ if (err) ++ goto out; ++ } ++ ++ if (wbuf->used) ++ new_wbuf_timer_nolock(wbuf); ++ ++ return 0; ++ ++out: ++ ubifs_err("cannot write %d bytes to LEB %d:%d, error %d", ++ len, wbuf->lnum, wbuf->offs, err); ++ dbg_dump_node(c, buf); ++ dbg_dump_stack(); ++ dbg_dump_leb(c, wbuf->lnum); ++ return err; ++} ++ ++/** ++ * ubifs_write_node - write node to the media. ++ * @c: UBIFS file-system description object ++ * @buf: the node to write ++ * @len: node length ++ * @lnum: logical eraseblock number ++ * @offs: offset within the logical eraseblock ++ * @dtype: node life-time hint (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN) ++ * ++ * This function automatically fills node magic number, assigns sequence ++ * number, and calculates node CRC checksum. The length of the @buf buffer has ++ * to be aligned to the minimal I/O unit size. This function automatically ++ * appends padding node and padding bytes if needed. Returns zero in case of ++ * success and a negative error code in case of failure. ++ */ ++int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum, ++ int offs, int dtype) ++{ ++ int err, buf_len = ALIGN(len, c->min_io_size); ++ ++ dbg_io("LEB %d:%d, %s, length %d (aligned %d)", ++ lnum, offs, dbg_ntype(((struct ubifs_ch *)buf)->node_type), len, ++ buf_len); ++ ubifs_assert(lnum >= 0 && lnum < c->leb_cnt && offs >= 0); ++ ubifs_assert(offs % c->min_io_size == 0 && offs < c->leb_size); ++ ++ if (c->ro_media) ++ return -EROFS; ++ ++ ubifs_prepare_node(c, buf, len, 1); ++ err = ubi_leb_write(c->ubi, lnum, buf, offs, buf_len, dtype); ++ if (err) { ++ ubifs_err("cannot write %d bytes to LEB %d:%d, error %d", ++ buf_len, lnum, offs, err); ++ dbg_dump_node(c, buf); ++ dbg_dump_stack(); ++ } ++ ++ return err; ++} ++ ++/** ++ * ubifs_read_node_wbuf - read node from the media or write-buffer. ++ * @wbuf: wbuf to check for un-written data ++ * @buf: buffer to read to ++ * @type: node type ++ * @len: node length ++ * @lnum: logical eraseblock number ++ * @offs: offset within the logical eraseblock ++ * ++ * This function reads a node of known type and length, checks it and stores ++ * in @buf. If the node partially or fully sits in the write-buffer, this ++ * function takes data from the buffer, otherwise it reads the flash media. ++ * Returns zero in case of success, %-EUCLEAN if CRC mismatched and a negative ++ * error code in case of failure. ++ */ ++int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len, ++ int lnum, int offs) ++{ ++ const struct ubifs_info *c = wbuf->c; ++ int err, rlen, overlap; ++ struct ubifs_ch *ch = buf; ++ ++ dbg_io("LEB %d:%d, %s, length %d", lnum, offs, dbg_ntype(type), len); ++ ubifs_assert(wbuf && lnum >= 0 && lnum < c->leb_cnt && offs >= 0); ++ ubifs_assert(!(offs & 7) && offs < c->leb_size); ++ ubifs_assert(type >= 0 && type < UBIFS_NODE_TYPES_CNT); ++ ++ spin_lock(&wbuf->lock); ++ overlap = (lnum == wbuf->lnum && offs + len > wbuf->offs); ++ if (!overlap) { ++ /* We may safely unlock the write-buffer and read the data */ ++ spin_unlock(&wbuf->lock); ++ return ubifs_read_node(c, buf, type, len, lnum, offs); ++ } ++ ++ /* Don't read under wbuf */ ++ rlen = wbuf->offs - offs; ++ if (rlen < 0) ++ rlen = 0; ++ ++ /* Copy the rest from the write-buffer */ ++ memcpy(buf + rlen, wbuf->buf + offs + rlen - wbuf->offs, len - rlen); ++ spin_unlock(&wbuf->lock); ++ ++ if (rlen > 0) { ++ /* Read everything that goes before write-buffer */ ++ err = ubi_read(c->ubi, lnum, buf, offs, rlen); ++ if (err && err != -EBADMSG) { ++ ubifs_err("failed to read node %d from LEB %d:%d, " ++ "error %d", type, lnum, offs, err); ++ dbg_dump_stack(); ++ return err; ++ } ++ } ++ ++ err = ubifs_check_node(c, buf, lnum, offs, 0); ++ if (err) { ++ ubifs_err("expected node type %d", type); ++ return err; ++ } ++ ++ if (type != ch->node_type) { ++ ubifs_err("bad node type (%d but expected %d)", ++ ch->node_type, type); ++ goto out; ++ } ++ ++ rlen = le32_to_cpu(ch->len); ++ if (rlen != len) { ++ ubifs_err("bad node length %d, expected %d", rlen, len); ++ goto out; ++ } ++ ++ return 0; ++ ++out: ++ ubifs_err("bad node at LEB %d:%d", lnum, offs); ++ dbg_dump_node(c, buf); ++ dbg_dump_stack(); ++ return -EINVAL; ++} ++ ++/** ++ * ubifs_read_node - read node. ++ * @c: UBIFS file-system description object ++ * @buf: buffer to read to ++ * @type: node type ++ * @len: node length (not aligned) ++ * @lnum: logical eraseblock number ++ * @offs: offset within the logical eraseblock ++ * ++ * This function reads a node of known type and and length, checks it and ++ * stores in @buf. Returns zero in case of success, %-EUCLEAN if CRC mismatched ++ * and a negative error code in case of failure. ++ */ ++int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len, ++ int lnum, int offs) ++{ ++ int err, l; ++ struct ubifs_ch *ch = buf; ++ ++ dbg_io("LEB %d:%d, %s, length %d", lnum, offs, dbg_ntype(type), len); ++ ubifs_assert(lnum >= 0 && lnum < c->leb_cnt && offs >= 0); ++ ubifs_assert(len >= UBIFS_CH_SZ && offs + len <= c->leb_size); ++ ubifs_assert(!(offs & 7) && offs < c->leb_size); ++ ubifs_assert(type >= 0 && type < UBIFS_NODE_TYPES_CNT); ++ ++ err = ubi_read(c->ubi, lnum, buf, offs, len); ++ if (err && err != -EBADMSG) { ++ ubifs_err("cannot read node %d from LEB %d:%d, error %d", ++ type, lnum, offs, err); ++ return err; ++ } ++ ++ err = ubifs_check_node(c, buf, lnum, offs, 0); ++ if (err) { ++ ubifs_err("expected node type %d", type); ++ return err; ++ } ++ ++ if (type != ch->node_type) { ++ ubifs_err("bad node type (%d but expected %d)", ++ ch->node_type, type); ++ goto out; ++ } ++ ++ l = le32_to_cpu(ch->len); ++ if (l != len) { ++ ubifs_err("bad node length %d, expected %d", l, len); ++ goto out; ++ } ++ ++ return 0; ++ ++out: ++ ubifs_err("bad node at LEB %d:%d", lnum, offs); ++ dbg_dump_node(c, buf); ++ dbg_dump_stack(); ++ return -EINVAL; ++} ++ ++/** ++ * ubifs_wbuf_init - initialize write-buffer. ++ * @c: UBIFS file-system description object ++ * @wbuf: write-buffer to initialize ++ * ++ * This function initializes write buffer. Returns zero in case of success ++ * %-ENOMEM in case of failure. ++ */ ++int ubifs_wbuf_init(struct ubifs_info *c, struct ubifs_wbuf *wbuf) ++{ ++ size_t size; ++ ++ wbuf->buf = kmalloc(c->min_io_size, GFP_KERNEL); ++ if (!wbuf->buf) ++ return -ENOMEM; ++ ++ size = (c->min_io_size / UBIFS_CH_SZ + 1) * sizeof(ino_t); ++ wbuf->inodes = kmalloc(size, GFP_KERNEL); ++ if (!wbuf->inodes) { ++ kfree(wbuf->buf); ++ wbuf->buf = NULL; ++ return -ENOMEM; ++ } ++ ++ wbuf->used = 0; ++ wbuf->lnum = wbuf->offs = -1; ++ wbuf->avail = c->min_io_size; ++ wbuf->dtype = UBI_UNKNOWN; ++ wbuf->sync_callback = NULL; ++ mutex_init(&wbuf->io_mutex); ++ spin_lock_init(&wbuf->lock); ++ ++ wbuf->c = c; ++ init_timer(&wbuf->timer); ++ wbuf->timer.function = wbuf_timer_callback_nolock; ++ wbuf->timer.data = (unsigned long)wbuf; ++ wbuf->timeout = DEFAULT_WBUF_TIMEOUT; ++ wbuf->next_ino = 0; ++ ++ return 0; ++} ++ ++/** ++ * ubifs_wbuf_add_ino_nolock - add an inode number into the wbuf inode array. ++ * @wbuf: the write-buffer whereto add ++ * @inum: the inode number ++ * ++ * This function adds an inode number to the inode array of the write-buffer. ++ */ ++void ubifs_wbuf_add_ino_nolock(struct ubifs_wbuf *wbuf, ino_t inum) ++{ ++ if (!wbuf->buf) ++ /* NOR flash or something similar */ ++ return; ++ ++ spin_lock(&wbuf->lock); ++ if (wbuf->used) ++ wbuf->inodes[wbuf->next_ino++] = inum; ++ spin_unlock(&wbuf->lock); ++} ++ ++/** ++ * wbuf_has_ino - returns if the wbuf contains data from the inode. ++ * @wbuf: the write-buffer ++ * @inum: the inode number ++ * ++ * This function returns with %1 if the write-buffer contains some data from the ++ * given inode otherwise it returns with %0. ++ */ ++static int wbuf_has_ino(struct ubifs_wbuf *wbuf, ino_t inum) ++{ ++ int i, ret = 0; ++ ++ spin_lock(&wbuf->lock); ++ for (i = 0; i < wbuf->next_ino; i++) ++ if (inum == wbuf->inodes[i]) { ++ ret = 1; ++ break; ++ } ++ spin_unlock(&wbuf->lock); ++ ++ return ret; ++} ++ ++/** ++ * ubifs_sync_wbufs_by_inodes - synchronize write-buffers which have data. ++ * belonging to specified inodes. ++ * @c: UBIFS file-system description object ++ * @inodes: array of inodes ++ * @count: number of elements in @inodes ++ * ++ * This function synchronizes write-buffers which contain nodes belonging to ++ * any inode specified in @inodes array. Returns zero in case of success and a ++ * negative error code in case of failure. ++ */ ++int ubifs_sync_wbufs_by_inodes(struct ubifs_info *c, ++ struct inode * const *inodes, int count) ++{ ++ int i, j, err = 0; ++ ++ ubifs_assert(count); ++ ++ for (i = 0; i < c->jhead_cnt; i++) { ++ struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf; ++ ++ if (i == GCHD) ++ /* ++ * GC head is special, do not look at it. Even if the ++ * head contains something related to this inode, it is ++ * a _copy_ of corresponding on-flash node which sits ++ * somewhere else. ++ */ ++ continue; ++ ++ for (j = 0; j < count && !err; j++) ++ if (wbuf_has_ino(wbuf, inodes[j]->i_ino)) { ++ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); ++ if (wbuf_has_ino(wbuf, inodes[j]->i_ino)) ++ err = ubifs_wbuf_sync_nolock(wbuf); ++ mutex_unlock(&wbuf->io_mutex); ++ break; ++ } ++ ++ if (err) { ++ ubifs_ro_mode(c, err); ++ break; ++ } ++ } ++ ++ return err; ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/ioctl.c linux-2.6.24.7/fs/ubifs/ioctl.c +--- linux-2.6.24.7.old/fs/ubifs/ioctl.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/ioctl.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,204 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * Copyright (C) 2006, 2007 University of Szeged, Hungary ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Zoltan Sogor ++ * Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* This file implements EXT2-compatible extended attribute ioctl() calls */ ++ ++#include ++#include ++#include "ubifs.h" ++ ++/** ++ * ubifs_set_inode_flags - set VFS inode flags. ++ * @inode: VFS inode to set flags for ++ * ++ * This function propagates flags from UBIFS inode object to VFS inode object. ++ */ ++void ubifs_set_inode_flags(struct inode *inode) ++{ ++ unsigned int flags = ubifs_inode(inode)->flags; ++ ++ inode->i_flags &= ~(S_SYNC | S_APPEND | S_IMMUTABLE | S_DIRSYNC); ++ if (flags & UBIFS_SYNC_FL) ++ inode->i_flags |= S_SYNC; ++ if (flags & UBIFS_APPEND_FL) ++ inode->i_flags |= S_APPEND; ++ if (flags & UBIFS_IMMUTABLE_FL) ++ inode->i_flags |= S_IMMUTABLE; ++ if (flags & UBIFS_DIRSYNC_FL) ++ inode->i_flags |= S_DIRSYNC; ++} ++ ++/* ++ * ioctl2ubifs - convert ioctl inode flags to UBIFS inode flags. ++ * @ioctl_flags: flags to convert ++ * ++ * This function convert ioctl flags (@FS_COMPR_FL, etc) to UBIFS inode flags ++ * (@UBIFS_COMPR_FL, etc). ++ */ ++static int ioctl2ubifs(int ioctl_flags) ++{ ++ int ubifs_flags = 0; ++ ++ if (ioctl_flags & FS_COMPR_FL) ++ ubifs_flags |= UBIFS_COMPR_FL; ++ if (ioctl_flags & FS_SYNC_FL) ++ ubifs_flags |= UBIFS_SYNC_FL; ++ if (ioctl_flags & FS_APPEND_FL) ++ ubifs_flags |= UBIFS_APPEND_FL; ++ if (ioctl_flags & FS_IMMUTABLE_FL) ++ ubifs_flags |= UBIFS_IMMUTABLE_FL; ++ if (ioctl_flags & FS_DIRSYNC_FL) ++ ubifs_flags |= UBIFS_DIRSYNC_FL; ++ ++ return ubifs_flags; ++} ++ ++/* ++ * ubifs2ioctl - convert UBIFS inode flags to ioctl inode flags. ++ * @ubifs_flags: flags to convert ++ * ++ * This function convert UBIFS (@UBIFS_COMPR_FL, etc) to ioctl flags ++ * (@FS_COMPR_FL, etc). ++ */ ++static int ubifs2ioctl(int ubifs_flags) ++{ ++ int ioctl_flags = 0; ++ ++ if (ubifs_flags & UBIFS_COMPR_FL) ++ ioctl_flags |= FS_COMPR_FL; ++ if (ubifs_flags & UBIFS_SYNC_FL) ++ ioctl_flags |= FS_SYNC_FL; ++ if (ubifs_flags & UBIFS_APPEND_FL) ++ ioctl_flags |= FS_APPEND_FL; ++ if (ubifs_flags & UBIFS_IMMUTABLE_FL) ++ ioctl_flags |= FS_IMMUTABLE_FL; ++ if (ubifs_flags & UBIFS_DIRSYNC_FL) ++ ioctl_flags |= FS_DIRSYNC_FL; ++ ++ return ioctl_flags; ++} ++ ++static int setflags(struct inode *inode, int flags) ++{ ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ struct ubifs_budget_req req; ++ int oldflags, err; ++ ++ mutex_lock(&inode->i_mutex); ++ ++ memset(&req, 0 , sizeof(struct ubifs_budget_req)); ++ err = ubifs_budget_inode_op(c, inode, &req); ++ if (err) ++ goto out; ++ ++ /* ++ * The IMMUTABLE and APPEND_ONLY flags can only be changed by ++ * the relevant capability. ++ */ ++ oldflags = ubifs2ioctl(ui->flags); ++ if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { ++ if (!capable(CAP_LINUX_IMMUTABLE)) { ++ err = -EPERM; ++ goto out_budg; ++ } ++ } ++ ++ ui->flags = ioctl2ubifs(flags); ++ ubifs_set_inode_flags(inode); ++ ++ inode->i_ctime = ubifs_current_time(inode); ++ mark_inode_dirty_sync(inode); ++ ++ ubifs_release_ino_dirty(c, inode, &req); ++ ++ if (IS_SYNC(inode)) ++ err = write_inode_now(inode, 1); ++ ++ mutex_unlock(&inode->i_mutex); ++ return err; ++ ++out_budg: ++ ubifs_cancel_ino_op(c, inode, &req); ++out: ++ ubifs_err("can't modify inode %lu attributes", inode->i_ino); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ int flags; ++ struct inode *inode = file->f_path.dentry->d_inode; ++ ++ switch (cmd) { ++ case FS_IOC_GETFLAGS: ++ flags = ubifs2ioctl(ubifs_inode(inode)->flags); ++ ++ return put_user(flags, (int __user *) arg); ++ ++ case FS_IOC_SETFLAGS: { ++ if (IS_RDONLY(inode)) ++ return -EROFS; ++ ++ if (!is_owner_or_cap(inode)) ++ return -EACCES; ++ ++ if (get_user(flags, (int __user *) arg)) ++ return -EFAULT; ++ ++ if (!S_ISDIR(inode->i_mode)) ++ flags &= ~FS_DIRSYNC_FL; ++ ++ return setflags(inode, flags); ++ } ++ ++ default: ++ return -ENOTTY; ++ } ++} ++ ++#ifdef CONFIG_COMPAT ++long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ int err; ++ ++ switch (cmd) { ++ case FS_IOC32_GETFLAGS: ++ cmd = FS_IOC_GETFLAGS; ++ break; ++ case FS_IOC32_SETFLAGS: ++ cmd = FS_IOC_SETFLAGS; ++ break; ++ default: ++ return -ENOIOCTLCMD; ++ } ++ ++ lock_kernel(); ++ err = ubifs_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); ++ unlock_kernel(); ++ ++ return err; ++} ++#endif +diff -urN linux-2.6.24.7.old/fs/ubifs/journal.c linux-2.6.24.7/fs/ubifs/journal.c +--- linux-2.6.24.7.old/fs/ubifs/journal.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/journal.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1275 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This file implements UBIFS journal. ++ * ++ * The journal consists of 2 parts - the log and bud LEBs. The log has fixed ++ * length and position, while a bud logical eraseblock is any LEB in the main ++ * area. Buds contain file system data - data nodes, inode nodes, etc. The log ++ * contains only references to buds and some other stuff like commit ++ * start node. The idea is that when we commit the journal, we do ++ * not copy the data, the buds just become indexed. Since after the commit the ++ * nodes in bud eraseblocks become leaf nodes of the file system index tree, we ++ * use term "bud". Analogy is obvious, bud eraseblocks contain nodes which will ++ * become leafs in the future. ++ * ++ * The journal is multi-headed because we want to write data to the journal as ++ * optimally as possible. It is nice to have nodes belonging to the same inode ++ * in one LEB, so we may write data owned by different inodes to different ++ * journal heads, although at present only one data head is used. ++ * ++ * For recovery reasons, the base head contains all inode nodes, all directory ++ * entry nodes and all truncate nodes. This means that the other heads contain ++ * only data nodes. ++ * ++ * Bud LEBs may be half-indexed. For example, if the bud was not full at the ++ * time of commit, the bud is retained to continue to be used in the journal, ++ * even though the "front" of the LEB is now indexed. In that case, the log ++ * reference contains the offset where the bud starts for the purposes of the ++ * journal. ++ * ++ * The journal size has to be limited, because the larger is the journal, the ++ * longer it takes to mount UBIFS (scanning the journal) and the more memory it ++ * takes (indexing in the TNC). ++ * ++ * Note, all the journal write operations like 'ubifs_jnl_update()' here, which ++ * write multiple UBIFS nodes to the journal at one go, are atomic with respect ++ * to unclean reboots. Should the unclean reboot happen, the recovery code drops ++ * all the nodes. ++ */ ++ ++#include "ubifs.h" ++ ++/** ++ * zero_ino_node_unused - zero out unused fields of an on-flash inode node. ++ * @ino: the inode to zero out ++ */ ++static inline void zero_ino_node_unused(struct ubifs_ino_node *ino) ++{ ++ memset(ino->padding, 0, 26); ++} ++ ++/** ++ * zero_dent_node_unused - zero out unused fields of an on-flash directory ++ * entry node. ++ * @ino: the directory entry to zero out ++ */ ++static inline void zero_dent_node_unused(struct ubifs_dent_node *dent) ++{ ++ dent->padding1 = 0; ++ memset(dent->padding2, 0, 4); ++} ++ ++/** ++ * zero_data_node_unused - zero out unused fields of an on-flash data node. ++ * @ino: the data node to zero out ++ */ ++static inline void zero_data_node_unused(struct ubifs_data_node *data) ++{ ++ memset(data->padding, 0, 2); ++} ++ ++/** ++ * reserve_space - reserve space in the journal. ++ * @c: UBIFS file-system description object ++ * @jhead: journal head number ++ * @len: node length ++ * ++ * This function reserves space in journal head @head. If the reservation ++ * succeeded, the journal head stays locked and later has to be unlocked using ++ * 'release_head()'. 'write_node()' and 'write_head()' functions also unlock ++ * it. Returns zero in case of success, %-EAGAIN if commit has to be done, and ++ * other negative error codes in case of other failures. ++ */ ++static int reserve_space(struct ubifs_info *c, int jhead, int len) ++{ ++ int err = 0, err1, retries = 0, avail, lnum, offs, free, squeeze; ++ struct ubifs_wbuf *wbuf = &c->jheads[jhead].wbuf; ++ ++ /* ++ * Typically, the base head has smaller nodes written to it, so it is ++ * better to try to allocate space at the ends of eraseblocks. This is ++ * what the squeeze parameter does. ++ */ ++ squeeze = (jhead == BASEHD); ++again: ++ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); ++ avail = c->leb_size - wbuf->offs - wbuf->used; ++ ++ if (wbuf->lnum != -1 && avail >= len) ++ return 0; ++ ++ /* ++ * Write buffer wasn't seek'ed or there is no enough space - look for an ++ * LEB with some empty space. ++ */ ++ lnum = ubifs_find_free_space(c, len, &free, squeeze); ++ if (lnum >= 0) { ++ /* Found an LEB, add it to the journal head */ ++ offs = c->leb_size - free; ++ err = ubifs_add_bud_to_log(c, jhead, lnum, offs); ++ if (err) ++ goto out_return; ++ /* A new bud was successfully allocated and added to the log */ ++ goto out; ++ } ++ ++ err = lnum; ++ if (err != -ENOSPC) ++ goto out_unlock; ++ ++ /* ++ * No free space, we have to run garbage collector to make ++ * some. But the write-buffer mutex has to be unlocked because ++ * GC have to sync write buffers, which may lead a deadlock. ++ */ ++ dbg_jnl("no free space jhead %d, run GC", jhead); ++ mutex_unlock(&wbuf->io_mutex); ++ ++ lnum = ubifs_garbage_collect(c, 0); ++ if (lnum < 0) { ++ err = lnum; ++ if (err != -ENOSPC) ++ return err; ++ ++ /* ++ * GC could not make a free LEB. But someone else may ++ * have allocated new bud for this journal head, ++ * because we dropped the 'io_mutex', so try once ++ * again. ++ */ ++ dbg_jnl("GC couldn't make a free LEB for jhead %d", jhead); ++ if (retries++ < 2) { ++ dbg_jnl("retry (%d)", retries); ++ goto again; ++ } ++ ++ dbg_jnl("return -ENOSPC"); ++ return err; ++ } ++ ++ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); ++ dbg_jnl("got LEB %d for jhead %d", lnum, jhead); ++ avail = c->leb_size - wbuf->offs - wbuf->used; ++ ++ if (wbuf->lnum != -1 && avail >= len) { ++ /* ++ * Someone else has switched the journal head and we have ++ * enough space now. This happens when more then one process is ++ * trying to write to the same journal head at the same time. ++ */ ++ dbg_jnl("return LEB %d back, already have LEB %d:%d", ++ lnum, wbuf->lnum, wbuf->offs + wbuf->used); ++ err = ubifs_return_leb(c, lnum); ++ if (err) ++ goto out_unlock; ++ return 0; ++ } ++ ++ err = ubifs_add_bud_to_log(c, jhead, lnum, 0); ++ if (err) ++ goto out_return; ++ offs = 0; ++ ++out: ++ err = ubifs_wbuf_seek_nolock(wbuf, lnum, offs, UBI_SHORTTERM); ++ if (err) ++ goto out_unlock; ++ ++ return 0; ++ ++out_unlock: ++ mutex_unlock(&wbuf->io_mutex); ++ return err; ++ ++out_return: ++ /* An error occurred and the LEB has to be returned to lprops */ ++ ubifs_assert(err < 0); ++ err1 = ubifs_return_leb(c, lnum); ++ if (err1 && err == -EAGAIN) ++ /* ++ * Return original error code 'err' only if it is not ++ * '-EAGAIN', which is not really an error. Otherwise, return ++ * the error code of 'ubifs_return_leb()'. ++ */ ++ err = err1; ++ mutex_unlock(&wbuf->io_mutex); ++ return err; ++} ++ ++/** ++ * write_node - write node to a journal head. ++ * @c: UBIFS file-system description object ++ * @jhead: journal head ++ * @node: node to write ++ * @len: node length ++ * @lnum: LEB number written is returned here ++ * @offs: offset written is returned here ++ * ++ * This function writes a node to reserved space of journal head @jhead. ++ * Returns zero in case of success and a negative error code in case of ++ * failure. ++ */ ++static int write_node(struct ubifs_info *c, int jhead, void *node, int len, ++ int *lnum, int *offs) ++{ ++ struct ubifs_wbuf *wbuf = &c->jheads[jhead].wbuf; ++ ++ ubifs_assert(jhead != GCHD); ++ ++ *lnum = c->jheads[jhead].wbuf.lnum; ++ *offs = c->jheads[jhead].wbuf.offs + c->jheads[jhead].wbuf.used; ++ ++ dbg_jnl("jhead %d, LEB %d:%d, len %d", jhead, *lnum, *offs, len); ++ ubifs_prepare_node(c, node, len, 0); ++ ++ return ubifs_wbuf_write_nolock(wbuf, node, len); ++} ++ ++/** ++ * write_head - write data to a journal head. ++ * @c: UBIFS file-system description object ++ * @jhead: journal head ++ * @buf: buffer to write ++ * @len: length to write ++ * @lnum: LEB number written is returned here ++ * @offs: offset written is returned here ++ * @sync: non-zero if the write-buffer has to by synchronized ++ * ++ * This function is the same as 'write_node()' but it does not assume the ++ * buffer it is writing is a node, so it does not prepare it (which means ++ * initializing common header and calculating CRC). ++ */ ++static int write_head(struct ubifs_info *c, int jhead, void *buf, int len, ++ int *lnum, int *offs, int sync) ++{ ++ int err; ++ struct ubifs_wbuf *wbuf = &c->jheads[jhead].wbuf; ++ ++ ubifs_assert(jhead != GCHD); ++ ++ *lnum = c->jheads[jhead].wbuf.lnum; ++ *offs = c->jheads[jhead].wbuf.offs + c->jheads[jhead].wbuf.used; ++ dbg_jnl("jhead %d, LEB %d:%d, len %d", jhead, *lnum, *offs, len); ++ ++ err = ubifs_wbuf_write_nolock(wbuf, buf, len); ++ if (err) ++ return err; ++ if (sync) ++ err = ubifs_wbuf_sync_nolock(wbuf); ++ return err; ++} ++ ++/** ++ * make_reservation - reserve journal space. ++ * @c: UBIFS file-system description object ++ * @jhead: journal head ++ * @len: how many bytes to reserve ++ * ++ * This function makes space reservation in journal head @jhead. The function ++ * takes the commit lock and locks the journal head, and the caller has to ++ * unlock the head and finish the reservation with 'finish_reservation()'. ++ * Returns zero in case of success and a negative error code in case of ++ * failure. ++ * ++ * Note, the journal head may be unlocked as soon as the data is written, while ++ * the commit lock has to be released after the data has been added to the ++ * TNC. ++ */ ++static int make_reservation(struct ubifs_info *c, int jhead, int len) ++{ ++ int err, cmt_retries = 0, nospc_retries = 0; ++ ++ ubifs_assert(len <= c->dark_wm); ++ ++again: ++ down_read(&c->commit_sem); ++ err = reserve_space(c, jhead, len); ++ if (!err) ++ return 0; ++ up_read(&c->commit_sem); ++ ++ if (err == -ENOSPC) { ++ /* ++ * GC could not make any progress. We should try to commit ++ * once because it could make some dirty space and GC would ++ * make progress, so make the error -EAGAIN so that the below ++ * will commit and re-try. ++ */ ++ if (nospc_retries++ < 2) { ++ dbg_jnl("no space, retry"); ++ err = -EAGAIN; ++ } ++ ++ /* ++ * This means that the budgeting is incorrect. We always have ++ * to be able to write to the media, because all operations are ++ * budgeted. Deletions are not budgeted, though, but we reserve ++ * an extra LEB for them. ++ */ ++ } ++ ++ if (err != -EAGAIN) ++ goto out; ++ ++ /* ++ * -EAGAIN means that the journal is full or too large, or the above ++ * code wants to do one commit. Do this and re-try. ++ */ ++ if (cmt_retries > 128) { ++ /* ++ * This should not happen unless the journal size limitations ++ * are too tough. ++ */ ++ ubifs_err("stuck in space allocation"); ++ err = -ENOSPC; ++ goto out; ++ } else if (cmt_retries > 32) ++ ubifs_warn("too many space allocation re-tries (%d)", ++ cmt_retries); ++ ++ dbg_jnl("-EAGAIN, commit and retry (retried %d times)", ++ cmt_retries); ++ cmt_retries += 1; ++ ++ err = ubifs_run_commit(c); ++ if (err) ++ return err; ++ goto again; ++ ++out: ++ ubifs_err("cannot reserve %d bytes in jhead %d, error %d", ++ len, jhead, err); ++ if (err == -ENOSPC) { ++ /* This are some budgeting problems, print useful information */ ++ down_write(&c->commit_sem); ++ spin_lock(&c->space_lock); ++ dbg_dump_stack(); ++ dbg_dump_budg(c); ++ spin_unlock(&c->space_lock); ++ dbg_dump_lprops(c); ++ cmt_retries = dbg_check_lprops(c); ++ up_write(&c->commit_sem); ++ } ++ ++ return err; ++} ++ ++/** ++ * release_head - release a journal head. ++ * @c: UBIFS file-system description object ++ * @jhead: journal head ++ * ++ * This function releases journal head @jhead which was locked by ++ * the 'make_reservation()' function. It has to be called after each successful ++ * 'make_reservation()' invocation. ++ */ ++static inline void release_head(struct ubifs_info *c, int jhead) ++{ ++ mutex_unlock(&c->jheads[jhead].wbuf.io_mutex); ++} ++ ++/** ++ * finish_reservation - finish a reservation. ++ * @c: UBIFS file-system description object ++ * ++ * This function finishes journal space reservation. It must be called after ++ * 'make_reservation()'. ++ */ ++static void finish_reservation(struct ubifs_info *c) ++{ ++ up_read(&c->commit_sem); ++} ++ ++/** ++ * get_dent_type - translate VFS inode mode to UBIFS directory entry type. ++ * @mode: inode mode ++ */ ++static int get_dent_type(int mode) ++{ ++ switch (mode & S_IFMT) { ++ case S_IFREG: ++ return UBIFS_ITYPE_REG; ++ case S_IFDIR: ++ return UBIFS_ITYPE_DIR; ++ case S_IFLNK: ++ return UBIFS_ITYPE_LNK; ++ case S_IFBLK: ++ return UBIFS_ITYPE_BLK; ++ case S_IFCHR: ++ return UBIFS_ITYPE_CHR; ++ case S_IFIFO: ++ return UBIFS_ITYPE_FIFO; ++ case S_IFSOCK: ++ return UBIFS_ITYPE_SOCK; ++ default: ++ BUG(); ++ } ++ return 0; ++} ++ ++/** ++ * pack_inode - pack an inode node. ++ * @c: UBIFS file-system description object ++ * @ino: buffer in which to pack inode node ++ * @inode: inode to pack ++ * @last: indicates the last node of the group ++ * @last_reference: non-zero if this is a deletion inode ++ */ ++static void pack_inode(struct ubifs_info *c, struct ubifs_ino_node *ino, ++ const struct inode *inode, int last, int last_reference) ++{ ++ int data_len = 0; ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ ++ ino->ch.node_type = UBIFS_INO_NODE; ++ ino_key_init_flash(c, &ino->key, inode->i_ino); ++ ino->creat_sqnum = cpu_to_le64(ui->creat_sqnum); ++ ino->size = cpu_to_le64(i_size_read(inode)); ++ ino->nlink = cpu_to_le32(inode->i_nlink); ++ ino->atime_sec = cpu_to_le64(inode->i_atime.tv_sec); ++ ino->atime_nsec = cpu_to_le32(inode->i_atime.tv_nsec); ++ ino->ctime_sec = cpu_to_le64(inode->i_ctime.tv_sec); ++ ino->ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); ++ ino->mtime_sec = cpu_to_le64(inode->i_mtime.tv_sec); ++ ino->mtime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec); ++ ino->uid = cpu_to_le32(inode->i_uid); ++ ino->gid = cpu_to_le32(inode->i_gid); ++ ino->mode = cpu_to_le32(inode->i_mode); ++ ino->flags = cpu_to_le32(ui->flags); ++ ino->compr_type = cpu_to_le16(ui->compr_type); ++ ino->xattr_cnt = cpu_to_le32(ui->xattr_cnt); ++ ino->xattr_size = cpu_to_le64(ui->xattr_size); ++ ino->xattr_names = cpu_to_le32(ui->xattr_names); ++ ino->data_len = cpu_to_le32(ui->data_len); ++ zero_ino_node_unused(ino); ++ ++ /* ++ * Drop the attached data if this is a deletion inode, the data is not ++ * needed anymore. ++ */ ++ if (!last_reference) { ++ memcpy(ino->data, ui->data, ui->data_len); ++ data_len = ui->data_len; ++ } ++ ++ ubifs_prep_grp_node(c, ino, UBIFS_INO_NODE_SZ + data_len, last); ++} ++ ++/** ++ * ubifs_jnl_update - update inode. ++ * @c: UBIFS file-system description object ++ * @dir: parent inode or host inode in case of extended attributes ++ * @nm: directory entry name ++ * @inode: inode ++ * @deletion: indicates a directory entry deletion i.e unlink or rmdir ++ * @sync: non-zero if the write-buffer has to be synchronized ++ * @xent: non-zero if the directory entry is an extended attribute entry ++ * ++ * This function updates an inode by writing a directory entry (or extended ++ * attribute entry), the inode itself, and the parent directory inode (or the ++ * host inode) to the journal. ++ * ++ * The function writes the host inode @dir last, which is important in case of ++ * extended attributes. Indeed, then we guarantee that if the host inode gets ++ * synchronized, and the write-buffer it sits in gets flushed, the extended ++ * attribute inode gets flushed too. And this is exactly what the user expects - ++ * synchronizing the host inode synchronizes its extended attributes. ++ * Similarly, this guarantees that if @dir is synchronized, its directory entry ++ * corresponding to @nm gets synchronized too. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir, ++ const struct qstr *nm, const struct inode *inode, ++ int deletion, int sync, int xent) ++{ ++ int err, dlen, ilen, len, lnum, ino_offs, dent_offs; ++ int aligned_dlen, aligned_ilen; ++ int last_reference = !!(deletion && inode->i_nlink == 0); ++ struct ubifs_dent_node *dent; ++ struct ubifs_ino_node *ino; ++ union ubifs_key dent_key, ino_key; ++ ++ dbg_jnl("ino %lu, dent '%.*s', data len %d in dir ino %lu", ++ inode->i_ino, nm->len, nm->name, ubifs_inode(inode)->data_len, ++ dir->i_ino); ++ ubifs_assert(ubifs_inode(dir)->data_len == 0); ++ ++ dlen = UBIFS_DENT_NODE_SZ + nm->len + 1; ++ ilen = UBIFS_INO_NODE_SZ; ++ ++ /* ++ * If the last reference to the inode is being deleted, then there is no ++ * need to attach and write inode data, it is being deleted anyway. ++ */ ++ if (!last_reference) ++ ilen += ubifs_inode(inode)->data_len; ++ ++ aligned_dlen = ALIGN(dlen, 8); ++ aligned_ilen = ALIGN(ilen, 8); ++ ++ len = aligned_dlen + aligned_ilen + UBIFS_INO_NODE_SZ; ++ ++ dent = kmalloc(len, GFP_NOFS); ++ if (!dent) ++ return -ENOMEM; ++ ++ /* Make reservation before allocating sequence numbers */ ++ err = make_reservation(c, BASEHD, len); ++ if (err) ++ goto out_free; ++ ++ if (!xent) { ++ dent->ch.node_type = UBIFS_DENT_NODE; ++ dent_key_init(c, &dent_key, dir->i_ino, nm); ++ } else { ++ dent->ch.node_type = UBIFS_XENT_NODE; ++ xent_key_init(c, &dent_key, dir->i_ino, nm); ++ } ++ ++ key_write(c, &dent_key, dent->key); ++ dent->inum = deletion ? 0 : cpu_to_le64(inode->i_ino); ++ dent->type = get_dent_type(inode->i_mode); ++ dent->nlen = cpu_to_le16(nm->len); ++ memcpy(dent->name, nm->name, nm->len); ++ dent->name[nm->len] = '\0'; ++ zero_dent_node_unused(dent); ++ ubifs_prep_grp_node(c, dent, dlen, 0); ++ ++ ino = (void *)dent + aligned_dlen; ++ pack_inode(c, ino, inode, 0, last_reference); ++ ++ ino = (void *)ino + aligned_ilen; ++ pack_inode(c, ino, dir, 1, 0); ++ ++ if (last_reference) { ++ err = ubifs_add_orphan(c, inode->i_ino); ++ if (err) { ++ release_head(c, BASEHD); ++ goto out_finish; ++ } ++ } ++ ++ err = write_head(c, BASEHD, dent, len, &lnum, &dent_offs, sync); ++ if (!sync && !err) { ++ struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf; ++ ++ ubifs_wbuf_add_ino_nolock(wbuf, inode->i_ino); ++ ubifs_wbuf_add_ino_nolock(wbuf, dir->i_ino); ++ } ++ release_head(c, BASEHD); ++ kfree(dent); ++ if (err) ++ goto out_ro; ++ ++ if (deletion) { ++ err = ubifs_tnc_remove_nm(c, &dent_key, nm); ++ if (err) ++ goto out_ro; ++ err = ubifs_add_dirt(c, lnum, dlen); ++ } else ++ err = ubifs_tnc_add_nm(c, &dent_key, lnum, dent_offs, dlen, nm); ++ if (err) ++ goto out_ro; ++ ++ /* ++ * Note, we do not remove the inode from TNC even if the last reference ++ * to it has just been deleted, because the inode may still be opened. ++ * Instead, the inode has been added to orphan lists and the orphan ++ * subsystem will take further care about it. ++ */ ++ ino_key_init(c, &ino_key, inode->i_ino); ++ ino_offs = dent_offs + aligned_dlen; ++ err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs, ilen); ++ if (err) ++ goto out_ro; ++ ++ ino_key_init(c, &ino_key, dir->i_ino); ++ ino_offs += aligned_ilen; ++ err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs, UBIFS_INO_NODE_SZ); ++ if (err) ++ goto out_ro; ++ ++ finish_reservation(c); ++ return 0; ++ ++out_finish: ++ finish_reservation(c); ++out_free: ++ kfree(dent); ++ return err; ++ ++out_ro: ++ ubifs_ro_mode(c, err); ++ if (last_reference) ++ ubifs_delete_orphan(c, inode->i_ino); ++ finish_reservation(c); ++ return err; ++} ++ ++/** ++ * ubifs_jnl_write_data - write a data node to the journal. ++ * @c: UBIFS file-system description object ++ * @inode: inode the data node belongs to ++ * @key: node key ++ * @buf: buffer to write ++ * @len: data length (must not exceed %UBIFS_BLOCK_SIZE) ++ * ++ * This function writes a data node to the journal. Returns %0 if the data node ++ * was successfully written, and a negative error code in case of failure. ++ */ ++int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode, ++ const union ubifs_key *key, const void *buf, int len) ++{ ++ int err, lnum, offs, compr_type, out_len; ++ int dlen = UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR; ++ const struct ubifs_inode *ui = ubifs_inode(inode); ++ struct ubifs_data_node *data; ++ ++ dbg_jnl("ino %lu, blk %u, len %d, key %s", key_ino(c, key), ++ key_block(c, key), len, DBGKEY(key)); ++ ubifs_assert(len <= UBIFS_BLOCK_SIZE); ++ ++ data = kmalloc(dlen, GFP_NOFS); ++ if (!data) ++ return -ENOMEM; ++ ++ data->ch.node_type = UBIFS_DATA_NODE; ++ key_write(c, key, &data->key); ++ data->size = cpu_to_le32(len); ++ zero_data_node_unused(data); ++ ++ if (!(ui->flags && UBIFS_COMPR_FL)) ++ /* Compression is disabled for this inode */ ++ compr_type = UBIFS_COMPR_NONE; ++ else ++ compr_type = ui->compr_type; ++ ++ out_len = dlen - UBIFS_DATA_NODE_SZ; ++ ubifs_compress(buf, len, &data->data, &out_len, &compr_type); ++ ubifs_assert(out_len <= UBIFS_BLOCK_SIZE); ++ ++ dlen = UBIFS_DATA_NODE_SZ + out_len; ++ data->compr_type = cpu_to_le16(compr_type); ++ ++ /* Make reservation before allocating sequence numbers */ ++ err = make_reservation(c, DATAHD, dlen); ++ if (err) ++ goto out_free; ++ ++ err = write_node(c, DATAHD, data, dlen, &lnum, &offs); ++ if (!err) ++ ubifs_wbuf_add_ino_nolock(&c->jheads[DATAHD].wbuf, ++ key_ino(c, key)); ++ release_head(c, DATAHD); ++ if (err) ++ goto out_ro; ++ ++ err = ubifs_tnc_add(c, key, lnum, offs, dlen); ++ if (err) ++ goto out_ro; ++ ++ finish_reservation(c); ++ kfree(data); ++ return 0; ++ ++out_ro: ++ ubifs_ro_mode(c, err); ++ finish_reservation(c); ++out_free: ++ kfree(data); ++ return err; ++} ++ ++/** ++ * ubifs_jnl_write_inode - flush inode to the journal. ++ * @c: UBIFS file-system description object ++ * @inode: inode to flush ++ * @last_reference: inode has been deleted ++ * @sync: non-zero if the write-buffer has to be synchronized ++ * ++ * This function writes inode @inode to the journal (to the base head). Returns ++ * zero in case of success and a negative error code in case of failure. ++ */ ++int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode, ++ int last_reference, int sync) ++{ ++ int err, len, lnum, offs; ++ struct ubifs_ino_node *ino; ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ ++ dbg_jnl("ino %lu%s", inode->i_ino, ++ last_reference ? " (last reference)" : ""); ++ if (last_reference) ++ ubifs_assert(inode->i_nlink == 0); ++ ++ /* If the inode is deleted, do not write the attached data */ ++ len = UBIFS_INO_NODE_SZ; ++ if (!last_reference) ++ len += ui->data_len; ++ ino = kmalloc(len, GFP_NOFS); ++ if (!ino) ++ return -ENOMEM; ++ ++ /* Make reservation before allocating sequence numbers */ ++ err = make_reservation(c, BASEHD, len); ++ if (err) ++ goto out_free; ++ ++ pack_inode(c, ino, inode, 1, last_reference); ++ ++ err = write_head(c, BASEHD, ino, len, &lnum, &offs, sync); ++ if (!sync && !err) ++ ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, ++ inode->i_ino); ++ release_head(c, BASEHD); ++ if (err) ++ goto out_ro; ++ ++ if (last_reference) { ++ err = ubifs_tnc_remove_ino(c, inode->i_ino); ++ if (err) ++ goto out_ro; ++ ubifs_delete_orphan(c, inode->i_ino); ++ err = ubifs_add_dirt(c, lnum, len); ++ } else { ++ union ubifs_key key; ++ ++ ino_key_init(c, &key, inode->i_ino); ++ err = ubifs_tnc_add(c, &key, lnum, offs, len); ++ } ++ if (err) ++ goto out_ro; ++ ++ finish_reservation(c); ++ kfree(ino); ++ return 0; ++ ++out_ro: ++ ubifs_ro_mode(c, err); ++ finish_reservation(c); ++out_free: ++ kfree(ino); ++ return err; ++} ++ ++/** ++ * ubifs_jnl_rename - rename a directory entry. ++ * @c: UBIFS file-system description object ++ * @old_dir: parent inode of directory entry to rename ++ * @old_dentry: directory entry to rename ++ * @new_dir: parent inode of directory entry to rename ++ * @new_dentry: new directory entry (or directory entry to replace) ++ * @sync: non-zero if the write-buffer has to be synchronized ++ * ++ * Returns zero in case of success and a negative error code in case of failure. ++ */ ++int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, ++ const struct dentry *old_dentry, ++ const struct inode *new_dir, ++ const struct dentry *new_dentry, int sync) ++{ ++ const struct inode *old_inode = old_dentry->d_inode; ++ const struct inode *new_inode = new_dentry->d_inode; ++ int err, dlen1, dlen2, ilen, lnum, offs, len; ++ int aligned_dlen1, aligned_dlen2, plen = UBIFS_INO_NODE_SZ; ++ int last_reference = !!(new_inode && new_inode->i_nlink == 0); ++ struct ubifs_dent_node *dent, *dent2; ++ void *p; ++ union ubifs_key key; ++ ++ dbg_jnl("dent '%.*s' in dir ino %lu to dent '%.*s' in dir ino %lu", ++ old_dentry->d_name.len, old_dentry->d_name.name, ++ old_dir->i_ino, new_dentry->d_name.len, ++ new_dentry->d_name.name, new_dir->i_ino); ++ ++ ubifs_assert(ubifs_inode(old_dir)->data_len == 0); ++ ubifs_assert(ubifs_inode(new_dir)->data_len == 0); ++ ++ dlen1 = UBIFS_DENT_NODE_SZ + new_dentry->d_name.len + 1; ++ dlen2 = UBIFS_DENT_NODE_SZ + old_dentry->d_name.len + 1; ++ if (new_inode) { ++ ilen = UBIFS_INO_NODE_SZ; ++ if (!last_reference) ++ ilen += ubifs_inode(new_inode)->data_len; ++ } else ++ ilen = 0; ++ ++ aligned_dlen1 = ALIGN(dlen1, 8); ++ aligned_dlen2 = ALIGN(dlen2, 8); ++ ++ len = aligned_dlen1 + aligned_dlen2 + ALIGN(ilen, 8) + ALIGN(plen, 8); ++ if (old_dir != new_dir) ++ len += plen; ++ ++ dent = kmalloc(len, GFP_NOFS); ++ if (!dent) ++ return -ENOMEM; ++ ++ /* Make reservation before allocating sequence numbers */ ++ err = make_reservation(c, BASEHD, len); ++ if (err) ++ goto out_free; ++ ++ /* Make new dent */ ++ dent->ch.node_type = UBIFS_DENT_NODE; ++ dent_key_init_flash(c, &dent->key, new_dir->i_ino, &new_dentry->d_name); ++ dent->inum = cpu_to_le64(old_inode->i_ino); ++ dent->type = get_dent_type(old_inode->i_mode); ++ dent->nlen = cpu_to_le16(new_dentry->d_name.len); ++ memcpy(dent->name, new_dentry->d_name.name, new_dentry->d_name.len); ++ dent->name[new_dentry->d_name.len] = '\0'; ++ zero_dent_node_unused(dent); ++ ubifs_prep_grp_node(c, dent, dlen1, 0); ++ ++ dent2 = (void *)dent + aligned_dlen1; ++ ++ /* Make deletion dent */ ++ dent2->ch.node_type = UBIFS_DENT_NODE; ++ dent_key_init_flash(c, &dent2->key, old_dir->i_ino, ++ &old_dentry->d_name); ++ dent2->inum = 0; ++ dent2->type = DT_UNKNOWN; ++ dent2->nlen = cpu_to_le16(old_dentry->d_name.len); ++ memcpy(dent2->name, old_dentry->d_name.name, old_dentry->d_name.len); ++ dent2->name[old_dentry->d_name.len] = '\0'; ++ zero_dent_node_unused(dent2); ++ ubifs_prep_grp_node(c, dent2, dlen2, 0); ++ ++ p = (void *)dent2 + aligned_dlen2; ++ if (new_inode) { ++ pack_inode(c, p, new_inode, 0, last_reference); ++ p += ALIGN(ilen, 8); ++ } ++ ++ if (old_dir == new_dir) ++ pack_inode(c, p, old_dir, 1, 0); ++ else { ++ pack_inode(c, p, old_dir, 0, 0); ++ p += ALIGN(plen, 8); ++ pack_inode(c, p, new_dir, 1, 0); ++ } ++ ++ if (last_reference) { ++ err = ubifs_add_orphan(c, new_inode->i_ino); ++ if (err) { ++ release_head(c, BASEHD); ++ goto out_finish; ++ } ++ } ++ ++ err = write_head(c, BASEHD, dent, len, &lnum, &offs, sync); ++ if (!sync && !err) { ++ struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf; ++ ++ ubifs_wbuf_add_ino_nolock(wbuf, new_dir->i_ino); ++ ubifs_wbuf_add_ino_nolock(wbuf, old_dir->i_ino); ++ if (new_inode) ++ ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, ++ new_inode->i_ino); ++ } ++ release_head(c, BASEHD); ++ if (err) ++ goto out_ro; ++ ++ dent_key_init(c, &key, new_dir->i_ino, &new_dentry->d_name); ++ err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, &new_dentry->d_name); ++ if (err) ++ goto out_ro; ++ ++ err = ubifs_add_dirt(c, lnum, dlen2); ++ if (err) ++ goto out_ro; ++ ++ dent_key_init(c, &key, old_dir->i_ino, &old_dentry->d_name); ++ err = ubifs_tnc_remove_nm(c, &key, &old_dentry->d_name); ++ if (err) ++ goto out_ro; ++ ++ offs += aligned_dlen1 + aligned_dlen2; ++ if (new_inode) { ++ ino_key_init(c, &key, new_inode->i_ino); ++ err = ubifs_tnc_add(c, &key, lnum, offs, ilen); ++ if (err) ++ goto out_ro; ++ offs += ALIGN(ilen, 8); ++ } ++ ++ ino_key_init(c, &key, old_dir->i_ino); ++ err = ubifs_tnc_add(c, &key, lnum, offs, plen); ++ if (err) ++ goto out_ro; ++ ++ if (old_dir != new_dir) { ++ offs += ALIGN(plen, 8); ++ ino_key_init(c, &key, new_dir->i_ino); ++ err = ubifs_tnc_add(c, &key, lnum, offs, plen); ++ if (err) ++ goto out_ro; ++ } ++ ++ finish_reservation(c); ++ kfree(dent); ++ return 0; ++ ++out_ro: ++ ubifs_ro_mode(c, err); ++ if (last_reference) ++ ubifs_delete_orphan(c, new_inode->i_ino); ++out_finish: ++ finish_reservation(c); ++out_free: ++ kfree(dent); ++ return err; ++} ++ ++/** ++ * recomp_data_node - re-compress a truncated data node. ++ * @dn: data node to re-compress ++ * @new_len: new length ++ * ++ * This function is used when an inode is truncated and the last data node of ++ * the inode has to be re-compressed and re-written. ++ */ ++static int recomp_data_node(struct ubifs_data_node *dn, int *new_len) ++{ ++ void *buf; ++ int err, len, compr_type, out_len; ++ ++ out_len = le32_to_cpu(dn->size); ++ buf = kmalloc(out_len * WORST_COMPR_FACTOR, GFP_NOFS); ++ if (!buf) ++ return -ENOMEM; ++ ++ len = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ; ++ compr_type = le16_to_cpu(dn->compr_type); ++ err = ubifs_decompress(&dn->data, len, buf, &out_len, compr_type); ++ if (err) ++ goto out; ++ ++ ubifs_compress(buf, *new_len, &dn->data, &out_len, &compr_type); ++ ubifs_assert(out_len <= UBIFS_BLOCK_SIZE); ++ dn->compr_type = cpu_to_le16(compr_type); ++ dn->size = cpu_to_le32(*new_len); ++ *new_len = UBIFS_DATA_NODE_SZ + out_len; ++out: ++ kfree(buf); ++ return err; ++} ++ ++/** ++ * ubifs_jnl_truncate - update the journal for a truncation. ++ * @c: UBIFS file-system description object ++ * @inum: inode number of inode being truncated ++ * @old_size: old size ++ * @new_size: new size ++ * ++ * When the size of a file decreases due to truncation, a truncation node is ++ * written, the journal tree is updated, and the last data block is re-written ++ * if it has been affected. ++ * ++ * This function returns %0 in the case of success, and a negative error code in ++ * case of failure. ++ */ ++int ubifs_jnl_truncate(struct ubifs_info *c, ino_t inum, ++ loff_t old_size, loff_t new_size) ++{ ++ union ubifs_key key, to_key; ++ struct ubifs_trun_node *trun; ++ struct ubifs_data_node *uninitialized_var(dn); ++ int err, dlen, len, lnum, offs, bit, sz; ++ unsigned int blk; ++ ++ dbg_jnl("ino %lu, size %lld -> %lld", inum, old_size, new_size); ++ ++ sz = UBIFS_TRUN_NODE_SZ + UBIFS_MAX_DATA_NODE_SZ * WORST_COMPR_FACTOR; ++ trun = kmalloc(sz, GFP_NOFS); ++ if (!trun) ++ return -ENOMEM; ++ ++ trun->ch.node_type = UBIFS_TRUN_NODE; ++ trun_key_init_flash(c, &trun->key, inum); ++ trun->old_size = cpu_to_le64(old_size); ++ trun->new_size = cpu_to_le64(new_size); ++ ++ dlen = new_size & (UBIFS_BLOCK_SIZE - 1); ++ ++ if (dlen) { ++ /* Get last data block so it can be truncated */ ++ dn = (void *)trun + ALIGN(UBIFS_TRUN_NODE_SZ, 8); ++ blk = new_size / UBIFS_BLOCK_SIZE; ++ data_key_init(c, &key, inum, blk); ++ dbg_jnl("last block key %s", DBGKEY(&key)); ++ err = ubifs_tnc_lookup(c, &key, dn); ++ if (err == -ENOENT) ++ dlen = 0; /* Not found (so it is a hole) */ ++ else if (err) ++ goto out_free; ++ else { ++ if (le32_to_cpu(dn->size) <= dlen) ++ dlen = 0; /* Nothing to do */ ++ else { ++ int compr_type = le16_to_cpu(dn->compr_type); ++ ++ if (compr_type != UBIFS_COMPR_NONE) { ++ err = recomp_data_node(dn, &dlen); ++ if (err) ++ goto out_free; ++ } else { ++ dn->size = cpu_to_le32(dlen); ++ dlen += UBIFS_DATA_NODE_SZ; ++ } ++ zero_data_node_unused(dn); ++ } ++ } ++ } ++ ++ if (dlen) ++ len = ALIGN(UBIFS_TRUN_NODE_SZ, 8) + dlen; ++ else ++ len = UBIFS_TRUN_NODE_SZ; ++ ++ /* Must make reservation before allocating sequence numbers */ ++ err = make_reservation(c, BASEHD, len); ++ if (err) ++ goto out_free; ++ ++ ubifs_prepare_node(c, trun, UBIFS_TRUN_NODE_SZ, 0); ++ if (dlen) ++ ubifs_prepare_node(c, dn, dlen, 0); ++ ++ err = write_head(c, BASEHD, trun, len, &lnum, &offs, 0); ++ if (!err) ++ ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, inum); ++ release_head(c, BASEHD); ++ if (err) ++ goto out_ro; ++ ++ if (dlen) { ++ offs += ALIGN(UBIFS_TRUN_NODE_SZ, 8); ++ err = ubifs_tnc_add(c, &key, lnum, offs, dlen); ++ if (err) ++ goto out_ro; ++ } ++ ++ err = ubifs_add_dirt(c, lnum, UBIFS_TRUN_NODE_SZ); ++ if (err) ++ goto out_ro; ++ ++ bit = new_size & (UBIFS_BLOCK_SIZE - 1); ++ ++ blk = new_size / UBIFS_BLOCK_SIZE + (bit ? 1 : 0); ++ data_key_init(c, &key, inum, blk); ++ ++ bit = old_size & (UBIFS_BLOCK_SIZE - 1); ++ ++ blk = old_size / UBIFS_BLOCK_SIZE - (bit ? 0: 1); ++ data_key_init(c, &to_key, inum, blk); ++ ++ err = ubifs_tnc_remove_range(c, &key, &to_key); ++ if (err) ++ goto out_ro; ++ ++ finish_reservation(c); ++ kfree(trun); ++ return 0; ++ ++out_ro: ++ ubifs_ro_mode(c, err); ++ finish_reservation(c); ++out_free: ++ kfree(trun); ++ return err; ++} ++ ++#ifdef CONFIG_UBIFS_FS_XATTR ++ ++int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host, ++ const struct inode *inode, const struct qstr *nm, ++ int sync) ++{ ++ int err, xlen, hlen, len, lnum, xent_offs, aligned_xlen; ++ struct ubifs_dent_node *xent; ++ struct ubifs_ino_node *ino; ++ union ubifs_key xent_key, key1, key2; ++ ++ dbg_jnl("host %lu, xattr ino %lu, name '%s', data len %d", ++ host->i_ino, inode->i_ino, nm->name, ++ ubifs_inode(inode)->data_len); ++ ubifs_assert(inode->i_nlink == 0); ++ ++ /* ++ * Since we are deleting the inode, we do not bother to attach any data ++ * to it and assume its length is %UBIFS_INO_NODE_SZ. ++ */ ++ xlen = UBIFS_DENT_NODE_SZ + nm->len + 1; ++ aligned_xlen = ALIGN(xlen, 8); ++ hlen = ubifs_inode(host)->data_len + UBIFS_INO_NODE_SZ; ++ len = aligned_xlen + UBIFS_INO_NODE_SZ + ALIGN(hlen, 8); ++ ++ xent = kmalloc(len, GFP_NOFS); ++ if (!xent) ++ return -ENOMEM; ++ ++ /* Make reservation before allocating sequence numbers */ ++ err = make_reservation(c, BASEHD, len); ++ if (err) { ++ kfree(xent); ++ return err; ++ } ++ ++ xent->ch.node_type = UBIFS_XENT_NODE; ++ xent_key_init(c, &xent_key, host->i_ino, nm); ++ key_write(c, &xent_key, xent->key); ++ xent->inum = 0; ++ xent->type = get_dent_type(inode->i_mode); ++ xent->nlen = cpu_to_le16(nm->len); ++ memcpy(xent->name, nm->name, nm->len); ++ xent->name[nm->len] = '\0'; ++ zero_dent_node_unused(xent); ++ ubifs_prep_grp_node(c, xent, xlen, 0); ++ ++ ino = (void *)xent + aligned_xlen; ++ pack_inode(c, ino, inode, 0, 1); ++ ++ ino = (void *)ino + UBIFS_INO_NODE_SZ; ++ pack_inode(c, ino, host, 1, 0); ++ ++ err = write_head(c, BASEHD, xent, len, &lnum, &xent_offs, sync); ++ if (!sync && !err) ++ ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, host->i_ino); ++ release_head(c, BASEHD); ++ kfree(xent); ++ if (err) ++ goto out_ro; ++ ++ /* Remove the extended attribute entry from TNC */ ++ err = ubifs_tnc_remove_nm(c, &xent_key, nm); ++ if (err) ++ goto out_ro; ++ err = ubifs_add_dirt(c, lnum, xlen); ++ if (err) ++ goto out_ro; ++ ++ /* ++ * Remove all nodes belonging to the extended attribute inode from TNC. ++ * Well, there actually must be only one node - the inode itself. ++ */ ++ lowest_ino_key(c, &key1, inode->i_ino); ++ highest_ino_key(c, &key2, inode->i_ino); ++ err = ubifs_tnc_remove_range(c, &key1, &key2); ++ if (err) ++ goto out_ro; ++ err = ubifs_add_dirt(c, lnum, UBIFS_INO_NODE_SZ); ++ if (err) ++ goto out_ro; ++ ++ /* And update TNC with the new host inode position */ ++ ino_key_init(c, &key1, host->i_ino); ++ err = ubifs_tnc_add(c, &key1, lnum, xent_offs + len - hlen, hlen); ++ if (err) ++ goto out_ro; ++ ++ finish_reservation(c); ++ return 0; ++ ++out_ro: ++ ubifs_ro_mode(c, err); ++ finish_reservation(c); ++ return err; ++} ++ ++/** ++ * ubifs_jnl_write_2_inodes - write 2 inodes to the journal. ++ * @c: UBIFS file-system description object ++ * @inode1: first inode to write ++ * @inode2: second inode to write ++ * @sync: non-zero if the write-buffer has to be synchronized ++ * ++ * This function writes 2 inodes @inode1 and @inode2 to the journal (to the ++ * base head - first @inode1, then @inode2). Returns zero in case of success ++ * and a negative error code in case of failure. ++ */ ++int ubifs_jnl_write_2_inodes(struct ubifs_info *c, const struct inode *inode1, ++ const struct inode *inode2, int sync) ++{ ++ int err, len1, len2, aligned_len, aligned_len1, lnum, offs; ++ struct ubifs_ino_node *ino; ++ union ubifs_key key; ++ ++ dbg_jnl("ino %lu, ino %lu", inode1->i_ino, inode2->i_ino); ++ ubifs_assert(inode1->i_nlink > 0); ++ ubifs_assert(inode2->i_nlink > 0); ++ ++ len1 = UBIFS_INO_NODE_SZ + ubifs_inode(inode1)->data_len; ++ len2 = UBIFS_INO_NODE_SZ + ubifs_inode(inode2)->data_len; ++ aligned_len1 = ALIGN(len1, 8); ++ aligned_len = aligned_len1 + ALIGN(len2, 8); ++ ++ ino = kmalloc(aligned_len, GFP_NOFS); ++ if (!ino) ++ return -ENOMEM; ++ ++ /* Make reservation before allocating sequence numbers */ ++ err = make_reservation(c, BASEHD, aligned_len); ++ if (err) ++ goto out_free; ++ ++ pack_inode(c, ino, inode1, 0, 0); ++ pack_inode(c, (void *)ino + aligned_len1, inode2, 1, 0); ++ ++ err = write_head(c, BASEHD, ino, aligned_len, &lnum, &offs, 0); ++ if (!sync && !err) { ++ struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf; ++ ++ ubifs_wbuf_add_ino_nolock(wbuf, inode1->i_ino); ++ ubifs_wbuf_add_ino_nolock(wbuf, inode2->i_ino); ++ } ++ release_head(c, BASEHD); ++ if (err) ++ goto out_ro; ++ ++ ino_key_init(c, &key, inode1->i_ino); ++ err = ubifs_tnc_add(c, &key, lnum, offs, len1); ++ if (err) ++ goto out_ro; ++ ++ ino_key_init(c, &key, inode2->i_ino); ++ err = ubifs_tnc_add(c, &key, lnum, offs + aligned_len1, len2); ++ if (err) ++ goto out_ro; ++ ++ finish_reservation(c); ++ kfree(ino); ++ return 0; ++ ++out_ro: ++ ubifs_ro_mode(c, err); ++ finish_reservation(c); ++out_free: ++ kfree(ino); ++ return err; ++} ++ ++#endif /* CONFIG_UBIFS_FS_XATTR */ +diff -urN linux-2.6.24.7.old/fs/ubifs/key.h linux-2.6.24.7/fs/ubifs/key.h +--- linux-2.6.24.7.old/fs/ubifs/key.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/key.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,566 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This header contains various key-related definitions and helper function. ++ * UBIFS allows several key schemes, so we access key fields only via these ++ * helpers. At the moment only one key scheme is supported. ++ * ++ * Simple key scheme ++ * ~~~~~~~~~~~~~~~~~ ++ * ++ * Keys are 64-bits long. First 32-bits are inode number (parent inode number ++ * in case of direntry key). Next 3 bits are node type. The last 29 bits are ++ * 4KiB offset in case of inode node, and direntry hash in case of a direntry ++ * node. We use "r5" hash borrowed from reiserfs. ++ */ ++ ++#ifndef __UBIFS_KEY_H__ ++#define __UBIFS_KEY_H__ ++ ++/** ++ * key_r5_hash - R5 hash function (borrowed from reiserfs). ++ * @s: direntry name ++ * @len: name length ++ */ ++static inline uint32_t key_r5_hash(const char *s, int len) ++{ ++ uint32_t a = 0; ++ const signed char *str = (const signed char *)s; ++ ++ while (*str) { ++ a += *str << 4; ++ a += *str >> 4; ++ a *= 11; ++ str++; ++ } ++ ++ /* TODO: change this to ++ * a &= UBIFS_S_KEY_HASH_MASK; */ ++ a &= 0x1FFFFFFF; ++ ++ /* ++ * We use hash values as offset in directories, so values %0 and %1 are ++ * reserved for "." and "..". %2 is reserved for "end of readdir" ++ * marker. ++ */ ++ if (unlikely(a >= 0 && a <= 2)) ++ a += 3; ++ return a; ++} ++ ++/** ++ * tmp_key_r5_hash - R5 hash function (borrowed from reiserfs). ++ * @s: direntry name ++ * @len: name length ++ * TODO: this should die soon ++ */ ++static inline uint32_t tmp_key_r5_hash(const char *s, int len) ++{ ++ uint32_t a = 0; ++ const signed char *str = (const signed char *)s; ++ ++ while (*str) { ++ a += *str << 4; ++ a += *str >> 4; ++ a *= 11; ++ str++; ++ } ++ ++ a &= 0x01FFFFFF; ++ ++ /* ++ * We use hash values as offset in directories, so values %0 and %1 are ++ * reserved for "." and "..". %2 is reserved for possible future use. ++ */ ++ if (unlikely(a >= 0 && a <= 2)) ++ a += 3; ++ return a; ++} ++ ++/** ++ * key_test_hash - testing hash function. ++ * @str: direntry name ++ * @len: name length ++ */ ++static inline uint32_t key_test_hash(const char *str, int len) ++{ ++ uint32_t a = 0; ++ ++ len = min_t(uint32_t, len, 4); ++ memcpy(&a, str, len); ++ /* TODO: change this to ++ * a &= UBIFS_S_KEY_HASH_MASK; */ ++ a &= 0x1FFFFFFF; ++ if (unlikely(a >= 0 && a <= 2)) ++ a += 3; ++ return a; ++} ++ ++/** ++ * ino_key_init - initialize inode key. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: inode number ++ */ ++static inline void ino_key_init(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS; ++} ++ ++/** ++ * ino_key_init_flash - initialize on-flash inode key. ++ * @c: UBIFS file-system description object ++ * @k: key to initialize ++ * @inum: inode number ++ */ ++static inline void ino_key_init_flash(const struct ubifs_info *c, void *k, ++ ino_t inum) ++{ ++ union ubifs_key *key = k; ++ ++ key->j32[0] = cpu_to_le32(inum); ++ key->j32[1] = cpu_to_le32(UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS); ++ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8); ++} ++ ++/** ++ * lowest_ino_key - get the lowest possible inode key. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: inode number ++ */ ++static inline void lowest_ino_key(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = 0; ++} ++ ++/** ++ * highest_ino_key - get the highest possible inode key. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: inode number ++ */ ++static inline void highest_ino_key(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = 0xffffffff; ++} ++ ++/** ++ * dent_key_init - initialize directory entry key. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: parent inode number ++ * @nm: direntry name and length ++ */ ++static inline void dent_key_init(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum, ++ const struct qstr *nm) ++{ ++ uint32_t hash = c->key_hash(nm->name, nm->len); ++ ++ ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK)); ++ key->u32[0] = inum; ++ key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS); ++} ++ ++/** ++ * dent_key_init_hash - initialize directory entry key without re-calculating ++ * hash function. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: parent inode number ++ * @hash: direntry name hash ++ */ ++static inline void dent_key_init_hash(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum, ++ uint32_t hash) ++{ ++ ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK)); ++ key->u32[0] = inum; ++ key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS); ++} ++ ++/** ++ * dent_key_init_flash - initialize on-flash directory entry key. ++ * @c: UBIFS file-system description object ++ * @k: key to initialize ++ * @inum: parent inode number ++ * @nm: direntry name and length ++ */ ++static inline void dent_key_init_flash(const struct ubifs_info *c, void *k, ++ ino_t inum, const struct qstr *nm) ++{ ++ union ubifs_key *key = k; ++ uint32_t hash = c->key_hash(nm->name, nm->len); ++ ++ ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK)); ++ key->j32[0] = cpu_to_le32(inum); ++ key->j32[1] = cpu_to_le32(hash | ++ (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS)); ++ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8); ++} ++ ++/** ++ * lowest_dent_key - get the lowest possible directory entry key. ++ * @c: UBIFS file-system description object ++ * @key: where to store the lowest key ++ * @inum: parent inode number ++ */ ++static inline void lowest_dent_key(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS; ++} ++ ++/** ++ * xent_key_init - initialize extended attribute entry key. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: host inode number ++ * @nm: extended attribute entry name and length ++ */ ++static inline void xent_key_init(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum, ++ const struct qstr *nm) ++{ ++ uint32_t hash = c->key_hash(nm->name, nm->len); ++ ++ ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK)); ++ key->u32[0] = inum; ++ key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS); ++} ++ ++/** ++ * xent_key_init_hash - initialize extended attribute entry key without ++ * re-calculating hash function. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: host inode number ++ * @hash: extended attribute entry name hash ++ */ ++static inline void xent_key_init_hash(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum, ++ uint32_t hash) ++{ ++ ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK)); ++ key->u32[0] = inum; ++ key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS); ++} ++ ++/** ++ * xent_key_init_flash - initialize on-flash extended attribute entry key. ++ * @c: UBIFS file-system description object ++ * @k: key to initialize ++ * @inum: host inode number ++ * @nm: extended attribute entry name and length ++ */ ++static inline void xent_key_init_flash(const struct ubifs_info *c, void *k, ++ ino_t inum, const struct qstr *nm) ++{ ++ union ubifs_key *key = k; ++ uint32_t hash = c->key_hash(nm->name, nm->len); ++ ++ ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK)); ++ key->j32[0] = cpu_to_le32(inum); ++ key->j32[1] = cpu_to_le32(hash | ++ (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS)); ++ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8); ++} ++ ++/** ++ * lowest_xent_key - get the lowest possible extended attribute entry key. ++ * @c: UBIFS file-system description object ++ * @key: where to store the lowest key ++ * @inum: host inode number ++ */ ++static inline void lowest_xent_key(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS; ++} ++ ++/** ++ * data_key_init - initialize data key. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: inode number ++ * @block: block number ++ */ ++static inline void data_key_init(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum, ++ unsigned int block) ++{ ++ ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK)); ++ key->u32[0] = inum; ++ key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS); ++} ++ ++/** ++ * data_key_init_flash - initialize on-flash data key. ++ * @c: UBIFS file-system description object ++ * @k: key to initialize ++ * @inum: inode number ++ * @block: block number ++ */ ++static inline void data_key_init_flash(const struct ubifs_info *c, void *k, ++ ino_t inum, unsigned int block) ++{ ++ union ubifs_key *key = k; ++ ++ ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK)); ++ key->j32[0] = cpu_to_le32(inum); ++ key->j32[1] = cpu_to_le32(block | ++ (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS)); ++ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8); ++} ++ ++/** ++ * trun_key_init - initialize truncation node key. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: inode number ++ */ ++static inline void trun_key_init(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = UBIFS_TRUN_KEY << UBIFS_S_KEY_BLOCK_BITS; ++} ++ ++/** ++ * trun_key_init_flash - initialize on-flash truncation node key. ++ * @c: UBIFS file-system description object ++ * @k: key to initialize ++ * @inum: inode number ++ */ ++static inline void trun_key_init_flash(const struct ubifs_info *c, void *k, ++ ino_t inum) ++{ ++ union ubifs_key *key = k; ++ ++ key->j32[0] = cpu_to_le32(inum); ++ key->j32[1] = cpu_to_le32(UBIFS_TRUN_KEY << UBIFS_S_KEY_BLOCK_BITS); ++ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8); ++} ++ ++/** ++ * key_type - get key type. ++ * @c: UBIFS file-system description object ++ * @key: key to get type of ++ */ ++static inline int key_type(const struct ubifs_info *c, ++ const union ubifs_key *key) ++{ ++ return key->u32[1] >> UBIFS_S_KEY_BLOCK_BITS; ++} ++ ++/** ++ * key_type_flash - get type of a on-flash formatted key. ++ * @c: UBIFS file-system description object ++ * @k: key to get type of ++ */ ++static inline int key_type_flash(const struct ubifs_info *c, const void *k) ++{ ++ const union ubifs_key *key = k; ++ ++ return le32_to_cpu(key->u32[1]) >> UBIFS_S_KEY_BLOCK_BITS; ++} ++ ++/** ++ * key_ino - fetch inode number from key. ++ * @c: UBIFS file-system description object ++ * @k: key to fetch inode number from ++ */ ++static inline ino_t key_ino(const struct ubifs_info *c, const void *k) ++{ ++ const union ubifs_key *key = k; ++ ++ return key->u32[0]; ++} ++ ++/** ++ * key_ino_flash - fetch inode number from an on-flash formatted key. ++ * @c: UBIFS file-system description object ++ * @k: key to fetch inode number from ++ */ ++static inline ino_t key_ino_flash(const struct ubifs_info *c, const void *k) ++{ ++ const union ubifs_key *key = k; ++ ++ return le32_to_cpu(key->j32[0]); ++} ++ ++/** ++ * key_hash - get directory entry hash. ++ * @c: UBIFS file-system description object ++ * @key: the key to get hash from ++ */ ++static inline int key_hash(const struct ubifs_info *c, ++ const union ubifs_key *key) ++{ ++ return key->u32[1] & UBIFS_S_KEY_HASH_MASK; ++} ++ ++/** ++ * key_hash_flash - get directory entry hash from an on-flash formatted key. ++ * @c: UBIFS file-system description object ++ * @k: the key to get hash from ++ */ ++static inline int key_hash_flash(const struct ubifs_info *c, const void *k) ++{ ++ const union ubifs_key *key = k; ++ ++ return le32_to_cpu(key->j32[1]) & UBIFS_S_KEY_HASH_MASK; ++} ++ ++/** ++ * key_block - get data block number. ++ * @c: UBIFS file-system description object ++ * @key: the key to get the block number from ++ */ ++static inline unsigned int key_block(const struct ubifs_info *c, ++ const union ubifs_key *key) ++{ ++ return key->u32[1] & UBIFS_S_KEY_BLOCK_MASK; ++} ++ ++/** ++ * key_read - transform a key to in-memory format. ++ * @c: UBIFS file-system description object ++ * @from: the key to transform ++ * @to: the key to store the result ++ */ ++static inline void key_read(const struct ubifs_info *c, const void *from, ++ union ubifs_key *to) ++{ ++ const union ubifs_key *f = from; ++ ++ to->u32[0] = le32_to_cpu(f->j32[0]); ++ to->u32[1] = le32_to_cpu(f->j32[1]); ++} ++ ++/** ++ * key_write - transform a key from in-memory format. ++ * @c: UBIFS file-system description object ++ * @from: the key to transform ++ * @to: the key to store the result ++ */ ++static inline void key_write(const struct ubifs_info *c, ++ const union ubifs_key *from, void *to) ++{ ++ union ubifs_key *t = to; ++ ++ t->j32[0] = cpu_to_le32(from->u32[0]); ++ t->j32[1] = cpu_to_le32(from->u32[1]); ++ memset(to + 8, 0, UBIFS_MAX_KEY_LEN - 8); ++} ++ ++/** ++ * key_write_idx - transform a key from in-memory format for the index. ++ * @c: UBIFS file-system description object ++ * @from: the key to transform ++ * @to: the key to store the result ++ */ ++static inline void key_write_idx(const struct ubifs_info *c, ++ const union ubifs_key *from, void *to) ++{ ++ union ubifs_key *t = to; ++ ++ t->j32[0] = cpu_to_le32(from->u32[0]); ++ t->j32[1] = cpu_to_le32(from->u32[1]); ++} ++ ++/** ++ * key_copy - copy a key. ++ * @c: UBIFS file-system description object ++ * @from: the key to copy from ++ * @to: the key to copy to ++ */ ++static inline void key_copy(const struct ubifs_info *c, ++ const union ubifs_key *from, union ubifs_key *to) ++{ ++ to->u64[0] = from->u64[0]; ++} ++ ++/** ++ * keys_cmp - compare keys. ++ * @c: UBIFS file-system description object ++ * @key1: the first key to compare ++ * @key2: the second key to compare ++ * ++ * This function compares 2 keys and returns %-1 if @key1 is less than ++ * @key2, 0 if the keys are equivalent and %1 if @key1 is greater than @key2. ++ */ ++static inline int keys_cmp(const struct ubifs_info *c, ++ const union ubifs_key *key1, ++ const union ubifs_key *key2) ++{ ++ if (key1->u32[0] < key2->u32[0]) ++ return -1; ++ if (key1->u32[0] > key2->u32[0]) ++ return 1; ++ if (key1->u32[1] < key2->u32[1]) ++ return -1; ++ if (key1->u32[1] > key2->u32[1]) ++ return 1; ++ ++ return 0; ++} ++ ++/** ++ * is_hash_key - is a key vulnerable to hash collisions. ++ * @c: UBIFS file-system description object ++ * @key: key ++ * ++ * This function returns %1 if @key is a hashed key or %0 otherwise. ++ */ ++static inline int is_hash_key(const struct ubifs_info *c, ++ const union ubifs_key *key) ++{ ++ int type = key_type(c, key); ++ ++ return type == UBIFS_DENT_KEY || type == UBIFS_XENT_KEY; ++} ++ ++/** ++ * key_max_inode_size - get maximum file size allowed by current key format. ++ * @c: UBIFS file-system description object ++ */ ++static inline unsigned long long key_max_inode_size(const struct ubifs_info *c) ++{ ++ switch (c->key_fmt) { ++ case UBIFS_SIMPLE_KEY_FMT: ++ return (1ULL << UBIFS_S_KEY_BLOCK_BITS) * UBIFS_BLOCK_SIZE; ++ default: ++ return 0; ++ } ++} ++#endif /* !__UBIFS_KEY_H__ */ +diff -urN linux-2.6.24.7.old/fs/ubifs/log.c linux-2.6.24.7/fs/ubifs/log.c +--- linux-2.6.24.7.old/fs/ubifs/log.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/log.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,803 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This file is a part of UBIFS journal implementation and contains various ++ * functions which manipulate the log. The log is a fixed area on the flash ++ * which does not contain any data but refers to buds. The log is a part of the ++ * journal. ++ */ ++ ++#include "ubifs.h" ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++static int dbg_check_bud_bytes(struct ubifs_info *c); ++#else ++#define dbg_check_bud_bytes(c) 0 ++#endif ++ ++/** ++ * ubifs_search_bud - search bud LEB. ++ * @c: UBIFS file-system description object ++ * @lnum: logical eraseblock number to search ++ * ++ * This function searches bud LEB @lnum. Returns bud description object in case ++ * of success and %NULL if there is no bud with this LEB number. ++ */ ++struct ubifs_bud *ubifs_search_bud(struct ubifs_info *c, int lnum) ++{ ++ struct rb_node *p; ++ struct ubifs_bud *bud; ++ ++ spin_lock(&c->buds_lock); ++ p = c->buds.rb_node; ++ while (p) { ++ bud = rb_entry(p, struct ubifs_bud, rb); ++ if (lnum < bud->lnum) ++ p = p->rb_left; ++ else if (lnum > bud->lnum) ++ p = p->rb_right; ++ else { ++ spin_unlock(&c->buds_lock); ++ return bud; ++ } ++ } ++ spin_unlock(&c->buds_lock); ++ return NULL; ++} ++ ++/** ++ * ubifs_get_wbuf - get the wbuf associated with a LEB, if there is one. ++ * @c: UBIFS file-system description object ++ * @lnum: logical eraseblock number to search ++ * ++ * This functions returns the wbuf for @lnum or %NULL if there is not one. ++ */ ++struct ubifs_wbuf *ubifs_get_wbuf(struct ubifs_info *c, int lnum) ++{ ++ struct rb_node *p; ++ struct ubifs_bud *bud; ++ int jhead; ++ ++ if (!c->jheads) ++ return NULL; ++ ++ spin_lock(&c->buds_lock); ++ p = c->buds.rb_node; ++ while (p) { ++ bud = rb_entry(p, struct ubifs_bud, rb); ++ if (lnum < bud->lnum) ++ p = p->rb_left; ++ else if (lnum > bud->lnum) ++ p = p->rb_right; ++ else { ++ jhead = bud->jhead; ++ spin_unlock(&c->buds_lock); ++ return &c->jheads[jhead].wbuf; ++ } ++ } ++ spin_unlock(&c->buds_lock); ++ return NULL; ++} ++ ++/** ++ * next_log_lnum - switch to the next log LEB. ++ * @c: UBIFS file-system description object ++ * @lnum: current log LEB ++ */ ++static inline int next_log_lnum(const struct ubifs_info *c, int lnum) ++{ ++ lnum += 1; ++ if (lnum > c->log_last) ++ lnum = UBIFS_LOG_LNUM; ++ ++ return lnum; ++} ++ ++/** ++ * empty_log_bytes - calculate amount of empty space in the log. ++ * @c: UBIFS file-system description object ++ */ ++static inline long long empty_log_bytes(const struct ubifs_info *c) ++{ ++ long long h, t; ++ ++ h = c->lhead_lnum * c->leb_size + c->lhead_offs; ++ t = c->ltail_lnum * c->leb_size; ++ ++ if (h >= t) ++ return c->log_bytes - h + t; ++ else ++ return t - h; ++} ++ ++/** ++ * ubifs_add_bud - add bud LEB to the tree of buds and its journal head list. ++ * @c: UBIFS file-system description object ++ * @bud: the bud to add ++ */ ++void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud) ++{ ++ struct rb_node **p, *parent = NULL; ++ struct ubifs_bud *b; ++ struct ubifs_jhead *jhead; ++ ++ spin_lock(&c->buds_lock); ++ p = &c->buds.rb_node; ++ while (*p) { ++ parent = *p; ++ b = rb_entry(parent, struct ubifs_bud, rb); ++ ubifs_assert(bud->lnum != b->lnum); ++ if (bud->lnum < b->lnum) ++ p = &(*p)->rb_left; ++ else ++ p = &(*p)->rb_right; ++ } ++ ++ rb_link_node(&bud->rb, parent, p); ++ rb_insert_color(&bud->rb, &c->buds); ++ if (c->jheads) { ++ jhead = &c->jheads[bud->jhead]; ++ list_add_tail(&bud->list, &jhead->buds_list); ++ } else ++ ubifs_assert(c->replaying && (c->vfs_sb->s_flags & MS_RDONLY)); ++ ++ /* ++ * Note, although this is a new bud, we anyway account this space now, ++ * before any data has been written to it, because this is about to ++ * guarantee fixed mount time, and this bud will anyway be read and ++ * scanned. ++ */ ++ c->bud_bytes += c->leb_size - bud->start; ++ ++ dbg_log("LEB %d:%d, jhead %d, bud_bytes %lld", bud->lnum, ++ bud->start, bud->jhead, c->bud_bytes); ++ spin_unlock(&c->buds_lock); ++} ++ ++/** ++ * ubifs_create_buds_lists - create journal head buds lists for remount rw. ++ * @c: UBIFS file-system description object ++ */ ++void ubifs_create_buds_lists(struct ubifs_info *c) ++{ ++ struct rb_node *p; ++ ++ spin_lock(&c->buds_lock); ++ p = rb_first(&c->buds); ++ while (p) { ++ struct ubifs_bud *bud = rb_entry(p, struct ubifs_bud, rb); ++ struct ubifs_jhead *jhead = &c->jheads[bud->jhead]; ++ ++ list_add_tail(&bud->list, &jhead->buds_list); ++ p = rb_next(p); ++ } ++ spin_unlock(&c->buds_lock); ++} ++ ++/** ++ * ubifs_add_bud_to_log - add a new bud to the log. ++ * @c: UBIFS file-system description object ++ * @jhead: journal head the bud belongs to ++ * @lnum: LEB number of the bud ++ * @offs: starting offset of the bud ++ * ++ * This function writes reference node for the new bud LEB @lnum it to the log, ++ * and adds it to the buds tress. It also makes sure that log size does not ++ * exceed the 'c->max_bud_bytes' limit. Returns zero in case of success, ++ * %-EAGAIN if commit is required, and a negative error codes in case of ++ * failure. ++ */ ++int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs) ++{ ++ int err; ++ struct ubifs_bud *bud; ++ struct ubifs_ref_node *ref; ++ ++ bud = kmalloc(sizeof(struct ubifs_bud), GFP_NOFS); ++ if (!bud) ++ return -ENOMEM; ++ ref = kzalloc(c->ref_node_alsz, GFP_NOFS); ++ if (!ref) { ++ kfree(bud); ++ return -ENOMEM; ++ } ++ ++ mutex_lock(&c->log_mutex); ++ /* Make sure we have enough space in the log */ ++ if (empty_log_bytes(c) - c->ref_node_alsz < c->min_log_bytes) { ++ dbg_log("not enough log space - %lld, required %d", ++ empty_log_bytes(c), c->min_log_bytes); ++ ubifs_commit_required(c); ++ err = -EAGAIN; ++ goto out_unlock; ++ } ++ ++ /* ++ * Make sure the the amount of space in buds will not exceed ++ * 'c->max_bud_bytes' limit, because we want to guarantee mount time ++ * limits. ++ * ++ * It is not necessary to hold @c->buds_lock when reading @c->bud_bytes ++ * because we are holding @c->log_mutex. All @c->bud_bytes take place ++ * when both @c->log_mutex and @c->bud_bytes are locked. ++ */ ++ if (c->bud_bytes + c->leb_size - offs > c->max_bud_bytes) { ++ dbg_log("bud bytes %lld (%lld max), require commit", ++ c->bud_bytes, c->max_bud_bytes); ++ ubifs_commit_required(c); ++ err = -EAGAIN; ++ goto out_unlock; ++ } ++ ++ /* ++ * If the journal is full enough - start background commit. Note, it is ++ * OK to read 'c->cmt_state' without spinlock because integer reads ++ * are atomic in the kernel. ++ */ ++ if (c->bud_bytes >= c->bg_bud_bytes && ++ c->cmt_state == COMMIT_RESTING) { ++ dbg_log("bud bytes %lld (%lld max), initiate BG commit", ++ c->bud_bytes, c->max_bud_bytes); ++ ubifs_request_bg_commit(c); ++ } ++ ++ bud->lnum = lnum; ++ bud->start = offs; ++ bud->jhead = jhead; ++ ++ ref->ch.node_type = UBIFS_REF_NODE; ++ ref->lnum = cpu_to_le32(bud->lnum); ++ ref->offs = cpu_to_le32(bud->start); ++ ref->jhead = cpu_to_le32(jhead); ++ ++ if (c->lhead_offs > c->leb_size - c->ref_node_alsz) { ++ c->lhead_lnum = next_log_lnum(c, c->lhead_lnum); ++ c->lhead_offs = 0; ++ } ++ ++ if (c->lhead_offs == 0) { ++ /* Must ensure next log LEB has been unmapped */ ++ err = ubifs_leb_unmap(c, c->lhead_lnum); ++ if (err) ++ goto out_unlock; ++ } ++ ++ if (bud->start == 0) { ++ /* ++ * Before writing the LEB reference which refers an empty LEB ++ * to the log, we have to make sure it is mapped, because ++ * otherwise we'd risk to refer an LEB with garbage in case of ++ * an unclean reboot, because the target LEB might have been ++ * unmapped, but not yet physically erased. ++ */ ++ err = ubi_leb_map(c->ubi, bud->lnum, UBI_SHORTTERM); ++ if (err) ++ goto out_unlock; ++ } ++ ++ dbg_log("write ref LEB %d:%d", ++ c->lhead_lnum, c->lhead_offs); ++ err = ubifs_write_node(c, ref, UBIFS_REF_NODE_SZ, c->lhead_lnum, ++ c->lhead_offs, UBI_SHORTTERM); ++ c->lhead_offs += c->ref_node_alsz; ++ if (err) ++ goto out_unlock; ++ ++ ubifs_add_bud(c, bud); ++ ++ mutex_unlock(&c->log_mutex); ++ kfree(ref); ++ return 0; ++ ++out_unlock: ++ mutex_unlock(&c->log_mutex); ++ kfree(ref); ++ kfree(bud); ++ return err; ++} ++ ++/** ++ * remove_buds - remove used buds. ++ * @c: UBIFS file-system description object ++ * ++ * This function removes use buds from the buds tree. It does not remove the ++ * buds which are pointed to by journal heads. ++ */ ++static void remove_buds(struct ubifs_info *c) ++{ ++ struct rb_node *p; ++ ++ ubifs_assert(list_empty(&c->old_buds)); ++ c->cmt_bud_bytes = 0; ++ spin_lock(&c->buds_lock); ++ p = rb_first(&c->buds); ++ while (p) { ++ struct rb_node *p1 = p; ++ struct ubifs_bud *bud; ++ struct ubifs_wbuf *wbuf; ++ ++ p = rb_next(p); ++ bud = rb_entry(p1, struct ubifs_bud, rb); ++ wbuf = &c->jheads[bud->jhead].wbuf; ++ ++ if (wbuf->lnum == bud->lnum) { ++ /* ++ * Do not remove buds which are pointed to by journal ++ * heads (non-closed buds). ++ */ ++ c->cmt_bud_bytes += wbuf->offs - bud->start; ++ dbg_log("preserve %d:%d, jhead %d, bud bytes %d, " ++ "cmt_bud_bytes %lld", bud->lnum, bud->start, ++ bud->jhead, wbuf->offs - bud->start, ++ c->cmt_bud_bytes); ++ bud->start = wbuf->offs; ++ } else { ++ c->cmt_bud_bytes += c->leb_size - bud->start; ++ dbg_log("remove %d:%d, jhead %d, bud bytes %d, " ++ "cmt_bud_bytes %lld", bud->lnum, bud->start, ++ bud->jhead, c->leb_size - bud->start, ++ c->cmt_bud_bytes); ++ rb_erase(p1, &c->buds); ++ list_del(&bud->list); ++ /* ++ * If the commit does not finish, the recovery will need ++ * to replay the journal, in which case the old buds ++ * must be unchanged. Do not release them until post ++ * commit i.e. do not allow them to be garbage ++ * collected. ++ */ ++ list_add(&bud->list, &c->old_buds); ++ } ++ } ++ spin_unlock(&c->buds_lock); ++} ++ ++/** ++ * ubifs_log_start_commit - start commit. ++ * @c: UBIFS file-system description object ++ * @ltail_lnum: return new log tail LEB number ++ * ++ * The commit operation starts with writing "commit start" node to the log and ++ * reference nodes for all journal heads which will define new journal after ++ * the commit has been finished. The commit start and reference nodes are ++ * written in one go to the nearest empty log LEB (hence, when commit is ++ * finished UBIFS may safely unmap all the previous log LEBs). This function ++ * returns zero in case of success and a negative error code in case of ++ * failure. ++ */ ++int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum) ++{ ++ void *buf; ++ struct ubifs_cs_node *cs; ++ struct ubifs_ref_node *ref; ++ int err, i, max_len, len; ++ ++ err = dbg_check_bud_bytes(c); ++ if (err) ++ return err; ++ ++ max_len = UBIFS_CS_NODE_SZ + c->jhead_cnt * UBIFS_REF_NODE_SZ; ++ max_len = ALIGN(max_len, c->min_io_size); ++ buf = cs = kmalloc(max_len, GFP_NOFS); ++ if (!buf) ++ return -ENOMEM; ++ ++ cs->ch.node_type = UBIFS_CS_NODE; ++ cs->cmt_no = cpu_to_le64(c->cmt_no + 1); ++ ubifs_prepare_node(c, cs, UBIFS_CS_NODE_SZ, 0); ++ ++ /* ++ * Note, we do not lock 'c->log_mutex' because this is the commit start ++ * phase and we are exclusively using the log. And we do not lock ++ * write-buffer because nobody can write to the file-system at this ++ * phase. ++ */ ++ ++ len = UBIFS_CS_NODE_SZ; ++ for (i = 0; i < c->jhead_cnt; i++) { ++ int lnum = c->jheads[i].wbuf.lnum; ++ int offs = c->jheads[i].wbuf.offs; ++ ++ if (lnum == -1 || offs == c->leb_size) ++ continue; ++ ++ dbg_log("add ref to LEB %d:%d for jhead %d", lnum, offs, i); ++ ref = buf + len; ++ ref->ch.node_type = UBIFS_REF_NODE; ++ ref->lnum = cpu_to_le32(lnum); ++ ref->offs = cpu_to_le32(offs); ++ ref->jhead = cpu_to_le32(i); ++ ++ ubifs_prepare_node(c, ref, UBIFS_REF_NODE_SZ, 0); ++ len += UBIFS_REF_NODE_SZ; ++ } ++ ++ ubifs_pad(c, buf + len, ALIGN(len, c->min_io_size) - len); ++ ++ /* Switch to the next log LEB */ ++ if (c->lhead_offs) { ++ c->lhead_lnum = next_log_lnum(c, c->lhead_lnum); ++ c->lhead_offs = 0; ++ } ++ ++ if (c->lhead_offs == 0) { ++ /* Must ensure next LEB has been unmapped */ ++ err = ubifs_leb_unmap(c, c->lhead_lnum); ++ if (err) ++ goto out; ++ } ++ ++ len = ALIGN(len, c->min_io_size); ++ dbg_log("writing commit start at LEB %d:0, len %d", c->lhead_lnum, len); ++ err = ubifs_leb_write(c, c->lhead_lnum, cs, 0, len, UBI_SHORTTERM); ++ if (err) ++ goto out; ++ ++ *ltail_lnum = c->lhead_lnum; ++ ++ c->lhead_offs += len; ++ if (c->lhead_offs == c->leb_size) { ++ c->lhead_lnum = next_log_lnum(c, c->lhead_lnum); ++ c->lhead_offs = 0; ++ } ++ ++ remove_buds(c); ++ ++ /* ++ * We have started the commit and now users may use the rest of the log ++ * for new writes. ++ */ ++ c->min_log_bytes = 0; ++ ++out: ++ kfree(buf); ++ return err; ++} ++ ++/** ++ * ubifs_log_end_commit - end commit. ++ * @c: UBIFS file-system description object ++ * @ltail_lnum: new log tail LEB number ++ * ++ * This function is called on when the commit operation was finished. It ++ * moves log tail to new position and unmaps LEBs which contain obsolete data. ++ * Returns zero in case of success and a negative error code in case of ++ * failure. ++ */ ++int ubifs_log_end_commit(struct ubifs_info *c, int ltail_lnum) ++{ ++ int err; ++ ++ /* ++ * At this phase we have to lock 'c->log_mutex' because UBIFS allows FS ++ * writes during commit. Its only short "commit" start phase when ++ * writers are blocked. ++ */ ++ mutex_lock(&c->log_mutex); ++ ++ dbg_log("old tail was LEB %d:0, new tail is LEB %d:0", ++ c->ltail_lnum, ltail_lnum); ++ ++ c->ltail_lnum = ltail_lnum; ++ /* ++ * The commit is finished and from now on it must be guaranteed that ++ * there is always enough space for the next commit. ++ */ ++ c->min_log_bytes = c->leb_size; ++ ++ spin_lock(&c->buds_lock); ++ c->bud_bytes -= c->cmt_bud_bytes; ++ spin_unlock(&c->buds_lock); ++ ++ err = dbg_check_bud_bytes(c); ++ ++ mutex_unlock(&c->log_mutex); ++ return err; ++} ++ ++/** ++ * ubifs_log_post_commit - things to do after commit is completed. ++ * @c: UBIFS file-system description object ++ * @old_ltail_lnum: old log tail LEB number ++ * ++ * Release buds only after commit is completed, because they must be unchanged ++ * if recovery is needed. ++ * ++ * Unmap log LEBs only after commit is completed, because they may be needed for ++ * recovery. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_log_post_commit(struct ubifs_info *c, int old_ltail_lnum) ++{ ++ int lnum, err = 0; ++ ++ while (!list_empty(&c->old_buds)) { ++ struct ubifs_bud *bud; ++ ++ bud = list_entry(c->old_buds.next, struct ubifs_bud, list); ++ err = ubifs_return_leb(c, bud->lnum); ++ if (err) ++ return err; ++ list_del(&bud->list); ++ kfree(bud); ++ } ++ mutex_lock(&c->log_mutex); ++ for (lnum = old_ltail_lnum; lnum != c->ltail_lnum; ++ lnum = next_log_lnum(c, lnum)) { ++ dbg_log("unmap log LEB %d", lnum); ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ goto out; ++ } ++out: ++ mutex_unlock(&c->log_mutex); ++ return err; ++} ++ ++/** ++ * struct done_ref - references that have been done. ++ * @rb: rb-tree node ++ * @lnum: LEB number ++ */ ++struct done_ref { ++ struct rb_node rb; ++ int lnum; ++}; ++ ++/** ++ * done_already - determine if a reference has been done already. ++ * @done_tree: rb-tree to store references that have been done ++ * @lnum: LEB number of reference ++ * ++ * This function returns %1 if the reference has been done, %0 if not, otherwise ++ * a negative error code is returned. ++ */ ++static int done_already(struct rb_root *done_tree, int lnum) ++{ ++ struct rb_node **p = &done_tree->rb_node, *parent = NULL; ++ struct done_ref *dr; ++ ++ while (*p) { ++ parent = *p; ++ dr = rb_entry(parent, struct done_ref, rb); ++ if (lnum < dr->lnum) ++ p = &(*p)->rb_left; ++ else if (lnum > dr->lnum) ++ p = &(*p)->rb_right; ++ else ++ return 1; ++ } ++ ++ dr = kzalloc(sizeof(struct done_ref), GFP_NOFS); ++ if (!dr) ++ return -ENOMEM; ++ ++ dr->lnum = lnum; ++ ++ rb_link_node(&dr->rb, parent, p); ++ rb_insert_color(&dr->rb, done_tree); ++ ++ return 0; ++} ++ ++/** ++ * destroy_done_tree - destroy the done tree. ++ * @done_tree: done tree to destroy ++ */ ++static void destroy_done_tree(struct rb_root *done_tree) ++{ ++ struct rb_node *this = done_tree->rb_node; ++ struct done_ref *dr; ++ ++ while (this) { ++ if (this->rb_left) { ++ this = this->rb_left; ++ continue; ++ } else if (this->rb_right) { ++ this = this->rb_right; ++ continue; ++ } ++ dr = rb_entry(this, struct done_ref, rb); ++ this = rb_parent(this); ++ if (this) { ++ if (this->rb_left == &dr->rb) ++ this->rb_left = NULL; ++ else ++ this->rb_right = NULL; ++ } ++ kfree(dr); ++ } ++} ++ ++/** ++ * add_node - add a node to the consolidated log. ++ * @c: UBIFS file-system description object ++ * @buf: buffer to which to add ++ * @lnum: LEB number to which to write is passed and returned here ++ * @offs: offset to where to write is passed and returned here ++ * @node: node to add ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int add_node(struct ubifs_info *c, void *buf, int *lnum, int *offs, ++ void *node) ++{ ++ struct ubifs_ch *ch = node; ++ int len = le32_to_cpu(ch->len), remains = c->leb_size - *offs; ++ ++ if (len > remains) { ++ int sz = ALIGN(*offs, c->min_io_size), err; ++ ++ ubifs_pad(c, buf + *offs, sz - *offs); ++ err = ubi_leb_change(c->ubi, *lnum, buf, sz, UBI_SHORTTERM); ++ if (err) ++ return err; ++ *lnum = next_log_lnum(c, *lnum); ++ *offs = 0; ++ } ++ memcpy(buf + *offs, node, len); ++ *offs += ALIGN(len, 8); ++ return 0; ++} ++ ++/** ++ * ubifs_consolidate_log - consolidate the log. ++ * @c: UBIFS file-system description object ++ * ++ * Repeated failed commits could cause the log to be full, but at least 1 LEB is ++ * needed for commit. This function rewrites the reference nodes in the log ++ * omitting duplicates, and failed CS nodes, and leaving no gaps. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_consolidate_log(struct ubifs_info *c) ++{ ++ struct ubifs_scan_leb *sleb; ++ struct ubifs_scan_node *snod; ++ struct rb_root done_tree = RB_ROOT; ++ int lnum, err, first = 1, write_lnum, offs = 0; ++ void *buf; ++ ++ dbg_rcvry("log tail LEB %d, log head LEB %d", c->ltail_lnum, ++ c->lhead_lnum); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ buf = kmalloc(c->leb_size, GFP_KERNEL); ++#else ++ buf = vmalloc(c->leb_size); ++#endif ++ if (!buf) ++ return -ENOMEM; ++ lnum = c->ltail_lnum; ++ write_lnum = lnum; ++ while (1) { ++ sleb = ubifs_scan(c, lnum, 0, c->sbuf); ++ if (IS_ERR(sleb)) { ++ err = PTR_ERR(sleb); ++ goto out_free; ++ } ++ list_for_each_entry(snod, &sleb->nodes, list) { ++ switch (snod->type) { ++ case UBIFS_REF_NODE: { ++ struct ubifs_ref_node *ref = snod->node; ++ int ref_lnum = le32_to_cpu(ref->lnum); ++ ++ err = done_already(&done_tree, ref_lnum); ++ if (err < 0) ++ goto out_scan; ++ if (err != 1) { ++ err = add_node(c, buf, &write_lnum, ++ &offs, snod->node); ++ if (err) ++ goto out_scan; ++ } ++ break; ++ } ++ case UBIFS_CS_NODE: ++ if (!first) ++ break; ++ err = add_node(c, buf, &write_lnum, &offs, ++ snod->node); ++ if (err) ++ goto out_scan; ++ first = 0; ++ break; ++ } ++ } ++ ubifs_scan_destroy(sleb); ++ if (lnum == c->lhead_lnum) ++ break; ++ lnum = next_log_lnum(c, lnum); ++ } ++ if (offs) { ++ int sz = ALIGN(offs, c->min_io_size); ++ ++ ubifs_pad(c, buf + offs, sz - offs); ++ err = ubi_leb_change(c->ubi, write_lnum, buf, sz, ++ UBI_SHORTTERM); ++ if (err) ++ goto out_free; ++ offs = ALIGN(offs, c->min_io_size); ++ } ++ destroy_done_tree(&done_tree); ++ vfree(buf); ++ if (write_lnum == c->lhead_lnum) { ++ ubifs_err("log is too full"); ++ return -EINVAL; ++ } ++ /* Unmap remaining LEBs */ ++ lnum = write_lnum; ++ do { ++ lnum = next_log_lnum(c, lnum); ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ return err; ++ } while (lnum != c->lhead_lnum); ++ c->lhead_lnum = write_lnum; ++ c->lhead_offs = offs; ++ dbg_rcvry("new log head at %d:%d", c->lhead_lnum, c->lhead_offs); ++ return 0; ++ ++out_scan: ++ ubifs_scan_destroy(sleb); ++out_free: ++ destroy_done_tree(&done_tree); ++ vfree(buf); ++ return err; ++} ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ ++/** ++ * dbg_check_bud_bytes - make sure bud bytes calculation are all right. ++ * @c: UBIFS file-system description object ++ * ++ * This function makes sure the amount of flash space used by closed buds ++ * ('c->bud_bytes' is correct). Returns zero in case of success and %-EINVAL in ++ * case of failure. ++ */ ++static int dbg_check_bud_bytes(struct ubifs_info *c) ++{ ++ int i, err = 0; ++ struct ubifs_bud *bud; ++ long long bud_bytes = 0; ++ ++ if (!(ubifs_chk_flags & UBIFS_CHK_GEN)) ++ return 0; ++ ++ spin_lock(&c->buds_lock); ++ for (i = 0; i < c->jhead_cnt; i++) ++ list_for_each_entry(bud, &c->jheads[i].buds_list, list) ++ bud_bytes += c->leb_size - bud->start; ++ ++ if (c->bud_bytes != bud_bytes) { ++ ubifs_err("bad bud_bytes %lld, calculated %lld", ++ c->bud_bytes, bud_bytes); ++ err = -EINVAL; ++ } ++ spin_unlock(&c->buds_lock); ++ ++ return err; ++} ++ ++#endif /* CONFIG_UBIFS_FS_DEBUG */ +diff -urN linux-2.6.24.7.old/fs/ubifs/lprops.c linux-2.6.24.7/fs/ubifs/lprops.c +--- linux-2.6.24.7.old/fs/ubifs/lprops.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/lprops.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1353 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy (Битюцкий Ðртём) ++ */ ++ ++/* ++ * This file implements the functions that access LEB properties and their ++ * categories. LEBs are categorized based on the needs of UBIFS, and the ++ * categories are stored as either heaps or lists to provide a fast way of ++ * finding a LEB in a particular category. For example, UBIFS may need to find ++ * an empty LEB for the journal, or a very dirty LEB for garbage collection. ++ */ ++ ++#include "ubifs.h" ++ ++/** ++ * get_heap_comp_val - get the LEB properties value for heap comparisons. ++ * @lprops: LEB properties ++ * @cat: LEB category ++ */ ++static int get_heap_comp_val(struct ubifs_lprops *lprops, int cat) ++{ ++ switch (cat) { ++ case LPROPS_FREE: ++ return lprops->free; ++ case LPROPS_DIRTY_IDX: ++ return lprops->free + lprops->dirty; ++ default: ++ return lprops->dirty; ++ } ++} ++ ++/** ++ * move_up_lpt_heap - move a new heap entry up as far as possible. ++ * @c: UBIFS file-system description object ++ * @heap: LEB category heap ++ * @lprops: LEB properties to move ++ * @cat: LEB category ++ * ++ * New entries to a heap are added at the bottom and then moved up until the ++ * parent's value is greater. In the case of LPT's category heaps, the value ++ * is either the amount of free space or the amount of dirty space, depending ++ * on the category. ++ */ ++static void move_up_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, ++ struct ubifs_lprops *lprops, int cat) ++{ ++ int val1, val2, hpos; ++ ++ hpos = lprops->hpos; ++ if (!hpos) ++ return; /* Already top of the heap */ ++ val1 = get_heap_comp_val(lprops, cat); ++ /* Compare to parent and, if greater, move up the heap */ ++ do { ++ int ppos = (hpos - 1) / 2; ++ ++ val2 = get_heap_comp_val(heap->arr[ppos], cat); ++ if (val2 >= val1) ++ return; ++ /* Greater than parent so move up */ ++ heap->arr[ppos]->hpos = hpos; ++ heap->arr[hpos] = heap->arr[ppos]; ++ heap->arr[ppos] = lprops; ++ lprops->hpos = ppos; ++ hpos = ppos; ++ } while (hpos); ++} ++ ++/** ++ * adjust_lpt_heap - move a changed heap entry up or down the heap. ++ * @c: UBIFS file-system description object ++ * @heap: LEB category heap ++ * @lprops: LEB properties to move ++ * @hpos: heap position of @lprops ++ * @cat: LEB category ++ * ++ * Changed entries in a heap are moved up or down until the parent's value is ++ * greater. In the case of LPT's category heaps, the value is either the amount ++ * of free space or the amount of dirty space, depending on the category. ++ */ ++static void adjust_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, ++ struct ubifs_lprops *lprops, int hpos, int cat) ++{ ++ int val1, val2, val3, cpos; ++ ++ val1 = get_heap_comp_val(lprops, cat); ++ /* Compare to parent and, if greater than parent, move up the heap */ ++ if (hpos) { ++ int ppos = (hpos - 1) / 2; ++ ++ val2 = get_heap_comp_val(heap->arr[ppos], cat); ++ if (val1 > val2) { ++ /* Greater than parent so move up */ ++ while (1) { ++ heap->arr[ppos]->hpos = hpos; ++ heap->arr[hpos] = heap->arr[ppos]; ++ heap->arr[ppos] = lprops; ++ lprops->hpos = ppos; ++ hpos = ppos; ++ if (!hpos) ++ return; ++ ppos = (hpos - 1) / 2; ++ val2 = get_heap_comp_val(heap->arr[ppos], cat); ++ if (val1 <= val2) ++ return; ++ /* Still greater than parent so keep going */ ++ } ++ } ++ } ++ /* Not greater than parent, so compare to children */ ++ while (1) { ++ /* Compare to left child */ ++ cpos = hpos * 2 + 1; ++ if (cpos >= heap->cnt) ++ return; ++ val2 = get_heap_comp_val(heap->arr[cpos], cat); ++ if (val1 < val2) { ++ /* Less than left child, so promote biggest child */ ++ if (cpos + 1 < heap->cnt) { ++ val3 = get_heap_comp_val(heap->arr[cpos + 1], ++ cat); ++ if (val3 > val2) ++ cpos += 1; /* Right child is bigger */ ++ } ++ heap->arr[cpos]->hpos = hpos; ++ heap->arr[hpos] = heap->arr[cpos]; ++ heap->arr[cpos] = lprops; ++ lprops->hpos = cpos; ++ hpos = cpos; ++ continue; ++ } ++ /* Compare to right child */ ++ cpos += 1; ++ if (cpos >= heap->cnt) ++ return; ++ val3 = get_heap_comp_val(heap->arr[cpos], cat); ++ if (val1 < val3) { ++ /* Less than right child, so promote right child */ ++ heap->arr[cpos]->hpos = hpos; ++ heap->arr[hpos] = heap->arr[cpos]; ++ heap->arr[cpos] = lprops; ++ lprops->hpos = cpos; ++ hpos = cpos; ++ continue; ++ } ++ return; ++ } ++} ++ ++/** ++ * add_to_lpt_heap - add LEB properties to a LEB category heap. ++ * @c: UBIFS file-system description object ++ * @lprops: LEB properties to add ++ * @cat: LEB category ++ * ++ * This function returns %1 if @lprops is added to the heap for LEB category ++ * @cat, otherwise %0 is returned because the heap is full. ++ */ ++static int add_to_lpt_heap(struct ubifs_info *c, struct ubifs_lprops *lprops, ++ int cat) ++{ ++ struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1]; ++ ++ if (heap->cnt >= heap->max_cnt) { ++ const int b = LPT_HEAP_SZ / 2 - 1; ++ int cpos, val1, val2; ++ ++ /* Compare to some other LEB on the bottom of heap */ ++ /* Pick a position kind of randomly */ ++ cpos = (((size_t)lprops >> 4) & b) + b; ++ ubifs_assert(cpos >= b); ++ ubifs_assert(cpos < LPT_HEAP_SZ); ++ ubifs_assert(cpos < heap->cnt); ++ ++ val1 = get_heap_comp_val(lprops, cat); ++ val2 = get_heap_comp_val(heap->arr[cpos], cat); ++ if (val1 > val2) { ++ struct ubifs_lprops *lp; ++ ++ lp = heap->arr[cpos]; ++ lp->flags &= ~LPROPS_CAT_MASK; ++ lp->flags |= LPROPS_UNCAT; ++ list_add(&lp->list, &c->uncat_list); ++ lprops->hpos = cpos; ++ heap->arr[cpos] = lprops; ++ move_up_lpt_heap(c, heap, lprops, cat); ++ dbg_check_heap(c, heap, cat, lprops->hpos); ++ return 1; /* Added to heap */ ++ } ++ dbg_check_heap(c, heap, cat, -1); ++ return 0; /* Not added to heap */ ++ } else { ++ lprops->hpos = heap->cnt++; ++ heap->arr[lprops->hpos] = lprops; ++ move_up_lpt_heap(c, heap, lprops, cat); ++ dbg_check_heap(c, heap, cat, lprops->hpos); ++ return 1; /* Added to heap */ ++ } ++} ++ ++/** ++ * remove_from_lpt_heap - remove LEB properties from a LEB category heap. ++ * @c: UBIFS file-system description object ++ * @lprops: LEB properties to remove ++ * @cat: LEB category ++ */ ++static void remove_from_lpt_heap(struct ubifs_info *c, ++ struct ubifs_lprops *lprops, int cat) ++{ ++ struct ubifs_lpt_heap *heap; ++ int hpos = lprops->hpos; ++ ++ heap = &c->lpt_heap[cat - 1]; ++ ubifs_assert(hpos >= 0 && hpos < heap->cnt); ++ ubifs_assert(heap->arr[hpos] == lprops); ++ heap->cnt -= 1; ++ if (hpos < heap->cnt) { ++ heap->arr[hpos] = heap->arr[heap->cnt]; ++ heap->arr[hpos]->hpos = hpos; ++ adjust_lpt_heap(c, heap, heap->arr[hpos], hpos, cat); ++ } ++ dbg_check_heap(c, heap, cat, -1); ++} ++ ++/** ++ * lpt_heap_replace - replace lprops in a category heap. ++ * @c: UBIFS file-system description object ++ * @old_lprops: LEB properties to replace ++ * @new_lprops: LEB properties with which to replace ++ * @cat: LEB category ++ * ++ * During commit it is sometimes necessary to copy a pnode (see dirty_cow_pnode) ++ * and the lprops that the pnode contains. When that happens, references in ++ * the category heaps to those lprops must be updated to point to the new ++ * lprops. This function does that. ++ */ ++static void lpt_heap_replace(struct ubifs_info *c, ++ struct ubifs_lprops *old_lprops, ++ struct ubifs_lprops *new_lprops, int cat) ++{ ++ struct ubifs_lpt_heap *heap; ++ int hpos = new_lprops->hpos; ++ ++ heap = &c->lpt_heap[cat - 1]; ++ heap->arr[hpos] = new_lprops; ++} ++ ++/** ++ * ubifs_add_to_cat - add LEB properties to a category list or heap. ++ * @c: UBIFS file-system description object ++ * @lprops: LEB properties to add ++ * @cat: LEB category to which to add ++ * ++ * LEB properties are categorized to enable fast find operations. ++ */ ++void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops, ++ int cat) ++{ ++ switch (cat) { ++ case LPROPS_DIRTY: ++ case LPROPS_DIRTY_IDX: ++ case LPROPS_FREE: ++ if (add_to_lpt_heap(c, lprops, cat)) ++ break; ++ /* No more room on heap so make it uncategorized */ ++ cat = LPROPS_UNCAT; ++ /* Fall through */ ++ case LPROPS_UNCAT: ++ list_add(&lprops->list, &c->uncat_list); ++ break; ++ case LPROPS_EMPTY: ++ list_add(&lprops->list, &c->empty_list); ++ break; ++ case LPROPS_FREEABLE: ++ list_add(&lprops->list, &c->freeable_list); ++ c->freeable_cnt += 1; ++ break; ++ case LPROPS_FRDI_IDX: ++ list_add(&lprops->list, &c->frdi_idx_list); ++ break; ++ default: ++ ubifs_assert(0); ++ } ++ lprops->flags &= ~LPROPS_CAT_MASK; ++ lprops->flags |= cat; ++} ++ ++/** ++ * ubifs_remove_from_cat - remove LEB properties from a category list or heap. ++ * @c: UBIFS file-system description object ++ * @lprops: LEB properties to remove ++ * @cat: LEB category from which to remove ++ * ++ * LEB properties are categorized to enable fast find operations. ++ */ ++static void ubifs_remove_from_cat(struct ubifs_info *c, ++ struct ubifs_lprops *lprops, int cat) ++{ ++ switch (cat) { ++ case LPROPS_DIRTY: ++ case LPROPS_DIRTY_IDX: ++ case LPROPS_FREE: ++ remove_from_lpt_heap(c, lprops, cat); ++ break; ++ case LPROPS_FREEABLE: ++ c->freeable_cnt -= 1; ++ ubifs_assert(c->freeable_cnt >= 0); ++ /* Fall through */ ++ case LPROPS_UNCAT: ++ case LPROPS_EMPTY: ++ case LPROPS_FRDI_IDX: ++ ubifs_assert(!list_empty(&lprops->list)); ++ list_del(&lprops->list); ++ break; ++ default: ++ ubifs_assert(0); ++ } ++} ++ ++/** ++ * ubifs_replace_cat - replace lprops in a category list or heap. ++ * @c: UBIFS file-system description object ++ * @old_lprops: LEB properties to replace ++ * @new_lprops: LEB properties with which to replace ++ * ++ * During commit it is sometimes necessary to copy a pnode (see dirty_cow_pnode) ++ * and the lprops that the pnode contains. When that happens, references in ++ * category lists and heaps must be replaced. This function does that. ++ */ ++void ubifs_replace_cat(struct ubifs_info *c, struct ubifs_lprops *old_lprops, ++ struct ubifs_lprops *new_lprops) ++{ ++ int cat; ++ ++ cat = new_lprops->flags & LPROPS_CAT_MASK; ++ switch (cat) { ++ case LPROPS_DIRTY: ++ case LPROPS_DIRTY_IDX: ++ case LPROPS_FREE: ++ lpt_heap_replace(c, old_lprops, new_lprops, cat); ++ break; ++ case LPROPS_UNCAT: ++ case LPROPS_EMPTY: ++ case LPROPS_FREEABLE: ++ case LPROPS_FRDI_IDX: ++ list_replace(&old_lprops->list, &new_lprops->list); ++ break; ++ default: ++ ubifs_assert(0); ++ } ++} ++ ++/** ++ * ubifs_ensure_cat - ensure LEB properties are categorized. ++ * @c: UBIFS file-system description object ++ * @lprops: LEB properties ++ * ++ * A LEB may have fallen off of the bottom of a heap, and ended up as ++ * uncategorized even though it has enough space for us now. If that is the case ++ * this function will put the LEB back onto a heap. ++ */ ++void ubifs_ensure_cat(struct ubifs_info *c, struct ubifs_lprops *lprops) ++{ ++ int cat = lprops->flags & LPROPS_CAT_MASK; ++ ++ if (cat != LPROPS_UNCAT) ++ return; ++ cat = ubifs_categorize_lprops(c, lprops); ++ if (cat == LPROPS_UNCAT) ++ return; ++ ubifs_remove_from_cat(c, lprops, LPROPS_UNCAT); ++ ubifs_add_to_cat(c, lprops, cat); ++} ++ ++/** ++ * ubifs_categorize_lprops - categorize LEB properties. ++ * @c: UBIFS file-system description object ++ * @lprops: LEB properties to categorize ++ * ++ * LEB properties are categorized to enable fast find operations. This function ++ * returns the LEB category to which the LEB properties belong. Note however ++ * that if the LEB category is stored as a heap and the heap is full, the ++ * LEB properties may have their category changed to %LPROPS_UNCAT. ++ */ ++int ubifs_categorize_lprops(const struct ubifs_info *c, ++ const struct ubifs_lprops *lprops) ++{ ++ if (lprops->flags & LPROPS_TAKEN) ++ return LPROPS_UNCAT; ++ ++ if (lprops->free == c->leb_size) { ++ ubifs_assert(!(lprops->flags & LPROPS_INDEX)); ++ return LPROPS_EMPTY; ++ } ++ ++ if (lprops->free + lprops->dirty == c->leb_size) { ++ if (lprops->flags & LPROPS_INDEX) ++ return LPROPS_FRDI_IDX; ++ else ++ return LPROPS_FREEABLE; ++ } ++ ++ if (lprops->flags & LPROPS_INDEX) { ++ if (lprops->dirty + lprops->free >= c->min_idx_node_sz) ++ return LPROPS_DIRTY_IDX; ++ } else { ++ if (lprops->dirty >= c->dead_wm && ++ lprops->dirty > lprops->free) ++ return LPROPS_DIRTY; ++ if (lprops->free > 0) ++ return LPROPS_FREE; ++ } ++ ++ return LPROPS_UNCAT; ++} ++ ++/** ++ * change_category - change LEB properties category. ++ * @c: UBIFS file-system description object ++ * @lprops: LEB properties to recategorize ++ * ++ * LEB properties are categorized to enable fast find operations. When the LEB ++ * properties change they must be recategorized. ++ */ ++static void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops) ++{ ++ int old_cat = lprops->flags & LPROPS_CAT_MASK; ++ int new_cat = ubifs_categorize_lprops(c, lprops); ++ ++ if (old_cat == new_cat) { ++ struct ubifs_lpt_heap *heap = &c->lpt_heap[new_cat - 1]; ++ ++ /* lprops on a heap now must be moved up or down */ ++ if (new_cat < 1 || new_cat > LPROPS_HEAP_CNT) ++ return; /* Not on a heap */ ++ heap = &c->lpt_heap[new_cat - 1]; ++ adjust_lpt_heap(c, heap, lprops, lprops->hpos, new_cat); ++ } else { ++ ubifs_remove_from_cat(c, lprops, old_cat); ++ ubifs_add_to_cat(c, lprops, new_cat); ++ } ++} ++ ++/** ++ * ubifs_get_lprops - get reference to LEB properties. ++ * @c: the UBIFS file-system description object ++ * ++ * This function locks lprops. Lprops have to be unlocked by ++ * 'ubifs_release_lprops()'. ++ */ ++void ubifs_get_lprops(struct ubifs_info *c) ++{ ++ mutex_lock(&c->lp_mutex); ++} ++ ++/** ++ * calc_dark - calculate LEB dark space size. ++ * @c: the UBIFS file-system description object ++ * @spc: amount of free and dirty space in the LEB ++ * ++ * This function calculates amount of dark space in an LEB which has @spc bytes ++ * of free and dirty space. Returns the calculations result. ++ * ++ * Dark space is the space which is not always usable - it depends on which ++ * nodes are written in which order. E.g., if an LEB has only 512 free bytes, ++ * it is dark space, because it cannot fit a large data node. So UBIFS cannot ++ * count on this LEB and treat these 512 bytes as usable because it is not true ++ * if, for example, only big chunks of uncompressible data will be written to ++ * the FS. ++ */ ++static int calc_dark(struct ubifs_info *c, int spc) ++{ ++ ubifs_assert(!(spc & 7)); ++ ++ if (spc < c->dark_wm) ++ return spc; ++ ++ /* ++ * If we have slightly more space then the dark space watermark, we can ++ * anyway safely assume it we'll be able to write a node of the ++ * smallest size there. ++ */ ++ if (spc - c->dark_wm < MIN_WRITE_SZ) ++ return spc - MIN_WRITE_SZ; ++ ++ return c->dark_wm; ++} ++ ++/** ++ * is_lprops_dirty - determine if LEB properties are dirty. ++ * @c: the UBIFS file-system description object ++ * @lprops: LEB properties to test ++ */ ++static int is_lprops_dirty(struct ubifs_info *c, struct ubifs_lprops *lprops) ++{ ++ struct ubifs_pnode *pnode; ++ int pos; ++ ++ pos = (lprops->lnum - c->main_first) & (UBIFS_LPT_FANOUT - 1); ++ pnode = (struct ubifs_pnode *)container_of(lprops - pos, ++ struct ubifs_pnode, ++ lprops[0]); ++ return !test_bit(COW_ZNODE, &pnode->flags) && ++ test_bit(DIRTY_CNODE, &pnode->flags); ++} ++ ++/** ++ * ubifs_change_lp - change LEB properties. ++ * @c: the UBIFS file-system description object ++ * @lp: LEB properties to change ++ * @free: new free space amount ++ * @dirty: new dirty space amount ++ * @flags: new flags ++ * @idx_gc_cnt: change to the count of idx_gc list ++ * ++ * This function changes LEB properties. This function does not change a LEB ++ * property (@free, @dirty or @flag) if the value passed is %-1. ++ * ++ * This function returns a pointer to the updated LEB properties on success ++ * and a negative error code on failure. N.B. the LEB properties may have had to ++ * be copied (due to COW) and consequently the pointer returned may not be the ++ * same as the pointer passed. ++ */ ++const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c, ++ const struct ubifs_lprops *lp, ++ int free, int dirty, int flags, ++ int idx_gc_cnt) ++{ ++ /* ++ * This is the only function that is allowed to change lprops, so we ++ * discard the const qualifier. ++ */ ++ struct ubifs_lprops *lprops = (struct ubifs_lprops *)lp; ++ ++ dbg_lp("LEB %d, free %d, dirty %d, flags %d", ++ lprops->lnum, free, dirty, flags); ++ ++ ubifs_assert(mutex_is_locked(&c->lp_mutex)); ++ ubifs_assert(c->lst.empty_lebs >= 0 && ++ c->lst.empty_lebs <= c->main_lebs); ++ ubifs_assert(c->freeable_cnt >= 0); ++ ubifs_assert(c->freeable_cnt <= c->main_lebs); ++ ubifs_assert(c->lst.taken_empty_lebs >= 0); ++ ubifs_assert(c->lst.taken_empty_lebs <= c->lst.empty_lebs); ++ ubifs_assert(!(c->lst.total_free & 7) && !(c->lst.total_dirty & 7)); ++ ubifs_assert(!(c->lst.total_dead & 7) && !(c->lst.total_dark & 7)); ++ ubifs_assert(!(c->lst.total_used & 7)); ++ ++ if (!is_lprops_dirty(c, lprops)) { ++ lprops = ubifs_lpt_lookup_dirty(c, lprops->lnum); ++ if (IS_ERR(lprops)) ++ return lprops; ++ } else ++ ubifs_assert(lprops == ubifs_lpt_lookup_dirty(c, lprops->lnum)); ++ ++ ubifs_assert(!(lprops->free & 7) && !(lprops->dirty & 7)); ++ ++ spin_lock(&c->space_lock); ++ ++ if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size) ++ c->lst.taken_empty_lebs -= 1; ++ ++ if (!(lprops->flags & LPROPS_INDEX)) { ++ int old_spc; ++ ++ old_spc = lprops->free + lprops->dirty; ++ if (old_spc < c->dead_wm) ++ c->lst.total_dead -= old_spc; ++ else ++ c->lst.total_dark -= calc_dark(c, old_spc); ++ ++ c->lst.total_used -= c->leb_size - old_spc; ++ } ++ ++ if (free != -1) { ++ free = ALIGN(free, 8); ++ c->lst.total_free += free - lprops->free; ++ ++ /* Increase or decrease empty LEBs counter if needed */ ++ if (free == c->leb_size) { ++ if (lprops->free != c->leb_size) ++ c->lst.empty_lebs += 1; ++ } else if (lprops->free == c->leb_size) ++ c->lst.empty_lebs -= 1; ++ lprops->free = free; ++ } ++ ++ if (dirty != -1) { ++ dirty = ALIGN(dirty, 8); ++ c->lst.total_dirty += dirty - lprops->dirty; ++ lprops->dirty = dirty; ++ } ++ ++ if (flags != -1) { ++ /* Take care about indexing LEBs counter if needed */ ++ if ((lprops->flags & LPROPS_INDEX)) { ++ if (!(flags & LPROPS_INDEX)) ++ c->lst.idx_lebs -= 1; ++ } else if (flags & LPROPS_INDEX) ++ c->lst.idx_lebs += 1; ++ lprops->flags = flags; ++ } ++ ++ if (!(lprops->flags & LPROPS_INDEX)) { ++ int new_spc; ++ ++ new_spc = lprops->free + lprops->dirty; ++ if (new_spc < c->dead_wm) ++ c->lst.total_dead += new_spc; ++ else ++ c->lst.total_dark += calc_dark(c, new_spc); ++ ++ c->lst.total_used += c->leb_size - new_spc; ++ } ++ ++ if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size) ++ c->lst.taken_empty_lebs += 1; ++ ++ change_category(c, lprops); ++ ++ c->idx_gc_cnt += idx_gc_cnt; ++ ++ spin_unlock(&c->space_lock); ++ ++ return lprops; ++} ++ ++/** ++ * ubifs_release_lprops - release lprops lock. ++ * @c: the UBIFS file-system description object ++ * ++ * This function has to be called after each 'ubifs_get_lprops()' call to ++ * unlock lprops. ++ */ ++void ubifs_release_lprops(struct ubifs_info *c) ++{ ++ ubifs_assert(mutex_is_locked(&c->lp_mutex)); ++ ubifs_assert(c->lst.empty_lebs >= 0 && ++ c->lst.empty_lebs <= c->main_lebs); ++ ++ mutex_unlock(&c->lp_mutex); ++} ++ ++/** ++ * ubifs_get_lp_stats - get lprops statistics. ++ * @c: UBIFS file-system description object ++ * @st: return statistics ++ */ ++void ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *st) ++{ ++ spin_lock(&c->space_lock); ++ memcpy(st, &c->lst, sizeof(struct ubifs_lp_stats)); ++ spin_unlock(&c->space_lock); ++} ++ ++/** ++ * ubifs_change_one_lp - change LEB properties. ++ * @c: the UBIFS file-system description object ++ * @lnum: LEB to change properties for ++ * @free: amount of free space ++ * @dirty: amount of dirty space ++ * @flags_set: flags to set ++ * @flags_clean: flags to clean ++ * @idx_gc_cnt: change to the count of idx_gc list ++ * ++ * This function changes properties of LEB @lnum. It is a helper wrapper over ++ * 'ubifs_change_lp()' which hides lprops get/release. The arguments are the ++ * same as in case of 'ubifs_change_lp()'. Returns zero in case of success and ++ * a negative error code in case of failure. ++ */ ++int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty, ++ int flags_set, int flags_clean, int idx_gc_cnt) ++{ ++ int err = 0, flags; ++ const struct ubifs_lprops *lp; ++ ++ ubifs_get_lprops(c); ++ ++ lp = ubifs_lpt_lookup_dirty(c, lnum); ++ if (IS_ERR(lp)) { ++ err = PTR_ERR(lp); ++ goto out; ++ } ++ ++ flags = (lp->flags | flags_set) & ~flags_clean; ++ lp = ubifs_change_lp(c, lp, free, dirty, flags, idx_gc_cnt); ++ if (IS_ERR(lp)) ++ err = PTR_ERR(lp); ++ ++out: ++ ubifs_release_lprops(c); ++ return err; ++} ++ ++/** ++ * ubifs_update_one_lp - update LEB properties. ++ * @c: the UBIFS file-system description object ++ * @lnum: LEB to change properties for ++ * @free: amount of free space ++ * @dirty: amount of dirty space to add ++ * @flags_set: flags to set ++ * @flags_clean: flags to clean ++ * ++ * This function is the same as 'ubifs_change_one_lp()' but @dirty is added to ++ * current dirty space, not substitutes it. ++ */ ++int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty, ++ int flags_set, int flags_clean) ++{ ++ int err = 0, flags; ++ const struct ubifs_lprops *lp; ++ ++ ubifs_get_lprops(c); ++ ++ lp = ubifs_lpt_lookup_dirty(c, lnum); ++ if (IS_ERR(lp)) { ++ err = PTR_ERR(lp); ++ goto out; ++ } ++ ++ flags = (lp->flags | flags_set) & ~flags_clean; ++ lp = ubifs_change_lp(c, lp, free, lp->dirty + dirty, flags, 0); ++ if (IS_ERR(lp)) ++ err = PTR_ERR(lp); ++ ++out: ++ ubifs_release_lprops(c); ++ return err; ++} ++ ++/** ++ * ubifs_read_one_lp - read LEB properties. ++ * @c: the UBIFS file-system description object ++ * @lnum: LEB to read properties for ++ * @lp: where to store read properties ++ * ++ * This helper function reads properties of a LEB @lnum and stores them in @lp. ++ * Returns zero in case of success and a negative error code in case of ++ * failure. ++ */ ++int ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp) ++{ ++ int err = 0; ++ const struct ubifs_lprops *lpp; ++ ++ ubifs_get_lprops(c); ++ ++ lpp = ubifs_lpt_lookup(c, lnum); ++ if (IS_ERR(lpp)) { ++ err = PTR_ERR(lpp); ++ goto out; ++ } ++ ++ memcpy(lp, lpp, sizeof(struct ubifs_lprops)); ++ ++out: ++ ubifs_release_lprops(c); ++ return err; ++} ++ ++/** ++ * ubifs_fast_find_free - try to find a LEB with free space quickly. ++ * @c: the UBIFS file-system description object ++ * ++ * This function returns LEB properties for a LEB with free space or %NULL if ++ * the function is unable to find a LEB quickly. ++ */ ++const struct ubifs_lprops *ubifs_fast_find_free(struct ubifs_info *c) ++{ ++ struct ubifs_lprops *lprops; ++ struct ubifs_lpt_heap *heap; ++ ++ ubifs_assert(mutex_is_locked(&c->lp_mutex)); ++ ++ heap = &c->lpt_heap[LPROPS_FREE - 1]; ++ if (heap->cnt == 0) ++ return NULL; ++ ++ lprops = heap->arr[0]; ++ ubifs_assert(!(lprops->flags & LPROPS_TAKEN)); ++ ubifs_assert(!(lprops->flags & LPROPS_INDEX)); ++ return lprops; ++} ++ ++/** ++ * ubifs_fast_find_empty - try to find an empty LEB quickly. ++ * @c: the UBIFS file-system description object ++ * ++ * This function returns LEB properties for an empty LEB or %NULL if the ++ * function is unable to find an empty LEB quickly. ++ */ ++const struct ubifs_lprops *ubifs_fast_find_empty(struct ubifs_info *c) ++{ ++ struct ubifs_lprops *lprops; ++ ++ ubifs_assert(mutex_is_locked(&c->lp_mutex)); ++ ++ if (list_empty(&c->empty_list)) ++ return NULL; ++ ++ lprops = list_entry(c->empty_list.next, struct ubifs_lprops, list); ++ ubifs_assert(!(lprops->flags & LPROPS_TAKEN)); ++ ubifs_assert(!(lprops->flags & LPROPS_INDEX)); ++ ubifs_assert(lprops->free == c->leb_size); ++ return lprops; ++} ++ ++/** ++ * ubifs_fast_find_freeable - try to find a freeable LEB quickly. ++ * @c: the UBIFS file-system description object ++ * ++ * This function returns LEB properties for a freeable LEB or %NULL if the ++ * function is unable to find a freeable LEB quickly. ++ */ ++const struct ubifs_lprops *ubifs_fast_find_freeable(struct ubifs_info *c) ++{ ++ struct ubifs_lprops *lprops; ++ ++ ubifs_assert(mutex_is_locked(&c->lp_mutex)); ++ ++ if (list_empty(&c->freeable_list)) ++ return NULL; ++ ++ lprops = list_entry(c->freeable_list.next, struct ubifs_lprops, list); ++ ubifs_assert(!(lprops->flags & LPROPS_TAKEN)); ++ ubifs_assert(!(lprops->flags & LPROPS_INDEX)); ++ ubifs_assert(lprops->free + lprops->dirty == c->leb_size); ++ ubifs_assert(c->freeable_cnt > 0); ++ return lprops; ++} ++ ++/** ++ * ubifs_fast_find_frdi_idx - try to find a freeable index LEB quickly. ++ * @c: the UBIFS file-system description object ++ * ++ * This function returns LEB properties for a freeable index LEB or %NULL if the ++ * function is unable to find a freeable index LEB quickly. ++ */ ++const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c) ++{ ++ struct ubifs_lprops *lprops; ++ ++ ubifs_assert(mutex_is_locked(&c->lp_mutex)); ++ ++ if (list_empty(&c->frdi_idx_list)) ++ return NULL; ++ ++ lprops = list_entry(c->frdi_idx_list.next, struct ubifs_lprops, list); ++ ubifs_assert(!(lprops->flags & LPROPS_TAKEN)); ++ ubifs_assert((lprops->flags & LPROPS_INDEX)); ++ ubifs_assert(lprops->free + lprops->dirty == c->leb_size); ++ return lprops; ++} ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ ++/** ++ * dbg_check_cats - check category heaps and lists. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int dbg_check_cats(struct ubifs_info *c) ++{ ++ struct ubifs_lprops *lprops; ++ struct list_head *pos; ++ int i, cat; ++ ++ if (!(ubifs_chk_flags & (UBIFS_CHK_GEN | UBIFS_CHK_LPROPS))) ++ return 0; ++ ++ list_for_each_entry(lprops, &c->empty_list, list) { ++ if (lprops->free != c->leb_size) { ++ ubifs_err("non-empty LEB %d on empty list " ++ "(free %d dirty %d flags %d)", lprops->lnum, ++ lprops->free, lprops->dirty, lprops->flags); ++ return -EINVAL; ++ } ++ if (lprops->flags & LPROPS_TAKEN) { ++ ubifs_err("taken LEB %d on empty list " ++ "(free %d dirty %d flags %d)", lprops->lnum, ++ lprops->free, lprops->dirty, lprops->flags); ++ return -EINVAL; ++ } ++ } ++ ++ i = 0; ++ list_for_each_entry(lprops, &c->freeable_list, list) { ++ if (lprops->free + lprops->dirty != c->leb_size) { ++ ubifs_err("non-freeable LEB %d on freeable list " ++ "(free %d dirty %d flags %d)", lprops->lnum, ++ lprops->free, lprops->dirty, lprops->flags); ++ return -EINVAL; ++ } ++ if (lprops->flags & LPROPS_TAKEN) { ++ ubifs_err("taken LEB %d on freeable list " ++ "(free %d dirty %d flags %d)", lprops->lnum, ++ lprops->free, lprops->dirty, lprops->flags); ++ return -EINVAL; ++ } ++ i += 1; ++ } ++ if (i != c->freeable_cnt) { ++ ubifs_err("freeable list count %d expected %d", i, ++ c->freeable_cnt); ++ return -EINVAL; ++ } ++ ++ i = 0; ++ list_for_each(pos, &c->idx_gc) ++ i += 1; ++ if (i != c->idx_gc_cnt) { ++ ubifs_err("idx_gc list count %d expected %d", i, ++ c->idx_gc_cnt); ++ return -EINVAL; ++ } ++ ++ list_for_each_entry(lprops, &c->frdi_idx_list, list) { ++ if (lprops->free + lprops->dirty != c->leb_size) { ++ ubifs_err("non-freeable LEB %d on frdi_idx list " ++ "(free %d dirty %d flags %d)", lprops->lnum, ++ lprops->free, lprops->dirty, lprops->flags); ++ return -EINVAL; ++ } ++ if (lprops->flags & LPROPS_TAKEN) { ++ ubifs_err("taken LEB %d on frdi_idx list " ++ "(free %d dirty %d flags %d)", lprops->lnum, ++ lprops->free, lprops->dirty, lprops->flags); ++ return -EINVAL; ++ } ++ if (!(lprops->flags & LPROPS_INDEX)) { ++ ubifs_err("non-index LEB %d on frdi_idx list " ++ "(free %d dirty %d flags %d)", lprops->lnum, ++ lprops->free, lprops->dirty, lprops->flags); ++ return -EINVAL; ++ } ++ } ++ ++ for (cat = 1; cat <= LPROPS_HEAP_CNT; cat++) { ++ struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1]; ++ ++ for (i = 0; i < heap->cnt; i++) { ++ lprops = heap->arr[i]; ++ if (!lprops) { ++ ubifs_err("null ptr in LPT heap cat %d", cat); ++ return -EINVAL; ++ } ++ if (lprops->hpos != i) { ++ ubifs_err("bad ptr in LPT heap cat %d", cat); ++ return -EINVAL; ++ } ++ if (lprops->flags & LPROPS_TAKEN) { ++ ubifs_err("taken LEB in LPT heap cat %d", cat); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat, ++ int add_pos) ++{ ++ int i = 0, j, err = 0; ++ ++ if (!(ubifs_chk_flags & (UBIFS_CHK_GEN | UBIFS_CHK_LPROPS))) ++ return; ++ ++ for (i = 0; i < heap->cnt; i++) { ++ struct ubifs_lprops *lprops = heap->arr[i]; ++ struct ubifs_lprops *lp; ++ ++ if (i != add_pos) ++ if ((lprops->flags & LPROPS_CAT_MASK) != cat) { ++ err = 1; ++ goto out; ++ } ++ if (lprops->hpos != i) { ++ err = 2; ++ goto out; ++ } ++ lp = ubifs_lpt_lookup(c, lprops->lnum); ++ if (IS_ERR(lp)) { ++ err = 3; ++ goto out; ++ } ++ if (lprops != lp) { ++ dbg_msg("lprops %zx lp %zx lprops->lnum %d lp->lnum %d", ++ (size_t)lprops, (size_t)lp, lprops->lnum, ++ lp->lnum); ++ err = 4; ++ goto out; ++ } ++ for (j = 0; j < i; j++) { ++ lp = heap->arr[j]; ++ if (lp == lprops) { ++ err = 5; ++ goto out; ++ } ++ if (lp->lnum == lprops->lnum) { ++ err = 6; ++ goto out; ++ } ++ } ++ } ++out: ++ if (err) { ++ dbg_msg("failed cat %d hpos %d err %d", cat, i, err); ++ dbg_dump_stack(); ++ dbg_dump_heap(c, heap, cat); ++ } ++} ++ ++/** ++ * struct scan_check_data - data provided to scan callback function. ++ * @lst: LEB properties statistics ++ * @err: error code ++ */ ++struct scan_check_data { ++ struct ubifs_lp_stats lst; ++ int err; ++}; ++ ++/** ++ * scan_check_cb - scan callback. ++ * @c: the UBIFS file-system description object ++ * @lp: LEB properties to scan ++ * @in_tree: whether the LEB properties are in main memory ++ * @data: information passed to and from the caller of the scan ++ * ++ * This function returns a code that indicates whether the scan should continue ++ * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree ++ * in main memory (%LPT_SCAN_ADD), or whether the scan should stop ++ * (%LPT_SCAN_STOP). ++ */ ++static int scan_check_cb(struct ubifs_info *c, ++ const struct ubifs_lprops *lp, int in_tree, ++ struct scan_check_data *data) ++{ ++ struct ubifs_scan_leb *sleb; ++ struct ubifs_scan_node *snod; ++ struct ubifs_lp_stats *lst = &data->lst; ++ int cat, lnum = lp->lnum, is_idx = 0, used = 0, free, dirty; ++ ++ cat = lp->flags & LPROPS_CAT_MASK; ++ if (cat != LPROPS_UNCAT) { ++ cat = ubifs_categorize_lprops(c, lp); ++ if (cat != (lp->flags & LPROPS_CAT_MASK)) { ++ ubifs_err("bad LEB category %d expected %d", ++ (lp->flags & LPROPS_CAT_MASK), cat); ++ goto out; ++ } ++ } ++ ++ /* Check lp is on its category list (if it has one) */ ++ if (in_tree) { ++ struct list_head *list = NULL; ++ ++ switch (cat) { ++ case LPROPS_EMPTY: ++ list = &c->empty_list; ++ break; ++ case LPROPS_FREEABLE: ++ list = &c->freeable_list; ++ break; ++ case LPROPS_FRDI_IDX: ++ list = &c->frdi_idx_list; ++ break; ++ case LPROPS_UNCAT: ++ list = &c->uncat_list; ++ break; ++ } ++ if (list) { ++ struct ubifs_lprops *lprops; ++ int found = 0; ++ ++ list_for_each_entry(lprops, list, list) { ++ if (lprops == lp) { ++ found = 1; ++ break; ++ } ++ } ++ if (!found) { ++ ubifs_err("bad LPT list (category %d)", cat); ++ goto out; ++ } ++ } ++ } ++ ++ /* Check lp is on its category heap (if it has one) */ ++ if (in_tree && cat > 0 && cat <= LPROPS_HEAP_CNT) { ++ struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1]; ++ ++ if ((lp->hpos != -1 && heap->arr[lp->hpos]->lnum != lnum) || ++ lp != heap->arr[lp->hpos]) { ++ ubifs_err("bad LPT heap (category %d)", cat); ++ goto out; ++ } ++ } ++ ++ sleb = ubifs_scan(c, lnum, 0, c->dbg_buf); ++ if (IS_ERR(sleb)) { ++ /* ++ * After an unclean unmount, empty and freeable LEBs ++ * may contain garbage. ++ */ ++ if (lp->free == c->leb_size) { ++ ubifs_err("scan errors were in empty LEB " ++ "- continuing checking"); ++ lst->empty_lebs += 1; ++ lst->total_free += c->leb_size; ++ lst->total_dark += calc_dark(c, c->leb_size); ++ return LPT_SCAN_CONTINUE; ++ } ++ ++ if (lp->free + lp->dirty == c->leb_size && ++ !(lp->flags & LPROPS_INDEX)) { ++ ubifs_err("scan errors were in freeable LEB " ++ "- continuing checking"); ++ lst->total_free += lp->free; ++ lst->total_dirty += lp->dirty; ++ lst->total_dark += calc_dark(c, c->leb_size); ++ return LPT_SCAN_CONTINUE; ++ } ++ data->err = PTR_ERR(sleb); ++ return LPT_SCAN_STOP; ++ } ++ ++ is_idx = -1; ++ list_for_each_entry(snod, &sleb->nodes, list) { ++ int found, level = 0; ++ ++ cond_resched(); ++ ++ if (is_idx == -1) ++ is_idx = (snod->type == UBIFS_IDX_NODE) ? 1 : 0; ++ ++ if (is_idx && snod->type != UBIFS_IDX_NODE) { ++ ubifs_err("indexing node in data LEB %d:%d", ++ lnum, snod->offs); ++ goto out_destroy; ++ } ++ ++ if (snod->type == UBIFS_IDX_NODE) { ++ struct ubifs_idx_node *idx = snod->node; ++ ++ key_read(c, ubifs_idx_key(c, idx), &snod->key); ++ level = le16_to_cpu(idx->level); ++ } ++ ++ found = ubifs_tnc_has_node(c, &snod->key, level, lnum, ++ snod->offs, is_idx); ++ if (found) { ++ if (found < 0) ++ goto out_destroy; ++ used += ALIGN(snod->len, 8); ++ } ++ } ++ ++ free = c->leb_size - sleb->endpt; ++ dirty = sleb->endpt - used; ++ ++ if (free > c->leb_size || free < 0 || dirty > c->leb_size || ++ dirty < 0) { ++ ubifs_err("bad calculated accounting for LEB %d: " ++ "free %d, dirty %d", lnum, free, dirty); ++ goto out_destroy; ++ } ++ ++ if (lp->free + lp->dirty == c->leb_size && ++ free + dirty == c->leb_size) ++ if ((is_idx && !(lp->flags & LPROPS_INDEX)) || ++ (!is_idx && free == c->leb_size)) { ++ /* ++ * Empty or freeable LEBs could contain index ++ * nodes from an uncompleted commit due to an ++ * unclean unmount. Or they could be empty for ++ * the same reason. ++ */ ++ free = lp->free; ++ dirty = lp->dirty; ++ is_idx = 0; ++ } ++ ++ if (is_idx && lp->free + lp->dirty == free + dirty && ++ lnum != c->ihead_lnum) { ++ /* ++ * After an unclean unmount, an index LEB could have a different ++ * amount of free space than the value recorded by lprops. That ++ * is because the in-the-gaps method may use free space or ++ * create free space (as a side-effect of using ubi_leb_change ++ * and not writing the whole LEB). The incorrect free space ++ * value is not a problem because the index is only ever ++ * allocated empty LEBs, so there will never be an attempt to ++ * write to the free space at the end of an index LEB - except ++ * by the in-the-gaps method for which it is not a problem. ++ */ ++ free = lp->free; ++ dirty = lp->dirty; ++ } ++ ++ if (lp->free != free || lp->dirty != dirty) ++ goto out_print; ++ ++ if (is_idx && !(lp->flags & LPROPS_INDEX)) { ++ if (free == c->leb_size) ++ /* Free but not unmapped LEB, it's fine */ ++ is_idx = 0; ++ else { ++ ubifs_err("indexing node without indexing " ++ "flag"); ++ goto out_print; ++ } ++ } ++ ++ if (!is_idx && (lp->flags & LPROPS_INDEX)) { ++ ubifs_err("data node with indexing flag"); ++ goto out_print; ++ } ++ ++ if (free == c->leb_size) ++ lst->empty_lebs += 1; ++ ++ if (is_idx) ++ lst->idx_lebs += 1; ++ ++ if (!(lp->flags & LPROPS_INDEX)) ++ lst->total_used += c->leb_size - free - dirty; ++ lst->total_free += free; ++ lst->total_dirty += dirty; ++ ++ if (!(lp->flags & LPROPS_INDEX)) { ++ int spc = free + dirty; ++ ++ if (spc < c->dead_wm) ++ lst->total_dead += spc; ++ else ++ lst->total_dark += calc_dark(c, spc); ++ } ++ ++ ubifs_scan_destroy(sleb); ++ ++ return LPT_SCAN_CONTINUE; ++ ++out_print: ++ ubifs_err("bad accounting of LEB %d: free %d, dirty %d flags %#x, " ++ "should be free %d, dirty %d", ++ lnum, lp->free, lp->dirty, lp->flags, free, dirty); ++ dbg_dump_leb(c, lnum); ++out_destroy: ++ ubifs_scan_destroy(sleb); ++out: ++ data->err = -EINVAL; ++ return LPT_SCAN_STOP; ++} ++ ++/** ++ * dbg_check_lprops - check all LEB properties. ++ * @c: UBIFS file-system description object ++ * ++ * This function checks all LEB properties and makes sure they are all correct. ++ * It returns zero if everything is fine, %-EINVAL if there is an inconsistency ++ * and other negative error codes in case of other errors. This function is ++ * called while the file system is locked (because of commit start), so no ++ * additional locking is required. Note that locking the LPT mutex would cause ++ * a circular lock dependency with the TNC mutex. ++ */ ++int dbg_check_lprops(struct ubifs_info *c) ++{ ++ int i, err; ++ struct scan_check_data data; ++ struct ubifs_lp_stats *lst = &data.lst; ++ ++ if (!(ubifs_chk_flags & UBIFS_CHK_LPROPS)) ++ return 0; ++ ++ /* ++ * As we are going to scan the media, the write buffers have to be ++ * synchronized. ++ */ ++ for (i = 0; i < c->jhead_cnt; i++) { ++ err = ubifs_wbuf_sync(&c->jheads[i].wbuf); ++ if (err) ++ return err; ++ } ++ ++ memset(lst, 0, sizeof(struct ubifs_lp_stats)); ++ ++ data.err = 0; ++ err = ubifs_lpt_scan_nolock(c, c->main_first, c->leb_cnt - 1, ++ (ubifs_lpt_scan_callback)scan_check_cb, ++ &data); ++ if (err && err != -ENOSPC) ++ goto out; ++ if (data.err) { ++ err = data.err; ++ goto out; ++ } ++ ++ if (lst->empty_lebs != c->lst.empty_lebs || ++ lst->idx_lebs != c->lst.idx_lebs || ++ lst->total_free != c->lst.total_free || ++ lst->total_dirty != c->lst.total_dirty || ++ lst->total_used != c->lst.total_used) { ++ ubifs_err("bad overall accounting"); ++ ubifs_err("calculated: empty_lebs %d, idx_lebs %d, " ++ "total_free %lld, total_dirty %lld, total_used %lld", ++ lst->empty_lebs, lst->idx_lebs, lst->total_free, ++ lst->total_dirty, lst->total_used); ++ ubifs_err("read from lprops: empty_lebs %d, idx_lebs %d, " ++ "total_free %lld, total_dirty %lld, total_used %lld", ++ c->lst.empty_lebs, c->lst.idx_lebs, c->lst.total_free, ++ c->lst.total_dirty, c->lst.total_used); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (lst->total_dead != c->lst.total_dead || ++ lst->total_dark != c->lst.total_dark) { ++ ubifs_err("bad dead/dark space accounting"); ++ ubifs_err("calculated: total_dead %lld, total_dark %lld", ++ lst->total_dead, lst->total_dark); ++ ubifs_err("read from lprops: total_dead %lld, total_dark %lld", ++ c->lst.total_dead, c->lst.total_dark); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = dbg_check_cats(c); ++out: ++ return err; ++} ++ ++#endif /* CONFIG_UBIFS_FS_DEBUG */ +diff -urN linux-2.6.24.7.old/fs/ubifs/lpt.c linux-2.6.24.7/fs/ubifs/lpt.c +--- linux-2.6.24.7.old/fs/ubifs/lpt.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/lpt.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,2279 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy (Битюцкий Ðртём) ++ */ ++ ++/* ++ * This file implements the LEB properties tree (LPT) area. The LPT area ++ * contains the LEB properties tree, a table of LPT area eraseblocks (ltab), and ++ * (for the "big" model) a table of saved LEB numbers (lsave). The LPT area sits ++ * between the log and the orphan area. ++ * ++ * The LPT area is like a miniature self-contained file system. It is required ++ * that it never runs out of space, is fast to access and update, and scales ++ * logarithmically. The LEB properties tree is implemented as a wandering tree ++ * much like the TNC, and the LPT area has its own garbage collection. ++ * ++ * The LPT has two slightly different forms called the "small model" and the ++ * "big model". The small model is used when the entire LEB properties table ++ * can be written into a single eraseblock. In that case, garbage collection ++ * consists of just writing the whole table, which therefore makes all other ++ * eraseblocks reusable. In the case of the big model, dirty eraseblocks are ++ * selected for garbage collection, which consists are marking the nodes in ++ * that LEB as dirty, and then only the dirty nodes are written out. Also, in ++ * the case of the big model, a table of LEB numbers is saved so that the entire ++ * LPT does not to be scanned looking for empty eraseblocks when UBIFS is first ++ * mounted. ++ */ ++ ++#include ++#include "ubifs.h" ++ ++/** ++ * do_calc_lpt_geom - calculate sizes for the LPT area. ++ * @c: the UBIFS file-system description object ++ * ++ * Calculate the sizes of LPT bit fields, nodes, and tree, based on the ++ * properties of the flash and whether LPT is "big" (c->big_lpt). ++ */ ++static void do_calc_lpt_geom(struct ubifs_info *c) ++{ ++ int i, n, bits, per_leb_wastage, max_pnode_cnt; ++ long long sz, tot_wastage; ++ ++ n = c->main_lebs + c->max_leb_cnt - c->leb_cnt; ++ max_pnode_cnt = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT); ++ ++ c->lpt_hght = 1; ++ n = UBIFS_LPT_FANOUT; ++ while (n < max_pnode_cnt) { ++ c->lpt_hght += 1; ++ n <<= UBIFS_LPT_FANOUT_SHIFT; ++ } ++ ++ c->pnode_cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT); ++ ++ n = DIV_ROUND_UP(c->pnode_cnt, UBIFS_LPT_FANOUT); ++ c->nnode_cnt = n; ++ for (i = 1; i < c->lpt_hght; i++) { ++ n = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT); ++ c->nnode_cnt += n; ++ } ++ ++ c->space_bits = fls(c->leb_size) - 3; ++ c->lpt_lnum_bits = fls(c->lpt_lebs); ++ c->lpt_offs_bits = fls(c->leb_size - 1); ++ c->lpt_spc_bits = fls(c->leb_size); ++ ++ n = DIV_ROUND_UP(c->max_leb_cnt, UBIFS_LPT_FANOUT); ++ c->pcnt_bits = fls(n - 1); ++ ++ c->lnum_bits = fls(c->max_leb_cnt - 1); ++ ++ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + ++ (c->big_lpt ? c->pcnt_bits : 0) + ++ (c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT; ++ c->pnode_sz = (bits + 7) / 8; ++ ++ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + ++ (c->big_lpt ? c->pcnt_bits : 0) + ++ (c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT; ++ c->nnode_sz = (bits + 7) / 8; ++ ++ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + ++ c->lpt_lebs * c->lpt_spc_bits * 2; ++ c->ltab_sz = (bits + 7) / 8; ++ ++ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + ++ c->lnum_bits * c->lsave_cnt; ++ c->lsave_sz = (bits + 7) / 8; ++ ++ /* Calculate the minimum LPT size */ ++ c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz; ++ c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz; ++ c->lpt_sz += c->ltab_sz; ++ c->lpt_sz += c->lsave_sz; ++ ++ /* Add wastage */ ++ sz = c->lpt_sz; ++ per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz); ++ sz += per_leb_wastage; ++ tot_wastage = per_leb_wastage; ++ while (sz > c->leb_size) { ++ sz += per_leb_wastage; ++ sz -= c->leb_size; ++ tot_wastage += per_leb_wastage; ++ } ++ tot_wastage += ALIGN(sz, c->min_io_size) - sz; ++ c->lpt_sz += tot_wastage; ++} ++ ++/** ++ * ubifs_calc_lpt_geom - calculate and check sizes for the LPT area. ++ * @c: the UBIFS file-system description object ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_calc_lpt_geom(struct ubifs_info *c) ++{ ++ int lebs_needed; ++ uint64_t sz; ++ ++ do_calc_lpt_geom(c); ++ ++ /* Verify that lpt_lebs is big enough */ ++ sz = c->lpt_sz * 2; /* Must have at least 2 times the size */ ++ sz += c->leb_size - 1; ++ do_div(sz, c->leb_size); ++ lebs_needed = sz; ++ if (lebs_needed > c->lpt_lebs) { ++ ubifs_err("too few LPT LEBs"); ++ return -EINVAL; ++ } ++ ++ /* Verify that ltab fits in a single LEB (since ltab is a single node */ ++ if (c->ltab_sz > c->leb_size) { ++ ubifs_err("LPT ltab too big"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * calc_dflt_lpt_geom - calculate default LPT geometry. ++ * @c: the UBIFS file-system description object ++ * @main_lebs: number of main area LEBs is passed and returned here ++ * @big_lpt: whether the LPT area is "big" is returned here ++ * ++ * The size of the LPT area depends on parameters that themselves are dependent ++ * on the size of the LPT area. This function, successively recalculates the LPT ++ * area geometry until the parameters and resultant geometry are consistent. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, ++ int *big_lpt) ++{ ++ int i, lebs_needed; ++ uint64_t sz; ++ ++ /* Start by assuming the minimum number of LPT LEBs */ ++ c->lpt_lebs = UBIFS_MIN_LPT_LEBS; ++ c->main_lebs = *main_lebs - c->lpt_lebs; ++ if (c->main_lebs <= 0) ++ return -EINVAL; ++ ++ /* And assume we will use the small LPT model */ ++ c->big_lpt = 0; ++ ++ /* ++ * Calculate the geometry based on assumptions above and then see if it ++ * makes sense ++ */ ++ do_calc_lpt_geom(c); ++ ++ /* Small LPT model must have lpt_sz < leb_size */ ++ if (c->lpt_sz > c->leb_size) { ++ /* Nope, so try again using big LPT model */ ++ c->big_lpt = 1; ++ do_calc_lpt_geom(c); ++ } ++ ++ /* Now check there are enough LPT LEBs */ ++ for (i = 0; i < 64 ; i++) { ++ sz = c->lpt_sz * 4; /* Allow 4 times the size */ ++ sz += c->leb_size - 1; ++ do_div(sz, c->leb_size); ++ lebs_needed = sz; ++ if (lebs_needed > c->lpt_lebs) { ++ /* Not enough LPT LEBs so try again with more */ ++ c->lpt_lebs = lebs_needed; ++ c->main_lebs = *main_lebs - c->lpt_lebs; ++ if (c->main_lebs <= 0) ++ return -EINVAL; ++ do_calc_lpt_geom(c); ++ continue; ++ } ++ if (c->ltab_sz > c->leb_size) { ++ ubifs_err("LPT ltab too big"); ++ return -EINVAL; ++ } ++ *main_lebs = c->main_lebs; ++ *big_lpt = c->big_lpt; ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++/** ++ * pack_bits - pack bit fields end-to-end. ++ * @addr: address at which to pack (passed and next address returned) ++ * @pos: bit position at which to pack (passed and next position returned) ++ * @val: value to pack ++ * @nrbits: number of bits of value to pack (1-32) ++ */ ++static void pack_bits(uint8_t **addr, int *pos, uint32_t val, int nrbits) ++{ ++ uint8_t *p = *addr; ++ int b = *pos; ++ ++ ubifs_assert(nrbits > 0); ++ ubifs_assert(nrbits <= 32); ++ ubifs_assert(*pos >= 0); ++ ubifs_assert(*pos < 8); ++ ubifs_assert((val >> nrbits) == 0 || nrbits == 32); ++ if (b) { ++ *p |= ((uint8_t)val) << b; ++ nrbits += b; ++ if (nrbits > 8) { ++ *++p = (uint8_t)(val >>= (8 - b)); ++ if (nrbits > 16) { ++ *++p = (uint8_t)(val >>= 8); ++ if (nrbits > 24) { ++ *++p = (uint8_t)(val >>= 8); ++ if (nrbits > 32) ++ *++p = (uint8_t)(val >>= 8); ++ } ++ } ++ } ++ } else { ++ *p = (uint8_t)val; ++ if (nrbits > 8) { ++ *++p = (uint8_t)(val >>= 8); ++ if (nrbits > 16) { ++ *++p = (uint8_t)(val >>= 8); ++ if (nrbits > 24) ++ *++p = (uint8_t)(val >>= 8); ++ } ++ } ++ } ++ b = nrbits & 7; ++ if (b == 0) ++ p++; ++ *addr = p; ++ *pos = b; ++} ++ ++/** ++ * ubifs_unpack_bits - unpack bit fields. ++ * @addr: address at which to unpack (passed and next address returned) ++ * @pos: bit position at which to unpack (passed and next position returned) ++ * @nrbits: number of bits of value to unpack (1-32) ++ * ++ * This functions returns the value unpacked. ++ */ ++uint32_t ubifs_unpack_bits(uint8_t **addr, int *pos, int nrbits) ++{ ++ const int k = 32 - nrbits; ++ uint8_t *p = *addr; ++ int b = *pos; ++ uint32_t val; ++ ++ ubifs_assert(nrbits > 0); ++ ubifs_assert(nrbits <= 32); ++ ubifs_assert(*pos >= 0); ++ ubifs_assert(*pos < 8); ++ if (b) { ++ val = p[1] | ((uint32_t)p[2] << 8) | ((uint32_t)p[3] << 16) | ++ ((uint32_t)p[4] << 24); ++ val <<= (8 - b); ++ val |= *p >> b; ++ nrbits += b; ++ } else ++ val = p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ++ ((uint32_t)p[3] << 24); ++ val <<= k; ++ val >>= k; ++ b = nrbits & 7; ++ p += nrbits / 8; ++ *addr = p; ++ *pos = b; ++ ubifs_assert((val >> nrbits) == 0 || nrbits - b == 32); ++ return val; ++} ++ ++/** ++ * ubifs_pack_pnode - pack all the bit fields of a pnode. ++ * @c: UBIFS file-system description object ++ * @buf: buffer into which to pack ++ * @pnode: pnode to pack ++ */ ++void ubifs_pack_pnode(struct ubifs_info *c, void *buf, ++ struct ubifs_pnode *pnode) ++{ ++ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; ++ int i, pos = 0; ++ uint16_t crc; ++ ++ pack_bits(&addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS); ++ if (c->big_lpt) ++ pack_bits(&addr, &pos, pnode->num, c->pcnt_bits); ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ pack_bits(&addr, &pos, pnode->lprops[i].free >> 3, ++ c->space_bits); ++ pack_bits(&addr, &pos, pnode->lprops[i].dirty >> 3, ++ c->space_bits); ++ if (pnode->lprops[i].flags & LPROPS_INDEX) ++ pack_bits(&addr, &pos, 1, 1); ++ else ++ pack_bits(&addr, &pos, 0, 1); ++ } ++ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, ++ c->pnode_sz - UBIFS_LPT_CRC_BYTES); ++ addr = buf; ++ pos = 0; ++ pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); ++} ++ ++/** ++ * ubifs_pack_nnode - pack all the bit fields of a nnode. ++ * @c: UBIFS file-system description object ++ * @buf: buffer into which to pack ++ * @nnode: nnode to pack ++ */ ++void ubifs_pack_nnode(struct ubifs_info *c, void *buf, ++ struct ubifs_nnode *nnode) ++{ ++ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; ++ int i, pos = 0; ++ uint16_t crc; ++ ++ pack_bits(&addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS); ++ if (c->big_lpt) ++ pack_bits(&addr, &pos, nnode->num, c->pcnt_bits); ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ int lnum = nnode->nbranch[i].lnum; ++ ++ if (lnum == 0) ++ lnum = c->lpt_last + 1; ++ pack_bits(&addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits); ++ pack_bits(&addr, &pos, nnode->nbranch[i].offs, ++ c->lpt_offs_bits); ++ } ++ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, ++ c->nnode_sz - UBIFS_LPT_CRC_BYTES); ++ addr = buf; ++ pos = 0; ++ pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); ++} ++ ++/** ++ * ubifs_pack_ltab - pack the LPT's own lprops table. ++ * @c: UBIFS file-system description object ++ * @buf: buffer into which to pack ++ * @ltab: LPT's own lprops table to pack ++ */ ++void ubifs_pack_ltab(struct ubifs_info *c, void *buf, ++ struct ubifs_lpt_lprops *ltab) ++{ ++ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; ++ int i, pos = 0; ++ uint16_t crc; ++ ++ pack_bits(&addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS); ++ for (i = 0; i < c->lpt_lebs; i++) { ++ pack_bits(&addr, &pos, ltab[i].free, c->lpt_spc_bits); ++ pack_bits(&addr, &pos, ltab[i].dirty, c->lpt_spc_bits); ++ } ++ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, ++ c->ltab_sz - UBIFS_LPT_CRC_BYTES); ++ addr = buf; ++ pos = 0; ++ pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); ++} ++ ++/** ++ * ubifs_pack_lsave - pack the LPT's save table. ++ * @c: UBIFS file-system description object ++ * @buf: buffer into which to pack ++ * @lsave: LPT's save table to pack ++ */ ++void ubifs_pack_lsave(struct ubifs_info *c, void *buf, int *lsave) ++{ ++ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; ++ int i, pos = 0; ++ uint16_t crc; ++ ++ pack_bits(&addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS); ++ for (i = 0; i < c->lsave_cnt; i++) ++ pack_bits(&addr, &pos, lsave[i], c->lnum_bits); ++ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, ++ c->lsave_sz - UBIFS_LPT_CRC_BYTES); ++ addr = buf; ++ pos = 0; ++ pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); ++} ++ ++/** ++ * ubifs_add_lpt_dirt - add dirty space to LPT LEB properties. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number to which to add dirty space ++ * @dirty: amount of dirty space to add ++ */ ++void ubifs_add_lpt_dirt(struct ubifs_info *c, int lnum, int dirty) ++{ ++ if (!dirty || !lnum) ++ return; ++ dbg_lp("LEB %d add %d to %d", ++ lnum, dirty, c->ltab[lnum - c->lpt_first].dirty); ++ ubifs_assert(lnum >= c->lpt_first && lnum <= c->lpt_last); ++ c->ltab[lnum - c->lpt_first].dirty += dirty; ++} ++ ++/** ++ * set_ltab - set LPT LEB properties. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number ++ * @free: amount of free space ++ * @dirty: amount of dirty space ++ */ ++static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty) ++{ ++ dbg_lp("LEB %d free %d dirty %d to %d %d", ++ lnum, c->ltab[lnum - c->lpt_first].free, ++ c->ltab[lnum - c->lpt_first].dirty, free, dirty); ++ ubifs_assert(lnum >= c->lpt_first && lnum <= c->lpt_last); ++ c->ltab[lnum - c->lpt_first].free = free; ++ c->ltab[lnum - c->lpt_first].dirty = dirty; ++} ++ ++/** ++ * ubifs_add_nnode_dirt - add dirty space to LPT LEB properties. ++ * @c: UBIFS file-system description object ++ * @nnode: nnode for which to add dirt ++ */ ++void ubifs_add_nnode_dirt(struct ubifs_info *c, struct ubifs_nnode *nnode) ++{ ++ struct ubifs_nnode *np = nnode->parent; ++ ++ if (np) ++ ubifs_add_lpt_dirt(c, np->nbranch[nnode->iip].lnum, ++ c->nnode_sz); ++ else { ++ ubifs_add_lpt_dirt(c, c->lpt_lnum, c->nnode_sz); ++ if (!(c->lpt_drty_flgs & LTAB_DIRTY)) { ++ c->lpt_drty_flgs |= LTAB_DIRTY; ++ ubifs_add_lpt_dirt(c, c->ltab_lnum, c->ltab_sz); ++ } ++ } ++} ++ ++/** ++ * add_pnode_dirt - add dirty space to LPT LEB properties. ++ * @c: UBIFS file-system description object ++ * @pnode: pnode for which to add dirt ++ */ ++static void add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode) ++{ ++ ubifs_add_lpt_dirt(c, pnode->parent->nbranch[pnode->iip].lnum, ++ c->pnode_sz); ++} ++ ++/** ++ * calc_nnode_num - calculate nnode number. ++ * @row: the row in the tree (root is zero) ++ * @col: the column in the row (leftmost is zero) ++ * ++ * The nnode number is a number that uniquely identifies a nnode and can be used ++ * easily to traverse the tree from the root to that nnode. ++ * ++ * This function calculates and returns the nnode number for the nnode at @row ++ * and @col. ++ */ ++static int calc_nnode_num(int row, int col) ++{ ++ int num, bits; ++ ++ num = 1; ++ while (row--) { ++ bits = (col & (UBIFS_LPT_FANOUT - 1)); ++ col >>= UBIFS_LPT_FANOUT_SHIFT; ++ num <<= UBIFS_LPT_FANOUT_SHIFT; ++ num |= bits; ++ } ++ return num; ++} ++ ++/** ++ * calc_nnode_num_from_parent - calculate nnode number. ++ * @c: UBIFS file-system description object ++ * @parent: parent nnode ++ * @iip: index in parent ++ * ++ * The nnode number is a number that uniquely identifies a nnode and can be used ++ * easily to traverse the tree from the root to that nnode. ++ * ++ * This function calculates and returns the nnode number based on the parent's ++ * nnode number and the index in parent. ++ */ ++static int calc_nnode_num_from_parent(struct ubifs_info *c, ++ struct ubifs_nnode *parent, int iip) ++{ ++ int num, shft; ++ ++ if (!parent) ++ return 1; ++ shft = (c->lpt_hght - parent->level) * UBIFS_LPT_FANOUT_SHIFT; ++ num = parent->num ^ (1 << shft); ++ num |= (UBIFS_LPT_FANOUT + iip) << shft; ++ return num; ++} ++ ++/** ++ * calc_pnode_num_from_parent - calculate pnode number. ++ * @c: UBIFS file-system description object ++ * @parent: parent nnode ++ * @iip: index in parent ++ * ++ * The pnode number is a number that uniquely identifies a pnode and can be used ++ * easily to traverse the tree from the root to that pnode. ++ * ++ * This function calculates and returns the pnode number based on the parent's ++ * nnode number and the index in parent. ++ */ ++static int calc_pnode_num_from_parent(struct ubifs_info *c, ++ struct ubifs_nnode *parent, int iip) ++{ ++ int i, n = c->lpt_hght - 1, pnum = parent->num, num = 0; ++ ++ for (i = 0; i < n; i++) { ++ num <<= UBIFS_LPT_FANOUT_SHIFT; ++ num |= pnum & (UBIFS_LPT_FANOUT - 1); ++ pnum >>= UBIFS_LPT_FANOUT_SHIFT; ++ } ++ num <<= UBIFS_LPT_FANOUT_SHIFT; ++ num |= iip; ++ return num; ++} ++ ++/** ++ * ubifs_create_dflt_lpt - create default LPT. ++ * @c: UBIFS file-system description object ++ * @main_lebs: number of main area LEBs is passed and returned here ++ * @lpt_first: LEB number of first LPT LEB ++ * @lpt_lebs: number of LEBs for LPT is passed and returned here ++ * @big_lpt: use big LPT model is passed and returned here ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first, ++ int *lpt_lebs, int *big_lpt) ++{ ++ int lnum, err = 0, node_sz, iopos, i, j, cnt, len, alen, row; ++ int blnum, boffs, bsz, bcnt; ++ struct ubifs_pnode *pnode = NULL; ++ struct ubifs_nnode *nnode = NULL; ++ void *buf = NULL, *p; ++ struct ubifs_lpt_lprops *ltab = NULL; ++ int *lsave = NULL; ++ ++ err = calc_dflt_lpt_geom(c, main_lebs, big_lpt); ++ if (err) ++ return err; ++ *lpt_lebs = c->lpt_lebs; ++ ++ /* Needed by 'ubifs_pack_nnode()' and 'set_ltab()' */ ++ c->lpt_first = lpt_first; ++ /* Needed by 'set_ltab()' */ ++ c->lpt_last = lpt_first + c->lpt_lebs - 1; ++ /* Needed by 'ubifs_pack_lsave()' */ ++ c->main_first = c->leb_cnt - *main_lebs; ++ ++ lsave = kmalloc(sizeof(int) * c->lsave_cnt, GFP_KERNEL); ++ pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_KERNEL); ++ nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_KERNEL); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ buf = kmalloc(c->leb_size, GFP_KERNEL); ++ ltab = kmalloc(sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs, GFP_KERNEL); ++#else ++ buf = vmalloc(c->leb_size); ++ ltab = vmalloc(sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs); ++#endif ++ if (!pnode || !nnode || !buf || !ltab || !lsave) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ ubifs_assert(!c->ltab); ++ c->ltab = ltab; /* Needed by set_ltab */ ++ ++ /* Initialize LPT's own lprops */ ++ for (i = 0; i < c->lpt_lebs; i++) { ++ ltab[i].free = c->leb_size; ++ ltab[i].dirty = 0; ++ ltab[i].tgc = 0; ++ ltab[i].cmt = 0; ++ } ++ ++ lnum = lpt_first; ++ p = buf; ++ /* Number of leaf nodes (pnodes) */ ++ cnt = c->pnode_cnt; ++ ++ /* ++ * The first pnode contains the LEB properties for the LEBs that contain ++ * the root inode node and the root index node of the index tree. ++ */ ++ node_sz = ALIGN(ubifs_idx_node_sz(c, 1), 8); ++ iopos = ALIGN(node_sz, c->min_io_size); ++ pnode->lprops[0].free = c->leb_size - iopos; ++ pnode->lprops[0].dirty = iopos - node_sz; ++ pnode->lprops[0].flags = LPROPS_INDEX; ++ ++ node_sz = UBIFS_INO_NODE_SZ; ++ iopos = ALIGN(node_sz, c->min_io_size); ++ pnode->lprops[1].free = c->leb_size - iopos; ++ pnode->lprops[1].dirty = iopos - node_sz; ++ ++ for (i = 2; i < UBIFS_LPT_FANOUT; i++) ++ pnode->lprops[i].free = c->leb_size; ++ ++ /* Add first pnode */ ++ ubifs_pack_pnode(c, p, pnode); ++ p += c->pnode_sz; ++ len = c->pnode_sz; ++ pnode->num += 1; ++ ++ /* Reset pnode values for remaining pnodes */ ++ pnode->lprops[0].free = c->leb_size; ++ pnode->lprops[0].dirty = 0; ++ pnode->lprops[0].flags = 0; ++ ++ pnode->lprops[1].free = c->leb_size; ++ pnode->lprops[1].dirty = 0; ++ ++ /* ++ * To calculate the internal node branches, we keep information about ++ * the level below. ++ */ ++ blnum = lnum; /* LEB number of level below */ ++ boffs = 0; /* Offset of level below */ ++ bcnt = cnt; /* Number of nodes in level below */ ++ bsz = c->pnode_sz; /* Size of nodes in level below */ ++ ++ /* Add all remaining pnodes */ ++ for (i = 1; i < cnt; i++) { ++ if (len + c->pnode_sz > c->leb_size) { ++ alen = ALIGN(len, c->min_io_size); ++ set_ltab(c, lnum, c->leb_size - alen, alen - len); ++ memset(p, 0xff, alen - len); ++ err = ubi_leb_change(c->ubi, lnum++, buf, alen, ++ UBI_SHORTTERM); ++ if (err) ++ goto out; ++ p = buf; ++ len = 0; ++ } ++ ubifs_pack_pnode(c, p, pnode); ++ p += c->pnode_sz; ++ len += c->pnode_sz; ++ /* ++ * pnodes are simply numbered left to right starting at zero, ++ * which means the pnode number can be used easily to traverse ++ * down the tree to the corresponding pnode. ++ */ ++ pnode->num += 1; ++ } ++ ++ row = 0; ++ for (i = UBIFS_LPT_FANOUT; cnt > i; i <<= UBIFS_LPT_FANOUT_SHIFT) ++ row += 1; ++ /* Add all nnodes, one level at a time */ ++ while (1) { ++ /* Number of internal nodes (nnodes) at next level */ ++ cnt = DIV_ROUND_UP(cnt, UBIFS_LPT_FANOUT); ++ for (i = 0; i < cnt; i++) { ++ if (len + c->nnode_sz > c->leb_size) { ++ alen = ALIGN(len, c->min_io_size); ++ set_ltab(c, lnum, c->leb_size - alen, ++ alen - len); ++ memset(p, 0xff, alen - len); ++ err = ubi_leb_change(c->ubi, lnum++, buf, alen, ++ UBI_SHORTTERM); ++ if (err) ++ goto out; ++ p = buf; ++ len = 0; ++ } ++ /* Only 1 nnode at this level, so it is the root */ ++ if (cnt == 1) { ++ c->lpt_lnum = lnum; ++ c->lpt_offs = len; ++ } ++ /* Set branches to the level below */ ++ for (j = 0; j < UBIFS_LPT_FANOUT; j++) { ++ if (bcnt) { ++ if (boffs + bsz > c->leb_size) { ++ blnum += 1; ++ boffs = 0; ++ } ++ nnode->nbranch[j].lnum = blnum; ++ nnode->nbranch[j].offs = boffs; ++ boffs += bsz; ++ bcnt--; ++ } else { ++ nnode->nbranch[j].lnum = 0; ++ nnode->nbranch[j].offs = 0; ++ } ++ } ++ nnode->num = calc_nnode_num(row, i); ++ ubifs_pack_nnode(c, p, nnode); ++ p += c->nnode_sz; ++ len += c->nnode_sz; ++ } ++ /* Only 1 nnode at this level, so it is the root */ ++ if (cnt == 1) ++ break; ++ /* Update the information about the level below */ ++ bcnt = cnt; ++ bsz = c->nnode_sz; ++ row -= 1; ++ } ++ ++ if (*big_lpt) { ++ /* Need to add LPT's save table */ ++ if (len + c->lsave_sz > c->leb_size) { ++ alen = ALIGN(len, c->min_io_size); ++ set_ltab(c, lnum, c->leb_size - alen, alen - len); ++ memset(p, 0xff, alen - len); ++ err = ubi_leb_change(c->ubi, lnum++, buf, alen, ++ UBI_SHORTTERM); ++ if (err) ++ goto out; ++ p = buf; ++ len = 0; ++ } ++ ++ c->lsave_lnum = lnum; ++ c->lsave_offs = len; ++ ++ for (i = 0; i < c->lsave_cnt && i < *main_lebs; i++) ++ lsave[i] = c->main_first + i; ++ for (; i < c->lsave_cnt; i++) ++ lsave[i] = c->main_first; ++ ++ ubifs_pack_lsave(c, p, lsave); ++ p += c->lsave_sz; ++ len += c->lsave_sz; ++ } ++ ++ /* Need to add LPT's own LEB properties table */ ++ if (len + c->ltab_sz > c->leb_size) { ++ alen = ALIGN(len, c->min_io_size); ++ set_ltab(c, lnum, c->leb_size - alen, alen - len); ++ memset(p, 0xff, alen - len); ++ err = ubi_leb_change(c->ubi, lnum++, buf, alen, UBI_SHORTTERM); ++ if (err) ++ goto out; ++ p = buf; ++ len = 0; ++ } ++ ++ c->ltab_lnum = lnum; ++ c->ltab_offs = len; ++ ++ /* Update ltab before packing it */ ++ len += c->ltab_sz; ++ alen = ALIGN(len, c->min_io_size); ++ set_ltab(c, lnum, c->leb_size - alen, alen - len); ++ ++ ubifs_pack_ltab(c, p, ltab); ++ p += c->ltab_sz; ++ ++ /* Write remaining buffer */ ++ memset(p, 0xff, alen - len); ++ err = ubi_leb_change(c->ubi, lnum, buf, alen, UBI_SHORTTERM); ++ if (err) ++ goto out; ++ ++ c->nhead_lnum = lnum; ++ c->nhead_offs = ALIGN(len, c->min_io_size); ++ ++ dbg_lp("space_bits %d", c->space_bits); ++ dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits); ++ dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits); ++ dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits); ++ dbg_lp("pcnt_bits %d", c->pcnt_bits); ++ dbg_lp("lnum_bits %d", c->lnum_bits); ++ dbg_lp("pnode_sz %d", c->pnode_sz); ++ dbg_lp("nnode_sz %d", c->nnode_sz); ++ dbg_lp("ltab_sz %d", c->ltab_sz); ++ dbg_lp("lsave_sz %d", c->lsave_sz); ++ dbg_lp("lsave_cnt %d", c->lsave_cnt); ++ dbg_lp("lpt_hght %d", c->lpt_hght); ++ dbg_lp("big_lpt %d", c->big_lpt); ++ dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); ++ dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); ++ dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); ++ if (c->big_lpt) ++ dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs); ++out: ++ c->ltab = NULL; ++ kfree(lsave); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(ltab); ++ kfree(buf); ++#else ++ vfree(ltab); ++ vfree(buf); ++#endif ++ kfree(nnode); ++ kfree(pnode); ++ return err; ++} ++ ++/** ++ * update_cats - add LEB properties of a pnode to LEB category lists and heaps. ++ * @c: UBIFS file-system description object ++ * @pnode: pnode ++ * ++ * When a pnode is loaded into memory, the LEB properties it contains are added, ++ * by this function, to the LEB category lists and heaps. ++ */ ++static void update_cats(struct ubifs_info *c, struct ubifs_pnode *pnode) ++{ ++ int i; ++ ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ int cat = pnode->lprops[i].flags & LPROPS_CAT_MASK; ++ int lnum = pnode->lprops[i].lnum; ++ ++ if (!lnum) ++ return; ++ ubifs_add_to_cat(c, &pnode->lprops[i], cat); ++ } ++} ++ ++/** ++ * replace_cats - add LEB properties of a pnode to LEB category lists and heaps. ++ * @c: UBIFS file-system description object ++ * @old_pnode: pnode copied ++ * @new_pnode: pnode copy ++ * ++ * During commit it is sometimes necessary to copy a pnode ++ * (see dirty_cow_pnode). When that happens, references in ++ * category lists and heaps must be replaced. This function does that. ++ */ ++static void replace_cats(struct ubifs_info *c, struct ubifs_pnode *old_pnode, ++ struct ubifs_pnode *new_pnode) ++{ ++ int i; ++ ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ if (!new_pnode->lprops[i].lnum) ++ return; ++ ubifs_replace_cat(c, &old_pnode->lprops[i], ++ &new_pnode->lprops[i]); ++ } ++} ++ ++/** ++ * check_lpt_crc - check LPT node crc is correct. ++ * @c: UBIFS file-system description object ++ * @buf: buffer containing node ++ * @len: length of node ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int check_lpt_crc(void *buf, int len) ++{ ++ int pos = 0; ++ uint8_t *addr = buf; ++ uint16_t crc, calc_crc; ++ ++ crc = ubifs_unpack_bits(&addr, &pos, UBIFS_LPT_CRC_BITS); ++ calc_crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, ++ len - UBIFS_LPT_CRC_BYTES); ++ if (crc != calc_crc) { ++ ubifs_err("invalid crc in LPT node: crc %hx calc %hx", crc, ++ calc_crc); ++ dbg_dump_stack(); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/** ++ * check_lpt_type - check LPT node type is correct. ++ * @c: UBIFS file-system description object ++ * @addr: address of type bit field is passed and returned updated here ++ * @pos: position of type bit field is passed and returned updated here ++ * @type: expected type ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int check_lpt_type(uint8_t **addr, int *pos, int type) ++{ ++ int node_type; ++ ++ node_type = ubifs_unpack_bits(addr, pos, UBIFS_LPT_TYPE_BITS); ++ if (node_type != type) { ++ ubifs_err("invalid type (%d) in LPT node type %d", node_type, ++ type); ++ dbg_dump_stack(); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/** ++ * unpack_pnode - unpack a pnode. ++ * @c: UBIFS file-system description object ++ * @buf: buffer containing packed pnode to unpack ++ * @pnode: pnode structure to fill ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int unpack_pnode(struct ubifs_info *c, void *buf, ++ struct ubifs_pnode *pnode) ++{ ++ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; ++ int i, pos = 0, err; ++ ++ err = check_lpt_type(&addr, &pos, UBIFS_LPT_PNODE); ++ if (err) ++ return err; ++ if (c->big_lpt) ++ pnode->num = ubifs_unpack_bits(&addr, &pos, c->pcnt_bits); ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ struct ubifs_lprops * const lprops = &pnode->lprops[i]; ++ ++ lprops->free = ubifs_unpack_bits(&addr, &pos, c->space_bits); ++ lprops->free <<= 3; ++ lprops->dirty = ubifs_unpack_bits(&addr, &pos, c->space_bits); ++ lprops->dirty <<= 3; ++ ++ if (ubifs_unpack_bits(&addr, &pos, 1)) ++ lprops->flags = LPROPS_INDEX; ++ else ++ lprops->flags = 0; ++ lprops->flags |= ubifs_categorize_lprops(c, lprops); ++ } ++ err = check_lpt_crc(buf, c->pnode_sz); ++ return err; ++} ++ ++/** ++ * unpack_nnode - unpack a nnode. ++ * @c: UBIFS file-system description object ++ * @buf: buffer containing packed nnode to unpack ++ * @nnode: nnode structure to fill ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int unpack_nnode(struct ubifs_info *c, void *buf, ++ struct ubifs_nnode *nnode) ++{ ++ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; ++ int i, pos = 0, err; ++ ++ err = check_lpt_type(&addr, &pos, UBIFS_LPT_NNODE); ++ if (err) ++ return err; ++ if (c->big_lpt) ++ nnode->num = ubifs_unpack_bits(&addr, &pos, c->pcnt_bits); ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ int lnum; ++ ++ lnum = ubifs_unpack_bits(&addr, &pos, c->lpt_lnum_bits) + ++ c->lpt_first; ++ if (lnum == c->lpt_last + 1) ++ lnum = 0; ++ nnode->nbranch[i].lnum = lnum; ++ nnode->nbranch[i].offs = ubifs_unpack_bits(&addr, &pos, ++ c->lpt_offs_bits); ++ } ++ err = check_lpt_crc(buf, c->nnode_sz); ++ return err; ++} ++ ++/** ++ * unpack_ltab - unpack the LPT's own lprops table. ++ * @c: UBIFS file-system description object ++ * @buf: buffer from which to unpack ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int unpack_ltab(struct ubifs_info *c, void *buf) ++{ ++ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; ++ int i, pos = 0, err; ++ ++ err = check_lpt_type(&addr, &pos, UBIFS_LPT_LTAB); ++ if (err) ++ return err; ++ for (i = 0; i < c->lpt_lebs; i++) { ++ int free = ubifs_unpack_bits(&addr, &pos, c->lpt_spc_bits); ++ int dirty = ubifs_unpack_bits(&addr, &pos, c->lpt_spc_bits); ++ ++ if (free < 0 || free > c->leb_size || dirty < 0 || ++ dirty > c->leb_size || free + dirty > c->leb_size) ++ return -EINVAL; ++ ++ c->ltab[i].free = free; ++ c->ltab[i].dirty = dirty; ++ c->ltab[i].tgc = 0; ++ c->ltab[i].cmt = 0; ++ } ++ err = check_lpt_crc(buf, c->ltab_sz); ++ return err; ++} ++ ++/** ++ * unpack_lsave - unpack the LPT's save table. ++ * @c: UBIFS file-system description object ++ * @buf: buffer from which to unpack ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int unpack_lsave(struct ubifs_info *c, void *buf) ++{ ++ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; ++ int i, pos = 0, err; ++ ++ err = check_lpt_type(&addr, &pos, UBIFS_LPT_LSAVE); ++ if (err) ++ return err; ++ for (i = 0; i < c->lsave_cnt; i++) { ++ int lnum = ubifs_unpack_bits(&addr, &pos, c->lnum_bits); ++ ++ if (lnum < c->main_first || lnum >= c->leb_cnt) ++ return -EINVAL; ++ c->lsave[i] = lnum; ++ } ++ err = check_lpt_crc(buf, c->lsave_sz); ++ return err; ++} ++ ++/** ++ * validate_nnode - validate a nnode. ++ * @c: UBIFS file-system description object ++ * @nnode: nnode to validate ++ * @parent: parent nnode (or NULL for the root nnode) ++ * @iip: index in parent ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int validate_nnode(struct ubifs_info *c, struct ubifs_nnode *nnode, ++ struct ubifs_nnode *parent, int iip) ++{ ++ int i, lvl, max_offs; ++ ++ if (c->big_lpt) { ++ int num = calc_nnode_num_from_parent(c, parent, iip); ++ ++ if (nnode->num != num) ++ return -EINVAL; ++ } ++ lvl = parent ? parent->level - 1 : c->lpt_hght; ++ if (lvl < 1) ++ return -EINVAL; ++ if (lvl == 1) ++ max_offs = c->leb_size - c->pnode_sz; ++ else ++ max_offs = c->leb_size - c->nnode_sz; ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ int lnum = nnode->nbranch[i].lnum; ++ int offs = nnode->nbranch[i].offs; ++ ++ if (lnum == 0) { ++ if (offs != 0) ++ return -EINVAL; ++ continue; ++ } ++ if (lnum < c->lpt_first || lnum > c->lpt_last) ++ return -EINVAL; ++ if (offs < 0 || offs > max_offs) ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/** ++ * validate_pnode - validate a pnode. ++ * @c: UBIFS file-system description object ++ * @pnode: pnode to validate ++ * @parent: parent nnode ++ * @iip: index in parent ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int validate_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, ++ struct ubifs_nnode *parent, int iip) ++{ ++ int i; ++ ++ if (c->big_lpt) { ++ int num = calc_pnode_num_from_parent(c, parent, iip); ++ ++ if (pnode->num != num) ++ return -EINVAL; ++ } ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ int free = pnode->lprops[i].free; ++ int dirty = pnode->lprops[i].dirty; ++ ++ if (free < 0 || free > c->leb_size || free % c->min_io_size || ++ (free & 7)) ++ return -EINVAL; ++ if (dirty < 0 || dirty > c->leb_size || (dirty & 7)) ++ return -EINVAL; ++ if (dirty + free > c->leb_size) ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/** ++ * set_pnode_lnum - set LEB numbers on a pnode. ++ * @c: UBIFS file-system description object ++ * @pnode: pnode to update ++ * ++ * This function calculates the LEB numbers for the LEB properties it contains ++ * based on the pnode number. ++ */ ++static void set_pnode_lnum(struct ubifs_info *c, struct ubifs_pnode *pnode) ++{ ++ int i, lnum; ++ ++ lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + c->main_first; ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ if (lnum >= c->leb_cnt) ++ return; ++ pnode->lprops[i].lnum = lnum++; ++ } ++} ++ ++/** ++ * ubifs_read_nnode - read a nnode from flash and link it to the tree in memory. ++ * @c: UBIFS file-system description object ++ * @parent: parent nnode (or NULL for the root) ++ * @iip: index in parent ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) ++{ ++ struct ubifs_nbranch *branch = NULL; ++ struct ubifs_nnode *nnode = NULL; ++ void *buf = c->lpt_nod_buf; ++ int err, lnum, offs; ++ ++ if (parent) { ++ branch = &parent->nbranch[iip]; ++ lnum = branch->lnum; ++ offs = branch->offs; ++ } else { ++ lnum = c->lpt_lnum; ++ offs = c->lpt_offs; ++ } ++ nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_NOFS); ++ if (!nnode) { ++ err = -ENOMEM; ++ goto out; ++ } ++ if (lnum == 0) { ++ /* ++ * This nnode was not written which just means that the LEB ++ * properties in the subtree below it describe empty LEBs. We ++ * make the nnode as though we had read it, which in fact means ++ * doing almost nothing. ++ */ ++ if (c->big_lpt) ++ nnode->num = calc_nnode_num_from_parent(c, parent, iip); ++ } else { ++ err = ubi_read(c->ubi, lnum, buf, offs, c->nnode_sz); ++ if (err) ++ goto out; ++ err = unpack_nnode(c, buf, nnode); ++ if (err) ++ goto out; ++ } ++ err = validate_nnode(c, nnode, parent, iip); ++ if (err) ++ goto out; ++ if (!c->big_lpt) ++ nnode->num = calc_nnode_num_from_parent(c, parent, iip); ++ if (parent) { ++ branch->nnode = nnode; ++ nnode->level = parent->level - 1; ++ } else { ++ c->nroot = nnode; ++ nnode->level = c->lpt_hght; ++ } ++ nnode->parent = parent; ++ nnode->iip = iip; ++ return 0; ++ ++out: ++ ubifs_err("error %d reading nnode at %d:%d", err, lnum, offs); ++ kfree(nnode); ++ return err; ++} ++ ++/** ++ * read_pnode - read a pnode from flash and link it to the tree in memory. ++ * @c: UBIFS file-system description object ++ * @parent: parent nnode ++ * @iip: index in parent ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) ++{ ++ struct ubifs_nbranch *branch; ++ struct ubifs_pnode *pnode = NULL; ++ void *buf = c->lpt_nod_buf; ++ int err, lnum, offs; ++ ++ branch = &parent->nbranch[iip]; ++ lnum = branch->lnum; ++ offs = branch->offs; ++ pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_NOFS); ++ if (!pnode) { ++ err = -ENOMEM; ++ goto out; ++ } ++ if (lnum == 0) { ++ /* ++ * This pnode was not written which just means that the LEB ++ * properties in it describe empty LEBs. We make the pnode as ++ * though we had read it. ++ */ ++ int i; ++ ++ if (c->big_lpt) ++ pnode->num = calc_pnode_num_from_parent(c, parent, iip); ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ struct ubifs_lprops * const lprops = &pnode->lprops[i]; ++ ++ lprops->free = c->leb_size; ++ lprops->flags = ubifs_categorize_lprops(c, lprops); ++ } ++ } else { ++ err = ubi_read(c->ubi, lnum, buf, offs, c->pnode_sz); ++ if (err) ++ goto out; ++ err = unpack_pnode(c, buf, pnode); ++ if (err) ++ goto out; ++ } ++ err = validate_pnode(c, pnode, parent, iip); ++ if (err) ++ goto out; ++ if (!c->big_lpt) ++ pnode->num = calc_pnode_num_from_parent(c, parent, iip); ++ branch->pnode = pnode; ++ pnode->parent = parent; ++ pnode->iip = iip; ++ set_pnode_lnum(c, pnode); ++ c->pnodes_have += 1; ++ return 0; ++ ++out: ++ ubifs_err("error %d reading pnode at %d:%d", err, lnum, offs); ++ dbg_dump_pnode(c, pnode, parent, iip); ++ dbg_msg("calc num: %d", calc_pnode_num_from_parent(c, parent, iip)); ++ kfree(pnode); ++ return err; ++} ++ ++/** ++ * read_ltab - read LPT's own lprops table. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int read_ltab(struct ubifs_info *c) ++{ ++ int err; ++ void *buf; ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ buf = kmalloc(c->ltab_sz, GFP_KERNEL); ++#else ++ buf = vmalloc(c->ltab_sz); ++#endif ++ if (!buf) ++ return -ENOMEM; ++ err = ubi_read(c->ubi, c->ltab_lnum, buf, c->ltab_offs, c->ltab_sz); ++ if (err) ++ goto out; ++ err = unpack_ltab(c, buf); ++out: ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(buf); ++#else ++ vfree(buf); ++#endif ++ return err; ++} ++ ++/** ++ * read_lsave - read LPT's save table. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int read_lsave(struct ubifs_info *c) ++{ ++ int err, i; ++ void *buf; ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ buf = kmalloc(c->lsave_sz, GFP_KERNEL); ++#else ++ buf = vmalloc(c->lsave_sz); ++#endif ++ if (!buf) ++ return -ENOMEM; ++ err = ubi_read(c->ubi, c->lsave_lnum, buf, c->lsave_offs, c->lsave_sz); ++ if (err) ++ goto out; ++ err = unpack_lsave(c, buf); ++ if (err) ++ goto out; ++ for (i = 0; i < c->lsave_cnt; i++) { ++ int lnum = c->lsave[i]; ++ ++ /* ++ * Due to automatic resizing, the values in the lsave table ++ * could be beyond the volume size - just ignore them. ++ */ ++ if (lnum >= c->leb_cnt) ++ continue; ++ ubifs_lpt_lookup(c, lnum); ++ } ++out: ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(buf); ++#else ++ vfree(buf); ++#endif ++ return err; ++} ++ ++/** ++ * ubifs_get_nnode - get a nnode. ++ * @c: UBIFS file-system description object ++ * @parent: parent nnode (or NULL for the root) ++ * @iip: index in parent ++ * ++ * This function returns a pointer to the nnode on success or a negative error ++ * code on failure. ++ */ ++struct ubifs_nnode *ubifs_get_nnode(struct ubifs_info *c, ++ struct ubifs_nnode *parent, int iip) ++{ ++ struct ubifs_nbranch *branch; ++ struct ubifs_nnode *nnode; ++ int err; ++ ++ branch = &parent->nbranch[iip]; ++ nnode = branch->nnode; ++ if (nnode) ++ return nnode; ++ err = ubifs_read_nnode(c, parent, iip); ++ if (err) ++ return ERR_PTR(err); ++ return branch->nnode; ++} ++ ++/** ++ * ubifs_get_pnode - get a pnode. ++ * @c: UBIFS file-system description object ++ * @parent: parent nnode ++ * @iip: index in parent ++ * ++ * This function returns a pointer to the pnode on success or a negative error ++ * code on failure. ++ */ ++struct ubifs_pnode *ubifs_get_pnode(struct ubifs_info *c, ++ struct ubifs_nnode *parent, int iip) ++{ ++ struct ubifs_nbranch *branch; ++ struct ubifs_pnode *pnode; ++ int err; ++ ++ branch = &parent->nbranch[iip]; ++ pnode = branch->pnode; ++ if (pnode) ++ return pnode; ++ err = read_pnode(c, parent, iip); ++ if (err) ++ return ERR_PTR(err); ++ update_cats(c, branch->pnode); ++ return branch->pnode; ++} ++ ++/** ++ * ubifs_lpt_lookup - lookup LEB properties in the LPT. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number to lookup ++ * ++ * This function returns a pointer to the LEB properties on success or a ++ * negative error code on failure. ++ */ ++struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum) ++{ ++ int err, i, h, iip, shft; ++ struct ubifs_nnode *nnode; ++ struct ubifs_pnode *pnode; ++ ++ if (!c->nroot) { ++ err = ubifs_read_nnode(c, NULL, 0); ++ if (err) ++ return ERR_PTR(err); ++ } ++ nnode = c->nroot; ++ i = lnum - c->main_first; ++ shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT; ++ for (h = 1; h < c->lpt_hght; h++) { ++ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); ++ shft -= UBIFS_LPT_FANOUT_SHIFT; ++ nnode = ubifs_get_nnode(c, nnode, iip); ++ if (IS_ERR(nnode)) ++ return ERR_PTR(PTR_ERR(nnode)); ++ } ++ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); ++ shft -= UBIFS_LPT_FANOUT_SHIFT; ++ pnode = ubifs_get_pnode(c, nnode, iip); ++ if (IS_ERR(pnode)) ++ return ERR_PTR(PTR_ERR(pnode)); ++ iip = (i & (UBIFS_LPT_FANOUT - 1)); ++ dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum, ++ pnode->lprops[iip].free, pnode->lprops[iip].dirty, ++ pnode->lprops[iip].flags); ++ return &pnode->lprops[iip]; ++} ++ ++/** ++ * dirty_cow_nnode - ensure a nnode is not being committed. ++ * @c: UBIFS file-system description object ++ * @nnode: nnode to check ++ * ++ * Returns dirtied nnode on success or negative error code on failure. ++ */ ++static struct ubifs_nnode *dirty_cow_nnode(struct ubifs_info *c, ++ struct ubifs_nnode *nnode) ++{ ++ struct ubifs_nnode *n; ++ int i; ++ ++ if (!test_bit(COW_CNODE, &nnode->flags)) { ++ /* nnode is not being committed */ ++ if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) { ++ c->dirty_nn_cnt += 1; ++ ubifs_add_nnode_dirt(c, nnode); ++ } ++ return nnode; ++ } ++ ++ /* nnode is being committed, so copy it */ ++ n = kmalloc(sizeof(struct ubifs_nnode), GFP_NOFS); ++ if (unlikely(!n)) ++ return ERR_PTR(-ENOMEM); ++ ++ memcpy(n, nnode, sizeof(struct ubifs_nnode)); ++ n->cnext = NULL; ++ __set_bit(DIRTY_CNODE, &n->flags); ++ __clear_bit(COW_CNODE, &n->flags); ++ ++ /* The children now have new parent */ ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ struct ubifs_nbranch *branch = &n->nbranch[i]; ++ ++ if (branch->cnode) ++ branch->cnode->parent = n; ++ } ++ ++ ubifs_assert(!test_bit(OBSOLETE_CNODE, &nnode->flags)); ++ __set_bit(OBSOLETE_CNODE, &nnode->flags); ++ ++ c->dirty_nn_cnt += 1; ++ ubifs_add_nnode_dirt(c, nnode); ++ if (nnode->parent) ++ nnode->parent->nbranch[n->iip].nnode = n; ++ else ++ c->nroot = n; ++ return n; ++} ++ ++/** ++ * dirty_cow_pnode - ensure a pnode is not being committed. ++ * @c: UBIFS file-system description object ++ * @pnode: pnode to check ++ * ++ * Returns dirtied pnode on success or negative error code on failure. ++ */ ++static struct ubifs_pnode *dirty_cow_pnode(struct ubifs_info *c, ++ struct ubifs_pnode *pnode) ++{ ++ struct ubifs_pnode *p; ++ ++ if (!test_bit(COW_CNODE, &pnode->flags)) { ++ /* pnode is not being committed */ ++ if (!test_and_set_bit(DIRTY_CNODE, &pnode->flags)) { ++ c->dirty_pn_cnt += 1; ++ add_pnode_dirt(c, pnode); ++ } ++ return pnode; ++ } ++ ++ /* pnode is being committed, so copy it */ ++ p = kmalloc(sizeof(struct ubifs_pnode), GFP_NOFS); ++ if (unlikely(!p)) ++ return ERR_PTR(-ENOMEM); ++ ++ memcpy(p, pnode, sizeof(struct ubifs_pnode)); ++ p->cnext = NULL; ++ __set_bit(DIRTY_CNODE, &p->flags); ++ __clear_bit(COW_CNODE, &p->flags); ++ replace_cats(c, pnode, p); ++ ++ ubifs_assert(!test_bit(OBSOLETE_CNODE, &pnode->flags)); ++ __set_bit(OBSOLETE_CNODE, &pnode->flags); ++ ++ c->dirty_pn_cnt += 1; ++ add_pnode_dirt(c, pnode); ++ pnode->parent->nbranch[p->iip].pnode = p; ++ return p; ++} ++ ++/** ++ * ubifs_lpt_lookup_dirty - lookup LEB properties in the LPT. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number to lookup ++ * ++ * This function returns a pointer to the LEB properties on success or a ++ * negative error code on failure. ++ */ ++struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum) ++{ ++ int err, i, h, iip, shft; ++ struct ubifs_nnode *nnode; ++ struct ubifs_pnode *pnode; ++ ++ if (!c->nroot) { ++ err = ubifs_read_nnode(c, NULL, 0); ++ if (err) ++ return ERR_PTR(err); ++ } ++ nnode = c->nroot; ++ nnode = dirty_cow_nnode(c, nnode); ++ if (IS_ERR(nnode)) ++ return ERR_PTR(PTR_ERR(nnode)); ++ i = lnum - c->main_first; ++ shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT; ++ for (h = 1; h < c->lpt_hght; h++) { ++ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); ++ shft -= UBIFS_LPT_FANOUT_SHIFT; ++ nnode = ubifs_get_nnode(c, nnode, iip); ++ if (IS_ERR(nnode)) ++ return ERR_PTR(PTR_ERR(nnode)); ++ nnode = dirty_cow_nnode(c, nnode); ++ if (IS_ERR(nnode)) ++ return ERR_PTR(PTR_ERR(nnode)); ++ } ++ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); ++ shft -= UBIFS_LPT_FANOUT_SHIFT; ++ pnode = ubifs_get_pnode(c, nnode, iip); ++ if (IS_ERR(pnode)) ++ return ERR_PTR(PTR_ERR(pnode)); ++ pnode = dirty_cow_pnode(c, pnode); ++ if (IS_ERR(pnode)) ++ return ERR_PTR(PTR_ERR(pnode)); ++ iip = (i & (UBIFS_LPT_FANOUT - 1)); ++ dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum, ++ pnode->lprops[iip].free, pnode->lprops[iip].dirty, ++ pnode->lprops[iip].flags); ++ ubifs_assert(test_bit(DIRTY_CNODE, &pnode->flags)); ++ return &pnode->lprops[iip]; ++} ++ ++/** ++ * lpt_init_rd - initialize the LPT for reading. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int lpt_init_rd(struct ubifs_info *c) ++{ ++ int err, i; ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ c->ltab = kmalloc(sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs, GFP_KERNEL); ++#else ++ c->ltab = vmalloc(sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs); ++#endif ++ if (!c->ltab) ++ return -ENOMEM; ++ ++ i = max_t(int, c->nnode_sz, c->pnode_sz); ++ c->lpt_nod_buf = kmalloc(i, GFP_KERNEL); ++ if (!c->lpt_nod_buf) ++ return -ENOMEM; ++ ++ for (i = 0; i < LPROPS_HEAP_CNT; i++) { ++ c->lpt_heap[i].arr = kmalloc(sizeof(void *) * LPT_HEAP_SZ, ++ GFP_KERNEL); ++ if (!c->lpt_heap[i].arr) ++ return -ENOMEM; ++ c->lpt_heap[i].cnt = 0; ++ c->lpt_heap[i].max_cnt = LPT_HEAP_SZ; ++ } ++ ++ c->dirty_idx.arr = kmalloc(sizeof(void *) * LPT_HEAP_SZ, GFP_KERNEL); ++ if (!c->dirty_idx.arr) ++ return -ENOMEM; ++ c->dirty_idx.cnt = 0; ++ c->dirty_idx.max_cnt = LPT_HEAP_SZ; ++ ++ err = read_ltab(c); ++ if (err) ++ return err; ++ ++ dbg_lp("space_bits %d", c->space_bits); ++ dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits); ++ dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits); ++ dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits); ++ dbg_lp("pcnt_bits %d", c->pcnt_bits); ++ dbg_lp("lnum_bits %d", c->lnum_bits); ++ dbg_lp("pnode_sz %d", c->pnode_sz); ++ dbg_lp("nnode_sz %d", c->nnode_sz); ++ dbg_lp("ltab_sz %d", c->ltab_sz); ++ dbg_lp("lsave_sz %d", c->lsave_sz); ++ dbg_lp("lsave_cnt %d", c->lsave_cnt); ++ dbg_lp("lpt_hght %d", c->lpt_hght); ++ dbg_lp("big_lpt %d", c->big_lpt); ++ dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); ++ dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); ++ dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); ++ if (c->big_lpt) ++ dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs); ++ ++ return 0; ++} ++ ++/** ++ * lpt_init_wr - initialize the LPT for writing. ++ * @c: UBIFS file-system description object ++ * ++ * 'lpt_init_rd()' must have been called already. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int lpt_init_wr(struct ubifs_info *c) ++{ ++ int err, i; ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ c->ltab_cmt = kmalloc(sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs, GFP_KERNEL); ++ if (!c->ltab_cmt) ++ return -ENOMEM; ++ c->lpt_buf = kmalloc(c->leb_size, GFP_KERNEL); ++ if (!c->lpt_buf) ++ return -ENOMEM; ++#else ++ c->ltab_cmt = vmalloc(sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs); ++ if (!c->ltab_cmt) ++ return -ENOMEM; ++ c->lpt_buf = vmalloc(c->leb_size); ++ if (!c->lpt_buf) ++ return -ENOMEM; ++#endif ++ ++ if (c->big_lpt) { ++ c->lsave = kmalloc(sizeof(int) * c->lsave_cnt, GFP_NOFS); ++ if (!c->lsave) ++ return -ENOMEM; ++ err = read_lsave(c); ++ if (err) ++ return err; ++ } ++ ++ for (i = 0; i < c->lpt_lebs; i++) ++ if (c->ltab[i].free == c->leb_size) { ++ err = ubifs_leb_unmap(c, i + c->lpt_first); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/** ++ * ubifs_lpt_init - initialize the LPT. ++ * @c: UBIFS file-system description object ++ * @rd: whether to initialize lpt for reading ++ * @wr: whether to initialize lpt for writing ++ * ++ * For mounting 'rw', @rd and @wr are both true. For mounting 'ro', @rd is true ++ * and @wr is false. For mounting from 'ro' to 'rw', @rd is false and @wr is ++ * true. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_lpt_init(struct ubifs_info *c, int rd, int wr) ++{ ++ int err; ++ ++ if (rd) { ++ err = lpt_init_rd(c); ++ if (err) ++ return err; ++ } ++ ++ if (wr) { ++ err = lpt_init_wr(c); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/** ++ * struct lpt_scan_node - somewhere to put nodes while we scan LPT. ++ * @nnode: where to keep a nnode ++ * @pnode: where to keep a pnode ++ * @cnode: where to keep a cnode ++ * @in_tree: is the node in the tree in memory ++ * @ptr.nnode: pointer to the nnode (if it is an nnode) which may be here or in ++ * the tree ++ * @ptr.pnode: ditto for pnode ++ * @ptr.cnode: ditto for cnode ++ */ ++struct lpt_scan_node { ++ union { ++ struct ubifs_nnode nnode; ++ struct ubifs_pnode pnode; ++ struct ubifs_cnode cnode; ++ }; ++ int in_tree; ++ union { ++ struct ubifs_nnode *nnode; ++ struct ubifs_pnode *pnode; ++ struct ubifs_cnode *cnode; ++ } ptr; ++}; ++ ++/** ++ * scan_get_nnode - for the scan, get a nnode from either the tree or flash. ++ * @c: the UBIFS file-system description object ++ * @path: where to put the nnode ++ * @parent: parent of the nnode ++ * @iip: index in parent of the nnode ++ * ++ * This function returns a pointer to the nnode on success or a negative error ++ * code on failure. ++ */ ++static struct ubifs_nnode *scan_get_nnode(struct ubifs_info *c, ++ struct lpt_scan_node *path, ++ struct ubifs_nnode *parent, int iip) ++{ ++ struct ubifs_nbranch *branch; ++ struct ubifs_nnode *nnode; ++ void *buf = c->lpt_nod_buf; ++ int err; ++ ++ branch = &parent->nbranch[iip]; ++ nnode = branch->nnode; ++ if (nnode) { ++ path->in_tree = 1; ++ path->ptr.nnode = nnode; ++ return nnode; ++ } ++ nnode = &path->nnode; ++ path->in_tree = 0; ++ path->ptr.nnode = nnode; ++ memset(nnode, 0, sizeof(struct ubifs_nnode)); ++ if (branch->lnum == 0) { ++ /* ++ * This nnode was not written which just means that the LEB ++ * properties in the subtree below it describe empty LEBs. We ++ * make the nnode as though we had read it, which in fact means ++ * doing almost nothing. ++ */ ++ if (c->big_lpt) ++ nnode->num = calc_nnode_num_from_parent(c, parent, iip); ++ } else { ++ err = ubi_read(c->ubi, branch->lnum, buf, branch->offs, ++ c->nnode_sz); ++ if (err) ++ return ERR_PTR(err); ++ err = unpack_nnode(c, buf, nnode); ++ if (err) ++ return ERR_PTR(err); ++ } ++ err = validate_nnode(c, nnode, parent, iip); ++ if (err) ++ return ERR_PTR(err); ++ if (!c->big_lpt) ++ nnode->num = calc_nnode_num_from_parent(c, parent, iip); ++ nnode->level = parent->level - 1; ++ nnode->parent = parent; ++ nnode->iip = iip; ++ return nnode; ++} ++ ++/** ++ * scan_get_pnode - for the scan, get a pnode from either the tree or flash. ++ * @c: the UBIFS file-system description object ++ * @path: where to put the pnode ++ * @parent: parent of the pnode ++ * @iip: index in parent of the pnode ++ * ++ * This function returns a pointer to the pnode on success or a negative error ++ * code on failure. ++ */ ++static struct ubifs_pnode *scan_get_pnode(struct ubifs_info *c, ++ struct lpt_scan_node *path, ++ struct ubifs_nnode *parent, int iip) ++{ ++ struct ubifs_nbranch *branch; ++ struct ubifs_pnode *pnode; ++ void *buf = c->lpt_nod_buf; ++ int err; ++ ++ branch = &parent->nbranch[iip]; ++ pnode = branch->pnode; ++ if (pnode) { ++ path->in_tree = 1; ++ path->ptr.pnode = pnode; ++ return pnode; ++ } ++ pnode = &path->pnode; ++ path->in_tree = 0; ++ path->ptr.pnode = pnode; ++ memset(pnode, 0, sizeof(struct ubifs_pnode)); ++ if (branch->lnum == 0) { ++ /* ++ * This pnode was not written which just means that the LEB ++ * properties in it describe empty LEBs. We make the pnode as ++ * though we had read it. ++ */ ++ int i; ++ ++ if (c->big_lpt) ++ pnode->num = calc_pnode_num_from_parent(c, parent, iip); ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ struct ubifs_lprops * const lprops = &pnode->lprops[i]; ++ ++ lprops->free = c->leb_size; ++ lprops->flags = ubifs_categorize_lprops(c, lprops); ++ } ++ } else { ++ ubifs_assert(branch->lnum >= c->lpt_first && ++ branch->lnum <= c->lpt_last); ++ ubifs_assert(branch->offs >= 0 && branch->offs < c->leb_size); ++ err = ubi_read(c->ubi, branch->lnum, buf, branch->offs, ++ c->pnode_sz); ++ if (err) ++ return ERR_PTR(err); ++ err = unpack_pnode(c, buf, pnode); ++ if (err) ++ return ERR_PTR(err); ++ } ++ err = validate_pnode(c, pnode, parent, iip); ++ if (err) ++ return ERR_PTR(err); ++ if (!c->big_lpt) ++ pnode->num = calc_pnode_num_from_parent(c, parent, iip); ++ pnode->parent = parent; ++ pnode->iip = iip; ++ set_pnode_lnum(c, pnode); ++ return pnode; ++} ++ ++/** ++ * ubifs_lpt_scan_nolock - scan the LPT. ++ * @c: the UBIFS file-system description object ++ * @start_lnum: LEB number from which to start scanning ++ * @end_lnum: LEB number at which to stop scanning ++ * @scan_cb: callback function called for each lprops ++ * @data: data to be passed to the callback function ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_lpt_scan_nolock(struct ubifs_info *c, int start_lnum, int end_lnum, ++ ubifs_lpt_scan_callback scan_cb, void *data) ++{ ++ int err = 0, i, h, iip, shft; ++ struct ubifs_nnode *nnode; ++ struct ubifs_pnode *pnode; ++ struct lpt_scan_node *path; ++ ++ if (start_lnum == -1) { ++ start_lnum = end_lnum + 1; ++ if (start_lnum >= c->leb_cnt) ++ start_lnum = c->main_first; ++ } ++ ++ ubifs_assert(start_lnum >= c->main_first && start_lnum < c->leb_cnt); ++ ubifs_assert(end_lnum >= c->main_first && end_lnum < c->leb_cnt); ++ ++ if (!c->nroot) { ++ err = ubifs_read_nnode(c, NULL, 0); ++ if (err) ++ return err; ++ } ++ ++ path = kmalloc(sizeof(struct lpt_scan_node) * (c->lpt_hght + 1), ++ GFP_NOFS); ++ if (!path) ++ return -ENOMEM; ++ ++ path[0].ptr.nnode = c->nroot; ++ path[0].in_tree = 1; ++again: ++ /* Descend to the pnode containing start_lnum */ ++ nnode = c->nroot; ++ i = start_lnum - c->main_first; ++ shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT; ++ for (h = 1; h < c->lpt_hght; h++) { ++ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); ++ shft -= UBIFS_LPT_FANOUT_SHIFT; ++ nnode = scan_get_nnode(c, path + h, nnode, iip); ++ if (IS_ERR(nnode)) { ++ err = PTR_ERR(nnode); ++ goto out; ++ } ++ } ++ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); ++ shft -= UBIFS_LPT_FANOUT_SHIFT; ++ pnode = scan_get_pnode(c, path + h, nnode, iip); ++ if (IS_ERR(pnode)) { ++ err = PTR_ERR(pnode); ++ goto out; ++ } ++ iip = (i & (UBIFS_LPT_FANOUT - 1)); ++ ++ /* Loop for each lprops */ ++ while (1) { ++ struct ubifs_lprops *lprops = &pnode->lprops[iip]; ++ int ret, lnum = lprops->lnum; ++ ++ ret = scan_cb(c, lprops, path[h].in_tree, data); ++ if (ret < 0) { ++ err = ret; ++ goto out; ++ } ++ if (ret & LPT_SCAN_ADD) { ++ /* Add all the nodes in path to the tree in memory */ ++ for (h = 1; h < c->lpt_hght; h++) { ++ const size_t sz = sizeof(struct ubifs_nnode); ++ struct ubifs_nnode *parent; ++ ++ if (path[h].in_tree) ++ continue; ++ nnode = kmalloc(sz, GFP_NOFS); ++ if (!nnode) { ++ err = -ENOMEM; ++ goto out; ++ } ++ memcpy(nnode, &path[h].nnode, sz); ++ parent = nnode->parent; ++ parent->nbranch[nnode->iip].nnode = nnode; ++ path[h].ptr.nnode = nnode; ++ path[h].in_tree = 1; ++ path[h + 1].cnode.parent = nnode; ++ } ++ if (path[h].in_tree) ++ ubifs_ensure_cat(c, lprops); ++ else { ++ const size_t sz = sizeof(struct ubifs_pnode); ++ struct ubifs_nnode *parent; ++ ++ pnode = kmalloc(sz, GFP_NOFS); ++ if (!pnode) { ++ err = -ENOMEM; ++ goto out; ++ } ++ memcpy(pnode, &path[h].pnode, sz); ++ parent = pnode->parent; ++ parent->nbranch[pnode->iip].pnode = pnode; ++ path[h].ptr.pnode = pnode; ++ path[h].in_tree = 1; ++ update_cats(c, pnode); ++ c->pnodes_have += 1; ++ } ++ err = dbg_check_lpt_nodes(c, (struct ubifs_cnode *) ++ c->nroot, 0, 0); ++ if (err) ++ goto out; ++ err = dbg_check_cats(c); ++ if (err) ++ goto out; ++ } ++ if (ret & LPT_SCAN_STOP) { ++ err = 0; ++ break; ++ } ++ /* Get the next lprops */ ++ if (lnum == end_lnum) { ++ /* ++ * We got to the end without finding what we were ++ * looking for ++ */ ++ err = -ENOSPC; ++ goto out; ++ } ++ if (lnum + 1 >= c->leb_cnt) { ++ /* Wrap-around to the beginning */ ++ start_lnum = c->main_first; ++ goto again; ++ } ++ if (iip + 1 < UBIFS_LPT_FANOUT) { ++ /* Next lprops is in the same pnode */ ++ iip += 1; ++ continue; ++ } ++ /* We need to get the next pnode. Go up until we can go right */ ++ iip = pnode->iip; ++ while (1) { ++ h -= 1; ++ ubifs_assert(h >= 0); ++ nnode = path[h].ptr.nnode; ++ if (iip + 1 < UBIFS_LPT_FANOUT) ++ break; ++ iip = nnode->iip; ++ } ++ /* Go right */ ++ iip += 1; ++ /* Descend to the pnode */ ++ h += 1; ++ for (; h < c->lpt_hght; h++) { ++ nnode = scan_get_nnode(c, path + h, nnode, iip); ++ if (IS_ERR(nnode)) { ++ err = PTR_ERR(nnode); ++ goto out; ++ } ++ iip = 0; ++ } ++ pnode = scan_get_pnode(c, path + h, nnode, iip); ++ if (IS_ERR(pnode)) { ++ err = PTR_ERR(pnode); ++ goto out; ++ } ++ iip = 0; ++ } ++out: ++ kfree(path); ++ return err; ++} ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ ++/** ++ * dbg_chk_pnode - check a pnode. ++ * @c: the UBIFS file-system description object ++ * @pnode: pnode to check ++ * @col: pnode column ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, ++ int col) ++{ ++ int i; ++ ++ if (pnode->num != col) { ++ dbg_err("pnode num %d expected %d parent num %d iip %d", ++ pnode->num, col, pnode->parent->num, pnode->iip); ++ return -EINVAL; ++ } ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ struct ubifs_lprops *lp, *lprops = &pnode->lprops[i]; ++ int lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + i + ++ c->main_first; ++ int found, cat = lprops->flags & LPROPS_CAT_MASK; ++ struct ubifs_lpt_heap *heap; ++ struct list_head *list = NULL; ++ ++ if (lnum >= c->leb_cnt) ++ continue; ++ if (lprops->lnum != lnum) { ++ dbg_err("bad LEB number %d expected %d", ++ lprops->lnum, lnum); ++ return -EINVAL; ++ } ++ if (lprops->flags & LPROPS_TAKEN) { ++ if (cat != LPROPS_UNCAT) { ++ dbg_err("LEB %d taken but not uncat %d", ++ lprops->lnum, cat); ++ return -EINVAL; ++ } ++ continue; ++ } ++ if (lprops->flags & LPROPS_INDEX) { ++ switch (cat) { ++ case LPROPS_UNCAT: ++ case LPROPS_DIRTY_IDX: ++ case LPROPS_FRDI_IDX: ++ break; ++ default: ++ dbg_err("LEB %d index but cat %d", ++ lprops->lnum, cat); ++ return -EINVAL; ++ } ++ } else { ++ switch (cat) { ++ case LPROPS_UNCAT: ++ case LPROPS_DIRTY: ++ case LPROPS_FREE: ++ case LPROPS_EMPTY: ++ case LPROPS_FREEABLE: ++ break; ++ default: ++ dbg_err("LEB %d not index but cat %d", ++ lprops->lnum, cat); ++ return -EINVAL; ++ } ++ } ++ switch (cat) { ++ case LPROPS_UNCAT: ++ list = &c->uncat_list; ++ break; ++ case LPROPS_EMPTY: ++ list = &c->empty_list; ++ break; ++ case LPROPS_FREEABLE: ++ list = &c->freeable_list; ++ break; ++ case LPROPS_FRDI_IDX: ++ list = &c->frdi_idx_list; ++ break; ++ } ++ found = 0; ++ switch (cat) { ++ case LPROPS_DIRTY: ++ case LPROPS_DIRTY_IDX: ++ case LPROPS_FREE: ++ heap = &c->lpt_heap[cat - 1]; ++ if (lprops->hpos < heap->cnt && ++ heap->arr[lprops->hpos] == lprops) ++ found = 1; ++ break; ++ case LPROPS_UNCAT: ++ case LPROPS_EMPTY: ++ case LPROPS_FREEABLE: ++ case LPROPS_FRDI_IDX: ++ list_for_each_entry(lp, list, list) ++ if (lprops == lp) { ++ found = 1; ++ break; ++ } ++ break; ++ } ++ if (!found) { ++ dbg_err("LEB %d cat %d not found in cat heap/list", ++ lprops->lnum, cat); ++ return -EINVAL; ++ } ++ switch (cat) { ++ case LPROPS_EMPTY: ++ if (lprops->free != c->leb_size) { ++ dbg_err("LEB %d cat %d free %d dirty %d", ++ lprops->lnum, cat, lprops->free, ++ lprops->dirty); ++ return -EINVAL; ++ } ++ case LPROPS_FREEABLE: ++ case LPROPS_FRDI_IDX: ++ if (lprops->free + lprops->dirty != c->leb_size) { ++ dbg_err("LEB %d cat %d free %d dirty %d", ++ lprops->lnum, cat, lprops->free, ++ lprops->dirty); ++ return -EINVAL; ++ } ++ } ++ } ++ return 0; ++} ++ ++/** ++ * dbg_check_lpt_nodes - check nnodes and pnodes. ++ * @c: the UBIFS file-system description object ++ * @cnode: next cnode (nnode or pnode) to check ++ * @row: row of cnode (root is zero) ++ * @col: column of cnode (leftmost is zero) ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode, ++ int row, int col) ++{ ++ struct ubifs_nnode *nnode, *nn; ++ struct ubifs_cnode *cn; ++ int num, iip = 0, err; ++ ++ if (!(ubifs_chk_flags & UBIFS_CHK_LPROPS)) ++ return 0; ++ ++ while (cnode) { ++ ubifs_assert(row >= 0); ++ nnode = cnode->parent; ++ if (cnode->level) { ++ /* cnode is a nnode */ ++ num = calc_nnode_num(row, col); ++ if (cnode->num != num) { ++ dbg_err("nnode num %d expected %d " ++ "parent num %d iip %d", cnode->num, num, ++ (nnode ? nnode->num : 0), cnode->iip); ++ return -EINVAL; ++ } ++ nn = (struct ubifs_nnode *)cnode; ++ while (iip < UBIFS_LPT_FANOUT) { ++ cn = nn->nbranch[iip].cnode; ++ if (cn) { ++ /* Go down */ ++ row += 1; ++ col <<= UBIFS_LPT_FANOUT_SHIFT; ++ col += iip; ++ iip = 0; ++ cnode = cn; ++ break; ++ } ++ /* Go right */ ++ iip += 1; ++ } ++ if (iip < UBIFS_LPT_FANOUT) ++ continue; ++ } else { ++ struct ubifs_pnode *pnode; ++ ++ /* cnode is a pnode */ ++ pnode = (struct ubifs_pnode *)cnode; ++ err = dbg_chk_pnode(c, pnode, col); ++ if (err) ++ return err; ++ } ++ /* Go up and to the right */ ++ row -= 1; ++ col >>= UBIFS_LPT_FANOUT_SHIFT; ++ iip = cnode->iip + 1; ++ cnode = (struct ubifs_cnode *)nnode; ++ } ++ return 0; ++} ++ ++#endif /* CONFIG_UBIFS_FS_DEBUG */ +diff -urN linux-2.6.24.7.old/fs/ubifs/lpt_commit.c linux-2.6.24.7/fs/ubifs/lpt_commit.c +--- linux-2.6.24.7.old/fs/ubifs/lpt_commit.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/lpt_commit.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1639 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy (Битюцкий Ðртём) ++ */ ++ ++/* ++ * This file implements commit-related functionality of the LEB properties ++ * subsystem. ++ */ ++ ++#include ++#include "ubifs.h" ++ ++/** ++ * first_dirty_cnode - find first dirty cnode. ++ * @c: UBIFS file-system description object ++ * @nnode: nnode at which to start ++ * ++ * This function returns the first dirty cnode or %NULL if there is not one. ++ */ ++static struct ubifs_cnode *first_dirty_cnode(struct ubifs_nnode *nnode) ++{ ++ ubifs_assert(nnode); ++ while (1) { ++ int i, cont = 0; ++ ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ struct ubifs_cnode *cnode; ++ ++ cnode = nnode->nbranch[i].cnode; ++ if (cnode && ++ test_bit(DIRTY_CNODE, &cnode->flags)) { ++ if (cnode->level == 0) ++ return cnode; ++ nnode = (struct ubifs_nnode *)cnode; ++ cont = 1; ++ break; ++ } ++ } ++ if (!cont) ++ return (struct ubifs_cnode *)nnode; ++ } ++} ++ ++/** ++ * next_dirty_cnode - find next dirty cnode. ++ * @cnode: cnode from which to begin searching ++ * ++ * This function returns the next dirty cnode or %NULL if there is not one. ++ */ ++static struct ubifs_cnode *next_dirty_cnode(struct ubifs_cnode *cnode) ++{ ++ struct ubifs_nnode *nnode; ++ int i; ++ ++ ubifs_assert(cnode); ++ nnode = cnode->parent; ++ if (!nnode) ++ return NULL; ++ for (i = cnode->iip + 1; i < UBIFS_LPT_FANOUT; i++) { ++ cnode = nnode->nbranch[i].cnode; ++ if (cnode && test_bit(DIRTY_CNODE, &cnode->flags)) { ++ if (cnode->level == 0) ++ return cnode; /* cnode is a pnode */ ++ /* cnode is a nnode */ ++ return first_dirty_cnode((struct ubifs_nnode *)cnode); ++ } ++ } ++ return (struct ubifs_cnode *)nnode; ++} ++ ++/** ++ * get_cnodes_to_commit - create list of dirty cnodes to commit. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns the number of cnodes to commit. ++ */ ++static int get_cnodes_to_commit(struct ubifs_info *c) ++{ ++ struct ubifs_cnode *cnode, *cnext; ++ int cnt = 0; ++ ++ if (!c->nroot) ++ return 0; ++ ++ if (!test_bit(DIRTY_CNODE, &c->nroot->flags)) ++ return 0; ++ ++ c->lpt_cnext = first_dirty_cnode(c->nroot); ++ cnode = c->lpt_cnext; ++ if (!cnode) ++ return 0; ++ cnt += 1; ++ while (1) { ++ ubifs_assert(!test_bit(COW_ZNODE, &cnode->flags)); ++ __set_bit(COW_ZNODE, &cnode->flags); ++ cnext = next_dirty_cnode(cnode); ++ if (!cnext) { ++ cnode->cnext = c->lpt_cnext; ++ break; ++ } ++ cnode->cnext = cnext; ++ cnode = cnext; ++ cnt += 1; ++ } ++ dbg_cmt("committing %d cnodes", cnt); ++ dbg_lp("committing %d cnodes", cnt); ++ ubifs_assert(cnt == c->dirty_nn_cnt + c->dirty_pn_cnt); ++ return cnt; ++} ++ ++/** ++ * upd_ltab - update LPT LEB properties. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number ++ * @free: amount of free space ++ * @dirty: amount of dirty space to add ++ */ ++static void upd_ltab(struct ubifs_info *c, int lnum, int free, int dirty) ++{ ++ dbg_lp("LEB %d free %d dirty %d to %d +%d", ++ lnum, c->ltab[lnum - c->lpt_first].free, ++ c->ltab[lnum - c->lpt_first].dirty, free, dirty); ++ ubifs_assert(lnum >= c->lpt_first && lnum <= c->lpt_last); ++ c->ltab[lnum - c->lpt_first].free = free; ++ c->ltab[lnum - c->lpt_first].dirty += dirty; ++} ++ ++/** ++ * alloc_lpt_leb - allocate an LPT LEB that is empty. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number is passed and returned here ++ * ++ * This function finds the next empty LEB in the ltab starting from @lnum. If a ++ * an empty LEB is found it is returned in @lnum and the function returns %0. ++ * Otherwise the function returns -ENOSPC. Note however, that LPT is designed ++ * never to run out of space. ++ */ ++static int alloc_lpt_leb(struct ubifs_info *c, int *lnum) ++{ ++ int i, n; ++ ++ n = *lnum - c->lpt_first + 1; ++ for (i = n; i < c->lpt_lebs; i++) { ++ if (c->ltab[i].tgc || c->ltab[i].cmt) ++ continue; ++ if (c->ltab[i].free == c->leb_size) { ++ c->ltab[i].cmt = 1; ++ *lnum = i + c->lpt_first; ++ return 0; ++ } ++ } ++ ++ for (i = 0; i < n; i++) { ++ if (c->ltab[i].tgc || c->ltab[i].cmt) ++ continue; ++ if (c->ltab[i].free == c->leb_size) { ++ c->ltab[i].cmt = 1; ++ *lnum = i + c->lpt_first; ++ return 0; ++ } ++ } ++ dbg_err("last LEB %d", *lnum); ++ dump_stack(); ++ return -ENOSPC; ++} ++ ++/** ++ * layout_cnodes - layout cnodes for commit. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int layout_cnodes(struct ubifs_info *c) ++{ ++ int lnum, offs, len, alen, done_lsave, done_ltab, err; ++ struct ubifs_cnode *cnode; ++ ++ cnode = c->lpt_cnext; ++ if (!cnode) ++ return 0; ++ lnum = c->nhead_lnum; ++ offs = c->nhead_offs; ++ /* Try to place lsave and ltab nicely */ ++ done_lsave = !c->big_lpt; ++ done_ltab = 0; ++ if (!done_lsave && offs + c->lsave_sz <= c->leb_size) { ++ done_lsave = 1; ++ c->lsave_lnum = lnum; ++ c->lsave_offs = offs; ++ offs += c->lsave_sz; ++ } ++ ++ if (offs + c->ltab_sz <= c->leb_size) { ++ done_ltab = 1; ++ c->ltab_lnum = lnum; ++ c->ltab_offs = offs; ++ offs += c->ltab_sz; ++ } ++ ++ do { ++ if (cnode->level) { ++ len = c->nnode_sz; ++ c->dirty_nn_cnt -= 1; ++ } else { ++ len = c->pnode_sz; ++ c->dirty_pn_cnt -= 1; ++ } ++ while (offs + len > c->leb_size) { ++ alen = ALIGN(offs, c->min_io_size); ++ upd_ltab(c, lnum, c->leb_size - alen, alen - offs); ++ err = alloc_lpt_leb(c, &lnum); ++ if (err) ++ return err; ++ offs = 0; ++ ubifs_assert(lnum >= c->lpt_first && ++ lnum <= c->lpt_last); ++ /* Try to place lsave and ltab nicely */ ++ if (!done_lsave) { ++ done_lsave = 1; ++ c->lsave_lnum = lnum; ++ c->lsave_offs = offs; ++ offs += c->lsave_sz; ++ continue; ++ } ++ if (!done_ltab) { ++ done_ltab = 1; ++ c->ltab_lnum = lnum; ++ c->ltab_offs = offs; ++ offs += c->ltab_sz; ++ continue; ++ } ++ break; ++ } ++ if (cnode->parent) { ++ cnode->parent->nbranch[cnode->iip].lnum = lnum; ++ cnode->parent->nbranch[cnode->iip].offs = offs; ++ } else { ++ c->lpt_lnum = lnum; ++ c->lpt_offs = offs; ++ } ++ offs += len; ++ cnode = cnode->cnext; ++ } while (cnode && cnode != c->lpt_cnext); ++ ++ /* Make sure to place LPT's save table */ ++ if (!done_lsave) { ++ if (offs + c->lsave_sz > c->leb_size) { ++ alen = ALIGN(offs, c->min_io_size); ++ upd_ltab(c, lnum, c->leb_size - alen, alen - offs); ++ err = alloc_lpt_leb(c, &lnum); ++ if (err) ++ return err; ++ offs = 0; ++ ubifs_assert(lnum >= c->lpt_first && ++ lnum <= c->lpt_last); ++ } ++ done_lsave = 1; ++ c->lsave_lnum = lnum; ++ c->lsave_offs = offs; ++ offs += c->lsave_sz; ++ } ++ ++ /* Make sure to place LPT's own lprops table */ ++ if (!done_ltab) { ++ if (offs + c->ltab_sz > c->leb_size) { ++ alen = ALIGN(offs, c->min_io_size); ++ upd_ltab(c, lnum, c->leb_size - alen, alen - offs); ++ err = alloc_lpt_leb(c, &lnum); ++ if (err) ++ return err; ++ offs = 0; ++ ubifs_assert(lnum >= c->lpt_first && ++ lnum <= c->lpt_last); ++ } ++ done_ltab = 1; ++ c->ltab_lnum = lnum; ++ c->ltab_offs = offs; ++ offs += c->ltab_sz; ++ } ++ ++ alen = ALIGN(offs, c->min_io_size); ++ upd_ltab(c, lnum, c->leb_size - alen, alen - offs); ++ return 0; ++} ++ ++/** ++ * realloc_lpt_leb - allocate an LPT LEB that is empty. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number is passed and returned here ++ * ++ * This function duplicates exactly the results of the function alloc_lpt_leb. ++ * It is used during end commit to reallocate the same LEB numbers that were ++ * allocated by alloc_lpt_leb during start commit. ++ * ++ * This function finds the next LEB that was allocated by the alloc_lpt_leb ++ * function starting from @lnum. If a LEB is found it is returned in @lnum and ++ * the function returns %0. Otherwise the function returns -ENOSPC. ++ * Note however, that LPT is designed never to run out of space. ++ */ ++static int realloc_lpt_leb(struct ubifs_info *c, int *lnum) ++{ ++ int i, n; ++ ++ n = *lnum - c->lpt_first + 1; ++ for (i = n; i < c->lpt_lebs; i++) ++ if (c->ltab[i].cmt) { ++ c->ltab[i].cmt = 0; ++ *lnum = i + c->lpt_first; ++ return 0; ++ } ++ ++ for (i = 0; i < n; i++) ++ if (c->ltab[i].cmt) { ++ c->ltab[i].cmt = 0; ++ *lnum = i + c->lpt_first; ++ return 0; ++ } ++ dbg_err("last LEB %d", *lnum); ++ dump_stack(); ++ return -ENOSPC; ++} ++ ++/** ++ * write_cnodes - write cnodes for commit. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int write_cnodes(struct ubifs_info *c) ++{ ++ int lnum, offs, len, from, err, wlen, alen, done_ltab, done_lsave; ++ struct ubifs_cnode *cnode; ++ void *buf = c->lpt_buf; ++ ++ cnode = c->lpt_cnext; ++ if (!cnode) ++ return 0; ++ lnum = c->nhead_lnum; ++ offs = c->nhead_offs; ++ from = offs; ++ /* Ensure empty LEB is unmapped */ ++ if (offs == 0) { ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ return err; ++ } ++ /* Try to place lsave and ltab nicely */ ++ done_lsave = !c->big_lpt; ++ done_ltab = 0; ++ if (!done_lsave && offs + c->lsave_sz <= c->leb_size) { ++ done_lsave = 1; ++ ubifs_pack_lsave(c, buf + offs, c->lsave); ++ offs += c->lsave_sz; ++ } ++ ++ if (offs + c->ltab_sz <= c->leb_size) { ++ done_ltab = 1; ++ ubifs_pack_ltab(c, buf + offs, c->ltab_cmt); ++ offs += c->ltab_sz; ++ } ++ ++ /* Loop for each cnode */ ++ do { ++ if (cnode->level) ++ len = c->nnode_sz; ++ else ++ len = c->pnode_sz; ++ while (offs + len > c->leb_size) { ++ wlen = offs - from; ++ if (wlen) { ++ alen = ALIGN(wlen, c->min_io_size); ++ memset(buf + offs, 0xff, alen - wlen); ++ err = ubifs_leb_write(c, lnum, buf + from, from, ++ alen, UBI_SHORTTERM); ++ if (err) ++ return err; ++ } ++ err = realloc_lpt_leb(c, &lnum); ++ if (err) ++ return err; ++ offs = 0; ++ from = 0; ++ ubifs_assert(lnum >= c->lpt_first && ++ lnum <= c->lpt_last); ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ return err; ++ /* Try to place lsave and ltab nicely */ ++ if (!done_lsave) { ++ done_lsave = 1; ++ ubifs_pack_lsave(c, buf + offs, c->lsave); ++ offs += c->lsave_sz; ++ continue; ++ } ++ if (!done_ltab) { ++ done_ltab = 1; ++ ubifs_pack_ltab(c, buf + offs, c->ltab_cmt); ++ offs += c->ltab_sz; ++ continue; ++ } ++ break; ++ } ++ if (cnode->level) ++ ubifs_pack_nnode(c, buf + offs, ++ (struct ubifs_nnode *)cnode); ++ else ++ ubifs_pack_pnode(c, buf + offs, ++ (struct ubifs_pnode *)cnode); ++ /* ++ * The reason for the barriers is the same as in case of TNC. ++ * See comment in 'write_index()'. 'dirty_cow_nnode()' and ++ * 'dirty_cow_pnode()' are the functions for which this is ++ * important. ++ */ ++ clear_bit(DIRTY_CNODE, &cnode->flags); ++ smp_mb__before_clear_bit(); ++ clear_bit(COW_ZNODE, &cnode->flags); ++ smp_mb__after_clear_bit(); ++ offs += len; ++ cnode = cnode->cnext; ++ } while (cnode && cnode != c->lpt_cnext); ++ ++ /* Make sure to place LPT's save table */ ++ if (!done_lsave) { ++ if (offs + c->lsave_sz > c->leb_size) { ++ wlen = offs - from; ++ alen = ALIGN(wlen, c->min_io_size); ++ memset(buf + offs, 0xff, alen - wlen); ++ err = ubifs_leb_write(c, lnum, buf + from, from, alen, ++ UBI_SHORTTERM); ++ if (err) ++ return err; ++ err = realloc_lpt_leb(c, &lnum); ++ if (err) ++ return err; ++ offs = 0; ++ ubifs_assert(lnum >= c->lpt_first && ++ lnum <= c->lpt_last); ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ return err; ++ } ++ done_lsave = 1; ++ ubifs_pack_lsave(c, buf + offs, c->lsave); ++ offs += c->lsave_sz; ++ } ++ ++ /* Make sure to place LPT's own lprops table */ ++ if (!done_ltab) { ++ if (offs + c->ltab_sz > c->leb_size) { ++ wlen = offs - from; ++ alen = ALIGN(wlen, c->min_io_size); ++ memset(buf + offs, 0xff, alen - wlen); ++ err = ubifs_leb_write(c, lnum, buf + from, from, alen, ++ UBI_SHORTTERM); ++ if (err) ++ return err; ++ err = realloc_lpt_leb(c, &lnum); ++ if (err) ++ return err; ++ offs = 0; ++ ubifs_assert(lnum >= c->lpt_first && ++ lnum <= c->lpt_last); ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ return err; ++ } ++ done_ltab = 1; ++ ubifs_pack_ltab(c, buf + offs, c->ltab_cmt); ++ offs += c->ltab_sz; ++ } ++ ++ /* Write remaining data in buffer */ ++ wlen = offs - from; ++ alen = ALIGN(wlen, c->min_io_size); ++ memset(buf + offs, 0xff, alen - wlen); ++ err = ubifs_leb_write(c, lnum, buf + from, from, alen, UBI_SHORTTERM); ++ if (err) ++ return err; ++ c->nhead_lnum = lnum; ++ c->nhead_offs = ALIGN(offs, c->min_io_size); ++ ++ dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); ++ dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); ++ dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); ++ if (c->big_lpt) ++ dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs); ++ return 0; ++} ++ ++/** ++ * next_pnode - find next pnode. ++ * @c: UBIFS file-system description object ++ * @pnode: pnode ++ * ++ * This function returns the next pnode or %NULL if there are no more pnodes. ++ */ ++static struct ubifs_pnode *next_pnode(struct ubifs_info *c, ++ struct ubifs_pnode *pnode) ++{ ++ struct ubifs_nnode *nnode; ++ int iip; ++ ++ /* Try to go right */ ++ nnode = pnode->parent; ++ iip = pnode->iip + 1; ++ if (iip < UBIFS_LPT_FANOUT) { ++ /* We assume here that LEB zero is never an LPT LEB */ ++ if (nnode->nbranch[iip].lnum) ++ return ubifs_get_pnode(c, nnode, iip); ++ else ++ return NULL; ++ } ++ ++ /* Go up while can't go right */ ++ do { ++ iip = nnode->iip + 1; ++ nnode = nnode->parent; ++ if (!nnode) ++ return NULL; ++ /* We assume here that LEB zero is never an LPT LEB */ ++ } while (iip >= UBIFS_LPT_FANOUT || !nnode->nbranch[iip].lnum); ++ ++ /* Go right */ ++ nnode = ubifs_get_nnode(c, nnode, iip); ++ if (IS_ERR(nnode)) ++ return (void *)nnode; ++ ++ /* Go down to level 1 */ ++ while (nnode->level > 1) { ++ nnode = ubifs_get_nnode(c, nnode, 0); ++ if (IS_ERR(nnode)) ++ return (void *)nnode; ++ } ++ ++ return ubifs_get_pnode(c, nnode, 0); ++} ++ ++/** ++ * pnode_lookup - lookup a pnode in the LPT. ++ * @c: UBIFS file-system description object ++ * @i: pnode number (0 to main_lebs - 1) ++ * ++ * This function returns a pointer to the pnode on success or a negative ++ * error code on failure. ++ */ ++static struct ubifs_pnode *pnode_lookup(struct ubifs_info *c, int i) ++{ ++ int err, h, iip, shft; ++ struct ubifs_nnode *nnode; ++ ++ if (!c->nroot) { ++ err = ubifs_read_nnode(c, NULL, 0); ++ if (err) ++ return ERR_PTR(err); ++ } ++ i <<= UBIFS_LPT_FANOUT_SHIFT; ++ nnode = c->nroot; ++ shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT; ++ for (h = 1; h < c->lpt_hght; h++) { ++ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); ++ shft -= UBIFS_LPT_FANOUT_SHIFT; ++ nnode = ubifs_get_nnode(c, nnode, iip); ++ if (IS_ERR(nnode)) ++ return ERR_PTR(PTR_ERR(nnode)); ++ } ++ iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); ++ return ubifs_get_pnode(c, nnode, iip); ++} ++ ++/** ++ * add_pnode_dirt - add dirty space to LPT LEB properties. ++ * @c: UBIFS file-system description object ++ * @pnode: pnode for which to add dirt ++ */ ++static void add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode) ++{ ++ ubifs_add_lpt_dirt(c, pnode->parent->nbranch[pnode->iip].lnum, ++ c->pnode_sz); ++} ++ ++/** ++ * do_make_pnode_dirty - mark a pnode dirty. ++ * @c: UBIFS file-system description object ++ * @pnode: pnode to mark dirty ++ */ ++static void do_make_pnode_dirty(struct ubifs_info *c, struct ubifs_pnode *pnode) ++{ ++ /* Assumes cnext list is empty i.e. not called during commit */ ++ if (!test_and_set_bit(DIRTY_CNODE, &pnode->flags)) { ++ struct ubifs_nnode *nnode; ++ ++ c->dirty_pn_cnt += 1; ++ add_pnode_dirt(c, pnode); ++ /* Mark parent and ancestors dirty too */ ++ nnode = pnode->parent; ++ while (nnode) { ++ if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) { ++ c->dirty_nn_cnt += 1; ++ ubifs_add_nnode_dirt(c, nnode); ++ nnode = nnode->parent; ++ } else ++ break; ++ } ++ } ++} ++ ++/** ++ * make_tree_dirty - mark the entire LEB properties tree dirty. ++ * @c: UBIFS file-system description object ++ * ++ * This function is used by the "small" LPT model to cause the entire LEB ++ * properties tree to be written. The "small" LPT model does not use LPT ++ * garbage collection because it is more efficient to write the entire tree ++ * (because it is small). ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int make_tree_dirty(struct ubifs_info *c) ++{ ++ struct ubifs_pnode *pnode; ++ ++ pnode = pnode_lookup(c, 0); ++ while (pnode) { ++ do_make_pnode_dirty(c, pnode); ++ pnode = next_pnode(c, pnode); ++ if (IS_ERR(pnode)) ++ return PTR_ERR(pnode); ++ } ++ return 0; ++} ++ ++/** ++ * need_write_all - determine if the LPT area is running out of free space. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns %1 if the LPT area is running out of free space and %0 ++ * if it is not. ++ */ ++static int need_write_all(struct ubifs_info *c) ++{ ++ long long free = 0; ++ int i; ++ ++ for (i = 0; i < c->lpt_lebs; i++) { ++ if (i + c->lpt_first == c->nhead_lnum) ++ free += c->leb_size - c->nhead_offs; ++ else if (c->ltab[i].free == c->leb_size) ++ free += c->leb_size; ++ else if (c->ltab[i].free + c->ltab[i].dirty == c->leb_size) ++ free += c->leb_size; ++ } ++ /* Less than twice the size left */ ++ if (free <= c->lpt_sz * 2) ++ return 1; ++ return 0; ++} ++ ++/** ++ * lpt_tgc_start - start trivial garbage collection of LPT LEBs. ++ * @c: UBIFS file-system description object ++ * ++ * LPT trivial garbage collection is where a LPT LEB contains only dirty and ++ * free space and so may be reused as soon as the next commit is completed. ++ * This function is called during start commit to mark LPT LEBs for trivial GC. ++ */ ++static void lpt_tgc_start(struct ubifs_info *c) ++{ ++ int i; ++ ++ for (i = 0; i < c->lpt_lebs; i++) { ++ if (i + c->lpt_first == c->nhead_lnum) ++ continue; ++ if (c->ltab[i].dirty > 0 && ++ c->ltab[i].free + c->ltab[i].dirty == c->leb_size) { ++ c->ltab[i].tgc = 1; ++ c->ltab[i].free = c->leb_size; ++ c->ltab[i].dirty = 0; ++ dbg_lp("LEB %d", i + c->lpt_first); ++ } ++ } ++} ++ ++/** ++ * lpt_tgc_end - end trivial garbage collection of LPT LEBs. ++ * @c: UBIFS file-system description object ++ * ++ * LPT trivial garbage collection is where a LPT LEB contains only dirty and ++ * free space and so may be reused as soon as the next commit is completed. ++ * This function is called after the commit is completed (master node has been ++ * written) and unmaps LPT LEBs that were marked for trivial GC. ++ */ ++static int lpt_tgc_end(struct ubifs_info *c) ++{ ++ int i, err; ++ ++ for (i = 0; i < c->lpt_lebs; i++) ++ if (c->ltab[i].tgc) { ++ err = ubifs_leb_unmap(c, i + c->lpt_first); ++ if (err) ++ return err; ++ c->ltab[i].tgc = 0; ++ dbg_lp("LEB %d", i + c->lpt_first); ++ } ++ return 0; ++} ++ ++/** ++ * populate_lsave - fill the lsave array with important LEB numbers. ++ * @c: the UBIFS file-system description object ++ * ++ * This function is only called for the "big" model. It records a small number ++ * of LEB numbers of important LEBs. Important LEBs are ones that are (from ++ * most important to least important): empty, freeable, freeable index, dirty ++ * index, dirty or free. Upon mount, we read this list of LEB numbers and bring ++ * their pnodes into memory. That will stop us from having to scan the LPT ++ * straight away. For the "small" model we assume that scanning the LPT is no ++ * big deal. ++ */ ++static void populate_lsave(struct ubifs_info *c) ++{ ++ struct ubifs_lprops *lprops; ++ struct ubifs_lpt_heap *heap; ++ int i, cnt = 0; ++ ++ ubifs_assert(c->big_lpt); ++ if (!(c->lpt_drty_flgs & LSAVE_DIRTY)) { ++ c->lpt_drty_flgs |= LSAVE_DIRTY; ++ ubifs_add_lpt_dirt(c, c->lsave_lnum, c->lsave_sz); ++ } ++ list_for_each_entry(lprops, &c->empty_list, list) { ++ c->lsave[cnt++] = lprops->lnum; ++ if (cnt >= c->lsave_cnt) ++ return; ++ } ++ list_for_each_entry(lprops, &c->freeable_list, list) { ++ c->lsave[cnt++] = lprops->lnum; ++ if (cnt >= c->lsave_cnt) ++ return; ++ } ++ list_for_each_entry(lprops, &c->frdi_idx_list, list) { ++ c->lsave[cnt++] = lprops->lnum; ++ if (cnt >= c->lsave_cnt) ++ return; ++ } ++ heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1]; ++ for (i = 0; i < heap->cnt; i++) { ++ c->lsave[cnt++] = heap->arr[i]->lnum; ++ if (cnt >= c->lsave_cnt) ++ return; ++ } ++ heap = &c->lpt_heap[LPROPS_DIRTY - 1]; ++ for (i = 0; i < heap->cnt; i++) { ++ c->lsave[cnt++] = heap->arr[i]->lnum; ++ if (cnt >= c->lsave_cnt) ++ return; ++ } ++ heap = &c->lpt_heap[LPROPS_FREE - 1]; ++ for (i = 0; i < heap->cnt; i++) { ++ c->lsave[cnt++] = heap->arr[i]->lnum; ++ if (cnt >= c->lsave_cnt) ++ return; ++ } ++ /* Fill it up completely */ ++ while (cnt < c->lsave_cnt) ++ c->lsave[cnt++] = c->main_first; ++} ++ ++/** ++ * ubifs_lpt_start_commit - UBIFS commit starts. ++ * @c: the UBIFS file-system description object ++ * ++ * This function has to be called when UBIFS starts the commit operation. ++ * This function "freezes" all currently dirty LEB properties and does not ++ * change them anymore. Further changes are saved and tracked separately ++ * because they are not part of this commit. This function returns zero in case ++ * of success and a negative error code in case of failure. ++ */ ++int ubifs_lpt_start_commit(struct ubifs_info *c) ++{ ++ int err, cnt; ++ ++ dbg_lp(""); ++ ++ mutex_lock(&c->lp_mutex); ++ err = dbg_check_ltab(c); ++ if (err) ++ goto out; ++ ++ lpt_tgc_start(c); ++ ++ if (!c->dirty_pn_cnt) { ++ dbg_cmt("no cnodes to commit"); ++ err = 0; ++ goto out; ++ } ++ ++ if (!c->big_lpt && need_write_all(c)) { ++ /* If needed, write everything */ ++ err = make_tree_dirty(c); ++ if (err) ++ goto out; ++ lpt_tgc_start(c); ++ } ++ ++ if (c->big_lpt) ++ populate_lsave(c); ++ ++ cnt = get_cnodes_to_commit(c); ++ ubifs_assert(cnt != 0); ++ ++ err = layout_cnodes(c); ++ if (err) ++ goto out; ++ ++ /* Copy the LPT's own lprops for end commit to write */ ++ memcpy(c->ltab_cmt, c->ltab, ++ sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs); ++ c->lpt_drty_flgs &= ~(LTAB_DIRTY | LSAVE_DIRTY); ++ ++out: ++ mutex_unlock(&c->lp_mutex); ++ return err; ++} ++ ++/** ++ * free_obsolete_cnodes - free obsolete cnodes for commit end. ++ * @c: UBIFS file-system description object ++ */ ++static void free_obsolete_cnodes(struct ubifs_info *c) ++{ ++ struct ubifs_cnode *cnode, *cnext; ++ ++ cnext = c->lpt_cnext; ++ if (!cnext) ++ return; ++ do { ++ cnode = cnext; ++ cnext = cnode->cnext; ++ if (test_bit(OBSOLETE_CNODE, &cnode->flags)) ++ kfree(cnode); ++ else ++ cnode->cnext = NULL; ++ } while (cnext != c->lpt_cnext); ++ c->lpt_cnext = NULL; ++} ++ ++/** ++ * ubifs_lpt_end_commit - finish the commit operation. ++ * @c: the UBIFS file-system description object ++ * ++ * This function has to be called when the commit operation finishes. It ++ * flushes the changes which were "frozen" by 'ubifs_lprops_start_commit()' to ++ * the media. Returns zero in case of success and a negative error code in case ++ * of failure. ++ */ ++int ubifs_lpt_end_commit(struct ubifs_info *c) ++{ ++ int err; ++ ++ dbg_lp(""); ++ ++ if (!c->lpt_cnext) ++ return 0; ++ ++ err = write_cnodes(c); ++ if (err) ++ return err; ++ ++ mutex_lock(&c->lp_mutex); ++ free_obsolete_cnodes(c); ++ mutex_unlock(&c->lp_mutex); ++ ++ return 0; ++} ++ ++/** ++ * nnode_lookup - lookup a nnode in the LPT. ++ * @c: UBIFS file-system description object ++ * @i: nnode number ++ * ++ * This function returns a pointer to the nnode on success or a negative ++ * error code on failure. ++ */ ++static struct ubifs_nnode *nnode_lookup(struct ubifs_info *c, int i) ++{ ++ int err, iip; ++ struct ubifs_nnode *nnode; ++ ++ if (!c->nroot) { ++ err = ubifs_read_nnode(c, NULL, 0); ++ if (err) ++ return ERR_PTR(err); ++ } ++ nnode = c->nroot; ++ while (1) { ++ iip = i & (UBIFS_LPT_FANOUT - 1); ++ i >>= UBIFS_LPT_FANOUT_SHIFT; ++ if (!i) ++ break; ++ nnode = ubifs_get_nnode(c, nnode, iip); ++ if (IS_ERR(nnode)) ++ return nnode; ++ } ++ return nnode; ++} ++ ++/** ++ * make_nnode_dirty - find a nnode and, if found, make it dirty. ++ * @c: UBIFS file-system description object ++ * @node_num: nnode number of nnode to make dirty ++ * @lnum: LEB number where nnode was written ++ * @offs: offset where nnode was written ++ * ++ * This function is used by LPT garbage collection. LPT garbage collection is ++ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection ++ * simply involves marking all the nodes in the LEB being garbage-collected as ++ * dirty. The dirty nodes are written next commit, after which the LEB is free ++ * to be reused. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int make_nnode_dirty(struct ubifs_info *c, int node_num, int lnum, ++ int offs) ++{ ++ struct ubifs_nnode *nnode; ++ ++ nnode = nnode_lookup(c, node_num); ++ if (IS_ERR(nnode)) ++ return PTR_ERR(nnode); ++ if (nnode->parent) { ++ struct ubifs_nbranch *branch; ++ ++ branch = &nnode->parent->nbranch[nnode->iip]; ++ if (branch->lnum != lnum || branch->offs != offs) ++ return 0; /* nnode is obsolete */ ++ } else if (c->lpt_lnum != lnum || c->lpt_offs != offs) ++ return 0; /* nnode is obsolete */ ++ /* Assumes cnext list is empty i.e. not called during commit */ ++ if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) { ++ c->dirty_nn_cnt += 1; ++ ubifs_add_nnode_dirt(c, nnode); ++ /* Mark parent and ancestors dirty too */ ++ nnode = nnode->parent; ++ while (nnode) { ++ if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) { ++ c->dirty_nn_cnt += 1; ++ ubifs_add_nnode_dirt(c, nnode); ++ nnode = nnode->parent; ++ } else ++ break; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * make_pnode_dirty - find a pnode and, if found, make it dirty. ++ * @c: UBIFS file-system description object ++ * @node_num: pnode number of pnode to make dirty ++ * @lnum: LEB number where pnode was written ++ * @offs: offset where pnode was written ++ * ++ * This function is used by LPT garbage collection. LPT garbage collection is ++ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection ++ * simply involves marking all the nodes in the LEB being garbage-collected as ++ * dirty. The dirty nodes are written next commit, after which the LEB is free ++ * to be reused. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int make_pnode_dirty(struct ubifs_info *c, int node_num, int lnum, ++ int offs) ++{ ++ struct ubifs_pnode *pnode; ++ struct ubifs_nbranch *branch; ++ ++ pnode = pnode_lookup(c, node_num); ++ if (IS_ERR(pnode)) ++ return PTR_ERR(pnode); ++ branch = &pnode->parent->nbranch[pnode->iip]; ++ if (branch->lnum != lnum || branch->offs != offs) ++ return 0; ++ do_make_pnode_dirty(c, pnode); ++ return 0; ++} ++ ++/** ++ * make_ltab_dirty - make ltab node dirty. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number where ltab was written ++ * @offs: offset where ltab was written ++ * ++ * This function is used by LPT garbage collection. LPT garbage collection is ++ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection ++ * simply involves marking all the nodes in the LEB being garbage-collected as ++ * dirty. The dirty nodes are written next commit, after which the LEB is free ++ * to be reused. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int make_ltab_dirty(struct ubifs_info *c, int lnum, int offs) ++{ ++ if (lnum != c->ltab_lnum || offs != c->ltab_offs) ++ return 0; /* This ltab node is obsolete */ ++ if (!(c->lpt_drty_flgs & LTAB_DIRTY)) { ++ c->lpt_drty_flgs |= LTAB_DIRTY; ++ ubifs_add_lpt_dirt(c, c->ltab_lnum, c->ltab_sz); ++ } ++ return 0; ++} ++ ++/** ++ * make_lsave_dirty - make lsave node dirty. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number where lsave was written ++ * @offs: offset where lsave was written ++ * ++ * This function is used by LPT garbage collection. LPT garbage collection is ++ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection ++ * simply involves marking all the nodes in the LEB being garbage-collected as ++ * dirty. The dirty nodes are written next commit, after which the LEB is free ++ * to be reused. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int make_lsave_dirty(struct ubifs_info *c, int lnum, int offs) ++{ ++ if (lnum != c->lsave_lnum || offs != c->lsave_offs) ++ return 0; /* This lsave node is obsolete */ ++ if (!(c->lpt_drty_flgs & LSAVE_DIRTY)) { ++ c->lpt_drty_flgs |= LSAVE_DIRTY; ++ ubifs_add_lpt_dirt(c, c->lsave_lnum, c->lsave_sz); ++ } ++ return 0; ++} ++ ++/** ++ * make_node_dirty - make node dirty. ++ * @c: UBIFS file-system description object ++ * @node_type: LPT node type ++ * @node_num: node number ++ * @lnum: LEB number where node was written ++ * @offs: offset where node was written ++ * ++ * This function is used by LPT garbage collection. LPT garbage collection is ++ * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection ++ * simply involves marking all the nodes in the LEB being garbage-collected as ++ * dirty. The dirty nodes are written next commit, after which the LEB is free ++ * to be reused. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int make_node_dirty(struct ubifs_info *c, int node_type, int node_num, ++ int lnum, int offs) ++{ ++ switch (node_type) { ++ case UBIFS_LPT_NNODE: ++ return make_nnode_dirty(c, node_num, lnum, offs); ++ case UBIFS_LPT_PNODE: ++ return make_pnode_dirty(c, node_num, lnum, offs); ++ case UBIFS_LPT_LTAB: ++ return make_ltab_dirty(c, lnum, offs); ++ case UBIFS_LPT_LSAVE: ++ return make_lsave_dirty(c, lnum, offs); ++ } ++ return -EINVAL; ++} ++ ++/** ++ * get_lpt_node_len - return the length of a node based on its type. ++ * @c: UBIFS file-system description object ++ * @node_type: LPT node type ++ */ ++static int get_lpt_node_len(struct ubifs_info *c, int node_type) ++{ ++ switch (node_type) { ++ case UBIFS_LPT_NNODE: ++ return c->nnode_sz; ++ case UBIFS_LPT_PNODE: ++ return c->pnode_sz; ++ case UBIFS_LPT_LTAB: ++ return c->ltab_sz; ++ case UBIFS_LPT_LSAVE: ++ return c->lsave_sz; ++ } ++ return 0; ++} ++ ++/** ++ * get_pad_len - return the length of padding in a buffer. ++ * @c: UBIFS file-system description object ++ * @buf: buffer ++ * @len: length of buffer ++ */ ++static int get_pad_len(struct ubifs_info *c, uint8_t *buf, int len) ++{ ++ int offs, pad_len; ++ ++ if (c->min_io_size == 1) ++ return 0; ++ offs = c->leb_size - len; ++ pad_len = ALIGN(offs, c->min_io_size) - offs; ++ return pad_len; ++} ++ ++/** ++ * get_lpt_node_type - return type (and node number) of a node in a buffer. ++ * @c: UBIFS file-system description object ++ * @buf: buffer ++ * @node_num: node number is returned here ++ */ ++static int get_lpt_node_type(struct ubifs_info *c, uint8_t *buf, int *node_num) ++{ ++ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; ++ int pos = 0, node_type; ++ ++ node_type = ubifs_unpack_bits(&addr, &pos, UBIFS_LPT_TYPE_BITS); ++ *node_num = ubifs_unpack_bits(&addr, &pos, c->pcnt_bits); ++ return node_type; ++} ++ ++/** ++ * is_a_node - determine if a buffer contains a node. ++ * @c: UBIFS file-system description object ++ * @buf: buffer ++ * @len: length of buffer ++ * ++ * This function returns %1 if the buffer contains a node or %0 if it does not. ++ */ ++static int is_a_node(struct ubifs_info *c, uint8_t *buf, int len) ++{ ++ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; ++ int pos = 0, node_type, node_len; ++ uint16_t crc, calc_crc; ++ ++ node_type = ubifs_unpack_bits(&addr, &pos, UBIFS_LPT_TYPE_BITS); ++ if (node_type == UBIFS_LPT_NOT_A_NODE) ++ return 0; ++ node_len = get_lpt_node_len(c, node_type); ++ if (!node_len || node_len > len) ++ return 0; ++ pos = 0; ++ addr = buf; ++ crc = ubifs_unpack_bits(&addr, &pos, UBIFS_LPT_CRC_BITS); ++ calc_crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, ++ node_len - UBIFS_LPT_CRC_BYTES); ++ if (crc != calc_crc) ++ return 0; ++ return 1; ++} ++ ++ ++/** ++ * lpt_gc_lnum - garbage collect a LPT LEB. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number to garbage collect ++ * ++ * LPT garbage collection is used only for the "big" LPT model ++ * (c->big_lpt == 1). Garbage collection simply involves marking all the nodes ++ * in the LEB being garbage-collected as dirty. The dirty nodes are written ++ * next commit, after which the LEB is free to be reused. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int lpt_gc_lnum(struct ubifs_info *c, int lnum) ++{ ++ int err, len = c->leb_size, node_type, node_num, node_len, offs; ++ void *buf = c->lpt_buf; ++ ++ dbg_lp("LEB %d", lnum); ++ err = ubi_read(c->ubi, lnum, buf, 0, c->leb_size); ++ if (err) { ++ ubifs_err("cannot read LEB %d, error %d", lnum, err); ++ return err; ++ } ++ while (1) { ++ if (!is_a_node(c, buf, len)) { ++ int pad_len; ++ ++ pad_len = get_pad_len(c, buf, len); ++ if (pad_len) { ++ buf += pad_len; ++ len -= pad_len; ++ continue; ++ } ++ return 0; ++ } ++ node_type = get_lpt_node_type(c, buf, &node_num); ++ node_len = get_lpt_node_len(c, node_type); ++ offs = c->leb_size - len; ++ ubifs_assert(node_len != 0); ++ mutex_lock(&c->lp_mutex); ++ err = make_node_dirty(c, node_type, node_num, lnum, offs); ++ mutex_unlock(&c->lp_mutex); ++ if (err) ++ return err; ++ buf += node_len; ++ len -= node_len; ++ } ++ return 0; ++} ++ ++/** ++ * lpt_gc - LPT garbage collection. ++ * @c: UBIFS file-system description object ++ * ++ * Select a LPT LEB for LPT garbage collection and call 'lpt_gc_lnum()'. ++ * Returns %0 on success and a negative error code on failure. ++ */ ++static int lpt_gc(struct ubifs_info *c) ++{ ++ int i, lnum = -1, dirty = 0; ++ ++ mutex_lock(&c->lp_mutex); ++ for (i = 0; i < c->lpt_lebs; i++) { ++ ubifs_assert(!c->ltab[i].tgc); ++ if (i + c->lpt_first == c->nhead_lnum || ++ c->ltab[i].free + c->ltab[i].dirty == c->leb_size) ++ continue; ++ if (c->ltab[i].dirty > dirty) { ++ dirty = c->ltab[i].dirty; ++ lnum = i + c->lpt_first; ++ } ++ } ++ mutex_unlock(&c->lp_mutex); ++ if (lnum == -1) ++ return -ENOSPC; ++ return lpt_gc_lnum(c, lnum); ++} ++ ++/** ++ * ubifs_lpt_post_commit - post commit LPT trivial GC and LPT GC. ++ * @c: UBIFS file-system description object ++ * ++ * LPT trivial GC is completed after a commit. Also LPT GC is done after a ++ * commit for the "big" LPT model. ++ */ ++int ubifs_lpt_post_commit(struct ubifs_info *c) ++{ ++ int err; ++ ++ mutex_lock(&c->lp_mutex); ++ err = lpt_tgc_end(c); ++ if (err) ++ goto out; ++ if (c->big_lpt) ++ while (need_write_all(c)) { ++ mutex_unlock(&c->lp_mutex); ++ err = lpt_gc(c); ++ if (err) ++ return err; ++ mutex_lock(&c->lp_mutex); ++ } ++out: ++ mutex_unlock(&c->lp_mutex); ++ return err; ++} ++ ++/** ++ * first_nnode - find the first nnode in memory. ++ * @c: UBIFS file-system description object ++ * @hght: height of tree where nnode found is returned here ++ * ++ * This function returns a pointer to the nnode found or %NULL if no nnode is ++ * found. This function is a helper to 'ubifs_lpt_free()'. ++ */ ++static struct ubifs_nnode *first_nnode(struct ubifs_info *c, int *hght) ++{ ++ struct ubifs_nnode *nnode; ++ int h, i, found; ++ ++ nnode = c->nroot; ++ *hght = 0; ++ if (!nnode) ++ return NULL; ++ for (h = 1; h < c->lpt_hght; h++) { ++ found = 0; ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ if (nnode->nbranch[i].nnode) { ++ found = 1; ++ nnode = nnode->nbranch[i].nnode; ++ *hght = h; ++ break; ++ } ++ } ++ if (!found) ++ break; ++ } ++ return nnode; ++} ++ ++/** ++ * next_nnode - find the next nnode in memory. ++ * @c: UBIFS file-system description object ++ * @nnode: nnode from which to start. ++ * @hght: height of tree where nnode is, is passed and returned here ++ * ++ * This function returns a pointer to the nnode found or %NULL if no nnode is ++ * found. This function is a helper to 'ubifs_lpt_free()'. ++ */ ++static struct ubifs_nnode *next_nnode(struct ubifs_info *c, ++ struct ubifs_nnode *nnode, int *hght) ++{ ++ struct ubifs_nnode *parent; ++ int iip, h, i, found; ++ ++ parent = nnode->parent; ++ if (!parent) ++ return NULL; ++ if (nnode->iip == UBIFS_LPT_FANOUT - 1) { ++ *hght -= 1; ++ return parent; ++ } ++ for (iip = nnode->iip + 1; iip < UBIFS_LPT_FANOUT; iip++) { ++ nnode = parent->nbranch[iip].nnode; ++ if (nnode) ++ break; ++ } ++ if (!nnode) { ++ *hght -= 1; ++ return parent; ++ } ++ for (h = *hght + 1; h < c->lpt_hght; h++) { ++ found = 0; ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ if (nnode->nbranch[i].nnode) { ++ found = 1; ++ nnode = nnode->nbranch[i].nnode; ++ *hght = h; ++ break; ++ } ++ } ++ if (!found) ++ break; ++ } ++ return nnode; ++} ++ ++/** ++ * ubifs_lpt_free - free resources owned by the LPT. ++ * @c: UBIFS file-system description object ++ * @wr_only: free only resources used for writing ++ */ ++void ubifs_lpt_free(struct ubifs_info *c, int wr_only) ++{ ++ struct ubifs_nnode *nnode; ++ int i, hght; ++ ++ /* Free write-only things first */ ++ ++ free_obsolete_cnodes(c); /* Leftover from a failed commit */ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(c->ltab_cmt); ++ kfree(c->lpt_buf); ++#else ++ vfree(c->ltab_cmt); ++ vfree(c->lpt_buf); ++#endif ++ c->ltab_cmt = NULL; ++ c->lpt_buf = NULL; ++ kfree(c->lsave); ++ c->lsave = NULL; ++ ++ if (wr_only) ++ return; ++ ++ /* Now free the rest */ ++ ++ nnode = first_nnode(c, &hght); ++ while (nnode) { ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) ++ kfree(nnode->nbranch[i].nnode); ++ nnode = next_nnode(c, nnode, &hght); ++ } ++ for (i = 0; i < LPROPS_HEAP_CNT; i++) ++ kfree(c->lpt_heap[i].arr); ++ kfree(c->dirty_idx.arr); ++ kfree(c->nroot); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(c->ltab); ++#else ++ vfree(c->ltab); ++#endif ++ kfree(c->lpt_nod_buf); ++} ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ ++/** ++ * dbg_is_all_ff - determine if a buffer contains only 0xff bytes. ++ * @buf: buffer ++ * @len: buffer length ++ */ ++static int dbg_is_all_ff(uint8_t *buf, int len) ++{ ++ int i; ++ ++ for (i = 0; i < len; i++) ++ if (buf[i] != 0xff) ++ return 0; ++ return 1; ++} ++ ++/** ++ * dbg_is_nnode_dirty - determine if a nnode is dirty. ++ * @c: the UBIFS file-system description object ++ * @lnum: LEB number where nnode was written ++ * @offs: offset where nnode was written ++ */ ++static int dbg_is_nnode_dirty(struct ubifs_info *c, int lnum, int offs) ++{ ++ struct ubifs_nnode *nnode; ++ int hght; ++ ++ /* Entire tree is in memory so first_nnode / next_nnode are ok */ ++ nnode = first_nnode(c, &hght); ++ for (; nnode; nnode = next_nnode(c, nnode, &hght)) { ++ struct ubifs_nbranch *branch; ++ ++ cond_resched(); ++ if (nnode->parent) { ++ branch = &nnode->parent->nbranch[nnode->iip]; ++ if (branch->lnum != lnum || branch->offs != offs) ++ continue; ++ if (test_bit(DIRTY_CNODE, &nnode->flags)) ++ return 1; ++ return 0; ++ } else { ++ if (c->lpt_lnum != lnum || c->lpt_offs != offs) ++ continue; ++ if (test_bit(DIRTY_CNODE, &nnode->flags)) ++ return 1; ++ return 0; ++ } ++ } ++ return 1; ++} ++ ++/** ++ * dbg_is_pnode_dirty - determine if a pnode is dirty. ++ * @c: the UBIFS file-system description object ++ * @lnum: LEB number where pnode was written ++ * @offs: offset where pnode was written ++ */ ++static int dbg_is_pnode_dirty(struct ubifs_info *c, int lnum, int offs) ++{ ++ int i, cnt; ++ ++ cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT); ++ for (i = 0; i < cnt; i++) { ++ struct ubifs_pnode *pnode; ++ struct ubifs_nbranch *branch; ++ ++ cond_resched(); ++ pnode = pnode_lookup(c, i); ++ if (IS_ERR(pnode)) ++ return PTR_ERR(pnode); ++ branch = &pnode->parent->nbranch[pnode->iip]; ++ if (branch->lnum != lnum || branch->offs != offs) ++ continue; ++ if (test_bit(DIRTY_CNODE, &pnode->flags)) ++ return 1; ++ return 0; ++ } ++ return 1; ++} ++ ++/** ++ * dbg_is_ltab_dirty - determine if a ltab node is dirty. ++ * @c: the UBIFS file-system description object ++ * @lnum: LEB number where ltab node was written ++ * @offs: offset where ltab node was written ++ */ ++static int dbg_is_ltab_dirty(struct ubifs_info *c, int lnum, int offs) ++{ ++ if (lnum != c->ltab_lnum || offs != c->ltab_offs) ++ return 1; ++ return (c->lpt_drty_flgs & LTAB_DIRTY) != 0; ++} ++ ++/** ++ * dbg_is_lsave_dirty - determine if a lsave node is dirty. ++ * @c: the UBIFS file-system description object ++ * @lnum: LEB number where lsave node was written ++ * @offs: offset where lsave node was written ++ */ ++static int dbg_is_lsave_dirty(struct ubifs_info *c, int lnum, int offs) ++{ ++ if (lnum != c->lsave_lnum || offs != c->lsave_offs) ++ return 1; ++ return (c->lpt_drty_flgs & LSAVE_DIRTY) != 0; ++} ++ ++/** ++ * dbg_is_node_dirty - determine if a node is dirty. ++ * @c: the UBIFS file-system description object ++ * @node_type: node type ++ * @lnum: LEB number where node was written ++ * @offs: offset where node was written ++ */ ++static int dbg_is_node_dirty(struct ubifs_info *c, int node_type, int lnum, ++ int offs) ++{ ++ switch (node_type) { ++ case UBIFS_LPT_NNODE: ++ return dbg_is_nnode_dirty(c, lnum, offs); ++ case UBIFS_LPT_PNODE: ++ return dbg_is_pnode_dirty(c, lnum, offs); ++ case UBIFS_LPT_LTAB: ++ return dbg_is_ltab_dirty(c, lnum, offs); ++ case UBIFS_LPT_LSAVE: ++ return dbg_is_lsave_dirty(c, lnum, offs); ++ } ++ return 1; ++} ++ ++/** ++ * dbg_check_ltab_lnum - check the ltab for a LPT LEB number. ++ * @c: the UBIFS file-system description object ++ * @lnum: LEB number where node was written ++ * @offs: offset where node was written ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum) ++{ ++ int err, len = c->leb_size, dirty = 0, node_type, node_num, node_len; ++ int ret; ++ void *buf = c->dbg_buf; ++ ++ dbg_lp("LEB %d", lnum); ++ err = ubi_read(c->ubi, lnum, buf, 0, c->leb_size); ++ if (err) { ++ dbg_msg("ubi_read failed, LEB %d, error %d", lnum, err); ++ return err; ++ } ++ while (1) { ++ if (!is_a_node(c, buf, len)) { ++ int i, pad_len; ++ ++ pad_len = get_pad_len(c, buf, len); ++ if (pad_len) { ++ buf += pad_len; ++ len -= pad_len; ++ dirty += pad_len; ++ continue; ++ } ++ if (!dbg_is_all_ff(buf, len)) { ++ dbg_msg("invalid empty space in LEB %d at %d", ++ lnum, c->leb_size - len); ++ err = -EINVAL; ++ } ++ i = lnum - c->lpt_first; ++ if (len != c->ltab[i].free) { ++ dbg_msg("invalid free space in LEB %d " ++ "(free %d, expected %d)", ++ lnum, len, c->ltab[i].free); ++ err = -EINVAL; ++ } ++ if (dirty != c->ltab[i].dirty) { ++ dbg_msg("invalid dirty space in LEB %d " ++ "(dirty %d, expected %d)", ++ lnum, dirty, c->ltab[i].dirty); ++ err = -EINVAL; ++ } ++ return err; ++ } ++ node_type = get_lpt_node_type(c, buf, &node_num); ++ node_len = get_lpt_node_len(c, node_type); ++ ret = dbg_is_node_dirty(c, node_type, lnum, c->leb_size - len); ++ if (ret == 1) ++ dirty += node_len; ++ buf += node_len; ++ len -= node_len; ++ } ++} ++ ++/** ++ * dbg_check_ltab - check the free and dirty space in the ltab. ++ * @c: the UBIFS file-system description object ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int dbg_check_ltab(struct ubifs_info *c) ++{ ++ int lnum, err, i, cnt; ++ ++ if (!(ubifs_chk_flags & UBIFS_CHK_LPROPS)) ++ return 0; ++ ++ /* Bring the entire tree into memory */ ++ cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT); ++ for (i = 0; i < cnt; i++) { ++ struct ubifs_pnode *pnode; ++ ++ pnode = pnode_lookup(c, i); ++ if (IS_ERR(pnode)) ++ return PTR_ERR(pnode); ++ cond_resched(); ++ } ++ ++ /* Check nodes */ ++ err = dbg_check_lpt_nodes(c, (struct ubifs_cnode *)c->nroot, 0, 0); ++ if (err) ++ return err; ++ ++ /* Check each LEB */ ++ for (lnum = c->lpt_first; lnum <= c->lpt_last; lnum++) { ++ err = dbg_check_ltab_lnum(c, lnum); ++ if (err) { ++ dbg_err("failed at LEB %d", lnum); ++ return err; ++ } ++ } ++ ++ dbg_lp("succeeded"); ++ return 0; ++} ++ ++#endif /* CONFIG_UBIFS_FS_DEBUG */ +diff -urN linux-2.6.24.7.old/fs/ubifs/master.c linux-2.6.24.7/fs/ubifs/master.c +--- linux-2.6.24.7.old/fs/ubifs/master.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/master.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,387 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* This file implements reading and writing the master node */ ++ ++#include "ubifs.h" ++ ++/** ++ * scan_for_master - search the valid master node. ++ * @c: UBIFS file-system description object ++ * ++ * This function scans the master node LEBs and search for the latest master ++ * node. Returns zero in case of success and a negative error code in case of ++ * failure. ++ */ ++static int scan_for_master(struct ubifs_info *c) ++{ ++ struct ubifs_scan_leb *sleb; ++ struct ubifs_scan_node *snod; ++ int lnum, offs = 0, nodes_cnt; ++ ++ lnum = UBIFS_MST_LNUM; ++ ++ sleb = ubifs_scan(c, lnum, 0, c->sbuf); ++ if (IS_ERR(sleb)) ++ return PTR_ERR(sleb); ++ nodes_cnt = sleb->nodes_cnt; ++ if (nodes_cnt > 0) { ++ snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, ++ list); ++ if (snod->type != UBIFS_MST_NODE) ++ goto out; ++ memcpy(c->mst_node, snod->node, snod->len); ++ offs = snod->offs; ++ } ++ ubifs_scan_destroy(sleb); ++ ++ lnum += 1; ++ ++ sleb = ubifs_scan(c, lnum, 0, c->sbuf); ++ if (IS_ERR(sleb)) ++ return PTR_ERR(sleb); ++ if (sleb->nodes_cnt != nodes_cnt) ++ goto out; ++ if (!sleb->nodes_cnt) ++ goto out; ++ snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, list); ++ if (snod->type != UBIFS_MST_NODE) ++ goto out; ++ if (snod->offs != offs) ++ goto out; ++ if (memcmp((void *)c->mst_node + UBIFS_CH_SZ, ++ (void *)snod->node + UBIFS_CH_SZ, ++ UBIFS_MST_NODE_SZ - UBIFS_CH_SZ)) ++ goto out; ++ c->mst_offs = offs; ++ ubifs_scan_destroy(sleb); ++ return 0; ++ ++out: ++ ubifs_scan_destroy(sleb); ++ return -EINVAL; ++} ++ ++/** ++ * validate_master - validate master node. ++ * @c: UBIFS file-system description object ++ * ++ * This function validates data which was read from master node. Returns zero ++ * if the data is all right and %-EINVAL if not. ++ */ ++static int validate_master(const struct ubifs_info *c) ++{ ++ unsigned long long main_sz; ++ int err; ++ ++ if (c->max_sqnum >= SQNUM_WATERMARK) { ++ err = 1; ++ goto out; ++ } ++ ++ if (c->cmt_no >= c->max_sqnum) { ++ err = 2; ++ goto out; ++ } ++ ++ if (c->highest_inum >= INUM_WATERMARK) { ++ err = 3; ++ goto out; ++ } ++ ++ if (c->lhead_lnum < UBIFS_LOG_LNUM || ++ c->lhead_lnum >= UBIFS_LOG_LNUM + c->log_lebs || ++ c->lhead_offs < 0 || c->lhead_offs >= c->leb_size || ++ c->lhead_offs & (c->min_io_size - 1)) { ++ err = 4; ++ goto out; ++ } ++ ++ if (c->zroot.lnum >= c->leb_cnt || c->zroot.lnum < c->main_first || ++ c->zroot.offs >= c->leb_size || c->zroot.offs & 7) { ++ err = 5; ++ goto out; ++ } ++ ++ if (c->zroot.len < c->ranges[UBIFS_IDX_NODE].min_len || ++ c->zroot.len > c->ranges[UBIFS_IDX_NODE].max_len) { ++ err = 6; ++ goto out; ++ } ++ ++ if (c->gc_lnum >= c->leb_cnt || c->gc_lnum < c->main_first) { ++ err = 7; ++ goto out; ++ } ++ ++ if (c->ihead_lnum >= c->leb_cnt || c->ihead_lnum < c->main_first || ++ c->ihead_offs % c->min_io_size || c->ihead_offs < 0 || ++ c->ihead_offs > c->leb_size || c->ihead_offs & 7) { ++ err = 8; ++ goto out; ++ } ++ ++ main_sz = c->main_lebs * (unsigned long long)c->leb_size; ++ if (c->old_idx_sz & 7 || c->old_idx_sz >= main_sz) { ++ err = 9; ++ goto out; ++ } ++ ++ if (c->lpt_lnum < c->lpt_first || c->lpt_lnum > c->lpt_last || ++ c->lpt_offs < 0 || c->lpt_offs + c->nnode_sz > c->leb_size) { ++ err = 10; ++ goto out; ++ } ++ ++ if (c->nhead_lnum < c->lpt_first || c->nhead_lnum > c->lpt_last || ++ c->nhead_offs < 0 || c->nhead_offs % c->min_io_size || ++ c->nhead_offs > c->leb_size) { ++ err = 11; ++ goto out; ++ } ++ ++ if (c->ltab_lnum < c->lpt_first || c->ltab_lnum > c->lpt_last || ++ c->ltab_offs < 0 || ++ c->ltab_offs + c->ltab_sz > c->leb_size) { ++ err = 12; ++ goto out; ++ } ++ ++ if (c->big_lpt && (c->lsave_lnum < c->lpt_first || ++ c->lsave_lnum > c->lpt_last || c->lsave_offs < 0 || ++ c->lsave_offs + c->lsave_sz > c->leb_size)) { ++ err = 13; ++ goto out; ++ } ++ ++ if (c->lscan_lnum < c->main_first || c->lscan_lnum >= c->leb_cnt) { ++ err = 14; ++ goto out; ++ } ++ ++ if (c->lst.empty_lebs < 0 || c->lst.empty_lebs > c->main_lebs - 2) { ++ err = 15; ++ goto out; ++ } ++ ++ if (c->lst.idx_lebs < 0 || c->lst.idx_lebs > c->main_lebs - 1) { ++ err = 16; ++ goto out; ++ } ++ ++ if (c->lst.total_free < 0 || c->lst.total_free > main_sz || ++ c->lst.total_free & 7) { ++ err = 17; ++ goto out; ++ } ++ ++ if (c->lst.total_dirty < 0 || (c->lst.total_dirty & 7)) { ++ err = 18; ++ goto out; ++ } ++ ++ if (c->lst.total_used < 0 || (c->lst.total_used & 7)) { ++ err = 19; ++ goto out; ++ } ++ ++ if (c->lst.total_free + c->lst.total_dirty + ++ c->lst.total_used > main_sz) { ++ err = 20; ++ goto out; ++ } ++ ++ if (c->lst.total_dead + c->lst.total_dark + ++ c->lst.total_used + c->old_idx_sz > main_sz) { ++ err = 21; ++ goto out; ++ } ++ ++ if (c->lst.total_dead < 0 || ++ c->lst.total_dead > c->lst.total_free + c->lst.total_dirty || ++ c->lst.total_dead & 7) { ++ err = 22; ++ goto out; ++ } ++ ++ if (c->lst.total_dark < 0 || ++ c->lst.total_dark > c->lst.total_free + c->lst.total_dirty || ++ c->lst.total_dark & 7) { ++ err = 23; ++ goto out; ++ } ++ ++ return 0; ++ ++out: ++ ubifs_err("bad master node at offset %d error %d", c->mst_offs, err); ++ dbg_dump_node(c, c->mst_node); ++ return -EINVAL; ++} ++ ++/** ++ * ubifs_read_master - read master node. ++ * @c: UBIFS file-system description object ++ * ++ * This function finds and reads the master node during file-system mount. If ++ * the flash is empty, it creates default master node as well. Returns zero in ++ * case of success and a negative error code in case of failure. ++ */ ++int ubifs_read_master(struct ubifs_info *c) ++{ ++ int err, old_leb_cnt; ++ ++ c->mst_node = kzalloc(c->mst_node_alsz, GFP_KERNEL); ++ if (!c->mst_node) ++ return -ENOMEM; ++ ++ err = scan_for_master(c); ++ if (err) { ++ err = ubifs_recover_master_node(c); ++ if (err) ++ /* ++ * Note, we do not free 'c->mst_node' here because the ++ * unmount routine will take care of this. ++ */ ++ return err; ++ } ++ ++ /* Make sure that the recovery flag is clear */ ++ c->mst_node->flags &= cpu_to_le32(~UBIFS_MST_RCVRY); ++ ++ c->max_sqnum = le64_to_cpu(c->mst_node->ch.sqnum); ++ c->highest_inum = le64_to_cpu(c->mst_node->highest_inum); ++ c->cmt_no = le64_to_cpu(c->mst_node->cmt_no); ++ c->zroot.lnum = le32_to_cpu(c->mst_node->root_lnum); ++ c->zroot.offs = le32_to_cpu(c->mst_node->root_offs); ++ c->zroot.len = le32_to_cpu(c->mst_node->root_len); ++ c->lhead_lnum = le32_to_cpu(c->mst_node->log_lnum); ++ c->gc_lnum = le32_to_cpu(c->mst_node->gc_lnum); ++ c->ihead_lnum = le32_to_cpu(c->mst_node->ihead_lnum); ++ c->ihead_offs = le32_to_cpu(c->mst_node->ihead_offs); ++ c->old_idx_sz = le64_to_cpu(c->mst_node->index_size); ++ c->lpt_lnum = le32_to_cpu(c->mst_node->lpt_lnum); ++ c->lpt_offs = le32_to_cpu(c->mst_node->lpt_offs); ++ c->nhead_lnum = le32_to_cpu(c->mst_node->nhead_lnum); ++ c->nhead_offs = le32_to_cpu(c->mst_node->nhead_offs); ++ c->ltab_lnum = le32_to_cpu(c->mst_node->ltab_lnum); ++ c->ltab_offs = le32_to_cpu(c->mst_node->ltab_offs); ++ c->lsave_lnum = le32_to_cpu(c->mst_node->lsave_lnum); ++ c->lsave_offs = le32_to_cpu(c->mst_node->lsave_offs); ++ c->lscan_lnum = le32_to_cpu(c->mst_node->lscan_lnum); ++ c->lst.empty_lebs = le32_to_cpu(c->mst_node->empty_lebs); ++ c->lst.idx_lebs = le32_to_cpu(c->mst_node->idx_lebs); ++ old_leb_cnt = le32_to_cpu(c->mst_node->leb_cnt); ++ c->lst.total_free = le64_to_cpu(c->mst_node->total_free); ++ c->lst.total_dirty = le64_to_cpu(c->mst_node->total_dirty); ++ c->lst.total_used = le64_to_cpu(c->mst_node->total_used); ++ c->lst.total_dead = le64_to_cpu(c->mst_node->total_dead); ++ c->lst.total_dark = le64_to_cpu(c->mst_node->total_dark); ++ ++ c->calc_idx_sz = c->old_idx_sz; ++ ++ if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS)) ++ c->no_orphs = 1; ++ ++ if (old_leb_cnt != c->leb_cnt) { ++ /* The file system has been resized */ ++ int growth = c->leb_cnt - old_leb_cnt; ++ ++ if (c->leb_cnt < old_leb_cnt || ++ c->leb_cnt < UBIFS_MIN_LEB_CNT) { ++ ubifs_err("bad leb_cnt on master node"); ++ dbg_dump_node(c, c->mst_node); ++ return -EINVAL; ++ } ++ ++ dbg_mnt("Auto resizing (master) from %d LEBs to %d LEBs", ++ old_leb_cnt, c->leb_cnt); ++ c->lst.empty_lebs += growth; ++ c->lst.total_free += growth * (long long)c->leb_size; ++ c->lst.total_dark += growth * (long long)c->dark_wm; ++ ++ /* ++ * Reflect changes back onto the master node. N.B. the master ++ * node gets written immediately whenever mounting (or ++ * remounting) in read-write mode, so we do not need to write it ++ * here. ++ */ ++ c->mst_node->leb_cnt = cpu_to_le32(c->leb_cnt); ++ c->mst_node->empty_lebs = cpu_to_le32(c->lst.empty_lebs); ++ c->mst_node->total_free = cpu_to_le64(c->lst.total_free); ++ c->mst_node->total_dark = cpu_to_le64(c->lst.total_dark); ++ } ++ ++ err = validate_master(c); ++ if (err) ++ return err; ++ ++ err = dbg_old_index_check_init(c, &c->zroot); ++ ++ return err; ++} ++ ++/** ++ * ubifs_write_master - write master node. ++ * @c: UBIFS file-system description object ++ * ++ * This function writes the master node. The caller has to take the ++ * @c->mst_mutex lock before calling this function. Returns zero in case of ++ * success and a negative error code in case of failure. The master node is ++ * written twice to enable recovery. ++ */ ++int ubifs_write_master(struct ubifs_info *c) ++{ ++ int err, lnum, offs, len; ++ ++ if (c->ro_media) ++ return -EINVAL; ++ ++ lnum = UBIFS_MST_LNUM; ++ offs = c->mst_offs + c->mst_node_alsz; ++ len = UBIFS_MST_NODE_SZ; ++ ++ if (offs + UBIFS_MST_NODE_SZ > c->leb_size) { ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ return err; ++ offs = 0; ++ } ++ ++ c->mst_offs = offs; ++ c->mst_node->highest_inum = cpu_to_le64(c->highest_inum); ++ ++ err = ubifs_write_node(c, c->mst_node, len, lnum, offs, UBI_SHORTTERM); ++ if (err) ++ return err; ++ ++ lnum += 1; ++ ++ if (offs == 0) { ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ return err; ++ } ++ err = ubifs_write_node(c, c->mst_node, len, lnum, offs, UBI_SHORTTERM); ++ ++ return err; ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/misc.h linux-2.6.24.7/fs/ubifs/misc.h +--- linux-2.6.24.7.old/fs/ubifs/misc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/misc.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,310 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This file contains miscellaneous helper functions. ++ */ ++ ++#ifndef __UBIFS_MISC_H__ ++#define __UBIFS_MISC_H__ ++ ++/** ++ * ubifs_zn_dirty - check if znode is dirty. ++ * @znode: znode to check ++ * ++ * This helper function returns %1 if @znode is dirty and %0 otherwise. ++ */ ++static inline int ubifs_zn_dirty(const struct ubifs_znode *znode) ++{ ++ return !!test_bit(DIRTY_ZNODE, &znode->flags); ++} ++ ++/** ++ * ubifs_wake_up_bgt - wake up background thread. ++ * @c: UBIFS file-system description object ++ */ ++static inline void ubifs_wake_up_bgt(struct ubifs_info *c) ++{ ++ if (c->bgt && !c->need_bgt) { ++ c->need_bgt = 1; ++ wake_up_process(c->bgt); ++ } ++} ++ ++/** ++ * ubifs_tnc_find_child - find next child in znode. ++ * @znode: znode to search at ++ * @start: the zbranch index to start at ++ * ++ * This helper function looks for znode child starting at index @start. Returns ++ * the child or %NULL if no children were found. ++ */ ++static inline struct ubifs_znode * ++ubifs_tnc_find_child(struct ubifs_znode *znode, int start) ++{ ++ while (start < znode->child_cnt) { ++ if (znode->zbranch[start].znode) ++ return znode->zbranch[start].znode; ++ start += 1; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * ubifs_inode - get UBIFS inode information by VFS 'struct inode' object. ++ * @inode: the VFS 'struct inode' pointer ++ */ ++static inline struct ubifs_inode *ubifs_inode(const struct inode *inode) ++{ ++ return container_of(inode, struct ubifs_inode, vfs_inode); ++} ++ ++/** ++ * ubifs_ro_mode - switch UBIFS to read read-only mode. ++ * @c: UBIFS file-system description object ++ * @err: error code which is the reason of switching to R/O mode ++ */ ++static inline void ubifs_ro_mode(struct ubifs_info *c, int err) ++{ ++ if (!c->ro_media) { ++ c->ro_media = 1; ++ ubifs_warn("switched to read-only mode, error %d", err); ++ dbg_dump_stack(); ++ } ++} ++ ++/** ++ * ubifs_compr_present - check if compressor was compiled in. ++ * @compr_type: compressor type to check ++ * ++ * This function returns %1 of compressor of type @compr_type is present, and ++ * %0 if not. ++ */ ++static inline int ubifs_compr_present(int compr_type) ++{ ++ ubifs_assert(compr_type >= 0 && compr_type < UBIFS_COMPR_TYPES_CNT); ++ return !!ubifs_compressors[compr_type]->capi_name; ++} ++ ++/** ++ * ubifs_compr_name - get compressor name string by its type. ++ * @compr_type: compressor type ++ * ++ * This function returns compressor type string. ++ */ ++static inline const char *ubifs_compr_name(int compr_type) ++{ ++ ubifs_assert(compr_type >= 0 && compr_type < UBIFS_COMPR_TYPES_CNT); ++ return ubifs_compressors[compr_type]->name; ++} ++ ++/** ++ * ubifs_wbuf_sync - synchronize write-buffer. ++ * @wbuf: write-buffer to synchronize ++ * ++ * This is the same as as 'ubifs_wbuf_sync_nolock()' but it does not assume ++ * that the write-buffer is already locked. ++ */ ++static inline int ubifs_wbuf_sync(struct ubifs_wbuf *wbuf) ++{ ++ int err; ++ ++ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); ++ err = ubifs_wbuf_sync_nolock(wbuf); ++ mutex_unlock(&wbuf->io_mutex); ++ return err; ++} ++ ++/** ++ * ubifs_leb_unmap - unmap an LEB. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number to unmap ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static inline int ubifs_leb_unmap(const struct ubifs_info *c, int lnum) ++{ ++ int err; ++ ++ err = ubi_leb_unmap(c->ubi, lnum); ++ if (err) { ++ ubifs_err("unmap LEB %d failed, error %d", lnum, err); ++ return err; ++ } ++ ++ return 0; ++} ++ ++/** ++ * ubifs_leb_write - write to a LEB. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number to write ++ * @buf: buffer to write from ++ * @offs: offset within LEB to write to ++ * @len: length to write ++ * @dtype: data type ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static inline int ubifs_leb_write(const struct ubifs_info *c, int lnum, ++ const void *buf, int offs, int len, int dtype) ++{ ++ int err; ++ ++ err = ubi_leb_write(c->ubi, lnum, buf, offs, len, dtype); ++ if (err) { ++ ubifs_err("writing %d bytes at %d:%d, error %d", ++ len, lnum, offs, err); ++ return err; ++ } ++ ++ return 0; ++} ++ ++/** ++ * ubifs_encode_dev - encode device node IDs. ++ * @dev: UBIFS device node information ++ * @rdev: device IDs to encode ++ * ++ * This is a helper function which encodes major/minor numbers of a device node ++ * into UBIFS device node description. We use standard Linux "new" and "huge" ++ * encodings. ++ */ ++static inline int ubifs_encode_dev(union ubifs_dev_desc *dev, dev_t rdev) ++{ ++ if (new_valid_dev(rdev)) { ++ dev->new = cpu_to_le32(new_encode_dev(rdev)); ++ return sizeof(dev->new); ++ } else { ++ dev->huge = cpu_to_le64(huge_encode_dev(rdev)); ++ return sizeof(dev->huge); ++ } ++} ++ ++/** ++ * ubifs_add_dirt - add dirty space to LEB properties. ++ * @c: the UBIFS file-system description object ++ * @lnum: LEB to add dirty space for ++ * @dirty: dirty space to add ++ * ++ * This is a helper function which increased amount of dirty LEB space. Returns ++ * zero in case of success and a negative error code in case of failure. ++ */ ++static inline int ubifs_add_dirt(struct ubifs_info *c, int lnum, int dirty) ++{ ++ return ubifs_update_one_lp(c, lnum, -1, dirty, 0, 0); ++} ++ ++/** ++ * ubifs_return_leb - return LEB to lprops. ++ * @c: the UBIFS file-system description object ++ * @lnum: LEB to return ++ * ++ * This helper function cleans the "taken" flag of a logical eraseblock in the ++ * lprops. Returns zero in case of success and a negative error code in case of ++ * failure. ++ */ ++static inline int ubifs_return_leb(struct ubifs_info *c, int lnum) ++{ ++ return ubifs_change_one_lp(c, lnum, -1, -1, 0, LPROPS_TAKEN, 0); ++} ++ ++/** ++ * ubifs_idx_node_sz - return index node size. ++ * @c: the UBIFS file-system description object ++ * @child_cnt: number of children of this index node ++ */ ++static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt) ++{ ++ return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len) * child_cnt; ++} ++ ++/** ++ * ubifs_idx_branch - return pointer to an index branch. ++ * @c: the UBIFS file-system description object ++ * @idx: index node ++ * @bnum: branch number ++ */ ++static inline ++struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c, ++ const struct ubifs_idx_node *idx, ++ int bnum) ++{ ++ return (struct ubifs_branch *)((void *)idx->branches + ++ (UBIFS_BRANCH_SZ + c->key_len) * bnum); ++} ++ ++/** ++ * ubifs_idx_key - return pointer to an index key. ++ * @c: the UBIFS file-system description object ++ * @idx: index node ++ */ ++static inline void *ubifs_idx_key(const struct ubifs_info *c, ++ const struct ubifs_idx_node *idx) ++{ ++ return (void *)((struct ubifs_branch *)idx->branches)->key; ++} ++ ++/** ++ * ubifs_reported_space - calculate reported free space. ++ * @c: the UBIFS file-system description object ++ * @free: amount of free space ++ * ++ * This function calculates amount of free space which will be reported to ++ * user-space. User-space application tend to expect that if the file-system ++ * (e.g., via the 'statfs()' call) reports that it has N bytes available, they ++ * are able to write a file of size N. UBIFS attaches node headers to each data ++ * node and it has to write indexind nodes as well. This introduces additional ++ * overhead, and UBIFS it has to report sligtly less free space to meet the ++ * above expectetion. ++ * ++ * This function assumes free space is made up of uncompressed data nodes and ++ * full index nodes (one per data node, doubled because we always allow enough ++ * space to write the index twice). ++ * ++ * Note, the calculation is pessimistic, which means that most of the time ++ * UBIFS reports less space than it actually has. ++ */ ++static inline long long ubifs_reported_space(const struct ubifs_info *c, ++ uint64_t free) ++{ ++ int divisor, factor; ++ ++ divisor = UBIFS_MAX_DATA_NODE_SZ + (c->max_idx_node_sz << 1); ++ factor = UBIFS_MAX_DATA_NODE_SZ - UBIFS_DATA_NODE_SZ; ++ do_div(free, divisor); ++ ++ return free * factor; ++} ++ ++/** ++ * ubifs_current_time - round current time to time granularity. ++ * @inode: inode ++ */ ++static inline struct timespec ubifs_current_time(struct inode *inode) ++{ ++ return (inode->i_sb->s_time_gran < NSEC_PER_SEC) ? ++ current_fs_time(inode->i_sb) : CURRENT_TIME_SEC; ++} ++ ++#endif /* __UBIFS_MISC_H__ */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/config linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/config +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/config 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/config 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,11 @@ ++[core] ++ repositoryformatversion = 0 ++ filemode = true ++ bare = false ++ logallrefupdates = true ++[remote "origin"] ++ url = git://git.infradead.org/~dedekind/mkfs.ubifs.git ++ fetch = +refs/heads/*:refs/remotes/origin/* ++[branch "master"] ++ remote = origin ++ merge = refs/heads/master +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/description linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/description +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/description 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/description 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++Unnamed repository; edit this file to name it for gitweb. +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/head linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/head +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/head 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/head 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++ref: refs/heads/master +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/applypatch-msg linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/applypatch-msg +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/applypatch-msg 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/applypatch-msg 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,15 @@ ++#!/bin/sh ++# ++# An example hook script to check the commit log message taken by ++# applypatch from an e-mail message. ++# ++# The hook should exit with non-zero status after issuing an ++# appropriate message if it wants to stop the commit. The hook is ++# allowed to edit the commit message file. ++# ++# To enable this hook, make this file executable. ++ ++. git-sh-setup ++test -x "$GIT_DIR/hooks/commit-msg" && ++ exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} ++: +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/commit-msg linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/commit-msg +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/commit-msg 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/commit-msg 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,24 @@ ++#!/bin/sh ++# ++# An example hook script to check the commit log message. ++# Called by git-commit with one argument, the name of the file ++# that has the commit message. The hook should exit with non-zero ++# status after issuing an appropriate message if it wants to stop the ++# commit. The hook is allowed to edit the commit message file. ++# ++# To enable this hook, make this file executable. ++ ++# Uncomment the below to add a Signed-off-by line to the message. ++# Doing this in a hook is a bad idea in general, but the prepare-commit-msg ++# hook is more suited to it. ++# ++# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') ++# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" ++ ++# This example catches duplicate Signed-off-by lines. ++ ++test "" = "$(grep '^Signed-off-by: ' "$1" | ++ sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { ++ echo >&2 Duplicate Signed-off-by lines. ++ exit 1 ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/post-commit linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/post-commit +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/post-commit 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/post-commit 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,8 @@ ++#!/bin/sh ++# ++# An example hook script that is called after a successful ++# commit is made. ++# ++# To enable this hook, make this file executable. ++ ++: Nothing +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/post-receive linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/post-receive +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/post-receive 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/post-receive 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,16 @@ ++#!/bin/sh ++# ++# An example hook script for the post-receive event ++# ++# This script is run after receive-pack has accepted a pack and the ++# repository has been updated. It is passed arguments in through stdin ++# in the form ++# ++# For example: ++# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master ++# ++# see contrib/hooks/ for an sample, or uncomment the next line (on debian) ++# ++ ++ ++#. /usr/share/doc/git-core/contrib/hooks/post-receive-email +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/post-update linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/post-update +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/post-update 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/post-update 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,8 @@ ++#!/bin/sh ++# ++# An example hook script to prepare a packed repository for use over ++# dumb transports. ++# ++# To enable this hook, make this file executable by "chmod +x post-update". ++ ++exec git-update-server-info +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/pre-applypatch linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/pre-applypatch +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/pre-applypatch 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/pre-applypatch 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,14 @@ ++#!/bin/sh ++# ++# An example hook script to verify what is about to be committed ++# by applypatch from an e-mail message. ++# ++# The hook should exit with non-zero status after issuing an ++# appropriate message if it wants to stop the commit. ++# ++# To enable this hook, make this file executable. ++ ++. git-sh-setup ++test -x "$GIT_DIR/hooks/pre-commit" && ++ exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} ++: +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/pre-commit linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/pre-commit +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/pre-commit 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/pre-commit 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,70 @@ ++#!/bin/sh ++# ++# An example hook script to verify what is about to be committed. ++# Called by git-commit with no arguments. The hook should ++# exit with non-zero status after issuing an appropriate message if ++# it wants to stop the commit. ++# ++# To enable this hook, make this file executable. ++ ++# This is slightly modified from Andrew Morton's Perfect Patch. ++# Lines you introduce should not have trailing whitespace. ++# Also check for an indentation that has SP before a TAB. ++ ++if git-rev-parse --verify HEAD 2>/dev/null ++then ++ git-diff-index -p -M --cached HEAD -- ++else ++ # NEEDSWORK: we should produce a diff with an empty tree here ++ # if we want to do the same verification for the initial import. ++ : ++fi | ++perl -e ' ++ my $found_bad = 0; ++ my $filename; ++ my $reported_filename = ""; ++ my $lineno; ++ sub bad_line { ++ my ($why, $line) = @_; ++ if (!$found_bad) { ++ print STDERR "*\n"; ++ print STDERR "* You have some suspicious patch lines:\n"; ++ print STDERR "*\n"; ++ $found_bad = 1; ++ } ++ if ($reported_filename ne $filename) { ++ print STDERR "* In $filename\n"; ++ $reported_filename = $filename; ++ } ++ print STDERR "* $why (line $lineno)\n"; ++ print STDERR "$filename:$lineno:$line\n"; ++ } ++ while (<>) { ++ if (m|^diff --git a/(.*) b/\1$|) { ++ $filename = $1; ++ next; ++ } ++ if (/^@@ -\S+ \+(\d+)/) { ++ $lineno = $1 - 1; ++ next; ++ } ++ if (/^ /) { ++ $lineno++; ++ next; ++ } ++ if (s/^\+//) { ++ $lineno++; ++ chomp; ++ if (/\s$/) { ++ bad_line("trailing whitespace", $_); ++ } ++ if (/^\s* \t/) { ++ bad_line("indent SP followed by a TAB", $_); ++ } ++ if (/^([<>])\1{6} |^={7}$/) { ++ bad_line("unresolved merge conflict", $_); ++ } ++ } ++ } ++ exit($found_bad); ++' +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/pre-rebase linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/pre-rebase +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/pre-rebase 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/pre-rebase 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,150 @@ ++#!/bin/sh ++# ++# Copyright (c) 2006 Junio C Hamano ++# ++ ++publish=next ++basebranch="$1" ++if test "$#" = 2 ++then ++ topic="refs/heads/$2" ++else ++ topic=`git symbolic-ref HEAD` ++fi ++ ++case "$basebranch,$topic" in ++master,refs/heads/??/*) ++ ;; ++*) ++ exit 0 ;# we do not interrupt others. ++ ;; ++esac ++ ++# Now we are dealing with a topic branch being rebased ++# on top of master. Is it OK to rebase it? ++ ++# Is topic fully merged to master? ++not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` ++if test -z "$not_in_master" ++then ++ echo >&2 "$topic is fully merged to master; better remove it." ++ exit 1 ;# we could allow it, but there is no point. ++fi ++ ++# Is topic ever merged to next? If so you should not be rebasing it. ++only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` ++only_next_2=`git-rev-list ^master ${publish} | sort` ++if test "$only_next_1" = "$only_next_2" ++then ++ not_in_topic=`git-rev-list "^$topic" master` ++ if test -z "$not_in_topic" ++ then ++ echo >&2 "$topic is already up-to-date with master" ++ exit 1 ;# we could allow it, but there is no point. ++ else ++ exit 0 ++ fi ++else ++ not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` ++ perl -e ' ++ my $topic = $ARGV[0]; ++ my $msg = "* $topic has commits already merged to public branch:\n"; ++ my (%not_in_next) = map { ++ /^([0-9a-f]+) /; ++ ($1 => 1); ++ } split(/\n/, $ARGV[1]); ++ for my $elem (map { ++ /^([0-9a-f]+) (.*)$/; ++ [$1 => $2]; ++ } split(/\n/, $ARGV[2])) { ++ if (!exists $not_in_next{$elem->[0]}) { ++ if ($msg) { ++ print STDERR $msg; ++ undef $msg; ++ } ++ print STDERR " $elem->[1]\n"; ++ } ++ } ++ ' "$topic" "$not_in_next" "$not_in_master" ++ exit 1 ++fi ++ ++exit 0 ++ ++################################################################ ++ ++This sample hook safeguards topic branches that have been ++published from being rewound. ++ ++The workflow assumed here is: ++ ++ * Once a topic branch forks from "master", "master" is never ++ merged into it again (either directly or indirectly). ++ ++ * Once a topic branch is fully cooked and merged into "master", ++ it is deleted. If you need to build on top of it to correct ++ earlier mistakes, a new topic branch is created by forking at ++ the tip of the "master". This is not strictly necessary, but ++ it makes it easier to keep your history simple. ++ ++ * Whenever you need to test or publish your changes to topic ++ branches, merge them into "next" branch. ++ ++The script, being an example, hardcodes the publish branch name ++to be "next", but it is trivial to make it configurable via ++$GIT_DIR/config mechanism. ++ ++With this workflow, you would want to know: ++ ++(1) ... if a topic branch has ever been merged to "next". Young ++ topic branches can have stupid mistakes you would rather ++ clean up before publishing, and things that have not been ++ merged into other branches can be easily rebased without ++ affecting other people. But once it is published, you would ++ not want to rewind it. ++ ++(2) ... if a topic branch has been fully merged to "master". ++ Then you can delete it. More importantly, you should not ++ build on top of it -- other people may already want to ++ change things related to the topic as patches against your ++ "master", so if you need further changes, it is better to ++ fork the topic (perhaps with the same name) afresh from the ++ tip of "master". ++ ++Let's look at this example: ++ ++ o---o---o---o---o---o---o---o---o---o "next" ++ / / / / ++ / a---a---b A / / ++ / / / / ++ / / c---c---c---c B / ++ / / / \ / ++ / / / b---b C \ / ++ / / / / \ / ++ ---o---o---o---o---o---o---o---o---o---o---o "master" ++ ++ ++A, B and C are topic branches. ++ ++ * A has one fix since it was merged up to "next". ++ ++ * B has finished. It has been fully merged up to "master" and "next", ++ and is ready to be deleted. ++ ++ * C has not merged to "next" at all. ++ ++We would want to allow C to be rebased, refuse A, and encourage ++B to be deleted. ++ ++To compute (1): ++ ++ git-rev-list ^master ^topic next ++ git-rev-list ^master next ++ ++ if these match, topic has not merged in next at all. ++ ++To compute (2): ++ ++ git-rev-list master..topic ++ ++ if this is empty, it is fully merged to "master". +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/prepare-commit-msg linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/prepare-commit-msg +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/prepare-commit-msg 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/prepare-commit-msg 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,36 @@ ++#!/bin/sh ++# ++# An example hook script to prepare the commit log message. ++# Called by git-commit with the name of the file that has the ++# commit message, followed by the description of the commit ++# message's source. The hook's purpose is to edit the commit ++# message file. If the hook fails with a non-zero status, ++# the commit is aborted. ++# ++# To enable this hook, make this file executable. ++ ++# This hook includes three examples. The first comments out the ++# "Conflicts:" part of a merge commit. ++# ++# The second includes the output of "git diff --name-status -r" ++# into the message, just before the "git status" output. It is ++# commented because it doesn't cope with --amend or with squashed ++# commits. ++# ++# The third example adds a Signed-off-by line to the message, that can ++# still be edited. This is rarely a good idea. ++ ++case "$2 $3" in ++ merge) ++ sed -i '/^Conflicts:/,/#/!b;s/^/# &/;s/^# #/#/' "$1" ;; ++ ++# ""|template) ++# perl -i -pe ' ++# print "\n" . `git diff --cached --name-status -r` ++# if /^#/ && $first++ == 0' "$1" ;; ++ ++ *) ;; ++esac ++ ++# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') ++# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/update linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/update +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/hooks/update 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/hooks/update 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,107 @@ ++#!/bin/sh ++# ++# An example hook script to blocks unannotated tags from entering. ++# Called by git-receive-pack with arguments: refname sha1-old sha1-new ++# ++# To enable this hook, make this file executable by "chmod +x update". ++# ++# Config ++# ------ ++# hooks.allowunannotated ++# This boolean sets whether unannotated tags will be allowed into the ++# repository. By default they won't be. ++# hooks.allowdeletetag ++# This boolean sets whether deleting tags will be allowed in the ++# repository. By default they won't be. ++# hooks.allowdeletebranch ++# This boolean sets whether deleting branches will be allowed in the ++# repository. By default they won't be. ++# ++ ++# --- Command line ++refname="$1" ++oldrev="$2" ++newrev="$3" ++ ++# --- Safety check ++if [ -z "$GIT_DIR" ]; then ++ echo "Don't run this script from the command line." >&2 ++ echo " (if you want, you could supply GIT_DIR then run" >&2 ++ echo " $0 )" >&2 ++ exit 1 ++fi ++ ++if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then ++ echo "Usage: $0 " >&2 ++ exit 1 ++fi ++ ++# --- Config ++allowunannotated=$(git config --bool hooks.allowunannotated) ++allowdeletebranch=$(git config --bool hooks.allowdeletebranch) ++allowdeletetag=$(git config --bool hooks.allowdeletetag) ++ ++# check for no description ++projectdesc=$(sed -e '1q' "$GIT_DIR/description") ++if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb." ]; then ++ echo "*** Project description file hasn't been set" >&2 ++ exit 1 ++fi ++ ++# --- Check types ++# if $newrev is 0000...0000, it's a commit to delete a ref. ++if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then ++ newrev_type=delete ++else ++ newrev_type=$(git-cat-file -t $newrev) ++fi ++ ++case "$refname","$newrev_type" in ++ refs/tags/*,commit) ++ # un-annotated tag ++ short_refname=${refname##refs/tags/} ++ if [ "$allowunannotated" != "true" ]; then ++ echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 ++ echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 ++ exit 1 ++ fi ++ ;; ++ refs/tags/*,delete) ++ # delete tag ++ if [ "$allowdeletetag" != "true" ]; then ++ echo "*** Deleting a tag is not allowed in this repository" >&2 ++ exit 1 ++ fi ++ ;; ++ refs/tags/*,tag) ++ # annotated tag ++ ;; ++ refs/heads/*,commit) ++ # branch ++ ;; ++ refs/heads/*,delete) ++ # delete branch ++ if [ "$allowdeletebranch" != "true" ]; then ++ echo "*** Deleting a branch is not allowed in this repository" >&2 ++ exit 1 ++ fi ++ ;; ++ refs/remotes/*,commit) ++ # tracking branch ++ ;; ++ refs/remotes/*,delete) ++ # delete tracking branch ++ if [ "$allowdeletebranch" != "true" ]; then ++ echo "*** Deleting a tracking branch is not allowed in this repository" >&2 ++ exit 1 ++ fi ++ ;; ++ *) ++ # Anything else (is there anything else?) ++ echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 ++ exit 1 ++ ;; ++esac ++ ++# --- Finished ++exit 0 +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/info/exclude linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/info/exclude +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/info/exclude 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/info/exclude 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,6 @@ ++# git-ls-files --others --exclude-from=.git/info/exclude ++# Lines that start with '#' are comments. ++# For a project mostly in C, the following would be a good set of ++# exclude patterns (uncomment them if you want to use them): ++# *.[oa] ++# *~ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/logs/head linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/logs/head +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/logs/head 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/logs/head 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++0000000000000000000000000000000000000000 549a9c94e7919c86fc260dd534fd908343348228 root 1208919344 -0400 +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/logs/refs/heads/master linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/logs/refs/heads/master +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/logs/refs/heads/master 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/logs/refs/heads/master 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++0000000000000000000000000000000000000000 549a9c94e7919c86fc260dd534fd908343348228 root 1208919344 -0400 +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/logs/refs/remotes/origin/master linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/logs/refs/remotes/origin/master +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/logs/refs/remotes/origin/master 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/logs/refs/remotes/origin/master 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++0000000000000000000000000000000000000000 549a9c94e7919c86fc260dd534fd908343348228 root 1208919344 -0400 clone: from git://git.infradead.org/~dedekind/mkfs.ubifs.git +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/objects/pack/pack-b57e0a817d6303b8f24187e61200a9f06e6974ce.keep linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/objects/pack/pack-b57e0a817d6303b8f24187e61200a9f06e6974ce.keep +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/objects/pack/pack-b57e0a817d6303b8f24187e61200a9f06e6974ce.keep 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/objects/pack/pack-b57e0a817d6303b8f24187e61200a9f06e6974ce.keep 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++fetch-pack 29608 on localhost.localdomain +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/refs/heads/master linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/refs/heads/master +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/refs/heads/master 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/refs/heads/master 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++549a9c94e7919c86fc260dd534fd908343348228 +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/refs/remotes/origin/head linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/refs/remotes/origin/head +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/refs/remotes/origin/head 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/refs/remotes/origin/head 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++ref: refs/remotes/origin/master +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/refs/remotes/origin/master linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/refs/remotes/origin/master +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.git/refs/remotes/origin/master 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.git/refs/remotes/origin/master 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1 @@ ++549a9c94e7919c86fc260dd534fd908343348228 +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.gitignore linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.gitignore +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/.gitignore 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/.gitignore 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,5 @@ ++*.o ++mkfs.ubifs ++patches ++cscope.out ++.pc +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/Makefile linux-2.6.24.7/fs/ubifs/mkfs.ubifs/Makefile +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,24 @@ ++ifeq ($(origin CC),default) ++CC = gcc ++endif ++ ++ALL_SOURCES=*.[ch] hashtable/*.[ch] ++ ++CFLAGS := $(CFLAGS) -Wall -O2 -I./lzo/include -I../../../include ++ ++LDFLAGS := $(LDFLAGS) -lz -llzo2 -lm -L./lzo/lib ++ ++TARGETS = mkfs.ubifs ++ ++all: $(TARGETS) ++ ++mkfs.ubifs: crc16.o crc32.o lpt.o compr.o hashtable/hashtable.o \ ++ hashtable/hashtable_itr.o devtable.o ++ ++clean: ++ rm -f *.o $(TARGETS) cscope.* ++ ++cscope: ++ @echo $(ALL_SOURCES) > cscope.files ++ @cscope -bR ++ @rm cscope.files +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/compr.c linux-2.6.24.7/fs/ubifs/mkfs.ubifs/compr.c +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/compr.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/compr.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,154 @@ ++/* ++ * Copyright (C) 2008 Nokia Corporation. ++ * Copyright (C) 2008 University of Szeged, Hungary ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy ++ * Adrian Hunter ++ * Zoltan Sogor ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "compr.h" ++#include "ubifs-media.h" ++ ++static void *lzo_mem; ++static unsigned long long errcnt = 0; ++ ++#define DEFLATE_DEF_LEVEL Z_DEFAULT_COMPRESSION ++#define DEFLATE_DEF_WINBITS 11 ++#define DEFLATE_DEF_MEMLEVEL 8 ++ ++static int zlib_deflate(void *in_buf, size_t in_len, void *out_buf, ++ size_t *out_len) ++{ ++ z_stream strm; ++ ++ strm.zalloc = NULL; ++ strm.zfree = NULL; ++ ++ /* ++ * Match exactly the zlib parameters used by the linux kernel crypto ++ * API. ++ */ ++ if (deflateInit2(&strm, DEFLATE_DEF_LEVEL, Z_DEFLATED, ++ -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL, ++ Z_DEFAULT_STRATEGY)) { ++ errcnt += 1; ++ return -1; ++ } ++ ++ strm.next_in = in_buf; ++ strm.avail_in = in_len; ++ strm.total_in = 0; ++ ++ strm.next_out = out_buf; ++ strm.avail_out = *out_len; ++ strm.total_out = 0; ++ ++ if (deflate(&strm, Z_FINISH) != Z_STREAM_END) { ++ deflateEnd(&strm); ++ errcnt += 1; ++ return -1; ++ } ++ ++ if (deflateEnd(&strm) != Z_OK) { ++ errcnt += 1; ++ return -1; ++ } ++ ++ *out_len = strm.total_out; ++ ++ return 0; ++} ++ ++static int lzo_compress(void *in_buf, size_t in_len, void *out_buf, ++ size_t *out_len) ++{ ++ lzo_uint len; ++ int ret; ++ ++ len = *out_len; ++ ret = lzo1x_999_compress(in_buf, in_len, out_buf, &len, lzo_mem); ++ *out_len = len; ++ ++ if (ret != LZO_E_OK) { ++ errcnt += 1; ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int no_compress(void *in_buf, size_t in_len, void *out_buf, ++ size_t *out_len) ++{ ++ memcpy(out_buf, in_buf, in_len); ++ *out_len = in_len; ++ return 0; ++} ++ ++int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, ++ int type) ++{ ++ int ret = 1; ++ ++ if (in_len < UBIFS_MIN_COMPR_LEN) { ++ no_compress(in_buf, in_len, out_buf, out_len); ++ return MKFS_UBIFS_COMPR_NONE; ++ } ++ ++ switch (type) { ++ case MKFS_UBIFS_COMPR_LZO: ++ ret = lzo_compress(in_buf, in_len, out_buf, out_len); ++ break; ++ case MKFS_UBIFS_COMPR_ZLIB: ++ ret = zlib_deflate(in_buf, in_len, out_buf, out_len); ++ break; ++ case MKFS_UBIFS_COMPR_NONE: ++ ret = 1; ++ break; ++ default: ++ errcnt += 1; ++ break; ++ } ++ if (ret || *out_len >= in_len) { ++ no_compress(in_buf, in_len, out_buf, out_len); ++ return MKFS_UBIFS_COMPR_NONE; ++ } ++ return type; ++} ++ ++int init_compression(void) ++{ ++ lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); ++ if (!lzo_mem) ++ return -1; ++ return 0; ++} ++ ++void destroy_compression(void) ++{ ++ free(lzo_mem); ++ if (errcnt) ++ fprintf(stderr, "%llu compression errors occurred\n", errcnt); ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/compr.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/compr.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/compr.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/compr.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (C) 2008 Nokia Corporation. ++ * Copyright (C) 2008 University of Szeged, Hungary ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy ++ * Adrian Hunter ++ * Zoltan Sogor ++ */ ++ ++#ifndef __UBIFS_COMPRESS_H__ ++#define __UBIFS_COMPRESS_H__ ++ ++enum compression_type ++{ ++ MKFS_UBIFS_COMPR_NONE, ++ MKFS_UBIFS_COMPR_LZO, ++ MKFS_UBIFS_COMPR_ZLIB, ++}; ++ ++int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, ++ int type); ++int init_compression(void); ++void destroy_compression(void); ++ ++#endif +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/copying linux-2.6.24.7/fs/ubifs/mkfs.ubifs/copying +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/copying 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/copying 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,340 @@ ++ GNU GENERAL PUBLIC LICENSE ++ Version 2, June 1991 ++ ++ Copyright (C) 1989, 1991 Free Software Foundation, Inc. ++ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ Everyone is permitted to copy and distribute verbatim copies ++ of this license document, but changing it is not allowed. ++ ++ Preamble ++ ++ The licenses for most software are designed to take away your ++freedom to share and change it. By contrast, the GNU General Public ++License is intended to guarantee your freedom to share and change free ++software--to make sure the software is free for all its users. This ++General Public License applies to most of the Free Software ++Foundation's software and to any other program whose authors commit to ++using it. (Some other Free Software Foundation software is covered by ++the GNU Library General Public License instead.) You can apply it to ++your programs, too. ++ ++ When we speak of free software, we are referring to freedom, not ++price. Our General Public Licenses are designed to make sure that you ++have the freedom to distribute copies of free software (and charge for ++this service if you wish), that you receive source code or can get it ++if you want it, that you can change the software or use pieces of it ++in new free programs; and that you know you can do these things. ++ ++ To protect your rights, we need to make restrictions that forbid ++anyone to deny you these rights or to ask you to surrender the rights. ++These restrictions translate to certain responsibilities for you if you ++distribute copies of the software, or if you modify it. ++ ++ For example, if you distribute copies of such a program, whether ++gratis or for a fee, you must give the recipients all the rights that ++you have. You must make sure that they, too, receive or can get the ++source code. And you must show them these terms so they know their ++rights. ++ ++ We protect your rights with two steps: (1) copyright the software, and ++(2) offer you this license which gives you legal permission to copy, ++distribute and/or modify the software. ++ ++ Also, for each author's protection and ours, we want to make certain ++that everyone understands that there is no warranty for this free ++software. If the software is modified by someone else and passed on, we ++want its recipients to know that what they have is not the original, so ++that any problems introduced by others will not reflect on the original ++authors' reputations. ++ ++ Finally, any free program is threatened constantly by software ++patents. We wish to avoid the danger that redistributors of a free ++program will individually obtain patent licenses, in effect making the ++program proprietary. To prevent this, we have made it clear that any ++patent must be licensed for everyone's free use or not licensed at all. ++ ++ The precise terms and conditions for copying, distribution and ++modification follow. ++ ++ GNU GENERAL PUBLIC LICENSE ++ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ++ ++ 0. This License applies to any program or other work which contains ++a notice placed by the copyright holder saying it may be distributed ++under the terms of this General Public License. The "Program", below, ++refers to any such program or work, and a "work based on the Program" ++means either the Program or any derivative work under copyright law: ++that is to say, a work containing the Program or a portion of it, ++either verbatim or with modifications and/or translated into another ++language. (Hereinafter, translation is included without limitation in ++the term "modification".) Each licensee is addressed as "you". ++ ++Activities other than copying, distribution and modification are not ++covered by this License; they are outside its scope. The act of ++running the Program is not restricted, and the output from the Program ++is covered only if its contents constitute a work based on the ++Program (independent of having been made by running the Program). ++Whether that is true depends on what the Program does. ++ ++ 1. You may copy and distribute verbatim copies of the Program's ++source code as you receive it, in any medium, provided that you ++conspicuously and appropriately publish on each copy an appropriate ++copyright notice and disclaimer of warranty; keep intact all the ++notices that refer to this License and to the absence of any warranty; ++and give any other recipients of the Program a copy of this License ++along with the Program. ++ ++You may charge a fee for the physical act of transferring a copy, and ++you may at your option offer warranty protection in exchange for a fee. ++ ++ 2. You may modify your copy or copies of the Program or any portion ++of it, thus forming a work based on the Program, and copy and ++distribute such modifications or work under the terms of Section 1 ++above, provided that you also meet all of these conditions: ++ ++ a) You must cause the modified files to carry prominent notices ++ stating that you changed the files and the date of any change. ++ ++ b) You must cause any work that you distribute or publish, that in ++ whole or in part contains or is derived from the Program or any ++ part thereof, to be licensed as a whole at no charge to all third ++ parties under the terms of this License. ++ ++ c) If the modified program normally reads commands interactively ++ when run, you must cause it, when started running for such ++ interactive use in the most ordinary way, to print or display an ++ announcement including an appropriate copyright notice and a ++ notice that there is no warranty (or else, saying that you provide ++ a warranty) and that users may redistribute the program under ++ these conditions, and telling the user how to view a copy of this ++ License. (Exception: if the Program itself is interactive but ++ does not normally print such an announcement, your work based on ++ the Program is not required to print an announcement.) ++ ++These requirements apply to the modified work as a whole. If ++identifiable sections of that work are not derived from the Program, ++and can be reasonably considered independent and separate works in ++themselves, then this License, and its terms, do not apply to those ++sections when you distribute them as separate works. But when you ++distribute the same sections as part of a whole which is a work based ++on the Program, the distribution of the whole must be on the terms of ++this License, whose permissions for other licensees extend to the ++entire whole, and thus to each and every part regardless of who wrote it. ++ ++Thus, it is not the intent of this section to claim rights or contest ++your rights to work written entirely by you; rather, the intent is to ++exercise the right to control the distribution of derivative or ++collective works based on the Program. ++ ++In addition, mere aggregation of another work not based on the Program ++with the Program (or with a work based on the Program) on a volume of ++a storage or distribution medium does not bring the other work under ++the scope of this License. ++ ++ 3. You may copy and distribute the Program (or a work based on it, ++under Section 2) in object code or executable form under the terms of ++Sections 1 and 2 above provided that you also do one of the following: ++ ++ a) Accompany it with the complete corresponding machine-readable ++ source code, which must be distributed under the terms of Sections ++ 1 and 2 above on a medium customarily used for software interchange; or, ++ ++ b) Accompany it with a written offer, valid for at least three ++ years, to give any third party, for a charge no more than your ++ cost of physically performing source distribution, a complete ++ machine-readable copy of the corresponding source code, to be ++ distributed under the terms of Sections 1 and 2 above on a medium ++ customarily used for software interchange; or, ++ ++ c) Accompany it with the information you received as to the offer ++ to distribute corresponding source code. (This alternative is ++ allowed only for noncommercial distribution and only if you ++ received the program in object code or executable form with such ++ an offer, in accord with Subsection b above.) ++ ++The source code for a work means the preferred form of the work for ++making modifications to it. For an executable work, complete source ++code means all the source code for all modules it contains, plus any ++associated interface definition files, plus the scripts used to ++control compilation and installation of the executable. However, as a ++special exception, the source code distributed need not include ++anything that is normally distributed (in either source or binary ++form) with the major components (compiler, kernel, and so on) of the ++operating system on which the executable runs, unless that component ++itself accompanies the executable. ++ ++If distribution of executable or object code is made by offering ++access to copy from a designated place, then offering equivalent ++access to copy the source code from the same place counts as ++distribution of the source code, even though third parties are not ++compelled to copy the source along with the object code. ++ ++ 4. You may not copy, modify, sublicense, or distribute the Program ++except as expressly provided under this License. Any attempt ++otherwise to copy, modify, sublicense or distribute the Program is ++void, and will automatically terminate your rights under this License. ++However, parties who have received copies, or rights, from you under ++this License will not have their licenses terminated so long as such ++parties remain in full compliance. ++ ++ 5. You are not required to accept this License, since you have not ++signed it. However, nothing else grants you permission to modify or ++distribute the Program or its derivative works. These actions are ++prohibited by law if you do not accept this License. Therefore, by ++modifying or distributing the Program (or any work based on the ++Program), you indicate your acceptance of this License to do so, and ++all its terms and conditions for copying, distributing or modifying ++the Program or works based on it. ++ ++ 6. Each time you redistribute the Program (or any work based on the ++Program), the recipient automatically receives a license from the ++original licensor to copy, distribute or modify the Program subject to ++these terms and conditions. You may not impose any further ++restrictions on the recipients' exercise of the rights granted herein. ++You are not responsible for enforcing compliance by third parties to ++this License. ++ ++ 7. If, as a consequence of a court judgment or allegation of patent ++infringement or for any other reason (not limited to patent issues), ++conditions are imposed on you (whether by court order, agreement or ++otherwise) that contradict the conditions of this License, they do not ++excuse you from the conditions of this License. If you cannot ++distribute so as to satisfy simultaneously your obligations under this ++License and any other pertinent obligations, then as a consequence you ++may not distribute the Program at all. For example, if a patent ++license would not permit royalty-free redistribution of the Program by ++all those who receive copies directly or indirectly through you, then ++the only way you could satisfy both it and this License would be to ++refrain entirely from distribution of the Program. ++ ++If any portion of this section is held invalid or unenforceable under ++any particular circumstance, the balance of the section is intended to ++apply and the section as a whole is intended to apply in other ++circumstances. ++ ++It is not the purpose of this section to induce you to infringe any ++patents or other property right claims or to contest validity of any ++such claims; this section has the sole purpose of protecting the ++integrity of the free software distribution system, which is ++implemented by public license practices. Many people have made ++generous contributions to the wide range of software distributed ++through that system in reliance on consistent application of that ++system; it is up to the author/donor to decide if he or she is willing ++to distribute software through any other system and a licensee cannot ++impose that choice. ++ ++This section is intended to make thoroughly clear what is believed to ++be a consequence of the rest of this License. ++ ++ 8. If the distribution and/or use of the Program is restricted in ++certain countries either by patents or by copyrighted interfaces, the ++original copyright holder who places the Program under this License ++may add an explicit geographical distribution limitation excluding ++those countries, so that distribution is permitted only in or among ++countries not thus excluded. In such case, this License incorporates ++the limitation as if written in the body of this License. ++ ++ 9. The Free Software Foundation may publish revised and/or new versions ++of the General Public License from time to time. Such new versions will ++be similar in spirit to the present version, but may differ in detail to ++address new problems or concerns. ++ ++Each version is given a distinguishing version number. If the Program ++specifies a version number of this License which applies to it and "any ++later version", you have the option of following the terms and conditions ++either of that version or of any later version published by the Free ++Software Foundation. If the Program does not specify a version number of ++this License, you may choose any version ever published by the Free Software ++Foundation. ++ ++ 10. If you wish to incorporate parts of the Program into other free ++programs whose distribution conditions are different, write to the author ++to ask for permission. For software which is copyrighted by the Free ++Software Foundation, write to the Free Software Foundation; we sometimes ++make exceptions for this. Our decision will be guided by the two goals ++of preserving the free status of all derivatives of our free software and ++of promoting the sharing and reuse of software generally. ++ ++ NO WARRANTY ++ ++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY ++FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN ++OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES ++PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED ++OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS ++TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE ++PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, ++REPAIR OR CORRECTION. ++ ++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING ++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR ++REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, ++INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING ++OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED ++TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY ++YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER ++PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGES. ++ ++ END OF TERMS AND CONDITIONS ++ ++ How to Apply These Terms to Your New Programs ++ ++ If you develop a new program, and you want it to be of the greatest ++possible use to the public, the best way to achieve this is to make it ++free software which everyone can redistribute and change under these terms. ++ ++ To do so, attach the following notices to the program. It is safest ++to attach them to the start of each source file to most effectively ++convey the exclusion of warranty; and each file should have at least ++the "copyright" line and a pointer to where the full notice is found. ++ ++ ++ Copyright (C) 19yy ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++ ++Also add information on how to contact you by electronic and paper mail. ++ ++If the program is interactive, make it output a short notice like this ++when it starts in an interactive mode: ++ ++ Gnomovision version 69, Copyright (C) 19yy name of author ++ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. ++ This is free software, and you are welcome to redistribute it ++ under certain conditions; type `show c' for details. ++ ++The hypothetical commands `show w' and `show c' should show the appropriate ++parts of the General Public License. Of course, the commands you use may ++be called something other than `show w' and `show c'; they could even be ++mouse-clicks or menu items--whatever suits your program. ++ ++You should also get your employer (if you work as a programmer) or your ++school, if any, to sign a "copyright disclaimer" for the program, if ++necessary. Here is a sample; alter the names: ++ ++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program ++ `Gnomovision' (which makes passes at compilers) written by James Hacker. ++ ++ , 1 April 1989 ++ Ty Coon, President of Vice ++ ++This General Public License does not permit incorporating your program into ++proprietary programs. If your program is a subroutine library, you may ++consider it more useful to permit linking proprietary applications with the ++library. If this is what you want to do, use the GNU Library General ++Public License instead of this License. +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/crc16.c linux-2.6.24.7/fs/ubifs/mkfs.ubifs/crc16.c +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/crc16.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/crc16.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,56 @@ ++/* ++ * This code was taken from the linux kernel. The license is GPL Version 2. ++ */ ++ ++#include "crc16.h" ++ ++/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ ++uint16_t const crc16_table[256] = { ++ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, ++ 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, ++ 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, ++ 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, ++ 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, ++ 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, ++ 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, ++ 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, ++ 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, ++ 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, ++ 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, ++ 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, ++ 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, ++ 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, ++ 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, ++ 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, ++ 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, ++ 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, ++ 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, ++ 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, ++ 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, ++ 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, ++ 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, ++ 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, ++ 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, ++ 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, ++ 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, ++ 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, ++ 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, ++ 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, ++ 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, ++ 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 ++}; ++ ++/** ++ * crc16 - compute the CRC-16 for the data buffer ++ * @crc: previous CRC value ++ * @buffer: data pointer ++ * @len: number of bytes in the buffer ++ * ++ * Returns the updated CRC value. ++ */ ++uint16_t crc16(uint16_t crc, uint8_t const *buffer, size_t len) ++{ ++ while (len--) ++ crc = crc16_byte(crc, *buffer++); ++ return crc; ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/crc16.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/crc16.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/crc16.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/crc16.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,27 @@ ++/* ++ * Implements the standard CRC-16: ++ * Width 16 ++ * Poly 0x8005 (x^16 + x^15 + x^2 + 1) ++ * Init 0 ++ * ++ * Copyright (c) 2005 Ben Gardner ++ * ++ * This code was taken from the linux kernel. The license is GPL Version 2. ++ */ ++ ++#ifndef __CRC16_H__ ++#define __CRC16_H__ ++ ++#include ++#include ++ ++extern uint16_t const crc16_table[256]; ++ ++extern uint16_t crc16(uint16_t crc, const uint8_t *buffer, size_t len); ++ ++static inline uint16_t crc16_byte(uint16_t crc, const uint8_t data) ++{ ++ return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff]; ++} ++ ++#endif /* __CRC16_H__ */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/crc32.c linux-2.6.24.7/fs/ubifs/mkfs.ubifs/crc32.c +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/crc32.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/crc32.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,95 @@ ++/* ++ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or ++ * code or tables extracted from it, as desired without restriction. ++ * ++ * First, the polynomial itself and its table of feedback terms. The ++ * polynomial is ++ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 ++ * ++ * Note that we take it "backwards" and put the highest-order term in ++ * the lowest-order bit. The X^32 term is "implied"; the LSB is the ++ * X^31 term, etc. The X^0 term (usually shown as "+1") results in ++ * the MSB being 1 ++ * ++ * Note that the usual hardware shift register implementation, which ++ * is what we're using (we're merely optimizing it by doing eight-bit ++ * chunks at a time) shifts bits into the lowest-order term. In our ++ * implementation, that means shifting towards the right. Why do we ++ * do it this way? Because the calculated CRC must be transmitted in ++ * order from highest-order term to lowest-order term. UARTs transmit ++ * characters in order from LSB to MSB. By storing the CRC this way ++ * we hand it to the UART in the order low-byte to high-byte; the UART ++ * sends each low-bit to hight-bit; and the result is transmission bit ++ * by bit from highest- to lowest-order term without requiring any bit ++ * shuffling on our part. Reception works similarly ++ * ++ * The feedback terms table consists of 256, 32-bit entries. Notes ++ * ++ * The table can be generated at runtime if desired; code to do so ++ * is shown later. It might not be obvious, but the feedback ++ * terms simply represent the results of eight shift/xor opera ++ * tions for all combinations of data and CRC register values ++ * ++ * The values must be right-shifted by eight bits by the "updcrc ++ * logic; the shift must be unsigned (bring in zeroes). On some ++ * hardware you could probably optimize the shift in assembler by ++ * using byte-swap instructions ++ * polynomial $edb88320 ++ */ ++ ++#include ++ ++const uint32_t crc32_table[256] = { ++ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, ++ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, ++ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, ++ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, ++ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, ++ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, ++ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, ++ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, ++ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, ++ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, ++ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, ++ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, ++ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, ++ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, ++ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, ++ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, ++ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, ++ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, ++ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, ++ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, ++ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, ++ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, ++ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, ++ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, ++ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, ++ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, ++ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, ++ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, ++ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, ++ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, ++ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, ++ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, ++ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, ++ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, ++ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, ++ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, ++ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, ++ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, ++ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, ++ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, ++ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, ++ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, ++ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, ++ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, ++ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, ++ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, ++ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, ++ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, ++ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, ++ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, ++ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, ++ 0x2d02ef8dL ++}; +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/crc32.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/crc32.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/crc32.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/crc32.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,22 @@ ++/* ++ * This code was taken from the linux kernel. The license is GPL Version 2. ++ */ ++ ++#ifndef __CRC32_H__ ++#define __CRC32_H__ ++ ++#include ++ ++extern const uint32_t crc32_table[256]; ++ ++/* Return a 32-bit CRC of the contents of the buffer. */ ++static inline uint32_t crc32(uint32_t val, const void *ss, int len) ++{ ++ const unsigned char *s = ss; ++ ++ while (--len >= 0) ++ val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8); ++ return val; ++} ++ ++#endif /* __CRC32_H__ */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/defs.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/defs.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/defs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/defs.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,105 @@ ++/* ++ * Greate deal of the code was taken from the kernel UBIFS implementation, and ++ * this file contains some "glue" definitions. ++ */ ++ ++#ifndef __UBIFS_DEFS_H__ ++#define __UBIFS_DEFS_H__ ++ ++#define t16(x) ({ \ ++ uint16_t __b = (x); \ ++ (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_16(__b); \ ++}) ++ ++#define t32(x) ({ \ ++ uint32_t __b = (x); \ ++ (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_32(__b); \ ++}) ++ ++#define t64(x) ({ \ ++ uint64_t __b = (x); \ ++ (__LITTLE_ENDIAN==__BYTE_ORDER) ? __b : bswap_64(__b); \ ++}) ++ ++#define cpu_to_le16(x) ((__le16){t16(x)}) ++#define cpu_to_le32(x) ((__le32){t32(x)}) ++#define cpu_to_le64(x) ((__le64){t64(x)}) ++ ++#define le16_to_cpu(x) (t16((x))) ++#define le32_to_cpu(x) (t32((x))) ++#define le64_to_cpu(x) (t64((x))) ++ ++#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) ++#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) ++ ++#define min_t(t,x,y) ({ \ ++ typeof((x)) _x = (x); \ ++ typeof((y)) _y = (y); \ ++ (_x < _y) ? _x : _y; \ ++}) ++ ++#define max_t(t,x,y) ({ \ ++ typeof((x)) _x = (x); \ ++ typeof((y)) _y = (y); \ ++ (_x > _y) ? _x : _y; \ ++}) ++ ++#define unlikely(x) (x) ++ ++struct qstr ++{ ++ char *name; ++ size_t len; ++}; ++ ++/** ++ * fls - find last (most-significant) bit set ++ * @x: the word to search ++ * ++ * This is defined the same way as ffs. ++ * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. ++ */ ++static inline int fls(int x) ++{ ++ int r = 32; ++ ++ if (!x) ++ return 0; ++ if (!(x & 0xffff0000u)) { ++ x <<= 16; ++ r -= 16; ++ } ++ if (!(x & 0xff000000u)) { ++ x <<= 8; ++ r -= 8; ++ } ++ if (!(x & 0xf0000000u)) { ++ x <<= 4; ++ r -= 4; ++ } ++ if (!(x & 0xc0000000u)) { ++ x <<= 2; ++ r -= 2; ++ } ++ if (!(x & 0x80000000u)) { ++ x <<= 1; ++ r -= 1; ++ } ++ return r; ++} ++ ++#define do_div(n,base) ({ \ ++int __res; \ ++__res = ((unsigned long) n) % (unsigned) base; \ ++n = ((unsigned long) n) / (unsigned) base; \ ++__res; }) ++ ++#if INT_MAX != 0x7fffffff ++#error : sizeof(int) must be 4 for this program ++#endif ++ ++#if (~0ULL) != 0xffffffffffffffffULL ++#error : sizeof(long long) must be 8 for this program ++#endif ++ ++#endif +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/devtable.c linux-2.6.24.7/fs/ubifs/mkfs.ubifs/devtable.c +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/devtable.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/devtable.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,524 @@ ++/* ++ * Copyright (C) 2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Author: Artem Bityutskiy ++ * ++ * Part of the device table parsing code was taken from the mkfs.jffs2 utility. ++ * The original author of that code is Erik Andersen, hence: ++ * Copyright (C) 2001, 2002 Erik Andersen ++ */ ++ ++/* ++ * This file implemented device table support. Device table entries take the ++ * form of: ++ * ++ * /dev/mem c 640 0 0 1 1 0 0 - ++ * ++ * Type can be one of: ++ * f A regular file ++ * d Directory ++ * c Character special device file ++ * b Block special device file ++ * p Fifo (named pipe) ++ * ++ * Don't bother with symlinks (permissions are irrelevant), hard links (special ++ * cases of regular files), or sockets (why bother). ++ * ++ * Regular files must exist in the target root directory. If a char, block, ++ * fifo, or directory does not exist, it will be created. ++ * ++ * Please, refer the device_table.txt file which can be found at MTD utilities ++ * for more information about what the device table is. ++ */ ++ ++#include "mkfs.ubifs.h" ++#include "hashtable/hashtable.h" ++#include "hashtable/hashtable_itr.h" ++ ++/* ++ * The hash table which contains paths to files/directories/device nodes ++ * referred to in the device table. For example, if the device table refers ++ * "/dev/loop0", the @path_htbl will contain "/dev" element. ++ */ ++static struct hashtable *path_htbl; ++ ++/* Hash function used for hash tables */ ++static unsigned int r5_hash(void *s) ++{ ++ unsigned int a = 0; ++ const signed char *str = s; ++ ++ while (*str) { ++ a += *str << 4; ++ a += *str >> 4; ++ a *= 11; ++ str++; ++ } ++ ++ return a; ++} ++ ++/* ++ * Check whether 2 keys of a hash table are equivalent. The keys are path/file ++ * names, so we simply use 'strcmp()'. ++ */ ++static int is_equivalent(void *k1, void *k2) ++{ ++ return !strcmp(k1, k2); ++} ++ ++/** ++ * separate_last - separate out the last path component ++ * @buf: the path to split ++ * @len: length of the @buf string ++ * @path: the beginning of path is returned here ++ * @name: the last path component is returned here ++ * ++ * This helper function separates out the the last component of the full path ++ * string. For example, "/dev/loop" would be split on "/dev" and "loop". This ++ * function allocates memory for @path and @name and return the result there. ++ * Returns zero in case of success and a negative error code in case of ++ * failure. ++ */ ++static int separate_last(const char *buf, int len, char **path, char **name) ++{ ++ int path_len = len, name_len; ++ const char *p = buf + len, *n; ++ ++ while (*--p != '/') ++ path_len -= 1; ++ ++ /* Drop the final '/' unless this is the root directory */ ++ name_len = len - path_len; ++ n = buf + path_len; ++ if (path_len > 1) ++ path_len -= 1; ++ ++ *path = malloc(path_len + 1); ++ if (!*path) ++ return err_msg("cannot allocate %d bytes of memory", ++ path_len + 1); ++ memcpy(*path, buf, path_len); ++ (*path)[path_len] = '\0'; ++ ++ *name = malloc(name_len + 1); ++ if (!*name) { ++ free(*path); ++ return err_msg("cannot allocate %d bytes of memory", ++ name_len + 1); ++ } ++ memcpy(*name, n, name_len + 1); ++ ++ return 0; ++} ++ ++static int interpret_table_entry(const char *root, const char *line) ++{ ++ char buf[1024], type, *path = NULL, *name = NULL; ++ int len; ++ struct path_htbl_element *ph_elt = NULL; ++ struct name_htbl_element *nh_elt = NULL; ++ unsigned int mode = 0755, uid = 0, gid = 0, major = 0, minor = 0; ++ unsigned int start = 0, increment = 0, count = 0; ++ ++ if (sscanf(line, "%1023s %c %o %u %u %u %u %u %u %u", ++ buf, &type, &mode, &uid, &gid, &major, &minor, ++ &start, &increment, &count) < 0) ++ return sys_err_msg("sscanf failed"); ++ ++ dbg_msg(3, "name %s, type %c, mode %o, uid %u, gid %u, major %u, " ++ "minor %u, start %u, inc %u, cnt %u", ++ buf, type, mode, uid, gid, major, minor, start, ++ increment, count); ++ ++ len = strnlen(buf, 1024); ++ if (len == 1024) ++ return err_msg("too long path"); ++ ++ if (!strcmp(buf, "/")) ++ return err_msg("device table entries require absolute paths"); ++ if (buf[1] == '\0') ++ return err_msg("root directory cannot be created"); ++ if (strstr(buf, "//")) ++ return err_msg("'//' cannot be used in the path"); ++ if (buf[len - 1] == '/') ++ return err_msg("do not put '/' at the end"); ++ ++ if (strstr(buf, "/./") || strstr(buf, "/../") || ++ !strcmp(buf + len - 2, "/.") || !strcmp(buf + len - 3, "/..")) ++ return err_msg("'.' and '..' cannot be used in the path"); ++ ++ switch (type) { ++ case 'd': ++ mode |= S_IFDIR; ++ break; ++ case 'f': ++ mode |= S_IFREG; ++ break; ++ case 'p': ++ mode |= S_IFIFO; ++ break; ++ case 'c': ++ mode |= S_IFCHR; ++ break; ++ case 'b': ++ mode |= S_IFBLK; ++ break; ++ default: ++ return err_msg("unsupported file type '%c'", type); ++ } ++ ++ if (separate_last(buf, len, &path, &name)) ++ return -1; ++ ++ /* ++ * Check if this path already exist in the path hash table and add it ++ * if it is not. ++ */ ++ ph_elt = hashtable_search(path_htbl, path); ++ if (!ph_elt) { ++ dbg_msg(3, "inserting '%s' into path hash table", path); ++ ph_elt = malloc(sizeof(struct path_htbl_element)); ++ if (!ph_elt) { ++ err_msg("cannot allocate %d bytes of memory", ++ sizeof(struct path_htbl_element)); ++ goto out_free; ++ } ++ ++ if (!hashtable_insert(path_htbl, path, ph_elt)) { ++ err_msg("cannot insert into path hash table"); ++ goto out_free; ++ } ++ ++ ph_elt->path = path; ++ path = NULL; ++ ph_elt->name_htbl = create_hashtable(128, &r5_hash, ++ &is_equivalent); ++ if (!ph_elt->name_htbl) { ++ err_msg("cannot create name hash table"); ++ goto out_free; ++ } ++ } ++ ++ if (increment != 0 && count == 0) ++ return err_msg("count cannot be zero if increment is non-zero"); ++ ++ /* ++ * Add the file/directory/device node (last component of the path) to ++ * the name hashtable. The name hashtable resides in the corresponding ++ * path hashtable element. ++ */ ++ ++ if (count == 0) { ++ /* This entry does not require any iterating */ ++ nh_elt = malloc(sizeof(struct name_htbl_element)); ++ if (!nh_elt) { ++ err_msg("cannot allocate %d bytes of memory", ++ sizeof(struct name_htbl_element)); ++ goto out_free; ++ } ++ ++ nh_elt->mode = mode; ++ nh_elt->uid = uid; ++ nh_elt->gid = gid; ++ nh_elt->dev = makedev(major, minor); ++ ++ dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)", ++ name, major(nh_elt->dev), minor(nh_elt->dev)); ++ ++ if (hashtable_search(ph_elt->name_htbl, name)) ++ return err_msg("'%s' is referred twice", buf); ++ ++ nh_elt->name = name; ++ if (!hashtable_insert(ph_elt->name_htbl, name, nh_elt)) { ++ err_msg("cannot insert into name hash table"); ++ goto out_free; ++ } ++ } else { ++ int i, num = start + increment * count, len = strlen(name) + 20; ++ char *nm; ++ ++ for (i = start; i < num; i++) { ++ nh_elt = malloc(sizeof(struct name_htbl_element)); ++ if (!nh_elt) { ++ err_msg("cannot allocate %d bytes of memory", ++ sizeof(struct name_htbl_element)); ++ goto out_free; ++ } ++ ++ nh_elt->mode = mode; ++ nh_elt->uid = uid; ++ nh_elt->gid = gid; ++ nh_elt->dev = makedev(major, minor + i - start); ++ ++ nm = malloc(len); ++ if (!nm) { ++ err_msg("cannot allocate %d bytes of memory", len); ++ goto out_free; ++ } ++ ++ sprintf(nm, "%s%d", name, i); ++ nh_elt->name = nm; ++ ++ dbg_msg(3, "inserting '%s' into name hash table (major %d, minor %d)", ++ nm, major(nh_elt->dev), minor(nh_elt->dev)); ++ ++ if (hashtable_search(ph_elt->name_htbl, nm)) { ++ err_msg("'%s' is referred twice", buf); ++ free (nm); ++ goto out_free; ++ } ++ ++ if (!hashtable_insert(ph_elt->name_htbl, nm, nh_elt)) { ++ err_msg("cannot insert into name hash table"); ++ free (nm); ++ goto out_free; ++ } ++ } ++ free(name); ++ name = NULL; ++ } ++ ++ return 0; ++ ++out_free: ++ free(ph_elt); ++ free(nh_elt); ++ free(path); ++ free(name); ++ return -1; ++} ++ ++/** ++ * parse_devtable - parse the device table. ++ * @tbl_file: device table file name ++ * ++ * This function parses the device table and prepare the hash table which will ++ * later be used by mkfs.ubifs to create the specified files/device nodes. ++ * Returns zero in case of success and a negative error code in case of ++ * failure. ++ */ ++int parse_devtable(const char *root, const char *tbl_file) ++{ ++ FILE *f; ++ char *line = NULL; ++ struct stat st; ++ size_t len; ++ ++ dbg_msg(1, "parsing device table file '%s'", tbl_file); ++ ++ path_htbl = create_hashtable(128, &r5_hash, &is_equivalent); ++ if (!path_htbl) ++ return err_msg("cannot create path hash table"); ++ ++ f = fopen(tbl_file, "r"); ++ if (!f) ++ return sys_err_msg("cannot open '%s'", tbl_file); ++ ++ if (fstat(fileno(f), &st) < 0) { ++ sys_err_msg("cannot stat '%s'", tbl_file); ++ goto out_close; ++ } ++ ++ if (st.st_size < 10) { ++ sys_err_msg("'%s' is too short", tbl_file); ++ goto out_close; ++ } ++ ++ /* ++ * The general plan now is to read in one line at a time, check for ++ * leading comment delimiters ('#'), then try and parse the line as a ++ * device table ++ */ ++ while (getline(&line, &len, f) != -1) { ++ /* First trim off any white-space */ ++ len = strlen(line); ++ ++ /* Trim trailing white-space */ ++ while (len > 0 && isspace(line[len - 1])) ++ line[--len] = '\0'; ++ /* Trim leading white-space */ ++ memmove(line, &line[strspn(line, " \n\r\t\v")], len); ++ ++ /* How long are we after trimming? */ ++ len = strlen(line); ++ ++ /* If this is not a comment line, try to interpret it */ ++ if (len && *line != '#') { ++ if (interpret_table_entry(root, line)) { ++ err_msg("cannot parse '%s'", line); ++ goto out_close; ++ } ++ } ++ ++ free(line); ++ line = NULL; ++ } ++ ++ dbg_msg(1, "finished parsing"); ++ fclose(f); ++ return 0; ++ ++out_close: ++ fclose(f); ++ free_devtable_info(); ++ return -1; ++} ++ ++/** ++ * devtbl_find_path - find a path in the path hash table. ++ * @path: UBIFS path to find. ++ * ++ * This looks up the path hash table. Returns the path hash table element ++ * reference if @path was found and %NULL if not. ++ */ ++struct path_htbl_element *devtbl_find_path(const char *path) ++{ ++ if (!path_htbl) ++ return NULL; ++ ++ return hashtable_search(path_htbl, (void *)path); ++} ++ ++/** ++ * devtbl_find_name - find a name in the name hash table. ++ * @ph_etl: path hash table element to find at ++ * @name: name to find ++ * ++ * This looks up the name hash table. Returns the name hash table element ++ * reference if @name found and %NULL if not. ++ */ ++struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt, ++ const char *name) ++{ ++ if (!path_htbl) ++ return NULL; ++ ++ return hashtable_search(ph_elt->name_htbl, (void *)name); ++} ++ ++/** ++ * override_attributes - override inode attributes. ++ * @st: struct stat object to containing the attributes to override ++ * @ph_elt: path hash table element object ++ * @nh_elt: name hash table element object containing the new values ++ * ++ * The device table file may override attributes like UID of files. For ++ * example, the device table may contain a "/dev" entry, and the UBIFS FS on ++ * the host may contain "/dev" directory. In this case the attributes of the ++ * "/dev" directory inode has to be as the device table specifies. ++ * ++ * Note, the hash element is removed by this function as well. ++ */ ++int override_attributes(struct stat *st, struct path_htbl_element *ph_elt, ++ struct name_htbl_element *nh_elt) ++{ ++ if (!path_htbl) ++ return 0; ++ ++ if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode) || ++ S_ISFIFO(st->st_mode)) ++ return err_msg("%s/%s both exists at UBIFS root at host, " ++ "and is referred from the device table", ++ strcmp(ph_elt->path, "/") ? ph_elt->path : "", ++ nh_elt->name); ++ ++ if ((st->st_mode & S_IFMT) != (nh_elt->mode & S_IFMT)) ++ return err_msg("%s/%s is referred from the device table also exists in " ++ "the UBIFS root directory at host, but the file type is " ++ "different", strcmp(ph_elt->path, "/") ? ph_elt->path : "", ++ nh_elt->name); ++ ++ dbg_msg(3, "set UID %d, GID %d, mode %o for %s/%s as device table says", ++ nh_elt->uid, nh_elt->gid, nh_elt->mode, ph_elt->path, nh_elt->name); ++ ++ st->st_uid = nh_elt->uid; ++ st->st_gid = nh_elt->gid; ++ st->st_mode = nh_elt->mode; ++ ++ hashtable_remove(ph_elt->name_htbl, (void *)nh_elt->name); ++ return 0; ++} ++ ++/** ++ * first_name_htbl_element - return first element of the name hash table. ++ * @ph_elt: the path hash table the name hash table belongs to ++ * @itr: double pointer to a 'struct hashtable_itr' object where the ++ * information about further iterations is stored ++ * ++ * This function implements name hash table iteration together with ++ * 'next_name_htbl_element()'. Returns the first name hash table element or ++ * %NULL if the hash table is empty. ++ */ ++struct name_htbl_element * ++first_name_htbl_element(struct path_htbl_element *ph_elt, ++ struct hashtable_itr **itr) ++{ ++ if (!path_htbl || !ph_elt || hashtable_count(ph_elt->name_htbl) == 0) ++ return NULL; ++ ++ *itr = hashtable_iterator(ph_elt->name_htbl); ++ return hashtable_iterator_value(*itr); ++} ++ ++/** ++ * first_name_htbl_element - return next element of the name hash table. ++ * @ph_elt: the path hash table the name hash table belongs to ++ * @itr: double pointer to a 'struct hashtable_itr' object where the ++ * information about further iterations is stored ++ * ++ * This function implements name hash table iteration together with ++ * 'first_name_htbl_element()'. Returns the next name hash table element or ++ * %NULL if there are no more elements. ++ */ ++struct name_htbl_element * ++next_name_htbl_element(struct path_htbl_element *ph_elt, ++ struct hashtable_itr **itr) ++{ ++ if (!path_htbl || !ph_elt || !hashtable_iterator_advance(*itr)) ++ return NULL; ++ ++ return hashtable_iterator_value(*itr); ++} ++ ++/** ++ * free_devtable_info - free device table information. ++ * ++ * This function frees the path hash table and the name hash tables. ++ */ ++void free_devtable_info(void) ++{ ++ struct hashtable_itr *ph_itr; ++ struct path_htbl_element *ph_elt; ++ ++ if (!path_htbl) ++ return; ++ ++ if (hashtable_count(path_htbl) > 0) { ++ ph_itr = hashtable_iterator(path_htbl); ++ do { ++ ph_elt = hashtable_iterator_value(ph_itr); ++ /* ++ * Note, since we use the same string for the key and ++ * @name in the name hash table elements, we do not ++ * have to iterate name hash table because @name memory ++ * will be freed when freeing the key. ++ */ ++ hashtable_destroy(ph_elt->name_htbl, 1); ++ } while (hashtable_iterator_advance(ph_itr)); ++ } ++ hashtable_destroy(path_htbl, 1); ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/hashtable/hashtable.c linux-2.6.24.7/fs/ubifs/mkfs.ubifs/hashtable/hashtable.c +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/hashtable/hashtable.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/hashtable/hashtable.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,274 @@ ++/* Copyright (C) 2004 Christopher Clark */ ++ ++#include "hashtable.h" ++#include "hashtable_private.h" ++#include ++#include ++#include ++#include ++ ++/* ++Credit for primes table: Aaron Krowne ++ http://br.endernet.org/~akrowne/ ++ http://planetmath.org/encyclopedia/GoodHashTablePrimes.html ++*/ ++static const unsigned int primes[] = { ++53, 97, 193, 389, ++769, 1543, 3079, 6151, ++12289, 24593, 49157, 98317, ++196613, 393241, 786433, 1572869, ++3145739, 6291469, 12582917, 25165843, ++50331653, 100663319, 201326611, 402653189, ++805306457, 1610612741 ++}; ++const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]); ++const float max_load_factor = 0.65; ++ ++/*****************************************************************************/ ++struct hashtable * ++create_hashtable(unsigned int minsize, ++ unsigned int (*hashf) (void*), ++ int (*eqf) (void*,void*)) ++{ ++ struct hashtable *h; ++ unsigned int pindex, size = primes[0]; ++ /* Check requested hashtable isn't too large */ ++ if (minsize > (1u << 30)) return NULL; ++ /* Enforce size as prime */ ++ for (pindex=0; pindex < prime_table_length; pindex++) { ++ if (primes[pindex] > minsize) { size = primes[pindex]; break; } ++ } ++ h = (struct hashtable *)malloc(sizeof(struct hashtable)); ++ if (NULL == h) return NULL; /*oom*/ ++ h->table = (struct entry **)malloc(sizeof(struct entry*) * size); ++ if (NULL == h->table) { free(h); return NULL; } /*oom*/ ++ memset(h->table, 0, size * sizeof(struct entry *)); ++ h->tablelength = size; ++ h->primeindex = pindex; ++ h->entrycount = 0; ++ h->hashfn = hashf; ++ h->eqfn = eqf; ++ h->loadlimit = (unsigned int) ceil(size * max_load_factor); ++ return h; ++} ++ ++/*****************************************************************************/ ++unsigned int ++hash(struct hashtable *h, void *k) ++{ ++ /* Aim to protect against poor hash functions by adding logic here ++ * - logic taken from java 1.4 hashtable source */ ++ unsigned int i = h->hashfn(k); ++ i += ~(i << 9); ++ i ^= ((i >> 14) | (i << 18)); /* >>> */ ++ i += (i << 4); ++ i ^= ((i >> 10) | (i << 22)); /* >>> */ ++ return i; ++} ++ ++/*****************************************************************************/ ++static int ++hashtable_expand(struct hashtable *h) ++{ ++ /* Double the size of the table to accomodate more entries */ ++ struct entry **newtable; ++ struct entry *e; ++ struct entry **pE; ++ unsigned int newsize, i, index; ++ /* Check we're not hitting max capacity */ ++ if (h->primeindex == (prime_table_length - 1)) return 0; ++ newsize = primes[++(h->primeindex)]; ++ ++ newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize); ++ if (NULL != newtable) ++ { ++ memset(newtable, 0, newsize * sizeof(struct entry *)); ++ /* This algorithm is not 'stable'. ie. it reverses the list ++ * when it transfers entries between the tables */ ++ for (i = 0; i < h->tablelength; i++) { ++ while (NULL != (e = h->table[i])) { ++ h->table[i] = e->next; ++ index = indexFor(newsize,e->h); ++ e->next = newtable[index]; ++ newtable[index] = e; ++ } ++ } ++ free(h->table); ++ h->table = newtable; ++ } ++ /* Plan B: realloc instead */ ++ else ++ { ++ newtable = (struct entry **) ++ realloc(h->table, newsize * sizeof(struct entry *)); ++ if (NULL == newtable) { (h->primeindex)--; return 0; } ++ h->table = newtable; ++ memset(newtable[h->tablelength], 0, newsize - h->tablelength); ++ for (i = 0; i < h->tablelength; i++) { ++ for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) { ++ index = indexFor(newsize,e->h); ++ if (index == i) ++ { ++ pE = &(e->next); ++ } ++ else ++ { ++ *pE = e->next; ++ e->next = newtable[index]; ++ newtable[index] = e; ++ } ++ } ++ } ++ } ++ h->tablelength = newsize; ++ h->loadlimit = (unsigned int) ceil(newsize * max_load_factor); ++ return -1; ++} ++ ++/*****************************************************************************/ ++unsigned int ++hashtable_count(struct hashtable *h) ++{ ++ return h->entrycount; ++} ++ ++/*****************************************************************************/ ++int ++hashtable_insert(struct hashtable *h, void *k, void *v) ++{ ++ /* This method allows duplicate keys - but they shouldn't be used */ ++ unsigned int index; ++ struct entry *e; ++ if (++(h->entrycount) > h->loadlimit) ++ { ++ /* Ignore the return value. If expand fails, we should ++ * still try cramming just this value into the existing table ++ * -- we may not have memory for a larger table, but one more ++ * element may be ok. Next time we insert, we'll try expanding again.*/ ++ hashtable_expand(h); ++ } ++ e = (struct entry *)malloc(sizeof(struct entry)); ++ if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ ++ e->h = hash(h,k); ++ index = indexFor(h->tablelength,e->h); ++ e->k = k; ++ e->v = v; ++ e->next = h->table[index]; ++ h->table[index] = e; ++ return -1; ++} ++ ++/*****************************************************************************/ ++void * /* returns value associated with key */ ++hashtable_search(struct hashtable *h, void *k) ++{ ++ struct entry *e; ++ unsigned int hashvalue, index; ++ hashvalue = hash(h,k); ++ index = indexFor(h->tablelength,hashvalue); ++ e = h->table[index]; ++ while (NULL != e) ++ { ++ /* Check hash value to short circuit heavier comparison */ ++ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; ++ e = e->next; ++ } ++ return NULL; ++} ++ ++/*****************************************************************************/ ++void * /* returns value associated with key */ ++hashtable_remove(struct hashtable *h, void *k) ++{ ++ /* TODO: consider compacting the table when the load factor drops enough, ++ * or provide a 'compact' method. */ ++ ++ struct entry *e; ++ struct entry **pE; ++ void *v; ++ unsigned int hashvalue, index; ++ ++ hashvalue = hash(h,k); ++ index = indexFor(h->tablelength,hash(h,k)); ++ pE = &(h->table[index]); ++ e = *pE; ++ while (NULL != e) ++ { ++ /* Check hash value to short circuit heavier comparison */ ++ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) ++ { ++ *pE = e->next; ++ h->entrycount--; ++ v = e->v; ++ freekey(e->k); ++ free(e); ++ return v; ++ } ++ pE = &(e->next); ++ e = e->next; ++ } ++ return NULL; ++} ++ ++/*****************************************************************************/ ++/* destroy */ ++void ++hashtable_destroy(struct hashtable *h, int free_values) ++{ ++ unsigned int i; ++ struct entry *e, *f; ++ struct entry **table = h->table; ++ if (free_values) ++ { ++ for (i = 0; i < h->tablelength; i++) ++ { ++ e = table[i]; ++ while (NULL != e) ++ { f = e; e = e->next; freekey(f->k); free(f->v); free(f); } ++ } ++ } ++ else ++ { ++ for (i = 0; i < h->tablelength; i++) ++ { ++ e = table[i]; ++ while (NULL != e) ++ { f = e; e = e->next; freekey(f->k); free(f); } ++ } ++ } ++ free(h->table); ++ free(h); ++} ++ ++/* ++ * Copyright (c) 2002, Christopher Clark ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * * Neither the name of the original author; nor the names of any contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER ++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++*/ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/hashtable/hashtable.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/hashtable/hashtable.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/hashtable/hashtable.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/hashtable/hashtable.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,199 @@ ++/* Copyright (C) 2002 Christopher Clark */ ++ ++#ifndef __HASHTABLE_CWC22_H__ ++#define __HASHTABLE_CWC22_H__ ++ ++struct hashtable; ++ ++/* Example of use: ++ * ++ * struct hashtable *h; ++ * struct some_key *k; ++ * struct some_value *v; ++ * ++ * static unsigned int hash_from_key_fn( void *k ); ++ * static int keys_equal_fn ( void *key1, void *key2 ); ++ * ++ * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn); ++ * k = (struct some_key *) malloc(sizeof(struct some_key)); ++ * v = (struct some_value *) malloc(sizeof(struct some_value)); ++ * ++ * (initialise k and v to suitable values) ++ * ++ * if (! hashtable_insert(h,k,v) ) ++ * { exit(-1); } ++ * ++ * if (NULL == (found = hashtable_search(h,k) )) ++ * { printf("not found!"); } ++ * ++ * if (NULL == (found = hashtable_remove(h,k) )) ++ * { printf("Not found\n"); } ++ * ++ */ ++ ++/* Macros may be used to define type-safe(r) hashtable access functions, with ++ * methods specialized to take known key and value types as parameters. ++ * ++ * Example: ++ * ++ * Insert this at the start of your file: ++ * ++ * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value); ++ * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value); ++ * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value); ++ * ++ * This defines the functions 'insert_some', 'search_some' and 'remove_some'. ++ * These operate just like hashtable_insert etc., with the same parameters, ++ * but their function signatures have 'struct some_key *' rather than ++ * 'void *', and hence can generate compile time errors if your program is ++ * supplying incorrect data as a key (and similarly for value). ++ * ++ * Note that the hash and key equality functions passed to create_hashtable ++ * still take 'void *' parameters instead of 'some key *'. This shouldn't be ++ * a difficult issue as they're only defined and passed once, and the other ++ * functions will ensure that only valid keys are supplied to them. ++ * ++ * The cost for this checking is increased code size and runtime overhead ++ * - if performance is important, it may be worth switching back to the ++ * unsafe methods once your program has been debugged with the safe methods. ++ * This just requires switching to some simple alternative defines - eg: ++ * #define insert_some hashtable_insert ++ * ++ */ ++ ++/***************************************************************************** ++ * create_hashtable ++ ++ * @name create_hashtable ++ * @param minsize minimum initial size of hashtable ++ * @param hashfunction function for hashing keys ++ * @param key_eq_fn function for determining key equality ++ * @return newly created hashtable or NULL on failure ++ */ ++ ++struct hashtable * ++create_hashtable(unsigned int minsize, ++ unsigned int (*hashfunction) (void*), ++ int (*key_eq_fn) (void*,void*)); ++ ++/***************************************************************************** ++ * hashtable_insert ++ ++ * @name hashtable_insert ++ * @param h the hashtable to insert into ++ * @param k the key - hashtable claims ownership and will free on removal ++ * @param v the value - does not claim ownership ++ * @return non-zero for successful insertion ++ * ++ * This function will cause the table to expand if the insertion would take ++ * the ratio of entries to table size over the maximum load factor. ++ * ++ * This function does not check for repeated insertions with a duplicate key. ++ * The value returned when using a duplicate key is undefined -- when ++ * the hashtable changes size, the order of retrieval of duplicate key ++ * entries is reversed. ++ * If in doubt, remove before insert. ++ */ ++ ++int ++hashtable_insert(struct hashtable *h, void *k, void *v); ++ ++#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ ++int fnname (struct hashtable *h, keytype *k, valuetype *v) \ ++{ \ ++ return hashtable_insert(h,k,v); \ ++} ++ ++/***************************************************************************** ++ * hashtable_search ++ ++ * @name hashtable_search ++ * @param h the hashtable to search ++ * @param k the key to search for - does not claim ownership ++ * @return the value associated with the key, or NULL if none found ++ */ ++ ++void * ++hashtable_search(struct hashtable *h, void *k); ++ ++#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ ++valuetype * fnname (struct hashtable *h, keytype *k) \ ++{ \ ++ return (valuetype *) (hashtable_search(h,k)); \ ++} ++ ++/***************************************************************************** ++ * hashtable_remove ++ ++ * @name hashtable_remove ++ * @param h the hashtable to remove the item from ++ * @param k the key to search for - does not claim ownership ++ * @return the value associated with the key, or NULL if none found ++ */ ++ ++void * /* returns value */ ++hashtable_remove(struct hashtable *h, void *k); ++ ++#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ ++valuetype * fnname (struct hashtable *h, keytype *k) \ ++{ \ ++ return (valuetype *) (hashtable_remove(h,k)); \ ++} ++ ++ ++/***************************************************************************** ++ * hashtable_count ++ ++ * @name hashtable_count ++ * @param h the hashtable ++ * @return the number of items stored in the hashtable ++ */ ++unsigned int ++hashtable_count(struct hashtable *h); ++ ++ ++/***************************************************************************** ++ * hashtable_destroy ++ ++ * @name hashtable_destroy ++ * @param h the hashtable ++ * @param free_values whether to call 'free' on the remaining values ++ */ ++ ++void ++hashtable_destroy(struct hashtable *h, int free_values); ++ ++#endif /* __HASHTABLE_CWC22_H__ */ ++ ++/* ++ * Copyright (c) 2002, Christopher Clark ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * * Neither the name of the original author; nor the names of any contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER ++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++*/ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.c linux-2.6.24.7/fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.c +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,188 @@ ++/* Copyright (C) 2002, 2004 Christopher Clark */ ++ ++#include "hashtable.h" ++#include "hashtable_private.h" ++#include "hashtable_itr.h" ++#include /* defines NULL */ ++ ++/*****************************************************************************/ ++/* hashtable_iterator - iterator constructor */ ++ ++struct hashtable_itr * ++hashtable_iterator(struct hashtable *h) ++{ ++ unsigned int i, tablelength; ++ struct hashtable_itr *itr = (struct hashtable_itr *) ++ malloc(sizeof(struct hashtable_itr)); ++ if (NULL == itr) return NULL; ++ itr->h = h; ++ itr->e = NULL; ++ itr->parent = NULL; ++ tablelength = h->tablelength; ++ itr->index = tablelength; ++ if (0 == h->entrycount) return itr; ++ ++ for (i = 0; i < tablelength; i++) ++ { ++ if (NULL != h->table[i]) ++ { ++ itr->e = h->table[i]; ++ itr->index = i; ++ break; ++ } ++ } ++ return itr; ++} ++ ++/*****************************************************************************/ ++/* key - return the key of the (key,value) pair at the current position */ ++/* value - return the value of the (key,value) pair at the current position */ ++ ++void * ++hashtable_iterator_key(struct hashtable_itr *i) ++{ return i->e->k; } ++ ++void * ++hashtable_iterator_value(struct hashtable_itr *i) ++{ return i->e->v; } ++ ++/*****************************************************************************/ ++/* advance - advance the iterator to the next element ++ * returns zero if advanced to end of table */ ++ ++int ++hashtable_iterator_advance(struct hashtable_itr *itr) ++{ ++ unsigned int j,tablelength; ++ struct entry **table; ++ struct entry *next; ++ if (NULL == itr->e) return 0; /* stupidity check */ ++ ++ next = itr->e->next; ++ if (NULL != next) ++ { ++ itr->parent = itr->e; ++ itr->e = next; ++ return -1; ++ } ++ tablelength = itr->h->tablelength; ++ itr->parent = NULL; ++ if (tablelength <= (j = ++(itr->index))) ++ { ++ itr->e = NULL; ++ return 0; ++ } ++ table = itr->h->table; ++ while (NULL == (next = table[j])) ++ { ++ if (++j >= tablelength) ++ { ++ itr->index = tablelength; ++ itr->e = NULL; ++ return 0; ++ } ++ } ++ itr->index = j; ++ itr->e = next; ++ return -1; ++} ++ ++/*****************************************************************************/ ++/* remove - remove the entry at the current iterator position ++ * and advance the iterator, if there is a successive ++ * element. ++ * If you want the value, read it before you remove: ++ * beware memory leaks if you don't. ++ * Returns zero if end of iteration. */ ++ ++int ++hashtable_iterator_remove(struct hashtable_itr *itr) ++{ ++ struct entry *remember_e, *remember_parent; ++ int ret; ++ ++ /* Do the removal */ ++ if (NULL == (itr->parent)) ++ { ++ /* element is head of a chain */ ++ itr->h->table[itr->index] = itr->e->next; ++ } else { ++ /* element is mid-chain */ ++ itr->parent->next = itr->e->next; ++ } ++ /* itr->e is now outside the hashtable */ ++ remember_e = itr->e; ++ itr->h->entrycount--; ++ freekey(remember_e->k); ++ ++ /* Advance the iterator, correcting the parent */ ++ remember_parent = itr->parent; ++ ret = hashtable_iterator_advance(itr); ++ if (itr->parent == remember_e) { itr->parent = remember_parent; } ++ free(remember_e); ++ return ret; ++} ++ ++/*****************************************************************************/ ++int /* returns zero if not found */ ++hashtable_iterator_search(struct hashtable_itr *itr, ++ struct hashtable *h, void *k) ++{ ++ struct entry *e, *parent; ++ unsigned int hashvalue, index; ++ ++ hashvalue = hash(h,k); ++ index = indexFor(h->tablelength,hashvalue); ++ ++ e = h->table[index]; ++ parent = NULL; ++ while (NULL != e) ++ { ++ /* Check hash value to short circuit heavier comparison */ ++ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) ++ { ++ itr->index = index; ++ itr->e = e; ++ itr->parent = parent; ++ itr->h = h; ++ return -1; ++ } ++ parent = e; ++ e = e->next; ++ } ++ return 0; ++} ++ ++ ++/* ++ * Copyright (c) 2002, 2004, Christopher Clark ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * * Neither the name of the original author; nor the names of any contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER ++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++*/ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,112 @@ ++/* Copyright (C) 2002, 2004 Christopher Clark */ ++ ++#ifndef __HASHTABLE_ITR_CWC22__ ++#define __HASHTABLE_ITR_CWC22__ ++#include "hashtable.h" ++#include "hashtable_private.h" /* needed to enable inlining */ ++ ++/*****************************************************************************/ ++/* This struct is only concrete here to allow the inlining of two of the ++ * accessor functions. */ ++struct hashtable_itr ++{ ++ struct hashtable *h; ++ struct entry *e; ++ struct entry *parent; ++ unsigned int index; ++}; ++ ++ ++/*****************************************************************************/ ++/* hashtable_iterator ++ */ ++ ++struct hashtable_itr * ++hashtable_iterator(struct hashtable *h); ++ ++/*****************************************************************************/ ++/* hashtable_iterator_key ++ * - return the value of the (key,value) pair at the current position */ ++ ++extern inline void * ++hashtable_iterator_key(struct hashtable_itr *i) ++{ ++ return i->e->k; ++} ++ ++/*****************************************************************************/ ++/* value - return the value of the (key,value) pair at the current position */ ++ ++extern inline void * ++hashtable_iterator_value(struct hashtable_itr *i) ++{ ++ return i->e->v; ++} ++ ++/*****************************************************************************/ ++/* advance - advance the iterator to the next element ++ * returns zero if advanced to end of table */ ++ ++int ++hashtable_iterator_advance(struct hashtable_itr *itr); ++ ++/*****************************************************************************/ ++/* remove - remove current element and advance the iterator to the next element ++ * NB: if you need the value to free it, read it before ++ * removing. ie: beware memory leaks! ++ * returns zero if advanced to end of table */ ++ ++int ++hashtable_iterator_remove(struct hashtable_itr *itr); ++ ++/*****************************************************************************/ ++/* search - overwrite the supplied iterator, to point to the entry ++ * matching the supplied key. ++ h points to the hashtable to be searched. ++ * returns zero if not found. */ ++int ++hashtable_iterator_search(struct hashtable_itr *itr, ++ struct hashtable *h, void *k); ++ ++#define DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \ ++int fnname (struct hashtable_itr *i, struct hashtable *h, keytype *k) \ ++{ \ ++ return (hashtable_iterator_search(i,h,k)); \ ++} ++ ++ ++ ++#endif /* __HASHTABLE_ITR_CWC22__*/ ++ ++/* ++ * Copyright (c) 2002, 2004, Christopher Clark ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * * Neither the name of the original author; nor the names of any contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER ++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++*/ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/hashtable/hashtable_private.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/hashtable/hashtable_private.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/hashtable/hashtable_private.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/hashtable/hashtable_private.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,85 @@ ++/* Copyright (C) 2002, 2004 Christopher Clark */ ++ ++#ifndef __HASHTABLE_PRIVATE_CWC22_H__ ++#define __HASHTABLE_PRIVATE_CWC22_H__ ++ ++#include "hashtable.h" ++ ++/*****************************************************************************/ ++struct entry ++{ ++ void *k, *v; ++ unsigned int h; ++ struct entry *next; ++}; ++ ++struct hashtable { ++ unsigned int tablelength; ++ struct entry **table; ++ unsigned int entrycount; ++ unsigned int loadlimit; ++ unsigned int primeindex; ++ unsigned int (*hashfn) (void *k); ++ int (*eqfn) (void *k1, void *k2); ++}; ++ ++/*****************************************************************************/ ++unsigned int ++hash(struct hashtable *h, void *k); ++ ++/*****************************************************************************/ ++/* indexFor */ ++static inline unsigned int ++indexFor(unsigned int tablelength, unsigned int hashvalue) { ++ return (hashvalue % tablelength); ++}; ++ ++/* Only works if tablelength == 2^N */ ++/*static inline unsigned int ++indexFor(unsigned int tablelength, unsigned int hashvalue) ++{ ++ return (hashvalue & (tablelength - 1u)); ++} ++*/ ++ ++/*****************************************************************************/ ++#define freekey(X) free(X) ++/*define freekey(X) ; */ ++ ++ ++/*****************************************************************************/ ++ ++#endif /* __HASHTABLE_PRIVATE_CWC22_H__*/ ++ ++/* ++ * Copyright (c) 2002, Christopher Clark ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * * Neither the name of the original author; nor the names of any contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * ++ * 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 MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER ++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE. ++*/ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/key.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/key.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/key.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/key.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,511 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This header contains various key-related definitions and helper function. ++ * UBIFS allows several key schemes, so we access key fields only via these ++ * helpers. At the moment only one key scheme is supported. ++ * ++ * Simple key scheme ++ * ~~~~~~~~~~~~~~~~~ ++ * ++ * Keys are 64-bits long. First 32-bits are inode number (parent inode number ++ * in case of direntry key). Next 3 bits are node type. The last 29 bits are ++ * 4KiB offset in case of inode node, and direntry hash in case of a direntry ++ * node. We use "r5" hash borrowed from reiserfs. ++ */ ++ ++#ifndef __UBIFS_KEY_H__ ++#define __UBIFS_KEY_H__ ++ ++/** ++ * key_r5_hash - R5 hash function (borrowed from reiserfs). ++ * @s: direntry name ++ * @len: name length ++ */ ++static inline uint32_t key_r5_hash(const char *s, int len) ++{ ++ uint32_t a = 0; ++ const signed char *str = (const signed char *)s; ++ ++ while (*str) { ++ a += *str << 4; ++ a += *str >> 4; ++ a *= 11; ++ str++; ++ } ++ ++ a &= UBIFS_S_KEY_HASH_MASK; ++ ++ /* ++ * We use hash values as offset in directories, so values %0 and %1 are ++ * reserved for "." and "..". %2 is reserved for possible future use. ++ */ ++ if (unlikely(a >= 0 && a <= 2)) ++ a += 3; ++ return a; ++} ++ ++/** ++ * key_test_hash - testing hash function. ++ * @str: direntry name ++ * @len: name length ++ */ ++static inline uint32_t key_test_hash(const char *str, int len) ++{ ++ uint32_t a = 0; ++ ++ len = min_t(uint32_t, len, 4); ++ memcpy(&a, str, len); ++ a &= UBIFS_S_KEY_HASH_MASK; ++ if (unlikely(a >= 0 && a <= 2)) ++ a += 3; ++ return a; ++} ++ ++/** ++ * ino_key_init - initialize inode key. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: inode number ++ */ ++static inline void ino_key_init(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS; ++} ++ ++/** ++ * ino_key_init_flash - initialize on-flash inode key. ++ * @c: UBIFS file-system description object ++ * @k: key to initialize ++ * @inum: inode number ++ */ ++static inline void ino_key_init_flash(const struct ubifs_info *c, void *k, ++ ino_t inum) ++{ ++ union ubifs_key *key = k; ++ ++ key->j32[0] = cpu_to_le32(inum); ++ key->j32[1] = cpu_to_le32(UBIFS_INO_KEY << UBIFS_S_KEY_BLOCK_BITS); ++ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8); ++} ++ ++/** ++ * lowest_ino_key - get the lowest possible inode key. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: inode number ++ */ ++static inline void lowest_ino_key(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = 0; ++} ++ ++/** ++ * highest_ino_key - get the highest possible inode key. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: inode number ++ */ ++static inline void highest_ino_key(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = 0xffffffff; ++} ++ ++/** ++ * dent_key_init - initialize directory entry key. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: parent inode number ++ * @nm: direntry name and length ++ */ ++static inline void dent_key_init(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum, ++ const struct qstr *nm) ++{ ++ uint32_t hash = c->key_hash(nm->name, nm->len); ++ ++ key->u32[0] = inum; ++ key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS); ++} ++ ++/** ++ * dent_key_init_hash - initialize directory entry key without re-calculating ++ * hash function. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: parent inode number ++ * @hash: direntry name hash ++ */ ++static inline void dent_key_init_hash(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum, ++ uint32_t hash) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS); ++} ++ ++/** ++ * dent_key_init_flash - initialize on-flash directory entry key. ++ * @c: UBIFS file-system description object ++ * @k: key to initialize ++ * @inum: parent inode number ++ * @nm: direntry name and length ++ */ ++static inline void dent_key_init_flash(const struct ubifs_info *c, void *k, ++ ino_t inum, const struct qstr *nm) ++{ ++ union ubifs_key *key = k; ++ uint32_t hash = c->key_hash(nm->name, nm->len); ++ ++ key->j32[0] = cpu_to_le32(inum); ++ key->j32[1] = cpu_to_le32(hash | ++ (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS)); ++ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8); ++} ++ ++/** ++ * lowest_dent_key - get the lowest possible directory entry key. ++ * @c: UBIFS file-system description object ++ * @key: where to store the lowest key ++ * @inum: parent inode number ++ */ ++static inline void lowest_dent_key(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS; ++} ++ ++/** ++ * xent_key_init - initialize extended attribute entry key. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: host inode number ++ * @nm: extended attribute entry name and length ++ */ ++static inline void xent_key_init(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum, ++ const struct qstr *nm) ++{ ++ uint32_t hash = c->key_hash(nm->name, nm->len); ++ ++ key->u32[0] = inum; ++ key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS); ++} ++ ++/** ++ * xent_key_init_hash - initialize extended attribute entry key without ++ * re-calculating hash function. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: host inode number ++ * @hash: extended attribute entry name hash ++ */ ++static inline void xent_key_init_hash(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum, ++ uint32_t hash) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS); ++} ++ ++/** ++ * xent_key_init_flash - initialize on-flash extended attribute entry key. ++ * @c: UBIFS file-system description object ++ * @k: key to initialize ++ * @inum: host inode number ++ * @nm: extended attribute entry name and length ++ */ ++static inline void xent_key_init_flash(const struct ubifs_info *c, void *k, ++ ino_t inum, const struct qstr *nm) ++{ ++ union ubifs_key *key = k; ++ uint32_t hash = c->key_hash(nm->name, nm->len); ++ ++ key->j32[0] = cpu_to_le32(inum); ++ key->j32[1] = cpu_to_le32(hash | ++ (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS)); ++ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8); ++} ++ ++/** ++ * lowest_xent_key - get the lowest possible extended attribute entry key. ++ * @c: UBIFS file-system description object ++ * @key: where to store the lowest key ++ * @inum: host inode number ++ */ ++static inline void lowest_xent_key(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS; ++} ++ ++/** ++ * data_key_init - initialize data key. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: inode number ++ * @block: block number ++ */ ++static inline void data_key_init(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum, ++ unsigned int block) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = block | (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS); ++} ++ ++/** ++ * data_key_init_flash - initialize on-flash data key. ++ * @c: UBIFS file-system description object ++ * @k: key to initialize ++ * @inum: inode number ++ * @block: block number ++ */ ++static inline void data_key_init_flash(const struct ubifs_info *c, void *k, ++ ino_t inum, unsigned int block) ++{ ++ union ubifs_key *key = k; ++ ++ key->j32[0] = cpu_to_le32(inum); ++ key->j32[1] = cpu_to_le32(block | ++ (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS)); ++ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8); ++} ++ ++/** ++ * trun_key_init - initialize truncation node key. ++ * @c: UBIFS file-system description object ++ * @key: key to initialize ++ * @inum: inode number ++ */ ++static inline void trun_key_init(const struct ubifs_info *c, ++ union ubifs_key *key, ino_t inum) ++{ ++ key->u32[0] = inum; ++ key->u32[1] = UBIFS_TRUN_KEY << UBIFS_S_KEY_BLOCK_BITS; ++} ++ ++/** ++ * trun_key_init_flash - initialize on-flash truncation node key. ++ * @c: UBIFS file-system description object ++ * @k: key to initialize ++ * @inum: inode number ++ */ ++static inline void trun_key_init_flash(const struct ubifs_info *c, void *k, ++ ino_t inum) ++{ ++ union ubifs_key *key = k; ++ ++ key->j32[0] = cpu_to_le32(inum); ++ key->j32[1] = cpu_to_le32(UBIFS_TRUN_KEY << UBIFS_S_KEY_BLOCK_BITS); ++ memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8); ++} ++ ++/** ++ * key_type - get key type. ++ * @c: UBIFS file-system description object ++ * @key: key to get type of ++ */ ++static inline int key_type(const struct ubifs_info *c, ++ const union ubifs_key *key) ++{ ++ return key->u32[1] >> UBIFS_S_KEY_BLOCK_BITS; ++} ++ ++/** ++ * key_type_flash - get type of a on-flash formatted key. ++ * @c: UBIFS file-system description object ++ * @k: key to get type of ++ */ ++static inline int key_type_flash(const struct ubifs_info *c, const void *k) ++{ ++ const union ubifs_key *key = k; ++ ++ return le32_to_cpu(key->u32[1]) >> UBIFS_S_KEY_BLOCK_BITS; ++} ++ ++/** ++ * key_ino - fetch inode number from key. ++ * @c: UBIFS file-system description object ++ * @k: key to fetch inode number from ++ */ ++static inline ino_t key_ino(const struct ubifs_info *c, const void *k) ++{ ++ const union ubifs_key *key = k; ++ ++ return key->u32[0]; ++} ++ ++/** ++ * key_ino_flash - fetch inode number from an on-flash formatted key. ++ * @c: UBIFS file-system description object ++ * @k: key to fetch inode number from ++ */ ++static inline ino_t key_ino_flash(const struct ubifs_info *c, const void *k) ++{ ++ const union ubifs_key *key = k; ++ ++ return le32_to_cpu(key->j32[0]); ++} ++ ++/** ++ * key_hash - get directory entry hash. ++ * @c: UBIFS file-system description object ++ * @key: the key to get hash from ++ */ ++static inline int key_hash(const struct ubifs_info *c, ++ const union ubifs_key *key) ++{ ++ return key->u32[1] & UBIFS_S_KEY_HASH_MASK; ++} ++ ++/** ++ * key_hash_flash - get directory entry hash from an on-flash formatted key. ++ * @c: UBIFS file-system description object ++ * @k: the key to get hash from ++ */ ++static inline int key_hash_flash(const struct ubifs_info *c, const void *k) ++{ ++ const union ubifs_key *key = k; ++ ++ return le32_to_cpu(key->j32[1]) & UBIFS_S_KEY_HASH_MASK; ++} ++ ++/** ++ * key_block - get data block number. ++ * @c: UBIFS file-system description object ++ * @key: the key to get the block number from ++ */ ++static inline unsigned int key_block(const struct ubifs_info *c, ++ const union ubifs_key *key) ++{ ++ return key->u32[1] & UBIFS_S_KEY_BLOCK_MASK; ++} ++ ++/** ++ * key_read - transform a key to in-memory format. ++ * @c: UBIFS file-system description object ++ * @from: the key to transform ++ * @to: the key to store the result ++ */ ++static inline void key_read(const struct ubifs_info *c, const void *from, ++ union ubifs_key *to) ++{ ++ const union ubifs_key *f = from; ++ ++ to->u32[0] = le32_to_cpu(f->j32[0]); ++ to->u32[1] = le32_to_cpu(f->j32[1]); ++} ++ ++/** ++ * key_write - transform a key from in-memory format. ++ * @c: UBIFS file-system description object ++ * @from: the key to transform ++ * @to: the key to store the result ++ */ ++static inline void key_write(const struct ubifs_info *c, ++ const union ubifs_key *from, void *to) ++{ ++ union ubifs_key *t = to; ++ ++ t->j32[0] = cpu_to_le32(from->u32[0]); ++ t->j32[1] = cpu_to_le32(from->u32[1]); ++ memset(to + 8, 0, UBIFS_MAX_KEY_LEN - 8); ++} ++ ++/** ++ * key_write_idx - transform a key from in-memory format for the index. ++ * @c: UBIFS file-system description object ++ * @from: the key to transform ++ * @to: the key to store the result ++ */ ++static inline void key_write_idx(const struct ubifs_info *c, ++ const union ubifs_key *from, void *to) ++{ ++ union ubifs_key *t = to; ++ ++ t->j32[0] = cpu_to_le32(from->u32[0]); ++ t->j32[1] = cpu_to_le32(from->u32[1]); ++} ++ ++/** ++ * key_copy - copy a key. ++ * @c: UBIFS file-system description object ++ * @from: the key to copy from ++ * @to: the key to copy to ++ */ ++static inline void key_copy(const struct ubifs_info *c, ++ const union ubifs_key *from, union ubifs_key *to) ++{ ++ to->u64[0] = from->u64[0]; ++} ++ ++/** ++ * keys_cmp - compare keys. ++ * @c: UBIFS file-system description object ++ * @key1: the first key to compare ++ * @key2: the second key to compare ++ * ++ * This function compares 2 keys and returns %-1 if @key1 is less than ++ * @key2, 0 if the keys are equivalent and %1 if @key1 is greater than @key2. ++ */ ++static inline int keys_cmp(const struct ubifs_info *c, ++ const union ubifs_key *key1, ++ const union ubifs_key *key2) ++{ ++ if (key1->u32[0] < key2->u32[0]) ++ return -1; ++ if (key1->u32[0] > key2->u32[0]) ++ return 1; ++ if (key1->u32[1] < key2->u32[1]) ++ return -1; ++ if (key1->u32[1] > key2->u32[1]) ++ return 1; ++ ++ return 0; ++} ++ ++/** ++ * is_hash_key - is a key vulnerable to hash collisions. ++ * @c: UBIFS file-system description object ++ * @key: key ++ * ++ * This function returns %1 if @key is a hashed key or %0 otherwise. ++ */ ++static inline int is_hash_key(const struct ubifs_info *c, ++ const union ubifs_key *key) ++{ ++ int type = key_type(c, key); ++ ++ return type == UBIFS_DENT_KEY || type == UBIFS_XENT_KEY; ++} ++ ++#endif /* !__UBIFS_KEY_H__ */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lpt.c linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lpt.c +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lpt.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lpt.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,577 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006, 2007 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy ++ */ ++ ++#include "mkfs.ubifs.h" ++ ++/** ++ * do_calc_lpt_geom - calculate sizes for the LPT area. ++ * @c: the UBIFS file-system description object ++ * ++ * Calculate the sizes of LPT bit fields, nodes, and tree, based on the ++ * properties of the flash and whether LPT is "big" (c->big_lpt). ++ */ ++static void do_calc_lpt_geom(struct ubifs_info *c) ++{ ++ int n, bits, per_leb_wastage; ++ long long sz, tot_wastage; ++ ++ c->pnode_cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; ++ ++ n = (c->pnode_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; ++ c->nnode_cnt = n; ++ while (n > 1) { ++ n = (n + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; ++ c->nnode_cnt += n; ++ } ++ ++ c->lpt_hght = 1; ++ n = UBIFS_LPT_FANOUT; ++ while (n < c->pnode_cnt) { ++ c->lpt_hght += 1; ++ n <<= UBIFS_LPT_FANOUT_SHIFT; ++ } ++ ++ c->space_bits = fls(c->leb_size) - 3; ++ c->lpt_lnum_bits = fls(c->lpt_lebs); ++ c->lpt_offs_bits = fls(c->leb_size - 1); ++ c->lpt_spc_bits = fls(c->leb_size); ++ ++ n = (c->max_leb_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; ++ c->pcnt_bits = fls(n - 1); ++ ++ c->lnum_bits = fls(c->max_leb_cnt - 1); ++ ++ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + ++ (c->big_lpt ? c->pcnt_bits : 0) + ++ (c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT; ++ c->pnode_sz = (bits + 7) / 8; ++ ++ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + ++ (c->big_lpt ? c->pcnt_bits : 0) + ++ (c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT; ++ c->nnode_sz = (bits + 7) / 8; ++ ++ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + ++ c->lpt_lebs * c->lpt_spc_bits * 2; ++ c->ltab_sz = (bits + 7) / 8; ++ ++ bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS + ++ c->lnum_bits * c->lsave_cnt; ++ c->lsave_sz = (bits + 7) / 8; ++ ++ /* Calculate the minimum LPT size */ ++ c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz; ++ c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz; ++ c->lpt_sz += c->ltab_sz; ++ c->lpt_sz += c->lsave_sz; ++ ++ /* Add wastage */ ++ sz = c->lpt_sz; ++ per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz); ++ sz += per_leb_wastage; ++ tot_wastage = per_leb_wastage; ++ while (sz > c->leb_size) { ++ sz += per_leb_wastage; ++ sz -= c->leb_size; ++ tot_wastage += per_leb_wastage; ++ } ++ tot_wastage += ALIGN(sz, c->min_io_size) - sz; ++ c->lpt_sz += tot_wastage; ++} ++ ++/** ++ * calc_dflt_lpt_geom - calculate default LPT geometry. ++ * @c: the UBIFS file-system description object ++ * @main_lebs: number of main area LEBs is passed and returned here ++ * @big_lpt: whether the LPT area is "big" is returned here ++ * ++ * The size of the LPT area depends on parameters that themselves are dependent ++ * on the size of the LPT area. This function, successively recalculates the LPT ++ * area geometry until the parameters and resultant geometry are consistent. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt) ++{ ++ int i, lebs_needed; ++ long long sz; ++ ++ /* Start by assuming the minimum number of LPT LEBs */ ++ c->lpt_lebs = UBIFS_MIN_LPT_LEBS; ++ c->main_lebs = *main_lebs - c->lpt_lebs; ++ if (c->main_lebs <= 0) ++ return -EINVAL; ++ ++ /* And assume we will use the small LPT model */ ++ c->big_lpt = 0; ++ ++ /* ++ * Calculate the geometry based on assumptions above and then see if it ++ * makes sense ++ */ ++ do_calc_lpt_geom(c); ++ ++ /* Small LPT model must have lpt_sz < leb_size */ ++ if (c->lpt_sz > c->leb_size) { ++ /* Nope, so try again using big LPT model */ ++ c->big_lpt = 1; ++ do_calc_lpt_geom(c); ++ } ++ ++ /* Now check there are enough LPT LEBs */ ++ for (i = 0; i < 64 ; i++) { ++ sz = c->lpt_sz * 4; /* Allow 4 times the size */ ++ sz += c->leb_size - 1; ++ do_div(sz, c->leb_size); ++ lebs_needed = sz; ++ if (lebs_needed > c->lpt_lebs) { ++ /* Not enough LPT LEBs so try again with more */ ++ c->lpt_lebs = lebs_needed; ++ c->main_lebs = *main_lebs - c->lpt_lebs; ++ if (c->main_lebs <= 0) ++ return -EINVAL; ++ do_calc_lpt_geom(c); ++ continue; ++ } ++ if (c->ltab_sz > c->leb_size) { ++ err_msg("LPT ltab too big"); ++ return -EINVAL; ++ } ++ *main_lebs = c->main_lebs; ++ *big_lpt = c->big_lpt; ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++/** ++ * pack_bits - pack bit fields end-to-end. ++ * @addr: address at which to pack (passed and next address returned) ++ * @pos: bit position at which to pack (passed and next position returned) ++ * @val: value to pack ++ * @nrbits: number of bits of value to pack (1-32) ++ */ ++static void pack_bits(uint8_t **addr, int *pos, uint32_t val, int nrbits) ++{ ++ uint8_t *p = *addr; ++ int b = *pos; ++ ++ if (b) { ++ *p |= ((uint8_t)val) << b; ++ nrbits += b; ++ if (nrbits > 8) { ++ *++p = (uint8_t)(val >>= (8 - b)); ++ if (nrbits > 16) { ++ *++p = (uint8_t)(val >>= 8); ++ if (nrbits > 24) { ++ *++p = (uint8_t)(val >>= 8); ++ if (nrbits > 32) ++ *++p = (uint8_t)(val >>= 8); ++ } ++ } ++ } ++ } else { ++ *p = (uint8_t)val; ++ if (nrbits > 8) { ++ *++p = (uint8_t)(val >>= 8); ++ if (nrbits > 16) { ++ *++p = (uint8_t)(val >>= 8); ++ if (nrbits > 24) ++ *++p = (uint8_t)(val >>= 8); ++ } ++ } ++ } ++ b = nrbits & 7; ++ if (b == 0) ++ p++; ++ *addr = p; ++ *pos = b; ++} ++ ++/** ++ * pack_pnode - pack all the bit fields of a pnode. ++ * @c: UBIFS file-system description object ++ * @buf: buffer into which to pack ++ * @pnode: pnode to pack ++ */ ++static void pack_pnode(struct ubifs_info *c, void *buf, ++ struct ubifs_pnode *pnode) ++{ ++ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; ++ int i, pos = 0; ++ uint16_t crc; ++ ++ pack_bits(&addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS); ++ if (c->big_lpt) ++ pack_bits(&addr, &pos, pnode->num, c->pcnt_bits); ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ pack_bits(&addr, &pos, pnode->lprops[i].free >> 3, ++ c->space_bits); ++ pack_bits(&addr, &pos, pnode->lprops[i].dirty >> 3, ++ c->space_bits); ++ if (pnode->lprops[i].flags & LPROPS_INDEX) ++ pack_bits(&addr, &pos, 1, 1); ++ else ++ pack_bits(&addr, &pos, 0, 1); ++ } ++ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, ++ c->pnode_sz - UBIFS_LPT_CRC_BYTES); ++ addr = buf; ++ pos = 0; ++ pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); ++} ++ ++/** ++ * pack_nnode - pack all the bit fields of a nnode. ++ * @c: UBIFS file-system description object ++ * @buf: buffer into which to pack ++ * @nnode: nnode to pack ++ */ ++static void pack_nnode(struct ubifs_info *c, void *buf, ++ struct ubifs_nnode *nnode) ++{ ++ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; ++ int i, pos = 0; ++ uint16_t crc; ++ ++ pack_bits(&addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS); ++ if (c->big_lpt) ++ pack_bits(&addr, &pos, nnode->num, c->pcnt_bits); ++ for (i = 0; i < UBIFS_LPT_FANOUT; i++) { ++ int lnum = nnode->nbranch[i].lnum; ++ ++ if (lnum == 0) ++ lnum = c->lpt_last + 1; ++ pack_bits(&addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits); ++ pack_bits(&addr, &pos, nnode->nbranch[i].offs, ++ c->lpt_offs_bits); ++ } ++ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, ++ c->nnode_sz - UBIFS_LPT_CRC_BYTES); ++ addr = buf; ++ pos = 0; ++ pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); ++} ++ ++/** ++ * pack_ltab - pack the LPT's own lprops table. ++ * @c: UBIFS file-system description object ++ * @buf: buffer into which to pack ++ * @ltab: LPT's own lprops table to pack ++ */ ++static void pack_ltab(struct ubifs_info *c, void *buf, ++ struct ubifs_lpt_lprops *ltab) ++{ ++ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; ++ int i, pos = 0; ++ uint16_t crc; ++ ++ pack_bits(&addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS); ++ for (i = 0; i < c->lpt_lebs; i++) { ++ pack_bits(&addr, &pos, ltab[i].free, c->lpt_spc_bits); ++ pack_bits(&addr, &pos, ltab[i].dirty, c->lpt_spc_bits); ++ } ++ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, ++ c->ltab_sz - UBIFS_LPT_CRC_BYTES); ++ addr = buf; ++ pos = 0; ++ pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); ++} ++ ++/** ++ * pack_lsave - pack the LPT's save table. ++ * @c: UBIFS file-system description object ++ * @buf: buffer into which to pack ++ * @lsave: LPT's save table to pack ++ */ ++static void pack_lsave(struct ubifs_info *c, void *buf, int *lsave) ++{ ++ uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; ++ int i, pos = 0; ++ uint16_t crc; ++ ++ pack_bits(&addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS); ++ for (i = 0; i < c->lsave_cnt; i++) ++ pack_bits(&addr, &pos, lsave[i], c->lnum_bits); ++ crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, ++ c->lsave_sz - UBIFS_LPT_CRC_BYTES); ++ addr = buf; ++ pos = 0; ++ pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); ++} ++ ++/** ++ * set_ltab - set LPT LEB properties. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number ++ * @free: amount of free space ++ * @dirty: amount of dirty space ++ */ ++static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty) ++{ ++ dbg_msg(3, "LEB %d free %d dirty %d to %d %d", ++ lnum, c->ltab[lnum - c->lpt_first].free, ++ c->ltab[lnum - c->lpt_first].dirty, free, dirty); ++ c->ltab[lnum - c->lpt_first].free = free; ++ c->ltab[lnum - c->lpt_first].dirty = dirty; ++} ++ ++/** ++ * calc_nnode_num - calculate nnode number. ++ * @row: the row in the tree (root is zero) ++ * @col: the column in the row (leftmost is zero) ++ * ++ * The nnode number is a number that uniquely identifies a nnode and can be used ++ * easily to traverse the tree from the root to that nnode. ++ * ++ * This function calculates and returns the nnode number for the nnode at @row ++ * and @col. ++ */ ++static int calc_nnode_num(int row, int col) ++{ ++ int num, bits; ++ ++ num = 1; ++ while (row--) { ++ bits = (col & (UBIFS_LPT_FANOUT - 1)); ++ col >>= UBIFS_LPT_FANOUT_SHIFT; ++ num <<= UBIFS_LPT_FANOUT_SHIFT; ++ num |= bits; ++ } ++ return num; ++} ++ ++/** ++ * create_lpt - create LPT. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int create_lpt(struct ubifs_info *c) ++{ ++ int lnum, err = 0, i, j, cnt, len, alen, row; ++ int blnum, boffs, bsz, bcnt; ++ struct ubifs_pnode *pnode = NULL; ++ struct ubifs_nnode *nnode = NULL; ++ void *buf = NULL, *p; ++ int *lsave = NULL; ++ ++ pnode = malloc(sizeof(struct ubifs_pnode)); ++ nnode = malloc(sizeof(struct ubifs_nnode)); ++ buf = malloc(c->leb_size); ++ lsave = malloc(sizeof(int) * c->lsave_cnt); ++ if (!pnode || !nnode || !buf || !lsave) { ++ err = -ENOMEM; ++ goto out; ++ } ++ memset(pnode, 0 , sizeof(struct ubifs_pnode)); ++ memset(nnode, 0 , sizeof(struct ubifs_pnode)); ++ ++ c->lscan_lnum = c->main_first; ++ ++ lnum = c->lpt_first; ++ p = buf; ++ len = 0; ++ /* Number of leaf nodes (pnodes) */ ++ cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) >> UBIFS_LPT_FANOUT_SHIFT; ++ //printf("pnode_cnt=%d\n",cnt); ++ ++ /* ++ * To calculate the internal node branches, we keep information about ++ * the level below. ++ */ ++ blnum = lnum; /* LEB number of level below */ ++ boffs = 0; /* Offset of level below */ ++ bcnt = cnt; /* Number of nodes in level below */ ++ bsz = c->pnode_sz; /* Size of nodes in level below */ ++ ++ /* Add pnodes */ ++ for (i = 0; i < cnt; i++) { ++ if (len + c->pnode_sz > c->leb_size) { ++ alen = ALIGN(len, c->min_io_size); ++ set_ltab(c, lnum, c->leb_size - alen, alen - len); ++ memset(p, 0xff, alen - len); ++ err = write_leb(lnum++, alen, buf); ++ if (err) ++ goto out; ++ p = buf; ++ len = 0; ++ } ++ /* Fill in the pnode */ ++ for (j = 0; j < UBIFS_LPT_FANOUT; j++) { ++ int k = (i << UBIFS_LPT_FANOUT_SHIFT) + j; ++ ++ if (k < c->main_lebs) ++ pnode->lprops[j] = c->lpt[k]; ++ else { ++ pnode->lprops[j].free = c->leb_size; ++ pnode->lprops[j].dirty = 0; ++ pnode->lprops[j].flags = 0; ++ } ++ } ++ pack_pnode(c, p, pnode); ++ p += c->pnode_sz; ++ len += c->pnode_sz; ++ /* ++ * pnodes are simply numbered left to right starting at zero, ++ * which means the pnode number can be used easily to traverse ++ * down the tree to the corresponding pnode. ++ */ ++ pnode->num += 1; ++ } ++ ++ row = c->lpt_hght - 1; ++ /* Add all nnodes, one level at a time */ ++ while (1) { ++ /* Number of internal nodes (nnodes) at next level */ ++ cnt = (cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT; ++ if (cnt == 0) ++ cnt = 1; ++ for (i = 0; i < cnt; i++) { ++ if (len + c->nnode_sz > c->leb_size) { ++ alen = ALIGN(len, c->min_io_size); ++ set_ltab(c, lnum, c->leb_size - alen, ++ alen - len); ++ memset(p, 0xff, alen - len); ++ err = write_leb(lnum++, alen, buf); ++ if (err) ++ goto out; ++ p = buf; ++ len = 0; ++ } ++ /* The root is on row zero */ ++ if (row == 0) { ++ c->lpt_lnum = lnum; ++ c->lpt_offs = len; ++ } ++ /* Set branches to the level below */ ++ for (j = 0; j < UBIFS_LPT_FANOUT; j++) { ++ if (bcnt) { ++ if (boffs + bsz > c->leb_size) { ++ blnum += 1; ++ boffs = 0; ++ } ++ nnode->nbranch[j].lnum = blnum; ++ nnode->nbranch[j].offs = boffs; ++ boffs += bsz; ++ bcnt--; ++ } else { ++ nnode->nbranch[j].lnum = 0; ++ nnode->nbranch[j].offs = 0; ++ } ++ } ++ nnode->num = calc_nnode_num(row, i); ++ pack_nnode(c, p, nnode); ++ p += c->nnode_sz; ++ len += c->nnode_sz; ++ } ++ /* Row zero is the top row */ ++ if (row == 0) ++ break; ++ /* Update the information about the level below */ ++ bcnt = cnt; ++ bsz = c->nnode_sz; ++ row -= 1; ++ } ++ ++ if (c->big_lpt) { ++ /* Need to add LPT's save table */ ++ if (len + c->lsave_sz > c->leb_size) { ++ alen = ALIGN(len, c->min_io_size); ++ set_ltab(c, lnum, c->leb_size - alen, alen - len); ++ memset(p, 0xff, alen - len); ++ err = write_leb(lnum++, alen, buf); ++ if (err) ++ goto out; ++ p = buf; ++ len = 0; ++ } ++ ++ c->lsave_lnum = lnum; ++ c->lsave_offs = len; ++ ++ for (i = 0; i < c->lsave_cnt; i++) ++ lsave[i] = c->main_first + i; ++ ++ pack_lsave(c, p, lsave); ++ p += c->lsave_sz; ++ len += c->lsave_sz; ++ } ++ ++ /* Need to add LPT's own LEB properties table */ ++ if (len + c->ltab_sz > c->leb_size) { ++ alen = ALIGN(len, c->min_io_size); ++ set_ltab(c, lnum, c->leb_size - alen, alen - len); ++ memset(p, 0xff, alen - len); ++ err = write_leb(lnum++, alen, buf); ++ if (err) ++ goto out; ++ p = buf; ++ len = 0; ++ } ++ ++ c->ltab_lnum = lnum; ++ c->ltab_offs = len; ++ ++ /* Update ltab before packing it */ ++ len += c->ltab_sz; ++ alen = ALIGN(len, c->min_io_size); ++ set_ltab(c, lnum, c->leb_size - alen, alen - len); ++ ++ pack_ltab(c, p, c->ltab); ++ p += c->ltab_sz; ++ ++ /* Write remaining buffer */ ++ memset(p, 0xff, alen - len); ++ err = write_leb(lnum, alen, buf); ++ if (err) ++ goto out; ++ ++ c->nhead_lnum = lnum; ++ c->nhead_offs = ALIGN(len, c->min_io_size); ++ ++ dbg_msg(1, "space_bits: %d", c->space_bits); ++ dbg_msg(1, "lpt_lnum_bits: %d", c->lpt_lnum_bits); ++ dbg_msg(1, "lpt_offs_bits: %d", c->lpt_offs_bits); ++ dbg_msg(1, "lpt_spc_bits: %d", c->lpt_spc_bits); ++ dbg_msg(1, "pcnt_bits: %d", c->pcnt_bits); ++ dbg_msg(1, "lnum_bits: %d", c->lnum_bits); ++ dbg_msg(1, "pnode_sz: %d", c->pnode_sz); ++ dbg_msg(1, "nnode_sz: %d", c->nnode_sz); ++ dbg_msg(1, "ltab_sz: %d", c->ltab_sz); ++ dbg_msg(1, "lsave_sz: %d", c->lsave_sz); ++ dbg_msg(1, "lsave_cnt: %d", c->lsave_cnt); ++ dbg_msg(1, "lpt_hght: %d", c->lpt_hght); ++ dbg_msg(1, "big_lpt: %d", c->big_lpt); ++ dbg_msg(1, "LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); ++ dbg_msg(1, "LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); ++ dbg_msg(1, "LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); ++ if (c->big_lpt) ++ dbg_msg(1, "LPT lsave is at %d:%d", ++ c->lsave_lnum, c->lsave_offs); ++out: ++ free(lsave); ++ free(buf); ++ free(nnode); ++ free(pnode); ++ return err; ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lpt.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lpt.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lpt.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lpt.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,28 @@ ++/* ++ * Copyright (C) 2008 Nokia Corporation. ++ * Copyright (C) 2008 University of Szeged, Hungary ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy ++ * Adrian Hunter ++ */ ++ ++#ifndef __UBIFS_LPT_H__ ++#define __UBIFS_LPT_H__ ++ ++int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, int *big_lpt); ++int create_lpt(struct ubifs_info *c); ++ ++#endif +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,92 @@ ++/* lzo1.h -- public interface of the LZO1 compression algorithm ++ ++ This file is part of the LZO real-time data compression library. ++ ++ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer ++ All Rights Reserved. ++ ++ The LZO library 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. ++ ++ The LZO library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with the LZO library; see the file COPYING. ++ If not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ ++ Markus F.X.J. Oberhumer ++ ++ http://www.oberhumer.com/opensource/lzo/ ++ */ ++ ++ ++#ifndef __LZO1_H_INCLUDED ++#define __LZO1_H_INCLUDED ++ ++#ifndef __LZOCONF_H_INCLUDED ++#include "lzoconf.h" ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++/* Memory required for the wrkmem parameter. ++ * When the required size is 0, you can also pass a NULL pointer. ++ */ ++ ++#define LZO1_MEM_COMPRESS ((lzo_uint32) (8192L * lzo_sizeof_dict_t)) ++#define LZO1_MEM_DECOMPRESS (0) ++ ++ ++LZO_EXTERN(int) ++lzo1_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++LZO_EXTERN(int) ++lzo1_decompress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++ ++/*********************************************************************** ++// better compression ratio at the cost of more memory and time ++************************************************************************/ ++ ++#define LZO1_99_MEM_COMPRESS ((lzo_uint32) (65536L * lzo_sizeof_dict_t)) ++ ++LZO_EXTERN(int) ++lzo1_99_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* already included */ ++ ++ ++/* vim:set ts=4 et: */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1a.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1a.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1a.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1a.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,92 @@ ++/* lzo1a.h -- public interface of the LZO1A compression algorithm ++ ++ This file is part of the LZO real-time data compression library. ++ ++ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer ++ All Rights Reserved. ++ ++ The LZO library 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. ++ ++ The LZO library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with the LZO library; see the file COPYING. ++ If not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ ++ Markus F.X.J. Oberhumer ++ ++ http://www.oberhumer.com/opensource/lzo/ ++ */ ++ ++ ++#ifndef __LZO1A_H_INCLUDED ++#define __LZO1A_H_INCLUDED ++ ++#ifndef __LZOCONF_H_INCLUDED ++#include "lzoconf.h" ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++/* Memory required for the wrkmem parameter. ++ * When the required size is 0, you can also pass a NULL pointer. ++ */ ++ ++#define LZO1A_MEM_COMPRESS ((lzo_uint32) (8192L * lzo_sizeof_dict_t)) ++#define LZO1A_MEM_DECOMPRESS (0) ++ ++ ++LZO_EXTERN(int) ++lzo1a_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++LZO_EXTERN(int) ++lzo1a_decompress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++ ++/*********************************************************************** ++// better compression ratio at the cost of more memory and time ++************************************************************************/ ++ ++#define LZO1A_99_MEM_COMPRESS ((lzo_uint32) (65536L * lzo_sizeof_dict_t)) ++ ++LZO_EXTERN(int) ++lzo1a_99_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* already included */ ++ ++ ++/* vim:set ts=4 et: */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1b.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1b.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1b.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1b.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,156 @@ ++/* lzo1b.h -- public interface of the LZO1B compression algorithm ++ ++ This file is part of the LZO real-time data compression library. ++ ++ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer ++ All Rights Reserved. ++ ++ The LZO library 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. ++ ++ The LZO library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with the LZO library; see the file COPYING. ++ If not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ ++ Markus F.X.J. Oberhumer ++ ++ http://www.oberhumer.com/opensource/lzo/ ++ */ ++ ++ ++#ifndef __LZO1B_H_INCLUDED ++#define __LZO1B_H_INCLUDED ++ ++#ifndef __LZOCONF_H_INCLUDED ++#include "lzoconf.h" ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++/* Memory required for the wrkmem parameter. ++ * When the required size is 0, you can also pass a NULL pointer. ++ */ ++ ++#define LZO1B_MEM_COMPRESS ((lzo_uint32) (16384L * lzo_sizeof_dict_t)) ++#define LZO1B_MEM_DECOMPRESS (0) ++ ++ ++/* compression levels */ ++#define LZO1B_BEST_SPEED 1 ++#define LZO1B_BEST_COMPRESSION 9 ++#define LZO1B_DEFAULT_COMPRESSION (-1) /* fastest by default */ ++ ++ ++LZO_EXTERN(int) ++lzo1b_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem, ++ int compression_level ); ++ ++/* decompression */ ++LZO_EXTERN(int) ++lzo1b_decompress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++/* safe decompression with overrun testing */ ++LZO_EXTERN(int) ++lzo1b_decompress_safe ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++LZO_EXTERN(int) ++lzo1b_1_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1b_2_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1b_3_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1b_4_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1b_5_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1b_6_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1b_7_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1b_8_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1b_9_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++/*********************************************************************** ++// better compression ratio at the cost of more memory and time ++************************************************************************/ ++ ++#define LZO1B_99_MEM_COMPRESS ((lzo_uint32) (65536L * lzo_sizeof_dict_t)) ++ ++LZO_EXTERN(int) ++lzo1b_99_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++#define LZO1B_999_MEM_COMPRESS ((lzo_uint32) (3 * 65536L * sizeof(lzo_xint))) ++ ++LZO_EXTERN(int) ++lzo1b_999_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* already included */ ++ ++ ++/* vim:set ts=4 et: */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1c.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1c.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1c.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1c.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,156 @@ ++/* lzo1c.h -- public interface of the LZO1C compression algorithm ++ ++ This file is part of the LZO real-time data compression library. ++ ++ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer ++ All Rights Reserved. ++ ++ The LZO library 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. ++ ++ The LZO library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with the LZO library; see the file COPYING. ++ If not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ ++ Markus F.X.J. Oberhumer ++ ++ http://www.oberhumer.com/opensource/lzo/ ++ */ ++ ++ ++#ifndef __LZO1C_H_INCLUDED ++#define __LZO1C_H_INCLUDED ++ ++#ifndef __LZOCONF_H_INCLUDED ++#include "lzoconf.h" ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++/* Memory required for the wrkmem parameter. ++ * When the required size is 0, you can also pass a NULL pointer. ++ */ ++ ++#define LZO1C_MEM_COMPRESS ((lzo_uint32) (16384L * lzo_sizeof_dict_t)) ++#define LZO1C_MEM_DECOMPRESS (0) ++ ++ ++/* compression levels */ ++#define LZO1C_BEST_SPEED 1 ++#define LZO1C_BEST_COMPRESSION 9 ++#define LZO1C_DEFAULT_COMPRESSION (-1) /* fastest by default */ ++ ++ ++LZO_EXTERN(int) ++lzo1c_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem, ++ int compression_level ); ++ ++/* decompression */ ++LZO_EXTERN(int) ++lzo1c_decompress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++/* safe decompression with overrun testing */ ++LZO_EXTERN(int) ++lzo1c_decompress_safe ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++LZO_EXTERN(int) ++lzo1c_1_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1c_2_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1c_3_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1c_4_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1c_5_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1c_6_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1c_7_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1c_8_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++LZO_EXTERN(int) ++lzo1c_9_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++/*********************************************************************** ++// better compression ratio at the cost of more memory and time ++************************************************************************/ ++ ++#define LZO1C_99_MEM_COMPRESS ((lzo_uint32) (65536L * lzo_sizeof_dict_t)) ++ ++LZO_EXTERN(int) ++lzo1c_99_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++#define LZO1C_999_MEM_COMPRESS ((lzo_uint32) (5 * 16384L * sizeof(short))) ++ ++LZO_EXTERN(int) ++lzo1c_999_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* already included */ ++ ++ ++/* vim:set ts=4 et: */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1f.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1f.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1f.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1f.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,104 @@ ++/* lzo1f.h -- public interface of the LZO1F compression algorithm ++ ++ This file is part of the LZO real-time data compression library. ++ ++ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer ++ All Rights Reserved. ++ ++ The LZO library 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. ++ ++ The LZO library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with the LZO library; see the file COPYING. ++ If not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ ++ Markus F.X.J. Oberhumer ++ ++ http://www.oberhumer.com/opensource/lzo/ ++ */ ++ ++ ++#ifndef __LZO1F_H_INCLUDED ++#define __LZO1F_H_INCLUDED ++ ++#ifndef __LZOCONF_H_INCLUDED ++#include "lzoconf.h" ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++/* Memory required for the wrkmem parameter. ++ * When the required size is 0, you can also pass a NULL pointer. ++ */ ++ ++#define LZO1F_MEM_COMPRESS ((lzo_uint32) (16384L * lzo_sizeof_dict_t)) ++#define LZO1F_MEM_DECOMPRESS (0) ++ ++ ++/* decompression */ ++LZO_EXTERN(int) ++lzo1f_decompress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++/* safe decompression with overrun testing */ ++LZO_EXTERN(int) ++lzo1f_decompress_safe ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++LZO_EXTERN(int) ++lzo1f_1_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++/*********************************************************************** ++// better compression ratio at the cost of more memory and time ++************************************************************************/ ++ ++#define LZO1F_999_MEM_COMPRESS ((lzo_uint32) (5 * 16384L * sizeof(short))) ++ ++LZO_EXTERN(int) ++lzo1f_999_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* already included */ ++ ++ ++/* vim:set ts=4 et: */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1x.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1x.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1x.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1x.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,173 @@ ++/* lzo1x.h -- public interface of the LZO1X compression algorithm ++ ++ This file is part of the LZO real-time data compression library. ++ ++ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer ++ All Rights Reserved. ++ ++ The LZO library 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. ++ ++ The LZO library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with the LZO library; see the file COPYING. ++ If not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ ++ Markus F.X.J. Oberhumer ++ ++ http://www.oberhumer.com/opensource/lzo/ ++ */ ++ ++ ++#ifndef __LZO1X_H_INCLUDED ++#define __LZO1X_H_INCLUDED ++ ++#ifndef __LZOCONF_H_INCLUDED ++#include "lzoconf.h" ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++/* Memory required for the wrkmem parameter. ++ * When the required size is 0, you can also pass a NULL pointer. ++ */ ++ ++#define LZO1X_MEM_COMPRESS LZO1X_1_MEM_COMPRESS ++#define LZO1X_MEM_DECOMPRESS (0) ++#define LZO1X_MEM_OPTIMIZE (0) ++ ++ ++/* decompression */ ++LZO_EXTERN(int) ++lzo1x_decompress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++/* safe decompression with overrun testing */ ++LZO_EXTERN(int) ++lzo1x_decompress_safe ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++#define LZO1X_1_MEM_COMPRESS ((lzo_uint32) (16384L * lzo_sizeof_dict_t)) ++ ++LZO_EXTERN(int) ++lzo1x_1_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++/*********************************************************************** ++// special compressor versions ++************************************************************************/ ++ ++/* this version needs only 8 kB work memory */ ++#define LZO1X_1_11_MEM_COMPRESS ((lzo_uint32) (2048L * lzo_sizeof_dict_t)) ++ ++LZO_EXTERN(int) ++lzo1x_1_11_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++/* this version needs 16 kB work memory */ ++#define LZO1X_1_12_MEM_COMPRESS ((lzo_uint32) (4096L * lzo_sizeof_dict_t)) ++ ++LZO_EXTERN(int) ++lzo1x_1_12_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++/* use this version if you need a little more compression speed */ ++#define LZO1X_1_15_MEM_COMPRESS ((lzo_uint32) (32768L * lzo_sizeof_dict_t)) ++ ++LZO_EXTERN(int) ++lzo1x_1_15_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++/*********************************************************************** ++// better compression ratio at the cost of more memory and time ++************************************************************************/ ++ ++#define LZO1X_999_MEM_COMPRESS ((lzo_uint32) (14 * 16384L * sizeof(short))) ++ ++LZO_EXTERN(int) ++lzo1x_999_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++LZO_EXTERN(int) ++lzo1x_999_compress_dict ( const lzo_bytep in , lzo_uint in_len, ++ lzo_bytep out, lzo_uintp out_len, ++ lzo_voidp wrkmem, ++ const lzo_bytep dict, lzo_uint dict_len ); ++ ++LZO_EXTERN(int) ++lzo1x_999_compress_level ( const lzo_bytep in , lzo_uint in_len, ++ lzo_bytep out, lzo_uintp out_len, ++ lzo_voidp wrkmem, ++ const lzo_bytep dict, lzo_uint dict_len, ++ lzo_callback_p cb, ++ int compression_level ); ++ ++LZO_EXTERN(int) ++lzo1x_decompress_dict_safe ( const lzo_bytep in, lzo_uint in_len, ++ lzo_bytep out, lzo_uintp out_len, ++ lzo_voidp wrkmem /* NOT USED */, ++ const lzo_bytep dict, lzo_uint dict_len ); ++ ++ ++/*********************************************************************** ++// optimize a compressed data block ++************************************************************************/ ++ ++LZO_EXTERN(int) ++lzo1x_optimize ( lzo_bytep in , lzo_uint in_len, ++ lzo_bytep out, lzo_uintp out_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++ ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* already included */ ++ ++ ++/* vim:set ts=4 et: */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1y.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1y.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1y.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1y.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,141 @@ ++/* lzo1y.h -- public interface of the LZO1Y compression algorithm ++ ++ This file is part of the LZO real-time data compression library. ++ ++ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer ++ All Rights Reserved. ++ ++ The LZO library 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. ++ ++ The LZO library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with the LZO library; see the file COPYING. ++ If not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ ++ Markus F.X.J. Oberhumer ++ ++ http://www.oberhumer.com/opensource/lzo/ ++ */ ++ ++ ++#ifndef __LZO1Y_H_INCLUDED ++#define __LZO1Y_H_INCLUDED ++ ++#ifndef __LZOCONF_H_INCLUDED ++#include "lzoconf.h" ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++/* Memory required for the wrkmem parameter. ++ * When the required size is 0, you can also pass a NULL pointer. ++ */ ++ ++#define LZO1Y_MEM_COMPRESS ((lzo_uint32) (16384L * lzo_sizeof_dict_t)) ++#define LZO1Y_MEM_DECOMPRESS (0) ++#define LZO1Y_MEM_OPTIMIZE (0) ++ ++ ++/* decompression */ ++LZO_EXTERN(int) ++lzo1y_decompress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++/* safe decompression with overrun testing */ ++LZO_EXTERN(int) ++lzo1y_decompress_safe ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++LZO_EXTERN(int) ++lzo1y_1_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++/*********************************************************************** ++// better compression ratio at the cost of more memory and time ++************************************************************************/ ++ ++#define LZO1Y_999_MEM_COMPRESS ((lzo_uint32) (14 * 16384L * sizeof(short))) ++ ++LZO_EXTERN(int) ++lzo1y_999_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++LZO_EXTERN(int) ++lzo1y_999_compress_dict ( const lzo_bytep in , lzo_uint in_len, ++ lzo_bytep out, lzo_uintp out_len, ++ lzo_voidp wrkmem, ++ const lzo_bytep dict, lzo_uint dict_len ); ++ ++LZO_EXTERN(int) ++lzo1y_999_compress_level ( const lzo_bytep in , lzo_uint in_len, ++ lzo_bytep out, lzo_uintp out_len, ++ lzo_voidp wrkmem, ++ const lzo_bytep dict, lzo_uint dict_len, ++ lzo_callback_p cb, ++ int compression_level ); ++ ++LZO_EXTERN(int) ++lzo1y_decompress_dict_safe ( const lzo_bytep in, lzo_uint in_len, ++ lzo_bytep out, lzo_uintp out_len, ++ lzo_voidp wrkmem /* NOT USED */, ++ const lzo_bytep dict, lzo_uint dict_len ); ++ ++ ++/*********************************************************************** ++// optimize a compressed data block ++************************************************************************/ ++ ++LZO_EXTERN(int) ++lzo1y_optimize ( lzo_bytep in , lzo_uint in_len, ++ lzo_bytep out, lzo_uintp out_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++ ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* already included */ ++ ++ ++/* vim:set ts=4 et: */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1z.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1z.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1z.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1z.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,146 @@ ++/* lzo1z.h -- public interface of the LZO1Z compression algorithm ++ ++ This file is part of the LZO real-time data compression library. ++ ++ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer ++ All Rights Reserved. ++ ++ The LZO library 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. ++ ++ The LZO library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with the LZO library; see the file COPYING. ++ If not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ ++ Markus F.X.J. Oberhumer ++ ++ http://www.oberhumer.com/opensource/lzo/ ++ */ ++ ++ ++#ifndef __LZO1Z_H_INCLUDED ++#define __LZO1Z_H_INCLUDED ++ ++#ifndef __LZOCONF_H_INCLUDED ++#include "lzoconf.h" ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++/* Memory required for the wrkmem parameter. ++ * When the required size is 0, you can also pass a NULL pointer. ++ */ ++ ++#define LZO1Z_MEM_COMPRESS ((lzo_uint32) (16384L * lzo_sizeof_dict_t)) ++#define LZO1Z_MEM_DECOMPRESS (0) ++#define LZO1Z_MEM_OPTIMIZE (0) ++ ++ ++/* decompression */ ++LZO_EXTERN(int) ++lzo1z_decompress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++/* safe decompression with overrun testing */ ++LZO_EXTERN(int) ++lzo1z_decompress_safe ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++#if 0 ++/* not yet implemented */ ++LZO_EXTERN(int) ++lzo1z_1_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++#endif ++ ++ ++/*********************************************************************** ++// better compression ratio at the cost of more memory and time ++************************************************************************/ ++ ++#define LZO1Z_999_MEM_COMPRESS ((lzo_uint32) (14 * 16384L * sizeof(short))) ++ ++LZO_EXTERN(int) ++lzo1z_999_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++LZO_EXTERN(int) ++lzo1z_999_compress_dict ( const lzo_bytep in , lzo_uint in_len, ++ lzo_bytep out, lzo_uintp out_len, ++ lzo_voidp wrkmem, ++ const lzo_bytep dict, lzo_uint dict_len ); ++ ++LZO_EXTERN(int) ++lzo1z_999_compress_level ( const lzo_bytep in , lzo_uint in_len, ++ lzo_bytep out, lzo_uintp out_len, ++ lzo_voidp wrkmem, ++ const lzo_bytep dict, lzo_uint dict_len, ++ lzo_callback_p cb, ++ int compression_level ); ++ ++LZO_EXTERN(int) ++lzo1z_decompress_dict_safe ( const lzo_bytep in, lzo_uint in_len, ++ lzo_bytep out, lzo_uintp out_len, ++ lzo_voidp wrkmem /* NOT USED */, ++ const lzo_bytep dict, lzo_uint dict_len ); ++ ++ ++/*********************************************************************** ++// optimize a compressed data block ++************************************************************************/ ++ ++#if 0 ++/* not yet implemented */ ++LZO_EXTERN(int) ++lzo1z_optimize ( lzo_bytep in , lzo_uint in_len, ++ lzo_bytep out, lzo_uintp out_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++#endif ++ ++ ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* already included */ ++ ++ ++/* vim:set ts=4 et: */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo2a.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo2a.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo2a.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo2a.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,88 @@ ++/* lzo2a.h -- public interface of the LZO2A compression algorithm ++ ++ This file is part of the LZO real-time data compression library. ++ ++ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer ++ All Rights Reserved. ++ ++ The LZO library 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. ++ ++ The LZO library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with the LZO library; see the file COPYING. ++ If not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ ++ Markus F.X.J. Oberhumer ++ ++ http://www.oberhumer.com/opensource/lzo/ ++ */ ++ ++ ++#ifndef __LZO2A_H_INCLUDED ++#define __LZO2A_H_INCLUDED ++ ++#ifndef __LZOCONF_H_INCLUDED ++#include "lzoconf.h" ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/*********************************************************************** ++// ++************************************************************************/ ++ ++#define LZO2A_MEM_DECOMPRESS (0) ++ ++/* decompression */ ++LZO_EXTERN(int) ++lzo2a_decompress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++/* safe decompression with overrun testing */ ++LZO_EXTERN(int) ++lzo2a_decompress_safe ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem /* NOT USED */ ); ++ ++ ++/*********************************************************************** ++// better compression ratio at the cost of more memory and time ++************************************************************************/ ++ ++#define LZO2A_999_MEM_COMPRESS ((lzo_uint32) (8 * 16384L * sizeof(short))) ++ ++LZO_EXTERN(int) ++lzo2a_999_compress ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++ ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* already included */ ++ ++ ++/* vim:set ts=4 et: */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo_asm.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo_asm.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo_asm.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo_asm.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,135 @@ ++/* lzo_asm.h -- assembler prototypes for the LZO data compression library ++ ++ This file is part of the LZO real-time data compression library. ++ ++ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer ++ All Rights Reserved. ++ ++ The LZO library 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. ++ ++ The LZO library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with the LZO library; see the file COPYING. ++ If not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ ++ Markus F.X.J. Oberhumer ++ ++ http://www.oberhumer.com/opensource/lzo/ ++ */ ++ ++ ++#ifndef __LZO_ASM_H_INCLUDED ++#define __LZO_ASM_H_INCLUDED ++ ++#ifndef __LZOCONF_H_INCLUDED ++#include "lzoconf.h" ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/*********************************************************************** ++// assembly decompressors ++************************************************************************/ ++ ++LZO_EXTERN(int) lzo1c_decompress_asm ++ (const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem); ++LZO_EXTERN(int) lzo1c_decompress_asm_safe ++ (const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem); ++ ++LZO_EXTERN(int) lzo1f_decompress_asm_fast ++ (const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem); ++LZO_EXTERN(int) lzo1f_decompress_asm_fast_safe ++ (const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem); ++ ++LZO_EXTERN(int) lzo1x_decompress_asm ++ (const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem); ++LZO_EXTERN(int) lzo1x_decompress_asm_safe ++ (const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem); ++LZO_EXTERN(int) lzo1x_decompress_asm_fast ++ (const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem); ++LZO_EXTERN(int) lzo1x_decompress_asm_fast_safe ++ (const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem); ++ ++LZO_EXTERN(int) lzo1y_decompress_asm ++ (const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem); ++LZO_EXTERN(int) lzo1y_decompress_asm_safe ++ (const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem); ++LZO_EXTERN(int) lzo1y_decompress_asm_fast ++ (const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem); ++LZO_EXTERN(int) lzo1y_decompress_asm_fast_safe ++ (const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem); ++ ++ ++/*********************************************************************** ++// checksum and misc functions ++************************************************************************/ ++ ++#if 0 ++ ++LZO_EXTERN(lzo_uint32) ++lzo_crc32_asm(lzo_uint32 _c, const lzo_bytep _buf, lzo_uint _len, ++ const lzo_uint32p _crc_table); ++ ++LZO_EXTERN(lzo_uint32) ++lzo_crc32_asm_small(lzo_uint32 _c, const lzo_bytep _buf, lzo_uint _len); ++ ++LZO_EXTERN(int) ++lzo_cpuid_asm(lzo_uint32p /* lzo_uint32 info[16] */ ); ++ ++LZO_EXTERN(lzo_uint32) ++lzo_rdtsc_asm(lzo_uint32p /* lzo_uint32 ticks[2] */ ); ++ ++#endif ++ ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* already included */ ++ ++ ++/* vim:set ts=4 et: */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoconf.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoconf.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoconf.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoconf.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,413 @@ ++/* lzoconf.h -- configuration for the LZO real-time data compression library ++ ++ This file is part of the LZO real-time data compression library. ++ ++ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer ++ All Rights Reserved. ++ ++ The LZO library 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. ++ ++ The LZO library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with the LZO library; see the file COPYING. ++ If not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ ++ Markus F.X.J. Oberhumer ++ ++ http://www.oberhumer.com/opensource/lzo/ ++ */ ++ ++ ++#ifndef __LZOCONF_H_INCLUDED ++#define __LZOCONF_H_INCLUDED ++ ++#define LZO_VERSION 0x2020 ++#define LZO_VERSION_STRING "2.02" ++#define LZO_VERSION_DATE "Oct 17 2005" ++ ++/* internal Autoconf configuration file - only used when building LZO */ ++#if defined(LZO_HAVE_CONFIG_H) ++# include ++#endif ++#include ++#include ++ ++ ++/*********************************************************************** ++// LZO requires a conforming ++************************************************************************/ ++ ++#if !defined(CHAR_BIT) || (CHAR_BIT != 8) ++# error "invalid CHAR_BIT" ++#endif ++#if !defined(UCHAR_MAX) || !defined(UINT_MAX) || !defined(ULONG_MAX) ++# error "check your compiler installation" ++#endif ++#if (USHRT_MAX < 1) || (UINT_MAX < 1) || (ULONG_MAX < 1) ++# error "your limits.h macros are broken" ++#endif ++ ++/* get OS and architecture defines */ ++#ifndef __LZODEFS_H_INCLUDED ++#include "lzodefs.h" ++#endif ++ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/*********************************************************************** ++// some core defines ++************************************************************************/ ++ ++#if !defined(LZO_UINT32_C) ++# if (UINT_MAX < LZO_0xffffffffL) ++# define LZO_UINT32_C(c) c ## UL ++# else ++# define LZO_UINT32_C(c) ((c) + 0U) ++# endif ++#endif ++ ++/* memory checkers */ ++#if !defined(__LZO_CHECKER) ++# if defined(__BOUNDS_CHECKING_ON) ++# define __LZO_CHECKER 1 ++# elif defined(__CHECKER__) ++# define __LZO_CHECKER 1 ++# elif defined(__INSURE__) ++# define __LZO_CHECKER 1 ++# elif defined(__PURIFY__) ++# define __LZO_CHECKER 1 ++# endif ++#endif ++ ++ ++/*********************************************************************** ++// integral and pointer types ++************************************************************************/ ++ ++/* lzo_uint should match size_t */ ++#if !defined(LZO_UINT_MAX) ++# if defined(LZO_ABI_LLP64) /* WIN64 */ ++# if defined(LZO_OS_WIN64) ++ typedef unsigned __int64 lzo_uint; ++ typedef __int64 lzo_int; ++# else ++ typedef unsigned long long lzo_uint; ++ typedef long long lzo_int; ++# endif ++# define LZO_UINT_MAX 0xffffffffffffffffull ++# define LZO_INT_MAX 9223372036854775807LL ++# define LZO_INT_MIN (-1LL - LZO_INT_MAX) ++# elif defined(LZO_ABI_IP32L64) /* MIPS R5900 */ ++ typedef unsigned int lzo_uint; ++ typedef int lzo_int; ++# define LZO_UINT_MAX UINT_MAX ++# define LZO_INT_MAX INT_MAX ++# define LZO_INT_MIN INT_MIN ++# elif (ULONG_MAX >= LZO_0xffffffffL) ++ typedef unsigned long lzo_uint; ++ typedef long lzo_int; ++# define LZO_UINT_MAX ULONG_MAX ++# define LZO_INT_MAX LONG_MAX ++# define LZO_INT_MIN LONG_MIN ++# else ++# error "lzo_uint" ++# endif ++#endif ++ ++/* Integral types with 32 bits or more. */ ++#if !defined(LZO_UINT32_MAX) ++# if (UINT_MAX >= LZO_0xffffffffL) ++ typedef unsigned int lzo_uint32; ++ typedef int lzo_int32; ++# define LZO_UINT32_MAX UINT_MAX ++# define LZO_INT32_MAX INT_MAX ++# define LZO_INT32_MIN INT_MIN ++# elif (ULONG_MAX >= LZO_0xffffffffL) ++ typedef unsigned long lzo_uint32; ++ typedef long lzo_int32; ++# define LZO_UINT32_MAX ULONG_MAX ++# define LZO_INT32_MAX LONG_MAX ++# define LZO_INT32_MIN LONG_MIN ++# else ++# error "lzo_uint32" ++# endif ++#endif ++ ++/* The larger type of lzo_uint and lzo_uint32. */ ++#if (LZO_UINT_MAX >= LZO_UINT32_MAX) ++# define lzo_xint lzo_uint ++#else ++# define lzo_xint lzo_uint32 ++#endif ++ ++/* Memory model that allows to access memory at offsets of lzo_uint. */ ++#if !defined(__LZO_MMODEL) ++# if (LZO_UINT_MAX <= UINT_MAX) ++# define __LZO_MMODEL ++# elif defined(LZO_HAVE_MM_HUGE_PTR) ++# define __LZO_MMODEL_HUGE 1 ++# define __LZO_MMODEL __huge ++# else ++# define __LZO_MMODEL ++# endif ++#endif ++ ++/* no typedef here because of const-pointer issues */ ++#define lzo_bytep unsigned char __LZO_MMODEL * ++#define lzo_charp char __LZO_MMODEL * ++#define lzo_voidp void __LZO_MMODEL * ++#define lzo_shortp short __LZO_MMODEL * ++#define lzo_ushortp unsigned short __LZO_MMODEL * ++#define lzo_uint32p lzo_uint32 __LZO_MMODEL * ++#define lzo_int32p lzo_int32 __LZO_MMODEL * ++#define lzo_uintp lzo_uint __LZO_MMODEL * ++#define lzo_intp lzo_int __LZO_MMODEL * ++#define lzo_xintp lzo_xint __LZO_MMODEL * ++#define lzo_voidpp lzo_voidp __LZO_MMODEL * ++#define lzo_bytepp lzo_bytep __LZO_MMODEL * ++/* deprecated - use `lzo_bytep' instead of `lzo_byte *' */ ++#define lzo_byte unsigned char __LZO_MMODEL ++ ++typedef int lzo_bool; ++ ++ ++/*********************************************************************** ++// function types ++************************************************************************/ ++ ++/* name mangling */ ++#if !defined(__LZO_EXTERN_C) ++# ifdef __cplusplus ++# define __LZO_EXTERN_C extern "C" ++# else ++# define __LZO_EXTERN_C extern ++# endif ++#endif ++ ++/* calling convention */ ++#if !defined(__LZO_CDECL) ++# define __LZO_CDECL __lzo_cdecl ++#endif ++ ++/* DLL export information */ ++#if !defined(__LZO_EXPORT1) ++# define __LZO_EXPORT1 ++#endif ++#if !defined(__LZO_EXPORT2) ++# define __LZO_EXPORT2 ++#endif ++ ++/* __cdecl calling convention for public C and assembly functions */ ++#if !defined(LZO_PUBLIC) ++# define LZO_PUBLIC(_rettype) __LZO_EXPORT1 _rettype __LZO_EXPORT2 __LZO_CDECL ++#endif ++#if !defined(LZO_EXTERN) ++# define LZO_EXTERN(_rettype) __LZO_EXTERN_C LZO_PUBLIC(_rettype) ++#endif ++#if !defined(LZO_PRIVATE) ++# define LZO_PRIVATE(_rettype) static _rettype __LZO_CDECL ++#endif ++ ++/* function types */ ++typedef int ++(__LZO_CDECL *lzo_compress_t) ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++typedef int ++(__LZO_CDECL *lzo_decompress_t) ( const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++typedef int ++(__LZO_CDECL *lzo_optimize_t) ( lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem ); ++ ++typedef int ++(__LZO_CDECL *lzo_compress_dict_t)(const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem, ++ const lzo_bytep dict, lzo_uint dict_len ); ++ ++typedef int ++(__LZO_CDECL *lzo_decompress_dict_t)(const lzo_bytep src, lzo_uint src_len, ++ lzo_bytep dst, lzo_uintp dst_len, ++ lzo_voidp wrkmem, ++ const lzo_bytep dict, lzo_uint dict_len ); ++ ++ ++/* Callback interface. Currently only the progress indicator ("nprogress") ++ * is used, but this may change in a future release. */ ++ ++struct lzo_callback_t; ++typedef struct lzo_callback_t lzo_callback_t; ++#define lzo_callback_p lzo_callback_t __LZO_MMODEL * ++ ++/* malloc & free function types */ ++typedef lzo_voidp (__LZO_CDECL *lzo_alloc_func_t) ++ (lzo_callback_p self, lzo_uint items, lzo_uint size); ++typedef void (__LZO_CDECL *lzo_free_func_t) ++ (lzo_callback_p self, lzo_voidp ptr); ++ ++/* a progress indicator callback function */ ++typedef void (__LZO_CDECL *lzo_progress_func_t) ++ (lzo_callback_p, lzo_uint, lzo_uint, int); ++ ++struct lzo_callback_t ++{ ++ /* custom allocators (set to 0 to disable) */ ++ lzo_alloc_func_t nalloc; /* [not used right now] */ ++ lzo_free_func_t nfree; /* [not used right now] */ ++ ++ /* a progress indicator callback function (set to 0 to disable) */ ++ lzo_progress_func_t nprogress; ++ ++ /* NOTE: the first parameter "self" of the nalloc/nfree/nprogress ++ * callbacks points back to this struct, so you are free to store ++ * some extra info in the following variables. */ ++ lzo_voidp user1; ++ lzo_xint user2; ++ lzo_xint user3; ++}; ++ ++ ++/*********************************************************************** ++// error codes and prototypes ++************************************************************************/ ++ ++/* Error codes for the compression/decompression functions. Negative ++ * values are errors, positive values will be used for special but ++ * normal events. ++ */ ++#define LZO_E_OK 0 ++#define LZO_E_ERROR (-1) ++#define LZO_E_OUT_OF_MEMORY (-2) /* [not used right now] */ ++#define LZO_E_NOT_COMPRESSIBLE (-3) /* [not used right now] */ ++#define LZO_E_INPUT_OVERRUN (-4) ++#define LZO_E_OUTPUT_OVERRUN (-5) ++#define LZO_E_LOOKBEHIND_OVERRUN (-6) ++#define LZO_E_EOF_NOT_FOUND (-7) ++#define LZO_E_INPUT_NOT_CONSUMED (-8) ++#define LZO_E_NOT_YET_IMPLEMENTED (-9) /* [not used right now] */ ++ ++ ++#ifndef lzo_sizeof_dict_t ++# define lzo_sizeof_dict_t ((unsigned)sizeof(lzo_bytep)) ++#endif ++ ++/* lzo_init() should be the first function you call. ++ * Check the return code ! ++ * ++ * lzo_init() is a macro to allow checking that the library and the ++ * compiler's view of various types are consistent. ++ */ ++#define lzo_init() __lzo_init_v2(LZO_VERSION,(int)sizeof(short),(int)sizeof(int),\ ++ (int)sizeof(long),(int)sizeof(lzo_uint32),(int)sizeof(lzo_uint),\ ++ (int)lzo_sizeof_dict_t,(int)sizeof(char *),(int)sizeof(lzo_voidp),\ ++ (int)sizeof(lzo_callback_t)) ++LZO_EXTERN(int) __lzo_init_v2(unsigned,int,int,int,int,int,int,int,int,int); ++ ++/* version functions (useful for shared libraries) */ ++LZO_EXTERN(unsigned) lzo_version(void); ++LZO_EXTERN(const char *) lzo_version_string(void); ++LZO_EXTERN(const char *) lzo_version_date(void); ++LZO_EXTERN(const lzo_charp) _lzo_version_string(void); ++LZO_EXTERN(const lzo_charp) _lzo_version_date(void); ++ ++/* string functions */ ++LZO_EXTERN(int) ++lzo_memcmp(const lzo_voidp _s1, const lzo_voidp _s2, lzo_uint _len); ++LZO_EXTERN(lzo_voidp) ++lzo_memcpy(lzo_voidp _dest, const lzo_voidp _src, lzo_uint _len); ++LZO_EXTERN(lzo_voidp) ++lzo_memmove(lzo_voidp _dest, const lzo_voidp _src, lzo_uint _len); ++LZO_EXTERN(lzo_voidp) ++lzo_memset(lzo_voidp _s, int _c, lzo_uint _len); ++ ++/* checksum functions */ ++LZO_EXTERN(lzo_uint32) ++lzo_adler32(lzo_uint32 _adler, const lzo_bytep _buf, lzo_uint _len); ++LZO_EXTERN(lzo_uint32) ++lzo_crc32(lzo_uint32 _c, const lzo_bytep _buf, lzo_uint _len); ++LZO_EXTERN(const lzo_uint32p) ++lzo_get_crc32_table(void); ++ ++/* misc. */ ++LZO_EXTERN(int) _lzo_config_check(void); ++typedef union { lzo_bytep p; lzo_uint u; } __lzo_pu_u; ++typedef union { lzo_bytep p; lzo_uint32 u32; } __lzo_pu32_u; ++typedef union { void *vp; lzo_bytep bp; lzo_uint32 u32; long l; } lzo_align_t; ++ ++/* align a char pointer on a boundary that is a multiple of `size' */ ++LZO_EXTERN(unsigned) __lzo_align_gap(const lzo_voidp _ptr, lzo_uint _size); ++#define LZO_PTR_ALIGN_UP(_ptr,_size) \ ++ ((_ptr) + (lzo_uint) __lzo_align_gap((const lzo_voidp)(_ptr),(lzo_uint)(_size))) ++ ++ ++/*********************************************************************** ++// deprecated macros - only for backward compatibility with LZO v1.xx ++************************************************************************/ ++ ++#if defined(LZO_CFG_COMPAT) ++ ++#define __LZOCONF_H 1 ++ ++#if defined(LZO_ARCH_I086) ++# define __LZO_i386 1 ++#elif defined(LZO_ARCH_I386) ++# define __LZO_i386 1 ++#endif ++ ++#if defined(LZO_OS_DOS16) ++# define __LZO_DOS 1 ++# define __LZO_DOS16 1 ++#elif defined(LZO_OS_DOS32) ++# define __LZO_DOS 1 ++#elif defined(LZO_OS_WIN16) ++# define __LZO_WIN 1 ++# define __LZO_WIN16 1 ++#elif defined(LZO_OS_WIN32) ++# define __LZO_WIN 1 ++#endif ++ ++#define __LZO_CMODEL ++#define __LZO_DMODEL ++#define __LZO_ENTRY __LZO_CDECL ++#define LZO_EXTERN_CDECL LZO_EXTERN ++#define LZO_ALIGN LZO_PTR_ALIGN_UP ++ ++#define lzo_compress_asm_t lzo_compress_t ++#define lzo_decompress_asm_t lzo_decompress_t ++ ++#endif /* LZO_CFG_COMPAT */ ++ ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* already included */ ++ ++ ++/* vim:set ts=4 et: */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzodefs.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzodefs.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzodefs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzodefs.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1545 @@ ++/* lzodefs.h -- architecture, OS and compiler specific defines ++ ++ This file is part of the LZO real-time data compression library. ++ ++ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer ++ All Rights Reserved. ++ ++ The LZO library 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. ++ ++ The LZO library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with the LZO library; see the file COPYING. ++ If not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ ++ Markus F.X.J. Oberhumer ++ ++ http://www.oberhumer.com/opensource/lzo/ ++ */ ++ ++ ++#ifndef __LZODEFS_H_INCLUDED ++#define __LZODEFS_H_INCLUDED 1 ++ ++#if defined(__CYGWIN32__) && !defined(__CYGWIN__) ++# define __CYGWIN__ __CYGWIN32__ ++#endif ++#if defined(__IBMCPP__) && !defined(__IBMC__) ++# define __IBMC__ __IBMCPP__ ++#endif ++#if defined(__ICL) && defined(_WIN32) && !defined(__INTEL_COMPILER) ++# define __INTEL_COMPILER __ICL ++#endif ++#if 1 && defined(__INTERIX) && defined(__GNUC__) && !defined(_ALL_SOURCE) ++# define _ALL_SOURCE 1 ++#endif ++#if defined(__mips__) && defined(__R5900__) ++# if !defined(__LONG_MAX__) ++# define __LONG_MAX__ 9223372036854775807L ++# endif ++#endif ++#if defined(__INTEL_COMPILER) && defined(__linux__) ++# pragma warning(disable: 193) ++#endif ++#if defined(__KEIL__) && defined(__C166__) ++# pragma warning disable = 322 ++#elif 0 && defined(__C251__) ++# pragma warning disable = 322 ++#endif ++#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__MWERKS__) ++# if (_MSC_VER >= 1300) ++# pragma warning(disable: 4668) ++# endif ++#endif ++#if 0 && defined(__WATCOMC__) ++# if (__WATCOMC__ >= 1050) && (__WATCOMC__ < 1060) ++# pragma warning 203 9 ++# endif ++#endif ++#if defined(__BORLANDC__) && defined(__MSDOS__) && !defined(__FLAT__) ++# pragma option -h ++#endif ++#if 0 ++#define LZO_0xffffL 0xfffful ++#define LZO_0xffffffffL 0xfffffffful ++#else ++#define LZO_0xffffL 65535ul ++#define LZO_0xffffffffL 4294967295ul ++#endif ++#if (LZO_0xffffL == LZO_0xffffffffL) ++# error "your preprocessor is broken 1" ++#endif ++#if (16ul * 16384ul != 262144ul) ++# error "your preprocessor is broken 2" ++#endif ++#if 0 ++#if (32767 >= 4294967295ul) ++# error "your preprocessor is broken 3" ++#endif ++#if (65535u >= 4294967295ul) ++# error "your preprocessor is broken 4" ++#endif ++#endif ++#if (UINT_MAX == LZO_0xffffL) ++#if defined(__ZTC__) && defined(__I86__) && !defined(__OS2__) ++# if !defined(MSDOS) ++# define MSDOS 1 ++# endif ++# if !defined(_MSDOS) ++# define _MSDOS 1 ++# endif ++#elif 0 && defined(__VERSION) && defined(MB_LEN_MAX) ++# if (__VERSION == 520) && (MB_LEN_MAX == 1) ++# if !defined(__AZTEC_C__) ++# define __AZTEC_C__ __VERSION ++# endif ++# if !defined(__DOS__) ++# define __DOS__ 1 ++# endif ++# endif ++#endif ++#endif ++#if defined(_MSC_VER) && defined(M_I86HM) && (UINT_MAX == LZO_0xffffL) ++# define ptrdiff_t long ++# define _PTRDIFF_T_DEFINED ++#endif ++#if (UINT_MAX == LZO_0xffffL) ++# undef __LZO_RENAME_A ++# undef __LZO_RENAME_B ++# if defined(__AZTEC_C__) && defined(__DOS__) ++# define __LZO_RENAME_A 1 ++# elif defined(_MSC_VER) && defined(MSDOS) ++# if (_MSC_VER < 600) ++# define __LZO_RENAME_A 1 ++# elif (_MSC_VER < 700) ++# define __LZO_RENAME_B 1 ++# endif ++# elif defined(__TSC__) && defined(__OS2__) ++# define __LZO_RENAME_A 1 ++# elif defined(__MSDOS__) && defined(__TURBOC__) && (__TURBOC__ < 0x0410) ++# define __LZO_RENAME_A 1 ++# elif defined(__PACIFIC__) && defined(DOS) ++# if !defined(__far) ++# define __far far ++# endif ++# if !defined(__near) ++# define __near near ++# endif ++# endif ++# if defined(__LZO_RENAME_A) ++# if !defined(__cdecl) ++# define __cdecl cdecl ++# endif ++# if !defined(__far) ++# define __far far ++# endif ++# if !defined(__huge) ++# define __huge huge ++# endif ++# if !defined(__near) ++# define __near near ++# endif ++# if !defined(__pascal) ++# define __pascal pascal ++# endif ++# if !defined(__huge) ++# define __huge huge ++# endif ++# elif defined(__LZO_RENAME_B) ++# if !defined(__cdecl) ++# define __cdecl _cdecl ++# endif ++# if !defined(__far) ++# define __far _far ++# endif ++# if !defined(__huge) ++# define __huge _huge ++# endif ++# if !defined(__near) ++# define __near _near ++# endif ++# if !defined(__pascal) ++# define __pascal _pascal ++# endif ++# elif (defined(__PUREC__) || defined(__TURBOC__)) && defined(__TOS__) ++# if !defined(__cdecl) ++# define __cdecl cdecl ++# endif ++# if !defined(__pascal) ++# define __pascal pascal ++# endif ++# endif ++# undef __LZO_RENAME_A ++# undef __LZO_RENAME_B ++#endif ++#if (UINT_MAX == LZO_0xffffL) ++#if defined(__AZTEC_C__) && defined(__DOS__) ++# define LZO_BROKEN_CDECL_ALT_SYNTAX 1 ++#elif defined(_MSC_VER) && defined(MSDOS) ++# if (_MSC_VER < 600) ++# define LZO_BROKEN_INTEGRAL_CONSTANTS 1 ++# endif ++# if (_MSC_VER < 700) ++# define LZO_BROKEN_INTEGRAL_PROMOTION 1 ++# define LZO_BROKEN_SIZEOF 1 ++# endif ++#elif defined(__PACIFIC__) && defined(DOS) ++# define LZO_BROKEN_INTEGRAL_CONSTANTS 1 ++#elif defined(__TURBOC__) && defined(__MSDOS__) ++# if (__TURBOC__ < 0x0150) ++# define LZO_BROKEN_CDECL_ALT_SYNTAX 1 ++# define LZO_BROKEN_INTEGRAL_CONSTANTS 1 ++# define LZO_BROKEN_INTEGRAL_PROMOTION 1 ++# endif ++# if (__TURBOC__ < 0x0200) ++# define LZO_BROKEN_SIZEOF 1 ++# endif ++# if (__TURBOC__ < 0x0400) && defined(__cplusplus) ++# define LZO_BROKEN_CDECL_ALT_SYNTAX 1 ++# endif ++#elif (defined(__PUREC__) || defined(__TURBOC__)) && defined(__TOS__) ++# define LZO_BROKEN_CDECL_ALT_SYNTAX 1 ++# define LZO_BROKEN_SIZEOF 1 ++#endif ++#endif ++#if defined(__WATCOMC__) && (__WATCOMC__ < 900) ++# define LZO_BROKEN_INTEGRAL_CONSTANTS 1 ++#endif ++#define LZO_CPP_STRINGIZE(x) #x ++#define LZO_CPP_MACRO_EXPAND(x) LZO_CPP_STRINGIZE(x) ++#define LZO_CPP_CONCAT2(a,b) a ## b ++#define LZO_CPP_CONCAT3(a,b,c) a ## b ## c ++#define LZO_CPP_CONCAT4(a,b,c,d) a ## b ## c ## d ++#define LZO_CPP_CONCAT5(a,b,c,d,e) a ## b ## c ## d ## e ++#define LZO_CPP_ECONCAT2(a,b) LZO_CPP_CONCAT2(a,b) ++#define LZO_CPP_ECONCAT3(a,b,c) LZO_CPP_CONCAT3(a,b,c) ++#define LZO_CPP_ECONCAT4(a,b,c,d) LZO_CPP_CONCAT4(a,b,c,d) ++#define LZO_CPP_ECONCAT5(a,b,c,d,e) LZO_CPP_CONCAT5(a,b,c,d,e) ++#define __LZO_MASK_GEN(o,b) (((((o) << ((b)-1)) - (o)) << 1) + (o)) ++#if 1 && defined(__cplusplus) ++# if !defined(__STDC_CONSTANT_MACROS) ++# define __STDC_CONSTANT_MACROS 1 ++# endif ++# if !defined(__STDC_LIMIT_MACROS) ++# define __STDC_LIMIT_MACROS 1 ++# endif ++#endif ++#if defined(__cplusplus) ++# define LZO_EXTERN_C extern "C" ++#else ++# define LZO_EXTERN_C extern ++#endif ++#if !defined(__LZO_OS_OVERRIDE) ++#if defined(LZO_OS_FREESTANDING) ++# define LZO_INFO_OS "freestanding" ++#elif defined(LZO_OS_EMBEDDED) ++# define LZO_INFO_OS "embedded" ++#elif defined(__CYGWIN__) && defined(__GNUC__) ++# define LZO_OS_CYGWIN 1 ++# define LZO_INFO_OS "cygwin" ++#elif defined(__EMX__) && defined(__GNUC__) ++# define LZO_OS_EMX 1 ++# define LZO_INFO_OS "emx" ++#elif defined(__BEOS__) ++# define LZO_OS_BEOS 1 ++# define LZO_INFO_OS "beos" ++#elif defined(__Lynx__) ++# define LZO_OS_LYNXOS 1 ++# define LZO_INFO_OS "lynxos" ++#elif defined(__OS400__) ++# define LZO_OS_OS400 1 ++# define LZO_INFO_OS "os400" ++#elif defined(__QNX__) ++# define LZO_OS_QNX 1 ++# define LZO_INFO_OS "qnx" ++#elif defined(__BORLANDC__) && defined(__DPMI32__) && (__BORLANDC__ >= 0x0460) ++# define LZO_OS_DOS32 1 ++# define LZO_INFO_OS "dos32" ++#elif defined(__BORLANDC__) && defined(__DPMI16__) ++# define LZO_OS_DOS16 1 ++# define LZO_INFO_OS "dos16" ++#elif defined(__ZTC__) && defined(DOS386) ++# define LZO_OS_DOS32 1 ++# define LZO_INFO_OS "dos32" ++#elif defined(__OS2__) || defined(__OS2V2__) ++# if (UINT_MAX == LZO_0xffffL) ++# define LZO_OS_OS216 1 ++# define LZO_INFO_OS "os216" ++# elif (UINT_MAX == LZO_0xffffffffL) ++# define LZO_OS_OS2 1 ++# define LZO_INFO_OS "os2" ++# else ++# error "check your limits.h header" ++# endif ++#elif defined(__WIN64__) || defined(_WIN64) || defined(WIN64) ++# define LZO_OS_WIN64 1 ++# define LZO_INFO_OS "win64" ++#elif defined(__WIN32__) || defined(_WIN32) || defined(WIN32) || defined(__WINDOWS_386__) ++# define LZO_OS_WIN32 1 ++# define LZO_INFO_OS "win32" ++#elif defined(__MWERKS__) && defined(__INTEL__) ++# define LZO_OS_WIN32 1 ++# define LZO_INFO_OS "win32" ++#elif defined(__WINDOWS__) || defined(_WINDOWS) || defined(_Windows) ++# if (UINT_MAX == LZO_0xffffL) ++# define LZO_OS_WIN16 1 ++# define LZO_INFO_OS "win16" ++# elif (UINT_MAX == LZO_0xffffffffL) ++# define LZO_OS_WIN32 1 ++# define LZO_INFO_OS "win32" ++# else ++# error "check your limits.h header" ++# endif ++#elif defined(__DOS__) || defined(__MSDOS__) || defined(_MSDOS) || defined(MSDOS) || (defined(__PACIFIC__) && defined(DOS)) ++# if (UINT_MAX == LZO_0xffffL) ++# define LZO_OS_DOS16 1 ++# define LZO_INFO_OS "dos16" ++# elif (UINT_MAX == LZO_0xffffffffL) ++# define LZO_OS_DOS32 1 ++# define LZO_INFO_OS "dos32" ++# else ++# error "check your limits.h header" ++# endif ++#elif defined(__WATCOMC__) ++# if defined(__NT__) && (UINT_MAX == LZO_0xffffL) ++# define LZO_OS_DOS16 1 ++# define LZO_INFO_OS "dos16" ++# elif defined(__NT__) && (__WATCOMC__ < 1100) ++# define LZO_OS_WIN32 1 ++# define LZO_INFO_OS "win32" ++# else ++# error "please specify a target using the -bt compiler option" ++# endif ++#elif defined(__palmos__) ++# define LZO_OS_PALMOS 1 ++# define LZO_INFO_OS "palmos" ++#elif defined(__TOS__) || defined(__atarist__) ++# define LZO_OS_TOS 1 ++# define LZO_INFO_OS "tos" ++#elif defined(macintosh) && !defined(__ppc__) ++# define LZO_OS_MACCLASSIC 1 ++# define LZO_INFO_OS "macclassic" ++#elif defined(__VMS) ++# define LZO_OS_VMS 1 ++# define LZO_INFO_OS "vms" ++#elif ((defined(__mips__) && defined(__R5900__)) || defined(__MIPS_PSX2__)) ++# define LZO_OS_CONSOLE 1 ++# define LZO_OS_CONSOLE_PS2 1 ++# define LZO_INFO_OS "console" ++# define LZO_INFO_OS_CONSOLE "ps2" ++#elif (defined(__mips__) && defined(__psp__)) ++# define LZO_OS_CONSOLE 1 ++# define LZO_OS_CONSOLE_PSP 1 ++# define LZO_INFO_OS "console" ++# define LZO_INFO_OS_CONSOLE "psp" ++#else ++# define LZO_OS_POSIX 1 ++# define LZO_INFO_OS "posix" ++#endif ++#if (LZO_OS_POSIX) ++# if defined(_AIX) || defined(__AIX__) || defined(__aix__) ++# define LZO_OS_POSIX_AIX 1 ++# define LZO_INFO_OS_POSIX "aix" ++# elif defined(__FreeBSD__) ++# define LZO_OS_POSIX_FREEBSD 1 ++# define LZO_INFO_OS_POSIX "freebsd" ++# elif defined(__hpux__) || defined(__hpux) ++# define LZO_OS_POSIX_HPUX 1 ++# define LZO_INFO_OS_POSIX "hpux" ++# elif defined(__INTERIX) ++# define LZO_OS_POSIX_INTERIX 1 ++# define LZO_INFO_OS_POSIX "interix" ++# elif defined(__IRIX__) || defined(__irix__) ++# define LZO_OS_POSIX_IRIX 1 ++# define LZO_INFO_OS_POSIX "irix" ++# elif defined(__linux__) || defined(__linux) ++# define LZO_OS_POSIX_LINUX 1 ++# define LZO_INFO_OS_POSIX "linux" ++# elif defined(__APPLE__) || defined(__MACOS__) ++# define LZO_OS_POSIX_MACOSX 1 ++# define LZO_INFO_OS_POSIX "macosx" ++# elif defined(__NetBSD__) ++# define LZO_OS_POSIX_NETBSD 1 ++# define LZO_INFO_OS_POSIX "netbsd" ++# elif defined(__OpenBSD__) ++# define LZO_OS_POSIX_OPENBSD 1 ++# define LZO_INFO_OS_POSIX "openbsd" ++# elif defined(__osf__) ++# define LZO_OS_POSIX_OSF 1 ++# define LZO_INFO_OS_POSIX "osf" ++# elif defined(__solaris__) || defined(__sun) ++# if defined(__SVR4) || defined(__svr4__) ++# define LZO_OS_POSIX_SOLARIS 1 ++# define LZO_INFO_OS_POSIX "solaris" ++# else ++# define LZO_OS_POSIX_SUNOS 1 ++# define LZO_INFO_OS_POSIX "sunos" ++# endif ++# elif defined(__ultrix__) || defined(__ultrix) ++# define LZO_OS_POSIX_ULTRIX 1 ++# define LZO_INFO_OS_POSIX "ultrix" ++# else ++# define LZO_OS_POSIX_UNKNOWN 1 ++# define LZO_INFO_OS_POSIX "unknown" ++# endif ++#endif ++#endif ++#if (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) ++# if (UINT_MAX != LZO_0xffffL) ++# error "this should not happen" ++# endif ++# if (ULONG_MAX != LZO_0xffffffffL) ++# error "this should not happen" ++# endif ++#endif ++#if (LZO_OS_DOS32 || LZO_OS_OS2 || LZO_OS_WIN32 || LZO_OS_WIN64) ++# if (UINT_MAX != LZO_0xffffffffL) ++# error "this should not happen" ++# endif ++# if (ULONG_MAX != LZO_0xffffffffL) ++# error "this should not happen" ++# endif ++#endif ++#if defined(CIL) && defined(_GNUCC) && defined(__GNUC__) ++# define LZO_CC_CILLY 1 ++# define LZO_INFO_CC "Cilly" ++# if defined(__CILLY__) ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__CILLY__) ++# else ++# define LZO_INFO_CCVER "unknown" ++# endif ++#elif 0 && defined(SDCC) && defined(__VERSION__) && !defined(__GNUC__) ++# define LZO_CC_SDCC 1 ++# define LZO_INFO_CC "sdcc" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(SDCC) ++#elif defined(__PATHSCALE__) && defined(__PATHCC_PATCHLEVEL__) ++# define LZO_CC_PATHSCALE (__PATHCC__ * 0x10000L + __PATHCC_MINOR__ * 0x100 + __PATHCC_PATCHLEVEL__) ++# define LZO_INFO_CC "Pathscale C" ++# define LZO_INFO_CCVER __PATHSCALE__ ++#elif defined(__INTEL_COMPILER) ++# define LZO_CC_INTELC 1 ++# define LZO_INFO_CC "Intel C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__INTEL_COMPILER) ++#elif defined(__POCC__) && defined(_WIN32) ++# define LZO_CC_PELLESC 1 ++# define LZO_INFO_CC "Pelles C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__POCC__) ++#elif defined(__llvm__) && defined(__GNUC__) && defined(__VERSION__) ++# define LZO_CC_LLVM 1 ++# define LZO_INFO_CC "llvm-gcc" ++# define LZO_INFO_CCVER __VERSION__ ++#elif defined(__GNUC__) && defined(__VERSION__) ++# if defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) ++# define LZO_CC_GNUC (__GNUC__ * 0x10000L + __GNUC_MINOR__ * 0x100 + __GNUC_PATCHLEVEL__) ++# elif defined(__GNUC_MINOR__) ++# define LZO_CC_GNUC (__GNUC__ * 0x10000L + __GNUC_MINOR__ * 0x100) ++# else ++# define LZO_CC_GNUC (__GNUC__ * 0x10000L) ++# endif ++# define LZO_INFO_CC "gcc" ++# define LZO_INFO_CCVER __VERSION__ ++#elif defined(__AZTEC_C__) ++# define LZO_CC_AZTECC 1 ++# define LZO_INFO_CC "Aztec C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__AZTEC_C__) ++#elif defined(__BORLANDC__) ++# define LZO_CC_BORLANDC 1 ++# define LZO_INFO_CC "Borland C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__BORLANDC__) ++#elif defined(__DMC__) && defined(__SC__) ++# define LZO_CC_DMC 1 ++# define LZO_INFO_CC "Digital Mars C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__DMC__) ++#elif defined(__DECC) ++# define LZO_CC_DECC 1 ++# define LZO_INFO_CC "DEC C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__DECC) ++#elif defined(__HIGHC__) ++# define LZO_CC_HIGHC 1 ++# define LZO_INFO_CC "MetaWare High C" ++# define LZO_INFO_CCVER "unknown" ++#elif defined(__IBMC__) ++# define LZO_CC_IBMC 1 ++# define LZO_INFO_CC "IBM C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__IBMC__) ++#elif defined(__KEIL__) && defined(__C166__) ++# define LZO_CC_KEILC 1 ++# define LZO_INFO_CC "Keil C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__C166__) ++#elif defined(__LCC__) && defined(_WIN32) && defined(__LCCOPTIMLEVEL) ++# define LZO_CC_LCCWIN32 1 ++# define LZO_INFO_CC "lcc-win32" ++# define LZO_INFO_CCVER "unknown" ++#elif defined(__LCC__) ++# define LZO_CC_LCC 1 ++# define LZO_INFO_CC "lcc" ++# if defined(__LCC_VERSION__) ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__LCC_VERSION__) ++# else ++# define LZO_INFO_CCVER "unknown" ++# endif ++#elif defined(_MSC_VER) ++# define LZO_CC_MSC 1 ++# define LZO_INFO_CC "Microsoft C" ++# if defined(_MSC_FULL_VER) ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(_MSC_VER) "." LZO_CPP_MACRO_EXPAND(_MSC_FULL_VER) ++# else ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(_MSC_VER) ++# endif ++#elif defined(__MWERKS__) ++# define LZO_CC_MWERKS 1 ++# define LZO_INFO_CC "Metrowerks C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__MWERKS__) ++#elif (defined(__NDPC__) || defined(__NDPX__)) && defined(__i386) ++# define LZO_CC_NDPC 1 ++# define LZO_INFO_CC "Microway NDP C" ++# define LZO_INFO_CCVER "unknown" ++#elif defined(__PACIFIC__) ++# define LZO_CC_PACIFICC 1 ++# define LZO_INFO_CC "Pacific C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__PACIFIC__) ++#elif defined(__PGI) && (defined(__linux__) || defined(__WIN32__)) ++# define LZO_CC_PGI 1 ++# define LZO_INFO_CC "Portland Group PGI C" ++# define LZO_INFO_CCVER "unknown" ++#elif defined(__PUREC__) && defined(__TOS__) ++# define LZO_CC_PUREC 1 ++# define LZO_INFO_CC "Pure C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__PUREC__) ++#elif defined(__SC__) && defined(__ZTC__) ++# define LZO_CC_SYMANTECC 1 ++# define LZO_INFO_CC "Symantec C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__SC__) ++#elif defined(__SUNPRO_C) ++# define LZO_CC_SUNPROC 1 ++# define LZO_INFO_CC "Sun C" ++# define LZO_INFO_CCVER "unknown" ++#elif defined(__TINYC__) ++# define LZO_CC_TINYC 1 ++# define LZO_INFO_CC "Tiny C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__TINYC__) ++#elif defined(__TSC__) ++# define LZO_CC_TOPSPEEDC 1 ++# define LZO_INFO_CC "TopSpeed C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__TSC__) ++#elif defined(__WATCOMC__) ++# define LZO_CC_WATCOMC 1 ++# define LZO_INFO_CC "Watcom C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__WATCOMC__) ++#elif defined(__TURBOC__) ++# define LZO_CC_TURBOC 1 ++# define LZO_INFO_CC "Turbo C" ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__TURBOC__) ++#elif defined(__ZTC__) ++# define LZO_CC_ZORTECHC 1 ++# define LZO_INFO_CC "Zortech C" ++# if (__ZTC__ == 0x310) ++# define LZO_INFO_CCVER "0x310" ++# else ++# define LZO_INFO_CCVER LZO_CPP_MACRO_EXPAND(__ZTC__) ++# endif ++#else ++# define LZO_CC_UNKNOWN 1 ++# define LZO_INFO_CC "unknown" ++# define LZO_INFO_CCVER "unknown" ++#endif ++#if 0 && (LZO_CC_MSC && (_MSC_VER >= 1200)) && !defined(_MSC_FULL_VER) ++# error "LZO_CC_MSC: _MSC_FULL_VER is not defined" ++#endif ++#if !defined(__LZO_ARCH_OVERRIDE) ++#if defined(LZO_ARCH_GENERIC) ++# define LZO_INFO_ARCH "generic" ++#elif (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) ++# define LZO_ARCH_I086 1 ++# define LZO_ARCH_IA16 1 ++# define LZO_INFO_ARCH "i086" ++#elif defined(__alpha__) || defined(__alpha) || defined(_M_ALPHA) ++# define LZO_ARCH_ALPHA 1 ++# define LZO_INFO_ARCH "alpha" ++#elif defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64) ++# define LZO_ARCH_AMD64 1 ++# define LZO_INFO_ARCH "amd64" ++#elif defined(__thumb__) || (defined(_M_ARM) && defined(_M_THUMB)) ++# define LZO_ARCH_ARM 1 ++# define LZO_ARCH_ARM_THUMB 1 ++# define LZO_INFO_ARCH "arm_thumb" ++#elif defined(__arm__) || defined(_M_ARM) ++# define LZO_ARCH_ARM 1 ++# define LZO_INFO_ARCH "arm" ++#elif (UINT_MAX <= LZO_0xffffL) && defined(__AVR__) ++# define LZO_ARCH_AVR 1 ++# define LZO_INFO_ARCH "avr" ++#elif defined(__bfin__) ++# define LZO_ARCH_BLACKFIN 1 ++# define LZO_INFO_ARCH "blackfin" ++#elif (UINT_MAX == LZO_0xffffL) && defined(__C166__) ++# define LZO_ARCH_C166 1 ++# define LZO_INFO_ARCH "c166" ++#elif defined(__cris__) ++# define LZO_ARCH_CRIS 1 ++# define LZO_INFO_ARCH "cris" ++#elif defined(__H8300__) || defined(__H8300H__) || defined(__H8300S__) || defined(__H8300SX__) ++# define LZO_ARCH_H8300 1 ++# define LZO_INFO_ARCH "h8300" ++#elif defined(__hppa__) || defined(__hppa) ++# define LZO_ARCH_HPPA 1 ++# define LZO_INFO_ARCH "hppa" ++#elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386) ++# define LZO_ARCH_I386 1 ++# define LZO_ARCH_IA32 1 ++# define LZO_INFO_ARCH "i386" ++#elif (LZO_CC_ZORTECHC && defined(__I86__)) ++# define LZO_ARCH_I386 1 ++# define LZO_ARCH_IA32 1 ++# define LZO_INFO_ARCH "i386" ++#elif (LZO_OS_DOS32 && LZO_CC_HIGHC) && defined(_I386) ++# define LZO_ARCH_I386 1 ++# define LZO_ARCH_IA32 1 ++# define LZO_INFO_ARCH "i386" ++#elif defined(__ia64__) || defined(__ia64) || defined(_M_IA64) ++# define LZO_ARCH_IA64 1 ++# define LZO_INFO_ARCH "ia64" ++#elif (UINT_MAX == LZO_0xffffL) && defined(__m32c__) ++# define LZO_ARCH_M16C 1 ++# define LZO_INFO_ARCH "m16c" ++#elif defined(__m32r__) ++# define LZO_ARCH_M32R 1 ++# define LZO_INFO_ARCH "m32r" ++#elif (LZO_OS_TOS) || defined(__m68k__) || defined(__m68000__) || defined(__mc68000__) || defined(_M_M68K) ++# define LZO_ARCH_M68K 1 ++# define LZO_INFO_ARCH "m68k" ++#elif (UINT_MAX == LZO_0xffffL) && defined(__C251__) ++# define LZO_ARCH_MCS251 1 ++# define LZO_INFO_ARCH "mcs251" ++#elif (UINT_MAX == LZO_0xffffL) && defined(__C51__) ++# define LZO_ARCH_MCS51 1 ++# define LZO_INFO_ARCH "mcs51" ++#elif defined(__mips__) || defined(__mips) || defined(_MIPS_ARCH) || defined(_M_MRX000) ++# define LZO_ARCH_MIPS 1 ++# define LZO_INFO_ARCH "mips" ++#elif (UINT_MAX == LZO_0xffffL) && defined(__MSP430__) ++# define LZO_ARCH_MSP430 1 ++# define LZO_INFO_ARCH "msp430" ++#elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) ++# define LZO_ARCH_POWERPC 1 ++# define LZO_INFO_ARCH "powerpc" ++#elif defined(__s390__) || defined(__s390) || defined(__s390x__) || defined(__s390x) ++# define LZO_ARCH_S390 1 ++# define LZO_INFO_ARCH "s390" ++#elif defined(__sh__) || defined(_M_SH) ++# define LZO_ARCH_SH 1 ++# define LZO_INFO_ARCH "sh" ++#elif defined(__sparc__) || defined(__sparc) || defined(__sparcv8) ++# define LZO_ARCH_SPARC 1 ++# define LZO_INFO_ARCH "sparc" ++#elif (UINT_MAX == LZO_0xffffL) && defined(__z80) ++# define LZO_ARCH_Z80 1 ++# define LZO_INFO_ARCH "z80" ++#else ++# define LZO_ARCH_UNKNOWN 1 ++# define LZO_INFO_ARCH "unknown" ++#endif ++#endif ++#if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_DOS32 || LZO_OS_OS2) ++# error "FIXME - missing define for CPU architecture" ++#endif ++#if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_WIN32) ++# error "FIXME - missing WIN32 define for CPU architecture" ++#endif ++#if 1 && (LZO_ARCH_UNKNOWN) && (LZO_OS_WIN64) ++# error "FIXME - missing WIN64 define for CPU architecture" ++#endif ++#if (LZO_OS_OS216 || LZO_OS_WIN16) ++# define LZO_ARCH_I086PM 1 ++# define LZO_ARCH_IA16PM 1 ++#elif 1 && (LZO_OS_DOS16 && defined(BLX286)) ++# define LZO_ARCH_I086PM 1 ++# define LZO_ARCH_IA16PM 1 ++#elif 1 && (LZO_OS_DOS16 && defined(DOSX286)) ++# define LZO_ARCH_I086PM 1 ++# define LZO_ARCH_IA16PM 1 ++#elif 1 && (LZO_OS_DOS16 && LZO_CC_BORLANDC && defined(__DPMI16__)) ++# define LZO_ARCH_I086PM 1 ++# define LZO_ARCH_IA16PM 1 ++#endif ++#if defined(LZO_ARCH_ARM_THUMB) && !defined(LZO_ARCH_ARM) ++# error "this should not happen" ++#endif ++#if defined(LZO_ARCH_I086PM) && !defined(LZO_ARCH_I086) ++# error "this should not happen" ++#endif ++#if (LZO_ARCH_I086) ++# if (UINT_MAX != LZO_0xffffL) ++# error "this should not happen" ++# endif ++# if (ULONG_MAX != LZO_0xffffffffL) ++# error "this should not happen" ++# endif ++#endif ++#if (LZO_ARCH_I386) ++# if (UINT_MAX != LZO_0xffffL) && defined(__i386_int16__) ++# error "this should not happen" ++# endif ++# if (UINT_MAX != LZO_0xffffffffL) && !defined(__i386_int16__) ++# error "this should not happen" ++# endif ++# if (ULONG_MAX != LZO_0xffffffffL) ++# error "this should not happen" ++# endif ++#endif ++#if !defined(__LZO_MM_OVERRIDE) ++#if (LZO_OS_DOS16 || LZO_OS_OS216 || LZO_OS_WIN16) ++#if (UINT_MAX != LZO_0xffffL) ++# error "this should not happen" ++#endif ++#if defined(__TINY__) || defined(M_I86TM) || defined(_M_I86TM) ++# define LZO_MM_TINY 1 ++#elif defined(__HUGE__) || defined(_HUGE_) || defined(M_I86HM) || defined(_M_I86HM) ++# define LZO_MM_HUGE 1 ++#elif defined(__SMALL__) || defined(M_I86SM) || defined(_M_I86SM) || defined(SMALL_MODEL) ++# define LZO_MM_SMALL 1 ++#elif defined(__MEDIUM__) || defined(M_I86MM) || defined(_M_I86MM) ++# define LZO_MM_MEDIUM 1 ++#elif defined(__COMPACT__) || defined(M_I86CM) || defined(_M_I86CM) ++# define LZO_MM_COMPACT 1 ++#elif defined(__LARGE__) || defined(M_I86LM) || defined(_M_I86LM) || defined(LARGE_MODEL) ++# define LZO_MM_LARGE 1 ++#elif (LZO_CC_AZTECC) ++# if defined(_LARGE_CODE) && defined(_LARGE_DATA) ++# define LZO_MM_LARGE 1 ++# elif defined(_LARGE_CODE) ++# define LZO_MM_MEDIUM 1 ++# elif defined(_LARGE_DATA) ++# define LZO_MM_COMPACT 1 ++# else ++# define LZO_MM_SMALL 1 ++# endif ++#elif (LZO_CC_ZORTECHC && defined(__VCM__)) ++# define LZO_MM_LARGE 1 ++#else ++# error "unknown memory model" ++#endif ++#define LZO_HAVE_MM_HUGE_PTR 1 ++#define LZO_HAVE_MM_HUGE_ARRAY 1 ++#if (LZO_MM_TINY) ++# undef LZO_HAVE_MM_HUGE_ARRAY ++#endif ++#if (LZO_CC_AZTECC || LZO_CC_PACIFICC || LZO_CC_ZORTECHC) ++# undef LZO_HAVE_MM_HUGE_PTR ++# undef LZO_HAVE_MM_HUGE_ARRAY ++#elif (LZO_CC_DMC || LZO_CC_SYMANTECC) ++# undef LZO_HAVE_MM_HUGE_ARRAY ++#elif (LZO_CC_MSC && defined(_QC)) ++# undef LZO_HAVE_MM_HUGE_ARRAY ++# if (_MSC_VER < 600) ++# undef LZO_HAVE_MM_HUGE_PTR ++# endif ++#elif (LZO_CC_TURBOC && (__TURBOC__ < 0x0295)) ++# undef LZO_HAVE_MM_HUGE_ARRAY ++#endif ++#if (LZO_ARCH_I086PM) && !defined(LZO_HAVE_MM_HUGE_PTR) ++# if (LZO_OS_DOS16) ++# error "this should not happen" ++# elif (LZO_CC_ZORTECHC) ++# else ++# error "this should not happen" ++# endif ++#endif ++#ifdef __cplusplus ++extern "C" { ++#endif ++#if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0200)) ++ extern void __near __cdecl _AHSHIFT(void); ++# define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) ++#elif (LZO_CC_DMC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) ++ extern void __near __cdecl _AHSHIFT(void); ++# define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) ++#elif (LZO_CC_MSC || LZO_CC_TOPSPEEDC) ++ extern void __near __cdecl _AHSHIFT(void); ++# define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) ++#elif (LZO_CC_TURBOC && (__TURBOC__ >= 0x0295)) ++ extern void __near __cdecl _AHSHIFT(void); ++# define LZO_MM_AHSHIFT ((unsigned) _AHSHIFT) ++#elif ((LZO_CC_AZTECC || LZO_CC_PACIFICC || LZO_CC_TURBOC) && LZO_OS_DOS16) ++# define LZO_MM_AHSHIFT 12 ++#elif (LZO_CC_WATCOMC) ++ extern unsigned char _HShift; ++# define LZO_MM_AHSHIFT ((unsigned) _HShift) ++#else ++# error "FIXME - implement LZO_MM_AHSHIFT" ++#endif ++#ifdef __cplusplus ++} ++#endif ++#elif (LZO_ARCH_C166) ++#if !defined(__MODEL__) ++# error "FIXME - C166 __MODEL__" ++#elif ((__MODEL__) == 0) ++# define LZO_MM_SMALL 1 ++#elif ((__MODEL__) == 1) ++# define LZO_MM_SMALL 1 ++#elif ((__MODEL__) == 2) ++# define LZO_MM_LARGE 1 ++#elif ((__MODEL__) == 3) ++# define LZO_MM_TINY 1 ++#elif ((__MODEL__) == 4) ++# define LZO_MM_XTINY 1 ++#elif ((__MODEL__) == 5) ++# define LZO_MM_XSMALL 1 ++#else ++# error "FIXME - C166 __MODEL__" ++#endif ++#elif (LZO_ARCH_MCS251) ++#if !defined(__MODEL__) ++# error "FIXME - MCS251 __MODEL__" ++#elif ((__MODEL__) == 0) ++# define LZO_MM_SMALL 1 ++#elif ((__MODEL__) == 2) ++# define LZO_MM_LARGE 1 ++#elif ((__MODEL__) == 3) ++# define LZO_MM_TINY 1 ++#elif ((__MODEL__) == 4) ++# define LZO_MM_XTINY 1 ++#elif ((__MODEL__) == 5) ++# define LZO_MM_XSMALL 1 ++#else ++# error "FIXME - MCS251 __MODEL__" ++#endif ++#elif (LZO_ARCH_MCS51) ++#if !defined(__MODEL__) ++# error "FIXME - MCS51 __MODEL__" ++#elif ((__MODEL__) == 1) ++# define LZO_MM_SMALL 1 ++#elif ((__MODEL__) == 2) ++# define LZO_MM_LARGE 1 ++#elif ((__MODEL__) == 3) ++# define LZO_MM_TINY 1 ++#elif ((__MODEL__) == 4) ++# define LZO_MM_XTINY 1 ++#elif ((__MODEL__) == 5) ++# define LZO_MM_XSMALL 1 ++#else ++# error "FIXME - MCS51 __MODEL__" ++#endif ++#else ++# define LZO_MM_FLAT 1 ++#endif ++#if (LZO_MM_FLAT) ++# define LZO_INFO_MM "flat" ++#elif (LZO_MM_TINY) ++# define LZO_INFO_MM "tiny" ++#elif (LZO_MM_SMALL) ++# define LZO_INFO_MM "small" ++#elif (LZO_MM_MEDIUM) ++# define LZO_INFO_MM "medium" ++#elif (LZO_MM_COMPACT) ++# define LZO_INFO_MM "compact" ++#elif (LZO_MM_LARGE) ++# define LZO_INFO_MM "large" ++#elif (LZO_MM_HUGE) ++# define LZO_INFO_MM "huge" ++#else ++# error "unknown memory model" ++#endif ++#endif ++#if defined(SIZEOF_SHORT) ++# define LZO_SIZEOF_SHORT (SIZEOF_SHORT) ++#endif ++#if defined(SIZEOF_INT) ++# define LZO_SIZEOF_INT (SIZEOF_INT) ++#endif ++#if defined(SIZEOF_LONG) ++# define LZO_SIZEOF_LONG (SIZEOF_LONG) ++#endif ++#if defined(SIZEOF_LONG_LONG) ++# define LZO_SIZEOF_LONG_LONG (SIZEOF_LONG_LONG) ++#endif ++#if defined(SIZEOF___INT16) ++# define LZO_SIZEOF___INT16 (SIZEOF___INT16) ++#endif ++#if defined(SIZEOF___INT32) ++# define LZO_SIZEOF___INT32 (SIZEOF___INT32) ++#endif ++#if defined(SIZEOF___INT64) ++# define LZO_SIZEOF___INT64 (SIZEOF___INT64) ++#endif ++#if defined(SIZEOF_VOID_P) ++# define LZO_SIZEOF_VOID_P (SIZEOF_VOID_P) ++#endif ++#if defined(SIZEOF_SIZE_T) ++# define LZO_SIZEOF_SIZE_T (SIZEOF_SIZE_T) ++#endif ++#if defined(SIZEOF_PTRDIFF_T) ++# define LZO_SIZEOF_PTRDIFF_T (SIZEOF_PTRDIFF_T) ++#endif ++#define __LZO_LSR(x,b) (((x)+0ul) >> (b)) ++#if !defined(LZO_SIZEOF_SHORT) ++# if (USHRT_MAX == LZO_0xffffL) ++# define LZO_SIZEOF_SHORT 2 ++# elif (__LZO_LSR(USHRT_MAX,7) == 1) ++# define LZO_SIZEOF_SHORT 1 ++# elif (__LZO_LSR(USHRT_MAX,15) == 1) ++# define LZO_SIZEOF_SHORT 2 ++# elif (__LZO_LSR(USHRT_MAX,31) == 1) ++# define LZO_SIZEOF_SHORT 4 ++# elif (__LZO_LSR(USHRT_MAX,63) == 1) ++# define LZO_SIZEOF_SHORT 8 ++# elif (__LZO_LSR(USHRT_MAX,127) == 1) ++# define LZO_SIZEOF_SHORT 16 ++# else ++# error "LZO_SIZEOF_SHORT" ++# endif ++#endif ++#if !defined(LZO_SIZEOF_INT) ++# if (UINT_MAX == LZO_0xffffL) ++# define LZO_SIZEOF_INT 2 ++# elif (UINT_MAX == LZO_0xffffffffL) ++# define LZO_SIZEOF_INT 4 ++# elif (__LZO_LSR(UINT_MAX,7) == 1) ++# define LZO_SIZEOF_INT 1 ++# elif (__LZO_LSR(UINT_MAX,15) == 1) ++# define LZO_SIZEOF_INT 2 ++# elif (__LZO_LSR(UINT_MAX,31) == 1) ++# define LZO_SIZEOF_INT 4 ++# elif (__LZO_LSR(UINT_MAX,63) == 1) ++# define LZO_SIZEOF_INT 8 ++# elif (__LZO_LSR(UINT_MAX,127) == 1) ++# define LZO_SIZEOF_INT 16 ++# else ++# error "LZO_SIZEOF_INT" ++# endif ++#endif ++#if !defined(LZO_SIZEOF_LONG) ++# if (ULONG_MAX == LZO_0xffffffffL) ++# define LZO_SIZEOF_LONG 4 ++# elif (__LZO_LSR(ULONG_MAX,7) == 1) ++# define LZO_SIZEOF_LONG 1 ++# elif (__LZO_LSR(ULONG_MAX,15) == 1) ++# define LZO_SIZEOF_LONG 2 ++# elif (__LZO_LSR(ULONG_MAX,31) == 1) ++# define LZO_SIZEOF_LONG 4 ++# elif (__LZO_LSR(ULONG_MAX,63) == 1) ++# define LZO_SIZEOF_LONG 8 ++# elif (__LZO_LSR(ULONG_MAX,127) == 1) ++# define LZO_SIZEOF_LONG 16 ++# else ++# error "LZO_SIZEOF_LONG" ++# endif ++#endif ++#if !defined(LZO_SIZEOF_LONG_LONG) && !defined(LZO_SIZEOF___INT64) ++#if (LZO_SIZEOF_LONG > 0 && LZO_SIZEOF_LONG < 8) ++# if defined(__LONG_MAX__) && defined(__LONG_LONG_MAX__) ++# if (LZO_CC_GNUC >= 0x030300ul) ++# if ((__LONG_MAX__)+0 == (__LONG_LONG_MAX__)+0) ++# define LZO_SIZEOF_LONG_LONG LZO_SIZEOF_LONG ++# endif ++# endif ++# endif ++#endif ++#endif ++#if !defined(LZO_SIZEOF_LONG_LONG) && !defined(LZO_SIZEOF___INT64) ++#if (LZO_SIZEOF_LONG > 0 && LZO_SIZEOF_LONG < 8) ++#if (LZO_ARCH_I086 && LZO_CC_DMC) ++#elif (LZO_CC_CILLY) && defined(__GNUC__) ++# define LZO_SIZEOF_LONG_LONG 8 ++#elif (LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) ++# define LZO_SIZEOF_LONG_LONG 8 ++#elif ((LZO_OS_WIN32 || LZO_OS_WIN64) && LZO_CC_MSC && (_MSC_VER >= 1400)) ++# define LZO_SIZEOF_LONG_LONG 8 ++#elif (LZO_OS_WIN64) ++# define LZO_SIZEOF___INT64 8 ++#elif (LZO_ARCH_I386 && (LZO_CC_DMC)) ++# define LZO_SIZEOF_LONG_LONG 8 ++#elif (LZO_ARCH_I386 && (LZO_CC_SYMANTECC && (__SC__ >= 0x700))) ++# define LZO_SIZEOF_LONG_LONG 8 ++#elif (LZO_ARCH_I386 && (LZO_CC_INTELC && defined(__linux__))) ++# define LZO_SIZEOF_LONG_LONG 8 ++#elif (LZO_ARCH_I386 && (LZO_CC_MWERKS || LZO_CC_PELLESC || LZO_CC_PGI)) ++# define LZO_SIZEOF_LONG_LONG 8 ++#elif (LZO_ARCH_I386 && (LZO_CC_INTELC || LZO_CC_MSC)) ++# define LZO_SIZEOF___INT64 8 ++#elif (LZO_OS_WIN32 && (LZO_CC_MSC)) ++# define LZO_SIZEOF___INT64 8 ++#elif (LZO_ARCH_I386 && (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0520))) ++# define LZO_SIZEOF___INT64 8 ++#elif (LZO_ARCH_I386 && (LZO_CC_WATCOMC && (__WATCOMC__ >= 1100))) ++# define LZO_SIZEOF___INT64 8 ++#elif (LZO_CC_WATCOMC && defined(_INTEGRAL_MAX_BITS) && (_INTEGRAL_MAX_BITS == 64)) ++# define LZO_SIZEOF___INT64 8 ++#elif (LZO_OS_OS400) && defined(__LLP64_IFC__) ++# define LZO_SIZEOF_LONG_LONG 8 ++#elif (defined(__vms) || defined(__VMS)) && (__INITIAL_POINTER_SIZE+0 == 64) ++# define LZO_SIZEOF_LONG_LONG 8 ++#elif (LZO_CC_SDCC) && (LZO_SIZEOF_INT == 2) ++#elif 1 && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) ++# define LZO_SIZEOF_LONG_LONG 8 ++#endif ++#endif ++#endif ++#if defined(__cplusplus) && defined(LZO_CC_GNUC) ++# if (LZO_CC_GNUC < 0x020800ul) ++# undef LZO_SIZEOF_LONG_LONG ++# endif ++#endif ++#if defined(LZO_CFG_NO_LONG_LONG) || defined(__NO_LONG_LONG) ++# undef LZO_SIZEOF_LONG_LONG ++#endif ++#if !defined(LZO_SIZEOF_VOID_P) ++#if (LZO_ARCH_I086) ++# define __LZO_WORDSIZE 2 ++# if (LZO_MM_TINY || LZO_MM_SMALL || LZO_MM_MEDIUM) ++# define LZO_SIZEOF_VOID_P 2 ++# elif (LZO_MM_COMPACT || LZO_MM_LARGE || LZO_MM_HUGE) ++# define LZO_SIZEOF_VOID_P 4 ++# else ++# error "LZO_MM" ++# endif ++#elif (LZO_ARCH_AVR || LZO_ARCH_Z80) ++# define __LZO_WORDSIZE 1 ++# define LZO_SIZEOF_VOID_P 2 ++#elif (LZO_ARCH_C166 || LZO_ARCH_MCS51 || LZO_ARCH_MCS251 || LZO_ARCH_MSP430) ++# define LZO_SIZEOF_VOID_P 2 ++#elif (LZO_ARCH_H8300) ++# if defined(__NORMAL_MODE__) ++# define __LZO_WORDSIZE 4 ++# define LZO_SIZEOF_VOID_P 2 ++# elif defined(__H8300H__) || defined(__H8300S__) || defined(__H8300SX__) ++# define __LZO_WORDSIZE 4 ++# define LZO_SIZEOF_VOID_P 4 ++# else ++# define __LZO_WORDSIZE 2 ++# define LZO_SIZEOF_VOID_P 2 ++# endif ++# if (LZO_CC_GNUC && (LZO_CC_GNUC < 0x040000ul)) && (LZO_SIZEOF_INT == 4) ++# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_INT ++# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_INT ++# endif ++#elif (LZO_ARCH_M16C) ++# define __LZO_WORDSIZE 2 ++# if defined(__m32c_cpu__) || defined(__m32cm_cpu__) ++# define LZO_SIZEOF_VOID_P 4 ++# else ++# define LZO_SIZEOF_VOID_P 2 ++# endif ++#elif (LZO_SIZEOF_LONG == 8) && ((defined(__mips__) && defined(__R5900__)) || defined(__MIPS_PSX2__)) ++# define __LZO_WORDSIZE 8 ++# define LZO_SIZEOF_VOID_P 4 ++#elif defined(__LLP64__) || defined(__LLP64) || defined(_LLP64) || defined(_WIN64) ++# define __LZO_WORDSIZE 8 ++# define LZO_SIZEOF_VOID_P 8 ++#elif (LZO_OS_OS400) && defined(__LLP64_IFC__) ++# define LZO_SIZEOF_VOID_P LZO_SIZEOF_LONG ++# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG ++# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG ++#elif (LZO_OS_OS400) ++# define __LZO_WORDSIZE LZO_SIZEOF_LONG ++# define LZO_SIZEOF_VOID_P 16 ++# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG ++# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG ++#elif (defined(__vms) || defined(__VMS)) && (__INITIAL_POINTER_SIZE+0 == 64) ++# define LZO_SIZEOF_VOID_P 8 ++# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_LONG ++# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_LONG ++#else ++# define LZO_SIZEOF_VOID_P LZO_SIZEOF_LONG ++#endif ++#endif ++#if !defined(LZO_WORDSIZE) ++# if defined(__LZO_WORDSIZE) ++# define LZO_WORDSIZE __LZO_WORDSIZE ++# else ++# define LZO_WORDSIZE LZO_SIZEOF_VOID_P ++# endif ++#endif ++#if !defined(LZO_SIZEOF_SIZE_T) ++#if (LZO_ARCH_I086 || LZO_ARCH_M16C) ++# define LZO_SIZEOF_SIZE_T 2 ++#else ++# define LZO_SIZEOF_SIZE_T LZO_SIZEOF_VOID_P ++#endif ++#endif ++#if !defined(LZO_SIZEOF_PTRDIFF_T) ++#if (LZO_ARCH_I086) ++# if (LZO_MM_TINY || LZO_MM_SMALL || LZO_MM_MEDIUM || LZO_MM_HUGE) ++# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_VOID_P ++# elif (LZO_MM_COMPACT || LZO_MM_LARGE) ++# if (LZO_CC_BORLANDC || LZO_CC_TURBOC) ++# define LZO_SIZEOF_PTRDIFF_T 4 ++# else ++# define LZO_SIZEOF_PTRDIFF_T 2 ++# endif ++# else ++# error "LZO_MM" ++# endif ++#else ++# define LZO_SIZEOF_PTRDIFF_T LZO_SIZEOF_SIZE_T ++#endif ++#endif ++#if !defined(LZO_ABI_BIG_ENDIAN) && !defined(LZO_ABI_LITTLE_ENDIAN) && !defined(LZO_ABI_NEUTRAL_ENDIAN) ++#if (LZO_ARCH_AMD64 || LZO_ARCH_CRIS || LZO_ARCH_I086 || LZO_ARCH_I386 || LZO_ARCH_MSP430) ++# define LZO_ABI_LITTLE_ENDIAN 1 ++#elif (LZO_ARCH_M68K || LZO_ARCH_S390) ++# define LZO_ABI_BIG_ENDIAN 1 ++#elif 1 && defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) ++# define LZO_ABI_BIG_ENDIAN 1 ++#elif 1 && defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) ++# define LZO_ABI_LITTLE_ENDIAN 1 ++#elif 1 && (LZO_ARCH_MIPS) && defined(__MIPSEB__) && !defined(__MIPSEL__) ++# define LZO_ABI_BIG_ENDIAN 1 ++#elif 1 && (LZO_ARCH_MIPS) && defined(__MIPSEL__) && !defined(__MIPSEB__) ++# define LZO_ABI_LITTLE_ENDIAN 1 ++#endif ++#endif ++#if defined(LZO_ABI_BIG_ENDIAN) && defined(LZO_ABI_LITTLE_ENDIAN) ++# error "this should not happen" ++#endif ++#if defined(LZO_ABI_BIG_ENDIAN) ++# define LZO_INFO_ABI_ENDIAN "be" ++#elif defined(LZO_ABI_LITTLE_ENDIAN) ++# define LZO_INFO_ABI_ENDIAN "le" ++#elif defined(LZO_ABI_NEUTRAL_ENDIAN) ++# define LZO_INFO_ABI_ENDIAN "neutral" ++#endif ++#if (LZO_SIZEOF_INT == 1 && LZO_SIZEOF_LONG == 2 && LZO_SIZEOF_VOID_P == 2) ++# define LZO_ABI_I8LP16 1 ++# define LZO_INFO_ABI_PM "i8lp16" ++#elif (LZO_SIZEOF_INT == 2 && LZO_SIZEOF_LONG == 2 && LZO_SIZEOF_VOID_P == 2) ++# define LZO_ABI_ILP16 1 ++# define LZO_INFO_ABI_PM "ilp16" ++#elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 4 && LZO_SIZEOF_VOID_P == 4) ++# define LZO_ABI_ILP32 1 ++# define LZO_INFO_ABI_PM "ilp32" ++#elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 4 && LZO_SIZEOF_VOID_P == 8 && LZO_SIZEOF_SIZE_T == 8) ++# define LZO_ABI_LLP64 1 ++# define LZO_INFO_ABI_PM "llp64" ++#elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 8) ++# define LZO_ABI_LP64 1 ++# define LZO_INFO_ABI_PM "lp64" ++#elif (LZO_SIZEOF_INT == 8 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 8) ++# define LZO_ABI_ILP64 1 ++# define LZO_INFO_ABI_PM "ilp64" ++#elif (LZO_SIZEOF_INT == 4 && LZO_SIZEOF_LONG == 8 && LZO_SIZEOF_VOID_P == 4) ++# define LZO_ABI_IP32L64 1 ++# define LZO_INFO_ABI_PM "ip32l64" ++#endif ++#if !defined(__LZO_LIBC_OVERRIDE) ++#if defined(LZO_LIBC_NAKED) ++# define LZO_INFO_LIBC "naked" ++#elif defined(LZO_LIBC_FREESTANDING) ++# define LZO_INFO_LIBC "freestanding" ++#elif defined(LZO_LIBC_MOSTLY_FREESTANDING) ++# define LZO_INFO_LIBC "mfreestanding" ++#elif defined(LZO_LIBC_ISOC90) ++# define LZO_INFO_LIBC "isoc90" ++#elif defined(LZO_LIBC_ISOC99) ++# define LZO_INFO_LIBC "isoc99" ++#elif defined(__dietlibc__) ++# define LZO_LIBC_DIETLIBC 1 ++# define LZO_INFO_LIBC "dietlibc" ++#elif defined(_NEWLIB_VERSION) ++# define LZO_LIBC_NEWLIB 1 ++# define LZO_INFO_LIBC "newlib" ++#elif defined(__UCLIBC__) && defined(__UCLIBC_MAJOR__) && defined(__UCLIBC_MINOR__) ++# if defined(__UCLIBC_SUBLEVEL__) ++# define LZO_LIBC_UCLIBC (__UCLIBC_MAJOR__ * 0x10000L + __UCLIBC_MINOR__ * 0x100 + __UCLIBC_SUBLEVEL__) ++# else ++# define LZO_LIBC_UCLIBC 0x00090bL ++# endif ++# define LZO_INFO_LIBC "uclibc" ++#elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) ++# define LZO_LIBC_GLIBC (__GLIBC__ * 0x10000L + __GLIBC_MINOR__ * 0x100) ++# define LZO_INFO_LIBC "glibc" ++#elif (LZO_CC_MWERKS) && defined(__MSL__) ++# define LZO_LIBC_MSL __MSL__ ++# define LZO_INFO_LIBC "msl" ++#else ++# define LZO_LIBC_DEFAULT 1 ++# define LZO_INFO_LIBC "default" ++#endif ++#endif ++#if (LZO_CC_GNUC >= 0x020800ul) ++# define __lzo_gnuc_extension__ __extension__ ++#elif (LZO_CC_LLVM || LZO_CC_PATHSCALE) ++# define __lzo_gnuc_extension__ __extension__ ++#else ++# define __lzo_gnuc_extension__ ++#endif ++#if (LZO_CC_CILLY || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) ++# define lzo_alignof(e) __alignof__(e) ++#elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 700)) ++# define lzo_alignof(e) __alignof__(e) ++#elif (LZO_CC_MSC && (_MSC_VER >= 1300)) ++# define lzo_alignof(e) __alignof(e) ++#endif ++#if (LZO_CC_TURBOC && (__TURBOC__ <= 0x0295)) ++#elif defined(__cplusplus) ++# define __lzo_inline inline ++#elif (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0550)) ++# define __lzo_inline __inline ++#elif (LZO_CC_CILLY || LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE || LZO_CC_PGI) ++# define __lzo_inline __inline__ ++#elif (LZO_CC_DMC) ++# define __lzo_inline __inline ++#elif (LZO_CC_INTELC) ++# define __lzo_inline __inline ++#elif (LZO_CC_MWERKS && (__MWERKS__ >= 0x2405)) ++# define __lzo_inline __inline ++#elif (LZO_CC_MSC && (_MSC_VER >= 900)) ++# define __lzo_inline __inline ++#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) ++# define __lzo_inline inline ++#endif ++#if (LZO_CC_GNUC >= 0x030200ul) ++# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) ++#elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 450) && (LZO_OS_WIN32 || LZO_OS_WIN64)) ++# define __lzo_forceinline __forceinline ++#elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 800)) ++# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) ++#elif (LZO_CC_LLVM || LZO_CC_PATHSCALE) ++# define __lzo_forceinline __inline__ __attribute__((__always_inline__)) ++#elif (LZO_CC_MSC && (_MSC_VER >= 1200)) ++# define __lzo_forceinline __forceinline ++#endif ++#if (LZO_CC_GNUC >= 0x030200ul) ++# define __lzo_noinline __attribute__((__noinline__)) ++#elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 600) && (LZO_OS_WIN32 || LZO_OS_WIN64)) ++# define __lzo_noinline __declspec(noinline) ++#elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 800)) ++# define __lzo_noinline __attribute__((__noinline__)) ++#elif (LZO_CC_LLVM || LZO_CC_PATHSCALE) ++# define __lzo_noinline __attribute__((__noinline__)) ++#elif (LZO_CC_MSC && (_MSC_VER >= 1300)) ++# define __lzo_noinline __declspec(noinline) ++#elif (LZO_CC_MWERKS && (__MWERKS__ >= 0x3200) && (LZO_OS_WIN32 || LZO_OS_WIN64)) ++# if defined(__cplusplus) ++# else ++# define __lzo_noinline __declspec(noinline) ++# endif ++#endif ++#if (defined(__lzo_forceinline) || defined(__lzo_noinline)) && !defined(__lzo_inline) ++# error "this should not happen" ++#endif ++#if (LZO_CC_GNUC >= 0x020700ul) ++# define __lzo_noreturn __attribute__((__noreturn__)) ++#elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 450) && (LZO_OS_WIN32 || LZO_OS_WIN64)) ++# define __lzo_noreturn __declspec(noreturn) ++#elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 600) && (LZO_OS_POSIX)) ++# define __lzo_noreturn __attribute__((__noreturn__)) ++#elif (LZO_CC_LLVM || LZO_CC_PATHSCALE) ++# define __lzo_noreturn __attribute__((__noreturn__)) ++#elif (LZO_CC_MSC && (_MSC_VER >= 1200)) ++# define __lzo_noreturn __declspec(noreturn) ++#endif ++#if (LZO_CC_GNUC >= 0x030400ul) ++# define __lzo_constructor __attribute__((__constructor__,__used__)) ++#elif (LZO_CC_GNUC >= 0x020700ul) ++# define __lzo_constructor __attribute__((__constructor__)) ++#elif (LZO_CC_LLVM || LZO_CC_PATHSCALE) ++# define __lzo_constructor __attribute__((__constructor__)) ++#endif ++#if (LZO_CC_GNUC >= 0x030400ul) ++# define __lzo_destructor __attribute__((__destructor__,__used__)) ++#elif (LZO_CC_GNUC >= 0x020700ul) ++# define __lzo_destructor __attribute__((__destructor__)) ++#elif (LZO_CC_LLVM || LZO_CC_PATHSCALE) ++# define __lzo_destructor __attribute__((__destructor__)) ++#endif ++#if defined(__lzo_destructor) && !defined(__lzo_constructor) ++# error "this should not happen" ++#endif ++#if (LZO_CC_GNUC >= 0x030200ul) ++# define __lzo_likely(e) (__builtin_expect(!!(e),1)) ++# define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) ++#elif (LZO_CC_INTELC && (__INTEL_COMPILER >= 800)) ++# define __lzo_likely(e) (__builtin_expect(!!(e),1)) ++# define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) ++#elif (LZO_CC_LLVM || LZO_CC_PATHSCALE) ++# define __lzo_likely(e) (__builtin_expect(!!(e),1)) ++# define __lzo_unlikely(e) (__builtin_expect(!!(e),0)) ++#else ++# define __lzo_likely(e) (e) ++# define __lzo_unlikely(e) (e) ++#endif ++#if !defined(LZO_UNUSED) ++# if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0600)) ++# define LZO_UNUSED(var) ((void) &var) ++# elif (LZO_CC_BORLANDC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PELLESC || LZO_CC_TURBOC) ++# define LZO_UNUSED(var) if (&var) ; else ++# elif (LZO_CC_GNUC || LZO_CC_LLVM || LZO_CC_PATHSCALE) ++# define LZO_UNUSED(var) ((void) var) ++# elif (LZO_CC_MSC && (_MSC_VER < 900)) ++# define LZO_UNUSED(var) if (&var) ; else ++# elif (LZO_CC_KEILC) ++# define LZO_UNUSED(var) {extern int __lzo_unused[1-2*!(sizeof(var)>0)];} ++# elif (LZO_CC_PACIFICC) ++# define LZO_UNUSED(var) ((void) sizeof(var)) ++# elif (LZO_CC_WATCOMC) && defined(__cplusplus) ++# define LZO_UNUSED(var) ((void) var) ++# else ++# define LZO_UNUSED(var) ((void) &var) ++# endif ++#endif ++#if !defined(LZO_UNUSED_FUNC) ++# if (LZO_CC_BORLANDC && (__BORLANDC__ >= 0x0600)) ++# define LZO_UNUSED_FUNC(func) ((void) func) ++# elif (LZO_CC_BORLANDC || LZO_CC_NDPC || LZO_CC_TURBOC) ++# define LZO_UNUSED_FUNC(func) if (func) ; else ++# elif (LZO_CC_LLVM) ++# define LZO_UNUSED_FUNC(func) ((void) &func) ++# elif (LZO_CC_MSC && (_MSC_VER < 900)) ++# define LZO_UNUSED_FUNC(func) if (func) ; else ++# elif (LZO_CC_MSC) ++# define LZO_UNUSED_FUNC(func) ((void) &func) ++# elif (LZO_CC_KEILC || LZO_CC_PELLESC) ++# define LZO_UNUSED_FUNC(func) {extern int __lzo_unused[1-2*!(sizeof((int)func)>0)];} ++# else ++# define LZO_UNUSED_FUNC(func) ((void) func) ++# endif ++#endif ++#if !defined(LZO_UNUSED_LABEL) ++# if (LZO_CC_WATCOMC) && defined(__cplusplus) ++# define LZO_UNUSED_LABEL(l) switch(0) case 1:goto l ++# elif (LZO_CC_INTELC || LZO_CC_WATCOMC) ++# define LZO_UNUSED_LABEL(l) if (0) goto l ++# else ++# define LZO_UNUSED_LABEL(l) switch(0) case 1:goto l ++# endif ++#endif ++#if !defined(LZO_COMPILE_TIME_ASSERT_HEADER) ++# if (LZO_CC_AZTECC || LZO_CC_ZORTECHC) ++# define LZO_COMPILE_TIME_ASSERT_HEADER(e) extern int __lzo_cta[1-!(e)]; ++# elif (LZO_CC_DMC || LZO_CC_SYMANTECC) ++# define LZO_COMPILE_TIME_ASSERT_HEADER(e) extern int __lzo_cta[1u-2*!(e)]; ++# elif (LZO_CC_TURBOC && (__TURBOC__ == 0x0295)) ++# define LZO_COMPILE_TIME_ASSERT_HEADER(e) extern int __lzo_cta[1-!(e)]; ++# else ++# define LZO_COMPILE_TIME_ASSERT_HEADER(e) extern int __lzo_cta[1-2*!(e)]; ++# endif ++#endif ++#if !defined(LZO_COMPILE_TIME_ASSERT) ++# if (LZO_CC_AZTECC) ++# define LZO_COMPILE_TIME_ASSERT(e) {typedef int __lzo_cta_t[1-!(e)];} ++# elif (LZO_CC_DMC || LZO_CC_PACIFICC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) ++# define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; ++# elif (LZO_CC_MSC && (_MSC_VER < 900)) ++# define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; ++# elif (LZO_CC_TURBOC && (__TURBOC__ == 0x0295)) ++# define LZO_COMPILE_TIME_ASSERT(e) switch(0) case 1:case !(e):break; ++# else ++# define LZO_COMPILE_TIME_ASSERT(e) {typedef int __lzo_cta_t[1-2*!(e)];} ++# endif ++#endif ++#if (LZO_ARCH_I086 || LZO_ARCH_I386) && (LZO_OS_DOS16 || LZO_OS_DOS32 || LZO_OS_OS2 || LZO_OS_OS216 || LZO_OS_WIN16 || LZO_OS_WIN32 || LZO_OS_WIN64) ++# if (LZO_CC_GNUC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PACIFICC) ++# elif (LZO_CC_DMC || LZO_CC_SYMANTECC || LZO_CC_ZORTECHC) ++# define __lzo_cdecl __cdecl ++# define __lzo_cdecl_atexit ++# define __lzo_cdecl_main __cdecl ++# if (LZO_OS_OS2 && (LZO_CC_DMC || LZO_CC_SYMANTECC)) ++# define __lzo_cdecl_qsort __pascal ++# elif (LZO_OS_OS2 && (LZO_CC_ZORTECHC)) ++# define __lzo_cdecl_qsort _stdcall ++# else ++# define __lzo_cdecl_qsort __cdecl ++# endif ++# elif (LZO_CC_WATCOMC) ++# define __lzo_cdecl __cdecl ++# else ++# define __lzo_cdecl __cdecl ++# define __lzo_cdecl_atexit __cdecl ++# define __lzo_cdecl_main __cdecl ++# define __lzo_cdecl_qsort __cdecl ++# endif ++# if (LZO_CC_GNUC || LZO_CC_HIGHC || LZO_CC_NDPC || LZO_CC_PACIFICC || LZO_CC_WATCOMC) ++# elif (LZO_OS_OS2 && (LZO_CC_DMC || LZO_CC_SYMANTECC)) ++# define __lzo_cdecl_sighandler __pascal ++# elif (LZO_OS_OS2 && (LZO_CC_ZORTECHC)) ++# define __lzo_cdecl_sighandler _stdcall ++# elif (LZO_CC_MSC && (_MSC_VER >= 1400)) && defined(_M_CEE_PURE) ++# define __lzo_cdecl_sighandler __clrcall ++# elif (LZO_CC_MSC && (_MSC_VER >= 600 && _MSC_VER < 700)) ++# if defined(_DLL) ++# define __lzo_cdecl_sighandler _far _cdecl _loadds ++# elif defined(_MT) ++# define __lzo_cdecl_sighandler _far _cdecl ++# else ++# define __lzo_cdecl_sighandler _cdecl ++# endif ++# else ++# define __lzo_cdecl_sighandler __cdecl ++# endif ++#elif (LZO_ARCH_M68K && LZO_OS_TOS && (LZO_CC_PUREC || LZO_CC_TURBOC)) ++# define __lzo_cdecl cdecl ++#endif ++#if !defined(__lzo_cdecl) ++# define __lzo_cdecl ++#endif ++#if !defined(__lzo_cdecl_atexit) ++# define __lzo_cdecl_atexit ++#endif ++#if !defined(__lzo_cdecl_main) ++# define __lzo_cdecl_main ++#endif ++#if !defined(__lzo_cdecl_qsort) ++# define __lzo_cdecl_qsort ++#endif ++#if !defined(__lzo_cdecl_sighandler) ++# define __lzo_cdecl_sighandler ++#endif ++#if !defined(__lzo_cdecl_va) ++# define __lzo_cdecl_va __lzo_cdecl ++#endif ++#if (LZO_OS_CYGWIN || (LZO_OS_EMX && defined(__RSXNT__)) || LZO_OS_WIN32 || LZO_OS_WIN64) ++# if (LZO_CC_WATCOMC && (__WATCOMC__ < 1000)) ++# elif (LZO_OS_WIN32 && LZO_CC_GNUC) && defined(__PW32__) ++# elif ((LZO_OS_CYGWIN || defined(__MINGW32__)) && (LZO_CC_GNUC && (LZO_CC_GNUC < 0x025f00ul))) ++# else ++# define LZO_HAVE_WINDOWS_H 1 ++# endif ++#endif ++#if (LZO_ARCH_ALPHA) ++# define LZO_OPT_AVOID_UINT_INDEX 1 ++# define LZO_OPT_AVOID_SHORT 1 ++# define LZO_OPT_AVOID_USHORT 1 ++#elif (LZO_ARCH_AMD64) ++# define LZO_OPT_AVOID_INT_INDEX 1 ++# define LZO_OPT_AVOID_UINT_INDEX 1 ++# define LZO_OPT_UNALIGNED16 1 ++# define LZO_OPT_UNALIGNED32 1 ++# define LZO_OPT_UNALIGNED64 1 ++#elif (LZO_ARCH_ARM && LZO_ARCH_ARM_THUMB) ++#elif (LZO_ARCH_ARM) ++# define LZO_OPT_AVOID_SHORT 1 ++# define LZO_OPT_AVOID_USHORT 1 ++#elif (LZO_ARCH_CRIS) ++# define LZO_OPT_UNALIGNED16 1 ++# define LZO_OPT_UNALIGNED32 1 ++#elif (LZO_ARCH_I386) ++# define LZO_OPT_UNALIGNED16 1 ++# define LZO_OPT_UNALIGNED32 1 ++#elif (LZO_ARCH_IA64) ++# define LZO_OPT_AVOID_INT_INDEX 1 ++# define LZO_OPT_AVOID_UINT_INDEX 1 ++# define LZO_OPT_PREFER_POSTINC 1 ++#elif (LZO_ARCH_M68K) ++# define LZO_OPT_PREFER_POSTINC 1 ++# define LZO_OPT_PREFER_PREDEC 1 ++# if defined(__mc68020__) && !defined(__mcoldfire__) ++# define LZO_OPT_UNALIGNED16 1 ++# define LZO_OPT_UNALIGNED32 1 ++# endif ++#elif (LZO_ARCH_MIPS) ++# define LZO_OPT_AVOID_UINT_INDEX 1 ++#elif (LZO_ARCH_POWERPC) ++# define LZO_OPT_PREFER_PREINC 1 ++# define LZO_OPT_PREFER_PREDEC 1 ++# if defined(LZO_ABI_BIG_ENDIAN) ++# define LZO_OPT_UNALIGNED16 1 ++# define LZO_OPT_UNALIGNED32 1 ++# endif ++#elif (LZO_ARCH_S390) ++# define LZO_OPT_UNALIGNED16 1 ++# define LZO_OPT_UNALIGNED32 1 ++# if (LZO_SIZEOF_SIZE_T == 8) ++# define LZO_OPT_UNALIGNED64 1 ++# endif ++#elif (LZO_ARCH_SH) ++# define LZO_OPT_PREFER_POSTINC 1 ++# define LZO_OPT_PREFER_PREDEC 1 ++#endif ++#if !defined(LZO_CFG_NO_INLINE_ASM) ++#if defined(LZO_CC_LLVM) ++# define LZO_CFG_NO_INLINE_ASM 1 ++#endif ++#endif ++#if !defined(LZO_CFG_NO_UNALIGNED) ++#if defined(LZO_ABI_NEUTRAL_ENDIAN) || defined(LZO_ARCH_GENERIC) ++# define LZO_CFG_NO_UNALIGNED 1 ++#endif ++#endif ++#if defined(LZO_CFG_NO_UNALIGNED) ++# undef LZO_OPT_UNALIGNED16 ++# undef LZO_OPT_UNALIGNED32 ++# undef LZO_OPT_UNALIGNED64 ++#endif ++#if defined(LZO_CFG_NO_INLINE_ASM) ++#elif (LZO_ARCH_I386 && (LZO_OS_DOS32 || LZO_OS_WIN32) && (LZO_CC_DMC || LZO_CC_INTELC || LZO_CC_MSC || LZO_CC_PELLESC)) ++# define LZO_ASM_SYNTAX_MSC 1 ++#elif (LZO_OS_WIN64 && (LZO_CC_DMC || LZO_CC_INTELC || LZO_CC_MSC || LZO_CC_PELLESC)) ++#elif (LZO_ARCH_I386 && (LZO_CC_GNUC || LZO_CC_INTELC || LZO_CC_PATHSCALE)) ++# define LZO_ASM_SYNTAX_GNUC 1 ++#elif (LZO_ARCH_AMD64 && (LZO_CC_GNUC || LZO_CC_INTELC || LZO_CC_PATHSCALE)) ++# define LZO_ASM_SYNTAX_GNUC 1 ++#endif ++#if (LZO_ASM_SYNTAX_GNUC) ++#if (LZO_ARCH_I386 && LZO_CC_GNUC && (LZO_CC_GNUC < 0x020000ul)) ++# define __LZO_ASM_CLOBBER "ax" ++#elif (LZO_CC_INTELC) ++# define __LZO_ASM_CLOBBER "memory" ++#else ++# define __LZO_ASM_CLOBBER "cc", "memory" ++#endif ++#endif ++#if defined(__LZO_INFOSTR_MM) ++#elif (LZO_MM_FLAT) && (defined(__LZO_INFOSTR_PM) || defined(LZO_INFO_ABI_PM)) ++# define __LZO_INFOSTR_MM "" ++#elif defined(LZO_INFO_MM) ++# define __LZO_INFOSTR_MM "." LZO_INFO_MM ++#else ++# define __LZO_INFOSTR_MM "" ++#endif ++#if defined(__LZO_INFOSTR_PM) ++#elif defined(LZO_INFO_ABI_PM) ++# define __LZO_INFOSTR_PM "." LZO_INFO_ABI_PM ++#else ++# define __LZO_INFOSTR_PM "" ++#endif ++#if defined(__LZO_INFOSTR_ENDIAN) ++#elif defined(LZO_INFO_ABI_ENDIAN) ++# define __LZO_INFOSTR_ENDIAN "." LZO_INFO_ABI_ENDIAN ++#else ++# define __LZO_INFOSTR_ENDIAN "" ++#endif ++#if defined(__LZO_INFOSTR_OSNAME) ++#elif defined(LZO_INFO_OS_CONSOLE) ++# define __LZO_INFOSTR_OSNAME LZO_INFO_OS "." LZO_INFO_OS_CONSOLE ++#elif defined(LZO_INFO_OS_POSIX) ++# define __LZO_INFOSTR_OSNAME LZO_INFO_OS "." LZO_INFO_OS_POSIX ++#else ++# define __LZO_INFOSTR_OSNAME LZO_INFO_OS ++#endif ++#if defined(__LZO_INFOSTR_LIBC) ++#elif defined(LZO_INFO_LIBC) ++# define __LZO_INFOSTR_LIBC "." LZO_INFO_LIBC ++#else ++# define __LZO_INFOSTR_LIBC "" ++#endif ++#if defined(__LZO_INFOSTR_CCVER) ++#elif defined(LZO_INFO_CCVER) ++# define __LZO_INFOSTR_CCVER " " LZO_INFO_CCVER ++#else ++# define __LZO_INFOSTR_CCVER "" ++#endif ++#define LZO_INFO_STRING \ ++ LZO_INFO_ARCH __LZO_INFOSTR_MM __LZO_INFOSTR_PM __LZO_INFOSTR_ENDIAN \ ++ " " __LZO_INFOSTR_OSNAME __LZO_INFOSTR_LIBC " " LZO_INFO_CC __LZO_INFOSTR_CCVER ++ ++#endif /* already included */ ++ ++/* vim:set ts=4 et: */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoutil.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoutil.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoutil.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoutil.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,69 @@ ++/* lzoutil.h -- utilitiy functions for use by applications [DEPRECATED] ++ ++ This file is part of the LZO real-time data compression library. ++ ++ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer ++ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer ++ All Rights Reserved. ++ ++ The LZO library 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. ++ ++ The LZO library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with the LZO library; see the file COPYING. ++ If not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ ++ Markus F.X.J. Oberhumer ++ ++ http://www.oberhumer.com/opensource/lzo/ ++ */ ++ ++ ++#ifndef __LZOUTIL_H_INCLUDED ++#define __LZOUTIL_H_INCLUDED ++ ++#ifndef __LZOCONF_H_INCLUDED ++#include "lzoconf.h" ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/*********************************************************************** ++// LZO-v1 deprecated macros (which were used in the old example programs) ++// DO NOT USE ++************************************************************************/ ++ ++#define lzo_alloc(a,b) (malloc((a)*(b))) ++#define lzo_malloc(a) (malloc(a)) ++#define lzo_free(a) (free(a)) ++ ++#define lzo_fread(f,b,s) (fread(b,1,s,f)) ++#define lzo_fwrite(f,b,s) (fwrite(b,1,s,f)) ++ ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* already included */ ++ ++ ++/* vim:set ts=4 et: */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/lib/liblzo2.la linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/lib/liblzo2.la +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/lzo/lib/liblzo2.la 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/lzo/lib/liblzo2.la 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,35 @@ ++# liblzo2.la - a libtool library file ++# Generated by ltmain.sh - GNU libtool 1.5.6 (1.1220.2.95 2004/04/11 05:50:42) Debian: 220 $ ++# ++# Please DO NOT delete this file! ++# It is necessary for linking the library. ++ ++# The name that we can dlopen(3). ++dlname='liblzo2.so.2' ++ ++# Names of this library. ++library_names='liblzo2.so.2.0.0 liblzo2.so.2 liblzo2.so' ++ ++# The name of the static archive. ++old_library='liblzo2.a' ++ ++# Libraries that this one depends upon. ++dependency_libs='' ++ ++# Version information for liblzo2. ++current=2 ++age=0 ++revision=0 ++ ++# Is this an already installed library? ++installed=yes ++ ++# Should we warn about portability when linking against -modules? ++shouldnotlink=no ++ ++# Files to dlopen/dlpreopen ++dlopen='' ++dlpreopen='' ++ ++# Directory that this library needs to be installed in: ++libdir='/home/yrtan/lzo/lib' +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/mkfs.ubifs.c linux-2.6.24.7/fs/ubifs/mkfs.ubifs/mkfs.ubifs.c +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/mkfs.ubifs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/mkfs.ubifs.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,2163 @@ ++/* ++ * Copyright (C) 2008 Nokia Corporation. ++ * Copyright (C) 2008 University of Szeged, Hungary ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy ++ * Zoltan Sogor ++ */ ++ ++#include "mkfs.ubifs.h" ++ ++#define PROGRAM_VERSION "0.6" ++ ++/* Size (prime number) of hash table for link counting */ ++#define HASH_TABLE_SIZE 10099 ++ ++/* The node buffer must allow for worst case compression */ ++#define NODE_BUFFER_SIZE (UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE * 4) ++ ++/* Default time granularity in nanoseconds */ ++#define DEFAULT_TIME_GRAN 1000000000 ++ ++/* UBI EC and VID header sizes - used when calculating LEB size from PRB size */ ++#define UBI_EC_HDR_SIZE 64 ++#define UBI_VID_HDR_SIZE 64 ++ ++/** ++ * struct idx_entry - index entry. ++ * @next: next index entry (NULL at end of list) ++ * @prev: previous index entry (NULL at beginning of list) ++ * @key: key ++ * @name: directory entry name used for sorting colliding keys by name ++ * @lnum: LEB number ++ * @offs: offset ++ * @len: length ++ * ++ * The index is recorded as a linked list which is sorted and used to create ++ * the bottom level of the on-flash index tree. The remaining levels of the ++ * index tree are each built from the level below. ++ */ ++struct idx_entry { ++ struct idx_entry *next; ++ struct idx_entry *prev; ++ union ubifs_key key; ++ char *name; ++ int lnum; ++ int offs; ++ int len; ++}; ++ ++/** ++ * struct inum_mapping - inode number mapping for link counting. ++ * @next: next inum_mapping (NULL at end of list) ++ * @prev: previous inum_mapping (NULL at beginning of list) ++ * @dev: source device on which the source inode number resides ++ * @inum: source inode number of the file ++ * @use_inum: target inode number of the file ++ * @use_nlink: number of links ++ * @path_name: a path name of the file ++ * @st: struct stat object containing inode attributes which have to be used ++ * when the inode is being created (actually only UID, GID, access ++ * mode, major and minor device numbers) ++ * ++ * If a file has more than one hard link, then the number of hard links that ++ * exist in the source directory hierarchy must be counted to exclude the ++ * possibility that the file is linked from outside the source directory ++ * hierarchy. ++ * ++ * The inum_mappings are stored in a hash_table of linked lists. ++ */ ++struct inum_mapping { ++ struct inum_mapping *next; ++ struct inum_mapping *prev; ++ dev_t dev; ++ ino_t inum; ++ ino_t use_inum; ++ unsigned int use_nlink; ++ char *path_name; ++ struct stat st; ++}; ++ ++/* ++ * Because we copy functions from the kernel, we use a subset of the UBIFS ++ * file-system description object struct ubifs_info. ++ */ ++static struct ubifs_info info_, *c = &info_; ++ ++/* Debug levels are: 0 (none), 1 (statistics), 2 (files) ,3 (more details) */ ++int debug_level; ++int verbose; ++ ++static char *root; ++static int root_len; ++static struct stat root_st; ++static char *output; ++static int out_fd; ++static int squash_owner; ++ ++/* The 'head' (position) which nodes are written */ ++static int head_lnum; ++static int head_offs; ++static int head_flags; ++ ++/* The index list */ ++static struct idx_entry *idx_list_first; ++static struct idx_entry *idx_list_last; ++static size_t idx_cnt; ++ ++/* Global buffers */ ++static void *leb_buf; ++static void *node_buf; ++static void *block_buf; ++ ++/* Hash table for inode link counting */ ++static struct inum_mapping **hash_table; ++ ++/* Inode creation sequence number */ ++static unsigned long long creat_sqnum; ++ ++static const char *optstring = "d:r:m:o:D:h?vVe:c:g:f:P:k:x:j:l:j:U"; ++ ++static const struct option longopts[] = { ++ {"root", 1, NULL, 'r'}, ++ {"min-io-size", 1, NULL, 'm'}, ++ {"leb-size", 1, NULL, 'e'}, ++ {"max-leb-cnt", 1, NULL, 'c'}, ++ {"output", 1, NULL, 'o'}, ++ {"devtable", 1, NULL, 'D'}, ++ {"help", 0, NULL, 'h'}, ++ {"verbose", 0, NULL, 'v'}, ++ {"version", 0, NULL, 'V'}, ++ {"debug-level", 1, NULL, 'g'}, ++ {"jrn-size", 1, NULL, 'j'}, ++ {"compr", 1, NULL, 'x'}, ++ {"fanout", 1, NULL, 'f'}, ++ {"keyhash", 1, NULL, 'k'}, ++ {"log-lebs", 1, NULL, 'l'}, ++ {"orph-lebs", 1, NULL, 'p'}, ++ {"squash-uids" , 0, NULL, 'U'}, ++ {NULL, 0, NULL, 0} ++}; ++ ++static const char *helptext = ++"Usage: mkfs.ubifs [OPTIONS]\n" ++"Make a UBIFS file system image from an existing directory tree\n\n" ++"Options:\n" ++"-r, -d, --root=DIR build file system from directory DIR\n" ++"-m, --min-io-size=SIZE minimum I/O unit size\n" ++"-e, --leb-size=SIZE logical erase block size\n" ++"-c, --max-leb-cnt=COUNT maximum logical erase block count\n" ++"-o, --output=FILE output to FILE\n" ++"-j, --jrn-size=SIZE journal size\n" ++"-x, --compr=TYPE compression type - \"lzo\", \"zlib\" or \"none\"\n" ++" (default: \"lzo\")\n" ++"-f, --fanout=NUM fanout NUM (default: 8)\n" ++"-k, --keyhash=TYPE key hash type - \"r5\" or \"test\" (default: \"r5\")\n" ++"-l, --log-lebs=COUNT count of erase blocks for the log\n" ++"-p, --orph-lebs=COUNT count of erase blocks for orphans (default: 1)\n" ++"-D, --devtable=FILE use device table FILE\n" ++"-U, --squash-uids squash owners making all files owned by root\n" ++"-v, --verbose verbose operation\n" ++"-V, --version display version information\n" ++"-g, --debug=LEVEL display debug information (0 - none, 1 - statistics,\n" ++" 2 - files, 3 - more details)\n" ++"-h, --help display this help text\n\n" ++"Note, SIZE is specified in bytes, but it may also be specified in Kilobytes,\n" ++"Megabytes, and Gigabytes if a KiB, MiB, or GiB suffix is used.\n"; ++ ++/** ++ * make_path - make a path name from a directory and a name. ++ * @dir: directory path name ++ * @name: name ++ */ ++static char *make_path(const char *dir, const char *name) ++{ ++ char *s; ++ ++ s = malloc(strlen(dir) + strlen(name) + 2); ++ if (!s) ++ return NULL; ++ strcpy(s, dir); ++ if (dir[strlen(dir) - 1] != '/') ++ strcat(s, "/"); ++ strcat(s, name); ++ return s; ++} ++ ++/** ++ * same_dir - determine if two file descriptors refer to the same directory. ++ * @fd1: file descriptor 1 ++ * @fd2: file descriptor 2 ++ */ ++static int same_dir(int fd1, int fd2) ++{ ++ struct stat stat1, stat2; ++ ++ if (fstat(fd1, &stat1) == -1) ++ return -1; ++ if (fstat(fd2, &stat2) == -1) ++ return -1; ++ return stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino; ++} ++ ++/** ++ * do_openat - open a file in a directory. ++ * @fd: file descriptor of open directory ++ * @path: path relative to directory ++ * @flags: open flags ++ * ++ * This function is provided because the library function openat is sometimes ++ * not available. ++ */ ++static int do_openat(int fd, const char *path, int flags) ++{ ++ int ret; ++ char *cwd; ++ ++ cwd = getcwd(NULL, 0); ++ if (!cwd) ++ return -1; ++ ret = fchdir(fd); ++ if (ret != -1) ++ ret = open(path, flags); ++ chdir(cwd); ++ free(cwd); ++ return ret; ++} ++ ++/** ++ * in_path - determine if a file is beneath a directory. ++ * @dir_name: directory path name ++ * @file_name: file path name ++ */ ++static int in_path(const char *dir_name, const char *file_name) ++{ ++ char *fn = strdup(file_name); ++ char *dn; ++ int fd1, fd2, fd3, ret = -1, top_fd; ++ ++ if (!fn) ++ return -1; ++ top_fd = open("/", O_RDONLY); ++ if (top_fd != -1) { ++ dn = dirname(fn); ++ fd1 = open(dir_name, O_RDONLY); ++ if (fd1 != -1) { ++ fd2 = open(dn, O_RDONLY); ++ if (fd2 != -1) { ++ while (1) { ++ int same; ++ ++ same = same_dir(fd1, fd2); ++ if (same) { ++ ret = same; ++ break; ++ } ++ if (same_dir(fd2, top_fd)) { ++ ret = 0; ++ break; ++ } ++ fd3 = do_openat(fd2, "..", O_RDONLY); ++ if (fd3 == -1) ++ break; ++ close(fd2); ++ fd2 = fd3; ++ } ++ close(fd2); ++ } ++ close(fd1); ++ } ++ close(top_fd); ++ } ++ free(fn); ++ return ret; ++} ++ ++/** ++ * calc_min_log_lebs - calculate the minimum number of log LEBs needed. ++ * @max_bud_bytes: journal size (buds only) ++ */ ++static int calc_min_log_lebs(unsigned long long max_bud_bytes) ++{ ++ int buds, log_lebs; ++ unsigned long long log_size; ++ ++ buds = (max_bud_bytes + c->leb_size - 1) / c->leb_size; ++ log_size = ALIGN(UBIFS_REF_NODE_SZ, c->min_io_size); ++ log_size *= buds; ++ log_size += ALIGN(UBIFS_CS_NODE_SZ + ++ UBIFS_REF_NODE_SZ * (c->jhead_cnt + 2), ++ c->min_io_size); ++ log_lebs = (log_size + c->leb_size - 1) / c->leb_size; ++ log_lebs += 1; ++ return log_lebs; ++} ++ ++static inline int is_power_of_2(unsigned long long n) ++{ ++ return (n != 0 && ((n & (n - 1)) == 0)); ++} ++ ++static int validate_options(void) ++{ ++ int tmp; ++ ++ if (!root) ++ return err_msg("root directory was not specified"); ++ if (!output) ++ return err_msg("no output file specified"); ++ if (in_path(root, output)) ++ return err_msg("output file cannot be in the UBIFS root " ++ "directory"); ++ if (!is_power_of_2(c->min_io_size)) ++ return err_msg("min. I/O unit size should be power of 2"); ++ if (c->leb_size < c->min_io_size) ++ return err_msg("min. I/O unit cannot be larger than LEB size"); ++ if (c->leb_size < UBIFS_MIN_LEB_SZ) ++ return err_msg("too smal LEB size %d, minimum is %d", ++ c->min_io_size, UBIFS_MIN_LEB_SZ); ++ if (c->leb_size % c->min_io_size) ++ return err_msg("LEB should be multiple of min. I/O units"); ++ if (c->leb_size % 8) ++ return err_msg("LEB size has to be multiple of 8"); ++ if (c->leb_size > 1024*1024) ++ return err_msg("too large LEB size %d", c->leb_size); ++ if (c->max_leb_cnt < UBIFS_MIN_LEB_CNT) ++ return err_msg("too low max. count of LEBs, minimum is %d", ++ UBIFS_MIN_LEB_CNT); ++ if (c->fanout < UBIFS_MIN_FANOUT) ++ return err_msg("too low fanout, minimum is %d", ++ UBIFS_MIN_FANOUT); ++ tmp = c->leb_size - UBIFS_IDX_NODE_SZ; ++ tmp /= UBIFS_BRANCH_SZ + UBIFS_MAX_KEY_LEN; ++ if (c->fanout > tmp) ++ return err_msg("too high fanout, maximum is %d", tmp); ++ if (c->log_lebs < UBIFS_MIN_LOG_LEBS) ++ return err_msg("too few log LEBs, minimum is %d", ++ UBIFS_MIN_LOG_LEBS); ++ if (c->log_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) ++ return err_msg("too many log LEBs, maximum is %d", ++ c->max_leb_cnt - UBIFS_MIN_LEB_CNT); ++ if (c->orph_lebs < UBIFS_MIN_ORPH_LEBS) ++ return err_msg("too few orphan LEBs, minimum is %d", ++ UBIFS_MIN_ORPH_LEBS); ++ if (c->orph_lebs >= c->max_leb_cnt - UBIFS_MIN_LEB_CNT) ++ return err_msg("too many orphan LEBs, maximum is %d", ++ c->max_leb_cnt - UBIFS_MIN_LEB_CNT); ++ tmp = UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs + c->lpt_lebs; ++ tmp += c->orph_lebs + 4; ++ if (tmp > c->max_leb_cnt) ++ return err_msg("too low max. count of LEBs, expected at " ++ "least %d", tmp); ++ tmp = calc_min_log_lebs(c->max_bud_bytes); ++ if (c->log_lebs < calc_min_log_lebs(c->max_bud_bytes)) ++ return err_msg("too few log LEBs, expected at least %d", ++ tmp); ++ return 0; ++} ++ ++/** ++ * get_multiplier - convert size specifier to an integer multiplier. ++ * @str: the size specifier string ++ * ++ * This function parses the @str size specifier, which may be one of ++ * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive ++ * size multiplier in case of success and %-1 in case of failure. ++ */ ++static int get_multiplier(const char *str) ++{ ++ if (!str) ++ return 1; ++ ++ /* Remove spaces before the specifier */ ++ while (*str == ' ' || *str == '\t') ++ str += 1; ++ ++ if (!strcmp(str, "KiB")) ++ return 1024; ++ if (!strcmp(str, "MiB")) ++ return 1024 * 1024; ++ if (!strcmp(str, "GiB")) ++ return 1024 * 1024 * 1024; ++ ++ return -1; ++} ++ ++/** ++ * get_bytes - convert a string containing amount of bytes into an ++ * integer. ++ * @str: string to convert ++ * ++ * This function parses @str which may have one of 'KiB', 'MiB', or 'GiB' size ++ * specifiers. Returns positive amount of bytes in case of success and %-1 in ++ * case of failure. ++ */ ++static long long get_bytes(const char *str) ++{ ++ char *endp; ++ long long bytes = strtoull(str, &endp, 0); ++ ++ if (endp == str || bytes < 0) ++ return err_msg("incorrect amount of bytes: \"%s\"", str); ++ ++ if (*endp != '\0') { ++ int mult = get_multiplier(endp); ++ ++ if (mult == -1) ++ return err_msg("bad size specifier: \"%s\" - " ++ "should be 'KiB', 'MiB' or 'GiB'", endp); ++ bytes *= mult; ++ } ++ ++ return bytes; ++} ++ ++static int get_options(int argc, char**argv) ++{ ++ int opt, i; ++ const char *tbl_file = NULL; ++ struct stat st; ++ char *endp; ++ ++ c->fanout = 8; ++ c->orph_lebs = 1; ++ c->key_hash = key_r5_hash; ++ c->key_len = UBIFS_SK_LEN; ++ c->default_compr = UBIFS_COMPR_LZO; ++ c->lsave_cnt = 256; ++ c->leb_size = -1; ++ c->min_io_size = -1; ++ c->max_bud_bytes = -1; ++ c->log_lebs = -1; ++ ++ while (1) { ++ opt = getopt_long(argc, argv, optstring, longopts, &i); ++ if (opt == -1) ++ break; ++ switch (opt) { ++ case 'r': ++ case 'd': ++ root_len = strlen(optarg); ++ root = malloc(root_len + 2); ++ if (!root) ++ return err_msg("cannot allocate memory"); ++ ++ /* ++ * The further code expects '/' at the end of the root ++ * UBIFS directory on the host. ++ */ ++ memcpy(root, optarg, root_len); ++ if (root[root_len - 1] != '/') ++ root[root_len++] = '/'; ++ root[root_len] = 0; ++ ++ /* Make sure the root directory exists */ ++ if (stat(root, &st)) ++ return sys_err_msg("bad root directory '%s'", ++ root); ++ break; ++ case 'm': ++ c->min_io_size = get_bytes(optarg); ++ if (c->min_io_size <= 0) ++ return err_msg("bad min. I/O size"); ++ break; ++ case 'e': ++ c->leb_size = get_bytes(optarg); ++ if (c->leb_size <= 0) ++ return err_msg("bad LEB size"); ++ break; ++ case 'c': ++ c->max_leb_cnt = get_bytes(optarg); ++ if (c->max_leb_cnt <= 0) ++ return err_msg("bad maximum LEB count"); ++ break; ++ case 'o': ++ output = strdup(optarg); ++ break; ++ case 'D': ++ tbl_file = optarg; ++ if (stat(tbl_file, &st) < 0) ++ return sys_err_msg("bad device table file '%s'", ++ tbl_file); ++ break; ++ case 'h': ++ case '?': ++ printf(helptext); ++ exit(0); ++ case 'v': ++ verbose = 1; ++ break; ++ case 'V': ++ printf("Version " PROGRAM_VERSION "\n"); ++ exit(0); ++ case 'g': ++ debug_level = strtol(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || ++ debug_level < 0 || debug_level > 3) ++ return err_msg("bad debugging level '%s'", ++ optarg); ++ break; ++ case 'f': ++ c->fanout = strtol(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || c->fanout <= 0) ++ return err_msg("bad fanout %s", optarg); ++ break; ++ case 'l': ++ c->log_lebs = strtol(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || c->log_lebs <= 0) ++ return err_msg("bad count of log LEBs '%s'", ++ optarg); ++ break; ++ case 'p': ++ c->orph_lebs = strtol(optarg, &endp, 0); ++ if (*endp != '\0' || endp == optarg || ++ c->orph_lebs <= 0) ++ return err_msg("bad orphan LEB count '%s'", ++ optarg); ++ break; ++ case 'k': ++ if (strcmp(optarg, "r5") == 0) { ++ c->key_hash = key_r5_hash; ++ c->key_hash_type = UBIFS_KEY_HASH_R5; ++ } else if (strcmp(optarg, "test") == 0) { ++ c->key_hash = key_test_hash; ++ c->key_hash_type = UBIFS_KEY_HASH_TEST; ++ } else ++ return err_msg("bad key hash"); ++ break; ++ case 'x': ++ if (strcmp(optarg, "lzo") == 0) ++ c->default_compr = UBIFS_COMPR_LZO; ++ else if (strcmp(optarg, "zlib") == 0) ++ c->default_compr = UBIFS_COMPR_ZLIB; ++ else if (strcmp(optarg, "none") == 0) ++ c->default_compr = UBIFS_COMPR_NONE; ++ else ++ return err_msg("bad compressor name"); ++ break; ++ case 'j': ++ c->max_bud_bytes = get_bytes(optarg); ++ if (c->max_bud_bytes <= 0) ++ return err_msg("bad maximum amount of buds"); ++ break; ++ case 'U': ++ squash_owner = 1; ++ break; ++ } ++ } ++ ++ if (c->min_io_size == -1) ++ return err_msg("min. I/O unit was not specified " ++ "(use -h for help)"); ++ ++ if (c->leb_size == -1) ++ return err_msg("LEB size was not specified (use -h for help)"); ++ ++ if (c->max_bud_bytes == -1) { ++ int lebs; ++ ++ lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; ++ lebs -= c->orph_lebs; ++ if (c->log_lebs) ++ lebs -= c->log_lebs; ++ else ++ lebs -= UBIFS_MIN_LOG_LEBS; ++ if (c->lpt_lebs) ++ lebs -= c->lpt_lebs; ++ else ++ lebs -= UBIFS_MIN_LPT_LEBS; ++ /* Make the journal about 12.5% of main area lebs */ ++ c->max_bud_bytes = (lebs / 8) * (long long)c->leb_size; ++ /* Make the max journal size 8MiB */ ++ if (c->max_bud_bytes > 8 * 1024 * 1024) ++ c->max_bud_bytes = 8 * 1024 * 1024; ++ if (c->max_bud_bytes < 4 * c->leb_size) ++ c->max_bud_bytes = 4 * c->leb_size; ++ } ++ ++ if (c->log_lebs == -1) { ++ c->log_lebs = calc_min_log_lebs(c->max_bud_bytes); ++ c->log_lebs += 2; ++ } ++ ++ if (verbose) { ++ printf("mkfs.ubifs\n"); ++ printf("\troot: %s\n", root); ++ printf("\tmin_io_size: %d\n", c->min_io_size); ++ printf("\tleb_size: %d\n", c->leb_size); ++ printf("\tmax_leb_cnt: %d\n", c->max_leb_cnt); ++ printf("\toutput: %s\n", output); ++ printf("\tjrn_size: %llu\n", c->max_bud_bytes); ++ switch (c->default_compr) { ++ case UBIFS_COMPR_LZO: ++ printf("\tcompr: lzo\n"); ++ break; ++ case UBIFS_COMPR_ZLIB: ++ printf("\tcompr: zlib\n"); ++ break; ++ case UBIFS_COMPR_NONE: ++ printf("\tcompr: none\n"); ++ break; ++ } ++ printf("\tkeyhash: %s\n", (c->key_hash == key_r5_hash) ? ++ "r5" : "test"); ++ printf("\tfanout: %d\n", c->fanout); ++ printf("\torph_lebs: %d\n", c->orph_lebs); ++ } ++ ++ if (c->min_io_size < 8) ++ c->min_io_size = 8; ++ ++ if (validate_options()) ++ return -1; ++ ++ if (tbl_file && parse_devtable(root, tbl_file)) ++ return err_msg("cannot parse device table file '%s'", tbl_file); ++ ++ return 0; ++} ++ ++/** ++ * prepare_node - fill in the common header. ++ * @node: node ++ * @len: node length ++ */ ++static void prepare_node(void *node, int len) ++{ ++ uint32_t crc; ++ struct ubifs_ch *ch = node; ++ ++ ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); ++ ch->len = cpu_to_le32(len); ++ ch->group_type = UBIFS_NO_NODE_GROUP; ++ ch->sqnum = cpu_to_le64(++c->max_sqnum); ++ ch->padding[0] = ch->padding[1] = 0; ++ crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8); ++ ch->crc = cpu_to_le32(crc); ++} ++ ++/** ++ * write_leb - copy the image of a LEB to the output file. ++ * @lnum: LEB number ++ * @len: length of data in the buffer ++ * @buf: buffer (must be at least c->leb_size bytes) ++ */ ++int write_leb(int lnum, int len, void *buf) ++{ ++ off64_t pos = (off64_t)lnum * c->leb_size; ++ ++ dbg_msg(3, "LEB %d len %d", lnum, len); ++ if (lseek64(out_fd, pos, SEEK_SET) != pos) ++ return sys_err_msg("lseek64 failed seeking %lld", pos); ++ ++ memset(buf + len, 0xff, c->leb_size - len); ++ ++ if (write(out_fd, buf, c->leb_size) != c->leb_size) ++ return sys_err_msg("write failed writing %d bytes at pos %lld", ++ c->leb_size, pos); ++ ++ return 0; ++} ++ ++/** ++ * write_empty_leb - copy the image of an empty LEB to the output file. ++ * @lnum: LEB number ++ */ ++static int write_empty_leb(int lnum) ++{ ++ return write_leb(lnum, 0, leb_buf); ++} ++ ++/** ++ * do_pad - pad a buffer to the minimum I/O size. ++ * @buf: buffer ++ * @len: buffer length ++ */ ++static int do_pad(void *buf, int len) ++{ ++ int pad_len, alen = ALIGN(len, 8), wlen = ALIGN(alen, c->min_io_size); ++ uint32_t crc; ++ ++ memset(buf + len, 0xff, alen - len); ++ pad_len = wlen - alen; ++ dbg_msg(3, "len %d pad_len %d", len, pad_len); ++ buf += alen; ++ if (pad_len >= UBIFS_PAD_NODE_SZ) { ++ struct ubifs_ch *ch = buf; ++ struct ubifs_pad_node *pad_node = buf; ++ ++ ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); ++ ch->node_type = UBIFS_PAD_NODE; ++ ch->group_type = UBIFS_NO_NODE_GROUP; ++ ch->padding[0] = ch->padding[1] = 0; ++ ch->sqnum = cpu_to_le64(0); ++ ch->len = cpu_to_le32(UBIFS_PAD_NODE_SZ); ++ ++ pad_len -= UBIFS_PAD_NODE_SZ; ++ pad_node->pad_len = cpu_to_le32(pad_len); ++ ++ crc = crc32(UBIFS_CRC32_INIT, buf + 8, UBIFS_PAD_NODE_SZ - 8); ++ ch->crc = cpu_to_le32(crc); ++ ++ memset(buf + UBIFS_PAD_NODE_SZ, 0, pad_len); ++ } else if (pad_len > 0) ++ memset(buf, UBIFS_PADDING_BYTE, pad_len); ++ ++ return wlen; ++} ++ ++/** ++ * write_node - write a node to a LEB. ++ * @node: node ++ * @len: node length ++ * @lnum: LEB number ++ */ ++static int write_node(void *node, int len, int lnum) ++{ ++ prepare_node(node, len); ++ ++ memcpy(leb_buf, node, len); ++ ++ len = do_pad(leb_buf, len); ++ ++ return write_leb(lnum, len, leb_buf); ++} ++ ++/** ++ * calc_dark - calculate LEB dark space size. ++ * @c: the UBIFS file-system description object ++ * @spc: amount of free and dirty space in the LEB ++ * ++ * This function calculates amount of dark space in an LEB which has @spc bytes ++ * of free and dirty space. Returns the calculations result. ++ * ++ * Dark space is the space which is not always usable - it depends on which ++ * nodes are written in which order. E.g., if an LEB has only 512 free bytes, ++ * it is dark space, because it cannot fit a large data node. So UBIFS cannot ++ * count on this LEB and treat these 512 bytes as usable because it is not true ++ * if, for example, only big chunks of uncompressible data will be written to ++ * the FS. ++ */ ++static int calc_dark(struct ubifs_info *c, int spc) ++{ ++ if (spc < c->dark_wm) ++ return spc; ++ ++ /* ++ * If we have slightly more space then the dark space watermark, we can ++ * anyway safely assume it we'll be able to write a node of the ++ * smallest size there. ++ */ ++ if (spc - c->dark_wm < MIN_WRITE_SZ) ++ return spc - MIN_WRITE_SZ; ++ ++ return c->dark_wm; ++} ++ ++/** ++ * set_lprops - set the LEB property values for a LEB. ++ * @lnum: LEB number ++ * @offs: end offset of data in the LEB ++ * @flags: LEB property flags ++ */ ++static void set_lprops(int lnum, int offs, int flags) ++{ ++ int i = lnum - c->main_first, free, dirty; ++ int a = max_t(int, c->min_io_size, 8); ++ ++ free = c->leb_size - ALIGN(offs, a); ++ dirty = c->leb_size - free - ALIGN(offs, 8); ++ dbg_msg(3, "LEB %d free %d dirty %d flags %d", lnum, free, dirty, ++ flags); ++ c->lpt[i].free = free; ++ c->lpt[i].dirty = dirty; ++ c->lpt[i].flags = flags; ++ c->lst.total_free += free; ++ c->lst.total_dirty += dirty; ++ if (flags & LPROPS_INDEX) ++ c->lst.idx_lebs += 1; ++ else { ++ int spc; ++ ++ spc = free + dirty; ++ if (spc < c->dead_wm) ++ c->lst.total_dead += spc; ++ else ++ c->lst.total_dark += calc_dark(c, spc); ++ c->lst.total_used += c->leb_size - spc; ++ } ++} ++ ++/** ++ * add_to_index - add a node key and position to the index. ++ * @key: node key ++ * @lnum: node LEB number ++ * @offs: node offset ++ * @len: node length ++ */ ++static int add_to_index(union ubifs_key *key, char *name, int lnum, int offs, ++ int len) ++{ ++ struct idx_entry *e; ++ ++ dbg_msg(3, "LEB %d offs %d len %d", lnum, offs, len); ++ e = malloc(sizeof(struct idx_entry)); ++ if (!e) ++ return err_msg("out of memory"); ++ e->next = NULL; ++ e->prev = idx_list_last; ++ e->key = *key; ++ e->name = name; ++ e->lnum = lnum; ++ e->offs = offs; ++ e->len = len; ++ if (!idx_list_first) ++ idx_list_first = e; ++ if (idx_list_last) ++ idx_list_last->next = e; ++ idx_list_last = e; ++ idx_cnt += 1; ++ return 0; ++} ++ ++/** ++ * flush_nodes - write the current head and move the head to the next LEB. ++ */ ++static int flush_nodes(void) ++{ ++ int len, err; ++ ++ if (!head_offs) ++ return 0; ++ len = do_pad(leb_buf, head_offs); ++ err = write_leb(head_lnum, len, leb_buf); ++ if (err) ++ return err; ++ set_lprops(head_lnum, head_offs, head_flags); ++ head_lnum += 1; ++ head_offs = 0; ++ return 0; ++} ++ ++/** ++ * reserve_space - reserve space for a node on the head. ++ * @len: node length ++ * @lnum: LEB number is returned here ++ * @offs: offset is returned here ++ */ ++static int reserve_space(int len, int *lnum, int *offs) ++{ ++ int err; ++ ++ if (len > c->leb_size - head_offs) { ++ err = flush_nodes(); ++ if (err) ++ return err; ++ } ++ *lnum = head_lnum; ++ *offs = head_offs; ++ head_offs += ALIGN(len, 8); ++ return 0; ++} ++ ++/** ++ * add_node - write a node to the head. ++ * @key: node key ++ * @node: node ++ * @len: node length ++ */ ++static int add_node(union ubifs_key *key, char *name, void *node, int len) ++{ ++ int err, lnum, offs; ++ ++ prepare_node(node, len); ++ ++ err = reserve_space(len, &lnum, &offs); ++ if (err) ++ return err; ++ ++ memcpy(leb_buf + offs, node, len); ++ memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); ++ ++ add_to_index(key, name, lnum, offs, len); ++ ++ return 0; ++} ++ ++/** ++ * add_inode_with_data - write an inode. ++ * @st: stat information of source inode ++ * @inum: target inode number ++ * @data: inode data (for special inodes e.g. symlink path etc) ++ * @data_len: inode data length ++ * @flags: source inode flags ++ */ ++static int add_inode_with_data(struct stat *st, ino_t inum, void *data, ++ unsigned int data_len, int flags) ++{ ++ struct ubifs_ino_node *ino = node_buf; ++ union ubifs_key key; ++ int len, use_flags = 0; ++ ++ if (c->default_compr != UBIFS_COMPR_NONE) ++ use_flags |= UBIFS_COMPR_FL; ++ if (flags & FS_COMPR_FL) ++ use_flags |= UBIFS_COMPR_FL; ++ if (flags & FS_SYNC_FL) ++ use_flags |= UBIFS_SYNC_FL; ++ if (flags & FS_IMMUTABLE_FL) ++ use_flags |= UBIFS_IMMUTABLE_FL; ++ if (flags & FS_APPEND_FL) ++ use_flags |= UBIFS_APPEND_FL; ++ if (flags & FS_DIRSYNC_FL && S_ISDIR(st->st_mode)) ++ use_flags |= UBIFS_DIRSYNC_FL; ++ ++ memset(ino, 0, UBIFS_INO_NODE_SZ); ++ ++ ino_key_init(c, &key, inum); ++ ino->ch.node_type = UBIFS_INO_NODE; ++ key_write(c, &key, &ino->key); ++ ino->creat_sqnum = cpu_to_le64(creat_sqnum); ++ ino->size = cpu_to_le64(st->st_size); ++ ino->nlink = cpu_to_le32(st->st_nlink); ++ /* ++ * The time fields are updated assuming the default time granularity ++ * of 1 second. To support finer granularities, utime() would be needed. ++ */ ++ ino->atime_sec = cpu_to_le64(st->st_atime); ++ ino->ctime_sec = cpu_to_le64(st->st_ctime); ++ ino->mtime_sec = cpu_to_le64(st->st_mtime); ++ ino->atime_nsec = 0; ++ ino->ctime_nsec = 0; ++ ino->mtime_nsec = 0; ++ ino->uid = cpu_to_le32(st->st_uid); ++ ino->gid = cpu_to_le32(st->st_gid); ++ ino->uid = cpu_to_le32(st->st_uid); ++ ino->gid = cpu_to_le32(st->st_gid); ++ ino->mode = cpu_to_le32(st->st_mode); ++ ino->flags = cpu_to_le32(use_flags); ++ ino->data_len = cpu_to_le32(data_len); ++ ino->compr_type = cpu_to_le16(c->default_compr); ++ if (data_len) ++ memcpy(&ino->data, data, data_len); ++ ++ len = UBIFS_INO_NODE_SZ + data_len; ++ ++ return add_node(&key, NULL, ino, len); ++} ++ ++/** ++ * add_inode - write an inode. ++ * @st: stat information of source inode ++ * @inum: target inode number ++ * @flags: source inode flags ++ */ ++static int add_inode(struct stat *st, ino_t inum, int flags) ++{ ++ return add_inode_with_data(st, inum, NULL, 0, flags); ++} ++ ++/** ++ * add_dir_inode - write an inode for a directory. ++ * @dir: source directory ++ * @inum: target inode number ++ * @size: target directory size ++ * @nlink: target directory link count ++ * @st: struct stat object describing attributes (except size and nlink) of the ++ * target inode to create ++ * ++ * Note, this function may be called with %NULL @dir, when the directory which ++ * is being created does not exist at the host file system, but is defined by ++ * the device table. ++ */ ++static int add_dir_inode(DIR *dir, ino_t inum, loff_t size, unsigned int nlink, ++ struct stat *st) ++{ ++ int fd, flags = 0; ++ ++ st->st_size = size; ++ st->st_nlink = nlink; ++ ++ if (dir) { ++ fd = dirfd(dir); ++ if (fd == -1) ++ return sys_err_msg("dirfd failed"); ++ if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) ++ flags = 0; ++ } ++ ++ return add_inode(st, inum, flags); ++} ++ ++/** ++ * add_dev_inode - write an inode for a character or block device. ++ * @st: stat information of source inode ++ * @inum: target inode number ++ * @flags: source inode flags ++ */ ++static int add_dev_inode(struct stat *st, ino_t inum, int flags) ++{ ++ union ubifs_dev_desc dev; ++ ++ dev.huge = cpu_to_le64(makedev(major(st->st_rdev), minor(st->st_rdev))); ++ return add_inode_with_data(st, inum, &dev, 8, flags); ++} ++ ++/** ++ * add_symlink_inode - write an inode for a symbolic link. ++ * @path_name: path name of symbolic link inode itself (not the link target) ++ * @st: stat information of source inode ++ * @inum: target inode number ++ * @flags: source inode flags ++ */ ++static int add_symlink_inode(const char *path_name, struct stat *st, ino_t inum, ++ int flags) ++{ ++ char buf[UBIFS_MAX_INO_DATA + 2]; ++ ssize_t len; ++ ++ /* Take the symlink as is */ ++ len = readlink(path_name, buf, UBIFS_MAX_INO_DATA + 1); ++ if (len <= 0) ++ return sys_err_msg("readlink failed for %s", path_name); ++ if (len > UBIFS_MAX_INO_DATA) ++ return err_msg("symlink too long for %s", path_name); ++ ++ return add_inode_with_data(st, inum, buf, len, flags); ++} ++ ++/** ++ * add_dent_node - write a directory entry node. ++ * @dir_inum: target inode number of directory ++ * @name: directory entry name ++ * @inum: target inode number of the directory entry ++ * @type: type of the target inode ++ */ ++static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum, ++ unsigned char type) ++{ ++ struct ubifs_dent_node *dent = node_buf; ++ union ubifs_key key; ++ struct qstr dname; ++ char *kname; ++ int len; ++ ++ dbg_msg(3, "%s ino %lu type %u dir ino %lu", name, inum, ++ (unsigned)type, dir_inum); ++ memset(dent, 0, UBIFS_DENT_NODE_SZ); ++ ++ dname.name = (void *)name; ++ dname.len = strlen(name); ++ ++ dent->ch.node_type = UBIFS_DENT_NODE; ++ ++ dent_key_init(c, &key, dir_inum, &dname); ++ key_write(c, &key, dent->key); ++ dent->inum = cpu_to_le64(inum); ++ dent->padding1 = 0; ++ dent->type = type; ++ dent->nlen = cpu_to_le16(dname.len); ++ memcpy(dent->name, dname.name, dname.len); ++ dent->name[dname.len] = '\0'; ++ ++ len = UBIFS_DENT_NODE_SZ + dname.len + 1; ++ ++ kname = strdup(name); ++ if (!kname) ++ return err_msg("cannot allocate memory"); ++ ++ return add_node(&key, kname, dent, len); ++} ++ ++/** ++ * lookup_inum_mapping - add an inode mapping for link counting. ++ * @dev: source device on which source inode number resides ++ * @inum: source inode number ++ */ ++static struct inum_mapping *lookup_inum_mapping(dev_t dev, ino_t inum) ++{ ++ struct inum_mapping *im; ++ unsigned int k; ++ ++ k = inum % HASH_TABLE_SIZE; ++ im = hash_table[k]; ++ while (im) { ++ if (im->dev == dev && im->inum == inum) ++ return im; ++ im = im->next; ++ } ++ im = malloc(sizeof(struct inum_mapping)); ++ if (!im) ++ return NULL; ++ im->next = hash_table[k]; ++ im->prev = NULL; ++ im->dev = dev; ++ im->inum = inum; ++ im->use_inum = 0; ++ im->use_nlink = 0; ++ if (hash_table[k]) ++ hash_table[k]->prev = im; ++ hash_table[k] = im; ++ return im; ++} ++ ++/** ++ * all_zero - does a buffer contain only zero bytes. ++ * @buf: buffer ++ * @len: buffer length ++ */ ++static int all_zero(void *buf, int len) ++{ ++ unsigned char *p = buf; ++ ++ while (len--) ++ if (*p++ != 0) ++ return 0; ++ return 1; ++} ++ ++/** ++ * add_file - write the data of a file and its inode to the output file. ++ * @path_name: source path name ++ * @st: source inode stat information ++ * @inum: target inode number ++ * @flags: source inode flags ++ */ ++static int add_file(const char *path_name, struct stat *st, ino_t inum, ++ int flags) ++{ ++ struct ubifs_data_node *dn = node_buf; ++ void *buf = block_buf; ++ loff_t file_size = 0; ++ ssize_t ret, bytes_read; ++ union ubifs_key key; ++ int fd, dn_len, err, compr_type, use_compr; ++ unsigned int block_no = 0; ++ size_t out_len; ++ ++ fd = open(path_name, O_RDONLY | O_LARGEFILE); ++ if (fd == -1) ++ return sys_err_msg("failed to open file '%s'", path_name); ++ do { ++ /* Read next block */ ++ bytes_read = 0; ++ do { ++ ret = read(fd, buf + bytes_read, ++ UBIFS_BLOCK_SIZE - bytes_read); ++ if (ret == -1) { ++ sys_err_msg("failed to read file '%s'", ++ path_name); ++ close(fd); ++ return 1; ++ } ++ bytes_read += ret; ++ } while (ret != 0 && bytes_read != UBIFS_BLOCK_SIZE); ++ if (bytes_read == 0) ++ break; ++ file_size += bytes_read; ++ /* Skip holes */ ++ if (all_zero(buf, bytes_read)) { ++ block_no += 1; ++ continue; ++ } ++ /* Make data node */ ++ memset(dn, 0, UBIFS_DATA_NODE_SZ); ++ data_key_init(c, &key, inum, block_no++); ++ dn->ch.node_type = UBIFS_DATA_NODE; ++ key_write(c, &key, &dn->key); ++ dn->size = cpu_to_le32(bytes_read); ++ out_len = NODE_BUFFER_SIZE - UBIFS_DATA_NODE_SZ; ++ if (c->default_compr == UBIFS_COMPR_NONE && ++ (flags & FS_COMPR_FL)) ++ use_compr = UBIFS_COMPR_LZO; ++ else ++ use_compr = c->default_compr; ++ compr_type = compress_data(buf, bytes_read, &dn->data, ++ &out_len, use_compr); ++ dn->compr_type = cpu_to_le16(compr_type); ++ dn_len = UBIFS_DATA_NODE_SZ + out_len; ++ /* Add data node to file system */ ++ err = add_node(&key, NULL, dn, dn_len); ++ if (err) { ++ close(fd); ++ return err; ++ } ++ } while (ret != 0); ++ if (close(fd) == -1) ++ return sys_err_msg("failed to close file '%s'", path_name); ++ if (file_size != st->st_size) ++ return err_msg("file size changed during writing file '%s'", ++ path_name); ++ return add_inode(st, inum, flags); ++} ++ ++/** ++ * add_non_dir - write a non-directory to the output file. ++ * @path_name: source path name ++ * @inum: target inode number is passed and returned here (due to link counting) ++ * @nlink: number of links if known otherwise zero ++ * @type: UBIFS inode type is returned here ++ * @st: struct stat object containing inode attributes which should be use when ++ * creating the UBIFS inode ++ */ ++static int add_non_dir(const char *path_name, ino_t *inum, unsigned int nlink, ++ unsigned char *type, struct stat *st) ++{ ++ int fd, flags = 0; ++ ++ dbg_msg(2, "%s", path_name); ++ ++ if (S_ISREG(st->st_mode)) { ++ fd = open(path_name, O_RDONLY); ++ if (fd == -1) ++ return sys_err_msg("failed to open file '%s'", ++ path_name); ++ if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) ++ flags = 0; ++ if (close(fd) == -1) ++ return sys_err_msg("failed to close file '%s'", ++ path_name); ++ *type = UBIFS_ITYPE_REG; ++ } else if (S_ISCHR(st->st_mode)) ++ *type = UBIFS_ITYPE_CHR; ++ else if (S_ISBLK(st->st_mode)) ++ *type = UBIFS_ITYPE_BLK; ++ else if (S_ISLNK(st->st_mode)) ++ *type = UBIFS_ITYPE_LNK; ++ else if (S_ISSOCK(st->st_mode)) ++ *type = UBIFS_ITYPE_SOCK; ++ else if (S_ISFIFO(st->st_mode)) ++ *type = UBIFS_ITYPE_FIFO; ++ else ++ return err_msg("file '%s' has unknown inode type", path_name); ++ ++ if (nlink) ++ st->st_nlink = nlink; ++ else if (st->st_nlink > 1) { ++ /* ++ * If the number of links is greater than 1, then add this file ++ * later when we know the number of links that we actually have. ++ * For now, we just put the inode mapping in the hash table. ++ */ ++ struct inum_mapping *im; ++ ++ im = lookup_inum_mapping(st->st_dev, st->st_ino); ++ if (!im) ++ return err_msg("out of memory"); ++ if (im->use_nlink == 0) { ++ /* New entry */ ++ im->use_inum = *inum; ++ im->use_nlink = 1; ++ im->path_name = malloc(strlen(path_name) + 1); ++ if (!im->path_name) ++ return err_msg("out of memory"); ++ strcpy(im->path_name, path_name); ++ } else { ++ /* Existing entry */ ++ *inum = im->use_inum; ++ im->use_nlink += 1; ++ /* Return unused inode number */ ++ c->highest_inum -= 1; ++ } ++ ++ memcpy(&im->st, st, sizeof(struct stat)); ++ return 0; ++ } else ++ st->st_nlink = 1; ++ ++ creat_sqnum = ++c->max_sqnum; ++ ++ if (S_ISREG(st->st_mode)) ++ return add_file(path_name, st, *inum, flags); ++ if (S_ISCHR(st->st_mode)) ++ return add_dev_inode(st, *inum, flags); ++ if (S_ISBLK(st->st_mode)) ++ return add_dev_inode(st, *inum, flags); ++ if (S_ISLNK(st->st_mode)) ++ return add_symlink_inode(path_name, st, *inum, flags); ++ if (S_ISSOCK(st->st_mode)) ++ return add_inode(st, *inum, flags); ++ if (S_ISFIFO(st->st_mode)) ++ return add_inode(st, *inum, flags); ++ ++ return err_msg("file '%s' has unknown inode type", path_name); ++} ++ ++/** ++ * add_directory - write a directory tree to the output file. ++ * @dir_name: directory path name ++ * @dir_inum: UBIFS inode number of directory ++ * @st: directory inode statistics ++ * @non_existing: non-zero if this function is called for a directory which ++ * does not exist on the host file-system and it is being ++ * created because it is defined in the device table file. ++ */ ++static int add_directory(const char *dir_name, ino_t dir_inum, struct stat *st, ++ int non_existing) ++{ ++ struct dirent *entry; ++ DIR *dir = NULL; ++ int err = 0; ++ loff_t size = UBIFS_INO_NODE_SZ; ++ char *name = NULL; ++ unsigned int nlink = 2; ++ struct path_htbl_element *ph_elt; ++ struct name_htbl_element *nh_elt = NULL; ++ struct hashtable_itr *itr; ++ ino_t inum; ++ unsigned char type; ++ unsigned long long dir_creat_sqnum = ++c->max_sqnum; ++ ++ dbg_msg(2, "%s", dir_name); ++ if (!non_existing) { ++ dir = opendir(dir_name); ++ if (dir == NULL) ++ return sys_err_msg("cannot open directory '%s'", ++ dir_name); ++ } ++ ++ /* ++ * Check whether this directory contains files which should be ++ * added/changed because they were specified in the device table. ++ * @ph_elt will be non-zero if yes. ++ */ ++ ph_elt = devtbl_find_path(dir_name + root_len - 1); ++ ++ /* ++ * Before adding the directory itself, we have to iterate over all the ++ * entries the device table adds to this directory and create them. ++ */ ++ for (; !non_existing;) { ++ struct stat dent_st; ++ ++ errno = 0; ++ entry = readdir(dir); ++ if (!entry) { ++ if (errno == 0) ++ break; ++ sys_err_msg("error reading directory '%s'", dir_name); ++ err = -1; ++ break; ++ } ++ ++ if (strcmp(".", entry->d_name) == 0) ++ continue; ++ if (strcmp("..", entry->d_name) == 0) ++ continue; ++ ++ if (ph_elt) ++ /* ++ * This directory was referred to at the device table ++ * file. Check if this directory entry is referred at ++ * too. ++ */ ++ nh_elt = devtbl_find_name(ph_elt, entry->d_name); ++ ++ /* ++ * We are going to create the file corresponding to this ++ * directory entry (@entry->d_name). We use 'struct stat' ++ * object to pass information about file attributes (actually ++ * only about UID, GID, mode, major, and minor). Get attributes ++ * for this file from the UBIFS rootfs on the host. ++ */ ++ free(name); ++ name = make_path(dir_name, entry->d_name); ++ if (lstat(name, &dent_st) == -1) { ++ sys_err_msg("lstat failed for file '%s'", name); ++ goto out_free; ++ } ++ ++ if (squash_owner) ++ /* ++ * Squash UID/GID. But the device table may override ++ * this. ++ */ ++ dent_st.st_uid = dent_st.st_gid = 0; ++ ++ /* ++ * And if the device table describes the same file, override ++ * the attributes. However, this is not allowed for device node ++ * files. ++ */ ++ if (nh_elt && override_attributes(&dent_st, ph_elt, nh_elt)) ++ goto out_free; ++ ++ inum = ++c->highest_inum; ++ ++ if (S_ISDIR(dent_st.st_mode)) { ++ err = add_directory(name, inum, &dent_st, 0); ++ if (err) ++ goto out_free; ++ nlink += 1; ++ type = UBIFS_ITYPE_DIR; ++ } else { ++ err = add_non_dir(name, &inum, 0, &type, &dent_st); ++ if (err) ++ goto out_free; ++ } ++ ++ err = add_dent_node(dir_inum, entry->d_name, inum, type); ++ if (err) ++ goto out_free; ++ size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(entry->d_name) + 1, ++ 8); ++ } ++ ++ /* ++ * OK, we have created all files in this directory (recursively), let's ++ * also create all files described in the device table. All t ++ */ ++ nh_elt = first_name_htbl_element(ph_elt, &itr); ++ while (nh_elt) { ++ struct stat fake_st; ++ ++ /* ++ * We prohibit creating regular files using the device table, ++ * the device table may only re-define attributes of regular ++ * files. ++ */ ++ if (S_ISREG(nh_elt->mode)) { ++ err_msg("Bad device table entry %s/%s - it is " ++ "prohibited to create regular files " ++ "via device table", ++ strcmp(ph_elt->path, "/") ? ph_elt->path : "", ++ nh_elt->name); ++ goto out_free; ++ } ++ ++ memcpy(&fake_st, &root_st, sizeof(struct stat)); ++ fake_st.st_uid = nh_elt->uid; ++ fake_st.st_uid = nh_elt->uid; ++ fake_st.st_mode = nh_elt->mode; ++ fake_st.st_rdev = nh_elt->dev; ++ fake_st.st_nlink = 1; ++ ++ free(name); ++ name = make_path(dir_name, nh_elt->name); ++ inum = ++c->highest_inum; ++ ++ if (S_ISDIR(nh_elt->mode)) { ++ err = add_directory(name, inum, &fake_st, 1); ++ if (err) ++ goto out_free; ++ nlink += 1; ++ type = UBIFS_ITYPE_DIR; ++ } else { ++ err = add_non_dir(name, &inum, 0, &type, &fake_st); ++ if (err) ++ goto out_free; ++ } ++ ++ err = add_dent_node(dir_inum, nh_elt->name, inum, type); ++ if (err) ++ goto out_free; ++ size += ALIGN(UBIFS_DENT_NODE_SZ + strlen(nh_elt->name) + 1, 8); ++ ++ nh_elt = next_name_htbl_element(ph_elt, &itr); ++ } ++ ++ creat_sqnum = dir_creat_sqnum; ++ ++ err = add_dir_inode(dir, dir_inum, size, nlink, st); ++ if (err) ++ goto out_free; ++ ++ free(name); ++ if (!non_existing && closedir(dir) == -1) ++ return sys_err_msg("error closing directory '%s'", dir_name); ++ ++ return 0; ++ ++out_free: ++ free(name); ++ if (!non_existing) ++ closedir(dir); ++ return -1; ++} ++ ++/** ++ * add_multi_linked_files - write all the files for which we counted links. ++ */ ++static int add_multi_linked_files(void) ++{ ++ int i, err; ++ ++ for (i = 0; i < HASH_TABLE_SIZE; i++) { ++ struct inum_mapping *im; ++ unsigned char type = 0; ++ ++ for (im = hash_table[i]; im; im = im->next) { ++ dbg_msg(2, "%s", im->path_name); ++ err = add_non_dir(im->path_name, &im->use_inum, ++ im->use_nlink, &type, &im->st); ++ if (err) ++ return err; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * write_data - write the files and directories. ++ */ ++static int write_data(void) ++{ ++ int err; ++ ++ err = stat(root, &root_st); ++ if (err) ++ return sys_err_msg("bad root file-system directory '%s'", root); ++ root_st.st_uid = root_st.st_gid = 0; ++ root_st.st_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO; ++ ++ head_flags = 0; ++ err = add_directory(root, UBIFS_ROOT_INO, &root_st, 0); ++ if (err) ++ return err; ++ err = add_multi_linked_files(); ++ if (err) ++ return err; ++ return flush_nodes(); ++} ++ ++static int namecmp(const char *name1, const char *name2) ++{ ++ size_t len1 = strlen(name1), len2 = strlen(name2); ++ size_t clen = (len1 < len2) ? len1 : len2; ++ int cmp; ++ ++ cmp = memcmp(name1, name2, clen); ++ if (cmp) ++ return cmp; ++ return (len1 < len2) ? -1 : 1; ++} ++ ++static int cmp_idx(const void *a, const void *b) ++{ ++ const struct idx_entry *e1 = *(const struct idx_entry **)a; ++ const struct idx_entry *e2 = *(const struct idx_entry **)b; ++ int cmp; ++ ++ cmp = keys_cmp(c, &e1->key, &e2->key); ++ if (cmp) ++ return cmp; ++ return namecmp(e1->name, e2->name); ++} ++ ++/** ++ * add_idx_node - write an index node to the head. ++ * @node: index node ++ * @child_cnt: number of children of this index node ++ */ ++static int add_idx_node(void *node, int child_cnt) ++{ ++ int err, lnum, offs, len; ++ ++ len = ubifs_idx_node_sz(c, child_cnt); ++ ++ prepare_node(node, len); ++ ++ err = reserve_space(len, &lnum, &offs); ++ if (err) ++ return err; ++ ++ memcpy(leb_buf + offs, node, len); ++ memset(leb_buf + offs + len, 0xff, ALIGN(len, 8) - len); ++ ++ c->old_idx_sz += ALIGN(len, 8); ++ ++ dbg_msg(3, "at %d:%d len %d index size %llu", lnum, offs, len, ++ c->old_idx_sz); ++ ++ /* The last index node written will be the root */ ++ c->zroot.lnum = lnum; ++ c->zroot.offs = offs; ++ c->zroot.len = len; ++ ++ return 0; ++} ++ ++/** ++ * write_index - write out the index. ++ */ ++static int write_index(void) ++{ ++ size_t sz, i, cnt, idx_sz, pstep, bcnt; ++ struct idx_entry **idx_ptr, **p; ++ struct ubifs_idx_node *idx; ++ struct ubifs_branch *br; ++ int child_cnt, j, level, blnum, boffs, blen, blast_len, err; ++ ++ dbg_msg(1, "leaf node count: %d", idx_cnt); ++ ++ /* Reset the head for the index */ ++ head_flags = LPROPS_INDEX; ++ /* Allocate index node */ ++ idx_sz = ubifs_idx_node_sz(c, c->fanout); ++ idx = malloc(idx_sz); ++ if (!idx) ++ return err_msg("out of memory"); ++ /* Make an array of pointers to sort the index list */ ++ sz = idx_cnt * sizeof(struct idx_entry *); ++ if (sz / sizeof(struct idx_entry *) != idx_cnt) { ++ free(idx); ++ return err_msg("index is too big (%zu entries)", idx_cnt); ++ } ++ idx_ptr = malloc(sz); ++ if (!idx_ptr) { ++ free(idx); ++ return err_msg("out of memory - needed %zu bytes for index", ++ sz); ++ } ++ idx_ptr[0] = idx_list_first; ++ for (i = 1; i < idx_cnt; i++) ++ idx_ptr[i] = idx_ptr[i - 1]->next; ++ qsort(idx_ptr, idx_cnt, sizeof(struct idx_entry *), cmp_idx); ++ /* Write level 0 index nodes */ ++ cnt = idx_cnt / c->fanout; ++ if (idx_cnt % c->fanout) ++ cnt += 1; ++ p = idx_ptr; ++ blnum = head_lnum; ++ boffs = head_offs; ++ for (i = 0; i < cnt; i++) { ++ /* ++ * Calculate the child count. All index nodes are created full ++ * except for the last index node on each row. ++ */ ++ if (i == cnt - 1) { ++ child_cnt = idx_cnt % c->fanout; ++ if (child_cnt == 0) ++ child_cnt = c->fanout; ++ } else ++ child_cnt = c->fanout; ++ memset(idx, 0, idx_sz); ++ idx->ch.node_type = UBIFS_IDX_NODE; ++ idx->child_cnt = cpu_to_le16(child_cnt); ++ idx->level = cpu_to_le16(0); ++ for (j = 0; j < child_cnt; j++, p++) { ++ br = ubifs_idx_branch(c, idx, j); ++ key_write_idx(c, &(*p)->key, &br->key); ++ br->lnum = cpu_to_le32((*p)->lnum); ++ br->offs = cpu_to_le32((*p)->offs); ++ br->len = cpu_to_le32((*p)->len); ++ } ++ add_idx_node(idx, child_cnt); ++ } ++ /* Write level 1 index nodes and above */ ++ level = 0; ++ pstep = 1; ++ while (cnt > 1) { ++ /* ++ * 'blast_len' is the length of the last index node in the level ++ * below. ++ */ ++ blast_len = ubifs_idx_node_sz(c, child_cnt); ++ /* 'bcnt' is the number of index nodes in the level below */ ++ bcnt = cnt; ++ /* 'cnt' is the number of index nodes in this level */ ++ cnt = (cnt + c->fanout - 1) / c->fanout; ++ if (cnt == 0) ++ cnt = 1; ++ level += 1; ++ /* ++ * The key of an index node is the same as the key of its first ++ * child. Thus we can get the key by stepping along the bottom ++ * level 'p' with an increasing large step 'pstep'. ++ */ ++ p = idx_ptr; ++ pstep *= c->fanout; ++ for (i = 0; i < cnt; i++) { ++ /* ++ * Calculate the child count. All index nodes are ++ * created full except for the last index node on each ++ * row. ++ */ ++ if (i == cnt - 1) { ++ child_cnt = bcnt % c->fanout; ++ if (child_cnt == 0) ++ child_cnt = c->fanout; ++ } else ++ child_cnt = c->fanout; ++ memset(idx, 0, idx_sz); ++ idx->ch.node_type = UBIFS_IDX_NODE; ++ idx->child_cnt = cpu_to_le16(child_cnt); ++ idx->level = cpu_to_le16(level); ++ for (j = 0; j < child_cnt; j++) { ++ size_t bn = i * c->fanout + j; ++ ++ /* ++ * The length of the index node in the level ++ * below is 'idx_sz' except when it is the last ++ * node on the row. i.e. all the others on the ++ * row are full. ++ */ ++ if (bn == bcnt - 1) ++ blen = blast_len; ++ else ++ blen = idx_sz; ++ /* ++ * 'blnum' and 'boffs' hold the position of the ++ * index node on the level below. ++ */ ++ if (boffs + blen > c->leb_size) { ++ blnum += 1; ++ boffs = 0; ++ } ++ /* ++ * Fill in the branch with the key and position ++ * of the index node from the level below. ++ */ ++ br = ubifs_idx_branch(c, idx, j); ++ key_write_idx(c, &(*p)->key, &br->key); ++ br->lnum = cpu_to_le32(blnum); ++ br->offs = cpu_to_le32(boffs); ++ br->len = cpu_to_le32(blen); ++ /* ++ * Step to the next index node on the level ++ * below. ++ */ ++ boffs += ALIGN(blen, 8); ++ p += pstep; ++ } ++ add_idx_node(idx, child_cnt); ++ } ++ } ++ ++ /* Free stuff */ ++ for (i = 0; i < idx_cnt; i++) ++ free(idx_ptr[i]); ++ free(idx_ptr); ++ free(idx); ++ ++ dbg_msg(1, "zroot is at %d:%d len %d", c->zroot.lnum, c->zroot.offs, ++ c->zroot.len); ++ ++ /* Set the index head */ ++ c->ihead_lnum = head_lnum; ++ c->ihead_offs = ALIGN(head_offs, c->min_io_size); ++ dbg_msg(1, "ihead is at %d:%d", c->ihead_lnum, c->ihead_offs); ++ ++ /* Flush the last index LEB */ ++ err = flush_nodes(); ++ if (err) ++ return err; ++ ++ return 0; ++} ++ ++/** ++ * set_gc_lnum - set the LEB number reserved for the garbage collector. ++ */ ++static int set_gc_lnum(void) ++{ ++ int err; ++ ++ c->gc_lnum = head_lnum++; ++ err = write_empty_leb(c->gc_lnum); ++ if (err) ++ return err; ++ set_lprops(c->gc_lnum, 0, 0); ++ c->lst.empty_lebs += 1; ++ return 0; ++} ++ ++/** ++ * finalize_leb_cnt - now that we know how many LEBs we used. ++ */ ++static int finalize_leb_cnt(void) ++{ ++ c->leb_cnt = head_lnum; ++ if (c->leb_cnt > c->max_leb_cnt) ++ /* TODO: in this case it segfaults because buffer overruns - we ++ * somewhere allocate smaller buffers - fix */ ++ return err_msg("max_leb_cnt too low (%d needed)", c->leb_cnt); ++ c->main_lebs = c->leb_cnt - c->main_first; ++ if (verbose) { ++ printf("\tsuper lebs: %d\n", UBIFS_SB_LEBS); ++ printf("\tmaster lebs: %d\n", UBIFS_MST_LEBS); ++ printf("\tlog_lebs: %d\n", c->log_lebs); ++ printf("\tlpt_lebs: %d\n", c->lpt_lebs); ++ printf("\torph_lebs: %d\n", c->orph_lebs); ++ printf("\tmain_lebs: %d\n", c->main_lebs); ++ printf("\tgc lebs: %d\n", 1); ++ printf("\tindex lebs: %d\n", c->lst.idx_lebs); ++ printf("\tleb_cnt: %d\n", c->leb_cnt); ++ } ++ dbg_msg(1, "total_free: %llu", c->lst.total_free); ++ dbg_msg(1, "total_dirty: %llu", c->lst.total_dirty); ++ dbg_msg(1, "total_used: %llu", c->lst.total_used); ++ dbg_msg(1, "total_dead: %llu", c->lst.total_dead); ++ dbg_msg(1, "total_dark: %llu", c->lst.total_dark); ++ dbg_msg(1, "index size: %llu", c->old_idx_sz); ++ dbg_msg(1, "empty_lebs: %d", c->lst.empty_lebs); ++ return 0; ++} ++ ++/** ++ * write_super - write the super block. ++ */ ++static int write_super(void) ++{ ++ struct ubifs_sb_node sup; ++ ++ memset(&sup, 0, UBIFS_SB_NODE_SZ); ++ ++ sup.ch.node_type = UBIFS_SB_NODE; ++ sup.key_hash = c->key_hash_type; ++ sup.min_io_size = cpu_to_le32(c->min_io_size); ++ sup.leb_size = cpu_to_le32(c->leb_size); ++ sup.leb_cnt = cpu_to_le32(c->leb_cnt); ++ sup.max_leb_cnt = cpu_to_le32(c->max_leb_cnt); ++ sup.max_bud_bytes = cpu_to_le64(c->max_bud_bytes); ++ sup.log_lebs = cpu_to_le32(c->log_lebs); ++ sup.lpt_lebs = cpu_to_le32(c->lpt_lebs); ++ sup.orph_lebs = cpu_to_le32(c->orph_lebs); ++ sup.jhead_cnt = cpu_to_le32(c->jhead_cnt); ++ sup.fanout = cpu_to_le32(c->fanout); ++ sup.lsave_cnt = cpu_to_le32(c->lsave_cnt); ++ sup.fmt_version = cpu_to_le32(UBIFS_FORMAT_VERSION); ++ sup.default_compr = cpu_to_le16(c->default_compr); ++ sup.time_gran = cpu_to_le32(DEFAULT_TIME_GRAN); ++ if (c->big_lpt) ++ sup.flags |= UBIFS_FLG_BIGLPT; ++ ++ return write_node(&sup, UBIFS_SB_NODE_SZ, UBIFS_SB_LNUM); ++} ++ ++/** ++ * write_master - write the master node. ++ */ ++static int write_master(void) ++{ ++ struct ubifs_mst_node mst; ++ int err; ++ ++ memset(&mst, 0, UBIFS_MST_NODE_SZ); ++ ++ mst.ch.node_type = UBIFS_MST_NODE; ++ mst.log_lnum = cpu_to_le32(UBIFS_LOG_LNUM); ++ mst.highest_inum = cpu_to_le64(c->highest_inum); ++ mst.cmt_no = cpu_to_le64(0); ++ mst.flags = cpu_to_le32(UBIFS_MST_NO_ORPHS); ++ mst.root_lnum = cpu_to_le32(c->zroot.lnum); ++ mst.root_offs = cpu_to_le32(c->zroot.offs); ++ mst.root_len = cpu_to_le32(c->zroot.len); ++ mst.gc_lnum = cpu_to_le32(c->gc_lnum); ++ mst.ihead_lnum = cpu_to_le32(c->ihead_lnum); ++ mst.ihead_offs = cpu_to_le32(c->ihead_offs); ++ mst.index_size = cpu_to_le64(c->old_idx_sz); ++ mst.lpt_lnum = cpu_to_le32(c->lpt_lnum); ++ mst.lpt_offs = cpu_to_le32(c->lpt_offs); ++ mst.nhead_lnum = cpu_to_le32(c->nhead_lnum); ++ mst.nhead_offs = cpu_to_le32(c->nhead_offs); ++ mst.ltab_lnum = cpu_to_le32(c->ltab_lnum); ++ mst.ltab_offs = cpu_to_le32(c->ltab_offs); ++ mst.lsave_lnum = cpu_to_le32(c->lsave_lnum); ++ mst.lsave_offs = cpu_to_le32(c->lsave_offs); ++ mst.lscan_lnum = cpu_to_le32(c->lscan_lnum); ++ mst.empty_lebs = cpu_to_le32(c->lst.empty_lebs); ++ mst.idx_lebs = cpu_to_le32(c->lst.idx_lebs); ++ mst.total_free = cpu_to_le64(c->lst.total_free); ++ mst.total_dirty = cpu_to_le64(c->lst.total_dirty); ++ mst.total_used = cpu_to_le64(c->lst.total_used); ++ mst.total_dead = cpu_to_le64(c->lst.total_dead); ++ mst.total_dark = cpu_to_le64(c->lst.total_dark); ++ mst.leb_cnt = cpu_to_le32(c->leb_cnt); ++ ++ err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM); ++ if (err) ++ return err; ++ ++ err = write_node(&mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1); ++ if (err) ++ return err; ++ ++ return 0; ++} ++ ++/** ++ * write_log - write an empty log. ++ */ ++static int write_log(void) ++{ ++ struct ubifs_cs_node cs; ++ int err, i, lnum; ++ ++ lnum = UBIFS_LOG_LNUM; ++ ++ cs.ch.node_type = UBIFS_CS_NODE; ++ cs.cmt_no = cpu_to_le64(0); ++ ++ err = write_node(&cs, UBIFS_CS_NODE_SZ, lnum); ++ if (err) ++ return err; ++ ++ lnum += 1; ++ ++ for (i = 1; i < c->log_lebs; i++, lnum++) { ++ err = write_empty_leb(lnum); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/** ++ * write_lpt - write the LEB properties tree. ++ */ ++static int write_lpt(void) ++{ ++ int err, lnum; ++ ++ err = create_lpt(c); ++ if (err) ++ return err; ++ ++ lnum = c->nhead_lnum + 1; ++ while (lnum <= c->lpt_last) { ++ err = write_empty_leb(lnum++); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/** ++ * write_orphan_area - write an empty orphan area. ++ */ ++static int write_orphan_area(void) ++{ ++ int err, i, lnum; ++ ++ lnum = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs; ++ for (i = 0; i < c->orph_lebs; i++, lnum++) { ++ err = write_empty_leb(lnum); ++ if (err) ++ return err; ++ } ++ return 0; ++} ++ ++/** ++ * open_target - open the output file. ++ */ ++static int open_target(void) ++{ ++ out_fd = open(output, O_CREAT | O_RDWR | O_TRUNC, ++ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); ++ if (out_fd == -1) ++ return sys_err_msg("cannot create output file '%s'", output); ++ return 0; ++} ++ ++/** ++ * close_target - close the output file. ++ */ ++static int close_target(void) ++{ ++ if (close(out_fd) == -1) ++ return sys_err_msg("cannot close output file '%s'", output); ++ return 0; ++} ++ ++/** ++ * init - initialize things. ++ */ ++static int init(void) ++{ ++ int err, i, main_lebs, big_lpt = 0, sz; ++ ++ c->highest_inum = UBIFS_FIRST_INO; ++ ++ c->jhead_cnt = 1; ++ ++ main_lebs = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; ++ main_lebs -= c->log_lebs + c->orph_lebs; ++ ++ err = calc_dflt_lpt_geom(c, &main_lebs, &big_lpt); ++ if (err) ++ return err; ++ ++ c->main_first = UBIFS_LOG_LNUM + c->log_lebs + c->lpt_lebs + ++ c->orph_lebs; ++ head_lnum = c->main_first; ++ head_offs = 0; ++ ++ c->lpt_first = UBIFS_LOG_LNUM + c->log_lebs; ++ c->lpt_last = c->lpt_first + c->lpt_lebs - 1; ++ ++ c->lpt = malloc(c->main_lebs * sizeof(struct ubifs_lprops)); ++ if (!c->lpt) ++ return err_msg("unable to allocate LPT"); ++ ++ c->ltab = malloc(c->lpt_lebs * sizeof(struct ubifs_lprops)); ++ if (!c->ltab) ++ return err_msg("unable to allocate LPT ltab"); ++ ++ /* Initialize LPT's own lprops */ ++ for (i = 0; i < c->lpt_lebs; i++) { ++ c->ltab[i].free = c->leb_size; ++ c->ltab[i].dirty = 0; ++ } ++ ++ c->dead_wm = ALIGN(MIN_WRITE_SZ, c->min_io_size); ++ c->dark_wm = ALIGN(UBIFS_MAX_NODE_SZ, c->min_io_size); ++ dbg_msg(1, "dead_wm %d dark_wm %d", c->dead_wm, c->dark_wm); ++ ++ leb_buf = malloc(c->leb_size); ++ if (!leb_buf) ++ return err_msg("out of memory"); ++ ++ node_buf = malloc(NODE_BUFFER_SIZE); ++ if (!node_buf) ++ return err_msg("out of memory"); ++ ++ block_buf = malloc(UBIFS_BLOCK_SIZE); ++ if (!block_buf) ++ return err_msg("out of memory"); ++ ++ sz = sizeof(struct inum_mapping *) * HASH_TABLE_SIZE; ++ hash_table = malloc(sz); ++ if (!hash_table) ++ return err_msg("out of memory"); ++ memset(hash_table, 0, sz); ++ ++ err = init_compression(); ++ if (err) ++ return err; ++ ++ return 0; ++} ++ ++static void destroy_hash_table(void) ++{ ++ int i; ++ ++ for (i = 0; i < HASH_TABLE_SIZE; i++) { ++ struct inum_mapping *im, *q; ++ ++ for (im = hash_table[i]; im; ) { ++ q = im; ++ im = im->next; ++ free(q->path_name); ++ free(q); ++ } ++ } ++} ++ ++/** ++ * deinit - deinitialize things. ++ */ ++static void deinit(void) ++{ ++ free(c->lpt); ++ free(c->ltab); ++ free(leb_buf); ++ free(node_buf); ++ free(block_buf); ++ destroy_hash_table(); ++ free(hash_table); ++ destroy_compression(); ++ free_devtable_info(); ++} ++ ++/** ++ * mkfs - make the file system. ++ * ++ * Each on-flash area has a corresponding function to create it. The order of ++ * the functions reflects what information must be known to complete each stage. ++ * As a consequence the output file is not written sequentially. No effort has ++ * been made to make efficient use of memory or to allow for the possibility of ++ * incremental updates to the output file. ++ */ ++static int mkfs(void) ++{ ++ int err = 0; ++ ++ err = init(); ++ if (err) ++ goto out; ++ ++ err = write_data(); ++ if (err) ++ goto out; ++ ++ err = set_gc_lnum(); ++ if (err) ++ goto out; ++ ++ err = write_index(); ++ if (err) ++ goto out; ++ ++ err = finalize_leb_cnt(); ++ if (err) ++ goto out; ++ ++ err = write_lpt(); ++ if (err) ++ goto out; ++ ++ err = write_super(); ++ if (err) ++ goto out; ++ ++ err = write_master(); ++ if (err) ++ goto out; ++ ++ err = write_log(); ++ if (err) ++ goto out; ++ ++ err = write_orphan_area(); ++ ++out: ++ deinit(); ++ return err; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int err; ++ ++ err = get_options(argc, argv); ++ if (err) ++ return err; ++ ++ err = open_target(); ++ if (err) ++ return err; ++ ++ err = mkfs(); ++ if (err) { ++ close_target(); ++ return err; ++ } ++ ++ err = close_target(); ++ if (err) ++ return err; ++ ++ if (verbose) ++ printf("Success!\n"); ++ ++ return 0; ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/mkfs.ubifs.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/mkfs.ubifs.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/mkfs.ubifs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/mkfs.ubifs.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,143 @@ ++/* ++ * Copyright (C) 2008 Nokia Corporation. ++ * Copyright (C) 2008 University of Szeged, Hungary ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy ++ * Adrian Hunter ++ * Zoltan Sogor ++ */ ++ ++#ifndef __MKFS_UBIFS_H__ ++#define __MKFS_UBIFS_H__ ++ ++#define _GNU_SOURCE ++#define _LARGEFILE64_SOURCE ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "crc32.h" ++#include "defs.h" ++#include "crc16.h" ++#include "ubifs-media.h" ++#include "ubifs.h" ++#include "key.h" ++#include "lpt.h" ++#include "compr.h" ++ ++/* ++ * Compression flags are duplicated so that compr.c can compile without ubifs.h. ++ * Here we make sure they are the same. ++ */ ++#if MKFS_UBIFS_COMPR_NONE != UBIFS_COMPR_NONE ++#error MKFS_UBIFS_COMPR_NONE != UBIFS_COMPR_NONE ++#endif ++#if MKFS_UBIFS_COMPR_LZO != UBIFS_COMPR_LZO ++#error MKFS_UBIFS_COMPR_LZO != UBIFS_COMPR_LZO ++#endif ++#if MKFS_UBIFS_COMPR_ZLIB != UBIFS_COMPR_ZLIB ++#error MKFS_UBIFS_COMPR_ZLIB != UBIFS_COMPR_ZLIB ++#endif ++ ++extern int verbose; ++extern int debug_level; ++ ++#define dbg_msg(lvl, fmt, ...) do {if (debug_level >= lvl) \ ++ printf("mkfs.ubifs: %s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__); \ ++} while(0) ++ ++#define err_msg(fmt, ...) ({ \ ++ fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ ++ -1; \ ++}) ++ ++#define sys_err_msg(fmt, ...) ({ \ ++ int err_ = errno; \ ++ fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ ++ fprintf(stderr, " %s (error %d)\n", strerror(err_), err_); \ ++ -1; \ ++}) ++ ++/** ++ * struct path_htbl_element - an element of the path hash table. ++ * @path: the UBIFS path the element describes (the key of the element) ++ * @name_htbl: one more (nested) hash table containing names of all ++ * files/directories/device nodes which should be created at this ++ * path ++ * ++ * See device table handling for more information. ++ */ ++struct path_htbl_element { ++ const char *path; ++ struct hashtable *name_htbl; ++}; ++ ++/** ++ * struct name_htbl_element - an element in the name hash table ++ * @name: name of the file/directory/device node (the key of the element) ++ * @mode: accsess rights and file type ++ * @uid: user ID ++ * @gid: group ID ++ * @major: device node major number ++ * @minor: device node minor number ++ * ++ * This is an element of the name hash table. Name hash table sits in the path ++ * hash table elements and describes file names which should be created/changed ++ * at this path. ++ */ ++struct name_htbl_element { ++ const char *name; ++ unsigned int mode; ++ unsigned int uid; ++ unsigned int gid; ++ dev_t dev; ++}; ++ ++struct hashtable_itr; ++ ++int write_leb(int lnum, int len, void *buf); ++int parse_devtable(const char *root, const char *tbl_file); ++struct path_htbl_element *devtbl_find_path(const char *path); ++struct name_htbl_element *devtbl_find_name(struct path_htbl_element *ph_elt, ++ const char *name); ++int override_attributes(struct stat *st, struct path_htbl_element *ph_elt, ++ struct name_htbl_element *nh_elt); ++struct name_htbl_element * ++first_name_htbl_element(struct path_htbl_element *ph_elt, ++ struct hashtable_itr **itr); ++struct name_htbl_element * ++next_name_htbl_element(struct path_htbl_element *ph_elt, ++ struct hashtable_itr **itr); ++void free_devtable_info(void); ++ ++#endif +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/readme linux-2.6.24.7/fs/ubifs/mkfs.ubifs/readme +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/readme 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/readme 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,28 @@ ++UBIFS File System - Make File System program ++ ++* crc16.h and crc16.c were copied from the linux kernel. ++* crc32.h and crc32.c were copied from mtd-utils and amended. ++* ubifs-media.h is fs/ubifs/ubifs-media.h from the linux kernel ++* ubifs.h is a selection of definitions from fs/ubifs/ubifs.h from the linux kernel. ++* key.h is copied from fs/ubifs/key.h from the linux kernel. ++* defs.h is a bunch of definitions to smooth things over. ++* lpt.c is a selection of functions copied from fs/ubifs/lpt.c from the linux kernel, and amended. ++* hashtable/* was downloaded from http://www.cl.cam.ac.uk/~cwc22/hashtable/ ++ ++*************************************************************************** ++ ++To build the mkfs.ubifs: ++ ++$ cd fs/ubifs/mkfs.ubifs ++$ make ++ ++To run mkfs.ubifs: ++ ++$ export LD_LIBRARY_PATH=`pwd`/lzo/lib:$LD_LIBRARY_PATH ++$ ./mkfs.ubifs -h ++ ++To build an ubifs image: ++ ++$ ./mkfs.ubifs -r /nfsroot/root26 -m 2048 -e 258048 -c 813 -o ubifs.img ++ ++*************************************************************************** +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/ubifs-media.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/ubifs-media.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/ubifs-media.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/ubifs-media.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,725 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This file describes UBIFS on-flash format and contains definitions of all the ++ * relevant data structures and constants. ++ * ++ * All UBIFS on-flash objects are stored in the form of nodes. All nodes start ++ * with the UBIFS node magic number and have the same common header. Nodes ++ * always sit at 8-byte aligned positions on the media and node header sizes are ++ * also 8-byte aligned (except for the indexing node and the padding node). ++ */ ++ ++#ifndef __UBIFS_MEDIA_H__ ++#define __UBIFS_MEDIA_H__ ++ ++/* UBIFS node magic number (must not have the padding byte first or last) */ ++#define UBIFS_NODE_MAGIC 0x06101831 ++ ++/* UBIFS on-flash format version */ ++#define UBIFS_FORMAT_VERSION 3 ++ ++/* Minimum logical eraseblock size in bytes */ ++#define UBIFS_MIN_LEB_SZ (15*1024) ++ ++/* Initial CRC32 value used when calculating CRC checksums */ ++#define UBIFS_CRC32_INIT 0xFFFFFFFFU ++ ++/* ++ * UBIFS does not try to compress data if its length is less than the below ++ * constant. ++ */ ++#define UBIFS_MIN_COMPR_LEN 128 ++ ++/* Root inode number */ ++#define UBIFS_ROOT_INO 1 ++ ++/* Lowest inode number used for regular inodes (not UBIFS-only internal ones) */ ++#define UBIFS_FIRST_INO 64 ++ ++/* ++ * Maximum file name and extended attribute length (must be a multiple of 8, ++ * minus 1). ++ */ ++#define UBIFS_MAX_NLEN 255 ++ ++/* Maximum number of data journal heads */ ++#define UBIFS_MAX_JHEADS 1 ++ ++/* ++ * Size of UBIFS data block. Note, UBIFS is not a block oriented file-system, ++ * which means that it does not treat the underlying media as consisting of ++ * blocks like in case of hard drives. Do not be confused. UBIFS block is just ++ * the maximum amount of data which one data node can have or which can be ++ * attached to an inode node. ++ */ ++#define UBIFS_BLOCK_SIZE 4096 ++#define UBIFS_BLOCK_SHIFT 12 ++#define UBIFS_BLOCK_MASK 0x00000FFF ++ ++/* UBIFS padding byte pattern (must not be first or last byte of node magic) */ ++#define UBIFS_PADDING_BYTE 0xCE ++ ++/* Maximum possible key length */ ++#define UBIFS_MAX_KEY_LEN 16 ++ ++/* Key length ("simple" format) */ ++#define UBIFS_SK_LEN 8 ++ ++/* Minimum index tree fanout */ ++#define UBIFS_MIN_FANOUT 2 ++ ++/* Maximum number of levels in UBIFS indexing B-tree */ ++#define UBIFS_MAX_LEVELS 512 ++ ++/* Maximum amount of data attached to an inode in bytes */ ++#define UBIFS_MAX_INO_DATA UBIFS_BLOCK_SIZE ++ ++/* LEB Properties Tree fanout (must be power of 2) and fanout shift */ ++#define UBIFS_LPT_FANOUT 4 ++#define UBIFS_LPT_FANOUT_SHIFT 2 ++ ++/* LEB Properties Tree bit field sizes */ ++#define UBIFS_LPT_CRC_BITS 16 ++#define UBIFS_LPT_CRC_BYTES 2 ++#define UBIFS_LPT_TYPE_BITS 4 ++ ++/* The key is always at the same position in all keyed nodes */ ++#define UBIFS_KEY_OFFSET offsetof(struct ubifs_ino_node, key) ++ ++/* ++ * LEB Properties Tree node types. ++ * ++ * UBIFS_LPT_PNODE: LPT leaf node (contains LEB properties) ++ * UBIFS_LPT_NNODE: LPT internal node ++ * UBIFS_LPT_LTAB: LPT's own lprops table ++ * UBIFS_LPT_LSAVE: LPT's save table (big model only) ++ * UBIFS_LPT_NODE_CNT: count of LPT node types ++ * UBIFS_LPT_NOT_A_NODE: all ones (15 for 4 bits) is never a valid node type ++ */ ++enum { ++ UBIFS_LPT_PNODE, ++ UBIFS_LPT_NNODE, ++ UBIFS_LPT_LTAB, ++ UBIFS_LPT_LSAVE, ++ UBIFS_LPT_NODE_CNT, ++ UBIFS_LPT_NOT_A_NODE = (1 << UBIFS_LPT_TYPE_BITS) - 1, ++}; ++ ++/* ++ * UBIFS inode types. ++ * ++ * UBIFS_ITYPE_REG: regular file ++ * UBIFS_ITYPE_DIR: directory ++ * UBIFS_ITYPE_LNK: soft link ++ * UBIFS_ITYPE_BLK: block device node ++ * UBIFS_ITYPE_CHR: character device node ++ * UBIFS_ITYPE_FIFO: fifo ++ * UBIFS_ITYPE_SOCK: socket ++ * UBIFS_ITYPES_CNT: count of supported file types ++ */ ++enum { ++ UBIFS_ITYPE_REG, ++ UBIFS_ITYPE_DIR, ++ UBIFS_ITYPE_LNK, ++ UBIFS_ITYPE_BLK, ++ UBIFS_ITYPE_CHR, ++ UBIFS_ITYPE_FIFO, ++ UBIFS_ITYPE_SOCK, ++ UBIFS_ITYPES_CNT, ++}; ++ ++/* ++ * Supported key hash functions. ++ * ++ * UBIFS_KEY_HASH_R5: R5 hash ++ * UBIFS_KEY_HASH_TEST: test hash which just returns first 4 bytes of the name ++ */ ++enum { ++ UBIFS_KEY_HASH_R5, ++ UBIFS_KEY_HASH_TEST, ++}; ++ ++/* ++ * Supported key formats. ++ * ++ * UBIFS_SIMPLE_KEY_FMT: simple key format ++ */ ++enum { ++ UBIFS_SIMPLE_KEY_FMT, ++}; ++ ++/* ++ * The simple key format uses 29 bits for storing UBIFS block number and hash ++ * value. ++ */ ++#define UBIFS_S_KEY_BLOCK_BITS 29 ++#define UBIFS_S_KEY_BLOCK_MASK 0x1FFFFFFF ++#define UBIFS_S_KEY_HASH_BITS UBIFS_S_KEY_BLOCK_BITS ++#define UBIFS_S_KEY_HASH_MASK UBIFS_S_KEY_BLOCK_MASK ++ ++/* ++ * Key types. ++ * ++ * UBIFS_INO_KEY: inode node key ++ * UBIFS_DATA_KEY: data node key ++ * UBIFS_DENT_KEY: directory entry node key ++ * UBIFS_XENT_KEY: extended attribute entry key ++ * UBIFS_TRUN_KEY: truncation node key ++ * UBIFS_KEY_TYPES_CNT: number of supported key types ++ */ ++enum { ++ UBIFS_INO_KEY, ++ UBIFS_DATA_KEY, ++ UBIFS_DENT_KEY, ++ UBIFS_XENT_KEY, ++ UBIFS_TRUN_KEY, ++ UBIFS_KEY_TYPES_CNT, ++}; ++ ++/* Count of LEBs reserved for the superblock area */ ++#define UBIFS_SB_LEBS 1 ++/* Count of LEBs reserved for the master area */ ++#define UBIFS_MST_LEBS 2 ++ ++/* First LEB of the superblock area */ ++#define UBIFS_SB_LNUM 0 ++/* First LEB of the master area */ ++#define UBIFS_MST_LNUM (UBIFS_SB_LNUM + UBIFS_SB_LEBS) ++/* First LEB of the log area */ ++#define UBIFS_LOG_LNUM (UBIFS_MST_LNUM + UBIFS_MST_LEBS) ++ ++/* Minimum number of logical eraseblocks in the log */ ++#define UBIFS_MIN_LOG_LEBS 2 ++/* Minimum number of bud logical eraseblocks */ ++#define UBIFS_MIN_BUD_LEBS 2 ++/* Minimum number of journal logical eraseblocks */ ++#define UBIFS_MIN_JNL_LEBS (UBIFS_MIN_LOG_LEBS + UBIFS_MIN_BUD_LEBS) ++/* Minimum number of LPT area logical eraseblocks */ ++#define UBIFS_MIN_LPT_LEBS 2 ++/* Minimum number of orphan area logical eraseblocks */ ++#define UBIFS_MIN_ORPH_LEBS 1 ++/* Minimum number of main area logical eraseblocks */ ++#define UBIFS_MIN_MAIN_LEBS 8 ++ ++/* Minimum number of logical eraseblocks */ ++#define UBIFS_MIN_LEB_CNT (UBIFS_SB_LEBS + UBIFS_MST_LEBS + \ ++ UBIFS_MIN_LOG_LEBS + UBIFS_MIN_BUD_LEBS + \ ++ UBIFS_MIN_LPT_LEBS + UBIFS_MIN_ORPH_LEBS + \ ++ UBIFS_MIN_MAIN_LEBS) ++ ++/* Node sizes (N.B. these are guaranteed to be multiples of 8) */ ++#define UBIFS_CH_SZ sizeof(struct ubifs_ch) ++#define UBIFS_INO_NODE_SZ sizeof(struct ubifs_ino_node) ++#define UBIFS_DATA_NODE_SZ sizeof(struct ubifs_data_node) ++#define UBIFS_DENT_NODE_SZ sizeof(struct ubifs_dent_node) ++#define UBIFS_TRUN_NODE_SZ sizeof(struct ubifs_trun_node) ++#define UBIFS_PAD_NODE_SZ sizeof(struct ubifs_pad_node) ++#define UBIFS_SB_NODE_SZ sizeof(struct ubifs_sb_node) ++#define UBIFS_MST_NODE_SZ sizeof(struct ubifs_mst_node) ++#define UBIFS_REF_NODE_SZ sizeof(struct ubifs_ref_node) ++#define UBIFS_IDX_NODE_SZ sizeof(struct ubifs_idx_node) ++#define UBIFS_CS_NODE_SZ sizeof(struct ubifs_cs_node) ++#define UBIFS_ORPH_NODE_SZ sizeof(struct ubifs_orph_node) ++/* Extended attribute entry nodes are identical to directory entry nodes */ ++#define UBIFS_XENT_NODE_SZ UBIFS_DENT_NODE_SZ ++/* Only this does not have to be multiple of 8 bytes */ ++#define UBIFS_BRANCH_SZ sizeof(struct ubifs_branch) ++ ++/* Maximum node sizes (N.B. these are guaranteed to be multiples of 8) */ ++#define UBIFS_MAX_DATA_NODE_SZ (UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE) ++#define UBIFS_MAX_INO_NODE_SZ (UBIFS_INO_NODE_SZ + UBIFS_MAX_INO_DATA) ++#define UBIFS_MAX_DENT_NODE_SZ (UBIFS_DENT_NODE_SZ + UBIFS_MAX_NLEN + 1) ++#define UBIFS_MAX_XENT_NODE_SZ UBIFS_MAX_DENT_NODE_SZ ++ ++/* The largest UBIFS node */ ++#define UBIFS_MAX_NODE_SZ UBIFS_MAX_INO_NODE_SZ ++ ++/* ++ * On-flash inode flags. ++ * ++ * UBIFS_COMPR_FL: use compression for this inode ++ * UBIFS_SYNC_FL: I/O on this inode has to be synchronous ++ * UBIFS_IMMUTABLE_FL: inode is immutable ++ * UBIFS_APPEND_FL: writes to the inode may only append data ++ * UBIFS_DIRSYNC_FL: I/O on this directory inode has to be synchronous ++ * ++ * Note, these are on-flash flags which correspond to ioctl flags ++ * (@FS_COMPR_FL, etc). They have the same values now, but generally, do not ++ * have to be the same. ++ */ ++enum { ++ UBIFS_COMPR_FL = 0x01, ++ UBIFS_SYNC_FL = 0x02, ++ UBIFS_IMMUTABLE_FL = 0x04, ++ UBIFS_APPEND_FL = 0x08, ++ UBIFS_DIRSYNC_FL = 0x10, ++}; ++ ++/* Inode flag bits used by UBIFS */ ++#define UBIFS_FL_MASK 0x0000001F ++ ++/* ++ * UBIFS compression algorithms. ++ * ++ * UBIFS_COMPR_NONE: no compression ++ * UBIFS_COMPR_LZO: LZO compression ++ * UBIFS_COMPR_ZLIB: ZLIB compression ++ * UBIFS_COMPR_TYPES_CNT: count of supported compression types ++ */ ++enum { ++ UBIFS_COMPR_NONE, ++ UBIFS_COMPR_LZO, ++ UBIFS_COMPR_ZLIB, ++ UBIFS_COMPR_TYPES_CNT, ++}; ++ ++/* ++ * UBIFS node types. ++ * ++ * UBIFS_INO_NODE: inode node ++ * UBIFS_DATA_NODE: data node ++ * UBIFS_DENT_NODE: directory entry node ++ * UBIFS_XENT_NODE: extended attribute node ++ * UBIFS_TRUN_NODE: truncation node ++ * UBIFS_PAD_NODE: padding node ++ * UBIFS_SB_NODE: superblock node ++ * UBIFS_MST_NODE: master node ++ * UBIFS_REF_NODE: LEB reference node ++ * UBIFS_IDX_NODE: index node ++ * UBIFS_CS_NODE: commit start node ++ * UBIFS_ORPH_NODE: orphan node ++ * UBIFS_NODE_TYPES_CNT: count of supported node types ++ * ++ * Note, we index arrays by these numbers, so keep them low and contiguous. ++ * Node type constants for inodes, direntries and so on have to be the same as ++ * corresponding key type constants. ++ */ ++enum { ++ UBIFS_INO_NODE, ++ UBIFS_DATA_NODE, ++ UBIFS_DENT_NODE, ++ UBIFS_XENT_NODE, ++ UBIFS_TRUN_NODE, ++ UBIFS_PAD_NODE, ++ UBIFS_SB_NODE, ++ UBIFS_MST_NODE, ++ UBIFS_REF_NODE, ++ UBIFS_IDX_NODE, ++ UBIFS_CS_NODE, ++ UBIFS_ORPH_NODE, ++ UBIFS_NODE_TYPES_CNT, ++}; ++ ++/* ++ * Master node flags. ++ * ++ * UBIFS_MST_DIRTY: rebooted uncleanly - master node is dirty ++ * UBIFS_MST_NO_ORPHS: no orphan inodes present ++ * UBIFS_MST_RCVRY: written by recovery ++ */ ++enum { ++ UBIFS_MST_DIRTY = 1, ++ UBIFS_MST_NO_ORPHS = 2, ++ UBIFS_MST_RCVRY = 4, ++}; ++ ++/* ++ * Node group type (used by recovery to recover whole group or none). ++ * ++ * UBIFS_NO_NODE_GROUP: this node is not part of a group ++ * UBIFS_IN_NODE_GROUP: this node is a part of a group ++ * UBIFS_LAST_OF_NODE_GROUP: this node is the last in a group ++ */ ++enum { ++ UBIFS_NO_NODE_GROUP = 0, ++ UBIFS_IN_NODE_GROUP, ++ UBIFS_LAST_OF_NODE_GROUP, ++}; ++ ++/* ++ * Superblock flags. ++ * ++ * UBIFS_FLG_BIGLPT: if "big" LPT model is used if set ++ */ ++enum { ++ UBIFS_FLG_BIGLPT = 0x02, ++}; ++ ++/** ++ * struct ubifs_ch - common header node. ++ * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC) ++ * @crc: CRC-32 checksum of the node header ++ * @sqnum: sequence number ++ * @len: full node length ++ * @node_type: node type ++ * @group_type: node group type ++ * @padding: reserved for future, zeroes ++ * ++ * Every UBIFS node starts with this common part. If the node has a key, the ++ * key always goes next. ++ */ ++struct ubifs_ch { ++ __le32 magic; ++ __le32 crc; ++ __le64 sqnum; ++ __le32 len; ++ __u8 node_type; ++ __u8 group_type; ++ __u8 padding[2]; ++} __attribute__ ((packed)); ++ ++/** ++ * union ubifs_dev_desc - device node descriptor. ++ * @new: new type device descriptor ++ * @huge: huge type device descriptor ++ * ++ * This data structure describes major/minor numbers of a device node. In an ++ * inode is a device node then its data contains an object of this type. UBIFS ++ * uses standard Linux "new" and "huge" device node encodings. ++ */ ++union ubifs_dev_desc { ++ __le32 new; ++ __le64 huge; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_ino_node - inode node. ++ * @ch: common header ++ * @key: node key ++ * @creat_sqnum: sequence number at time of creation ++ * @size: inode size in bytes (amount of uncompressed data) ++ * @atime_sec: access time seconds ++ * @ctime_sec: creation time seconds ++ * @mtime_sec: modification time seconds ++ * @atime_nsec: access time nanoseconds ++ * @ctime_nsec: creation time nanoseconds ++ * @mtime_nsec: modification time nanoseconds ++ * @nlink: number of hard links ++ * @uid: owner ID ++ * @gid: group ID ++ * @mode: access flags ++ * @flags: per-inode flags (%UBIFS_COMPR_FL, %UBIFS_SYNC_FL, etc) ++ * @data_len: inode data length ++ * @xattr_cnt: count of extended attributes this inode has ++ * @xattr_size: summarized size of all extended attributes in bytes ++ * @xattr_names: sum of lengths of all extended attribute names belonging to ++ * this inode ++ * @compr_type: compression type used for this inode ++ * @padding: reserved for future, zeroes ++ * @data: data attached to the inode ++ * ++ * Note, even though inode compression type is defined by @compr_type, some ++ * nodes of this inode may be compressed with different compressor - this ++ * happens if compression type is changed while the inode already has data ++ * nodes. But @compr_type will be use for further writes to the inode. ++ * ++ * Note, do not forget to amend 'zero_ino_node_unused()' function when changing ++ * the padding fields. ++ */ ++struct ubifs_ino_node { ++ struct ubifs_ch ch; ++ __u8 key[UBIFS_MAX_KEY_LEN]; ++ __le64 creat_sqnum; ++ __le64 size; ++ __le64 atime_sec; ++ __le64 ctime_sec; ++ __le64 mtime_sec; ++ __le32 atime_nsec; ++ __le32 ctime_nsec; ++ __le32 mtime_nsec; ++ __le32 nlink; ++ __le32 uid; ++ __le32 gid; ++ __le32 mode; ++ __le32 flags; ++ __le32 data_len; ++ __le32 xattr_cnt; ++ __le64 xattr_size; ++ __le32 xattr_names; ++ __le16 compr_type; ++ __u8 padding[26]; /* Watch 'zero_ino_node_unused()' if changing! */ ++ __u8 data[]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_dent_node - directory entry node. ++ * @ch: common header ++ * @key: node key ++ * @inum: target inode number ++ * @padding1: reserved for future, zeroes ++ * @type: type of the target inode (%UBIFS_ITYPE_REG, %UBIFS_ITYPE_DIR, etc) ++ * @nlen: name length ++ * @padding2: reserved for future, zeroes ++ * @name: zero-terminated name ++ * ++ * Note, do not forget to amend 'zero_dent_node_unused()' function when ++ * changing the padding fields. ++ */ ++struct ubifs_dent_node { ++ struct ubifs_ch ch; ++ __u8 key[UBIFS_MAX_KEY_LEN]; ++ __le64 inum; ++ __u8 padding1; ++ __u8 type; ++ __le16 nlen; ++ __u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */ ++ __u8 name[]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_data_node - data node. ++ * @ch: common header ++ * @key: node key ++ * @size: uncompressed data size in bytes ++ * @compr_type: compression type (%UBIFS_COMPR_NONE, %UBIFS_COMPR_LZO, etc) ++ * @padding: reserved for future, zeroes ++ * @data: data ++ * ++ * Note, do not forget to amend 'zero_data_node_unused()' function when ++ * changing the padding fields. ++ */ ++struct ubifs_data_node { ++ struct ubifs_ch ch; ++ __u8 key[UBIFS_MAX_KEY_LEN]; ++ __le32 size; ++ __le16 compr_type; ++ __u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */ ++ __u8 data[]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_trun_node - truncation node. ++ * @ch: common header ++ * @key: truncation node key ++ * @old_size: size before truncation ++ * @new_size: size after truncation ++ * ++ * This node exists only in the journal and never goes to the main area. ++ */ ++struct ubifs_trun_node { ++ struct ubifs_ch ch; ++ __u8 key[UBIFS_MAX_KEY_LEN]; ++ __le64 old_size; ++ __le64 new_size; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_pad_node - padding node. ++ * @ch: common header ++ * @pad_len: how many bytes after this node are unused (because padded) ++ * @padding: reserved for future, zeroes ++ */ ++struct ubifs_pad_node { ++ struct ubifs_ch ch; ++ __le32 pad_len; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_sb_node - superblock node. ++ * @ch: common header ++ * @padding: reserved for future, zeroes ++ * @key_hash: type of hash function used in keys ++ * @key_fmt: format of the key ++ * @flags: file-system flags (%UBIFS_FLG_BIGLPT, etc) ++ * @min_io_size: minimal input/output unit size ++ * @leb_size: logical eraseblock size in bytes ++ * @leb_cnt: count of LEBs used by filesystem ++ * @max_leb_cnt: maximum count of LEBs used by filesystem ++ * @max_bud_bytes: maximum amount of data stored in buds ++ * @log_lebs: log size in logical eraseblocks ++ * @lpt_lebs: number of LEBs used for lprops table ++ * @orph_lebs: number of LEBs used for recording orphans ++ * @jhead_cnt: count of journal heads ++ * @fanout: tree fanout (max. number of links per indexing node) ++ * @lsave_cnt: number of LEB numbers in LPT's save table ++ * @fmt_version: UBIFS on-flash format version ++ * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc) ++ * @padding1: reserved for future, zeroes ++ * @rp_uid: reserve pool UID ++ * @rp_gid: reserve pool GID ++ * @rp_size: size of the reserved pool in bytes ++ * @padding2: reserved for future, zeroes ++ * @time_gran: time granularity in nanoseconds ++ */ ++struct ubifs_sb_node { ++ struct ubifs_ch ch; ++ __u8 padding[2]; ++ __u8 key_hash; ++ __u8 key_fmt; ++ __le32 flags; ++ __le32 min_io_size; ++ __le32 leb_size; ++ __le32 leb_cnt; ++ __le32 max_leb_cnt; ++ __le64 max_bud_bytes; ++ __le32 log_lebs; ++ __le32 lpt_lebs; ++ __le32 orph_lebs; ++ __le32 jhead_cnt; ++ __le32 fanout; ++ __le32 lsave_cnt; ++ __le32 fmt_version; ++ __le16 default_compr; ++ __u8 padding1[2]; ++ __le32 rp_uid; ++ __le32 rp_gid; ++ __le64 rp_size; ++ __le32 time_gran; ++ __u8 padding2[3988]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_mst_node - master node. ++ * @ch: common header ++ * @highest_inum: highest inode number in the committed index ++ * @cmt_no: commit number ++ * @flags: various flags (%UBIFS_MST_DIRTY, etc) ++ * @log_lnum: start of the log ++ * @root_lnum: LEB number of the root indexing node ++ * @root_offs: offset within @root_lnum ++ * @root_len: root indexing node length ++ * @gc_lnum: LEB reserved for garbage collection (%-1 value means the LEB was ++ * not reserved and should be reserved on mount) ++ * @ihead_lnum: LEB number of index head ++ * @ihead_offs: offset of index head ++ * @index_size: size of index on flash ++ * @total_free: total free space in bytes ++ * @total_dirty: total dirty space in bytes ++ * @total_used: total used space in bytes (includes only data LEBs) ++ * @total_dead: total dead space in bytes (includes only data LEBs) ++ * @total_dark: total dark space in bytes (includes only data LEBs) ++ * @lpt_lnum: LEB number of LPT root nnode ++ * @lpt_offs: offset of LPT root nnode ++ * @nhead_lnum: LEB number of LPT head ++ * @nhead_offs: offset of LPT head ++ * @ltab_lnum: LEB number of LPT's own lprops table ++ * @ltab_offs: offset of LPT's own lprops table ++ * @lsave_lnum: LEB number of LPT's save table (big model only) ++ * @lsave_offs: offset of LPT's save table (big model only) ++ * @lscan_lnum: LEB number of last LPT scan ++ * @empty_lebs: number of empty logical eraseblocks ++ * @idx_lebs: number of indexing logical eraseblocks ++ * @leb_cnt: count of LEBs used by filesystem ++ * @padding: reserved for future, zeroes ++ */ ++struct ubifs_mst_node { ++ struct ubifs_ch ch; ++ __le64 highest_inum; ++ __le64 cmt_no; ++ __le32 flags; ++ __le32 log_lnum; ++ __le32 root_lnum; ++ __le32 root_offs; ++ __le32 root_len; ++ __le32 gc_lnum; ++ __le32 ihead_lnum; ++ __le32 ihead_offs; ++ __le64 index_size; ++ __le64 total_free; ++ __le64 total_dirty; ++ __le64 total_used; ++ __le64 total_dead; ++ __le64 total_dark; ++ __le32 lpt_lnum; ++ __le32 lpt_offs; ++ __le32 nhead_lnum; ++ __le32 nhead_offs; ++ __le32 ltab_lnum; ++ __le32 ltab_offs; ++ __le32 lsave_lnum; ++ __le32 lsave_offs; ++ __le32 lscan_lnum; ++ __le32 empty_lebs; ++ __le32 idx_lebs; ++ __le32 leb_cnt; ++ __u8 padding[344]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_ref_node - logical eraseblock reference node. ++ * @ch: common header ++ * @lnum: the referred logical eraseblock number ++ * @offs: start offset in the referred LEB ++ * @jhead: journal head number ++ * @padding: reserved for future, zeroes ++ */ ++struct ubifs_ref_node { ++ struct ubifs_ch ch; ++ __le32 lnum; ++ __le32 offs; ++ __le32 jhead; ++ __u8 padding[28]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_branch - key/reference/length branch ++ * @lnum: LEB number of the target node ++ * @offs: offset within @lnum ++ * @len: target node length ++ * @key: key ++ */ ++struct ubifs_branch { ++ __le32 lnum; ++ __le32 offs; ++ __le32 len; ++ __u8 key[]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_idx_node - indexing node. ++ * @ch: common header ++ * @child_cnt: number of child index nodes ++ * @level: tree level ++ * @branches: LEB number / offset / length / key branches ++ */ ++struct ubifs_idx_node { ++ struct ubifs_ch ch; ++ __le16 child_cnt; ++ __le16 level; ++ __u8 branches[]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_cs_node - commit start node. ++ * @ch: common header ++ * @cmt_no: commit number ++ */ ++struct ubifs_cs_node { ++ struct ubifs_ch ch; ++ __le64 cmt_no; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_orph_node - orphan node. ++ * @ch: common header ++ * @cmt_no: commit number (also top bit is set on the last node of the commit) ++ * @inos: inode numbers of orphans ++ */ ++struct ubifs_orph_node { ++ struct ubifs_ch ch; ++ __le64 cmt_no; ++ __le64 inos[]; ++} __attribute__ ((packed)); ++ ++#endif /* __UBIFS_MEDIA_H__ */ +diff -urN linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/ubifs.h linux-2.6.24.7/fs/ubifs/mkfs.ubifs/ubifs.h +--- linux-2.6.24.7.old/fs/ubifs/mkfs.ubifs/ubifs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/mkfs.ubifs/ubifs.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,424 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2008 Nokia Corporation. ++ * Copyright (C) 2008 University of Szeged, Hungary ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy ++ * Adrian Hunter ++ * Zoltan Sogor ++ */ ++ ++#ifndef __UBIFS_H__ ++#define __UBIFS_H__ ++ ++/* Minimum amount of data UBIFS writes to the flash */ ++#define MIN_WRITE_SZ (UBIFS_DATA_NODE_SZ + 8) ++ ++/* Largest key size supported in this implementation */ ++#define CUR_MAX_KEY_LEN UBIFS_SK_LEN ++ ++/* The below union makes it easier to deal with keys */ ++union ubifs_key ++{ ++ uint8_t u8[CUR_MAX_KEY_LEN]; ++ uint32_t u32[CUR_MAX_KEY_LEN/4]; ++ uint64_t u64[CUR_MAX_KEY_LEN/8]; ++ __le32 j32[CUR_MAX_KEY_LEN/4]; ++}; ++ ++/* ++ * LEB properties flags. ++ * ++ * LPROPS_UNCAT: not categorized ++ * LPROPS_DIRTY: dirty > 0, not index ++ * LPROPS_DIRTY_IDX: dirty + free > UBIFS_CH_SZ and index ++ * LPROPS_FREE: free > 0, not empty, not index ++ * LPROPS_HEAP_CNT: number of heaps used for storing categorized LEBs ++ * LPROPS_EMPTY: LEB is empty, not taken ++ * LPROPS_FREEABLE: free + dirty == leb_size, not index, not taken ++ * LPROPS_FRDI_IDX: free + dirty == leb_size and index, may be taken ++ * LPROPS_CAT_MASK: mask for the LEB categories above ++ * LPROPS_TAKEN: LEB was taken (this flag is not saved on the media) ++ * LPROPS_INDEX: LEB contains indexing nodes (this flag also exists on flash) ++ */ ++enum { ++ LPROPS_UNCAT = 0, ++ LPROPS_DIRTY = 1, ++ LPROPS_DIRTY_IDX = 2, ++ LPROPS_FREE = 3, ++ LPROPS_HEAP_CNT = 3, ++ LPROPS_EMPTY = 4, ++ LPROPS_FREEABLE = 5, ++ LPROPS_FRDI_IDX = 6, ++ LPROPS_CAT_MASK = 15, ++ LPROPS_TAKEN = 16, ++ LPROPS_INDEX = 32, ++}; ++ ++/** ++ * struct ubifs_lprops - logical eraseblock properties. ++ * @free: amount of free space in bytes ++ * @dirty: amount of dirty space in bytes ++ * @flags: LEB properties flags (see above) ++ */ ++struct ubifs_lprops ++{ ++ int free; ++ int dirty; ++ int flags; ++}; ++ ++/** ++ * struct ubifs_lpt_lprops - LPT logical eraseblock properties. ++ * @free: amount of free space in bytes ++ * @dirty: amount of dirty space in bytes ++ */ ++struct ubifs_lpt_lprops ++{ ++ int free; ++ int dirty; ++}; ++ ++struct ubifs_nnode; ++ ++/** ++ * struct ubifs_cnode - LEB Properties Tree common node. ++ * @parent: parent nnode ++ * @cnext: next cnode to commit ++ * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) ++ * @iip: index in parent ++ * @level: level in the tree (zero for pnodes, greater than zero for nnodes) ++ * @num: node number ++ */ ++struct ubifs_cnode ++{ ++ struct ubifs_nnode *parent; ++ struct ubifs_cnode *cnext; ++ unsigned long flags; ++ int iip; ++ int level; ++ int num; ++}; ++ ++/** ++ * struct ubifs_pnode - LEB Properties Tree leaf node. ++ * @parent: parent nnode ++ * @cnext: next cnode to commit ++ * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) ++ * @iip: index in parent ++ * @level: level in the tree (always zero for pnodes) ++ * @num: node number ++ * @lprops: LEB properties array ++ */ ++struct ubifs_pnode ++{ ++ struct ubifs_nnode *parent; ++ struct ubifs_cnode *cnext; ++ unsigned long flags; ++ int iip; ++ int level; ++ int num; ++ struct ubifs_lprops lprops[UBIFS_LPT_FANOUT]; ++}; ++ ++/** ++ * struct ubifs_nbranch - LEB Properties Tree internal node branch. ++ * @lnum: LEB number of child ++ * @offs: offset of child ++ * @nnode: nnode child ++ * @pnode: pnode child ++ * @cnode: cnode child ++ */ ++struct ubifs_nbranch ++{ ++ int lnum; ++ int offs; ++ union ++ { ++ struct ubifs_nnode *nnode; ++ struct ubifs_pnode *pnode; ++ struct ubifs_cnode *cnode; ++ }; ++}; ++ ++/** ++ * struct ubifs_nnode - LEB Properties Tree internal node. ++ * @parent: parent nnode ++ * @cnext: next cnode to commit ++ * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) ++ * @iip: index in parent ++ * @level: level in the tree (always greater than zero for nnodes) ++ * @num: node number ++ * @nbranch: branches to child nodes ++ */ ++struct ubifs_nnode ++{ ++ struct ubifs_nnode *parent; ++ struct ubifs_cnode *cnext; ++ unsigned long flags; ++ int iip; ++ int level; ++ int num; ++ struct ubifs_nbranch nbranch[UBIFS_LPT_FANOUT]; ++}; ++ ++/** ++ * struct ubifs_lp_stats - statistics of eraseblocks in the main area. ++ * @empty_lebs: number of empty LEBs ++ * @taken_empty_lebs: number of taken LEBs ++ * @idx_lebs: number of indexing LEBs ++ * @total_free: total free space in bytes ++ * @total_dirty: total dirty space in bytes ++ * @total_used: total used space in bytes (includes only data LEBs) ++ * @total_dead: total dead space in bytes (includes only data LEBs) ++ * @total_dark: total dark space in bytes (includes only data LEBs) ++ */ ++struct ubifs_lp_stats { ++ int empty_lebs; ++ int taken_empty_lebs; ++ int idx_lebs; ++ long long total_free; ++ long long total_dirty; ++ long long total_used; ++ long long total_dead; ++ long long total_dark; ++}; ++ ++/** ++ * struct ubifs_zbranch - key/coordinate/length branch stored in znodes. ++ * @key: key ++ * @znode: znode address in memory ++ * @lnum: LEB number of the indexing node ++ * @offs: offset of the indexing node within @lnum ++ * @len: target node length ++ */ ++struct ubifs_zbranch ++{ ++ union ubifs_key key; ++ struct ubifs_znode *znode; ++ int lnum; ++ int offs; ++ int len; ++}; ++ ++/** ++ * struct ubifs_znode - in-memory representation of an indexing node. ++ * @parent: parent znode or NULL if it is the root ++ * @cnext: next znode to commit ++ * @flags: flags ++ * @time: last access time (seconds) ++ * @level: level of the entry in the TNC tree ++ * @child_cnt: count of child znodes ++ * @iip: index in parent's zbranch array ++ * @alt: lower bound of key range has altered i.e. child inserted at slot 0 ++ * @zbranch: array of znode branches (@c->fanout elements) ++ */ ++struct ubifs_znode ++{ ++ struct ubifs_znode *parent; ++ struct ubifs_znode *cnext; ++ unsigned long flags; ++ unsigned long time; ++ int level; ++ int child_cnt; ++ int iip; ++ int alt; ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ int lnum, offs, len; ++#endif ++ struct ubifs_zbranch zbranch[]; ++}; ++ ++/** ++ * struct ubifs_info - UBIFS file-system description data structure ++ * (per-superblock). ++ * ++ * @highest_inum: highest used inode number ++ * @max_sqnum: current global sequence number ++ * ++ * @jhead_cnt: count of journal heads ++ * @max_bud_bytes: maximum number of bytes allowed in buds ++ * ++ * @zroot: zbranch which points to the root index node and znode ++ * @ihead_lnum: LEB number of index head ++ * @ihead_offs: offset of index head ++ * ++ * @log_lebs: number of logical eraseblocks in the log ++ * @lpt_lebs: number of LEBs used for lprops table ++ * @lpt_first: first LEB of the lprops table area ++ * @lpt_last: last LEB of the lprops table area ++ * @main_lebs: count of LEBs in the main area ++ * @main_first: first LEB of the main area ++ * @default_compr: default compression type ++ * ++ * @key_hash_type: type of the key hash ++ * @key_hash: direntry key hash function ++ * @key_len: key length ++ * @fanout: fanout of the index tree (number of links per indexing node) ++ * ++ * @min_io_size: minimal input/output unit size ++ * @leb_size: logical eraseblock size in bytes ++ * @leb_cnt: count of logical eraseblocks ++ * @max_leb_cnt: maximum count of logical eraseblocks ++ * ++ * @old_idx_sz: size of index on flash ++ * @lst: lprops statistics ++ * ++ * @dead_wm: LEB dead space watermark ++ * @dark_wm: LEB dark space watermark ++ * ++ * @gc_lnum: LEB number used for garbage collection ++ * @space_bits: number of bits needed to record free or dirty space ++ * @lpt_lnum_bits: number of bits needed to record a LEB number in the LPT ++ * @lpt_offs_bits: number of bits needed to record an offset in the LPT ++ * @lpt_spc_bits: number of bits needed to space in the LPT ++ * @pcnt_bits: number of bits needed to record pnode or nnode number ++ * @lnum_bits: number of bits needed to record LEB number ++ * @nnode_sz: size of on-flash nnode ++ * @pnode_sz: size of on-flash pnode ++ * @ltab_sz: size of on-flash LPT lprops table ++ * @lsave_sz: size of on-flash LPT save table ++ * @pnode_cnt: number of pnodes ++ * @nnode_cnt: number of nnodes ++ * @lpt_hght: height of the LPT ++ * ++ * @lpt_lnum: LEB number of the root nnode of the LPT ++ * @lpt_offs: offset of the root nnode of the LPT ++ * @nhead_lnum: LEB number of LPT head ++ * @nhead_offs: offset of LPT head ++ * @big_lpt: flag that LPT is too big to write whole during commit ++ * @lpt_sz: LPT size ++ * ++ * @ltab_lnum: LEB number of LPT's own lprops table ++ * @ltab_offs: offset of LPT's own lprops table ++ * @lpt: lprops table ++ * @ltab: LPT's own lprops table ++ * @lsave_cnt: number of LEB numbers in LPT's save table ++ * @lsave_lnum: LEB number of LPT's save table ++ * @lsave_offs: offset of LPT's save table ++ * @lsave: LPT's save table ++ * @lscan_lnum: LEB number of last LPT scan ++ */ ++struct ubifs_info ++{ ++ ino_t highest_inum; ++ unsigned long long max_sqnum; ++ ++ int jhead_cnt; ++ long long max_bud_bytes; ++ ++ struct ubifs_zbranch zroot; ++ int ihead_lnum; ++ int ihead_offs; ++ ++ int log_lebs; ++ int lpt_lebs; ++ int lpt_first; ++ int lpt_last; ++ int orph_lebs; ++ int main_lebs; ++ int main_first; ++ int default_compr; ++ ++ uint8_t key_hash_type; ++ uint32_t (*key_hash)(const char *str, int len); ++ int key_len; ++ int fanout; ++ ++ int min_io_size; ++ int leb_size; ++ int leb_cnt; ++ int max_leb_cnt; ++ ++ unsigned long long old_idx_sz; ++ struct ubifs_lp_stats lst; ++ ++ int dead_wm; ++ int dark_wm; ++ ++ int gc_lnum; ++ ++ int space_bits; ++ int lpt_lnum_bits; ++ int lpt_offs_bits; ++ int lpt_spc_bits; ++ int pcnt_bits; ++ int lnum_bits; ++ int nnode_sz; ++ int pnode_sz; ++ int ltab_sz; ++ int lsave_sz; ++ int pnode_cnt; ++ int nnode_cnt; ++ int lpt_hght; ++ ++ int lpt_lnum; ++ int lpt_offs; ++ int nhead_lnum; ++ int nhead_offs; ++ int big_lpt; ++ long long lpt_sz; ++ ++ int ltab_lnum; ++ int ltab_offs; ++ struct ubifs_lprops *lpt; ++ struct ubifs_lpt_lprops *ltab; ++ int lsave_cnt; ++ int lsave_lnum; ++ int lsave_offs; ++ int *lsave; ++ int lscan_lnum; ++ ++}; ++ ++/** ++ * ubifs_idx_node_sz - return index node size. ++ * @c: the UBIFS file-system description object ++ * @child_cnt: number of children of this index node ++ */ ++static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt) ++{ ++ return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len) * child_cnt; ++} ++ ++/** ++ * ubifs_idx_branch - return pointer to an index branch. ++ * @c: the UBIFS file-system description object ++ * @idx: index node ++ * @bnum: branch number ++ */ ++static inline ++struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c, ++ const struct ubifs_idx_node *idx, ++ int bnum) ++{ ++ return (struct ubifs_branch *)((void *)idx->branches + ++ (UBIFS_BRANCH_SZ + c->key_len) * bnum); ++} ++ ++/** ++ * ubifs_idx_key - return pointer to an index key. ++ * @c: the UBIFS file-system description object ++ * @idx: index node ++ */ ++static inline void *ubifs_idx_key(const struct ubifs_info *c, ++ const struct ubifs_idx_node *idx) ++{ ++ return (void *)((struct ubifs_branch *)idx->branches)->key; ++} ++ ++#endif /* __UBIFS_H__ */ +diff -urN linux-2.6.24.7.old/fs/ubifs/orphan.c linux-2.6.24.7/fs/ubifs/orphan.c +--- linux-2.6.24.7.old/fs/ubifs/orphan.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/orphan.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,959 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Author: Adrian Hunter ++ */ ++ ++#include "ubifs.h" ++ ++/* ++ * An orphan is an inode number whose inode node has been committed to the index ++ * with a link count of zero. That happens when an open file is deleted ++ * (unlinked) and then a commit is run. In the normal course of events the inode ++ * would be deleted when the file is closed. However in the case of an unclean ++ * unmount, orphans need to be accounted for. After an unclean unmount, the ++ * orphans' inodes must be deleted which means either scanning the entire index ++ * looking for them, or keeping a list on flash somewhere. This unit implements ++ * the latter approach. ++ * ++ * The orphan area is a fixed number of LEBs situated between the LPT area and ++ * the main area. The number of orphan area LEBs is specified when the file ++ * system is created. The minimum number is 1. The size of the orphan area ++ * should be so that it can hold the maximum number of orphans that are expected ++ * to ever exist at one time. ++ * ++ * The number of orphans that can fit in a LEB is: ++ * ++ * (c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64) ++ * ++ * For example: a 15872 byte LEB can fit 1980 orphans so 1 LEB may be enough. ++ * ++ * Orphans are accumulated in a rb-tree. When an inode's link count drops to ++ * zero, the inode number is added to the rb-tree. It is removed from the tree ++ * when the inode is deleted. Any new orphans that are in the orphan tree when ++ * the commit is run, are written to the orphan area in 1 or more orph nodes. ++ * If the orphan area is full, it is consolidated to make space. There is ++ * always enough space because validation prevents the user from creating more ++ * than the maximum number of orphans allowed. ++ */ ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++static int dbg_check_orphans(struct ubifs_info *c); ++#else ++#define dbg_check_orphans(c) 0 ++#endif ++ ++/** ++ * ubifs_add_orphan - add an orphan. ++ * @c: UBIFS file-system description object ++ * @inum: orphan inode number ++ * ++ * Add an orphan. This function is called when an inodes link count drops to ++ * zero. ++ */ ++int ubifs_add_orphan(struct ubifs_info *c, ino_t inum) ++{ ++ struct ubifs_orphan *orphan, *o; ++ struct rb_node **p, *parent = NULL; ++ ++ orphan = kzalloc(sizeof(struct ubifs_orphan), GFP_NOFS); ++ if (!orphan) ++ return -ENOMEM; ++ orphan->inum = inum; ++ orphan->new = 1; ++ ++ spin_lock(&c->orphan_lock); ++ if (c->tot_orphans >= c->max_orphans) { ++ spin_unlock(&c->orphan_lock); ++ kfree(orphan); ++ return -ENFILE; ++ } ++ p = &c->orph_tree.rb_node; ++ while (*p) { ++ parent = *p; ++ o = rb_entry(parent, struct ubifs_orphan, rb); ++ if (inum < o->inum) ++ p = &(*p)->rb_left; ++ else if (inum > o->inum) ++ p = &(*p)->rb_right; ++ else { ++ dbg_err("orphaned twice"); ++ spin_unlock(&c->orphan_lock); ++ kfree(orphan); ++ return 0; ++ } ++ } ++ c->tot_orphans += 1; ++ c->new_orphans += 1; ++ rb_link_node(&orphan->rb, parent, p); ++ rb_insert_color(&orphan->rb, &c->orph_tree); ++ list_add_tail(&orphan->list, &c->orph_list); ++ list_add_tail(&orphan->new_list, &c->orph_new); ++ spin_unlock(&c->orphan_lock); ++ dbg_gen("ino %lu", inum); ++ return 0; ++} ++ ++/** ++ * ubifs_delete_orphan - delete an orphan. ++ * @c: UBIFS file-system description object ++ * @inum: orphan inode number ++ * ++ * Delete an orphan. This function is called when an inode is deleted. ++ */ ++void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum) ++{ ++ struct ubifs_orphan *o; ++ struct rb_node *p; ++ ++ spin_lock(&c->orphan_lock); ++ p = c->orph_tree.rb_node; ++ while (p) { ++ o = rb_entry(p, struct ubifs_orphan, rb); ++ if (inum < o->inum) ++ p = p->rb_left; ++ else if (inum > o->inum) ++ p = p->rb_right; ++ else { ++ if (o->dnext) { ++ spin_unlock(&c->orphan_lock); ++ dbg_gen("deleted twice ino %lu", inum); ++ return; ++ } ++ if (o->cnext) { ++ o->dnext = c->orph_dnext; ++ c->orph_dnext = o; ++ spin_unlock(&c->orphan_lock); ++ dbg_gen("delete later ino %lu", inum); ++ return; ++ } ++ rb_erase(p, &c->orph_tree); ++ list_del(&o->list); ++ c->tot_orphans -= 1; ++ if (o->new) { ++ list_del(&o->new_list); ++ c->new_orphans -= 1; ++ } ++ spin_unlock(&c->orphan_lock); ++ kfree(o); ++ dbg_gen("inum %lu", inum); ++ return; ++ } ++ } ++ spin_unlock(&c->orphan_lock); ++ dbg_err("missing orphan ino %lu", inum); ++ dbg_dump_stack(); ++} ++ ++/** ++ * ubifs_orphan_start_commit - start commit of orphans. ++ * @c: UBIFS file-system description object ++ * ++ * Start commit of orphans. ++ */ ++int ubifs_orphan_start_commit(struct ubifs_info *c) ++{ ++ struct ubifs_orphan *orphan, **last; ++ ++ spin_lock(&c->orphan_lock); ++ last = &c->orph_cnext; ++ list_for_each_entry(orphan, &c->orph_new, new_list) { ++ ubifs_assert(orphan->new); ++ orphan->new = 0; ++ *last = orphan; ++ last = &orphan->cnext; ++ } ++ *last = orphan->cnext; ++ c->cmt_orphans = c->new_orphans; ++ c->new_orphans = 0; ++ dbg_cmt("%d orphans to commit", c->cmt_orphans); ++ INIT_LIST_HEAD(&c->orph_new); ++ if (c->tot_orphans == 0) ++ c->no_orphs = 1; ++ else ++ c->no_orphs = 0; ++ spin_unlock(&c->orphan_lock); ++ return 0; ++} ++ ++/** ++ * avail_orphs - calculate available space. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns the number of orphans that can be written in the ++ * available space. ++ */ ++static int avail_orphs(struct ubifs_info *c) ++{ ++ int avail_lebs, avail, gap; ++ ++ avail_lebs = c->orph_lebs - (c->ohead_lnum - c->orph_first) - 1; ++ avail = avail_lebs * ++ ((c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64)); ++ gap = c->leb_size - c->ohead_offs; ++ if (gap >= UBIFS_ORPH_NODE_SZ + sizeof(__le64)) ++ avail += (gap - UBIFS_ORPH_NODE_SZ) / sizeof(__le64); ++ return avail; ++} ++ ++/** ++ * tot_avail_orphs - calculate total space. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns the number of orphans that can be written in half ++ * the total space. That leaves half the space for adding new orphans. ++ */ ++static int tot_avail_orphs(struct ubifs_info *c) ++{ ++ int avail_lebs, avail; ++ ++ avail_lebs = c->orph_lebs; ++ avail = avail_lebs * ++ ((c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64)); ++ return avail / 2; ++} ++ ++/** ++ * do_write_orph_node - write a node ++ * @c: UBIFS file-system description object ++ * @len: length of node ++ * @atomic: write atomically ++ * ++ * This function writes a node to the orphan head from the orphan buffer. If ++ * %atomic is not zero, then the write is done atomically. On success, %0 is ++ * returned, otherwise a negative error code is returned. ++ */ ++static int do_write_orph_node(struct ubifs_info *c, int len, int atomic) ++{ ++ int err = 0; ++ ++ if (atomic) { ++ ubifs_assert(c->ohead_offs == 0); ++ ubifs_prepare_node(c, c->orph_buf, len, 1); ++ len = ALIGN(len, c->min_io_size); ++ err = ubi_leb_change(c->ubi, c->ohead_lnum, c->orph_buf, len, ++ UBI_SHORTTERM); ++ } else { ++ if (c->ohead_offs == 0) { ++ /* Ensure LEB has been unmapped */ ++ err = ubifs_leb_unmap(c, c->ohead_lnum); ++ if (err) ++ return err; ++ } ++ err = ubifs_write_node(c, c->orph_buf, len, c->ohead_lnum, ++ c->ohead_offs, UBI_SHORTTERM); ++ } ++ return err; ++} ++ ++/** ++ * write_orph_node - write an orph node ++ * @c: UBIFS file-system description object ++ * @atomic: write atomically ++ * ++ * This function builds an orph node from the cnext list and writes it to the ++ * orphan head. On success, %0 is returned, otherwise a negative error code ++ * is returned. ++ */ ++static int write_orph_node(struct ubifs_info *c, int atomic) ++{ ++ struct ubifs_orphan *orphan, *cnext; ++ struct ubifs_orph_node *orph; ++ int gap, err, len, cnt, i; ++ ++ ubifs_assert(c->cmt_orphans > 0); ++ gap = c->leb_size - c->ohead_offs; ++ if (gap < UBIFS_ORPH_NODE_SZ + sizeof(__le64)) { ++ c->ohead_lnum += 1; ++ c->ohead_offs = 0; ++ gap = c->leb_size; ++ if (c->ohead_lnum > c->orph_last) { ++ /* ++ * We limit the number of orphans so that this should ++ * never happen. ++ */ ++ ubifs_err("out of space in orphan area"); ++ return -EINVAL; ++ } ++ } ++ cnt = (gap - UBIFS_ORPH_NODE_SZ) / sizeof(__le64); ++ if (cnt > c->cmt_orphans) ++ cnt = c->cmt_orphans; ++ len = UBIFS_ORPH_NODE_SZ + cnt * sizeof(__le64); ++ ubifs_assert(c->orph_buf); ++ orph = c->orph_buf; ++ orph->ch.node_type = UBIFS_ORPH_NODE; ++ spin_lock(&c->orphan_lock); ++ cnext = c->orph_cnext; ++ for (i = 0; i < cnt; i++) { ++ orphan = cnext; ++ orph->inos[i] = cpu_to_le64(orphan->inum); ++ cnext = orphan->cnext; ++ orphan->cnext = NULL; ++ } ++ c->orph_cnext = cnext; ++ c->cmt_orphans -= cnt; ++ spin_unlock(&c->orphan_lock); ++ if (c->cmt_orphans) ++ orph->cmt_no = cpu_to_le64(c->cmt_no + 1); ++ else ++ /* Mark the last node of the commit */ ++ orph->cmt_no = cpu_to_le64((c->cmt_no + 1) | (1ULL << 63)); ++ ubifs_assert(c->ohead_offs + len <= c->leb_size); ++ ubifs_assert(c->ohead_lnum >= c->orph_first); ++ ubifs_assert(c->ohead_lnum <= c->orph_last); ++ err = do_write_orph_node(c, len, atomic); ++ c->ohead_offs += ALIGN(len, c->min_io_size); ++ c->ohead_offs = ALIGN(c->ohead_offs, 8); ++ return err; ++} ++ ++/** ++ * write_orph_nodes - write orph nodes until there are no more to commit ++ * @c: UBIFS file-system description object ++ * @atomic: write atomically ++ * ++ * This function writes orph nodes for all the orphans to commit. On success, ++ * %0 is returned, otherwise a negative error code is returned. ++ */ ++static int write_orph_nodes(struct ubifs_info *c, int atomic) ++{ ++ int err; ++ ++ while (c->cmt_orphans > 0) { ++ err = write_orph_node(c, atomic); ++ if (err) ++ return err; ++ } ++ if (atomic) { ++ int lnum; ++ ++ /* Unmap any unused LEBs after consolidation */ ++ lnum = c->ohead_lnum + 1; ++ for (lnum = c->ohead_lnum + 1; lnum <= c->orph_last; lnum++) { ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ return err; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * consolidate - consolidate the orphan area. ++ * @c: UBIFS file-system description object ++ * ++ * This function enables consolidation by putting all the orphans into the list ++ * to commit. The list is in the order that the orphans were added, and the ++ * LEBs are written atomically in order, so at no time can orphans be lost by ++ * an unclean unmount. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int consolidate(struct ubifs_info *c) ++{ ++ int tot_avail = tot_avail_orphs(c), err = 0; ++ ++ spin_lock(&c->orphan_lock); ++ dbg_cmt("there is space for %d orphans and there are %d", ++ tot_avail, c->tot_orphans); ++ if (c->tot_orphans - c->new_orphans <= tot_avail) { ++ struct ubifs_orphan *orphan, **last; ++ int cnt = 0; ++ ++ /* Change the cnext list to include all non-new orphans */ ++ last = &c->orph_cnext; ++ list_for_each_entry(orphan, &c->orph_list, list) { ++ if (orphan->new) ++ continue; ++ *last = orphan; ++ last = &orphan->cnext; ++ cnt += 1; ++ } ++ *last = orphan->cnext; ++ ubifs_assert(cnt == c->tot_orphans - c->new_orphans); ++ c->cmt_orphans = cnt; ++ c->ohead_lnum = c->orph_first; ++ c->ohead_offs = 0; ++ } else { ++ /* ++ * We limit the number of orphans so that this should ++ * never happen. ++ */ ++ ubifs_err("out of space in orphan area"); ++ err = -EINVAL; ++ } ++ spin_unlock(&c->orphan_lock); ++ return err; ++} ++ ++/** ++ * commit_orphans - commit orphans. ++ * @c: UBIFS file-system description object ++ * ++ * This function commits orphans to flash. On success, %0 is returned, ++ * otherwise a negative error code is returned. ++ */ ++static int commit_orphans(struct ubifs_info *c) ++{ ++ int avail, atomic = 0, err; ++ ++ ubifs_assert(c->cmt_orphans > 0); ++ avail = avail_orphs(c); ++ if (avail < c->cmt_orphans) { ++ /* Not enough space to write new orphans, so consolidate */ ++ err = consolidate(c); ++ if (err) ++ return err; ++ atomic = 1; ++ } ++ err = write_orph_nodes(c, atomic); ++ return err; ++} ++ ++/** ++ * erase_deleted - erase the orphans marked for deletion. ++ * @c: UBIFS file-system description object ++ * ++ * During commit, the orphans being committed cannot be deleted, so they are ++ * marked for deletion and deleted by this function. Also, the recovery ++ * adds killed orphans to the deletion list, and therefore they are deleted ++ * here too. ++ */ ++static void erase_deleted(struct ubifs_info *c) ++{ ++ struct ubifs_orphan *orphan, *dnext; ++ ++ spin_lock(&c->orphan_lock); ++ dnext = c->orph_dnext; ++ while (dnext) { ++ orphan = dnext; ++ dnext = orphan->dnext; ++ ubifs_assert(!orphan->new); ++ rb_erase(&orphan->rb, &c->orph_tree); ++ list_del(&orphan->list); ++ c->tot_orphans -= 1; ++ dbg_gen("deleting orphan ino %lu", orphan->inum); ++ kfree(orphan); ++ } ++ c->orph_dnext = NULL; ++ spin_unlock(&c->orphan_lock); ++} ++ ++/** ++ * ubifs_orphan_end_commit - end commit of orphans. ++ * @c: UBIFS file-system description object ++ * ++ * End commit of orphans. ++ */ ++int ubifs_orphan_end_commit(struct ubifs_info *c) ++{ ++ int err; ++ ++ if (c->cmt_orphans != 0) { ++ err = commit_orphans(c); ++ if (err) ++ return err; ++ } ++ erase_deleted(c); ++ err = dbg_check_orphans(c); ++ return err; ++} ++ ++/** ++ * clear_orphans - erase all LEBs used for orphans. ++ * @c: UBIFS file-system description object ++ * ++ * If recovery is not required, then the orphans from the previous session ++ * are not needed. This function locates the LEBs used to record ++ * orphans, and un-maps them. ++ */ ++static int clear_orphans(struct ubifs_info *c) ++{ ++ int lnum, err; ++ ++ for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) { ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ return err; ++ } ++ c->ohead_lnum = c->orph_first; ++ c->ohead_offs = 0; ++ return 0; ++} ++ ++/** ++ * insert_dead_orphan - insert an orphan. ++ * @c: UBIFS file-system description object ++ * @inum: orphan inode number ++ * ++ * This function is a helper to the 'do_kill_orphans()' function. The orphan ++ * must be kept until the next commit, so it is added to the rb-tree and the ++ * deletion list. ++ */ ++static int insert_dead_orphan(struct ubifs_info *c, ino_t inum) ++{ ++ struct ubifs_orphan *orphan, *o; ++ struct rb_node **p, *parent = NULL; ++ ++ orphan = kzalloc(sizeof(struct ubifs_orphan), GFP_KERNEL); ++ if (!orphan) ++ return -ENOMEM; ++ orphan->inum = inum; ++ ++ p = &c->orph_tree.rb_node; ++ while (*p) { ++ parent = *p; ++ o = rb_entry(parent, struct ubifs_orphan, rb); ++ if (inum < o->inum) ++ p = &(*p)->rb_left; ++ else if (inum > o->inum) ++ p = &(*p)->rb_right; ++ else { ++ /* Already added - no problem */ ++ kfree(orphan); ++ return 0; ++ } ++ } ++ c->tot_orphans += 1; ++ rb_link_node(&orphan->rb, parent, p); ++ rb_insert_color(&orphan->rb, &c->orph_tree); ++ list_add_tail(&orphan->list, &c->orph_list); ++ orphan->dnext = c->orph_dnext; ++ c->orph_dnext = orphan; ++ dbg_mnt("ino %lu, new %d, tot %d", ++ inum, c->new_orphans, c->tot_orphans); ++ return 0; ++} ++ ++/** ++ * do_kill_orphans - remove orphan inodes from the index. ++ * @c: UBIFS file-system description object ++ * @sleb: scanned LEB ++ * @last_cmt_no: cmt_no of last orph node read is passed and returned here ++ * @outofdate: whether the LEB is out of date is returned here ++ * @last_flagged: whether the end orph node is encountered ++ * ++ * This function is a helper to the 'kill_orphans()' function. It goes through ++ * every orphan node in a LEB and for every inode number recorded, removes ++ * all keys for that inode from the TNC. ++ */ ++static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb, ++ unsigned long long *last_cmt_no, int *outofdate, ++ int *last_flagged) ++{ ++ struct ubifs_scan_node *snod; ++ struct ubifs_orph_node *orph; ++ unsigned long long cmt_no; ++ ino_t inum; ++ int i, n, err, first = 1; ++ ++ list_for_each_entry(snod, &sleb->nodes, list) { ++ if (snod->type != UBIFS_ORPH_NODE) { ++ ubifs_err("invalid node type %d in orphan area at " ++ "%d:%d", snod->type, sleb->lnum, snod->offs); ++ dbg_dump_node(c, snod->node); ++ return -EINVAL; ++ } ++ ++ orph = snod->node; ++ ++ /* Check commit number */ ++ cmt_no = le64_to_cpu(orph->cmt_no) & LLONG_MAX; ++ /* ++ * The commit number on the master node may be less, because ++ * of a failed commit. If there are several failed commits in a ++ * row, the commit number written on orph nodes will continue to ++ * increase (because the commit number is adjusted here) even ++ * though the commit number on the master node stays the same ++ * because the master node has not been re-written. ++ */ ++ if (cmt_no > c->cmt_no) ++ c->cmt_no = cmt_no; ++ if (cmt_no < *last_cmt_no && *last_flagged) { ++ /* ++ * The last orph node had a higher commit number and was ++ * flagged as the last written for that commit number. ++ * That makes this orph node, out of date. ++ */ ++ if (!first) { ++ ubifs_err("out of order commit number %llu in " ++ "orphan node at %d:%d", ++ cmt_no, sleb->lnum, snod->offs); ++ dbg_dump_node(c, snod->node); ++ return -EINVAL; ++ } ++ dbg_rcvry("out of date LEB %d", sleb->lnum); ++ *outofdate = 1; ++ return 0; ++ } ++ ++ if (first) ++ first = 0; ++ ++ n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3; ++ for (i = 0; i < n; i++) { ++ inum = le64_to_cpu(orph->inos[i]); ++ dbg_rcvry("deleting orphaned inode %lu", inum); ++ err = ubifs_tnc_remove_ino(c, inum); ++ if (err) ++ return err; ++ err = insert_dead_orphan(c, inum); ++ if (err) ++ return err; ++ } ++ ++ *last_cmt_no = cmt_no; ++ if (le64_to_cpu(orph->cmt_no) & (1ULL << 63)) { ++ dbg_rcvry("last orph node for commit %llu at %d:%d", ++ cmt_no, sleb->lnum, snod->offs); ++ *last_flagged = 1; ++ } else ++ *last_flagged = 0; ++ } ++ ++ return 0; ++} ++ ++/** ++ * kill_orphans - remove all orphan inodes from the index. ++ * @c: UBIFS file-system description object ++ * ++ * If recovery is required, then orphan inodes recorded during the previous ++ * session (which ended with an unclean unmount) must be deleted from the index. ++ * This is done by updating the TNC, but since the index is not updated until ++ * the next commit, the LEBs where the orphan information is recorded are not ++ * erased until the next commit. ++ */ ++static int kill_orphans(struct ubifs_info *c) ++{ ++ unsigned long long last_cmt_no = 0; ++ int lnum, err = 0, outofdate = 0, last_flagged = 0; ++ ++ c->ohead_lnum = c->orph_first; ++ c->ohead_offs = 0; ++ /* Check no-orphans flag and skip this if no orphans */ ++ if (c->no_orphs) { ++ dbg_rcvry("no orphans"); ++ return 0; ++ } ++ /* ++ * Orph nodes always start at c->orph_first and are written to each ++ * successive LEB in turn. Generally unused LEBs will have been unmapped ++ * but may contain out of date orph nodes if the unmap didn't go ++ * through. In addition, the last orph node written for each commit is ++ * marked (top bit of orph->cmt_no is set to 1). It is possible that ++ * there are orph nodes from the next commit (i.e. the commit did not ++ * complete successfully). In that case, no orphans will have been lost ++ * due to the way that orphans are written, and any orphans added will ++ * be valid orphans anyway and so can be deleted. ++ */ ++ for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) { ++ struct ubifs_scan_leb *sleb; ++ ++ dbg_rcvry("LEB %d", lnum); ++ sleb = ubifs_scan(c, lnum, 0, c->sbuf); ++ if (IS_ERR(sleb)) { ++ sleb = ubifs_recover_leb(c, lnum, 0, c->sbuf, 0); ++ if (IS_ERR(sleb)) { ++ err = PTR_ERR(sleb); ++ break; ++ } ++ } ++ err = do_kill_orphans(c, sleb, &last_cmt_no, &outofdate, ++ &last_flagged); ++ if (err || outofdate) { ++ ubifs_scan_destroy(sleb); ++ break; ++ } ++ if (sleb->endpt) { ++ c->ohead_lnum = lnum; ++ c->ohead_offs = sleb->endpt; ++ } ++ ubifs_scan_destroy(sleb); ++ } ++ return err; ++} ++ ++/** ++ * ubifs_mount_orphans - delete orphan inodes and erase LEBs that recorded them. ++ * @c: UBIFS file-system description object ++ * @unclean: %1 => recover from unclean unmount ++ * ++ * This function is called when mounting to erase orphans from the previous ++ * session. If UBIFS was not unmounted cleanly, then the inodes recorded as ++ * orphans are deleted. ++ */ ++int ubifs_mount_orphans(struct ubifs_info *c, int unclean) ++{ ++ int err = 0; ++ ++ c->max_orphans = tot_avail_orphs(c); ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ c->orph_buf = kmalloc(c->leb_size, GFP_KERNEL); ++#else ++ c->orph_buf = vmalloc(c->leb_size); ++#endif ++ if (!c->orph_buf) ++ return -ENOMEM; ++ ++ if (unclean) ++ err = kill_orphans(c); ++ else ++ err = clear_orphans(c); ++ ++ return err; ++} ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ ++struct check_orphan { ++ struct rb_node rb; ++ ino_t inum; ++}; ++ ++struct check_info { ++ unsigned long last_ino; ++ unsigned long tot_inos; ++ unsigned long missing; ++ unsigned long long leaf_cnt; ++ struct ubifs_ino_node *node; ++ struct rb_root root; ++}; ++ ++static int dbg_find_orphan(struct ubifs_info *c, ino_t inum) ++{ ++ struct ubifs_orphan *o; ++ struct rb_node *p; ++ ++ spin_lock(&c->orphan_lock); ++ p = c->orph_tree.rb_node; ++ while (p) { ++ o = rb_entry(p, struct ubifs_orphan, rb); ++ if (inum < o->inum) ++ p = p->rb_left; ++ else if (inum > o->inum) ++ p = p->rb_right; ++ else { ++ spin_unlock(&c->orphan_lock); ++ return 1; ++ } ++ } ++ spin_unlock(&c->orphan_lock); ++ return 0; ++} ++ ++static int dbg_ins_check_orphan(struct rb_root *root, ino_t inum) ++{ ++ struct check_orphan *orphan, *o; ++ struct rb_node **p, *parent = NULL; ++ ++ orphan = kzalloc(sizeof(struct check_orphan), GFP_NOFS); ++ if (!orphan) ++ return -ENOMEM; ++ orphan->inum = inum; ++ ++ p = &root->rb_node; ++ while (*p) { ++ parent = *p; ++ o = rb_entry(parent, struct check_orphan, rb); ++ if (inum < o->inum) ++ p = &(*p)->rb_left; ++ else if (inum > o->inum) ++ p = &(*p)->rb_right; ++ else { ++ kfree(orphan); ++ return 0; ++ } ++ } ++ rb_link_node(&orphan->rb, parent, p); ++ rb_insert_color(&orphan->rb, root); ++ return 0; ++} ++ ++static int dbg_find_check_orphan(struct rb_root *root, ino_t inum) ++{ ++ struct check_orphan *o; ++ struct rb_node *p; ++ ++ p = root->rb_node; ++ while (p) { ++ o = rb_entry(p, struct check_orphan, rb); ++ if (inum < o->inum) ++ p = p->rb_left; ++ else if (inum > o->inum) ++ p = p->rb_right; ++ else ++ return 1; ++ } ++ return 0; ++} ++ ++static void dbg_free_check_tree(struct rb_root *root) ++{ ++ struct rb_node *this = root->rb_node; ++ struct check_orphan *o; ++ ++ while (this) { ++ if (this->rb_left) { ++ this = this->rb_left; ++ continue; ++ } else if (this->rb_right) { ++ this = this->rb_right; ++ continue; ++ } ++ o = rb_entry(this, struct check_orphan, rb); ++ this = rb_parent(this); ++ if (this) { ++ if (this->rb_left == &o->rb) ++ this->rb_left = NULL; ++ else ++ this->rb_right = NULL; ++ } ++ kfree(o); ++ } ++} ++ ++static int dbg_orphan_check(struct ubifs_info *c, struct ubifs_zbranch *zbr, ++ void *priv) ++{ ++ struct check_info *ci = priv; ++ ino_t inum; ++ int err; ++ ++ inum = key_ino(c, &zbr->key); ++ if (inum != ci->last_ino) { ++ /* Lowest node type is the inode node, so it comes first */ ++ if (key_type(c, &zbr->key) != UBIFS_INO_KEY) ++ ubifs_err("found orphan node ino %lu, type %d", inum, ++ key_type(c, &zbr->key)); ++ ci->last_ino = inum; ++ ci->tot_inos += 1; ++ err = dbg_read_leaf_nolock(c, zbr, ci->node); ++ if (err) { ++ ubifs_err("node read failed, error %d", err); ++ return err; ++ } ++ if (ci->node->nlink == 0) ++ /* Must be recorded as an orphan */ ++ if (!dbg_find_check_orphan(&ci->root, inum) && ++ !dbg_find_orphan(c, inum)) { ++ ubifs_err("missing orphan, ino %lu", inum); ++ ci->missing += 1; ++ } ++ } ++ ci->leaf_cnt += 1; ++ return 0; ++} ++ ++static int dbg_read_orphans(struct check_info *ci, struct ubifs_scan_leb *sleb) ++{ ++ struct ubifs_scan_node *snod; ++ struct ubifs_orph_node *orph; ++ ino_t inum; ++ int i, n, err; ++ ++ list_for_each_entry(snod, &sleb->nodes, list) { ++ cond_resched(); ++ if (snod->type != UBIFS_ORPH_NODE) ++ continue; ++ orph = snod->node; ++ n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3; ++ for (i = 0; i < n; i++) { ++ inum = le64_to_cpu(orph->inos[i]); ++ err = dbg_ins_check_orphan(&ci->root, inum); ++ if (err) ++ return err; ++ } ++ } ++ return 0; ++} ++ ++static int dbg_scan_orphans(struct ubifs_info *c, struct check_info *ci) ++{ ++ int lnum, err = 0; ++ ++ /* Check no-orphans flag and skip this if no orphans */ ++ if (c->no_orphs) ++ return 0; ++ ++ for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) { ++ struct ubifs_scan_leb *sleb; ++ ++ sleb = ubifs_scan(c, lnum, 0, c->dbg_buf); ++ if (IS_ERR(sleb)) { ++ err = PTR_ERR(sleb); ++ break; ++ } ++ ++ err = dbg_read_orphans(ci, sleb); ++ ubifs_scan_destroy(sleb); ++ if (err) ++ break; ++ } ++ ++ return err; ++} ++ ++static int dbg_check_orphans(struct ubifs_info *c) ++{ ++ struct check_info ci; ++ int err; ++ ++ if (!(ubifs_chk_flags & UBIFS_CHK_ORPH)) ++ return 0; ++ ++ ci.last_ino = 0; ++ ci.tot_inos = 0; ++ ci.missing = 0; ++ ci.leaf_cnt = 0; ++ ci.root = RB_ROOT; ++ ci.node = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS); ++ if (!ci.node) { ++ ubifs_err("out of memory"); ++ return -ENOMEM; ++ } ++ ++ err = dbg_scan_orphans(c, &ci); ++ if (err) ++ goto out; ++ ++ err = dbg_walk_index(c, &dbg_orphan_check, NULL, &ci); ++ if (err) { ++ ubifs_err("cannot scan TNC, error %d", err); ++ goto out; ++ } ++ ++ if (ci.missing) { ++ ubifs_err("%lu missing orphan(s)", ci.missing); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ dbg_cmt("last inode number is %lu", ci.last_ino); ++ dbg_cmt("total number of inodes is %lu", ci.tot_inos); ++ dbg_cmt("total number of leaf nodes is %llu", ci.leaf_cnt); ++ ++out: ++ dbg_free_check_tree(&ci.root); ++ kfree(ci.node); ++ return err; ++} ++ ++#endif /* CONFIG_UBIFS_FS_DEBUG */ +diff -urN linux-2.6.24.7.old/fs/ubifs/recovery.c linux-2.6.24.7/fs/ubifs/recovery.c +--- linux-2.6.24.7.old/fs/ubifs/recovery.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/recovery.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1524 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy (Битюцкий Ðртём) ++ */ ++ ++/* ++ * This file implements functions needed to recover from unclean un-mounts. ++ * When UBIFS is mounted, it checks a flag on the master node to determine if ++ * an un-mount was completed sucessfully. If not, the process of mounting ++ * incorparates additional checking and fixing of on-flash data structures. ++ * UBIFS always cleans away all remnants of an unclean un-mount, so that ++ * errors do not accumulate. However UBIFS defers recovery if it is mounted ++ * read-only, and the flash is not modified in that case. ++ */ ++ ++#include ++#include "ubifs.h" ++ ++/** ++ * is_empty - determine whether a buffer is empty (contains all 0xff). ++ * @buf: buffer to clean ++ * @len: length of buffer ++ * ++ * This function returns %1 if the buffer is empty (contains all 0xff) otherwise ++ * %0 is returned. ++ */ ++static int is_empty(void *buf, int len) ++{ ++ uint8_t *p = buf; ++ int i; ++ ++ for (i = 0; i < len; i++) ++ if (*p++ != 0xff) ++ return 0; ++ return 1; ++} ++ ++/** ++ * get_master_node - get the last valid master node allowing for corruption. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number ++ * @pbuf: buffer containing the LEB read, is returned here ++ * @mst: master node, if found, is returned here ++ * @cor: corruption, if found, is returned here ++ * ++ * This function allocates a buffer, reads the LEB into it, and finds and ++ * returns the last valid master node allowing for one area of corruption. ++ * The corrupt area, if there is one, must be consistent with the assumption ++ * that it is the result of an unclean unmount while the master node was being ++ * written. Under those circumstances, it is valid to use the previously written ++ * master node. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int get_master_node(const struct ubifs_info *c, int lnum, void **pbuf, ++ struct ubifs_mst_node **mst, void **cor) ++{ ++ const int sz = c->mst_node_alsz; ++ int err, offs, len; ++ void *sbuf, *buf; ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ sbuf = kmalloc(c->leb_size, GFP_KERNEL); ++#else ++ sbuf = vmalloc(c->leb_size); ++#endif ++ if (!sbuf) ++ return -ENOMEM; ++ ++ err = ubi_read(c->ubi, lnum, sbuf, 0, c->leb_size); ++ if (err && err != -EBADMSG) ++ goto out_free; ++ ++ /* Find the first position that is definitely not a node */ ++ offs = 0; ++ buf = sbuf; ++ len = c->leb_size; ++ while (offs + UBIFS_MST_NODE_SZ <= c->leb_size) { ++ struct ubifs_ch *ch = buf; ++ ++ if (le32_to_cpu(ch->magic) != UBIFS_NODE_MAGIC) ++ break; ++ offs += sz; ++ buf += sz; ++ len -= sz; ++ } ++ /* See if there was a valid master node before that */ ++ if (offs) { ++ int ret; ++ ++ offs -= sz; ++ buf -= sz; ++ len += sz; ++ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1); ++ if (ret != SCANNED_A_NODE && offs) { ++ /* Could have been corruption so check one place back */ ++ offs -= sz; ++ buf -= sz; ++ len += sz; ++ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1); ++ if (ret != SCANNED_A_NODE) ++ /* ++ * We accept only one area of corruption because ++ * we are assuming that it was caused while ++ * trying to write a master node. ++ */ ++ goto out_err; ++ } ++ if (ret == SCANNED_A_NODE) { ++ struct ubifs_ch *ch = buf; ++ ++ if (ch->node_type != UBIFS_MST_NODE) ++ goto out_err; ++ dbg_rcvry("found a master node at %d:%d", lnum, offs); ++ *mst = buf; ++ offs += sz; ++ buf += sz; ++ len -= sz; ++ } ++ } ++ /* Check for corruption */ ++ if (offs < c->leb_size) { ++ if (!is_empty(buf, min_t(int, len, sz))) { ++ *cor = buf; ++ dbg_rcvry("found corruption at %d:%d", lnum, offs); ++ } ++ offs += sz; ++ buf += sz; ++ len -= sz; ++ } ++ /* Check remaining empty space */ ++ if (offs < c->leb_size) ++ if (!is_empty(buf, len)) ++ goto out_err; ++ *pbuf = sbuf; ++ return 0; ++ ++out_err: ++ err = -EINVAL; ++out_free: ++ vfree(sbuf); ++ *mst = NULL; ++ *cor = NULL; ++ return err; ++} ++ ++/** ++ * write_rcvrd_mst_node - write recovered master node. ++ * @c: UBIFS file-system description object ++ * @mst: master node ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int write_rcvrd_mst_node(struct ubifs_info *c, ++ struct ubifs_mst_node *mst) ++{ ++ int err = 0, lnum = UBIFS_MST_LNUM, sz = c->mst_node_alsz; ++ uint32_t save_flags; ++ ++ dbg_rcvry("recovery"); ++ ++ save_flags = mst->flags; ++ mst->flags = cpu_to_le32(le32_to_cpu(mst->flags) | UBIFS_MST_RCVRY); ++ ++ ubifs_prepare_node(c, mst, UBIFS_MST_NODE_SZ, 1); ++ err = ubi_leb_change(c->ubi, lnum, mst, sz, UBI_SHORTTERM); ++ if (err) ++ goto out; ++ err = ubi_leb_change(c->ubi, lnum + 1, mst, sz, UBI_SHORTTERM); ++ if (err) ++ goto out; ++out: ++ mst->flags = save_flags; ++ return err; ++} ++ ++/** ++ * ubifs_recover_master_node - recover the master node. ++ * @c: UBIFS file-system description object ++ * ++ * This function recovers the master node from corruption that may occur due to ++ * an unclean unmount. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_recover_master_node(struct ubifs_info *c) ++{ ++ void *buf1 = NULL, *buf2 = NULL, *cor1 = NULL, *cor2 = NULL; ++ struct ubifs_mst_node *mst1 = NULL, *mst2 = NULL, *mst; ++ const int sz = c->mst_node_alsz; ++ int err, offs1, offs2; ++ ++ dbg_rcvry("recovery"); ++ ++ err = get_master_node(c, UBIFS_MST_LNUM, &buf1, &mst1, &cor1); ++ if (err) ++ goto out_free; ++ ++ err = get_master_node(c, UBIFS_MST_LNUM + 1, &buf2, &mst2, &cor2); ++ if (err) ++ goto out_free; ++ ++ if (mst1) { ++ offs1 = (void *)mst1 - buf1; ++ if ((le32_to_cpu(mst1->flags) & UBIFS_MST_RCVRY) && ++ (offs1 == 0 && !cor1)) { ++ /* ++ * mst1 was written by recovery at offset 0 with no ++ * corruption. ++ */ ++ dbg_rcvry("recovery recovery"); ++ mst = mst1; ++ } else if (mst2) { ++ offs2 = (void *)mst2 - buf2; ++ if (offs1 == offs2) { ++ /* Same offset, so must be the same */ ++ if (memcmp((void *)mst1 + UBIFS_CH_SZ, ++ (void *)mst2 + UBIFS_CH_SZ, ++ UBIFS_MST_NODE_SZ - UBIFS_CH_SZ)) ++ goto out_err; ++ mst = mst1; ++ } else if (offs2 + sz == offs1) { ++ /* 1st LEB was written, 2nd was not */ ++ if (cor1) ++ goto out_err; ++ mst = mst1; ++ } else if (offs1 == 0 && offs2 + sz >= c->leb_size) { ++ /* 1st LEB was unmapped and written, 2nd not */ ++ if (cor1) ++ goto out_err; ++ mst = mst1; ++ } else ++ goto out_err; ++ } else { ++ /* ++ * 2nd LEB was unmapped and about to be written, so ++ * there must be only one master node in the first LEB ++ * and no corruption. ++ */ ++ if (offs1 != 0 || cor1) ++ goto out_err; ++ mst = mst1; ++ } ++ } else { ++ if (!mst2) ++ goto out_err; ++ /* ++ * 1st LEB was unmapped and about to be written, so there must ++ * be no room left in 2nd LEB. ++ */ ++ offs2 = (void *)mst2 - buf2; ++ if (offs2 + sz + sz <= c->leb_size) ++ goto out_err; ++ mst = mst2; ++ } ++ ++ dbg_rcvry("recovered master node from LEB %d", ++ (mst == mst1 ? UBIFS_MST_LNUM : UBIFS_MST_LNUM + 1)); ++ ++ memcpy(c->mst_node, mst, UBIFS_MST_NODE_SZ); ++ ++ if ((c->vfs_sb->s_flags & MS_RDONLY)) { ++ /* Read-only mode. Keep a copy for switching to rw mode */ ++ c->rcvrd_mst_node = kmalloc(sz, GFP_KERNEL); ++ if (!c->rcvrd_mst_node) { ++ err = -ENOMEM; ++ goto out_free; ++ } ++ memcpy(c->rcvrd_mst_node, c->mst_node, UBIFS_MST_NODE_SZ); ++ } else { ++ /* Write the recovered master node */ ++ c->max_sqnum = le64_to_cpu(mst->ch.sqnum) - 1; ++ err = write_rcvrd_mst_node(c, c->mst_node); ++ if (err) ++ goto out_free; ++ } ++ ++ vfree(buf2); ++ vfree(buf1); ++ ++ return 0; ++ ++out_err: ++ err = -EINVAL; ++out_free: ++ ubifs_err("failed to recover master node"); ++ if (mst1) { ++ dbg_err("dumping first master node"); ++ dbg_dump_node(c, mst1); ++ } ++ if (mst2) { ++ dbg_err("dumping second master node"); ++ dbg_dump_node(c, mst2); ++ } ++ vfree(buf2); ++ vfree(buf1); ++ return err; ++} ++ ++/** ++ * ubifs_write_rcvrd_mst_node - write the recovered master node. ++ * @c: UBIFS file-system description object ++ * ++ * This function writes the master node that was recovered during mounting in ++ * read-only mode and must now be written because we are remounting rw. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_write_rcvrd_mst_node(struct ubifs_info *c) ++{ ++ int err; ++ ++ if (!c->rcvrd_mst_node) ++ return 0; ++ c->rcvrd_mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY); ++ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY); ++ err = write_rcvrd_mst_node(c, c->rcvrd_mst_node); ++ if (err) ++ return err; ++ kfree(c->rcvrd_mst_node); ++ c->rcvrd_mst_node = NULL; ++ return 0; ++} ++ ++/** ++ * is_last_write - determine if an offset was in the last write to a LEB. ++ * @c: UBIFS file-system description object ++ * @buf: buffer to check ++ * @offs: offset to check ++ * ++ * This function returns %1 if @offs was in the last write to the LEB whose data ++ * is in @buf, otherwise %0 is returned. The determination is made by checking ++ * for subsequent empty space starting from the next min_io_size boundary (or a ++ * bit less than the common header size if min_io_size is one). ++ */ ++static int is_last_write(const struct ubifs_info *c, void *buf, int offs) ++{ ++ int empty_offs; ++ int check_len; ++ uint8_t *p; ++ ++ if (c->min_io_size == 1) { ++ check_len = c->leb_size - offs; ++ p = buf + check_len; ++ for (; check_len > 0; check_len--) ++ if (*--p != 0xff) ++ break; ++ /* ++ * 'check_len' is the size of the corruption which cannot be ++ * more than the size of 1 node if it was caused by an unclean ++ * unmount. ++ */ ++ if (check_len > UBIFS_MAX_NODE_SZ) ++ return 0; ++ return 1; ++ } ++ ++ /* ++ * Round up to the next c->min_io_size boundary i.e. 'offs' is in the ++ * last wbuf written. After that should be empty space. ++ */ ++ empty_offs = ALIGN(offs + 1, c->min_io_size); ++ check_len = c->leb_size - empty_offs; ++ p = buf + empty_offs - offs; ++ ++ for (; check_len > 0; check_len--) ++ if (*p++ != 0xff) ++ return 0; ++ return 1; ++} ++ ++/** ++ * clean_buf - clean the data from an LEB sitting in a buffer. ++ * @c: UBIFS file-system description object ++ * @buf: buffer to clean ++ * @lnum: LEB number to clean ++ * @offs: offset from which to clean ++ * @len: length of buffer ++ * ++ * This function pads up to the next min_io_size boundary (if there is one) and ++ * sets empty space to all 0xff. @buf, @offs and @len are updated to the next ++ * min_io_size boundary (if there is one). ++ */ ++static void clean_buf(const struct ubifs_info *c, void **buf, int lnum, ++ int *offs, int *len) ++{ ++ int empty_offs, pad_len; ++ ++ lnum = lnum; ++ dbg_rcvry("cleaning corruption at %d:%d", lnum, *offs); ++ ++ if (c->min_io_size == 1) { ++ memset(*buf, 0xff, c->leb_size - *offs); ++ return; ++ } ++ ++ ubifs_assert(!(*offs & 7)); ++ empty_offs = ALIGN(*offs, c->min_io_size); ++ pad_len = empty_offs - *offs; ++ ubifs_pad(c, *buf, pad_len); ++ *offs += pad_len; ++ *buf += pad_len; ++ *len -= pad_len; ++ memset(*buf, 0xff, c->leb_size - empty_offs); ++} ++ ++/** ++ * no_more_nodes - determine if there are no more nodes in a buffer. ++ * @c: UBIFS file-system description object ++ * @buf: buffer to check ++ * @len: length of buffer ++ * @lnum: LEB number of the LEB from which @buf was read ++ * @offs: offset from which @buf was read ++ * ++ * This function scans @buf for more nodes and returns %0 is a node is found and ++ * %1 if no more nodes are found. ++ */ ++static int no_more_nodes(const struct ubifs_info *c, void *buf, int len, ++ int lnum, int offs) ++{ ++ int skip, next_offs = 0; ++ ++ if (len > UBIFS_DATA_NODE_SZ) { ++ struct ubifs_ch *ch = buf; ++ int dlen = le32_to_cpu(ch->len); ++ ++ if (ch->node_type == UBIFS_DATA_NODE && dlen >= UBIFS_CH_SZ && ++ dlen <= UBIFS_MAX_DATA_NODE_SZ) ++ /* The corrupt node looks like a data node */ ++ next_offs = ALIGN(offs + dlen, 8); ++ } ++ ++ if (c->min_io_size == 1) ++ skip = 8; ++ else ++ skip = ALIGN(offs + 1, c->min_io_size) - offs; ++ ++ offs += skip; ++ buf += skip; ++ len -= skip; ++ while (len > 8) { ++ struct ubifs_ch *ch = buf; ++ uint32_t magic = le32_to_cpu(ch->magic); ++ int ret; ++ ++ if (magic == UBIFS_NODE_MAGIC) { ++ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1); ++ if (ret == SCANNED_A_NODE || ret > 0) { ++ /* ++ * There is a small chance this is just data in ++ * a data node, so check that possibility. e.g. ++ * this is part of a file that itself contains ++ * a UBIFS image. ++ */ ++ if (next_offs && offs + le32_to_cpu(ch->len) <= ++ next_offs) ++ continue; ++ dbg_rcvry("unexpected node at %d:%d", lnum, ++ offs); ++ return 0; ++ } ++ } ++ offs += 8; ++ buf += 8; ++ len -= 8; ++ } ++ return 1; ++} ++ ++/** ++ * fix_unclean_leb - fix an unclean LEB. ++ * @c: UBIFS file-system description object ++ * @sleb: scanned LEB information ++ * @start: offset where scan started ++ */ ++static int fix_unclean_leb(struct ubifs_info *c, struct ubifs_scan_leb *sleb, ++ int start) ++{ ++ int lnum = sleb->lnum, endpt = start; ++ ++ /* Get the end offset of the last node we are keeping */ ++ if (!list_empty(&sleb->nodes)) { ++ struct ubifs_scan_node *snod; ++ ++ snod = list_entry(sleb->nodes.prev, ++ struct ubifs_scan_node, list); ++ endpt = snod->offs + snod->len; ++ } ++ ++ if ((c->vfs_sb->s_flags & MS_RDONLY) && !c->remounting_rw) { ++ /* Add to recovery list */ ++ struct ubifs_unclean_leb *ucleb; ++ ++ dbg_rcvry("need to fix LEB %d start %d endpt %d", ++ lnum, start, sleb->endpt); ++ ucleb = kzalloc(sizeof(struct ubifs_unclean_leb), GFP_NOFS); ++ if (!ucleb) ++ return -ENOMEM; ++ ucleb->lnum = lnum; ++ ucleb->endpt = endpt; ++ list_add_tail(&ucleb->list, &c->unclean_leb_list); ++ } else { ++ /* Write the fixed LEB back to flash */ ++ int err; ++ ++ dbg_rcvry("fixing LEB %d start %d endpt %d", ++ lnum, start, sleb->endpt); ++ if (endpt == 0) { ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ return err; ++ } else { ++ int len = ALIGN(endpt, c->min_io_size); ++ ++ if (start) { ++ err = ubi_read(c->ubi, lnum, sleb->buf, 0, ++ start); ++ if (err) ++ return err; ++ } ++ /* Pad to min_io_size */ ++ if (len > endpt) { ++ int pad_len = len - ALIGN(endpt, 8); ++ ++ if (pad_len > 0) { ++ void *buf = sleb->buf + len - pad_len; ++ ++ ubifs_pad(c, buf, pad_len); ++ } ++ } ++ err = ubi_leb_change(c->ubi, lnum, sleb->buf, len, ++ UBI_UNKNOWN); ++ if (err) ++ return err; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * drop_incomplete_group - drop nodes from an incomplete group. ++ * @sleb: scanned LEB information ++ * @offs: offset of dropped nodes is returned here ++ * ++ * This function returns %1 if nodes are dropped and %0 otherwise. ++ */ ++static int drop_incomplete_group(struct ubifs_scan_leb *sleb, int *offs) ++{ ++ int dropped = 0; ++ ++ while (!list_empty(&sleb->nodes)) { ++ struct ubifs_scan_node *snod; ++ struct ubifs_ch *ch; ++ ++ snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, ++ list); ++ ch = snod->node; ++ if (ch->group_type != UBIFS_IN_NODE_GROUP) ++ return dropped; ++ dbg_rcvry("dropping node at %d:%d", sleb->lnum, snod->offs); ++ *offs = snod->offs; ++ list_del(&snod->list); ++ kfree(snod); ++ sleb->nodes_cnt -= 1; ++ dropped = 1; ++ } ++ return dropped; ++} ++ ++/** ++ * ubifs_recover_leb - scan and recover a LEB. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number ++ * @offs: offset ++ * @sbuf: LEB-sized buffer to use ++ * @grouped: nodes may be grouped for recovery ++ * ++ * This function does a scan of a LEB, but caters for errors that might have ++ * been caused by the unclean unmount from which we are attempting to recover. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum, ++ int offs, void *sbuf, int grouped) ++{ ++ int err, len = c->leb_size - offs, need_clean = 0, quiet = 1; ++ int empty_chkd = 0, start = offs; ++ struct ubifs_scan_leb *sleb; ++ void *buf = sbuf + offs; ++ ++ dbg_rcvry("%d:%d", lnum, offs); ++ ++ sleb = ubifs_start_scan(c, lnum, offs, sbuf); ++ if (IS_ERR(sleb)) ++ return sleb; ++ ++ if (sleb->ecc) ++ need_clean = 1; ++ ++ while (len >= 8) { ++ int ret; ++ ++ dbg_scan("look at LEB %d:%d (%d bytes left)", ++ lnum, offs, len); ++ ++ cond_resched(); ++ ++ /* ++ * Scan quietly until there is an error from which we cannot ++ * recover ++ */ ++ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, quiet); ++ ++ if (ret == SCANNED_A_NODE) { ++ /* A valid node, and not a padding node */ ++ struct ubifs_ch *ch = buf; ++ int node_len; ++ ++ err = ubifs_add_snod(c, sleb, buf, offs); ++ if (err) ++ goto error; ++ node_len = ALIGN(le32_to_cpu(ch->len), 8); ++ offs += node_len; ++ buf += node_len; ++ len -= node_len; ++ continue; ++ } ++ ++ if (ret > 0) { ++ /* Padding bytes or a valid padding node */ ++ offs += ret; ++ buf += ret; ++ len -= ret; ++ continue; ++ } ++ ++ if (ret == SCANNED_EMPTY_SPACE) { ++ if (!is_empty(buf, len)) { ++ if (!is_last_write(c, buf, offs)) ++ break; ++ clean_buf(c, &buf, lnum, &offs, &len); ++ need_clean = 1; ++ } ++ empty_chkd = 1; ++ break; ++ } ++ ++ if (ret == SCANNED_GARBAGE || ret == SCANNED_A_BAD_PAD_NODE) ++ if (is_last_write(c, buf, offs)) { ++ clean_buf(c, &buf, lnum, &offs, &len); ++ need_clean = 1; ++ empty_chkd = 1; ++ break; ++ } ++ ++ if (ret == SCANNED_A_CORRUPT_NODE) ++ if (no_more_nodes(c, buf, len, lnum, offs)) { ++ clean_buf(c, &buf, lnum, &offs, &len); ++ need_clean = 1; ++ empty_chkd = 1; ++ break; ++ } ++ ++ if (quiet) { ++ /* Redo the last scan but noisily */ ++ quiet = 0; ++ continue; ++ } ++ ++ switch (ret) { ++ case SCANNED_GARBAGE: ++ dbg_err("garbage"); ++ goto corrupted; ++ case SCANNED_A_CORRUPT_NODE: ++ case SCANNED_A_BAD_PAD_NODE: ++ dbg_err("bad node"); ++ goto corrupted; ++ default: ++ dbg_err("unknown"); ++ goto corrupted; ++ } ++ } ++ ++ if (!empty_chkd && !is_empty(buf, len)) { ++ if (is_last_write(c, buf, offs)) { ++ clean_buf(c, &buf, lnum, &offs, &len); ++ need_clean = 1; ++ } else { ++ ubifs_err("corrupt empty space at LEB %d:%d", ++ lnum, offs); ++ goto corrupted; ++ } ++ } ++ ++ /* Drop nodes from incomplete group */ ++ if (grouped && drop_incomplete_group(sleb, &offs)) { ++ buf = sbuf + offs; ++ len = c->leb_size - offs; ++ clean_buf(c, &buf, lnum, &offs, &len); ++ need_clean = 1; ++ } ++ ++ if (offs % c->min_io_size) { ++ clean_buf(c, &buf, lnum, &offs, &len); ++ need_clean = 1; ++ } ++ ++ ubifs_end_scan(c, sleb, lnum, offs); ++ ++ if (need_clean) { ++ err = fix_unclean_leb(c, sleb, start); ++ if (err) ++ goto error; ++ } ++ ++ return sleb; ++ ++corrupted: ++ ubifs_scanned_corruption(c, lnum, offs, buf); ++ err = -EUCLEAN; ++error: ++ ubifs_err("LEB %d scanning failed", lnum); ++ ubifs_scan_destroy(sleb); ++ return ERR_PTR(err); ++} ++ ++/** ++ * get_cs_sqnum - get commit start sequence number. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number of commit start node ++ * @offs: offset of commit start node ++ * @cs_sqnum: commit start sequence number is returned here ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int get_cs_sqnum(struct ubifs_info *c, int lnum, int offs, ++ unsigned long long *cs_sqnum) ++{ ++ struct ubifs_cs_node *cs_node = NULL; ++ int err, ret; ++ ++ dbg_rcvry("at %d:%d", lnum, offs); ++ cs_node = kmalloc(UBIFS_CS_NODE_SZ, GFP_KERNEL); ++ if (!cs_node) ++ return -ENOMEM; ++ if (c->leb_size - offs < UBIFS_CS_NODE_SZ) ++ goto out_err; ++ err = ubi_read(c->ubi, lnum, (void *)cs_node, offs, UBIFS_CS_NODE_SZ); ++ if (err && err != -EBADMSG) ++ goto out_free; ++ ret = ubifs_scan_a_node(c, cs_node, UBIFS_CS_NODE_SZ, lnum, offs, 0); ++ if (ret != SCANNED_A_NODE) { ++ dbg_err("Not a valid node"); ++ goto out_err; ++ } ++ if (cs_node->ch.node_type != UBIFS_CS_NODE) { ++ dbg_err("Node a CS node, type is %d", cs_node->ch.node_type); ++ goto out_err; ++ } ++ if (le64_to_cpu(cs_node->cmt_no) != c->cmt_no) { ++ dbg_err("CS node cmt_no %llu != current cmt_no %llu", ++ (unsigned long long)le64_to_cpu(cs_node->cmt_no), ++ c->cmt_no); ++ goto out_err; ++ } ++ *cs_sqnum = le64_to_cpu(cs_node->ch.sqnum); ++ dbg_rcvry("commit start sqnum %llu", *cs_sqnum); ++ kfree(cs_node); ++ return 0; ++ ++out_err: ++ err = -EINVAL; ++out_free: ++ ubifs_err("failed to get CS sqnum"); ++ kfree(cs_node); ++ return err; ++} ++ ++/** ++ * ubifs_recover_log_leb - scan and recover a log LEB. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number ++ * @offs: offset ++ * @sbuf: LEB-sized buffer to use ++ * ++ * This function does a scan of a LEB, but caters for errors that might have ++ * been caused by the unclean unmount from which we are attempting to recover. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++struct ubifs_scan_leb *ubifs_recover_log_leb(struct ubifs_info *c, int lnum, ++ int offs, void *sbuf) ++{ ++ struct ubifs_scan_leb *sleb; ++ int next_lnum; ++ ++ dbg_rcvry("LEB %d", lnum); ++ next_lnum = lnum + 1; ++ if (next_lnum >= UBIFS_LOG_LNUM + c->log_lebs) ++ next_lnum = UBIFS_LOG_LNUM; ++ if (next_lnum != c->ltail_lnum) { ++ /* ++ * We can only recover at the end of the log, so check that the ++ * next log LEB is empty or out of date. ++ */ ++ sleb = ubifs_scan(c, next_lnum, 0, sbuf); ++ if (IS_ERR(sleb)) ++ return sleb; ++ if (sleb->nodes_cnt) { ++ struct ubifs_scan_node *snod; ++ unsigned long long cs_sqnum = c->cs_sqnum; ++ ++ snod = list_entry(sleb->nodes.next, ++ struct ubifs_scan_node, list); ++ if (cs_sqnum == 0) { ++ int err; ++ ++ err = get_cs_sqnum(c, lnum, offs, &cs_sqnum); ++ if (err) { ++ ubifs_scan_destroy(sleb); ++ return ERR_PTR(err); ++ } ++ } ++ if (snod->sqnum > cs_sqnum) { ++ ubifs_err("unrecoverable log corruption " ++ "in LEB %d", lnum); ++ ubifs_scan_destroy(sleb); ++ return ERR_PTR(-EUCLEAN); ++ } ++ } ++ ubifs_scan_destroy(sleb); ++ } ++ return ubifs_recover_leb(c, lnum, offs, sbuf, 0); ++} ++ ++/** ++ * recover_head - recover a head. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number of head to recover ++ * @offs: offset of head to recover ++ * @sbuf: LEB-sized buffer to use ++ * ++ * This function ensures that there is no data on the flash at a head location. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int recover_head(const struct ubifs_info *c, int lnum, int offs, ++ void *sbuf) ++{ ++ int len, err, need_clean = 0; ++ ++ if (c->min_io_size > 1) ++ len = c->min_io_size; ++ else ++ len = 512; ++ if (offs + len > c->leb_size) ++ len = c->leb_size - offs; ++ ++ if (!len) ++ return 0; ++ ++ /* Read at the head location and check it is empty flash */ ++ err = ubi_read(c->ubi, lnum, sbuf, offs, len); ++ if (err) ++ need_clean = 1; ++ else { ++ uint8_t *p = sbuf; ++ ++ while (len--) ++ if (*p++ != 0xff) { ++ need_clean = 1; ++ break; ++ } ++ } ++ ++ if (need_clean) { ++ dbg_rcvry("cleaning head at %d:%d", lnum, offs); ++ if (offs == 0) ++ return ubifs_leb_unmap(c, lnum); ++ err = ubi_read(c->ubi, lnum, sbuf, 0, offs); ++ if (err) ++ return err; ++ return ubi_leb_change(c->ubi, lnum, sbuf, offs, UBI_UNKNOWN); ++ } ++ ++ return 0; ++} ++ ++/** ++ * ubifs_recover_inl_heads - recover index and LPT heads. ++ * @c: UBIFS file-system description object ++ * @sbuf: LEB-sized buffer to use ++ * ++ * This function ensures that there is no data on the flash at the index and ++ * LPT head locations. ++ * ++ * This deals with the recovery of a half-completed journal commit. UBIFS is ++ * careful never to overwrite the last version of the index or the LPT. Because ++ * the index and LPT are wandering trees, data from a half-completed commit will ++ * not be referenced anywhere in UBIFS. The data will be either in LEBs that are ++ * assumed to be empty and will be unmapped anyway before use, or in the index ++ * and LPT heads. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_recover_inl_heads(const struct ubifs_info *c, void *sbuf) ++{ ++ int err; ++ ++ ubifs_assert(!(c->vfs_sb->s_flags & MS_RDONLY) || c->remounting_rw); ++ ++ dbg_rcvry("checking index head at %d:%d", c->ihead_lnum, c->ihead_offs); ++ err = recover_head(c, c->ihead_lnum, c->ihead_offs, sbuf); ++ if (err) ++ return err; ++ ++ dbg_rcvry("checking LPT head at %d:%d", c->nhead_lnum, c->nhead_offs); ++ err = recover_head(c, c->nhead_lnum, c->nhead_offs, sbuf); ++ if (err) ++ return err; ++ ++ return 0; ++} ++ ++/** ++ * clean_an_unclean_leb - read and write a LEB to remove corruption. ++ * @c: UBIFS file-system description object ++ * @ucleb: unclean LEB information ++ * @sbuf: LEB-sized buffer to use ++ * ++ * This function reads a LEB up to a point pre-determined by the mount recovery, ++ * checks the nodes, and writes the result back to the flash, thereby cleaning ++ * off any following corruption, or non-fatal ECC errors. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int clean_an_unclean_leb(const struct ubifs_info *c, ++ struct ubifs_unclean_leb *ucleb, void *sbuf) ++{ ++ int err, lnum = ucleb->lnum, offs = 0, len = ucleb->endpt, quiet = 1; ++ void *buf = sbuf; ++ ++ dbg_rcvry("LEB %d len %d", lnum, len); ++ ++ if (len == 0) { ++ /* Nothing to read, just unmap it */ ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ return err; ++ return 0; ++ } ++ ++ err = ubi_read(c->ubi, lnum, buf, offs, len); ++ if (err && err != -EBADMSG) ++ return err; ++ ++ while (len >= 8) { ++ int ret; ++ ++ cond_resched(); ++ ++ /* Scan quietly until there is an error */ ++ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, quiet); ++ ++ if (ret == SCANNED_A_NODE) { ++ /* A valid node, and not a padding node */ ++ struct ubifs_ch *ch = buf; ++ int node_len; ++ ++ node_len = ALIGN(le32_to_cpu(ch->len), 8); ++ offs += node_len; ++ buf += node_len; ++ len -= node_len; ++ continue; ++ } ++ ++ if (ret > 0) { ++ /* Padding bytes or a valid padding node */ ++ offs += ret; ++ buf += ret; ++ len -= ret; ++ continue; ++ } ++ ++ if (ret == SCANNED_EMPTY_SPACE) { ++ ubifs_err("unexpected empty space at %d:%d", ++ lnum, offs); ++ return -EUCLEAN; ++ } ++ ++ if (quiet) { ++ /* Redo the last scan but noisily */ ++ quiet = 0; ++ continue; ++ } ++ ++ ubifs_scanned_corruption(c, lnum, offs, buf); ++ return -EUCLEAN; ++ } ++ ++ /* Pad to min_io_size */ ++ len = ALIGN(ucleb->endpt, c->min_io_size); ++ if (len > ucleb->endpt) { ++ int pad_len = len - ALIGN(ucleb->endpt, 8); ++ ++ if (pad_len > 0) { ++ buf = c->sbuf + len - pad_len; ++ ubifs_pad(c, buf, pad_len); ++ } ++ } ++ ++ /* Write back the LEB atomically */ ++ err = ubi_leb_change(c->ubi, lnum, sbuf, len, UBI_UNKNOWN); ++ if (err) ++ return err; ++ ++ dbg_rcvry("cleaned LEB %d", lnum); ++ ++ return 0; ++} ++ ++/** ++ * ubifs_clean_lebs - clean LEBs recovered during read-only mount. ++ * @c: UBIFS file-system description object ++ * @sbuf: LEB-sized buffer to use ++ * ++ * This function cleans a LEB identified during recovery that needs to be ++ * written but was not because UBIFS was mounted read-only. This happens when ++ * remounting to read-write mode. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_clean_lebs(const struct ubifs_info *c, void *sbuf) ++{ ++ dbg_rcvry("recovery"); ++ while (!list_empty(&c->unclean_leb_list)) { ++ struct ubifs_unclean_leb *ucleb; ++ int err; ++ ++ ucleb = list_entry(c->unclean_leb_list.next, ++ struct ubifs_unclean_leb, list); ++ err = clean_an_unclean_leb(c, ucleb, sbuf); ++ if (err) ++ return err; ++ list_del(&ucleb->list); ++ kfree(ucleb); ++ } ++ return 0; ++} ++ ++/** ++ * ubifs_recover_gc_lnum - recover the GC LEB number. ++ * @c: UBIFS file-system description object ++ * ++ * Out-of-place garbage collection requires always one empty LEB with which to ++ * start garbage collection. The LEB number is recorded in c->gc_lnum and is ++ * written to the master node on unmounting. In the case of an unclean unmount ++ * the value of gc_lnum recorded in the master node is out of date and cannot ++ * be used. Instead, recovery must allocate an empty LEB for this purpose. ++ * However, there may not be enough empty space, in which case it must be ++ * possible to GC the dirtiest LEB into the GC head LEB. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_recover_gc_lnum(struct ubifs_info *c) ++{ ++ struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf; ++ struct ubifs_lprops lp; ++ int lnum, err; ++ ++ c->gc_lnum = -1; ++ if (wbuf->lnum == -1) { ++ dbg_rcvry("no GC head LEB"); ++ goto find_free; ++ } ++ /* ++ * See whether the used space in the dirtiest LEB fits in the GC head ++ * LEB. ++ */ ++ err = ubifs_find_dirty_leb(c, &lp, c->dead_wm, 2); ++ if (err) { ++ if (err == -ENOSPC) ++ dbg_err("could not find a dirty LEB"); ++ return err; ++ } ++ ubifs_assert(!(lp.flags & LPROPS_INDEX)); ++ lnum = lp.lnum; ++ if (lp.free + lp.dirty == c->leb_size) { ++ /* An empty LEB was returned */ ++ if (lp.free != c->leb_size) { ++ err = ubifs_change_one_lp(c, lnum, c->leb_size, ++ 0, 0, 0, 0); ++ if (err) ++ return err; ++ } ++ err = ubifs_leb_unmap(c, lnum); ++ if (err) ++ return err; ++ c->gc_lnum = lnum; ++ dbg_rcvry("allocated LEB %d for GC", lnum); ++ return 0; ++ } ++ /* ++ * There was no empty LEB so the used space in the dirtiest LEB must fit ++ * in the GC head LEB. ++ */ ++ if (lp.free + lp.dirty < wbuf->offs) { ++ dbg_rcvry("LEB %d doesn't fit in GC head LEB %d:%d", ++ lnum, wbuf->lnum, wbuf->offs); ++ err = ubifs_return_leb(c, lnum); ++ if (err) ++ return err; ++ goto find_free; ++ } ++ /* It fits, so GC it - use locking to keep 'ubifs_assert()' happy */ ++ dbg_rcvry("GC'ing LEB %d", lnum); ++ mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); ++ err = ubifs_garbage_collect_leb(c, &lp); ++ if (err >= 0) { ++ int err2 = ubifs_wbuf_sync_nolock(wbuf); ++ ++ if (err2) ++ err = err2; ++ } ++ mutex_unlock(&wbuf->io_mutex); ++ if (err < 0) { ++ dbg_err("GC failed, error %d", err); ++ if (err == -EAGAIN) ++ err = -EINVAL; ++ return err; ++ } ++ if (err != LEB_RETAINED) { ++ dbg_err("GC returned %d", err); ++ return -EINVAL; ++ } ++ err = ubifs_leb_unmap(c, c->gc_lnum); ++ if (err) ++ return err; ++ dbg_rcvry("allocated LEB %d for GC", lnum); ++ return 0; ++ ++find_free: ++ /* ++ * There is no GC head LEB or the free space in the GC head LEB is too ++ * small. Allocate gc_lnum by calling 'ubifs_find_free_leb_for_idx()' so ++ * GC is not run. ++ */ ++ lnum = ubifs_find_free_leb_for_idx(c); ++ if (lnum < 0) { ++ dbg_err("could not find an empty LEB"); ++ return lnum; ++ } ++ /* And reset the index flag */ ++ err = ubifs_change_one_lp(c, lnum, -1, -1, 0, LPROPS_INDEX, 0); ++ if (err) ++ return err; ++ c->gc_lnum = lnum; ++ dbg_rcvry("allocated LEB %d for GC", lnum); ++ return 0; ++} ++ ++/** ++ * struct size_entry - inode size information for recovery. ++ * @rb: link in the RB-tree of sizes ++ * @inum: inode number ++ * @i_size: size on inode ++ * @d_size: maximum size based on data nodes ++ * @exists: indicates whether the inode exists ++ * @inode: inode if pinned in memory awaiting rw mode to fix it ++ */ ++struct size_entry { ++ struct rb_node rb; ++ ino_t inum; ++ loff_t i_size; ++ loff_t d_size; ++ int exists; ++ struct inode *inode; ++}; ++ ++/** ++ * add_ino - add an entry to the size tree. ++ * @c: UBIFS file-system description object ++ * @inum: inode number ++ * @i_size: size on inode ++ * @d_size: maximum size based on data nodes ++ * @exists: indicates whether the inode exists ++ */ ++static int add_ino(struct ubifs_info *c, ino_t inum, loff_t i_size, ++ loff_t d_size, int exists) ++{ ++ struct rb_node **p = &c->size_tree.rb_node, *parent = NULL; ++ struct size_entry *e; ++ ++ while (*p) { ++ parent = *p; ++ e = rb_entry(parent, struct size_entry, rb); ++ if (inum < e->inum) ++ p = &(*p)->rb_left; ++ else ++ p = &(*p)->rb_right; ++ } ++ ++ e = kzalloc(sizeof(struct size_entry), GFP_KERNEL); ++ if (!e) ++ return -ENOMEM; ++ ++ e->inum = inum; ++ e->i_size = i_size; ++ e->d_size = d_size; ++ e->exists = exists; ++ ++ rb_link_node(&e->rb, parent, p); ++ rb_insert_color(&e->rb, &c->size_tree); ++ ++ return 0; ++} ++ ++/** ++ * find_ino - find an entry on the size tree. ++ * @c: UBIFS file-system description object ++ * @inum: inode number ++ */ ++static struct size_entry *find_ino(struct ubifs_info *c, ino_t inum) ++{ ++ struct rb_node *p = c->size_tree.rb_node; ++ struct size_entry *e; ++ ++ while (p) { ++ e = rb_entry(p, struct size_entry, rb); ++ if (inum < e->inum) ++ p = p->rb_left; ++ else if (inum > e->inum) ++ p = p->rb_right; ++ else ++ return e; ++ } ++ return NULL; ++} ++ ++/** ++ * remove_ino - remove an entry from the size tree. ++ * @c: UBIFS file-system description object ++ * @inum: inode number ++ */ ++static void remove_ino(struct ubifs_info *c, ino_t inum) ++{ ++ struct size_entry *e = find_ino(c, inum); ++ ++ if (!e) ++ return; ++ rb_erase(&e->rb, &c->size_tree); ++ kfree(e); ++} ++ ++/** ++ * ubifs_destroy_size_tree - free resources related to the size tree. ++ * @c: UBIFS file-system description object ++ */ ++void ubifs_destroy_size_tree(struct ubifs_info *c) ++{ ++ struct rb_node *this = c->size_tree.rb_node; ++ struct size_entry *e; ++ ++ while (this) { ++ if (this->rb_left) { ++ this = this->rb_left; ++ continue; ++ } else if (this->rb_right) { ++ this = this->rb_right; ++ continue; ++ } ++ e = rb_entry(this, struct size_entry, rb); ++ if (e->inode) ++ iput(e->inode); ++ this = rb_parent(this); ++ if (this) { ++ if (this->rb_left == &e->rb) ++ this->rb_left = NULL; ++ else ++ this->rb_right = NULL; ++ } ++ kfree(e); ++ } ++ c->size_tree = RB_ROOT; ++} ++ ++/** ++ * ubifs_recover_size_accum - accumulate inode sizes for recovery. ++ * @c: UBIFS file-system description object ++ * @key: node key ++ * @deletion: node is for a deletion ++ * @new_size: inode size ++ * ++ * This function has two purposes: ++ * 1) to ensure there are no data nodes that fall outside the inode size ++ * 2) to ensure there are no data nodes for inodes that do not exist ++ * To accomplish those purposes, a rb-tree is constructed containing an entry ++ * for each inode number in the journal that has not been deleted, and recording ++ * the size from the inode node, the maximum size of any data node (also altered ++ * by truncations) and a flag indicating a inode number for which no inode node ++ * was present in the journal. ++ * ++ * Note that there is still the possibility that there are data nodes that have ++ * been committed that are beyond the inode size, however the only way to find ++ * them would be to scan the entire index. Alternatively, some provision could ++ * be made to record the size of inodes at the start of commit, which would seem ++ * very cumbersome for a scenario that is quite unlikely and the only negative ++ * consequence of which is wasted space. ++ * ++ * This functions returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_recover_size_accum(struct ubifs_info *c, union ubifs_key *key, ++ int deletion, loff_t new_size) ++{ ++ ino_t inum = key_ino(c, key); ++ struct size_entry *e; ++ int err; ++ ++ switch (key_type(c, key)) { ++ case UBIFS_INO_KEY: ++ if (deletion) ++ remove_ino(c, inum); ++ else { ++ e = find_ino(c, inum); ++ if (e) { ++ e->i_size = new_size; ++ e->exists = 1; ++ } else { ++ err = add_ino(c, inum, new_size, 0, 1); ++ if (err) ++ return err; ++ } ++ } ++ break; ++ case UBIFS_DATA_KEY: ++ e = find_ino(c, inum); ++ if (e) { ++ if (new_size > e->d_size) ++ e->d_size = new_size; ++ } else { ++ err = add_ino(c, inum, 0, new_size, 0); ++ if (err) ++ return err; ++ } ++ break; ++ case UBIFS_TRUN_KEY: ++ e = find_ino(c, inum); ++ if (e) ++ e->d_size = new_size; ++ break; ++ } ++ return 0; ++} ++ ++/** ++ * fix_size_in_place - fix inode size in place on flash. ++ * @c: UBIFS file-system description object ++ * @e: inode size information for recovery ++ */ ++static int fix_size_in_place(struct ubifs_info *c, struct size_entry *e) ++{ ++ struct ubifs_ino_node *ino = c->sbuf; ++ unsigned char *p; ++ union ubifs_key key; ++ int err, lnum, offs, len; ++ loff_t i_size; ++ uint32_t crc; ++ ++ /* Locate the inode node LEB number and offset */ ++ ino_key_init(c, &key, e->inum); ++ err = ubifs_tnc_locate(c, &key, ino, &lnum, &offs); ++ if (err) ++ goto out; ++ /* ++ * If the size recorded on the inode node is greater than the size that ++ * was calculated from nodes in the journal then don't change the inode. ++ */ ++ i_size = le64_to_cpu(ino->size); ++ if (i_size >= e->d_size) ++ return 0; ++ /* Read the LEB */ ++ err = ubi_read(c->ubi, lnum, c->sbuf, 0, c->leb_size); ++ if (err) ++ goto out; ++ /* Change the size field and recalculate the CRC */ ++ ino = c->sbuf + offs; ++ ino->size = cpu_to_le64(e->d_size); ++ len = le32_to_cpu(ino->ch.len); ++ crc = crc32(UBIFS_CRC32_INIT, (void *)ino + 8, len - 8); ++ ino->ch.crc = cpu_to_le32(crc); ++ /* Work out where data in the LEB ends and free space begins */ ++ p = c->sbuf; ++ len = c->leb_size - 1; ++ while (p[len] == 0xff) ++ len -= 1; ++ len = ALIGN(len + 1, c->min_io_size); ++ /* Atomically write the fixed LEB back again */ ++ err = ubi_leb_change(c->ubi, lnum, c->sbuf, len, UBI_UNKNOWN); ++ if (err) ++ goto out; ++ dbg_rcvry("inode %lu at %d:%d size %lld -> %lld ", e->inum, lnum, offs, ++ i_size, e->d_size); ++ return 0; ++ ++out: ++ ubifs_warn("inode %lu failed to fix size %lld -> %lld error %d", ++ e->inum, e->i_size, e->d_size, err); ++ return err; ++} ++ ++/** ++ * ubifs_recover_size - recover inode size. ++ * @c: UBIFS file-system description object ++ * ++ * This function attempts to fix inode size discrepancies identified by the ++ * 'ubifs_recover_size_accum()' function. ++ * ++ * This functions returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_recover_size(struct ubifs_info *c) ++{ ++ struct rb_node *this = rb_first(&c->size_tree); ++ ++ while (this) { ++ struct size_entry *e; ++ int err; ++ ++ e = rb_entry(this, struct size_entry, rb); ++ if (!e->exists) { ++ union ubifs_key key; ++ ++ ino_key_init(c, &key, e->inum); ++ err = ubifs_tnc_lookup(c, &key, c->sbuf); ++ if (err && err != -ENOENT) ++ return err; ++ if (err == -ENOENT) { ++ /* Remove data nodes that have no inode */ ++ dbg_rcvry("removing ino %lu", e->inum); ++ err = ubifs_tnc_remove_ino(c, e->inum); ++ if (err) ++ return err; ++ /* ++ * If we later unmount cleanly without ++ * committing, the TNC changes will be lost, ++ * hence we set a flag to ensure a commit is ++ * done. ++ */ ++ c->recovery_needs_commit = 1; ++ } else { ++ struct ubifs_ino_node *ino = c->sbuf; ++ ++ e->exists = 1; ++ e->i_size = le64_to_cpu(ino->size); ++ } ++ } ++ if (e->exists && e->i_size < e->d_size) { ++ if (!e->inode && (c->vfs_sb->s_flags & MS_RDONLY)) { ++ /* Fix the inode size and pin it in memory */ ++ struct inode *inode; ++ ++ inode = ubifs_iget(c->vfs_sb, e->inum); ++ if (IS_ERR(inode)) ++ return PTR_ERR(inode); ++ if (inode->i_size < e->d_size) { ++ dbg_rcvry("ino %lu size %lld -> %lld", ++ e->inum, e->d_size, ++ inode->i_size); ++ inode->i_size = e->d_size; ++ e->inode = inode; ++ this = rb_next(this); ++ continue; ++ } ++ iput(inode); ++ } else { ++ /* Fix the size in place */ ++ err = fix_size_in_place(c, e); ++ if (err) { ++ if (e->inode) ++ /* ++ * We have changed the inode ++ * size in memory but failed to ++ * fix it on flash. Mark it ++ * dirty without budgeting, and ++ * hope we don't run out of ++ * space. ++ */ ++ mark_inode_dirty_sync(e->inode); ++ /* ++ * We consider that failing to recover ++ * the size is not fatal, because it ++ * only affects files that were being ++ * written without synchronization and ++ * the only down side is that some space ++ * may be wasted. ++ */ ++ err = 0; ++ } ++ if (e->inode) ++ iput(e->inode); ++ } ++ } ++ this = rb_next(this); ++ rb_erase(&e->rb, &c->size_tree); ++ kfree(e); ++ } ++ return 0; ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/replay.c linux-2.6.24.7/fs/ubifs/replay.c +--- linux-2.6.24.7.old/fs/ubifs/replay.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/replay.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1049 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy (Битюцкий Ðртём) ++ */ ++ ++/* ++ * This file contains journal replay code. It runs when the file-system is being ++ * mounted and requires no locking. ++ * ++ * The larger is the journal, the longer it takes to scan it, so the longer it ++ * takes to mount UBIFS. This is why the journal has limited size which may be ++ * changed depending on the system requirements. But a larger journal gives ++ * faster I/O speed because it writes the index less frequently. So this is a ++ * trade-off. Also, the journal is indexed by the in-memory index (TNC), so the ++ * larger is the journal, the more memory its index may consume. ++ */ ++ ++#include "ubifs.h" ++ ++/* ++ * Replay flags. ++ * ++ * REPLAY_DELETION: node was deleted ++ * REPLAY_REF: node is a reference node ++ */ ++enum { ++ REPLAY_DELETION = 1, ++ REPLAY_REF = 2, ++}; ++ ++/** ++ * struct replay_entry - replay tree entry. ++ * @lnum: logical eraseblock number of the node ++ * @offs: node offset ++ * @len: node length ++ * @sqnum: node sequence number ++ * @flags: replay flags ++ * @rb: links the replay tree ++ * @key: node key ++ * @nm: directory entry name ++ * @old_size: truncation old size ++ * @new_size: truncation new size ++ * @free: amount of free space in a bud ++ * @dirty: amount of dirty space in a bud from padding and deletion nodes ++ * ++ * UBIFS journal replay must compare node sequence numbers, which means it must ++ * build a tree of node information to insert into the TNC. ++ */ ++struct replay_entry { ++ int lnum; ++ int offs; ++ int len; ++ unsigned long long sqnum; ++ int flags; ++ struct rb_node rb; ++ union ubifs_key key; ++ union { ++ struct qstr nm; ++ struct { ++ loff_t old_size; ++ loff_t new_size; ++ }; ++ struct { ++ int free; ++ int dirty; ++ }; ++ }; ++}; ++ ++/** ++ * struct bud_entry - entry in the list of buds to replay. ++ * @list: next bud in the list ++ * @bud: bud description object ++ * @free: free bytes in the bud ++ * @sqnum: reference node sequence number ++ */ ++struct bud_entry { ++ struct list_head list; ++ struct ubifs_bud *bud; ++ int free; ++ unsigned long long sqnum; ++}; ++ ++/** ++ * set_bud_lprops - set free and dirty space used by a bud. ++ * @c: UBIFS file-system description object ++ * @r: replay entry of bud ++ */ ++static int set_bud_lprops(struct ubifs_info *c, struct replay_entry *r) ++{ ++ const struct ubifs_lprops *lp; ++ int err = 0, dirty; ++ ++ ubifs_get_lprops(c); ++ ++ lp = ubifs_lpt_lookup_dirty(c, r->lnum); ++ if (IS_ERR(lp)) { ++ err = PTR_ERR(lp); ++ goto out; ++ } ++ ++ dirty = lp->dirty; ++ if (r->offs == 0 && (lp->free != c->leb_size || lp->dirty != 0)) { ++ dbg_mnt("bud LEB %d was GC'd (%d free, %d dirty)", r->lnum, ++ lp->free, lp->dirty); ++ dbg_gc("bud LEB %d was GC'd (%d free, %d dirty)", r->lnum, ++ lp->free, lp->dirty); ++ dirty -= c->leb_size - lp->free; ++ if (dirty != 0) ++ dbg_msg("LEB %d lp: %d free %d dirty " ++ "replay: %d free %d dirty", r->lnum, lp->free, ++ lp->dirty, r->free, r->dirty); ++ } ++ lp = ubifs_change_lp(c, lp, r->free, dirty + r->dirty, ++ lp->flags | LPROPS_TAKEN, 0); ++ if (IS_ERR(lp)) { ++ err = PTR_ERR(lp); ++ goto out; ++ } ++out: ++ ubifs_release_lprops(c); ++ return err; ++} ++ ++/** ++ * trun_remove_range - apply a replay entry for a truncation to the TNC. ++ * @c: UBIFS file-system description object ++ * @r: replay entry of truncation ++ */ ++static int trun_remove_range(struct ubifs_info *c, struct replay_entry *r) ++{ ++ unsigned min_blk, max_blk; ++ union ubifs_key min_key, max_key; ++ ino_t ino; ++ ++ min_blk = r->new_size / UBIFS_BLOCK_SIZE; ++ if (r->new_size & (UBIFS_BLOCK_SIZE - 1)) ++ min_blk += 1; ++ ++ max_blk = r->old_size / UBIFS_BLOCK_SIZE; ++ if ((r->old_size & (UBIFS_BLOCK_SIZE - 1)) == 0) ++ max_blk -= 1; ++ ++ ino = key_ino(c, &r->key); ++ ++ data_key_init(c, &min_key, ino, min_blk); ++ data_key_init(c, &max_key, ino, max_blk); ++ ++ return ubifs_tnc_remove_range(c, &min_key, &max_key); ++} ++ ++/** ++ * apply_replay_entry - apply a replay entry to the TNC. ++ * @c: UBIFS file-system description object ++ * @r: replay entry to apply ++ * ++ * Apply a replay entry to the TNC. ++ */ ++static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r) ++{ ++ int err, deletion = ((r->flags & REPLAY_DELETION) != 0); ++ ++ dbg_mnt("LEB %d:%d len %d flgs %d sqnum %llu %s", r->lnum, ++ r->offs, r->len, r->flags, r->sqnum, DBGKEY(&r->key)); ++ ++ /* Set c->replay_sqnum to help deal with dangling branches. */ ++ c->replay_sqnum = r->sqnum; ++ ++ if (r->flags & REPLAY_REF) ++ err = set_bud_lprops(c, r); ++ else if (is_hash_key(c, &r->key)) { ++ if (deletion) ++ err = ubifs_tnc_remove_nm(c, &r->key, &r->nm); ++ else ++ err = ubifs_tnc_add_nm(c, &r->key, r->lnum, r->offs, ++ r->len, &r->nm); ++ } else { ++ if (deletion) ++ switch (key_type(c, &r->key)) { ++ case UBIFS_INO_KEY: ++ { ++ ino_t inum = key_ino(c, &r->key); ++ ++ err = ubifs_tnc_remove_ino(c, inum); ++ break; ++ } ++ case UBIFS_TRUN_KEY: ++ err = trun_remove_range(c, r); ++ break; ++ default: ++ err = ubifs_tnc_remove(c, &r->key); ++ break; ++ } ++ else ++ err = ubifs_tnc_add(c, &r->key, r->lnum, r->offs, ++ r->len); ++ if (err) ++ return err; ++ ++ if (c->need_recovery) ++ err = ubifs_recover_size_accum(c, &r->key, deletion, ++ r->new_size); ++ } ++ ++ return err; ++} ++ ++/** ++ * destroy_replay_tree - destroy the replay. ++ * @c: UBIFS file-system description object ++ * ++ * Destroy the replay tree. ++ */ ++static void destroy_replay_tree(struct ubifs_info *c) ++{ ++ struct rb_node *this = c->replay_tree.rb_node; ++ struct replay_entry *r; ++ ++ while (this) { ++ if (this->rb_left) { ++ this = this->rb_left; ++ continue; ++ } else if (this->rb_right) { ++ this = this->rb_right; ++ continue; ++ } ++ r = rb_entry(this, struct replay_entry, rb); ++ this = rb_parent(this); ++ if (this) { ++ if (this->rb_left == &r->rb) ++ this->rb_left = NULL; ++ else ++ this->rb_right = NULL; ++ } ++ if (is_hash_key(c, &r->key)) ++ kfree(r->nm.name); ++ kfree(r); ++ } ++ c->replay_tree = RB_ROOT; ++} ++ ++/** ++ * apply_replay_tree - apply the replay tree to the TNC. ++ * @c: UBIFS file-system description object ++ * ++ * Apply the replay tree. ++ * Returns zero in case of success and a negative error code in case of ++ * failure. ++ */ ++static int apply_replay_tree(struct ubifs_info *c) ++{ ++ struct rb_node *this = rb_first(&c->replay_tree); ++ ++ while (this) { ++ struct replay_entry *r; ++ int err; ++ ++ cond_resched(); ++ ++ r = rb_entry(this, struct replay_entry, rb); ++ err = apply_replay_entry(c, r); ++ if (err) ++ return err; ++ this = rb_next(this); ++ } ++ return 0; ++} ++ ++/** ++ * insert_node - insert a node to the replay tree. ++ * @c: UBIFS file-system description object ++ * @lnum: node logical eraseblock number ++ * @offs: node offset ++ * @len: node length ++ * @key: node key ++ * @sqnum: sequence number ++ * @deletion: non-zero if this is a deletion ++ * @used: number of bytes in use in a LEB ++ * @old_size: truncation old size ++ * @new_size: truncation new size ++ * ++ * This function inserts a scanned non-direntry node to the replay tree. The ++ * replay tree is an RB-tree containing @struct replay_entry elements which are ++ * indexed by the sequence number. The replay tree is applied at the very end ++ * of the replay process. Since the tree is sorted in sequence number order, ++ * the older modifications are applied first. This function returns zero in ++ * case of success and a negative error code in case of failure. ++ */ ++static int insert_node(struct ubifs_info *c, int lnum, int offs, int len, ++ union ubifs_key *key, unsigned long long sqnum, ++ int deletion, int *used, loff_t old_size, ++ loff_t new_size) ++{ ++ struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL; ++ struct replay_entry *r; ++ ++ if (key_ino(c, key) >= c->highest_inum) ++ c->highest_inum = key_ino(c, key); ++ ++ dbg_mnt("add LEB %d:%d, key %s", lnum, offs, DBGKEY(key)); ++ while (*p) { ++ parent = *p; ++ r = rb_entry(parent, struct replay_entry, rb); ++ if (sqnum < r->sqnum) { ++ p = &(*p)->rb_left; ++ continue; ++ } else if (sqnum > r->sqnum) { ++ p = &(*p)->rb_right; ++ continue; ++ } ++ ubifs_err("duplicate sqnum in replay"); ++ return -EINVAL; ++ } ++ ++ r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL); ++ if (!r) ++ return -ENOMEM; ++ ++ if (!deletion) ++ *used += ALIGN(len, 8); ++ r->lnum = lnum; ++ r->offs = offs; ++ r->len = len; ++ r->sqnum = sqnum; ++ r->flags = (deletion ? REPLAY_DELETION : 0); ++ r->old_size = old_size; ++ r->new_size = new_size; ++ key_copy(c, key, &r->key); ++ ++ rb_link_node(&r->rb, parent, p); ++ rb_insert_color(&r->rb, &c->replay_tree); ++ return 0; ++} ++ ++/** ++ * insert_dent - insert a directory entry node into the replay tree. ++ * @c: UBIFS file-system description object ++ * @lnum: node logical eraseblock number ++ * @offs: node offset ++ * @len: node length ++ * @key: node key ++ * @name: directory entry name ++ * @nlen: directory entry name length ++ * @sqnum: sequence number ++ * @deletion: non-zero if this is a deletion ++ * @used: number of bytes in use in a LEB ++ * ++ * This function inserts a scanned directory entry node to the replay tree. ++ * Returns zero in case of success and a negative error code in case of ++ * failure. ++ * ++ * This function is also used for extended attribute entries because they are ++ * implemented as directory entry nodes. ++ */ ++static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len, ++ union ubifs_key *key, const char *name, int nlen, ++ unsigned long long sqnum, int deletion, int *used) ++{ ++ struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL; ++ struct replay_entry *r; ++ char *nbuf; ++ ++ if (key_ino(c, key) >= c->highest_inum) ++ c->highest_inum = key_ino(c, key); ++ ++ dbg_mnt("add LEB %d:%d, key %s", lnum, offs, DBGKEY(key)); ++ while (*p) { ++ parent = *p; ++ r = rb_entry(parent, struct replay_entry, rb); ++ if (sqnum < r->sqnum) { ++ p = &(*p)->rb_left; ++ continue; ++ } ++ if (sqnum > r->sqnum) { ++ p = &(*p)->rb_right; ++ continue; ++ } ++ ubifs_err("duplicate sqnum in replay"); ++ return -EINVAL; ++ } ++ ++ r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL); ++ if (!r) ++ return -ENOMEM; ++ nbuf = kmalloc(nlen + 1, GFP_KERNEL); ++ if (!nbuf) { ++ kfree(r); ++ return -ENOMEM; ++ } ++ ++ if (!deletion) ++ *used += ALIGN(len, 8); ++ r->lnum = lnum; ++ r->offs = offs; ++ r->len = len; ++ r->sqnum = sqnum; ++ r->nm.len = nlen; ++ memcpy(nbuf, name, nlen); ++ nbuf[nlen] = '\0'; ++ r->nm.name = nbuf; ++ r->flags = (deletion ? REPLAY_DELETION : 0); ++ key_copy(c, key, &r->key); ++ ++ ubifs_assert(!*p); ++ rb_link_node(&r->rb, parent, p); ++ rb_insert_color(&r->rb, &c->replay_tree); ++ return 0; ++} ++ ++/** ++ * ubifs_validate_entry - validate directory or extended attribute entry node. ++ * @c: UBIFS file-system description object ++ * @dent: the node to validate ++ * ++ * This function validates directory or extended attribute entry node @dent. ++ * Returns zero if the node is all right and a %-EINVAL if not. ++ */ ++int ubifs_validate_entry(struct ubifs_info *c, ++ const struct ubifs_dent_node *dent) ++{ ++ int key_type = key_type_flash(c, dent->key); ++ int nlen = le16_to_cpu(dent->nlen); ++ ++ if (le32_to_cpu(dent->ch.len) != nlen + UBIFS_DENT_NODE_SZ + 1 || ++ dent->type >= UBIFS_ITYPES_CNT || ++ nlen > UBIFS_MAX_NLEN || dent->name[nlen] != 0 || ++ strnlen(dent->name, nlen) != nlen || ++ le64_to_cpu(dent->inum) > MAX_INUM) { ++ ubifs_err("bad %s node", key_type == UBIFS_DENT_KEY ? ++ "directory entry" : "extended attribute entry"); ++ return -EINVAL; ++ } ++ ++ if (key_type != UBIFS_DENT_KEY && key_type != UBIFS_XENT_KEY) { ++ ubifs_err("bad key type %d", key_type); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * replay_bud - replay a bud logical eraseblock. ++ * @c: UBIFS file-system description object ++ * @lnum: bud logical eraseblock number to replay ++ * @offs: bud start offset ++ * @jhead: journal head to which this bud belongs ++ * @free: amount of free space in the bud is returned here ++ * @dirty: amount of dirty space from padding and deletion nodes is returned ++ * here ++ * ++ * This function returns zero in case of success and a negative error code in ++ * case of failure. ++ */ ++static int replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, ++ int *free, int *dirty) ++{ ++ int err = 0, used = 0; ++ struct ubifs_scan_leb *sleb; ++ struct ubifs_scan_node *snod; ++ struct ubifs_bud *bud; ++ ++ dbg_mnt("replay bud LEB %d, head %d", lnum, jhead); ++ if (c->need_recovery) ++ sleb = ubifs_recover_leb(c, lnum, offs, c->sbuf, jhead != GCHD); ++ else ++ sleb = ubifs_scan(c, lnum, offs, c->sbuf); ++ if (IS_ERR(sleb)) ++ return PTR_ERR(sleb); ++ ++ /* ++ * The bud does not have to start from offset zero - the beginning of ++ * the 'lnum' LEB may contain previously committed data. One of the ++ * things we have to do in replay is to correctly update lprops with ++ * newer information about this LEB. ++ * ++ * At this point lprops thinks that this LEB has 'c->leb_size - offs' ++ * bytes of free space because it only contain information about ++ * committed data. ++ * ++ * But we know that real amount of free space is 'c->leb_size - ++ * sleb->endpt', and the space in the 'lnum' LEB between 'offs' and ++ * 'sleb->endpt' is used by bud data. We have to correctly calculate ++ * how much of these data are dirty and update lprops with this ++ * information. ++ * ++ * The dirt in that LEB region is comprised of padding nodes, deletion ++ * nodes, truncation nodes and nodes which are obsoleted by subsequent ++ * nodes in this LEB. So instead of calculating clean space, we ++ * calculate used space ('used' variable). ++ */ ++ ++ list_for_each_entry(snod, &sleb->nodes, list) { ++ int deletion = 0; ++ ++ cond_resched(); ++ ++ if (snod->sqnum >= SQNUM_WATERMARK) { ++ ubifs_err("file system's life ended"); ++ goto out_dump; ++ } ++ ++ if (snod->sqnum > c->max_sqnum) ++ c->max_sqnum = snod->sqnum; ++ ++ switch (snod->type) { ++ case UBIFS_INO_NODE: ++ { ++ struct ubifs_ino_node *ino = snod->node; ++ loff_t new_size = le64_to_cpu(ino->size); ++ ++ if (le32_to_cpu(ino->nlink) == 0) ++ deletion = 1; ++ err = insert_node(c, lnum, snod->offs, snod->len, ++ &snod->key, snod->sqnum, deletion, ++ &used, 0, new_size); ++ break; ++ } ++ case UBIFS_DATA_NODE: ++ { ++ struct ubifs_data_node *dn = snod->node; ++ loff_t new_size = le32_to_cpu(dn->size) + ++ key_block(c, &snod->key) * ++ UBIFS_BLOCK_SIZE; ++ ++ err = insert_node(c, lnum, snod->offs, snod->len, ++ &snod->key, snod->sqnum, deletion, ++ &used, 0, new_size); ++ break; ++ } ++ case UBIFS_DENT_NODE: ++ case UBIFS_XENT_NODE: ++ { ++ struct ubifs_dent_node *dent = snod->node; ++ ++ err = ubifs_validate_entry(c, dent); ++ if (err) ++ goto out_dump; ++ ++ err = insert_dent(c, lnum, snod->offs, snod->len, ++ &snod->key, dent->name, ++ le16_to_cpu(dent->nlen), snod->sqnum, ++ !le64_to_cpu(dent->inum), &used); ++ break; ++ } ++ case UBIFS_TRUN_NODE: ++ { ++ struct ubifs_trun_node *trun = snod->node; ++ loff_t old_size = le64_to_cpu(trun->old_size); ++ loff_t new_size = le64_to_cpu(trun->new_size); ++ ++ /* Validate truncation node */ ++ if (old_size < 0 || old_size > c->max_inode_sz || ++ new_size < 0 || new_size > c->max_inode_sz || ++ old_size <= new_size) { ++ ubifs_err("bad truncation node"); ++ goto out_dump; ++ } ++ ++ err = insert_node(c, lnum, snod->offs, snod->len, ++ &snod->key, snod->sqnum, 1, &used, ++ old_size, new_size); ++ break; ++ } ++ default: ++ ubifs_err("unexpected node type %d in bud LEB %d:%d", ++ snod->type, lnum, snod->offs); ++ err = -EINVAL; ++ goto out_dump; ++ } ++ if (err) ++ goto out; ++ } ++ ++ bud = ubifs_search_bud(c, lnum); ++ if (!bud) ++ BUG(); ++ ++ ubifs_assert(sleb->endpt - offs >= used); ++ ubifs_assert(sleb->endpt % c->min_io_size == 0); ++ ++ if (sleb->endpt + c->min_io_size <= c->leb_size && ++ !(c->vfs_sb->s_flags & MS_RDONLY)) ++ err = ubifs_wbuf_seek_nolock(&c->jheads[jhead].wbuf, lnum, ++ sleb->endpt, UBI_SHORTTERM); ++ ++ *dirty = sleb->endpt - offs - used; ++ *free = c->leb_size - sleb->endpt; ++ ++out: ++ ubifs_scan_destroy(sleb); ++ return err; ++ ++out_dump: ++ ubifs_err("bad node is at LEB %d:%d", lnum, snod->offs); ++ dbg_dump_node(c, snod->node); ++ ubifs_scan_destroy(sleb); ++ return -EINVAL; ++} ++ ++/** ++ * insert_ref_node - insert a reference node to the replay tree. ++ * @c: UBIFS file-system description object ++ * @lnum: node logical eraseblock number ++ * @offs: node offset ++ * @sqnum: sequence number ++ * @free: amount of free space in bud ++ * @dirty: amount of dirty space from padding and deletion nodes ++ * ++ * This function inserts a reference node to the replay tree and returns zero ++ * in case of success ort a negative error code in case of failure. ++ */ ++static int insert_ref_node(struct ubifs_info *c, int lnum, int offs, ++ unsigned long long sqnum, int free, int dirty) ++{ ++ struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL; ++ struct replay_entry *r; ++ ++ dbg_mnt("add ref LEB %d:%d", lnum, offs); ++ while (*p) { ++ parent = *p; ++ r = rb_entry(parent, struct replay_entry, rb); ++ if (sqnum < r->sqnum) { ++ p = &(*p)->rb_left; ++ continue; ++ } else if (sqnum > r->sqnum) { ++ p = &(*p)->rb_right; ++ continue; ++ } ++ ubifs_err("duplicate sqnum in replay tree"); ++ return -EINVAL; ++ } ++ ++ r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL); ++ if (!r) ++ return -ENOMEM; ++ ++ r->lnum = lnum; ++ r->offs = offs; ++ r->sqnum = sqnum; ++ r->flags = REPLAY_REF; ++ r->free = free; ++ r->dirty = dirty; ++ ++ rb_link_node(&r->rb, parent, p); ++ rb_insert_color(&r->rb, &c->replay_tree); ++ return 0; ++} ++ ++/** ++ * replay_buds - replay all buds. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns zero in case of success and a negative error code in ++ * case of failure. ++ */ ++static int replay_buds(struct ubifs_info *c) ++{ ++ struct bud_entry *b; ++ int err, uninitialized_var(free), uninitialized_var(dirty); ++ ++ list_for_each_entry(b, &c->replay_buds, list) { ++ err = replay_bud(c, b->bud->lnum, b->bud->start, b->bud->jhead, ++ &free, &dirty); ++ if (err) ++ return err; ++ err = insert_ref_node(c, b->bud->lnum, b->bud->start, b->sqnum, ++ free, dirty); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/** ++ * destroy_bud_list - destroy the list of buds to replay. ++ * @c: UBIFS file-system description object ++ */ ++static void destroy_bud_list(struct ubifs_info *c) ++{ ++ struct bud_entry *b; ++ ++ while (!list_empty(&c->replay_buds)) { ++ b = list_entry(c->replay_buds.next, struct bud_entry, list); ++ list_del(&b->list); ++ kfree(b); ++ } ++} ++ ++/** ++ * add_replay_bud - add a bud to the list of buds to replay. ++ * @c: UBIFS file-system description object ++ * @lnum: bud logical eraseblock number to replay ++ * @offs: bud start offset ++ * @jhead: journal head to which this bud belongs ++ * @sqnum: reference node sequence number ++ * ++ * This function returns zero in case of success and a negative error code in ++ * case of failure. ++ */ ++static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, ++ unsigned long long sqnum) ++{ ++ struct ubifs_bud *bud; ++ struct bud_entry *b; ++ ++ dbg_mnt("add replay bud LEB %d:%d, head %d", lnum, offs, jhead); ++ ++ bud = kmalloc(sizeof(struct ubifs_bud), GFP_KERNEL); ++ if (!bud) ++ return -ENOMEM; ++ ++ b = kmalloc(sizeof(struct bud_entry), GFP_KERNEL); ++ if (!b) { ++ kfree(bud); ++ return -ENOMEM; ++ } ++ ++ bud->lnum = lnum; ++ bud->start = offs; ++ bud->jhead = jhead; ++ ubifs_add_bud(c, bud); ++ ++ b->bud = bud; ++ b->sqnum = sqnum; ++ list_add_tail(&b->list, &c->replay_buds); ++ ++ return 0; ++} ++ ++/** ++ * validate_ref - validate a reference node. ++ * @c: UBIFS file-system description object ++ * @ref: the reference node to validate ++ * @ref_lnum: LEB number of the reference node ++ * @ref_offs: reference node offset ++ * ++ * This function returns %1 if a bud reference already exists for the LEB. %0 is ++ * returned if the reference node is new, otherwise %-EINVAL is returned if ++ * validation failed. ++ */ ++static int validate_ref(struct ubifs_info *c, const struct ubifs_ref_node *ref) ++{ ++ struct ubifs_bud *bud; ++ int lnum = le32_to_cpu(ref->lnum); ++ unsigned int offs = le32_to_cpu(ref->offs); ++ unsigned int jhead = le32_to_cpu(ref->jhead); ++ ++ /* ++ * ref->offs may point to the end of LEB when the journal head points ++ * to the end of LEB and we write reference node for it during commit. ++ * So this is why we require 'offs > c->leb_size'. ++ */ ++ if (jhead >= c->jhead_cnt || lnum >= c->leb_cnt || ++ lnum < c->main_first || offs > c->leb_size || ++ offs & (c->min_io_size - 1)) ++ return -EINVAL; ++ ++ /* Make sure we have not already looked at this bud */ ++ bud = ubifs_search_bud(c, lnum); ++ if (bud) { ++ if (bud->jhead == jhead && bud->start <= offs) ++ return 1; ++ ubifs_err("bud at LEB %d:%d was already referred", lnum, offs); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * replay_log_leb - replay a log logical eraseblock. ++ * @c: UBIFS file-system description object ++ * @lnum: log logical eraseblock to replay ++ * @offs: offset to start replaying from ++ * @sbuf: scan buffer ++ * ++ * This function replays a log LEB and returns zero in case of success, %1 if ++ * this is the last LEB in the log, and a negative error code in case of ++ * failure. ++ */ ++static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) ++{ ++ int err; ++ struct ubifs_scan_leb *sleb; ++ struct ubifs_scan_node *snod; ++ const struct ubifs_cs_node *node; ++ ++ dbg_mnt("replay log LEB %d:%d", lnum, offs); ++ sleb = ubifs_scan(c, lnum, offs, sbuf); ++ if (IS_ERR(sleb)) { ++ if (c->need_recovery) ++ sleb = ubifs_recover_log_leb(c, lnum, offs, sbuf); ++ if (IS_ERR(sleb)) ++ return PTR_ERR(sleb); ++ } ++ ++ if (sleb->nodes_cnt == 0) { ++ err = 1; ++ goto out; ++ } ++ ++ node = sleb->buf; ++ ++ snod = list_entry(sleb->nodes.next, struct ubifs_scan_node, list); ++ if (c->cs_sqnum == 0) { ++ /* ++ * This is the first log LEB we are looking at, make sure that ++ * the first node is a commit start node. Also record its ++ * sequence number so that UBIFS can determine where the log ++ * ends, because all nodes which were have higher sequence ++ * numbers. ++ */ ++ if (snod->type != UBIFS_CS_NODE) { ++ dbg_err("first log node at LEB %d:%d is not CS node", ++ lnum, offs); ++ goto out_dump; ++ } ++ if (le64_to_cpu(node->cmt_no) != c->cmt_no) { ++ dbg_err("first CS node at LEB %d:%d has wrong " ++ "commit number %llu expected %llu", ++ lnum, offs, ++ (unsigned long long)le64_to_cpu(node->cmt_no), ++ c->cmt_no); ++ goto out_dump; ++ } ++ ++ c->cs_sqnum = le64_to_cpu(node->ch.sqnum); ++ dbg_mnt("commit start sqnum %llu", c->cs_sqnum); ++ } ++ ++ if (snod->sqnum < c->cs_sqnum) { ++ /* ++ * This means that we reached end of log and now ++ * look to the older log data, which was already ++ * committed but the eraseblock was not erased (UBIFS ++ * only unmaps it). So this basically means we have to ++ * exit with "end of log" code. ++ */ ++ err = 1; ++ goto out; ++ } ++ ++ /* Make sure the first node sits at offset zero of the LEB */ ++ if (snod->offs != 0) { ++ dbg_err("first node is not at zero offset"); ++ goto out_dump; ++ } ++ ++ list_for_each_entry(snod, &sleb->nodes, list) { ++ ++ cond_resched(); ++ ++ if (snod->sqnum >= SQNUM_WATERMARK) { ++ ubifs_err("file system's life ended"); ++ goto out_dump; ++ } ++ ++ if (snod->sqnum < c->cs_sqnum) { ++ dbg_err("bad sqnum %llu, commit sqnum %llu", ++ snod->sqnum, c->cs_sqnum); ++ goto out_dump; ++ } ++ ++ if (snod->sqnum > c->max_sqnum) ++ c->max_sqnum = snod->sqnum; ++ ++ switch (snod->type) { ++ case UBIFS_REF_NODE: { ++ const struct ubifs_ref_node *ref = snod->node; ++ ++ err = validate_ref(c, ref); ++ if (err == 1) ++ break; /* Already have this bud */ ++ if (err) ++ goto out_dump; ++ ++ err = add_replay_bud(c, le32_to_cpu(ref->lnum), ++ le32_to_cpu(ref->offs), ++ le32_to_cpu(ref->jhead), ++ snod->sqnum); ++ if (err) ++ goto out; ++ ++ break; ++ } ++ case UBIFS_CS_NODE: ++ /* Make sure it sits at the beginning of LEB */ ++ if (snod->offs != 0) { ++ ubifs_err("unexpected node in log"); ++ goto out_dump; ++ } ++ break; ++ default: ++ ubifs_err("unexpected node in log"); ++ goto out_dump; ++ } ++ } ++ ++ if (sleb->endpt || c->lhead_offs >= c->leb_size) { ++ c->lhead_lnum = lnum; ++ c->lhead_offs = sleb->endpt; ++ } ++ ++ err = !sleb->endpt; ++out: ++ ubifs_scan_destroy(sleb); ++ return err; ++ ++out_dump: ++ ubifs_err("log error detected while replying the log at LEB %d:%d", ++ lnum, offs + snod->offs); ++ dbg_dump_node(c, snod->node); ++ ubifs_scan_destroy(sleb); ++ return -EINVAL; ++} ++ ++/** ++ * take_ihead - update the status of the index head in lprops to 'taken'. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns the amount of free space in the index head LEB or a ++ * negative error code. ++ */ ++static int take_ihead(struct ubifs_info *c) ++{ ++ const struct ubifs_lprops *lp; ++ int err, free; ++ ++ ubifs_get_lprops(c); ++ ++ lp = ubifs_lpt_lookup_dirty(c, c->ihead_lnum); ++ if (IS_ERR(lp)) { ++ err = PTR_ERR(lp); ++ goto out; ++ } ++ ++ free = lp->free; ++ ++ lp = ubifs_change_lp(c, lp, -1, -1, lp->flags | LPROPS_TAKEN, 0); ++ if (IS_ERR(lp)) { ++ err = PTR_ERR(lp); ++ goto out; ++ } ++ ++ err = free; ++out: ++ ubifs_release_lprops(c); ++ return err; ++} ++ ++/** ++ * ubifs_replay_journal - replay journal. ++ * @c: UBIFS file-system description object ++ * ++ * This function scans the journal, replays and cleans it up. It makes sure all ++ * memory data structures related to uncommitted journal are built (dirty TNC ++ * tree, tree of buds, modified lprops, etc). ++ */ ++int ubifs_replay_journal(struct ubifs_info *c) ++{ ++ int err, i, lnum, offs, free; ++ void *sbuf = NULL; ++ ++ /* Update the status of the index head in lprops to 'taken' */ ++ free = take_ihead(c); ++ if (free < 0) ++ return free; /* Error code */ ++ ++ if (c->ihead_offs != c->leb_size - free) { ++ ubifs_err("bad index head LEB %d:%d", c->ihead_lnum, ++ c->ihead_offs); ++ return -EINVAL; ++ } ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ sbuf = kmalloc(c->leb_size, GFP_KERNEL); ++#else ++ sbuf = vmalloc(c->leb_size); ++#endif ++ if (!sbuf) ++ return -ENOMEM; ++ ++ dbg_mnt("start replaying the journal"); ++ ++ c->replaying = 1; ++ ++ lnum = c->ltail_lnum = c->lhead_lnum; ++ offs = c->lhead_offs; ++ ++ for (i = 0; i < c->log_lebs; i++, lnum++) { ++ if (lnum >= UBIFS_LOG_LNUM + c->log_lebs) { ++ /* ++ * The log is logically circular, we reached the last ++ * LEB, switch to the first one. ++ */ ++ lnum = UBIFS_LOG_LNUM; ++ offs = 0; ++ } ++ err = replay_log_leb(c, lnum, offs, sbuf); ++ if (err == 1) ++ /* We hit the end of the log */ ++ break; ++ if (err) ++ goto out; ++ offs = 0; ++ } ++ ++ err = replay_buds(c); ++ if (err) ++ goto out; ++ ++ err = apply_replay_tree(c); ++ if (err) ++ goto out; ++ ++ ubifs_assert(c->bud_bytes <= c->max_bud_bytes || c->need_recovery); ++ dbg_mnt("finished, log head LEB %d:%d, max_sqnum %llu, " ++ "highest_inum %lu", c->lhead_lnum, c->lhead_offs, c->max_sqnum, ++ c->highest_inum); ++out: ++ destroy_replay_tree(c); ++ destroy_bud_list(c); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(sbuf); ++#else ++ vfree(sbuf); ++#endif ++ c->replaying = 0; ++ return err; ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/sb.c linux-2.6.24.7/fs/ubifs/sb.c +--- linux-2.6.24.7.old/fs/ubifs/sb.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/sb.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,617 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This file implements UBIFS superblock. The superblock is stored at the first ++ * LEB of the volume and is never changed by UBIFS. Only user-space tools may ++ * change it. The superblock node mostly contains geometry information. ++ */ ++ ++#include "ubifs.h" ++ ++/* ++ * Default journal size in logical eraseblocks as a percent of total ++ * flash size. ++ */ ++#define DEFAULT_JNL_PERCENT 5 ++ ++/* Default maximum journal size in bytes */ ++#define DEFAULT_MAX_JNL (32*1024*1024) ++ ++/* Default indexing tree fanout */ ++#define DEFAULT_FANOUT 8 ++ ++/* Default number of LEBs for orphan information */ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++#define DEFAULT_ORPHAN_LEBS 2 /* 2 is better for testing */ ++#else ++#define DEFAULT_ORPHAN_LEBS 1 ++#endif ++ ++/* Default number of journal heads */ ++#define DEFAULT_JHEADS_CNT 1 ++ ++/* Default positions of different LEBs in the main area */ ++#define DEFAULT_IDX_LEB 0 ++#define DEFAULT_DATA_LEB 1 ++#define DEFAULT_GC_LEB 2 ++ ++/* Default number of LEB numbers in LPT's save table */ ++#define DEFAULT_LSAVE_CNT 256 ++ ++/* Default reserved pool size as a percent of maximum free space */ ++#define DEFAULT_RP_PERCENT 5 ++ ++/* The default maximum size of reserved pool in bytes */ ++#define DEFAULT_MAX_RP_SIZE (5*1024*1024) ++ ++/* Default UBIFS compressor */ ++#define DEFAULT_COMPRESSOR UBIFS_COMPR_LZO ++ ++/* Default time granularity in nanoseconds */ ++#define DEFAULT_TIME_GRAN 1000000000 ++ ++/** ++ * create_default_filesystem - format empty UBI volume. ++ * @c: UBIFS file-system description object ++ * ++ * This function creates default empty file-system. Returns zero in case of ++ * success and a negative error code in case of failure. ++ */ ++static int create_default_filesystem(struct ubifs_info *c) ++{ ++ struct ubifs_sb_node *sup; ++ struct ubifs_mst_node *mst; ++ struct ubifs_idx_node *idx; ++ struct ubifs_branch *br; ++ struct ubifs_ino_node *ino; ++ struct ubifs_cs_node *cs; ++ union ubifs_key key; ++ int err, tmp, jnl_lebs, log_lebs, max_buds, main_lebs, main_first; ++ int lpt_lebs, lpt_first, orph_lebs, big_lpt, ino_waste, sup_flags = 0; ++ uint64_t tmp64, main_bytes; ++ ++ /* Some functions called from here depend on the @c->key_len filed */ ++ c->key_len = UBIFS_SK_LEN; ++ ++ /* ++ * First of all, we have to calculate default file-system geometry - ++ * log size, journal size, etc. ++ */ ++ c->max_leb_cnt = c->leb_cnt; ++ if (c->leb_cnt < 0x7FFFFFFF / DEFAULT_JNL_PERCENT) ++ /* We can first multiply then divide and have no overflow */ ++ jnl_lebs = c->leb_cnt * DEFAULT_JNL_PERCENT / 100; ++ else ++ jnl_lebs = (c->leb_cnt / 100) * DEFAULT_JNL_PERCENT; ++ ++ if (jnl_lebs < UBIFS_MIN_JNL_LEBS) ++ jnl_lebs = UBIFS_MIN_JNL_LEBS; ++ if (jnl_lebs * c->leb_size > DEFAULT_MAX_JNL) ++ jnl_lebs = DEFAULT_MAX_JNL / c->leb_size; ++ ++ /* ++ * The log should be large enough to fit reference nodes for all bud ++ * LEBs. Because buds do not have to start from the beginning of LEBs ++ * (half of the LEB may contain committed data), the log should ++ * generally be larger, make it twice as large. ++ */ ++ tmp = 2 * (c->ref_node_alsz * jnl_lebs) + c->leb_size - 1; ++ log_lebs = tmp / c->leb_size; ++ /* Plus one LEB reserved for commit */ ++ log_lebs += 1; ++ /* And some extra space to allow writes while committing */ ++ log_lebs += 1; ++ ++ max_buds = jnl_lebs - log_lebs; ++ if (max_buds < UBIFS_MIN_BUD_LEBS) ++ max_buds = UBIFS_MIN_BUD_LEBS; ++ ++ /* ++ * Orphan nodes are stored in a separate area. One node can store a lot ++ * of orphan inode numbers, but when new orphan comes we just add a new ++ * orphan node. At some point the nodes are consolidated into one ++ * orphan node. ++ */ ++ orph_lebs = DEFAULT_ORPHAN_LEBS; ++ ++ main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS - log_lebs; ++ main_lebs -= orph_lebs; ++ ++ lpt_first = UBIFS_LOG_LNUM + log_lebs; ++ c->lsave_cnt = DEFAULT_LSAVE_CNT; ++ err = ubifs_create_dflt_lpt(c, &main_lebs, lpt_first, &lpt_lebs, ++ &big_lpt); ++ if (err) ++ return err; ++ ++ dbg_gen("LEB Properties Tree created (LEBs %d-%d)", lpt_first, ++ lpt_first + lpt_lebs - 1); ++ ++ main_first = c->leb_cnt - main_lebs; ++ ++ /* Create default superblock */ ++ tmp = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size); ++ sup = kzalloc(tmp, GFP_KERNEL); ++ if (!sup) ++ return -ENOMEM; ++ ++ tmp64 = (uint64_t)max_buds * c->leb_size; ++ if (big_lpt) ++ sup_flags |= UBIFS_FLG_BIGLPT; ++ ++ sup->ch.node_type = UBIFS_SB_NODE; ++ sup->key_hash = UBIFS_KEY_HASH_R5; ++ sup->flags = cpu_to_le32(sup_flags); ++ sup->min_io_size = cpu_to_le32(c->min_io_size); ++ sup->leb_size = cpu_to_le32(c->leb_size); ++ sup->leb_cnt = cpu_to_le32(c->leb_cnt); ++ sup->max_leb_cnt = cpu_to_le32(c->max_leb_cnt); ++ sup->max_bud_bytes = cpu_to_le64(tmp64); ++ sup->log_lebs = cpu_to_le32(log_lebs); ++ sup->lpt_lebs = cpu_to_le32(lpt_lebs); ++ sup->orph_lebs = cpu_to_le32(orph_lebs); ++ sup->jhead_cnt = cpu_to_le32(DEFAULT_JHEADS_CNT); ++ sup->fanout = cpu_to_le32(DEFAULT_FANOUT); ++ sup->lsave_cnt = cpu_to_le32(c->lsave_cnt); ++ sup->fmt_version = cpu_to_le32(UBIFS_FORMAT_VERSION); ++ sup->default_compr = cpu_to_le16(DEFAULT_COMPRESSOR); ++ sup->time_gran = cpu_to_le32(DEFAULT_TIME_GRAN); ++ ++ main_bytes = (uint64_t)main_lebs * c->leb_size; ++ tmp64 = main_bytes * DEFAULT_RP_PERCENT; ++ do_div(tmp64, 100); ++ if (tmp64 > DEFAULT_MAX_RP_SIZE) ++ tmp64 = DEFAULT_MAX_RP_SIZE; ++ sup->rp_size = cpu_to_le64(tmp64); ++ ++ err = ubifs_write_node(c, sup, UBIFS_SB_NODE_SZ, 0, 0, UBI_LONGTERM); ++ kfree(sup); ++ if (err) ++ return err; ++ ++ dbg_gen("default superblock created at LEB 0:0"); ++ ++ /* Create default master node */ ++ mst = kzalloc(c->mst_node_alsz, GFP_KERNEL); ++ if (!mst) ++ return -ENOMEM; ++ ++ mst->ch.node_type = UBIFS_MST_NODE; ++ mst->log_lnum = cpu_to_le32(UBIFS_LOG_LNUM); ++ mst->highest_inum = cpu_to_le64(UBIFS_FIRST_INO); ++ mst->cmt_no = 0; ++ mst->root_lnum = cpu_to_le32(main_first + DEFAULT_IDX_LEB); ++ mst->root_offs = 0; ++ tmp = ubifs_idx_node_sz(c, 1); ++ mst->root_len = cpu_to_le32(tmp); ++ mst->gc_lnum = cpu_to_le32(main_first + DEFAULT_GC_LEB); ++ mst->ihead_lnum = cpu_to_le32(main_first + DEFAULT_IDX_LEB); ++ mst->ihead_offs = cpu_to_le32(ALIGN(tmp, c->min_io_size)); ++ mst->index_size = cpu_to_le64(ALIGN(tmp, 8)); ++ mst->lpt_lnum = cpu_to_le32(c->lpt_lnum); ++ mst->lpt_offs = cpu_to_le32(c->lpt_offs); ++ mst->nhead_lnum = cpu_to_le32(c->nhead_lnum); ++ mst->nhead_offs = cpu_to_le32(c->nhead_offs); ++ mst->ltab_lnum = cpu_to_le32(c->ltab_lnum); ++ mst->ltab_offs = cpu_to_le32(c->ltab_offs); ++ mst->lsave_lnum = cpu_to_le32(c->lsave_lnum); ++ mst->lsave_offs = cpu_to_le32(c->lsave_offs); ++ mst->lscan_lnum = cpu_to_le32(main_first); ++ mst->empty_lebs = cpu_to_le32(main_lebs - 2); ++ mst->idx_lebs = cpu_to_le32(1); ++ mst->leb_cnt = cpu_to_le32(c->leb_cnt); ++ ++ /* Calculate lprops statistics */ ++ tmp64 = main_bytes; ++ tmp64 -= ALIGN(ubifs_idx_node_sz(c, 1), c->min_io_size); ++ tmp64 -= ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size); ++ mst->total_free = cpu_to_le64(tmp64); ++ ++ tmp64 = ALIGN(ubifs_idx_node_sz(c, 1), c->min_io_size); ++ ino_waste = ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size) - ++ UBIFS_INO_NODE_SZ; ++ tmp64 += ino_waste; ++ tmp64 -= ALIGN(ubifs_idx_node_sz(c, 1), 8); ++ mst->total_dirty = cpu_to_le64(tmp64); ++ ++ /* The indexing LEB does not contribute to dark space */ ++ tmp64 = (c->main_lebs - 1) * c->dark_wm; ++ mst->total_dark = cpu_to_le64(tmp64); ++ ++ mst->total_used = cpu_to_le64(UBIFS_INO_NODE_SZ); ++ ++ err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM, 0, ++ UBI_UNKNOWN); ++ if (err) { ++ kfree(mst); ++ return err; ++ } ++ err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1, 0, ++ UBI_UNKNOWN); ++ kfree(mst); ++ if (err) ++ return err; ++ ++ dbg_gen("default master node created at LEB %d:0", UBIFS_MST_LNUM); ++ ++ /* Create the root indexing node */ ++ tmp = ubifs_idx_node_sz(c, 1); ++ idx = kzalloc(ALIGN(tmp, c->min_io_size), GFP_KERNEL); ++ if (!idx) ++ return -ENOMEM; ++ ++ c->key_fmt = UBIFS_SIMPLE_KEY_FMT; ++ c->key_hash = key_r5_hash; ++ ++ idx->ch.node_type = UBIFS_IDX_NODE; ++ idx->child_cnt = cpu_to_le16(1); ++ ino_key_init(c, &key, UBIFS_ROOT_INO); ++ br = ubifs_idx_branch(c, idx, 0); ++ key_write_idx(c, &key, &br->key); ++ br->lnum = cpu_to_le32(main_first + DEFAULT_DATA_LEB); ++ br->len = cpu_to_le32(UBIFS_INO_NODE_SZ); ++ err = ubifs_write_node(c, idx, tmp, main_first + DEFAULT_IDX_LEB, 0, ++ UBI_UNKNOWN); ++ kfree(idx); ++ if (err) ++ return err; ++ ++ dbg_gen("default root indexing node created LEB %d:0", ++ main_first + DEFAULT_IDX_LEB); ++ ++ /* Create default root inode */ ++ tmp = ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size); ++ ino = kzalloc(tmp, GFP_KERNEL); ++ if (!ino) ++ return -ENOMEM; ++ ++ ino_key_init_flash(c, &ino->key, UBIFS_ROOT_INO); ++ ino->ch.node_type = UBIFS_INO_NODE; ++ ino->creat_sqnum = cpu_to_le64(++c->max_sqnum); ++ ino->nlink = cpu_to_le32(2); ++ tmp = cpu_to_le64(CURRENT_TIME_SEC.tv_sec); ++ ino->atime_sec = tmp; ++ ino->ctime_sec = tmp; ++ ino->mtime_sec = tmp; ++ ino->atime_nsec = 0; ++ ino->ctime_nsec = 0; ++ ino->mtime_nsec = 0; ++ ino->mode = cpu_to_le32(S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO); ++ ino->size = cpu_to_le64(UBIFS_INO_NODE_SZ); ++ ++ /* Set compression enabled by default */ ++ ino->flags = cpu_to_le32(UBIFS_COMPR_FL); ++ ++ err = ubifs_write_node(c, ino, UBIFS_INO_NODE_SZ, ++ main_first + DEFAULT_DATA_LEB, 0, ++ UBI_UNKNOWN); ++ kfree(ino); ++ if (err) ++ return err; ++ ++ dbg_gen("root inode created at LEB %d:0", ++ main_first + DEFAULT_DATA_LEB); ++ ++ /* ++ * The first node in the log has to be the commit start node. This is ++ * always the case during normal file-system operation. Write a fake ++ * commit start node to the log. ++ */ ++ tmp = ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size); ++ cs = kzalloc(tmp, GFP_KERNEL); ++ if (!cs) ++ return -ENOMEM; ++ ++ cs->ch.node_type = UBIFS_CS_NODE; ++ err = ubifs_write_node(c, cs, UBIFS_CS_NODE_SZ, UBIFS_LOG_LNUM, ++ 0, UBI_UNKNOWN); ++ kfree(cs); ++ ++ ubifs_msg("default file-system created"); ++ return 0; ++} ++ ++/** ++ * validate_sb - validate superblock node. ++ * @c: UBIFS file-system description object ++ * @sup: superblock node ++ * ++ * This function validates superblock node @sup. Since most of data was read ++ * from the superblock and stored in @c, the function validates fields in @c ++ * instead. Returns zero in case of success and %-EINVAL in case of validation ++ * failure. ++ */ ++static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup) ++{ ++ long long max_bytes; ++ int err = 1; ++ ++ if (!c->key_hash) { ++ err = 2; ++ goto failed; ++ } ++ ++ if (sup->key_fmt != UBIFS_SIMPLE_KEY_FMT) { ++ err = 3; ++ goto failed; ++ } ++ ++ if (le32_to_cpu(sup->min_io_size) != c->min_io_size) { ++ ubifs_err("min. I/O unit mismatch: %d in superblock, %d real", ++ le32_to_cpu(sup->min_io_size), c->min_io_size); ++ goto failed; ++ } ++ ++ if (le32_to_cpu(sup->leb_size) != c->leb_size) { ++ ubifs_err("LEB size mismatch: %d in superblock, %d real", ++ le32_to_cpu(sup->leb_size), c->leb_size); ++ goto failed; ++ } ++ ++ if (c->leb_cnt < UBIFS_MIN_LEB_CNT || c->leb_cnt > c->vi.size) { ++ ubifs_err("bad LEB count: %d in superblock, %d on UBI volume, " ++ "%d minimum required", c->leb_cnt, c->vi.size, ++ UBIFS_MIN_LEB_CNT); ++ goto failed; ++ } ++ ++ if (c->max_leb_cnt < c->leb_cnt) { ++ ubifs_err("max. LEB count %d less than LEB count %d", ++ c->max_leb_cnt, c->leb_cnt); ++ goto failed; ++ } ++ ++ if (c->log_lebs < UBIFS_MIN_LOG_LEBS || ++ c->lpt_lebs < UBIFS_MIN_LPT_LEBS || ++ c->orph_lebs < UBIFS_MIN_ORPH_LEBS || ++ c->main_lebs < UBIFS_MIN_MAIN_LEBS) { ++ err = 6; ++ goto failed; ++ } ++ ++ if (c->main_lebs < UBIFS_MIN_MAIN_LEBS) { ++ err = 7; ++ goto failed; ++ } ++ ++ if (c->max_bud_bytes < (long long)c->leb_size * UBIFS_MIN_BUD_LEBS || ++ c->max_bud_bytes > (long long)c->leb_size * c->main_lebs) { ++ err = 8; ++ goto failed; ++ } ++ ++ if (c->jhead_cnt < NONDATA_JHEADS_CNT + 1 || ++ c->jhead_cnt > NONDATA_JHEADS_CNT + UBIFS_MAX_JHEADS) { ++ err = 9; ++ goto failed; ++ } ++ ++ if (c->fanout < UBIFS_MIN_FANOUT || ++ ubifs_idx_node_sz(c, c->fanout) > c->leb_size) { ++ err = 10; ++ goto failed; ++ } ++ ++ if (c->lsave_cnt < 0 || (c->lsave_cnt > DEFAULT_LSAVE_CNT && ++ c->lsave_cnt > c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS - ++ c->log_lebs - c->lpt_lebs - c->orph_lebs)) { ++ err = 11; ++ goto failed; ++ } ++ ++ if (UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs + c->lpt_lebs + ++ c->orph_lebs + c->main_lebs != c->leb_cnt) { ++ err = 12; ++ goto failed; ++ } ++ ++ if (c->default_compr < 0 || c->default_compr >= UBIFS_COMPR_TYPES_CNT) { ++ err = 13; ++ goto failed; ++ } ++ ++ max_bytes = c->main_lebs * (long long)c->leb_size; ++ if (c->rp_size < 0 || max_bytes < c->rp_size) { ++ err = 14; ++ goto failed; ++ } ++ ++ if (le32_to_cpu(sup->time_gran) > 1000000000 || ++ le32_to_cpu(sup->time_gran) < 1) { ++ err = 15; ++ goto failed; ++ } ++ ++ return 0; ++ ++failed: ++ ubifs_err("bad superblock, error %d", err); ++ dbg_dump_node(c, sup); ++ return -EINVAL; ++} ++ ++/** ++ * ubifs_read_sb_node - read superblock node. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns a pointer to the superblock node or a negative error ++ * code. ++ */ ++struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c) ++{ ++ struct ubifs_sb_node *sup; ++ int err; ++ ++ sup = kmalloc(ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size), GFP_NOFS); ++ if (!sup) ++ return ERR_PTR(-ENOMEM); ++ ++ err = ubifs_read_node(c, sup, UBIFS_SB_NODE, UBIFS_SB_NODE_SZ, ++ UBIFS_SB_LNUM, 0); ++ if (err) { ++ kfree(sup); ++ return ERR_PTR(err); ++ } ++ ++ return sup; ++} ++ ++/** ++ * ubifs_write_sb_node - write superblock node. ++ * @c: UBIFS file-system description object ++ * @sup: superblock node read with 'ubifs_read_sb_node()' ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup) ++{ ++ int len = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size); ++ ++ ubifs_prepare_node(c, sup, UBIFS_SB_NODE_SZ, 1); ++ return ubi_leb_change(c->ubi, UBIFS_SB_LNUM, sup, len, UBI_LONGTERM); ++} ++ ++/** ++ * ubifs_read_superblock - read superblock. ++ * @c: UBIFS file-system description object ++ * ++ * This function finds, reads and checks the superblock. If an empty UBI volume ++ * is being mounted, this function creates default superblock. Returns zero in ++ * case of success, and a negative error code in case of failure. ++ */ ++int ubifs_read_superblock(struct ubifs_info *c) ++{ ++ int err, sup_flags; ++ struct ubifs_sb_node *sup; ++ ++ if (c->empty) { ++ err = create_default_filesystem(c); ++ if (err) ++ return err; ++ } ++ ++ sup = ubifs_read_sb_node(c); ++ if (IS_ERR(sup)) ++ return PTR_ERR(sup); ++ ++ /* ++ * The software supports all previous versions but not future versions, ++ * due to the unavailability of time-travelling equipment. ++ */ ++ c->fmt_version = le32_to_cpu(sup->fmt_version); ++ if (c->fmt_version > UBIFS_FORMAT_VERSION) { ++ ubifs_err("on-flash format version is %d, but software only " ++ "supports up to version %d", c->fmt_version, ++ UBIFS_FORMAT_VERSION); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (c->fmt_version == 1) { ++ ubifs_err("on-flash format version %d is not supported", ++ c->fmt_version); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ switch (sup->key_hash) { ++ case UBIFS_KEY_HASH_R5: ++ /* TODO: this should die soon */ ++ if (c->fmt_version == 2) ++ c->key_hash = tmp_key_r5_hash; ++ else ++ c->key_hash = key_r5_hash; ++ c->key_hash_type = UBIFS_KEY_HASH_R5; ++ break; ++ ++ case UBIFS_KEY_HASH_TEST: ++ c->key_hash = key_test_hash; ++ c->key_hash_type = UBIFS_KEY_HASH_TEST; ++ break; ++ }; ++ ++ c->key_fmt = sup->key_fmt; ++ ++ switch (c->key_fmt) { ++ case UBIFS_SIMPLE_KEY_FMT: ++ c->key_len = UBIFS_SK_LEN; ++ break; ++ default: ++ ubifs_err("unsupported key format"); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ c->leb_cnt = le32_to_cpu(sup->leb_cnt); ++ c->max_leb_cnt = le32_to_cpu(sup->max_leb_cnt); ++ c->max_bud_bytes = le64_to_cpu(sup->max_bud_bytes); ++ c->log_lebs = le32_to_cpu(sup->log_lebs); ++ c->lpt_lebs = le32_to_cpu(sup->lpt_lebs); ++ c->orph_lebs = le32_to_cpu(sup->orph_lebs); ++ c->jhead_cnt = le32_to_cpu(sup->jhead_cnt) + NONDATA_JHEADS_CNT; ++ c->fanout = le32_to_cpu(sup->fanout); ++ c->lsave_cnt = le32_to_cpu(sup->lsave_cnt); ++ c->default_compr = le16_to_cpu(sup->default_compr); ++ c->rp_size = le64_to_cpu(sup->rp_size); ++ c->rp_uid = le32_to_cpu(sup->rp_uid); ++ c->rp_gid = le32_to_cpu(sup->rp_gid); ++ sup_flags = le32_to_cpu(sup->flags); ++ ++ c->vfs_sb->s_time_gran = le32_to_cpu(sup->time_gran); ++ ++ c->big_lpt = !!(sup_flags & UBIFS_FLG_BIGLPT); ++ ++ /* Automatically increase file system size to the maximum size */ ++ c->old_leb_cnt = c->leb_cnt; ++ if (c->leb_cnt < c->vi.size && c->leb_cnt < c->max_leb_cnt) { ++ c->leb_cnt = min_t(int, c->max_leb_cnt, c->vi.size); ++ if (c->vfs_sb->s_flags & MS_RDONLY) ++ dbg_mnt("Auto resizing (ro) from %d LEBs to %d LEBs", ++ c->old_leb_cnt, c->leb_cnt); ++ else { ++ dbg_mnt("Auto resizing (sb) from %d LEBs to %d LEBs", ++ c->old_leb_cnt, c->leb_cnt); ++ sup->leb_cnt = cpu_to_le32(c->leb_cnt); ++ err = ubifs_write_sb_node(c, sup); ++ if (err) ++ goto out; ++ c->old_leb_cnt = c->leb_cnt; ++ } ++ } ++ ++ c->log_bytes = (long long)c->log_lebs * c->leb_size; ++ c->log_last = UBIFS_LOG_LNUM + c->log_lebs - 1; ++ c->lpt_first = UBIFS_LOG_LNUM + c->log_lebs; ++ c->lpt_last = c->lpt_first + c->lpt_lebs - 1; ++ c->orph_first = c->lpt_last + 1; ++ c->orph_last = c->orph_first + c->orph_lebs - 1; ++ c->main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; ++ c->main_lebs -= c->log_lebs + c->lpt_lebs + c->orph_lebs; ++ c->main_first = c->leb_cnt - c->main_lebs; ++ c->report_rp_size = ubifs_reported_space(c, c->rp_size); ++ ++ err = validate_sb(c, sup); ++out: ++ kfree(sup); ++ return err; ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/scan.c linux-2.6.24.7/fs/ubifs/scan.c +--- linux-2.6.24.7.old/fs/ubifs/scan.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/scan.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,362 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy (Битюцкий Ðртём) ++ */ ++ ++/* ++ * This file implements the scan which is a general-purpose function for ++ * determining what nodes are in an eraseblock. The scan is used to replay the ++ * journal, to do garbage collection. for the TNC in-the-gaps method, and by ++ * debugging functions. ++ */ ++ ++#include "ubifs.h" ++ ++/** ++ * scan_padding_bytes - scan for padding bytes. ++ * @buf: buffer to scan ++ * @len: length of buffer ++ * ++ * This function returns the number of padding bytes on success and ++ * %SCANNED_GARBAGE on failure. ++ */ ++static int scan_padding_bytes(void *buf, int len) ++{ ++ int pad_len = 0, max_pad_len = min_t(int, UBIFS_PAD_NODE_SZ, len); ++ uint8_t *p = buf; ++ ++ dbg_scan("not a node"); ++ ++ while (pad_len < max_pad_len && *p++ == UBIFS_PADDING_BYTE) ++ pad_len += 1; ++ ++ if (!pad_len || (pad_len & 7)) ++ return SCANNED_GARBAGE; ++ ++ dbg_scan("%d padding bytes", pad_len); ++ ++ return pad_len; ++} ++ ++/** ++ * ubifs_scan_a_node - scan for a node or padding. ++ * @c: UBIFS file-system description object ++ * @buf: buffer to scan ++ * @len: length of buffer ++ * @lnum: logical eraseblock number ++ * @offs: offset within the logical eraseblock ++ * @quiet: print no messages ++ * ++ * This function returns a scanning code to indicate what was scanned. ++ */ ++int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum, ++ int offs, int quiet) ++{ ++ struct ubifs_ch *ch = buf; ++ uint32_t magic; ++ ++ magic = le32_to_cpu(ch->magic); ++ ++ if (magic == 0xFFFFFFFF) { ++ dbg_scan("hit empty space"); ++ return SCANNED_EMPTY_SPACE; ++ } ++ ++ if (magic != UBIFS_NODE_MAGIC) ++ return scan_padding_bytes(buf, len); ++ ++ if (len < UBIFS_CH_SZ) ++ return SCANNED_GARBAGE; ++ ++ dbg_scan("scanning %s", dbg_ntype(ch->node_type)); ++ ++ if (ubifs_check_node(c, buf, lnum, offs, quiet)) ++ return SCANNED_A_CORRUPT_NODE; ++ ++ if (ch->node_type == UBIFS_PAD_NODE) { ++ struct ubifs_pad_node *pad = buf; ++ int pad_len = le32_to_cpu(pad->pad_len); ++ int node_len = le32_to_cpu(ch->len); ++ ++ /* Validate the padding node */ ++ if (pad_len < 0 || ++ offs + node_len + pad_len > c->leb_size) { ++ if (!quiet) { ++ ubifs_err("bad pad node at LEB %d:%d", ++ lnum, offs); ++ dbg_dump_node(c, pad); ++ } ++ return SCANNED_A_BAD_PAD_NODE; ++ } ++ ++ /* Make the node pads to 8-byte boundary */ ++ if ((node_len + pad_len) & 7) { ++ if (!quiet) { ++ dbg_err("bad padding length %d - %d", ++ offs, offs + node_len + pad_len); ++ } ++ return SCANNED_A_BAD_PAD_NODE; ++ } ++ ++ dbg_scan("%d bytes padded, offset now %d", ++ pad_len, ALIGN(offs + node_len + pad_len, 8)); ++ ++ return node_len + pad_len; ++ } ++ ++ return SCANNED_A_NODE; ++} ++ ++/** ++ * ubifs_start_scan - create LEB scanning information at start of scan. ++ * @c: UBIFS file-system description object ++ * @lnum: logical eraseblock number ++ * @offs: offset to start at (usually zero) ++ * @sbuf: scan buffer (must be c->leb_size) ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++struct ubifs_scan_leb *ubifs_start_scan(const struct ubifs_info *c, int lnum, ++ int offs, void *sbuf) ++{ ++ struct ubifs_scan_leb *sleb; ++ int err; ++ ++ dbg_scan("scan LEB %d:%d", lnum, offs); ++ ++ sleb = kzalloc(sizeof(struct ubifs_scan_leb), GFP_NOFS); ++ if (!sleb) ++ return ERR_PTR(-ENOMEM); ++ ++ sleb->lnum = lnum; ++ INIT_LIST_HEAD(&sleb->nodes); ++ sleb->buf = sbuf; ++ ++ err = ubi_read(c->ubi, lnum, sbuf + offs, offs, c->leb_size - offs); ++ if (err && err != -EBADMSG) { ++ ubifs_err("cannot read %d bytes from LEB %d:%d," ++ " error %d", c->leb_size - offs, lnum, offs, err); ++ kfree(sleb); ++ return ERR_PTR(err); ++ } ++ ++ if (err == -EBADMSG) ++ sleb->ecc = 1; ++ ++ return sleb; ++} ++ ++/** ++ * ubifs_end_scan - update LEB scanning information at end of scan. ++ * @c: UBIFS file-system description object ++ * @sleb: scanning information ++ * @lnum: logical eraseblock number ++ * @offs: offset to start at (usually zero) ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++void ubifs_end_scan(const struct ubifs_info *c, struct ubifs_scan_leb *sleb, ++ int lnum, int offs) ++{ ++ lnum = lnum; ++ dbg_scan("stop scanning LEB %d at offset %d", lnum, offs); ++ ubifs_assert(offs % c->min_io_size == 0); ++ ++ sleb->endpt = ALIGN(offs, c->min_io_size); ++} ++ ++/** ++ * ubifs_add_snod - add a scanned node to LEB scanning information. ++ * @c: UBIFS file-system description object ++ * @sleb: scanning information ++ * @buf: buffer containing node ++ * @offs: offset of node on flash ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb, ++ void *buf, int offs) ++{ ++ struct ubifs_ch *ch = buf; ++ struct ubifs_ino_node *ino = buf; ++ struct ubifs_scan_node *snod; ++ ++ snod = kzalloc(sizeof(struct ubifs_scan_node), GFP_NOFS); ++ if (!snod) ++ return -ENOMEM; ++ ++ snod->sqnum = le64_to_cpu(ch->sqnum); ++ snod->type = ch->node_type; ++ snod->offs = offs; ++ snod->len = le32_to_cpu(ch->len); ++ snod->node = buf; ++ ++ switch (ch->node_type) { ++ case UBIFS_INO_NODE: ++ case UBIFS_DENT_NODE: ++ case UBIFS_XENT_NODE: ++ case UBIFS_DATA_NODE: ++ case UBIFS_TRUN_NODE: ++ /* ++ * The key is in the same place in all keyed ++ * nodes. ++ */ ++ key_read(c, &ino->key, &snod->key); ++ break; ++ } ++ list_add_tail(&snod->list, &sleb->nodes); ++ sleb->nodes_cnt += 1; ++ return 0; ++} ++ ++/** ++ * ubifs_scanned_corruption - print information after UBIFS scanned corruption. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number of corruption ++ * @offs: offset of corruption ++ * @buf: buffer containing corruption ++ */ ++void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs, ++ void *buf) ++{ ++ int len; ++ ++ ubifs_err("corrupted data at LEB %d:%d", lnum, offs); ++ if (dbg_failure_mode) ++ return; ++ len = c->leb_size - offs; ++ if (len > 4096) ++ len = 4096; ++ dbg_err("first %d bytes from LEB %d:%d", len, lnum, offs); ++ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 4, buf, len, 1); ++} ++ ++/** ++ * ubifs_scan - scan a logical eraseblock. ++ * @c: UBIFS file-system description object ++ * @lnum: logical eraseblock number ++ * @offs: offset to start at (usually zero) ++ * @sbuf: scan buffer (must be c->leb_size) ++ * ++ * This function scans LEB number @lnum and returns complete information about ++ * its contents. Returns an error code in case of failure. ++ */ ++struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum, ++ int offs, void *sbuf) ++{ ++ void *buf = sbuf + offs; ++ int err, len = c->leb_size - offs; ++ struct ubifs_scan_leb *sleb; ++ ++ sleb = ubifs_start_scan(c, lnum, offs, sbuf); ++ if (IS_ERR(sleb)) ++ return sleb; ++ ++ while (len >= 8) { ++ struct ubifs_ch *ch = buf; ++ int node_len, ret; ++ ++ dbg_scan("look at LEB %d:%d (%d bytes left)", ++ lnum, offs, len); ++ ++ cond_resched(); ++ ++ ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 0); ++ ++ if (ret > 0) { ++ /* Padding bytes or a valid padding node */ ++ offs += ret; ++ buf += ret; ++ len -= ret; ++ continue; ++ } ++ ++ if (ret == SCANNED_EMPTY_SPACE) ++ /* Empty space is checked later */ ++ break; ++ ++ switch (ret) { ++ case SCANNED_GARBAGE: ++ dbg_err("garbage"); ++ goto corrupted; ++ case SCANNED_A_NODE: ++ break; ++ case SCANNED_A_CORRUPT_NODE: ++ case SCANNED_A_BAD_PAD_NODE: ++ dbg_err("bad node"); ++ goto corrupted; ++ default: ++ dbg_err("unknown"); ++ goto corrupted; ++ } ++ ++ err = ubifs_add_snod(c, sleb, buf, offs); ++ if (err) ++ goto error; ++ ++ node_len = ALIGN(le32_to_cpu(ch->len), 8); ++ offs += node_len; ++ buf += node_len; ++ len -= node_len; ++ } ++ ++ if (offs % c->min_io_size) ++ goto corrupted; ++ ++ ubifs_end_scan(c, sleb, lnum, offs); ++ ++ for (; len > 4; offs += 4, buf = buf + 4, len -= 4) ++ if (*(uint32_t *)buf != 0xffffffff) ++ break; ++ for (; len; offs++, buf++, len--) ++ if (*(uint8_t *)buf != 0xff) { ++ ubifs_err("corrupt empty space at LEB %d:%d", ++ lnum, offs); ++ goto corrupted; ++ } ++ ++ return sleb; ++ ++corrupted: ++ ubifs_scanned_corruption(c, lnum, offs, buf); ++ err = -EUCLEAN; ++error: ++ ubifs_err("LEB %d scanning failed", lnum); ++ ubifs_scan_destroy(sleb); ++ return ERR_PTR(err); ++} ++ ++/** ++ * ubifs_scan_destroy - destroy LEB scanning information. ++ * @sleb: scanning information to free ++ */ ++void ubifs_scan_destroy(struct ubifs_scan_leb *sleb) ++{ ++ struct ubifs_scan_node *node; ++ struct list_head *head; ++ ++ head = &sleb->nodes; ++ while (!list_empty(head)) { ++ node = list_entry(head->next, struct ubifs_scan_node, list); ++ list_del(&node->list); ++ kfree(node); ++ } ++ kfree(sleb); ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/shrinker.c linux-2.6.24.7/fs/ubifs/shrinker.c +--- linux-2.6.24.7.old/fs/ubifs/shrinker.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/shrinker.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,322 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This file implements UBIFS shrinker which evicts clean znodes from the TNC ++ * tree when Linux VM needs more RAM. ++ * ++ * We do not implement any LRU lists to find oldest znodes to free because it ++ * would add additional overhead to the file system fast paths. So the shrinker ++ * just walks the TNC tree when searching for znodes to free. ++ * ++ * If the root of a TNC sub-tree is clean and old enough, then the children are ++ * also clean and old enough. So the shrinker walks the TNC in level order and ++ * dumps entire sub-trees. ++ * ++ * The age of znodes is just the time-stamp when they were last looked at. ++ * The current shrinker first tries to evict old znodes, then young ones. ++ * ++ * Since the shrinker is global, it has to protect against races with FS ++ * un-mounts, which is done by the 'ubifs_infos_lock' and 'c->umount_mutex'. ++ */ ++ ++#include "ubifs.h" ++ ++/* List of all UBIFS file-system instances */ ++LIST_HEAD(ubifs_infos); ++ ++/* ++ * We number each shrinker run and record the number on the ubifs_info structure ++ * so that we can easily work out which ubifs_info structures have already been ++ * done by the current run. ++ */ ++static unsigned int shrinker_run_no; ++ ++/* Protects 'ubifs_infos' list */ ++DEFINE_SPINLOCK(ubifs_infos_lock); ++ ++/* Global clean znode counter (for all mounted UBIFS instances) */ ++atomic_long_t ubifs_clean_zn_cnt; ++ ++/** ++ * shrink_tnc - shrink TNC tree. ++ * @c: UBIFS file-system description object ++ * @nr: number of znodes to free ++ * @age: the age of znodes to free ++ * @contention: if any contention, this is set to %1 ++ * ++ * This function traverses TNC tree and frees clean znodes. It does not free ++ * clean znodes which younger then @age. Returns number of freed znodes. ++ */ ++static int shrink_tnc(struct ubifs_info *c, int nr, int age, int *contention) ++{ ++ int total_freed = 0; ++ struct ubifs_znode *znode, *zprev; ++ int time = get_seconds(); ++ ++ ubifs_assert(mutex_is_locked(&c->umount_mutex)); ++ ubifs_assert(mutex_is_locked(&c->tnc_mutex)); ++ ++ if (!c->zroot.znode || atomic_long_read(&c->clean_zn_cnt) == 0) ++ return 0; ++ ++ /* ++ * Traverse the TNC tree in levelorder manner, so that it is possible ++ * to destroy large sub-trees. Indeed, if a znode is old, then all its ++ * children are older or of the same age. ++ * ++ * Note, we are holding 'c->tnc_mutex', so we do not have to lock the ++ * 'c->space_lock' when _reading_ 'c->clean_zn_cnt', because it is ++ * changed only when the 'c->tnc_mutex' is held. ++ */ ++ zprev = NULL; ++ znode = ubifs_tnc_levelorder_next(c->zroot.znode, NULL); ++ while (znode && total_freed < nr && ++ atomic_long_read(&c->clean_zn_cnt) > 0) { ++ int freed; ++ ++ /* ++ * If the znode is clean, but it is in the 'c->cnext' list, this ++ * means that this znode has just been written to flash as a ++ * part of commit and was marked clean. They will be removed ++ * from the list at end commit. We cannot change the list, ++ * because it is not protected by any mutex (design decision to ++ * make commit really independent and parallel to main I/O). So ++ * we just skip these znodes. ++ * ++ * Note, the 'clean_zn_cnt' counters are not updated until ++ * after the commit, so the UBIFS shrinker does not report ++ * the znodes which are in the 'c->cnext' list as freeable. ++ * ++ * Also note, if the root of a sub-tree is not in 'c->cnext', ++ * then the whole sub-tree is not in 'c->cnext' as well, so it ++ * is safe to dump whole sub-tree. ++ */ ++ ++ if (znode->cnext) { ++ /* ++ * Very soon these znodes will be removed from the list ++ * and become freeable. ++ */ ++ *contention = 1; ++ } else if (!ubifs_zn_dirty(znode) && ++ abs(time - znode->time) >= age) { ++ if (znode->parent) ++ znode->parent->zbranch[znode->iip].znode = NULL; ++ else ++ c->zroot.znode = NULL; ++ ++ freed = ubifs_destroy_tnc_subtree(znode); ++ atomic_long_sub(freed, &ubifs_clean_zn_cnt); ++ atomic_long_sub(freed, &c->clean_zn_cnt); ++ ubifs_assert(atomic_long_read(&c->clean_zn_cnt) >= 0); ++ total_freed += freed; ++ znode = zprev; ++ } ++ ++ if (unlikely(!c->zroot.znode)) ++ break; ++ ++ zprev = znode; ++ znode = ubifs_tnc_levelorder_next(c->zroot.znode, znode); ++ cond_resched(); ++ } ++ ++ return total_freed; ++} ++ ++/** ++ * shrink_tnc_trees - shrink UBIFS TNC trees. ++ * @nr: number of znodes to free ++ * @age: the age of znodes to free ++ * @contention: if any contention, this is set to %1 ++ * ++ * This function walks the list of mounted UBIFS file-systems and frees clean ++ * znodes which are older then @age, until at least @nr znodes are freed. ++ * Returns the number of freed znodes. ++ */ ++static int shrink_tnc_trees(int nr, int age, int *contention) ++{ ++ struct ubifs_info *c; ++ struct list_head *p; ++ unsigned int run_no; ++ int freed = 0; ++ ++ spin_lock(&ubifs_infos_lock); ++ do ++ run_no = ++shrinker_run_no; ++ while (run_no == 0); ++ /* Iterate over all mounted UBIFS file-systems and try to shrink them */ ++ p = ubifs_infos.next; ++ while (p != &ubifs_infos) { ++ c = list_entry(p, struct ubifs_info, infos_list); ++ /* ++ * We move the ones we do to the end of the list, so we stop ++ * when we see one we have already done. ++ */ ++ if (c->shrinker_run_no == run_no) ++ break; ++ if (!mutex_trylock(&c->umount_mutex)) { ++ /* Some un-mount is in progress, try next FS */ ++ *contention = 1; ++ p = p->next; ++ continue; ++ } ++ /* ++ * We're holding 'c->umount_mutex', so the file-system won't go ++ * away. ++ */ ++ if (!mutex_trylock(&c->tnc_mutex)) { ++ mutex_unlock(&c->umount_mutex); ++ *contention = 1; ++ p = p->next; ++ continue; ++ } ++ spin_unlock(&ubifs_infos_lock); ++ /* ++ * OK, now we have TNC locked, the file-system cannot go away - ++ * it is safe to reap the cache. ++ */ ++ c->shrinker_run_no = run_no; ++ freed += shrink_tnc(c, nr, age, contention); ++ mutex_unlock(&c->tnc_mutex); ++ spin_lock(&ubifs_infos_lock); ++ /* Get the next list element before we move this one */ ++ p = p->next; ++ /* ++ * Move this one to the end of the list to provide some ++ * fairness. ++ */ ++ list_del(&c->infos_list); ++ list_add_tail(&c->infos_list, &ubifs_infos); ++ mutex_unlock(&c->umount_mutex); ++ if (freed >= nr) ++ break; ++ } ++ spin_unlock(&ubifs_infos_lock); ++ return freed; ++} ++ ++/** ++ * kick_a_thread - kick a background thread to start commit. ++ * ++ * This function kicks a background thread to start background commit. Returns ++ * %-1 if a thread was kicked or there is another reason to assume the memory ++ * will soon be freed or become freeable. If there are no dirty znodes, returns ++ * %0. ++ */ ++static int kick_a_thread(void) ++{ ++ int i; ++ struct ubifs_info *c; ++ ++ /* ++ * Iterate over all mounted UBIFS file-systems and find out if there is ++ * already an ongoing commit operation there. If no, then iterate for ++ * the second time and initiate background commit. ++ */ ++ spin_lock(&ubifs_infos_lock); ++ for (i = 0; i < 2; i++) { ++ list_for_each_entry(c, &ubifs_infos, infos_list) { ++ long dirty_zn_cnt; ++ ++ if (!mutex_trylock(&c->umount_mutex)) { ++ /* ++ * Some un-mount is in progress, it will ++ * certainly free memory, so just return. ++ */ ++ spin_unlock(&ubifs_infos_lock); ++ return -1; ++ } ++ ++ dirty_zn_cnt = atomic_long_read(&c->dirty_zn_cnt); ++ ++ if (!dirty_zn_cnt || c->cmt_state == COMMIT_BROKEN || ++ c->ro_media) { ++ mutex_unlock(&c->umount_mutex); ++ continue; ++ } ++ ++ if (c->cmt_state != COMMIT_RESTING) { ++ spin_unlock(&ubifs_infos_lock); ++ mutex_unlock(&c->umount_mutex); ++ return -1; ++ } ++ ++ if (i == 1) { ++ list_del(&c->infos_list); ++ list_add_tail(&c->infos_list, &ubifs_infos); ++ spin_unlock(&ubifs_infos_lock); ++ ++ ubifs_request_bg_commit(c); ++ mutex_unlock(&c->umount_mutex); ++ return -1; ++ } ++ mutex_unlock(&c->umount_mutex); ++ } ++ } ++ spin_unlock(&ubifs_infos_lock); ++ ++ return 0; ++} ++ ++int ubifs_shrinker(int nr, gfp_t gfp_mask) ++{ ++ int freed, contention = 0; ++ long clean_zn_cnt = atomic_long_read(&ubifs_clean_zn_cnt); ++ ++ if (nr == 0) ++ return clean_zn_cnt; ++ ++ if (!clean_zn_cnt) { ++ /* ++ * No clean znodes, nothing to reap. All we can do in this case ++ * is to kick background threads to start commit, which will ++ * probably make clean znodes which, in turn, will be freeable. ++ * And we return -1 which means will make VM call us again ++ * later. ++ */ ++ dbg_tnc("no clean znodes, kick a thread"); ++ return kick_a_thread(); ++ } ++ ++ freed = shrink_tnc_trees(nr, OLD_ZNODE_AGE, &contention); ++ if (freed >= nr) ++ goto out; ++ ++ dbg_tnc("not enough old znodes, try to free young ones"); ++ freed += shrink_tnc_trees(nr - freed, YOUNG_ZNODE_AGE, &contention); ++ if (freed >= nr) ++ goto out; ++ ++ dbg_tnc("not enough young znodes, free all"); ++ freed += shrink_tnc_trees(nr - freed, 0, &contention); ++ ++ if (!freed && contention) { ++ dbg_tnc("freed nothing, but contention"); ++ return -1; ++ } ++ ++out: ++ dbg_tnc("%d znodes were freed, requested %d", freed, nr); ++ return freed; ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/super.c linux-2.6.24.7/fs/ubifs/super.c +--- linux-2.6.24.7.old/fs/ubifs/super.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/super.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,2016 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This file implements UBIFS initialization and VFS superblock operations. Some ++ * initialization stuff which is rather large and complex is placed at ++ * corresponding subsystems, but most of it is here. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ubifs.h" ++ ++/* Slab cache for UBIFS inodes */ ++struct kmem_cache *ubifs_inode_slab; ++ ++#ifndef UBIFS_COMPAT_NO_SHRINKER ++/* UBIFS TNC shrinker description */ ++static struct shrinker ubifs_shrinker_info = { ++ .shrink = ubifs_shrinker, ++ .seeks = DEFAULT_SEEKS, ++}; ++#endif ++ ++/** ++ * validate_inode - validate inode. ++ * @c: UBIFS file-system description object ++ * @inode: the inode to validate ++ * ++ * This is a helper function for 'ubifs_iget()' which validates various fields ++ * of a newly built inode to make sure they contain sane values and prevent ++ * possible vulnerabilities. Returns zero if the inode is all right and ++ * a non-zero error code if not. ++ */ ++#ifndef UBIFS_COMPAT_USE_OLD_IGET ++static int validate_inode(struct ubifs_info *c, const struct inode *inode) ++#else ++int validate_inode(struct ubifs_info *c, const struct inode *inode) ++#endif ++{ ++ int err; ++ const struct ubifs_inode *ui = ubifs_inode(inode); ++ ++ if (inode->i_size > c->max_inode_sz) { ++ ubifs_err("inode is too large (%lld)", ++ (long long)inode->i_size); ++ return 1; ++ } ++ ++ if (ui->compr_type < 0 || ui->compr_type >= UBIFS_COMPR_TYPES_CNT) { ++ ubifs_err("unknown compression type %d", ui->compr_type); ++ return 2; ++ } ++ ++ if (ui->xattr_cnt < 0) ++ return 3; ++ ++ if (ui->xattr_size < 0) ++ return 4; ++ ++ if (ui->xattr_names < 0 || ++ ui->xattr_names + ui->xattr_cnt > XATTR_LIST_MAX) ++ return 5; ++ ++ if (ui->data_len < 0 || ui->data_len > UBIFS_MAX_INO_DATA) ++ return 6; ++ ++ if (!ubifs_compr_present(ui->compr_type)) { ++ ubifs_warn("inode %lu uses '%s' compression, but it was not " ++ "compiled in", inode->i_ino, ++ ubifs_compr_name(ui->compr_type)); ++ } ++ ++ err = dbg_check_dir_size(c, inode); ++ return err; ++} ++ ++#ifndef UBIFS_COMPAT_USE_OLD_IGET ++struct inode *ubifs_iget(struct super_block *sb, unsigned long inum) ++{ ++ int err; ++ union ubifs_key key; ++ struct ubifs_ino_node *ino; ++ struct ubifs_info *c = sb->s_fs_info; ++ struct inode *inode; ++ struct ubifs_inode *ui; ++ ++ dbg_gen("inode %lu", inum); ++ ++ inode = iget_locked(sb, inum); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ if (!(inode->i_state & I_NEW)) ++ return inode; ++ ui = ubifs_inode(inode); ++ ++ ino = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS); ++ if (!ino) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ ino_key_init(c, &key, inode->i_ino); ++ ++ err = ubifs_tnc_lookup(c, &key, ino); ++ if (err) ++ goto out_ino; ++ ++ inode->i_flags |= (S_NOCMTIME | S_NOATIME); ++ inode->i_nlink = le32_to_cpu(ino->nlink); ++ inode->i_uid = le32_to_cpu(ino->uid); ++ inode->i_gid = le32_to_cpu(ino->gid); ++ inode->i_atime.tv_sec = (int64_t)le64_to_cpu(ino->atime_sec); ++ inode->i_atime.tv_nsec = le32_to_cpu(ino->atime_nsec); ++ inode->i_mtime.tv_sec = (int64_t)le64_to_cpu(ino->mtime_sec); ++ inode->i_mtime.tv_nsec = le32_to_cpu(ino->mtime_nsec); ++ inode->i_ctime.tv_sec = (int64_t)le64_to_cpu(ino->ctime_sec); ++ inode->i_ctime.tv_nsec = le32_to_cpu(ino->ctime_nsec); ++ inode->i_mode = le32_to_cpu(ino->mode); ++ inode->i_size = le64_to_cpu(ino->size); ++ ++ ui->data_len = le32_to_cpu(ino->data_len); ++ ui->flags = le32_to_cpu(ino->flags); ++ ui->compr_type = le16_to_cpu(ino->compr_type); ++ ui->creat_sqnum = le64_to_cpu(ino->creat_sqnum); ++ ui->xattr_cnt = le32_to_cpu(ino->xattr_cnt); ++ ui->xattr_size = le64_to_cpu(ino->xattr_size); ++ ui->xattr_names = le32_to_cpu(ino->xattr_names); ++ ++ err = validate_inode(c, inode); ++ if (err) ++ goto out_invalid; ++ ++ /* Disable readahead */ ++ inode->i_mapping->backing_dev_info = &ubifs_backing_dev_info; ++ ++ switch (inode->i_mode & S_IFMT) { ++ case S_IFREG: ++ inode->i_mapping->a_ops = &ubifs_file_address_operations; ++ inode->i_op = &ubifs_file_inode_operations; ++ inode->i_fop = &ubifs_file_operations; ++ if (ui->data_len != 0) { ++ err = 10; ++ goto out_invalid; ++ } ++ break; ++ case S_IFDIR: ++ inode->i_op = &ubifs_dir_inode_operations; ++ inode->i_fop = &ubifs_dir_operations; ++ if (ui->data_len != 0) { ++ err = 11; ++ goto out_invalid; ++ } ++ break; ++ case S_IFLNK: ++ inode->i_op = &ubifs_symlink_inode_operations; ++ if (ui->data_len <= 0 || ui->data_len > UBIFS_MAX_INO_DATA) { ++ err = 12; ++ goto out_invalid; ++ } ++ ui->data = kmalloc(ui->data_len + 1, GFP_NOFS); ++ if (!ui->data) { ++ err = -ENOMEM; ++ goto out_ino; ++ } ++ memcpy(ui->data, ino->data, ui->data_len); ++ ((char *)ui->data)[ui->data_len] = '\0'; ++ break; ++ case S_IFBLK: ++ case S_IFCHR: ++ { ++ dev_t rdev; ++ union ubifs_dev_desc *dev; ++ ++ ui->data = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS); ++ if (!ui->data) { ++ err = -ENOMEM; ++ goto out_ino; ++ } ++ ++ dev = (union ubifs_dev_desc *)ino->data; ++ if (ui->data_len == sizeof(dev->new)) ++ rdev = new_decode_dev(le32_to_cpu(dev->new)); ++ else if (ui->data_len == sizeof(dev->huge)) ++ rdev = huge_decode_dev(le64_to_cpu(dev->huge)); ++ else { ++ err = 13; ++ goto out_invalid; ++ } ++ memcpy(ui->data, ino->data, ui->data_len); ++ inode->i_op = &ubifs_file_inode_operations; ++ init_special_inode(inode, inode->i_mode, rdev); ++ break; ++ } ++ case S_IFSOCK: ++ case S_IFIFO: ++ inode->i_op = &ubifs_file_inode_operations; ++ init_special_inode(inode, inode->i_mode, 0); ++ if (ui->data_len != 0) { ++ err = 14; ++ goto out_invalid; ++ } ++ break; ++ default: ++ err = 15; ++ goto out_invalid; ++ } ++ ++ kfree(ino); ++ ubifs_set_inode_flags(inode); ++ unlock_new_inode(inode); ++ return inode; ++ ++out_invalid: ++ ubifs_err("inode %lu validation failed, error %d", inode->i_ino, err); ++ dbg_dump_node(c, ino); ++ dbg_dump_inode(c, inode); ++ err = -EINVAL; ++out_ino: ++ kfree(ino); ++out: ++ ubifs_err("failed to read inode %lu, error %d", inode->i_ino, err); ++ iget_failed(inode); ++ return ERR_PTR(err); ++} ++ ++#endif /* UBIFS_COMPAT_USE_OLD_IGET */ ++ ++static struct inode *ubifs_alloc_inode(struct super_block *sb) ++{ ++ struct ubifs_inode *ui; ++ ++ ui = kmem_cache_alloc(ubifs_inode_slab, GFP_NOFS); ++ if (!ui) ++ return NULL; ++ ++ memset((void *)ui + sizeof(struct inode), 0, ++ sizeof(struct ubifs_inode) - sizeof(struct inode)); ++ mutex_init(&ui->budg_mutex); ++ return &ui->vfs_inode; ++}; ++ ++static void ubifs_destroy_inode(struct inode *inode) ++{ ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ ++ kfree(ui->data); ++ kmem_cache_free(ubifs_inode_slab, inode); ++} ++ ++/* ++ * Note, Linux write-back code calls this without 'i_mutex'. ++ */ ++static int ubifs_write_inode(struct inode *inode, int wait) ++{ ++ int err; ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ struct ubifs_budget_req req = {.dd_growth = c->inode_budget, ++ .dirtied_ino_d = ui->data_len}; ++ ++ ubifs_assert(!ui->xattr); ++ if (is_bad_inode(inode)) ++ return 0; ++ ++ mutex_lock(&ui->budg_mutex); ++ ++ /* ++ * Due to races between write-back forced by budgeting ++ * (see 'sync_some_inodes()') and pdflush write-back, the inode may ++ * have already been synchronized, do not do this again. ++ * ++ * This might also happen if it was synchronized in e.g. ubifs_link()', ++ * etc. ++ */ ++ if (!ui->dirty) { ++ mutex_unlock(&ui->budg_mutex); ++ return 0; ++ } ++ ++ ubifs_assert(ui->budgeted); ++ dbg_gen("inode %lu", inode->i_ino); ++ ++ err = ubifs_jnl_write_inode(c, inode, 0, IS_SYNC(inode)); ++ if (err) ++ ubifs_err("can't write inode %lu, error %d", inode->i_ino, err); ++ ++ ui->dirty = 0; ++ UBIFS_DBG(ui->budgeted = 0); ++ atomic_long_dec(&c->dirty_ino_cnt); ++ ++ ubifs_release_budget(c, &req); ++ mutex_unlock(&ui->budg_mutex); ++ ++ return err; ++} ++ ++static void ubifs_delete_inode(struct inode *inode) ++{ ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ struct ubifs_budget_req req = {.dd_growth = c->inode_budget, ++ .dirtied_ino_d = ui->data_len}; ++ int err; ++ ++ if (ui->xattr) { ++ /* ++ * Extended attribute inode deletions are fully handled in ++ * 'ubifs_removexattr()'. These inodes are special and have ++ * limited usage, so there is nothing to do here. ++ */ ++ ubifs_assert(!ui->dirty); ++ goto out; ++ } ++ ++ dbg_gen("inode %lu", inode->i_ino); ++ ubifs_assert(!atomic_read(&inode->i_count)); ++ ubifs_assert(inode->i_nlink == 0); ++ ++ truncate_inode_pages(&inode->i_data, 0); ++ if (is_bad_inode(inode)) ++ goto out; ++ ++ mutex_lock(&ui->budg_mutex); ++ ++ inode->i_size = 0; ++ ++ err = ubifs_jnl_write_inode(c, inode, 1, IS_SYNC(inode)); ++ if (err) ++ /* ++ * Worst case we have a lost orphan inode wasting space, so a ++ * simple error message is ok here. ++ */ ++ ubifs_err("can't write inode %lu, error %d", inode->i_ino, err); ++ ++ if (ui->dirty) { ++ ubifs_assert(ui->budgeted); ++ atomic_long_dec(&c->dirty_ino_cnt); ++ ui->dirty = 0; ++ UBIFS_DBG(ui->budgeted = 0); ++ ubifs_release_budget(c, &req); ++ } ++ ++ mutex_unlock(&ui->budg_mutex); ++out: ++ clear_inode(inode); ++} ++ ++static void ubifs_dirty_inode(struct inode *inode) ++{ ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ ++ ubifs_assert(mutex_is_locked(&ui->budg_mutex)); ++ if (!ui->dirty) { ++ struct ubifs_info *c = inode->i_sb->s_fs_info; ++ ++ ui->dirty = 1; ++ atomic_long_inc(&c->dirty_ino_cnt); ++ dbg_gen("inode %lu", inode->i_ino); ++ } ++} ++ ++static int ubifs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct ubifs_info *c = dentry->d_sb->s_fs_info; ++ unsigned long long free; ++ ++ free = ubifs_budg_get_free_space(c); ++ dbg_gen("free space %lld bytes (%lld blocks)", ++ free, free >> UBIFS_BLOCK_SHIFT); ++ ++ buf->f_type = UBIFS_SUPER_MAGIC; ++ buf->f_bsize = UBIFS_BLOCK_SIZE; ++ buf->f_blocks = c->block_cnt; ++ buf->f_bfree = free >> UBIFS_BLOCK_SHIFT; ++ if (free > c->report_rp_size) ++ buf->f_bavail = (free - c->report_rp_size) >> UBIFS_BLOCK_SHIFT; ++ else ++ buf->f_bavail = 0; ++ buf->f_files = 0; ++ buf->f_ffree = 0; ++ buf->f_namelen = UBIFS_MAX_NLEN; ++ ++ return 0; ++} ++ ++static int ubifs_show_options(struct seq_file *s, struct vfsmount *mnt) ++{ ++ struct ubifs_info *c = mnt->mnt_sb->s_fs_info; ++ ++ if (c->mount_opts.unmount_mode == 2) ++ seq_printf(s, ",fast_unmount"); ++ else if (c->mount_opts.unmount_mode == 1) ++ seq_printf(s, ",norm_unmount"); ++ ++ return 0; ++} ++ ++static int ubifs_sync_fs(struct super_block *sb, int wait) ++{ ++ struct ubifs_info *c = sb->s_fs_info; ++ int i, ret = 0, err; ++ ++ if (c->jheads) ++ for (i = 0; i < c->jhead_cnt; i++) { ++ err = ubifs_wbuf_sync(&c->jheads[i].wbuf); ++ if (err && !ret) ++ ret = err; ++ } ++ /* ++ * We ought to call sync for c->ubi but it does not have one. If it had ++ * it would in turn call mtd->sync, however mtd operations are ++ * synchronous anyway, so we don't lose any sleep here. ++ */ ++ return ret; ++} ++ ++/** ++ * init_constants_early - initialize UBIFS constants. ++ * @c: UBIFS file-system description object ++ * ++ * This function initialize UBIFS constants which do not need the superblock to ++ * be read. It also checks that the UBI volume satisfies basic UBIFS ++ * requirements. Returns zero in case of success and a negative error code in ++ * case of failure. ++ */ ++static int init_constants_early(struct ubifs_info *c) ++{ ++ if (c->vi.corrupted) { ++ ubifs_warn("UBI volume is corrupted - read-only mode"); ++ c->ro_media = 1; ++ } ++ ++ if (c->di.ro_mode) { ++ ubifs_msg("read-only UBI device"); ++ c->ro_media = 1; ++ } ++ ++ if (c->vi.vol_type == UBI_STATIC_VOLUME) { ++ ubifs_msg("static UBI volume - read-only mode"); ++ c->ro_media = 1; ++ } ++ ++ c->leb_cnt = c->vi.size; ++ c->leb_size = c->vi.usable_leb_size; ++ c->half_leb_size = c->leb_size / 2; ++ c->min_io_size = c->di.min_io_size; ++ c->min_io_shift = fls(c->min_io_size) - 1; ++ ++ if (c->leb_size < UBIFS_MIN_LEB_SZ) { ++ ubifs_err("too small LEBs (%d bytes), min. is %d bytes", ++ c->leb_size, UBIFS_MIN_LEB_SZ); ++ return -EINVAL; ++ } ++ ++ if (c->leb_cnt < UBIFS_MIN_LEB_CNT) { ++ ubifs_err("too few LEBs (%d), min. is %d", ++ c->leb_cnt, UBIFS_MIN_LEB_CNT); ++ return -EINVAL; ++ } ++ ++ if (!is_power_of_2(c->min_io_size)) { ++ ubifs_err("bad min. I/O size %d", c->min_io_size); ++ return -EINVAL; ++ } ++ ++ /* ++ * UBIFS aligns all node to 8-byte boundary, so to make function in ++ * io.c simpler, assume minimum I/O unit size to be 8 bytes if it is ++ * less than 8. ++ */ ++ if (c->min_io_size < 8) { ++ c->min_io_size = 8; ++ c->min_io_shift = 3; ++ } ++ ++ c->ref_node_alsz = ALIGN(UBIFS_REF_NODE_SZ, c->min_io_size); ++ c->mst_node_alsz = ALIGN(UBIFS_MST_NODE_SZ, c->min_io_size); ++ ++ /* ++ * Initialize node length ranges which are mostly needed for node ++ * length validation. ++ */ ++ c->ranges[UBIFS_PAD_NODE].len = UBIFS_PAD_NODE_SZ; ++ c->ranges[UBIFS_SB_NODE].len = UBIFS_SB_NODE_SZ; ++ c->ranges[UBIFS_MST_NODE].len = UBIFS_MST_NODE_SZ; ++ c->ranges[UBIFS_REF_NODE].len = UBIFS_REF_NODE_SZ; ++ c->ranges[UBIFS_TRUN_NODE].len = UBIFS_TRUN_NODE_SZ; ++ c->ranges[UBIFS_CS_NODE].len = UBIFS_CS_NODE_SZ; ++ ++ c->ranges[UBIFS_INO_NODE].min_len = UBIFS_INO_NODE_SZ; ++ c->ranges[UBIFS_INO_NODE].max_len = UBIFS_MAX_INO_NODE_SZ; ++ c->ranges[UBIFS_ORPH_NODE].min_len = ++ UBIFS_ORPH_NODE_SZ + sizeof(__le64); ++ c->ranges[UBIFS_ORPH_NODE].max_len = c->leb_size; ++ c->ranges[UBIFS_DENT_NODE].min_len = UBIFS_DENT_NODE_SZ; ++ c->ranges[UBIFS_DENT_NODE].max_len = UBIFS_MAX_DENT_NODE_SZ; ++ c->ranges[UBIFS_XENT_NODE].min_len = UBIFS_XENT_NODE_SZ; ++ c->ranges[UBIFS_XENT_NODE].max_len = UBIFS_MAX_XENT_NODE_SZ; ++ c->ranges[UBIFS_DATA_NODE].min_len = UBIFS_DATA_NODE_SZ; ++ c->ranges[UBIFS_DATA_NODE].max_len = UBIFS_MAX_DATA_NODE_SZ; ++ /* ++ * Minimum indexing node size is amended later when superblock is ++ * read and the key length is known. ++ */ ++ c->ranges[UBIFS_IDX_NODE].min_len = UBIFS_IDX_NODE_SZ + UBIFS_BRANCH_SZ; ++ /* ++ * Maximum indexing node size is amended later when superblock is ++ * read and the fanout is known. ++ */ ++ c->ranges[UBIFS_IDX_NODE].max_len = INT_MAX; ++ ++ /* ++ * Initialize dead and dark LEB space watermarks. ++ * ++ * Dead space is the space which cannot be used. Its watermark is ++ * equivalent to min. I/O unit or minimum node size if it is greater ++ * then min. I/O unit. ++ * ++ * Dark space is the space which might be used, or might not, depending ++ * on which node should be written to the LEB. Its watermark is ++ * equivalent to maximum UBIFS node size. ++ */ ++ c->dead_wm = ALIGN(MIN_WRITE_SZ, c->min_io_size); ++ c->dark_wm = ALIGN(UBIFS_MAX_NODE_SZ, c->min_io_size); ++ ++ return 0; ++} ++ ++/** ++ * bud_wbuf_callback - bud LEB write-buffer synchronization call-back. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB the write-buffer was synchronized to ++ * @free: how many free bytes left in this LEB ++ * @pad: how many bytes were padded ++ * ++ * This is a callback function which is called by the I/O unit when the ++ * write-buffer is synchronized. We need this to correctly maintain space ++ * accounting in bud logical eraseblocks. This function returns zero in case of ++ * success and a negative error code in case of failure. ++ * ++ * This function actually belongs to the journal, but we keep it here because ++ * we want to keep it static. ++ */ ++static int bud_wbuf_callback(struct ubifs_info *c, int lnum, int free, int pad) ++{ ++ return ubifs_update_one_lp(c, lnum, free, pad, 0, 0); ++} ++ ++/* ++ * init_constants_late - initialize UBIFS constants. ++ * @c: UBIFS file-system description object ++ * ++ * This is a helper function which initializes various UBIFS constants after ++ * the superblock has been read. It also checks various UBIFS parameters and ++ * makes sure they are all right. Returns zero in case of success and a ++ * negative error code in case of failure. ++ */ ++static int init_constants_late(struct ubifs_info *c) ++{ ++ int tmp, err; ++ uint64_t tmp64; ++ ++ c->main_bytes = c->main_lebs * c->leb_size; ++ ++ c->max_znode_sz = sizeof(struct ubifs_znode) + ++ c->fanout * sizeof(struct ubifs_zbranch); ++ ++ tmp = ubifs_idx_node_sz(c, 1); ++ c->ranges[UBIFS_IDX_NODE].min_len = tmp; ++ c->min_idx_node_sz = ALIGN(tmp, 8); ++ ++ tmp = ubifs_idx_node_sz(c, c->fanout); ++ c->ranges[UBIFS_IDX_NODE].max_len = tmp; ++ c->max_idx_node_sz = ALIGN(tmp, 8); ++ ++ /* Make sure LEB size is large enough to fit full commit */ ++ tmp = UBIFS_CS_NODE_SZ + UBIFS_REF_NODE_SZ * c->jhead_cnt; ++ tmp = ALIGN(tmp, c->min_io_size); ++ if (tmp > c->leb_size) { ++ dbg_err("too small LEB size %d, at least %d needed", ++ c->leb_size, tmp); ++ return -EINVAL; ++ } ++ ++ /* ++ * Make sure that the log is large enough to fit reference nodes for ++ * all buds plus one reserved LEB. ++ */ ++ tmp64 = c->max_bud_bytes; ++ tmp = do_div(tmp64, c->leb_size); ++ c->max_bud_cnt = tmp64 + !!tmp; ++ tmp = (c->ref_node_alsz * c->max_bud_cnt + c->leb_size - 1); ++ tmp /= c->leb_size; ++ tmp += 1; ++ if (c->log_lebs < tmp) { ++ dbg_err("too small log %d LEBs, required min. %d LEBs", ++ c->log_lebs, tmp); ++ return -EINVAL; ++ } ++ ++ /* ++ * When budgeting we assume worst-case scenarios when the pages are not ++ * be compressed and direntries are of the maximum size. ++ * ++ * Note, data, which may be stored in inodes is budgeted separately, so ++ * it is not included into 'c->inode_budget'. ++ */ ++ c->page_budget = UBIFS_MAX_DATA_NODE_SZ * UBIFS_BLOCKS_PER_PAGE; ++ c->inode_budget = UBIFS_INO_NODE_SZ; ++ c->dent_budget = UBIFS_MAX_DENT_NODE_SZ; ++ ++ /* ++ * When the amount of flash space used by buds becomes ++ * 'c->max_bud_bytes', UBIFS just blocks all writers and starts commit. ++ * The writers are unblocked when the commit is finished. To avoid ++ * writers to be blocked UBIFS initiates background commit in advance, ++ * when number of bud bytes becomes above the limit defined below. ++ */ ++ c->bg_bud_bytes = (c->max_bud_bytes * 13) >> 4; ++ ++ /* ++ * Ensure minimum journal size. All the bytes in the journal heads are ++ * considered to be used, when calculating the current journal usage. ++ * Consequently, if the journal is too small, UBIFS will treat it as ++ * always full. ++ */ ++ tmp64 = (uint64_t)(c->jhead_cnt + 1) * c->leb_size + 1; ++ if (c->bg_bud_bytes < tmp64) ++ c->bg_bud_bytes = tmp64; ++ if (c->max_bud_bytes < tmp64 + c->leb_size) ++ c->max_bud_bytes = tmp64 + c->leb_size; ++ ++ err = ubifs_calc_lpt_geom(c); ++ if (err) ++ return err; ++ ++ c->min_idx_lebs = ubifs_calc_min_idx_lebs(c); ++ ++ /* ++ * Calculate total amount of FS blocks. This number is not used ++ * internally because it does not make much sense for UBIFS, but it is ++ * necessary to report something for the 'statfs()' call. ++ * ++ * Subtract the LEB reserved for GC and the LEB which is reserved for ++ * deletions. ++ * ++ * Review 'ubifs_calc_available()' if changing this calculation. ++ */ ++ tmp64 = c->main_lebs - 2; ++ tmp64 *= c->leb_size - c->dark_wm; ++ tmp64 = ubifs_reported_space(c, tmp64); ++ c->block_cnt = tmp64 >> UBIFS_BLOCK_SHIFT; ++ ++ return 0; ++} ++ ++/** ++ * care_about_gc_lnum - take care about reserved GC LEB. ++ * @c: UBIFS file-system description object ++ * ++ * This function ensures that the LEB reserved for garbage collection is ++ * unmapped and is marked as "taken" in lprops. We also have to set free space ++ * to LEB size and dirty space to zero, because lprops may contain out-of-date ++ * information if the file-system was un-mounted before it has been committed. ++ * This function returns zero in case of success and a negative error code in ++ * case of failure. ++ */ ++static int care_about_gc_lnum(struct ubifs_info *c) ++{ ++ int err; ++ ++ if (c->gc_lnum == -1) { ++ ubifs_err("no LEB for GC"); ++ return -EINVAL; ++ } ++ ++ err = ubifs_leb_unmap(c, c->gc_lnum); ++ if (err) ++ return err; ++ ++ /* And we have to tell lprops that this LEB is taken */ ++ err = ubifs_change_one_lp(c, c->gc_lnum, c->leb_size, 0, ++ LPROPS_TAKEN, 0, 0); ++ return err; ++} ++ ++/** ++ * alloc_wbufs - allocate write-buffers. ++ * @c: UBIFS file-system description object ++ * ++ * This helper function allocates and initializes UBIFS write-buffers. Returns ++ * zero in case of success and %-ENOMEM in case of failure. ++ */ ++static int alloc_wbufs(struct ubifs_info *c) ++{ ++ int i, err; ++ ++ c->jheads = kzalloc(c->jhead_cnt * sizeof(struct ubifs_jhead), ++ GFP_KERNEL); ++ if (!c->jheads) ++ return -ENOMEM; ++ ++ /* Initialize journal heads */ ++ for (i = 0; i < c->jhead_cnt; i++) { ++ INIT_LIST_HEAD(&c->jheads[i].buds_list); ++ err = ubifs_wbuf_init(c, &c->jheads[i].wbuf); ++ if (err) ++ return err; ++ ++ c->jheads[i].wbuf.sync_callback = &bud_wbuf_callback; ++ c->jheads[i].wbuf.jhead = i; ++ } ++ ++ c->jheads[BASEHD].wbuf.dtype = UBI_SHORTTERM; ++ /* ++ * Garbage Collector head likely contains long-term data and ++ * does not need to be synchronized by timer. ++ */ ++ c->jheads[GCHD].wbuf.dtype = UBI_LONGTERM; ++ c->jheads[GCHD].wbuf.timeout = 0; ++ ++ return 0; ++} ++ ++/** ++ * free_wbufs - free write-buffers. ++ * @c: UBIFS file-system description object ++ */ ++static void free_wbufs(struct ubifs_info *c) ++{ ++ int i; ++ ++ if (c->jheads) { ++ for (i = 0; i < c->jhead_cnt; i++) { ++ kfree(c->jheads[i].wbuf.buf); ++ kfree(c->jheads[i].wbuf.inodes); ++ } ++ kfree(c->jheads); ++ c->jheads = NULL; ++ } ++} ++ ++/** ++ * free_orphans - free orphans. ++ * @c: UBIFS file-system description object ++ */ ++static void free_orphans(struct ubifs_info *c) ++{ ++ struct ubifs_orphan *orph; ++ ++ while (c->orph_dnext) { ++ orph = c->orph_dnext; ++ c->orph_dnext = orph->dnext; ++ list_del(&orph->list); ++ kfree(orph); ++ } ++ ++ while (!list_empty(&c->orph_list)) { ++ orph = list_entry(c->orph_list.next, struct ubifs_orphan, list); ++ list_del(&orph->list); ++ kfree(orph); ++ dbg_err("orphan list not empty at unmount"); ++ } ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(c->orph_buf); ++#else ++ vfree(c->orph_buf); ++#endif ++ c->orph_buf = NULL; ++} ++ ++/** ++ * free_buds - free per-bud objects. ++ * @c: UBIFS file-system description object ++ */ ++static void free_buds(struct ubifs_info *c) ++{ ++ struct rb_node *this = c->buds.rb_node; ++ struct ubifs_bud *bud; ++ ++ while (this) { ++ if (this->rb_left) ++ this = this->rb_left; ++ else if (this->rb_right) ++ this = this->rb_right; ++ else { ++ bud = rb_entry(this, struct ubifs_bud, rb); ++ this = rb_parent(this); ++ if (this) { ++ if (this->rb_left == &bud->rb) ++ this->rb_left = NULL; ++ else ++ this->rb_right = NULL; ++ } ++ kfree(bud); ++ } ++ } ++} ++ ++/** ++ * check_volume_empty - check if the UBI volume is empty. ++ * @c: UBIFS file-system description object ++ * ++ * This function checks if the UBIFS volume is empty by looking if its LEBs are ++ * mapped or not. The result of checking is stored in the @c->empty variable. ++ * Returns zero in case of success and a negative error code in case of ++ * failure. ++ */ ++static int check_volume_empty(struct ubifs_info *c) ++{ ++ int lnum, err; ++ ++ c->empty = 1; ++ for (lnum = 0; lnum < c->leb_cnt; lnum++) { ++ err = ubi_is_mapped(c->ubi, lnum); ++ if (unlikely(err < 0)) ++ return err; ++ if (err == 1) { ++ c->empty = 0; ++ break; ++ } ++ ++ cond_resched(); ++ } ++ ++ return 0; ++} ++ ++/* ++ * UBIFS mount options. ++ * ++ * Opt_fast_unmount: do not run a journal commit before un-mounting ++ * Opt_norm_unmount: run a journal commit before un-mounting ++ * Opt_err: just end of array marker ++ */ ++enum { ++ Opt_fast_unmount, ++ Opt_norm_unmount, ++ Opt_err, ++}; ++ ++static match_table_t tokens = { ++ {Opt_fast_unmount, "fast_unmount"}, ++ {Opt_norm_unmount, "norm_unmount"}, ++ {Opt_err, NULL}, ++}; ++ ++/** ++ * ubifs_parse_options - parse mount parameters. ++ * @c: UBIFS file-system description object ++ * @options: parameters to parse ++ * @is_remount: non-zero if this is FS re-mount ++ * ++ * This function parses UBIFS mount options and returns zero in case success ++ * and a negative error code in case of failure. ++ */ ++static int ubifs_parse_options(struct ubifs_info *c, char *options, ++ int is_remount) ++{ ++ char *p; ++ substring_t args[MAX_OPT_ARGS]; ++ ++ if (!options) ++ return 0; ++ ++ while ((p = strsep(&options, ","))) { ++ int token; ++ ++ if (!*p) ++ continue; ++ ++ token = match_token(p, tokens, args); ++ switch (token) { ++ case Opt_fast_unmount: ++ c->mount_opts.unmount_mode = 2; ++ c->fast_unmount = 1; ++ break; ++ case Opt_norm_unmount: ++ c->mount_opts.unmount_mode = 1; ++ c->fast_unmount = 0; ++ break; ++ default: ++ ubifs_err("unrecognized mount option \"%s\" " ++ "or missing value", p); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * destroy_journal - destroy journal data structures. ++ * @c: UBIFS file-system description object ++ * ++ * This function destroys journal data structures including those that may have ++ * been created by recovery functions. ++ */ ++static void destroy_journal(struct ubifs_info *c) ++{ ++ while (!list_empty(&c->unclean_leb_list)) { ++ struct ubifs_unclean_leb *ucleb; ++ ++ ucleb = list_entry(c->unclean_leb_list.next, ++ struct ubifs_unclean_leb, list); ++ list_del(&ucleb->list); ++ kfree(ucleb); ++ } ++ while (!list_empty(&c->old_buds)) { ++ struct ubifs_bud *bud; ++ ++ bud = list_entry(c->old_buds.next, struct ubifs_bud, list); ++ list_del(&bud->list); ++ kfree(bud); ++ } ++ ubifs_destroy_idx_gc(c); ++ ubifs_destroy_size_tree(c); ++ ubifs_tnc_close(c); ++ free_buds(c); ++} ++ ++/** ++ * mount_ubifs - mount UBIFS file-system. ++ * @c: UBIFS file-system description object ++ * ++ * This function mounts UBIFS file system. Returns zero in case of success and ++ * a negative error code in case of failure. ++ * ++ * Note, the function does not de-allocate resources it it fails half way ++ * through, and the caller has to do this instead. ++ */ ++static int mount_ubifs(struct ubifs_info *c) ++{ ++ struct super_block *sb = c->vfs_sb; ++ int err, mounted_read_only = (sb->s_flags & MS_RDONLY); ++ unsigned long long x; ++ size_t sz; ++ ++ err = init_constants_early(c); ++ if (err) ++ return err; ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ c->dbg_buf = kmalloc(c->leb_size, GFP_KERNEL); ++#else ++ c->dbg_buf = vmalloc(c->leb_size); ++#endif ++ if (!c->dbg_buf) ++ return -ENOMEM; ++#endif ++ ++ err = check_volume_empty(c); ++ if (err) ++ goto out_free; ++ ++ if (c->empty && (mounted_read_only || c->ro_media)) { ++ /* ++ * This UBI volume is empty, and read-only, or the file system ++ * is mounted read-only - we cannot format it. ++ */ ++ ubifs_err("can't format empty UBI volume: read-only %s", ++ c->ro_media ? "UBI volume" : "mount"); ++ err = -EROFS; ++ goto out_free; ++ } ++ ++ if (c->ro_media && !mounted_read_only) { ++ ubifs_err("cannot mount read-write - read-only media"); ++ err = -EROFS; ++ goto out_free; ++ } ++ ++ /* ++ * The requirement for the buffer is that it should fit indexing B-tree ++ * height amount of integers. We assume the height if the TNC tree will ++ * never exceed 64. ++ */ ++ err = -ENOMEM; ++ c->bottom_up_buf = kmalloc(BOTTOM_UP_HEIGHT * sizeof(int), GFP_KERNEL); ++ if (!c->bottom_up_buf) ++ goto out_free; ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ c->sbuf = kmalloc(c->leb_size, GFP_KERNEL); ++#else ++ c->sbuf = vmalloc(c->leb_size); ++#endif ++ if (!c->sbuf) ++ goto out_free; ++ ++ if (!mounted_read_only) { ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ c->ileb_buf = kmalloc(c->leb_size, GFP_KERNEL); ++#else ++ c->ileb_buf = vmalloc(c->leb_size); ++#endif ++ if (!c->ileb_buf) ++ goto out_free; ++ } ++ ++ err = ubifs_read_superblock(c); ++ if (err) ++ goto out_free; ++ ++ /* ++ * Make sure the compressor which is set as the default on in the ++ * superblock was actually compiled in. ++ */ ++ if (!ubifs_compr_present(c->default_compr)) { ++ ubifs_warn("'%s' compressor is set by superblock, but not " ++ "compiled in", ubifs_compr_name(c->default_compr)); ++ c->default_compr = UBIFS_COMPR_NONE; ++ } ++ ++ dbg_failure_mode_registration(c); ++ ++ err = init_constants_late(c); ++ if (err) ++ goto out_dereg; ++ ++ sz = ALIGN(c->max_idx_node_sz, c->min_io_size); ++ sz = ALIGN(sz + c->max_idx_node_sz, c->min_io_size); ++ c->cbuf = kmalloc(sz, GFP_NOFS); ++ if (!c->cbuf) { ++ err = -ENOMEM; ++ goto out_dereg; ++ } ++ ++ if (!mounted_read_only) { ++ err = alloc_wbufs(c); ++ if (err) ++ goto out_cbuf; ++ ++ /* Create background thread */ ++ sprintf(c->bgt_name, BGT_NAME_PATTERN, c->vi.ubi_num, ++ c->vi.vol_id); ++ c->bgt = kthread_create(ubifs_bg_thread, c, c->bgt_name); ++ if (!c->bgt) ++ c->bgt = ERR_PTR(-EINVAL); ++ if (IS_ERR(c->bgt)) { ++ err = PTR_ERR(c->bgt); ++ c->bgt = NULL; ++ ubifs_err("cannot spawn \"%s\", error %d", ++ c->bgt_name, err); ++ goto out_wbufs; ++ } ++ wake_up_process(c->bgt); ++ } ++ ++ err = ubifs_read_master(c); ++ if (err) ++ goto out_stop; ++ ++ if ((c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY)) != 0) { ++ ubifs_msg("recovery needed"); ++ c->need_recovery = 1; ++ if (!mounted_read_only) { ++ err = ubifs_recover_inl_heads(c, c->sbuf); ++ if (err) ++ goto out_master; ++ } ++ } else if (!mounted_read_only) { ++ /* ++ * Set the "dirty" flag so that if we reboot uncleanly we ++ * will notice this immediately on the next mount. ++ */ ++ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY); ++ err = ubifs_write_master(c); ++ if (err) ++ goto out_master; ++ } ++ ++ err = ubifs_lpt_init(c, 1, !mounted_read_only); ++ if (err) ++ goto out_master; ++ ++ err = dbg_check_idx_size(c, c->old_idx_sz); ++ if (err) ++ goto out_lpt; ++ ++ err = ubifs_replay_journal(c); ++ if (err) ++ goto out_journal; ++ ++ if (!mounted_read_only) { ++ int lnum; ++ ++ err = ubifs_mount_orphans(c, c->need_recovery); ++ if (err) ++ goto out_journal; ++ ++ if (c->need_recovery) ++ err = ubifs_recover_gc_lnum(c); ++ else ++ err = care_about_gc_lnum(c); ++ if (err) ++ goto out_orphans; ++ ++ /* Check for enough log space */ ++ lnum = c->lhead_lnum + 1; ++ if (lnum >= UBIFS_LOG_LNUM + c->log_lebs) ++ lnum = UBIFS_LOG_LNUM; ++ if (lnum == c->ltail_lnum) { ++ err = ubifs_consolidate_log(c); ++ if (err) ++ goto out_orphans; ++ } ++ ++ /* Check for enough free space */ ++ if (ubifs_calc_available(c) <= 0) { ++ ubifs_err("insufficient available space"); ++ err = -EINVAL; ++ goto out_orphans; ++ } ++ ++ err = dbg_check_lprops(c); ++ if (err) ++ goto out_orphans; ++ } ++ ++ if (c->need_recovery) { ++ err = ubifs_recover_size(c); ++ if (err) ++ goto out_orphans; ++ } ++ ++ spin_lock(&ubifs_infos_lock); ++ list_add_tail(&c->infos_list, &ubifs_infos); ++ spin_unlock(&ubifs_infos_lock); ++ ++ if (c->need_recovery) { ++ if (mounted_read_only) ++ ubifs_msg("recovery deferred"); ++ else { ++ c->need_recovery = 0; ++ ubifs_msg("recovery completed"); ++ } ++ } ++ ++ ubifs_msg("mounted UBI device %d, volume %d", c->vi.ubi_num, ++ c->vi.vol_id); ++ if (mounted_read_only) ++ ubifs_msg("mounted read-only"); ++ ubifs_msg("minimal I/O unit size: %d bytes", c->min_io_size); ++ ubifs_msg("logical eraseblock size: %d bytes (%d KiB)", ++ c->leb_size, c->leb_size / 1024); ++ x = (unsigned long long)c->main_lebs * c->leb_size; ++ ubifs_msg("file system size: %lld bytes (%lld KiB, %lld MiB, " ++ "%d LEBs)", x, x >> 10, x >> 20, c->main_lebs); ++ x = (unsigned long long)c->log_lebs * c->leb_size + c->max_bud_bytes; ++ ubifs_msg("journal size: %lld bytes (%lld KiB, %lld MiB, " ++ "%d LEBs)", x, x >> 10, x >> 20, ++ c->log_lebs + c->max_bud_cnt); ++ ubifs_msg("data journal heads: %d", ++ c->jhead_cnt - NONDATA_JHEADS_CNT); ++ ubifs_msg("default compressor: %s", ++ ubifs_compr_name(c->default_compr)); ++ ubifs_msg("media format %d, latest format %d", ++ c->fmt_version, UBIFS_FORMAT_VERSION); ++ ++ dbg_msg("compiled on: " __DATE__ " at " __TIME__); ++ dbg_msg("fast unmount: %d", c->fast_unmount); ++ dbg_msg("big_lpt %d", c->big_lpt); ++ dbg_msg("log LEBs: %d (%d - %d)", ++ c->log_lebs, UBIFS_LOG_LNUM, c->log_last); ++ dbg_msg("LPT area LEBs: %d (%d - %d)", ++ c->lpt_lebs, c->lpt_first, c->lpt_last); ++ dbg_msg("orphan area LEBs: %d (%d - %d)", ++ c->orph_lebs, c->orph_first, c->orph_last); ++ dbg_msg("main area LEBs: %d (%d - %d)", ++ c->main_lebs, c->main_first, c->leb_cnt - 1); ++ dbg_msg("index LEBs: %d", c->lst.idx_lebs); ++ dbg_msg("total index bytes: %lld (%lld KiB, %lld MiB)", ++ c->old_idx_sz, c->old_idx_sz >> 10, c->old_idx_sz >> 20); ++ dbg_msg("key hash type: %d", c->key_hash_type); ++ dbg_msg("tree fanout: %d", c->fanout); ++ dbg_msg("reserved GC LEB: %d", c->gc_lnum); ++ dbg_msg("first main LEB: %d", c->main_first); ++ dbg_msg("dead watermark: %d", c->dead_wm); ++ dbg_msg("dark watermark: %d", c->dark_wm); ++ x = c->main_lebs * c->dark_wm; ++ dbg_msg("max. dark space: %lld (%lld KiB, %lld MiB)", ++ x, x >> 10, x >> 20); ++ dbg_msg("maximum bud bytes: %lld (%lld KiB, %lld MiB)", ++ c->max_bud_bytes, c->max_bud_bytes >> 10, ++ c->max_bud_bytes >> 20); ++ dbg_msg("BG commit bud bytes: %lld (%lld KiB, %lld MiB)", ++ c->bg_bud_bytes, c->bg_bud_bytes >> 10, ++ c->bg_bud_bytes >> 20); ++ dbg_msg("current bud bytes %lld (%lld KiB, %lld MiB)", ++ c->bud_bytes, c->bud_bytes >> 10, c->bud_bytes >> 20); ++ dbg_msg("max. seq. number: %llu", c->max_sqnum); ++ dbg_msg("commit number: %llu", c->cmt_no); ++ ++ return 0; ++ ++out_orphans: ++ free_orphans(c); ++out_journal: ++ destroy_journal(c); ++out_lpt: ++ ubifs_lpt_free(c, 0); ++out_master: ++ kfree(c->mst_node); ++ kfree(c->rcvrd_mst_node); ++out_stop: ++ if (c->bgt) ++ kthread_stop(c->bgt); ++out_wbufs: ++ free_wbufs(c); ++out_cbuf: ++ kfree(c->cbuf); ++out_dereg: ++ dbg_failure_mode_deregistration(c); ++out_free: ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(c->ileb_buf); ++ kfree(c->sbuf); ++ UBIFS_DBG(kfree(c->dbg_buf)); ++#else ++ vfree(c->ileb_buf); ++ vfree(c->sbuf); ++ UBIFS_DBG(vfree(c->dbg_buf)); ++#endif ++ kfree(c->bottom_up_buf); ++ return err; ++} ++ ++/** ++ * ubifs_umount - un-mount UBIFS file-system. ++ * @c: UBIFS file-system description object ++ * ++ * Note, this function is called to free allocated resourced when un-mounting, ++ * as well as free resources when an error occurred while we were half way ++ * through mounting (error path cleanup function). So it has to make sure the ++ * resource was actually allocated before freeing it. ++ */ ++static void ubifs_umount(struct ubifs_info *c) ++{ ++ dbg_gen("un-mounting UBI device %d, volume %d", c->vi.ubi_num, ++ c->vi.vol_id); ++ ++ if (c->bgt) ++ kthread_stop(c->bgt); ++ ++ destroy_journal(c); ++ free_wbufs(c); ++ free_orphans(c); ++ ubifs_lpt_free(c, 0); ++ ++ kfree(c->cbuf); ++ kfree(c->rcvrd_mst_node); ++ kfree(c->mst_node); ++ kfree(c->bottom_up_buf); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(c->sbuf); ++ UBIFS_DBG(kfree(c->dbg_buf)); ++ kfree(c->ileb_buf); ++#else ++ vfree(c->sbuf); ++ UBIFS_DBG(vfree(c->dbg_buf)); ++ vfree(c->ileb_buf); ++#endif ++ dbg_failure_mode_deregistration(c); ++} ++ ++/** ++ * ubifs_remount_rw - re-mount in read-write mode. ++ * @c: UBIFS file-system description object ++ * ++ * UBIFS avoids allocating many unnecessary resources when mounted in read-only ++ * mode. This function allocates the needed resources and re-mounts UBIFS in ++ * read-write mode. ++ */ ++static int ubifs_remount_rw(struct ubifs_info *c) ++{ ++ int err, lnum; ++ ++ if (c->ro_media) ++ return -EINVAL; ++ ++ mutex_lock(&c->umount_mutex); ++ c->remounting_rw = 1; ++ ++ /* Check for enough free space */ ++ if (ubifs_calc_available(c) <= 0) { ++ ubifs_err("insufficient available space"); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (c->old_leb_cnt != c->leb_cnt) { ++ struct ubifs_sb_node *sup; ++ ++ sup = ubifs_read_sb_node(c); ++ if (IS_ERR(sup)) { ++ err = PTR_ERR(sup); ++ goto out; ++ } ++ sup->leb_cnt = cpu_to_le32(c->leb_cnt); ++ err = ubifs_write_sb_node(c, sup); ++ if (err) ++ goto out; ++ } ++ ++ if (c->need_recovery) { ++ ubifs_msg("completing deferred recovery"); ++ err = ubifs_write_rcvrd_mst_node(c); ++ if (err) ++ goto out; ++ err = ubifs_recover_size(c); ++ if (err) ++ goto out; ++ err = ubifs_clean_lebs(c, c->sbuf); ++ if (err) ++ goto out; ++ err = ubifs_recover_inl_heads(c, c->sbuf); ++ if (err) ++ goto out; ++ } ++ ++ if (!(c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY))) { ++ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY); ++ err = ubifs_write_master(c); ++ if (err) ++ goto out; ++ } ++ ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ c->ileb_buf = kmalloc(c->leb_size, GFP_KERNEL); ++#else ++ c->ileb_buf = vmalloc(c->leb_size); ++#endif ++ if (!c->ileb_buf) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ err = ubifs_lpt_init(c, 0, 1); ++ if (err) ++ goto out; ++ ++ err = alloc_wbufs(c); ++ if (err) ++ goto out; ++ ++ ubifs_create_buds_lists(c); ++ ++ /* Create background thread */ ++ c->bgt = kthread_create(ubifs_bg_thread, c, c->bgt_name); ++ if (!c->bgt) ++ c->bgt = ERR_PTR(-EINVAL); ++ if (IS_ERR(c->bgt)) { ++ err = PTR_ERR(c->bgt); ++ c->bgt = NULL; ++ ubifs_err("cannot spawn \"%s\", error %d", ++ c->bgt_name, err); ++ return err; ++ } ++ wake_up_process(c->bgt); ++ ++ err = ubifs_mount_orphans(c, c->need_recovery); ++ if (err) ++ goto out; ++ ++ if (c->need_recovery) ++ err = ubifs_recover_gc_lnum(c); ++ else ++ err = care_about_gc_lnum(c); ++ if (err) ++ goto out; ++ ++ /* Check for enough log space */ ++ lnum = c->lhead_lnum + 1; ++ if (lnum >= UBIFS_LOG_LNUM + c->log_lebs) ++ lnum = UBIFS_LOG_LNUM; ++ if (lnum == c->ltail_lnum) { ++ err = ubifs_consolidate_log(c); ++ if (err) ++ goto out; ++ } ++ ++ if (c->need_recovery) { ++ c->need_recovery = 0; ++ ubifs_msg("deferred recovery completed"); ++ } ++ ++ dbg_gen("re-mounted read-write"); ++ c->vfs_sb->s_flags &= ~MS_RDONLY; ++ c->remounting_rw = 0; ++ mutex_unlock(&c->umount_mutex); ++ return 0; ++ ++out: ++ free_orphans(c); ++ if (c->bgt) { ++ kthread_stop(c->bgt); ++ c->bgt = NULL; ++ } ++ free_wbufs(c); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(c->ileb_buf); ++#else ++ vfree(c->ileb_buf); ++#endif ++ c->ileb_buf = NULL; ++ ubifs_lpt_free(c, 1); ++ c->remounting_rw = 0; ++ mutex_unlock(&c->umount_mutex); ++ return err; ++} ++ ++/** ++ * commit_on_unmount - commit the journal when un-mounting. ++ * @c: UBIFS file-system description object ++ * ++ * This function is called during un-mounting and it commits the journal unless ++ * the "fast unmount" mode is enabled. It also avoids committing the journal if ++ * it contains too few data. ++ * ++ * Sometimes recovery requires the journal to be committed at least once, and ++ * this function takes care about this. ++ */ ++static void commit_on_unmount(struct ubifs_info *c) ++{ ++ if (!c->fast_unmount) { ++ long long bud_bytes; ++ ++ spin_lock(&c->buds_lock); ++ bud_bytes = c->bud_bytes; ++ spin_unlock(&c->buds_lock); ++ if (bud_bytes > c->leb_size) ++ ubifs_run_commit(c); ++ } ++ ++ if (c->recovery_needs_commit) ++ ubifs_recovery_commit(c); ++} ++ ++/** ++ * ubifs_remount_ro - re-mount in read-only mode. ++ * @c: UBIFS file-system description object ++ * ++ * We rely on VFS to have stopped writing. Possibly the background thread could ++ * be running a commit, however kthread_stop will wait in that case. ++ */ ++static void ubifs_remount_ro(struct ubifs_info *c) ++{ ++ int i, err; ++ ++ ubifs_assert(!c->need_recovery); ++ commit_on_unmount(c); ++ ++ mutex_lock(&c->umount_mutex); ++ if (c->bgt) { ++ kthread_stop(c->bgt); ++ c->bgt = NULL; ++ } ++ ++ for (i = 0; i < c->jhead_cnt; i++) { ++ ubifs_wbuf_sync(&c->jheads[i].wbuf); ++ del_timer_sync(&c->jheads[i].wbuf.timer); ++ } ++ ++ if (!c->ro_media) { ++ c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY); ++ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS); ++ c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum); ++ err = ubifs_write_master(c); ++ if (err) ++ ubifs_ro_mode(c, err); ++ } ++ ++ ubifs_destroy_idx_gc(c); ++ free_wbufs(c); ++ free_orphans(c); ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(c->ileb_buf); ++#else ++ vfree(c->ileb_buf); ++#endif ++ c->ileb_buf = NULL; ++ ubifs_lpt_free(c, 1); ++ mutex_unlock(&c->umount_mutex); ++} ++ ++static void ubifs_put_super(struct super_block *sb) ++{ ++ int i; ++ struct ubifs_info *c = sb->s_fs_info; ++ ++ ubifs_msg("un-mount UBI device %d, volume %d", c->vi.ubi_num, ++ c->vi.vol_id); ++ /* ++ * The following asserts are only valid if there has not been a failure ++ * of the media. For example, there will be dirty inodes if we failed ++ * to write them back because of I/O errors. ++ */ ++ ubifs_assert(atomic_long_read(&c->dirty_pg_cnt) == 0); ++ ubifs_assert(atomic_long_read(&c->dirty_ino_cnt) == 0); ++ ubifs_assert(c->budg_idx_growth == 0); ++ ubifs_assert(c->budg_data_growth == 0); ++ ++ /* ++ * The 'c->umount_lock' prevents races between UBIFS memory shrinker ++ * and file system un-mount. Namely, it prevents the shrinker from ++ * picking this superblock for shrinking - it will be just skipped if ++ * the mutex is locked. ++ */ ++ mutex_lock(&c->umount_mutex); ++ ++ spin_lock(&ubifs_infos_lock); ++ list_del(&c->infos_list); ++ spin_unlock(&ubifs_infos_lock); ++ ++ if (!(c->vfs_sb->s_flags & MS_RDONLY)) { ++ /* ++ * First of all kill the background thread to make sure it does ++ * not interfere with un-mounting and freeing resources. ++ */ ++ if (c->bgt) { ++ kthread_stop(c->bgt); ++ c->bgt = NULL; ++ } ++ ++ /* Synchronize write-buffers */ ++ if (c->jheads) ++ for (i = 0; i < c->jhead_cnt; i++) { ++ ubifs_wbuf_sync(&c->jheads[i].wbuf); ++ del_timer_sync(&c->jheads[i].wbuf.timer); ++ } ++ ++ /* ++ * On fatal errors c->ro_media is set to 1, in which case we do ++ * not write the master node. ++ */ ++ if (!c->ro_media) { ++ /* ++ * We are being cleanly unmounted which means the ++ * orphans were killed - indicate this in the master ++ * node. Also save the reserved GC LEB number. ++ */ ++ int err; ++ ++ c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY); ++ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS); ++ c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum); ++ err = ubifs_write_master(c); ++ if (err) ++ /* ++ * Recovery will attempt to fix the master area ++ * next mount, so we just print a message and ++ * continue to unmount normally. ++ */ ++ ubifs_err("failed to write master node, " ++ "error %d", err); ++ } ++ } ++ ++ ubifs_umount(c); ++ ubi_close_volume(c->ubi); ++ mutex_unlock(&c->umount_mutex); ++ kfree(c); ++} ++ ++static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data) ++{ ++ int err; ++ struct ubifs_info *c = sb->s_fs_info; ++ ++ dbg_gen("old flags %#lx, new flags %#x", sb->s_flags, *flags); ++ ++ err = ubifs_parse_options(c, data, 1); ++ if (err) { ++ ubifs_err("invalid or unknown remount parameter"); ++ return err; ++ } ++ if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) { ++ err = ubifs_remount_rw(c); ++ if (err) ++ return err; ++ } else if (!(sb->s_flags & MS_RDONLY) && (*flags & MS_RDONLY)) ++ ubifs_remount_ro(c); ++ ++ return 0; ++} ++ ++struct super_operations ubifs_super_operations = { ++#ifdef UBIFS_COMPAT_USE_OLD_IGET ++ .read_inode = ubifs_read_inode, ++#endif ++ .alloc_inode = ubifs_alloc_inode, ++ .destroy_inode = ubifs_destroy_inode, ++ .put_super = ubifs_put_super, ++ .write_inode = ubifs_write_inode, ++ .delete_inode = ubifs_delete_inode, ++ .statfs = ubifs_statfs, ++ .dirty_inode = ubifs_dirty_inode, ++ .remount_fs = ubifs_remount_fs, ++ .show_options = ubifs_show_options, ++ .sync_fs = ubifs_sync_fs, ++}; ++ ++/** ++ * open_ubi - parse UBI device name string and open the UBI device. ++ * @name: UBI volume name ++ * @mode: UBI volume open mode ++ * ++ * There are several ways to specify UBI volumes when mounting UBIFS: ++ * o ubiX_Y - UBI device number X, volume Y; ++ * o ubiY - UBI device number 0, volume Y; ++ * o ubiX:NAME - mount UBI device X, volume with name NAME; ++ * o ubi:NAME - mount UBI device 0, volume with name NAME. ++ * ++ * Alternative '!' separator may be used instead of ':' (because some shells ++ * like busybox may interpret ':' as an NFS host name separator). This function ++ * returns ubi volume object in case of success and a negative error code in ++ * case of failure. ++ */ ++static struct ubi_volume_desc *open_ubi(const char *name, int mode) ++{ ++ int dev, vol; ++ char *endptr; ++ ++ if (name[0] != 'u' || name[1] != 'b' || name[2] != 'i') ++ return ERR_PTR(-EINVAL); ++ ++ /* ubi:NAME method */ ++ if ((name[3] == ':' || name[3] == '!') && name[4] != '\0') ++ return ubi_open_volume_nm(0, name + 4, mode); ++ ++ if (!isdigit(name[3])) ++ return ERR_PTR(-EINVAL); ++ ++ dev = simple_strtoul(name + 3, &endptr, 0); ++ ++ /* ubiY method */ ++ if (*endptr == '\0') ++ return ubi_open_volume(0, dev, mode); ++ ++ /* ubiX_Y method */ ++ if (*endptr == '_' && isdigit(endptr[1])) { ++ vol = simple_strtoul(endptr + 1, &endptr, 0); ++ if (*endptr != '\0') ++ return ERR_PTR(-EINVAL); ++ return ubi_open_volume(dev, vol, mode); ++ } ++ ++ /* ubiX:NAME method */ ++ if ((*endptr == ':' || *endptr == '!') && endptr[1] != '\0') ++ return ubi_open_volume_nm(dev, ++endptr, mode); ++ ++ return ERR_PTR(-EINVAL); ++} ++ ++static int ubifs_fill_super(struct super_block *sb, void *data, int silent) ++{ ++ struct ubi_volume_desc *ubi = sb->s_fs_info; ++ struct ubifs_info *c; ++ struct inode *root; ++ int err; ++ ++ c = kzalloc(sizeof(struct ubifs_info), GFP_KERNEL); ++ if (!c) ++ return -ENOMEM; ++ ++ spin_lock_init(&c->cnt_lock); ++ spin_lock_init(&c->cs_lock); ++ spin_lock_init(&c->buds_lock); ++ spin_lock_init(&c->space_lock); ++ spin_lock_init(&c->orphan_lock); ++ init_rwsem(&c->commit_sem); ++ mutex_init(&c->lp_mutex); ++ mutex_init(&c->tnc_mutex); ++ mutex_init(&c->log_mutex); ++ mutex_init(&c->mst_mutex); ++ mutex_init(&c->umount_mutex); ++ init_waitqueue_head(&c->cmt_wq); ++ c->buds = RB_ROOT; ++ c->old_idx = RB_ROOT; ++ c->size_tree = RB_ROOT; ++ c->orph_tree = RB_ROOT; ++ INIT_LIST_HEAD(&c->infos_list); ++ INIT_LIST_HEAD(&c->idx_gc); ++ INIT_LIST_HEAD(&c->replay_list); ++ INIT_LIST_HEAD(&c->replay_buds); ++ INIT_LIST_HEAD(&c->uncat_list); ++ INIT_LIST_HEAD(&c->empty_list); ++ INIT_LIST_HEAD(&c->freeable_list); ++ INIT_LIST_HEAD(&c->frdi_idx_list); ++ INIT_LIST_HEAD(&c->unclean_leb_list); ++ INIT_LIST_HEAD(&c->old_buds); ++ INIT_LIST_HEAD(&c->orph_list); ++ INIT_LIST_HEAD(&c->orph_new); ++ ++ c->highest_inum = UBIFS_FIRST_INO; ++ get_random_bytes(&c->vfs_gen, sizeof(int)); ++ c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM; ++ ++ ubi_get_volume_info(ubi, &c->vi); ++ ubi_get_device_info(c->vi.ubi_num, &c->di); ++ ++ /* Re-open the UBI device in read-write mode */ ++ c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READWRITE); ++ if (IS_ERR(c->ubi)) { ++ err = PTR_ERR(c->ubi); ++ goto out_free; ++ } ++ ++ err = ubifs_parse_options(c, data, 0); ++ if (err) ++ goto out_close; ++ ++ c->vfs_sb = sb; ++ ++ sb->s_fs_info = c; ++ sb->s_magic = UBIFS_SUPER_MAGIC; ++ sb->s_blocksize = UBIFS_BLOCK_SIZE; ++ sb->s_blocksize_bits = UBIFS_BLOCK_SHIFT; ++ sb->s_dev = c->vi.cdev; ++ sb->s_maxbytes = c->max_inode_sz = key_max_inode_size(c); ++ if (c->max_inode_sz > MAX_LFS_FILESIZE) ++ sb->s_maxbytes = c->max_inode_sz = MAX_LFS_FILESIZE; ++ sb->s_op = &ubifs_super_operations; ++ ++ mutex_lock(&c->umount_mutex); ++ err = mount_ubifs(c); ++ if (err) { ++ ubifs_assert(err < 0); ++ goto out_unlock; ++ } ++ ++ /* Read the root inode */ ++ root = ubifs_iget(sb, UBIFS_ROOT_INO); ++ if (IS_ERR(root)) { ++ err = PTR_ERR(root); ++ goto out_umount; ++ } ++ ++ sb->s_root = d_alloc_root(root); ++ if (!sb->s_root) ++ goto out_iput; ++ ++ mutex_unlock(&c->umount_mutex); ++ ++ return 0; ++ ++out_iput: ++ iput(root); ++out_umount: ++ spin_lock(&ubifs_infos_lock); ++ list_del(&c->infos_list); ++ spin_unlock(&ubifs_infos_lock); ++ ubifs_umount(c); ++out_unlock: ++ mutex_unlock(&c->umount_mutex); ++out_close: ++ ubi_close_volume(c->ubi); ++out_free: ++ kfree(c); ++ return err; ++} ++ ++static int sb_test(struct super_block *sb, void *data) ++{ ++ dev_t *dev = data; ++ ++ return sb->s_dev == *dev; ++} ++ ++static int sb_set(struct super_block *sb, void *data) ++{ ++ dev_t *dev = data; ++ ++ sb->s_dev = *dev; ++ return 0; ++} ++ ++static int ubifs_get_sb(struct file_system_type *fs_type, int flags, ++ const char *name, void *data, struct vfsmount *mnt) ++{ ++ struct ubi_volume_desc *ubi; ++ struct ubi_volume_info vi; ++ struct super_block *sb; ++ int err; ++ ++ dbg_gen("name %s, flags %#x", name, flags); ++ ++ /* ++ * Get UBI device number and volume ID. Mount it read-only so far ++ * because this might be a new mount point, and UBI allows only one ++ * read-write user at a time. ++ */ ++ ubi = open_ubi(name, UBI_READONLY); ++ if (IS_ERR(ubi)) { ++ ubifs_err("cannot open \"%s\", error %d", ++ name, (int)PTR_ERR(ubi)); ++ return PTR_ERR(ubi); ++ } ++ ubi_get_volume_info(ubi, &vi); ++ ++ dbg_gen("opened ubi%d_%d", vi.ubi_num, vi.vol_id); ++ ++ sb = sget(fs_type, &sb_test, &sb_set, &vi.cdev); ++ if (IS_ERR(sb)) { ++ err = PTR_ERR(sb); ++ goto out_close; ++ } ++ ++ if (sb->s_root) { ++ /* A new mount point for already mounted UBIFS */ ++ dbg_gen("this ubi volume is already mounted"); ++ if ((flags ^ sb->s_flags) & MS_RDONLY) { ++ err = -EBUSY; ++ goto out_deact; ++ } ++ } else { ++ sb->s_flags = flags; ++ /* ++ * Pass 'ubi' to 'fill_super()' in sb->s_fs_info where it is ++ * replaced by 'c'. ++ */ ++ sb->s_fs_info = ubi; ++ err = ubifs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); ++ if (err) ++ goto out_deact; ++ /* We do not support atime */ ++ sb->s_flags |= MS_ACTIVE | MS_NOATIME; ++ } ++ ++ /* 'fill_super()' opens ubi again so we must close it here */ ++ ubi_close_volume(ubi); ++ ++ return simple_set_mnt(mnt, sb); ++ ++out_deact: ++ up_write(&sb->s_umount); ++ deactivate_super(sb); ++out_close: ++ ubi_close_volume(ubi); ++ return err; ++} ++ ++static void ubifs_kill_sb(struct super_block *sb) ++{ ++ struct ubifs_info *c = sb->s_fs_info; ++ ++ /* ++ * We do 'commit_on_unmount()' here instead of 'ubifs_put_super()' ++ * in order to be outside BKL. ++ */ ++ if (sb->s_root && !(sb->s_flags & MS_RDONLY)) ++ commit_on_unmount(c); ++ /* The un-mount routine is actually done in put_super() */ ++ generic_shutdown_super(sb); ++} ++ ++static struct file_system_type ubifs_fs_type = { ++ .name = "ubifs", ++ .owner = THIS_MODULE, ++ .get_sb = ubifs_get_sb, ++ .kill_sb = ubifs_kill_sb ++}; ++ ++/* ++ * Inode slab cache constructor. ++ */ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)) ++static void inode_slab_ctor(void *obj, struct kmem_cache *cachep, ++ unsigned long flags) ++#else ++static void inode_slab_ctor(struct kmem_cache *cachep, void *obj) ++#endif ++{ ++ struct ubifs_inode *inode = obj; ++ inode_init_once(&inode->vfs_inode); ++} ++ ++static int __init ubifs_init(void) ++{ ++ int err; ++ ++ BUILD_BUG_ON(sizeof(struct ubifs_ch) != 24); ++ ++ /* Make sure node sizes are 8-byte aligned */ ++ BUILD_BUG_ON(UBIFS_CH_SZ & 7); ++ BUILD_BUG_ON(UBIFS_INO_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_DENT_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_XENT_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_DATA_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_TRUN_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_SB_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_MST_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_REF_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_CS_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_ORPH_NODE_SZ & 7); ++ ++ BUILD_BUG_ON(UBIFS_MAX_DENT_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_MAX_XENT_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_MAX_DATA_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_MAX_INO_NODE_SZ & 7); ++ BUILD_BUG_ON(UBIFS_MAX_NODE_SZ & 7); ++ BUILD_BUG_ON(MIN_WRITE_SZ & 7); ++ ++ /* Check min. node size */ ++ BUILD_BUG_ON(UBIFS_INO_NODE_SZ < MIN_WRITE_SZ); ++ BUILD_BUG_ON(UBIFS_DENT_NODE_SZ < MIN_WRITE_SZ); ++ BUILD_BUG_ON(UBIFS_XENT_NODE_SZ < MIN_WRITE_SZ); ++ BUILD_BUG_ON(UBIFS_TRUN_NODE_SZ < MIN_WRITE_SZ); ++ ++ BUILD_BUG_ON(UBIFS_MAX_DENT_NODE_SZ > UBIFS_MAX_NODE_SZ); ++ BUILD_BUG_ON(UBIFS_MAX_XENT_NODE_SZ > UBIFS_MAX_NODE_SZ); ++ BUILD_BUG_ON(UBIFS_MAX_DATA_NODE_SZ > UBIFS_MAX_NODE_SZ); ++ BUILD_BUG_ON(UBIFS_MAX_INO_NODE_SZ > UBIFS_MAX_NODE_SZ); ++ ++ /* Defined node sizes */ ++ BUILD_BUG_ON(UBIFS_SB_NODE_SZ != 4096); ++ BUILD_BUG_ON(UBIFS_MST_NODE_SZ != 512); ++ BUILD_BUG_ON(UBIFS_INO_NODE_SZ != 160); ++ BUILD_BUG_ON(UBIFS_REF_NODE_SZ != 64); ++ ++ /* ++ * We require that PAGE_CACHE_SIZE is greater-than-or-equal-to ++ * UBIFS_BLOCK_SIZE. It is assumed that both are powers of 2. ++ */ ++ if (PAGE_CACHE_SIZE < UBIFS_BLOCK_SIZE) { ++ ubifs_err("VFS page cache size is %u bytes, but UBIFS requires" ++ " at least 4096 bytes", ++ (unsigned int)PAGE_CACHE_SIZE); ++ return -EINVAL; ++ } ++ ++ err = bdi_init(&ubifs_backing_dev_info); ++ if (err) ++ return err; ++ ++ err = register_filesystem(&ubifs_fs_type); ++ if (err) { ++ ubifs_err("cannot register file system, error %d", err); ++ goto out; ++ } ++ ++ err = -ENOMEM; ++ ubifs_inode_slab = kmem_cache_create("ubifs_inode_slab", ++ sizeof(struct ubifs_inode), 0, ++ SLAB_MEM_SPREAD | SLAB_RECLAIM_ACCOUNT, ++ &inode_slab_ctor UBIFSCOMPATNULL); ++ if (!ubifs_inode_slab) ++ goto out_reg; ++ ++ register_shrinker(&ubifs_shrinker_info); ++ ++ err = ubifs_compressors_init(); ++ if (err) ++ goto out_compr; ++ ++ return 0; ++ ++out_compr: ++ unregister_shrinker(&ubifs_shrinker_info); ++ kmem_cache_destroy(ubifs_inode_slab); ++out_reg: ++ unregister_filesystem(&ubifs_fs_type); ++out: ++ bdi_destroy(&ubifs_backing_dev_info); ++ return err; ++} ++/* late_initcall to let compressors initialize first */ ++late_initcall(ubifs_init); ++ ++static void __exit ubifs_exit(void) ++{ ++ ubifs_assert(list_empty(&ubifs_infos)); ++ ubifs_assert(atomic_long_read(&ubifs_clean_zn_cnt) == 0); ++ ++ ubifs_compressors_exit(); ++ unregister_shrinker(&ubifs_shrinker_info); ++ kmem_cache_destroy(ubifs_inode_slab); ++ unregister_filesystem(&ubifs_fs_type); ++ bdi_destroy(&ubifs_backing_dev_info); ++} ++module_exit(ubifs_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(__stringify(UBIFS_VERSION)); ++MODULE_AUTHOR("Artem Bityutskiy, Adrian Hunter"); ++MODULE_DESCRIPTION("UBIFS - UBI File System"); +diff -urN linux-2.6.24.7.old/fs/ubifs/tnc.c linux-2.6.24.7/fs/ubifs/tnc.c +--- linux-2.6.24.7.old/fs/ubifs/tnc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/tnc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,3300 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy (Битюцкий Ðртём) ++ */ ++ ++/* ++ * This file implements TNC (Tree Node Cache) which caches indexing nodes of ++ * the UBIFS B-tree. ++ * ++ * At the moment the locking rules of the TNC tree are quite simple and ++ * straightforward. We just have a mutex and lock it when we traverse the ++ * tree. If a znode is not in memory, we read it from flash while still having ++ * the mutex locked. ++ */ ++ ++#include ++#include "ubifs.h" ++ ++/* ++ * Returned codes of 'matches_name()' and 'fallible_matches_name()' functions. ++ * @NAME_LESS: name corresponding to the first argument is less than second ++ * @NAME_MATCHES: names match ++ * @NAME_GREATER: name corresponding to the second argument is greater than ++ * first ++ * @NOT_ON_MEDIA: node referred by zbranch does not exist on the media ++ * ++ * These constants were introduce to improve readability. ++ */ ++enum { ++ NAME_LESS = 0, ++ NAME_MATCHES = 1, ++ NAME_GREATER = 2, ++ NOT_ON_MEDIA = 3, ++}; ++ ++/** ++ * insert_old_idx - record an index node obsoleted since the last commit start. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number of obsoleted index node ++ * @offs: offset of obsoleted index node ++ * ++ * Returns %0 on success, and a negative error code on failure. ++ * ++ * For recovery, there must always be a complete intact version of the index on ++ * flash at all times. That is called the "old index". It is the index as at the ++ * time of the last successful commit. Many of the index nodes in the old index ++ * may be dirty, but they must not be erased until the next successful commit ++ * (at which point that index becomes the old index). ++ * ++ * That means that the garbage collection and the in-the-gaps method of ++ * committing must be able to determine if an index node is in the old index. ++ * Most of the old index nodes can be found by looking up the TNC using the ++ * 'lookup_znode()' function. However, some of the old index nodes may have ++ * been deleted from the current index or may have been changed so much that ++ * they cannot be easily found. In those cases, an entry is added to an RB-tree. ++ * That is what this function does. The RB-tree is ordered by LEB number and ++ * offset because they uniquely identify the old index node. ++ */ ++static int insert_old_idx(struct ubifs_info *c, int lnum, int offs) ++{ ++ struct ubifs_old_idx *old_idx, *o; ++ struct rb_node **p, *parent = NULL; ++ ++ old_idx = kmalloc(sizeof(struct ubifs_old_idx), GFP_NOFS); ++ if (unlikely(!old_idx)) ++ return -ENOMEM; ++ old_idx->lnum = lnum; ++ old_idx->offs = offs; ++ ++ p = &c->old_idx.rb_node; ++ while (*p) { ++ parent = *p; ++ o = rb_entry(parent, struct ubifs_old_idx, rb); ++ if (lnum < o->lnum) ++ p = &(*p)->rb_left; ++ else if (lnum > o->lnum) ++ p = &(*p)->rb_right; ++ else if (offs < o->offs) ++ p = &(*p)->rb_left; ++ else if (offs > o->offs) ++ p = &(*p)->rb_right; ++ else { ++ ubifs_err("old idx added twice!"); ++ kfree(old_idx); ++ return 0; ++ } ++ } ++ rb_link_node(&old_idx->rb, parent, p); ++ rb_insert_color(&old_idx->rb, &c->old_idx); ++ return 0; ++} ++ ++/** ++ * insert_old_idx_znode - record a znode obsoleted since last commit start. ++ * @c: UBIFS file-system description object ++ * @znode: znode of obsoleted index node ++ * ++ * Returns %0 on success, and a negative error code on failure. ++ */ ++int insert_old_idx_znode(struct ubifs_info *c, struct ubifs_znode *znode) ++{ ++ if (znode->parent) { ++ struct ubifs_zbranch *zbr; ++ ++ zbr = &znode->parent->zbranch[znode->iip]; ++ if (zbr->len) ++ return insert_old_idx(c, zbr->lnum, zbr->offs); ++ } else ++ if (c->zroot.len) ++ return insert_old_idx(c, c->zroot.lnum, ++ c->zroot.offs); ++ return 0; ++} ++ ++/** ++ * ins_clr_old_idx_znode - record a znode obsoleted since last commit start. ++ * @c: UBIFS file-system description object ++ * @znode: znode of obsoleted index node ++ * ++ * Returns %0 on success, and a negative error code on failure. ++ */ ++static int ins_clr_old_idx_znode(struct ubifs_info *c, ++ struct ubifs_znode *znode) ++{ ++ int err; ++ ++ if (znode->parent) { ++ struct ubifs_zbranch *zbr; ++ ++ zbr = &znode->parent->zbranch[znode->iip]; ++ if (zbr->len) { ++ err = insert_old_idx(c, zbr->lnum, zbr->offs); ++ if (err) ++ return err; ++ zbr->lnum = 0; ++ zbr->offs = 0; ++ zbr->len = 0; ++ } ++ } else ++ if (c->zroot.len) { ++ err = insert_old_idx(c, c->zroot.lnum, c->zroot.offs); ++ if (err) ++ return err; ++ c->zroot.lnum = 0; ++ c->zroot.offs = 0; ++ c->zroot.len = 0; ++ } ++ return 0; ++} ++ ++/** ++ * destroy_old_idx - destroy the old_idx RB-tree. ++ * @c: UBIFS file-system description object ++ * ++ * During start commit, the old_idx RB-tree is used to avoid overwriting index ++ * nodes that were in the index last commit but have since been deleted. This ++ * is necessary for recovery i.e. the old index must be kept intact until the ++ * new index is successfully written. The old-idx RB-tree is used for the ++ * in-the-gaps method of writing index nodes and is destroyed every commit. ++ */ ++void destroy_old_idx(struct ubifs_info *c) ++{ ++ struct rb_node *this = c->old_idx.rb_node; ++ struct ubifs_old_idx *old_idx; ++ ++ while (this) { ++ if (this->rb_left) { ++ this = this->rb_left; ++ continue; ++ } else if (this->rb_right) { ++ this = this->rb_right; ++ continue; ++ } ++ old_idx = rb_entry(this, struct ubifs_old_idx, rb); ++ this = rb_parent(this); ++ if (this) { ++ if (this->rb_left == &old_idx->rb) ++ this->rb_left = NULL; ++ else ++ this->rb_right = NULL; ++ } ++ kfree(old_idx); ++ } ++ c->old_idx = RB_ROOT; ++} ++ ++/** ++ * read_znode - read an indexing node from flash and fill znode. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB of the indexing node to read ++ * @offs: node offset ++ * @len: node length ++ * @znode: znode to read to ++ * ++ * This function reads an indexing node from the flash media and fills znode ++ * with the read data. Returns zero in case of success and a negative error ++ * code in case of failure. The read indexing node is validated and if anything ++ * is wrong with it, this function prints complaint messages and returns ++ * %-EINVAL. ++ */ ++static int read_znode(struct ubifs_info *c, int lnum, int offs, int len, ++ struct ubifs_znode *znode) ++{ ++ int i, err, type, cmp; ++ struct ubifs_idx_node *idx; ++ ++ idx = kmalloc(c->max_idx_node_sz, GFP_NOFS); ++ if (!idx) ++ return -ENOMEM; ++ ++ err = ubifs_read_node(c, idx, UBIFS_IDX_NODE, len, lnum, offs); ++ if (err < 0) ++ goto out; ++ ++ znode->child_cnt = le16_to_cpu(idx->child_cnt); ++ znode->level = le16_to_cpu(idx->level); ++ ++ dbg_tnc("LEB %d:%d, level %d, %d branch", ++ lnum, offs, znode->level, znode->child_cnt); ++ ++ if (znode->child_cnt > c->fanout || znode->level > UBIFS_MAX_LEVELS) { ++ dbg_err("current fanout %d, branch count %d", ++ c->fanout, znode->child_cnt); ++ dbg_err("max levels %d, znode level %d", ++ UBIFS_MAX_LEVELS, znode->level); ++ goto out_dump; ++ } ++ ++ for (i = 0; i < znode->child_cnt; i++) { ++ const struct ubifs_branch *br = ubifs_idx_branch(c, idx, i); ++ struct ubifs_zbranch *zbr = &znode->zbranch[i]; ++ ++ key_read(c, &br->key, &zbr->key); ++ zbr->lnum = le32_to_cpu(br->lnum); ++ zbr->offs = le32_to_cpu(br->offs); ++ zbr->len = le32_to_cpu(br->len); ++ zbr->znode = NULL; ++ ++ /* Validate branch */ ++ ++ if (zbr->lnum < c->main_first || ++ zbr->lnum >= c->leb_cnt || zbr->offs < 0 || ++ zbr->offs + zbr->len > c->leb_size || zbr->offs & 7) { ++ dbg_err("bad branch %d", i); ++ goto out_dump; ++ } ++ ++ switch (key_type(c, &zbr->key)) { ++ case UBIFS_INO_KEY: ++ case UBIFS_DATA_KEY: ++ case UBIFS_DENT_KEY: ++ case UBIFS_XENT_KEY: ++ break; ++ default: ++ dbg_msg("bad key type at slot %d: %s", i, ++ DBGKEY(&zbr->key)); ++ goto out_dump; ++ } ++ ++ if (znode->level) ++ continue; ++ ++ type = key_type(c, &zbr->key); ++ if (c->ranges[type].max_len == 0) { ++ if (zbr->len != c->ranges[type].len) { ++ dbg_err("bad target node (type %d) length (%d)", ++ type, zbr->len); ++ dbg_err("have to be %d", c->ranges[type].len); ++ goto out_dump; ++ } ++ } else if (zbr->len < c->ranges[type].min_len || ++ zbr->len > c->ranges[type].max_len) { ++ dbg_err("bad target node (type %d) length (%d)", ++ type, zbr->len); ++ dbg_err("have to be in range of %d-%d", ++ c->ranges[type].min_len, ++ c->ranges[type].max_len); ++ goto out_dump; ++ } ++ } ++ ++ /* ++ * Ensure that the next key is greater or equivalent to the ++ * previous one. ++ */ ++ for (i = 0; i < znode->child_cnt - 1; i++) { ++ const union ubifs_key *key1, *key2; ++ ++ key1 = &znode->zbranch[i].key; ++ key2 = &znode->zbranch[i + 1].key; ++ ++ cmp = keys_cmp(c, key1, key2); ++ if (cmp > 0) { ++ dbg_err("bad key order (keys %d and %d)", i, i + 1); ++ goto out_dump; ++ } else if (cmp == 0 && !is_hash_key(c, key1)) { ++ /* These can only be keys with colliding hash */ ++ dbg_err("keys %d and %d are not hashed but equivalent", ++ i, i + 1); ++ goto out_dump; ++ } ++ } ++ ++ kfree(idx); ++ return 0; ++ ++out: ++ kfree(idx); ++ return err; ++ ++out_dump: ++ ubifs_err("bad indexing node at LEB %d:%d", lnum, offs); ++ dbg_dump_node(c, idx); ++ kfree(idx); ++ return -EINVAL; ++} ++ ++/** ++ * load_znode - load znode to TNC cache. ++ * @c: UBIFS file-system description object ++ * @zbr: znode branch ++ * @parent: znode's parent ++ * @iip: index in parent ++ * ++ * This function loads znode pointed to by @zbr into the TNC cache and ++ * returns pointer to it in case of success and a negative error code in case ++ * of failure. ++ */ ++static struct ubifs_znode *load_znode(struct ubifs_info *c, ++ struct ubifs_zbranch *zbr, ++ struct ubifs_znode *parent, int iip) ++{ ++ int err; ++ struct ubifs_znode *znode; ++ ++ ubifs_assert(!zbr->znode); ++ /* ++ * A slab cache is not presently used for znodes because the znode size ++ * depends on the fanout which is stored in the superblock. ++ */ ++ znode = kzalloc(c->max_znode_sz, GFP_NOFS); ++ if (!znode) ++ return ERR_PTR(-ENOMEM); ++ ++ err = read_znode(c, zbr->lnum, zbr->offs, zbr->len, znode); ++ if (err) ++ goto out; ++ ++ atomic_long_inc(&c->clean_zn_cnt); ++ ++ /* ++ * Increment the global clean znode counter as well. It is OK that ++ * global and per-FS clean znode counters may be inconsistent for some ++ * short time (because we might be preempted at this point), the global ++ * one is only used in shrinker. ++ */ ++ atomic_long_inc(&ubifs_clean_zn_cnt); ++ ++ zbr->znode = znode; ++ znode->parent = parent; ++ znode->time = get_seconds(); ++ znode->iip = iip; ++ ++ return znode; ++ ++out: ++ kfree(znode); ++ return ERR_PTR(err); ++} ++ ++/** ++ * copy_znode - copy a dirty znode. ++ * @c: UBIFS file-system description object ++ * @znode: znode to copy ++ * ++ * A dirty znode being committed may not be changed, so it is copied. ++ */ ++static struct ubifs_znode *copy_znode(struct ubifs_info *c, ++ struct ubifs_znode *znode) ++{ ++ struct ubifs_znode *zn; ++ ++ zn = kmalloc(c->max_znode_sz, GFP_NOFS); ++ if (unlikely(!zn)) ++ return ERR_PTR(-ENOMEM); ++ ++ memcpy(zn, znode, c->max_znode_sz); ++ zn->cnext = NULL; ++ __set_bit(DIRTY_ZNODE, &zn->flags); ++ __clear_bit(COW_ZNODE, &zn->flags); ++ ++ ubifs_assert(!test_bit(OBSOLETE_ZNODE, &znode->flags)); ++ __set_bit(OBSOLETE_ZNODE, &znode->flags); ++ ++ if (znode->level != 0) { ++ int i; ++ const int n = zn->child_cnt; ++ ++ /* The children now have new parent */ ++ for (i = 0; i < n; i++) { ++ struct ubifs_zbranch *zbr = &zn->zbranch[i]; ++ ++ if (zbr->znode) ++ zbr->znode->parent = zn; ++ } ++ } ++ ++ atomic_long_inc(&c->dirty_zn_cnt); ++ return zn; ++} ++ ++/** ++ * add_idx_dirt - add dirt due to a dirty znode. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number of index node ++ * @dirt: size of index node ++ * ++ * This function updates lprops dirty space and the new size of the index. ++ */ ++static int add_idx_dirt(struct ubifs_info *c, int lnum, int dirt) ++{ ++ c->calc_idx_sz -= ALIGN(dirt, 8); ++ return ubifs_add_dirt(c, lnum, dirt); ++} ++ ++/** ++ * dirty_cow_znode - ensure a znode is not being committed. ++ * @c: UBIFS file-system description object ++ * @zbr: branch of znode to check ++ * ++ * Returns dirtied znode on success or negative error code on failure. ++ */ ++static struct ubifs_znode *dirty_cow_znode(struct ubifs_info *c, ++ struct ubifs_zbranch *zbr) ++{ ++ struct ubifs_znode *znode = zbr->znode; ++ struct ubifs_znode *zn; ++ int err; ++ ++ if (!test_bit(COW_ZNODE, &znode->flags)) { ++ /* znode is not being committed */ ++ if (!test_and_set_bit(DIRTY_ZNODE, &znode->flags)) { ++ atomic_long_inc(&c->dirty_zn_cnt); ++ atomic_long_dec(&c->clean_zn_cnt); ++ atomic_long_dec(&ubifs_clean_zn_cnt); ++ err = add_idx_dirt(c, zbr->lnum, zbr->len); ++ if (unlikely(err)) ++ return ERR_PTR(err); ++ } ++ return znode; ++ } ++ ++ zn = copy_znode(c, znode); ++ if (unlikely(IS_ERR(zn))) ++ return zn; ++ ++ if (zbr->len) { ++ err = insert_old_idx(c, zbr->lnum, zbr->offs); ++ if (unlikely(err)) ++ return ERR_PTR(err); ++ err = add_idx_dirt(c, zbr->lnum, zbr->len); ++ } else ++ err = 0; ++ ++ zbr->znode = zn; ++ zbr->lnum = 0; ++ zbr->offs = 0; ++ zbr->len = 0; ++ ++ if (unlikely(err)) ++ return ERR_PTR(err); ++ return zn; ++} ++ ++/** ++ * lnc_add - add a leaf node to the leaf node cache. ++ * @c: UBIFS file-system description object ++ * @zbr: zbranch of leaf node ++ * @node: leaf node ++ * ++ * Leaf nodes are non-index nodes directory entry nodes or data nodes. The ++ * purpose of the leaf node cache is to save re-reading the same leaf node over ++ * and over again. Most things are cached by VFS, however the file system must ++ * cache directory entries for readdir and for resolving hash collisions. The ++ * present implementation of the leaf node cache is extremely simple, and ++ * allows for error returns that are not used but that may be needed if a more ++ * complex implementation is created. ++ * ++ * Note, this function does not add the @node object to LNC directly, but ++ * allocates a copy of the object and adds the copy to LNC. The reason for this ++ * is that @node has been allocated outside of the TNC subsystem and will be ++ * used with @c->tnc_mutex unlock upon return from the TNC subsystem. But LNC ++ * may be changed at any time, e.g. freed by the shrinker. ++ */ ++static int lnc_add(struct ubifs_info *c, struct ubifs_zbranch *zbr, ++ const void *node) ++{ ++ int err; ++ void *lnc_node; ++ const struct ubifs_dent_node *dent = node; ++ ++ ubifs_assert(!zbr->leaf); ++ ubifs_assert(zbr->len != 0); ++ ubifs_assert(is_hash_key(c, &zbr->key)); ++ ++ err = ubifs_validate_entry(c, dent); ++ if (err) { ++ dbg_dump_stack(); ++ dbg_dump_node(c, dent); ++ return err; ++ } ++ ++ lnc_node = kmalloc(zbr->len, GFP_NOFS); ++ if (!lnc_node) ++ /* We don't have to have the cache, so no error */ ++ return 0; ++ ++ memcpy(lnc_node, node, zbr->len); ++ zbr->leaf = lnc_node; ++ return 0; ++} ++ ++ /** ++ * lnc_add_directly - add a leaf node to the leaf-node-cache. ++ * @c: UBIFS file-system description object ++ * @zbr: zbranch of leaf node ++ * @node: leaf node ++ * ++ * This function is similar to 'lnc_add()', but it does not create a copy of ++ * @node but inserts @node to TNC directly. ++ */ ++static int lnc_add_directly(struct ubifs_info *c, struct ubifs_zbranch *zbr, ++ void *node) ++{ ++ int err; ++ ++ ubifs_assert(!zbr->leaf); ++ ubifs_assert(zbr->len != 0); ++ ++ err = ubifs_validate_entry(c, node); ++ if (err) { ++ dbg_dump_stack(); ++ dbg_dump_node(c, node); ++ return err; ++ } ++ ++ zbr->leaf = node; ++ return 0; ++} ++ ++/** ++ * lnc_free - remove a leaf node from the leaf node cache. ++ * @zbr: zbranch of leaf node ++ * @node: leaf node ++ */ ++static void lnc_free(struct ubifs_zbranch *zbr) ++{ ++ if (!zbr->leaf) ++ return; ++ kfree(zbr->leaf); ++ zbr->leaf = NULL; ++} ++ ++/** ++ * tnc_read_node - read a leaf node from the flash media. ++ * @c: UBIFS file-system description object ++ * @zbr: key and position of the node ++ * @node: node is returned here ++ * ++ * This function reads a node defined by @zbr from the flash media. Returns ++ * zero in case of success or a negative negative error code in case of ++ * failure. ++ */ ++static int tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr, ++ void *node) ++{ ++ union ubifs_key key1, *key = &zbr->key; ++ int err, type = key_type(c, key); ++ struct ubifs_wbuf *wbuf; ++ ++ ubifs_assert(!zbr->leaf); ++ ++ /* ++ * 'zbr' has to point to on-flash node. The node may sit in a bud and ++ * may even be in a write buffer, so we have to take care about this. ++ */ ++ wbuf = ubifs_get_wbuf(c, zbr->lnum); ++ if (wbuf) ++ err = ubifs_read_node_wbuf(wbuf, node, type, zbr->len, ++ zbr->lnum, zbr->offs); ++ else ++ err = ubifs_read_node(c, node, type, zbr->len, zbr->lnum, ++ zbr->offs); ++ ++ if (err) { ++ dbg_tnc("key %s", DBGKEY(key)); ++ return err; ++ } ++ ++ /* Make sure the key of the read node is correct */ ++ key_read(c, key, &key1); ++ if (memcmp(node + UBIFS_KEY_OFFSET, &key1, c->key_len)) { ++ ubifs_err("bad key in node at LEB %d:%d", ++ zbr->lnum, zbr->offs); ++ dbg_tnc("looked for key %s found node's key %s", ++ DBGKEY(key), DBGKEY1(&key1)); ++ dbg_dump_node(c, node); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * tnc_read_node_nm - read a "hashed" leaf node. ++ * @c: UBIFS file-system description object ++ * @zbr: key and position of the node ++ * @node: node is returned here ++ * ++ * This function reads a "hashed" node defined by @zbr from the leaf node cache ++ * (in it is there) or from the hash media, in which case the node is also ++ * added to LNC. Returns zero in case of success or a negative negative error ++ * code in case of failure. ++ */ ++static int tnc_read_node_nm(struct ubifs_info *c, struct ubifs_zbranch *zbr, ++ void *node) ++{ ++ int err; ++ ++ ubifs_assert(is_hash_key(c, &zbr->key)); ++ ++ if (zbr->leaf) { ++ /* Read from the leaf node cache */ ++ ubifs_assert(zbr->len != 0); ++ memcpy(node, zbr->leaf, zbr->len); ++ return 0; ++ } ++ ++ err = tnc_read_node(c, zbr, node); ++ if (err) ++ return err; ++ ++ /* Add the node to the leaf node cache */ ++ err = lnc_add(c, zbr, node); ++ return err; ++} ++ ++/** ++ * try_read_node - read a node if it is a node. ++ * @c: UBIFS file-system description object ++ * @buf: buffer to read to ++ * @type: node type ++ * @len: node length (not aligned) ++ * @lnum: LEB number of node to read ++ * @offs: offset of node to read ++ * ++ * This function tries to read a node of known type and length, checks it and ++ * stores it in @buf. This function returns %1 if a node is present and %0 if ++ * a node is not present. A negative error code is returned for I/O errors. ++ * This function performs that same function as ubifs_read_node except that ++ * it does not require that there is actually a node present and instead ++ * the return code indicates if a node was read. ++ */ ++static int try_read_node(const struct ubifs_info *c, void *buf, int type, ++ int len, int lnum, int offs) ++{ ++ int err, node_len; ++ struct ubifs_ch *ch = buf; ++ uint32_t crc, node_crc; ++ ++ dbg_io("LEB %d:%d, %s, length %d", lnum, offs, dbg_ntype(type), len); ++ ++ err = ubi_read(c->ubi, lnum, buf, offs, len); ++ if (err) { ++ ubifs_err("cannot read node type %d from LEB %d:%d, error %d", ++ type, lnum, offs, err); ++ return err; ++ } ++ ++ if (le32_to_cpu(ch->magic) != UBIFS_NODE_MAGIC) ++ return 0; ++ ++ if (ch->node_type != type) ++ return 0; ++ ++ node_len = le32_to_cpu(ch->len); ++ if (node_len != len) ++ return 0; ++ ++ crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8); ++ node_crc = le32_to_cpu(ch->crc); ++ if (crc != node_crc) ++ return 0; ++ ++ return 1; ++} ++ ++/** ++ * fallible_read_node - try to read a leaf node. ++ * @c: UBIFS file-system description object ++ * @key: key of node to read ++ * @zbr: position of node ++ * @node: node returned ++ * ++ * This function tries to read a node and returns %1 if the node is read, %0 ++ * if the node is not present, and a negative error code in the case of error. ++ */ ++static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key, ++ struct ubifs_zbranch *zbr, void *node) ++{ ++ int ret; ++ ++ dbg_tnc("LEB %d:%d, key %s", zbr->lnum, zbr->offs, DBGKEY(key)); ++ ++ ret = try_read_node(c, node, key_type(c, key), zbr->len, zbr->lnum, ++ zbr->offs); ++ if (ret == 1) { ++ union ubifs_key node_key; ++ struct ubifs_dent_node *dent = node; ++ ++ /* All nodes have key in the same place */ ++ key_read(c, &dent->key, &node_key); ++ if (keys_cmp(c, key, &node_key) != 0) ++ ret = 0; ++ } ++ if (ret == 0) ++ dbg_mnt("dangling branch LEB %d:%d len %d, key %s", ++ zbr->lnum, zbr->offs, zbr->len, DBGKEY(key)); ++ return ret; ++} ++ ++/** ++ * matches_name - determine if a directory or extended attribute entry matches ++ * a given name. ++ * @c: UBIFS file-system description object ++ * @zbr: zbranch of dent ++ * @nm: name to match ++ * ++ * This function checks if xentry/direntry referred by zbranch @zbr matches name ++ * @nm. Returns %NAME_MATCHES if it does, %NAME_LESS if the name referred by ++ * @zbr is less than @nm, and %NAME_GREATER if it is greater than @nm. In case ++ * of failure, a negative error code is returned. ++ */ ++static int matches_name(struct ubifs_info *c, struct ubifs_zbranch *zbr, ++ const struct qstr *nm) ++{ ++ struct ubifs_dent_node *dent; ++ int nlen, err; ++ ++ /* If possible, match against the dent in the leaf node cache */ ++ if (!zbr->leaf) { ++ dent = kmalloc(zbr->len, GFP_NOFS); ++ if (!dent) ++ return -ENOMEM; ++ ++ err = tnc_read_node(c, zbr, dent); ++ if (err) ++ goto out_free; ++ ++ /* Add the node to the leaf node cache */ ++ err = lnc_add_directly(c, zbr, dent); ++ if (err) ++ goto out_free; ++ } else ++ dent = zbr->leaf; ++ ++ nlen = le16_to_cpu(dent->nlen); ++ err = memcmp(dent->name, nm->name, min_t(int, nlen, nm->len)); ++ if (err == 0) { ++ if (nlen == nm->len) ++ return NAME_MATCHES; ++ else if (nlen < nm->len) ++ return NAME_LESS; ++ else ++ return NAME_GREATER; ++ } else if (err < 0) ++ return NAME_LESS; ++ else ++ return NAME_GREATER; ++ ++out_free: ++ kfree(dent); ++ return err; ++} ++ ++/** ++ * get_znode - get a TNC znode that may not be loaded yet. ++ * @c: UBIFS file-system description object ++ * @znode: parent znode ++ * @n: znode branch slot number ++ * ++ * This function returns the znode or a negative error code. ++ */ ++static struct ubifs_znode *get_znode(struct ubifs_info *c, ++ struct ubifs_znode *znode, int n) ++{ ++ struct ubifs_zbranch *zbr; ++ ++ zbr = &znode->zbranch[n]; ++ if (zbr->znode) ++ znode = zbr->znode; ++ else ++ znode = load_znode(c, zbr, znode, n); ++ return znode; ++} ++ ++/** ++ * tnc_next - find next TNC entry. ++ * @c: UBIFS file-system description object ++ * @zn: znode is passed and returned here ++ * @n: znode branch slot number is passed and returned here ++ * ++ * This function returns %0 if the next TNC entry is found, %-ENOENT if there is ++ * no next entry, or a negative error code otherwise. ++ */ ++static int tnc_next(struct ubifs_info *c, struct ubifs_znode **zn, int *n) ++{ ++ struct ubifs_znode *znode = *zn; ++ int nn = *n; ++ ++ nn += 1; ++ if (nn < znode->child_cnt) { ++ *n = nn; ++ return 0; ++ } ++ while (1) { ++ struct ubifs_znode *zp; ++ ++ zp = znode->parent; ++ if (!zp) ++ return -ENOENT; ++ nn = znode->iip + 1; ++ znode = zp; ++ if (nn < znode->child_cnt) { ++ znode = get_znode(c, znode, nn); ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ while (znode->level != 0) { ++ znode = get_znode(c, znode, 0); ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ } ++ nn = 0; ++ break; ++ } ++ } ++ *zn = znode; ++ *n = nn; ++ return 0; ++} ++ ++/** ++ * tnc_prev - find previous TNC entry. ++ * @c: UBIFS file-system description object ++ * @zn: znode is returned here ++ * @n: znode branch slot number is passed and returned here ++ * ++ * This function returns %0 if the previous TNC entry is found, %-ENOENT if ++ * there is no next entry, or a negative error code otherwise. ++ */ ++static int tnc_prev(struct ubifs_info *c, struct ubifs_znode **zn, int *n) ++{ ++ struct ubifs_znode *znode = *zn; ++ int nn = *n; ++ ++ if (nn > 0) { ++ *n = nn - 1; ++ return 0; ++ } ++ while (1) { ++ struct ubifs_znode *zp; ++ ++ zp = znode->parent; ++ if (!zp) ++ return -ENOENT; ++ nn = znode->iip - 1; ++ znode = zp; ++ if (nn >= 0) { ++ znode = get_znode(c, znode, nn); ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ while (znode->level != 0) { ++ nn = znode->child_cnt - 1; ++ znode = get_znode(c, znode, nn); ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ } ++ nn = znode->child_cnt - 1; ++ break; ++ } ++ } ++ *zn = znode; ++ *n = nn; ++ return 0; ++} ++ ++/** ++ * resolve_collision - resolve a collision. ++ * @c: UBIFS file-system description object ++ * @key: key of a directory or extended attribute entry ++ * @zn: znode is returned here ++ * @n: zbranch number is passed and returned here ++ * @nm: name of the entry ++ * ++ * This function is called for "hashed" keys to make sure that the found key ++ * really corresponds to the looked up node (directory or extended attribute ++ * entry). It returns %1 and sets @zn and @n if the collision is resolved. ++ * %0 is returned if @nm is not found and @zn and @n are set to the previous ++ * entry, i.e. to the entry after which @nm could follow if it were in TNC. ++ * This means that @n may be set to %-1 if the leftmost key in @zn is the ++ * previous one. A negative error code is returned on failures. ++ */ ++static int resolve_collision(struct ubifs_info *c, const union ubifs_key *key, ++ struct ubifs_znode **zn, int *n, ++ const struct qstr *nm) ++{ ++ int err; ++ ++ err = matches_name(c, &(*zn)->zbranch[*n], nm); ++ if (unlikely(err < 0)) ++ return err; ++ if (err == NAME_MATCHES) ++ return 1; ++ ++ if (err == NAME_GREATER) { ++ /* Look left */ ++ while (1) { ++ err = tnc_prev(c, zn, n); ++ if (err == -ENOENT) { ++ ubifs_assert(*n == 0); ++ *n = -1; ++ return 0; ++ } ++ if (err < 0) ++ return err; ++ if (keys_cmp(c, &(*zn)->zbranch[*n].key, key)) { ++ /* ++ * We have found the branch after which we would ++ * like to insert, but inserting in this znode ++ * may still be wrong. Consider the following 3 ++ * znodes, in the case where we are resolving a ++ * collision with Key2. ++ * ++ * znode zp ++ * ---------------------- ++ * level 1 | Key0 | Key1 | ++ * ----------------------- ++ * | | ++ * znode za | | znode zb ++ * ------------ ------------ ++ * level 0 | Key0 | | Key2 | ++ * ------------ ------------ ++ * ++ * The lookup finds Key2 in znode zb. Lets say ++ * there is no match and the name is greater so ++ * we look left. When we find Key0, we end up ++ * here. If we return now, we will insert into ++ * znode za at slot n = 1. But that is invalid ++ * according to the parent's keys. Key2 must ++ * be inserted into znode zb. ++ * ++ * Note, this problem is not relevant for the ++ * case when we go right, because ++ * 'tnc_insert()' would correct the parent key. ++ */ ++ if (*n == (*zn)->child_cnt - 1) { ++ err = tnc_next(c, zn, n); ++ if (err) { ++ /* Should be impossible */ ++ ubifs_assert(0); ++ if (err == -ENOENT) ++ err = -EINVAL; ++ return err; ++ } ++ ubifs_assert(*n == 0); ++ *n = -1; ++ } ++ return 0; ++ } ++ err = matches_name(c, &(*zn)->zbranch[*n], nm); ++ if (err < 0) ++ return err; ++ if (err == NAME_LESS) ++ return 0; ++ if (err == NAME_MATCHES) ++ return 1; ++ ubifs_assert(err == NAME_GREATER); ++ } ++ } else { ++ int nn = *n; ++ struct ubifs_znode *znode = *zn; ++ ++ /* Look right */ ++ while (1) { ++ err = tnc_next(c, &znode, &nn); ++ if (err == -ENOENT) ++ return 0; ++ if (err < 0) ++ return err; ++ if (keys_cmp(c, &znode->zbranch[nn].key, key)) ++ return 0; ++ err = matches_name(c, &znode->zbranch[nn], nm); ++ if (err < 0) ++ return err; ++ if (err == NAME_GREATER) ++ return 0; ++ *zn = znode; ++ *n = nn; ++ if (err == NAME_MATCHES) ++ return 1; ++ ubifs_assert(err == NAME_LESS); ++ } ++ } ++} ++ ++/** ++ * fallible_matches_name - determine if a dent matches a given name. ++ * @c: UBIFS file-system description object ++ * @zbr: zbranch of dent ++ * @nm: name to match ++ * ++ * This is a "fallible" version of 'matches_name()' function which does not ++ * panic if the direntry/xentry referred by @zbr does not exist on the media. ++ * ++ * This function checks if xentry/direntry referred by zbranch @zbr matches name ++ * @nm. Returns %NAME_MATCHES it does, %NAME_LESS if the name referred by @zbr ++ * is less than @nm, %NAME_GREATER if it is greater than @nm, and @NOT_ON_MEDIA ++ * if xentry/direntry referred by @zbr does not exist on the media. A negative ++ * error code is returned in case of failure. ++ */ ++static int fallible_matches_name(struct ubifs_info *c, ++ struct ubifs_zbranch *zbr, ++ const struct qstr *nm) ++{ ++ struct ubifs_dent_node *dent; ++ int nlen, err; ++ ++ /* If possible, match against the dent in the leaf node cache */ ++ if (!zbr->leaf) { ++ dent = kmalloc(zbr->len, GFP_NOFS); ++ if (!dent) ++ return -ENOMEM; ++ ++ err = fallible_read_node(c, &zbr->key, zbr, dent); ++ if (err < 0) ++ goto out_free; ++ if (err == 0) { ++ /* The node was not present */ ++ err = NOT_ON_MEDIA; ++ goto out_free; ++ } ++ ubifs_assert(err == 1); ++ ++ err = lnc_add_directly(c, zbr, dent); ++ if (err) ++ goto out_free; ++ } else ++ dent = zbr->leaf; ++ ++ nlen = le16_to_cpu(dent->nlen); ++ err = memcmp(dent->name, nm->name, min_t(int, nlen, nm->len)); ++ if (err == 0) { ++ if (nlen == nm->len) ++ return NAME_MATCHES; ++ else if (nlen < nm->len) ++ return NAME_LESS; ++ else ++ return NAME_GREATER; ++ } else if (err < 0) ++ return NAME_LESS; ++ else ++ return NAME_GREATER; ++ ++out_free: ++ kfree(dent); ++ return err; ++} ++ ++/** ++ * fallible_resolve_collision - resolve a collision even if nodes are missing. ++ * @c: UBIFS file-system description object ++ * @key: key ++ * @zn: znode is returned here ++ * @n: branch number is passed and returned here ++ * @nm: name of directory entry ++ * @adding: indicates caller is adding a key to the TNC ++ * ++ * This is a "fallible" version of the 'resolve_collision()' function which ++ * does not panic if one of the nodes referred to by TNC does not exist on the ++ * media. This may happen when replaying the journal if a deleted node was ++ * Garbage-collected and the commit was not done. A branch that refers to a node ++ * that is not present is called a dangling branch. The following are the return ++ * codes for this function: ++ * o if @nm was found, %1 is returned and @zn and @n are set to the found ++ * branch; ++ * o if we are @adding and @nm was not found, %0 is returned; ++ * o if we are not @adding and @nm was not found, but a dangling branch was ++ * found, then %1 is returned and @zn and @n are set to the dangling branch; ++ * o a negative error code is returned in case of failure. ++ */ ++static int fallible_resolve_collision(struct ubifs_info *c, ++ const union ubifs_key *key, ++ struct ubifs_znode **zn, int *n, ++ const struct qstr *nm, int adding) ++{ ++ struct ubifs_znode *o_znode = NULL, *znode = *zn; ++ int uninitialized_var(o_n), err, cmp, unsure = 0, nn = *n; ++ ++ cmp = fallible_matches_name(c, &znode->zbranch[nn], nm); ++ if (unlikely(cmp < 0)) ++ return cmp; ++ if (cmp == NAME_MATCHES) ++ return 1; ++ if (cmp == NOT_ON_MEDIA) { ++ o_znode = znode; ++ o_n = nn; ++ /* ++ * We are unlucky and hit a dangling branch straight away. ++ * Now we do not really know where to go to find the needed ++ * branch - to the left or to the right. Well, let's try left. ++ */ ++ unsure = 1; ++ } else if (!adding) ++ unsure = 1; /* Remove a dangling branch wherever it is */ ++ ++ if (cmp == NAME_GREATER || unsure) { ++ /* Look left */ ++ while (1) { ++ err = tnc_prev(c, zn, n); ++ if (err == -ENOENT) { ++ ubifs_assert(*n == 0); ++ *n = -1; ++ break; ++ } ++ if (err < 0) ++ return err; ++ if (keys_cmp(c, &(*zn)->zbranch[*n].key, key)) { ++ /* See comments in 'resolve_collision()' */ ++ if (*n == (*zn)->child_cnt - 1) { ++ err = tnc_next(c, zn, n); ++ if (err) { ++ /* Should be impossible */ ++ ubifs_assert(0); ++ if (err == -ENOENT) ++ err = -EINVAL; ++ return err; ++ } ++ ubifs_assert(*n == 0); ++ *n = -1; ++ } ++ break; ++ } ++ err = fallible_matches_name(c, &(*zn)->zbranch[*n], nm); ++ if (err < 0) ++ return err; ++ if (err == NAME_MATCHES) ++ return 1; ++ if (err == NOT_ON_MEDIA) { ++ o_znode = *zn; ++ o_n = *n; ++ continue; ++ } ++ if (!adding) ++ continue; ++ if (err == NAME_LESS) ++ break; ++ else ++ unsure = 0; ++ } ++ } ++ ++ if (cmp == NAME_LESS || unsure) { ++ /* Look right */ ++ *zn = znode; ++ *n = nn; ++ while (1) { ++ err = tnc_next(c, &znode, &nn); ++ if (err == -ENOENT) ++ break; ++ if (err < 0) ++ return err; ++ if (keys_cmp(c, &znode->zbranch[nn].key, key)) ++ break; ++ err = fallible_matches_name(c, &znode->zbranch[nn], nm); ++ if (err < 0) ++ return err; ++ if (err == NAME_GREATER) ++ break; ++ *zn = znode; ++ *n = nn; ++ if (err == NAME_MATCHES) ++ return 1; ++ if (err == NOT_ON_MEDIA) { ++ o_znode = znode; ++ o_n = nn; ++ } ++ } ++ } ++ ++ /* Never match a dangling branch when adding */ ++ if (adding || !o_znode) ++ return 0; ++ ++ dbg_mnt("dangling match LEB %d:%d len %d %s", ++ o_znode->zbranch[o_n].lnum, o_znode->zbranch[o_n].offs, ++ o_znode->zbranch[o_n].len, DBGKEY(key)); ++ *zn = o_znode; ++ *n = o_n; ++ return 1; ++} ++ ++/** ++ * matches_position - determine if a zbranch matches a given position. ++ * @zbr: zbranch of dent ++ * @lnum: LEB number of dent to match ++ * @offs: offset of dent to match ++ * ++ * This function returns %1 if @lnum:@offs matches, and %0 otherwise. ++ */ ++static int matches_position(struct ubifs_zbranch *zbr, int lnum, int offs) ++{ ++ if (zbr->lnum == lnum && zbr->offs == offs) ++ return 1; ++ else ++ return 0; ++} ++ ++/** ++ * resolve_collision_directly - resolve a collision directly. ++ * @c: UBIFS file-system description object ++ * @key: key of directory entry ++ * @zn: znode is passed and returned here ++ * @n: zbranch number is passed and returned here ++ * @lnum: LEB number of dent node to match ++ * @offs: offset of dent node to match ++ * ++ * This function is used for "hashed" keys to make sure the found directory or ++ * extended attribute entry node is what was looked for. It is used when the ++ * flash address of the right node is known (@lnum:@offs) which makes it much ++ * easier to resolve collisions (no need to read entries and match full ++ * names). This function returns %1 and sets @zn and @n if the collision is ++ * resolved, %0 if @lnum:@offs is not found and @zn and @n are set to the ++ * previous directory entry. Otherwise a negative error code is returned. ++ */ ++static int resolve_collision_directly(struct ubifs_info *c, ++ const union ubifs_key *key, ++ struct ubifs_znode **zn, int *n, ++ int lnum, int offs) ++{ ++ struct ubifs_znode *znode; ++ int nn, err; ++ ++ znode = *zn; ++ nn = *n; ++ if (matches_position(&znode->zbranch[nn], lnum, offs)) ++ return 1; ++ ++ /* Look left */ ++ while (1) { ++ err = tnc_prev(c, &znode, &nn); ++ if (err == -ENOENT) ++ break; ++ if (err < 0) ++ return err; ++ if (keys_cmp(c, &znode->zbranch[nn].key, key)) ++ break; ++ if (matches_position(&znode->zbranch[nn], lnum, offs)) { ++ *zn = znode; ++ *n = nn; ++ return 1; ++ } ++ } ++ ++ /* Look right */ ++ znode = *zn; ++ nn = *n; ++ while (1) { ++ err = tnc_next(c, &znode, &nn); ++ if (err == -ENOENT) ++ return 0; ++ if (err < 0) ++ return err; ++ if (keys_cmp(c, &znode->zbranch[nn].key, key)) ++ return 0; ++ *zn = znode; ++ *n = nn; ++ if (matches_position(&znode->zbranch[nn], lnum, offs)) ++ return 1; ++ } ++} ++ ++/** ++ * dirty_cow_bottom_up - dirty a znode and its ancestors. ++ * @c: UBIFS file-system description object ++ * @znode: znode to dirty ++ * ++ * If we do not have a unique key that resides in a znode, then we cannot ++ * dirty that znode from the top down (i.e. by using lookup_level0_dirty) ++ * This function records the path back to the last dirty ancestor, and then ++ * dirties the znodes on that path. ++ */ ++static struct ubifs_znode *dirty_cow_bottom_up(struct ubifs_info *c, ++ struct ubifs_znode *znode) ++{ ++ struct ubifs_znode *zp; ++ int *path = c->bottom_up_buf, p = 0; ++ ++ ubifs_assert(c->zroot.znode); ++ ubifs_assert(znode); ++ if (c->zroot.znode->level > BOTTOM_UP_HEIGHT) { ++ kfree(c->bottom_up_buf); ++ c->bottom_up_buf = kmalloc(c->zroot.znode->level * sizeof(int), ++ GFP_NOFS); ++ if (!c->bottom_up_buf) ++ return ERR_PTR(-ENOMEM); ++ path = c->bottom_up_buf; ++ } ++ if (c->zroot.znode->level) { ++ /* Go up until parent is dirty */ ++ while (1) { ++ int n; ++ ++ zp = znode->parent; ++ if (!zp) ++ break; ++ n = znode->iip; ++ ubifs_assert(p < c->zroot.znode->level); ++ path[p++] = n; ++ if (!zp->cnext && ubifs_zn_dirty(znode)) ++ break; ++ znode = zp; ++ } ++ } ++ ++ /* Come back down, dirtying as we go */ ++ while (1) { ++ struct ubifs_zbranch *zbr; ++ ++ zp = znode->parent; ++ if (zp) { ++ ubifs_assert(path[p - 1] >= 0); ++ ubifs_assert(path[p - 1] < zp->child_cnt); ++ zbr = &zp->zbranch[path[--p]]; ++ znode = dirty_cow_znode(c, zbr); ++ } else { ++ ubifs_assert(znode == c->zroot.znode); ++ znode = dirty_cow_znode(c, &c->zroot); ++ } ++ if (unlikely(IS_ERR(znode)) || !p) ++ break; ++ ubifs_assert(path[p - 1] >= 0); ++ ubifs_assert(path[p - 1] < znode->child_cnt); ++ znode = znode->zbranch[path[p - 1]].znode; ++ } ++ ++ return znode; ++} ++ ++/** ++ * lookup_level0 - search for zero-level znode. ++ * @c: UBIFS file-system description object ++ * @key: key to lookup ++ * @zn: znode is returned here ++ * @n: znode branch slot number is returned here ++ * ++ * This function looks up the TNC tree and search for zero-level znode which ++ * refers key @key. The found zero-level znode is returned in @zn. There are 3 ++ * cases: ++ * o exact match, i.e. the found zero-level znode contains key @key, then %1 ++ * is returned and slot number of the matched branch is stored in @n; ++ * o not exact match, which means that zero-level znode does not contain ++ * @key, then %0 is returned and slot number of the closed branch is stored ++ * in @n; ++ * o @key is so small that it is even less than the lowest key of the ++ * leftmost zero-level node, then %0 is returned and %0 is stored in @n. ++ * ++ * Note, when the TNC tree is traversed, some znodes may be absent, then this ++ * function reads corresponding indexing nodes and inserts them to TNC. In ++ * case of failure, a negative error code is returned. ++ */ ++static int lookup_level0(struct ubifs_info *c, const union ubifs_key *key, ++ struct ubifs_znode **zn, int *n) ++{ ++ int err, exact; ++ struct ubifs_znode *znode; ++ unsigned long time = get_seconds(); ++ ++ dbg_tnc("search key %s", DBGKEY(key)); ++ ++ znode = c->zroot.znode; ++ if (unlikely(!znode)) { ++ znode = load_znode(c, &c->zroot, NULL, 0); ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ } ++ ++ znode->time = time; ++ ++ while (1) { ++ struct ubifs_zbranch *zbr; ++ ++ exact = ubifs_search_zbranch(c, znode, key, n); ++ ++ if (znode->level == 0) ++ break; ++ ++ if (*n < 0) ++ *n = 0; ++ zbr = &znode->zbranch[*n]; ++ ++ if (zbr->znode) { ++ znode->time = time; ++ znode = zbr->znode; ++ continue; ++ } ++ ++ /* znode is not in TNC cache, load it from the media */ ++ znode = load_znode(c, zbr, znode, *n); ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ } ++ ++ *zn = znode; ++ if (exact || !is_hash_key(c, key) || *n != -1) { ++ dbg_tnc("found %d, lvl %d, n %d", exact, znode->level, *n); ++ return exact; ++ } ++ ++ /* ++ * Here is a tricky place. We have not found the key and this is a ++ * "hashed" key, which may collide. The rest of the code deals with ++ * situations like this: ++ * ++ * | 3 | 5 | ++ * / \ ++ * | 3 | 5 | | 6 | 7 | (x) ++ * ++ * Or more a complex example: ++ * ++ * | 1 | 5 | ++ * / \ ++ * | 1 | 3 | | 5 | 8 | ++ * \ / ++ * | 5 | 5 | | 6 | 7 | (x) ++ * ++ * In the examples, if we are looking for key "5", we may reach nodes ++ * marked with "(x)". In this case what we have do is to look at the ++ * left and see if there is "5" key there. If there is, we have to ++ * return it. ++ * ++ * Note, this whole situation is possible because we allow to have ++ * elements which are equivalent to the next key in the parent in the ++ * children of current znode. For example, this happens if we split a ++ * znode like this: | 3 | 5 | 5 | 6 | 7 |, which results in something ++ * like this: ++ * | 3 | 5 | ++ * / \ ++ * | 3 | 5 | | 5 | 6 | 7 | ++ * ^ ++ * And this becomes what is at the first "picture" after key "5" marked ++ * with "^" is removed. What could be done is we could prohibit ++ * splitting in the middle of the colliding sequence. Also, when ++ * removing the leftmost key, we would have to correct the key of the ++ * parent node, which would introduce additional complications. Namely, ++ * if we changed the the leftmost key of the parent znode, the garbage ++ * collector would be unable to find it (GC is doing this when GC'ing ++ * indexing LEBs). Although we already have an additional RB-tree where ++ * we save such changed znodes (see 'ins_clr_old_idx_znode()') until ++ * after the commit. But anyway, this does not look easy to implement ++ * so we did not try this. ++ */ ++ err = tnc_prev(c, &znode, n); ++ if (err == -ENOENT) { ++ dbg_tnc("found 0, lvl %d, n -1", znode->level); ++ *n = -1; ++ return 0; ++ } ++ if (unlikely(err < 0)) ++ return err; ++ if (keys_cmp(c, key, &znode->zbranch[*n].key)) { ++ dbg_tnc("found 0, lvl %d, n -1", znode->level); ++ *n = -1; ++ return 0; ++ } ++ ++ dbg_tnc("found 1, lvl %d, n %d", znode->level, *n); ++ *zn = znode; ++ return 1; ++} ++ ++/** ++ * lookup_level0_dirty - search for zero-level znode dirtying. ++ * @c: UBIFS file-system description object ++ * @key: key to lookup ++ * @zn: znode is returned here ++ * @n: znode branch slot number is returned here ++ * ++ * This function looks up the TNC tree and search for zero-level znode which ++ * refers key @key. The found zero-level znode is returned in @zn. There are 3 ++ * cases: ++ * o exact match, i.e. the found zero-level znode contains key @key, then %1 ++ * is returned and slot number of the matched branch is stored in @n; ++ * o not exact match, which means that zero-level znode does not contain @key ++ * then %0 is returned and slot number of the closed branch is stored in ++ * @n; ++ * o @key is so small that it is even less than the lowest key of the ++ * leftmost zero-level node, then %0 is returned and %-1 is stored in @n. ++ * ++ * Additionally all znodes in the path from the root to the located zero-level ++ * znode are marked as dirty. ++ * ++ * Note, when the TNC tree is traversed, some znodes may be absent, then this ++ * function reads corresponding indexing nodes and inserts them to TNC. In ++ * case of failure, a negative error code is returned. ++ */ ++static int lookup_level0_dirty(struct ubifs_info *c, const union ubifs_key *key, ++ struct ubifs_znode **zn, int *n) ++{ ++ int err, exact; ++ struct ubifs_znode *znode; ++ unsigned long time = get_seconds(); ++ ++ dbg_tnc("search and dirty key %s", DBGKEY(key)); ++ ++ znode = c->zroot.znode; ++ if (unlikely(!znode)) { ++ znode = load_znode(c, &c->zroot, NULL, 0); ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ } ++ ++ znode = dirty_cow_znode(c, &c->zroot); ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ ++ znode->time = time; ++ ++ while (1) { ++ struct ubifs_zbranch *zbr; ++ ++ exact = ubifs_search_zbranch(c, znode, key, n); ++ ++ if (znode->level == 0) ++ break; ++ ++ if (*n < 0) ++ *n = 0; ++ zbr = &znode->zbranch[*n]; ++ ++ if (zbr->znode) { ++ znode->time = time; ++ znode = dirty_cow_znode(c, zbr); ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ continue; ++ } ++ ++ /* znode is not in TNC cache, load it from the media */ ++ znode = load_znode(c, zbr, znode, *n); ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ znode = dirty_cow_znode(c, zbr); ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ } ++ ++ *zn = znode; ++ if (exact || !is_hash_key(c, key) || *n != -1) { ++ dbg_tnc("found %d, lvl %d, n %d", exact, znode->level, *n); ++ return exact; ++ } ++ ++ /* ++ * See huge comment at 'lookup_level0_dirty()' what is the rest of the ++ * code. ++ */ ++ err = tnc_prev(c, &znode, n); ++ if (err == -ENOENT) { ++ *n = -1; ++ dbg_tnc("found 0, lvl %d, n -1", znode->level); ++ return 0; ++ } ++ if (unlikely(err < 0)) ++ return err; ++ if (keys_cmp(c, key, &znode->zbranch[*n].key)) { ++ *n = -1; ++ dbg_tnc("found 0, lvl %d, n -1", znode->level); ++ return 0; ++ } ++ ++ if (znode->cnext || !ubifs_zn_dirty(znode)) { ++ znode = dirty_cow_bottom_up(c, znode); ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ } ++ ++ dbg_tnc("found 1, lvl %d, n %d", znode->level, *n); ++ *zn = znode; ++ return 1; ++} ++ ++/** ++ * ubifs_tnc_lookup - look up a file-system node. ++ * @c: UBIFS file-system description object ++ * @key: node key to lookup ++ * @node: the node is returned here ++ * ++ * This function look up and reads node with key @key. The caller has to make ++ * sure the @node buffer is large enough to fit the node. Returns zero in case ++ * of success, %-ENOENT if the node was not found, and a negative error code in ++ * case of failure. ++ */ ++int ubifs_tnc_lookup(struct ubifs_info *c, const union ubifs_key *key, ++ void *node) ++{ ++ int found, n, err; ++ struct ubifs_znode *znode; ++ struct ubifs_zbranch zbr, *zt; ++ ++ mutex_lock(&c->tnc_mutex); ++ found = lookup_level0(c, key, &znode, &n); ++ if (!found) { ++ err = -ENOENT; ++ goto out; ++ } else if (found < 0) { ++ err = found; ++ goto out; ++ } ++ zt = &znode->zbranch[n]; ++ if (is_hash_key(c, key)) { ++ /* ++ * In this case the leaf node cache gets used, so we pass the ++ * address of the zbranch and keep the mutex locked ++ */ ++ err = tnc_read_node_nm(c, zt, node); ++ goto out; ++ } ++ zbr = znode->zbranch[n]; ++ mutex_unlock(&c->tnc_mutex); ++ ++ err = tnc_read_node(c, &zbr, node); ++ return err; ++ ++out: ++ mutex_unlock(&c->tnc_mutex); ++ return err; ++} ++ ++/** ++ * ubifs_tnc_locate - look up a file-system node and return it and its location. ++ * @c: UBIFS file-system description object ++ * @key: node key to lookup ++ * @node: the node is returned here ++ * @lnum: LEB number is returned here ++ * @offs: offset is returned here ++ * ++ * This function is the same as 'ubifs_tnc_lookup()' but it returns the node ++ * location also. See 'ubifs_tnc_lookup()'. ++ */ ++int ubifs_tnc_locate(struct ubifs_info *c, const union ubifs_key *key, ++ void *node, int *lnum, int *offs) ++{ ++ int found, n, err; ++ struct ubifs_znode *znode; ++ struct ubifs_zbranch zbr, *zt; ++ ++ mutex_lock(&c->tnc_mutex); ++ found = lookup_level0(c, key, &znode, &n); ++ if (!found) { ++ err = -ENOENT; ++ goto out; ++ } else if (found < 0) { ++ err = found; ++ goto out; ++ } ++ zt = &znode->zbranch[n]; ++ if (is_hash_key(c, key)) { ++ /* ++ * In this case the leaf node cache gets used, so we pass the ++ * address of the zbranch and keep the mutex locked ++ */ ++ *lnum = zt->lnum; ++ *offs = zt->offs; ++ err = tnc_read_node_nm(c, zt, node); ++ goto out; ++ } ++ zbr = znode->zbranch[n]; ++ mutex_unlock(&c->tnc_mutex); ++ ++ *lnum = zbr.lnum; ++ *offs = zbr.offs; ++ ++ err = tnc_read_node(c, &zbr, node); ++ return err; ++ ++out: ++ mutex_unlock(&c->tnc_mutex); ++ return err; ++} ++ ++/** ++ * do_lookup_nm- look up a "hashed" node. ++ * directory entry file-system node. ++ * @c: UBIFS file-system description object ++ * @key: node key to lookup ++ * @node: the node is returned here ++ * @nm: node name ++ * ++ * This function look up and reads a node which contains name hash in the key. ++ * Since the hash may have collisions, there may be many nodes with the same ++ * key, so we have to sequentially look to all of them until the needed one is ++ * found. This function returns zero in case of success, %-ENOENT if the node ++ * was not found, and a negative error code in case of failure. ++ */ ++static int do_lookup_nm(struct ubifs_info *c, const union ubifs_key *key, ++ void *node, const struct qstr *nm) ++{ ++ int found, n, err; ++ struct ubifs_znode *znode; ++ struct ubifs_zbranch zbr; ++ ++ dbg_tnc("name '%.*s' key %s", nm->len, nm->name, DBGKEY(key)); ++ mutex_lock(&c->tnc_mutex); ++ found = lookup_level0(c, key, &znode, &n); ++ if (!found) { ++ err = -ENOENT; ++ goto out_unlock; ++ } else if (found < 0) { ++ err = found; ++ goto out_unlock; ++ } ++ ++ ubifs_assert(n >= 0); ++ ++ err = resolve_collision(c, key, &znode, &n, nm); ++ dbg_tnc("rc returned %d, znode %p, n %d", err, znode, n); ++ if (unlikely(err < 0)) ++ goto out_unlock; ++ if (err == 0) { ++ err = -ENOENT; ++ goto out_unlock; ++ } ++ ++ zbr = znode->zbranch[n]; ++ mutex_unlock(&c->tnc_mutex); ++ ++ err = tnc_read_node_nm(c, &zbr, node); ++ return err; ++ ++out_unlock: ++ mutex_unlock(&c->tnc_mutex); ++ return err; ++} ++ ++/** ++ * ubifs_tnc_lookup_nm- look up a "hashed" node. ++ * directory entry file-system node. ++ * @c: UBIFS file-system description object ++ * @key: node key to lookup ++ * @node: the node is returned here ++ * @nm: node name ++ * ++ * This function look up and reads a node which contains name hash in the key. ++ * Since the hash may have collisions, there may be many nodes with the same ++ * key, so we have to sequentially look to all of them until the needed one is ++ * found. This function returns zero in case of success, %-ENOENT if the node ++ * was not found, and a negative error code in case of failure. ++ */ ++int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key, ++ void *node, const struct qstr *nm) ++{ ++ int err, len; ++ const struct ubifs_dent_node *dent = node; ++ ++ /* ++ * We assume that in most of the cases there are no name collisions and ++ * 'ubifs_tnc_lookup()' returns us the right direntry. ++ */ ++ err = ubifs_tnc_lookup(c, key, node); ++ if (err) ++ return err; ++ ++ len = le16_to_cpu(dent->nlen); ++ if (nm->len == len && !memcmp(dent->name, nm->name, len)) ++ return 0; ++ ++ /* ++ * Unluckily, there are hash collisions and we have to iterate over ++ * them look at each direntry with colliding name hash sequentially. ++ */ ++ return do_lookup_nm(c, key, node, nm); ++} ++ ++/** ++ * correct_parent_keys - correct parent znodes' keys. ++ * @c: UBIFS file-system description object ++ * @znode: znode to correct parent znodes for ++ * ++ * This is a helper function for 'tnc_insert()'. When the key of the leftmost ++ * zbranch changes, keys of parent znodes have to be corrected. This helper ++ * function is called in such situations and corrects the keys if needed. ++ */ ++static void correct_parent_keys(const struct ubifs_info *c, ++ struct ubifs_znode *znode) ++{ ++ union ubifs_key *key, *key1; ++ ++ ubifs_assert(znode->parent); ++ ubifs_assert(znode->iip == 0); ++ ++ key = &znode->zbranch[0].key; ++ key1 = &znode->parent->zbranch[0].key; ++ ++ while (keys_cmp(c, key, key1) < 0) { ++ key_copy(c, key, key1); ++ znode = znode->parent; ++ if (!znode->parent || znode->iip) ++ break; ++ key1 = &znode->parent->zbranch[0].key; ++ } ++} ++ ++/** ++ * insert_zbranch - insert a zbranch into a znode. ++ * @znode: znode into which to insert ++ * @zbr: zbranch to insert ++ * @n: slot number to insert to ++ * ++ * This is a helper function for 'tnc_insert()'. UBIFS does not allow "gaps" in ++ * znode's array of zbranches and keeps zbranches consolidated, so when a new ++ * zbranch has to be inserted to the @znode->zbranches[]' array at the @n-th ++ * slot, zbranches starting from @n have to be moved right. ++ */ ++static void insert_zbranch(struct ubifs_znode *znode, ++ const struct ubifs_zbranch *zbr, int n) ++{ ++ int i; ++ ++ ubifs_assert(ubifs_zn_dirty(znode)); ++ ++ if (znode->level) { ++ for (i = znode->child_cnt; i > n; i--) { ++ znode->zbranch[i] = znode->zbranch[i - 1]; ++ if (znode->zbranch[i].znode) ++ znode->zbranch[i].znode->iip = i; ++ } ++ if (zbr->znode) ++ zbr->znode->iip = n; ++ } else ++ for (i = znode->child_cnt; i > n; i--) ++ znode->zbranch[i] = znode->zbranch[i - 1]; ++ ++ znode->zbranch[n] = *zbr; ++ znode->child_cnt += 1; ++ ++ /* ++ * After inserting at slot zero, the lower bound of the key range of ++ * this znode may have changed. If this znode is subsequently split ++ * then the upper bound of the key range may change, and furthermore ++ * it could change to be lower than the original lower bound. If that ++ * happens, then it will no longer be possible to find this znode in the ++ * TNC using the key from the index node on flash. That is bad because ++ * if it is not found, we will assume it is obsolete and may overwrite ++ * it. Then if there is an unclean unmount, we will start using the ++ * old index which will be broken. ++ * ++ * So we first mark znodes that have insertions at slot zero, and then ++ * if they are split we add their lnum/offs to the old_idx tree. ++ */ ++ if (n == 0) ++ znode->alt = 1; ++} ++ ++/** ++ * tnc_insert - insert a node into TNC. ++ * @c: UBIFS file-system description object ++ * @znode: znode to insert into ++ * @zbr: branch to insert ++ * @n: slot number to insert new zbranch to ++ * ++ * This function inserts a new node described by @zbr into znode @znode. If ++ * znode does not have a free slot for new zbranch, it is split. Parent znodes ++ * are splat as well if needed. Returns zero in case of success or a negative ++ * error code in case of failure. ++ */ ++static int tnc_insert(struct ubifs_info *c, struct ubifs_znode *znode, ++ struct ubifs_zbranch *zbr, int n) ++{ ++ struct ubifs_znode *zn, *zi, *zp; ++ int i, keep, move, appending = 0; ++ union ubifs_key *key = &zbr->key; ++ ++ ubifs_assert(n >= 0 && n <= c->fanout); ++ ++ /* Implement naive insert for now */ ++again: ++ zp = znode->parent; ++ if (znode->child_cnt < c->fanout) { ++ ubifs_assert(n != c->fanout); ++ dbg_tnc("inserted at %d level %d, key %s", n, znode->level, ++ DBGKEY(key)); ++ ++ insert_zbranch(znode, zbr, n); ++ ++ /* Ensure parent's key is correct */ ++ if (n == 0 && zp && znode->iip == 0) ++ correct_parent_keys(c, znode); ++ ++ return 0; ++ } ++ ++ /* ++ * Unfortunately, @znode does not have more empty slots and we have to ++ * split it. ++ */ ++ dbg_tnc("splitting level %d, key %s", znode->level, DBGKEY(key)); ++ ++ if (znode->alt) ++ /* ++ * We can no longer be sure of finding this znode by key, so we ++ * record it in the old_idx tree. ++ */ ++ ins_clr_old_idx_znode(c, znode); ++ ++ zn = kzalloc(c->max_znode_sz, GFP_NOFS); ++ if (!zn) ++ return -ENOMEM; ++ zn->parent = zp; ++ zn->level = znode->level; ++ ++ /* Decide where to split */ ++ if (znode->level == 0 && n == c->fanout && ++ key_type(c, key) == UBIFS_DATA_KEY) { ++ union ubifs_key *key1; ++ ++ /* ++ * If this is an inode which is being appended - do not split ++ * it because no other zbranches can be inserted between ++ * zbranches of consecutive data nodes anyway. ++ */ ++ key1 = &znode->zbranch[n - 1].key; ++ if (key_ino(c, key1) == key_ino(c, key) && ++ key_type(c, key1) == UBIFS_DATA_KEY && ++ key_block(c, key1) == key_block(c, key) - 1) ++ appending = 1; ++ } ++ ++ if (appending) { ++ keep = c->fanout; ++ move = 0; ++ } else { ++ keep = (c->fanout + 1) / 2; ++ move = c->fanout - keep; ++ } ++ ++ /* ++ * Although we don't at present, we could look at the neighbors and see ++ * if we can move some zbranches there. ++ */ ++ ++ if (n < keep) { ++ /* Insert into existing znode */ ++ zi = znode; ++ move += 1; ++ keep -= 1; ++ } else { ++ /* Insert into new znode */ ++ zi = zn; ++ n -= keep; ++ /* Re-parent */ ++ if (zn->level != 0) ++ zbr->znode->parent = zn; ++ } ++ ++ __set_bit(DIRTY_ZNODE, &zn->flags); ++ atomic_long_inc(&c->dirty_zn_cnt); ++ ++ zn->child_cnt = move; ++ znode->child_cnt = keep; ++ ++ dbg_tnc("moving %d, keeping %d", move, keep); ++ ++ /* Move zbranch */ ++ for (i = 0; i < move; i++) { ++ zn->zbranch[i] = znode->zbranch[keep + i]; ++ /* Re-parent */ ++ if (zn->level != 0) ++ if (zn->zbranch[i].znode) { ++ zn->zbranch[i].znode->parent = zn; ++ zn->zbranch[i].znode->iip = i; ++ } ++ } ++ ++ /* Insert new key and branch */ ++ dbg_tnc("inserting at %d level %d, key %s", n, zn->level, DBGKEY(key)); ++ ++ insert_zbranch(zi, zbr, n); ++ ++ /* Insert new znode (produced by spitting) into the parent */ ++ if (zp) { ++ i = n; ++ /* Locate insertion point */ ++ n = znode->iip + 1; ++ if (appending && n != c->fanout) ++ appending = 0; ++ ++ if (i == 0 && zi == znode && znode->iip == 0) ++ correct_parent_keys(c, znode); ++ ++ /* Tail recursion */ ++ zbr->key = zn->zbranch[0].key; ++ zbr->znode = zn; ++ zbr->lnum = 0; ++ zbr->offs = 0; ++ zbr->len = 0; ++ znode = zp; ++ ++ goto again; ++ } ++ ++ /* We have to split root znode */ ++ dbg_tnc("creating new zroot at level %d", znode->level + 1); ++ ++ zi = kzalloc(c->max_znode_sz, GFP_NOFS); ++ if (!zi) ++ return -ENOMEM; ++ ++ zi->child_cnt = 2; ++ zi->level = znode->level + 1; ++ ++ __set_bit(DIRTY_ZNODE, &zi->flags); ++ atomic_long_inc(&c->dirty_zn_cnt); ++ ++ zi->zbranch[0].key = znode->zbranch[0].key; ++ zi->zbranch[0].znode = znode; ++ zi->zbranch[0].lnum = c->zroot.lnum; ++ zi->zbranch[0].offs = c->zroot.offs; ++ zi->zbranch[0].len = c->zroot.len; ++ zi->zbranch[1].key = zn->zbranch[0].key; ++ zi->zbranch[1].znode = zn; ++ ++ c->zroot.lnum = 0; ++ c->zroot.offs = 0; ++ c->zroot.len = 0; ++ c->zroot.znode = zi; ++ ++ zn->parent = zi; ++ zn->iip = 1; ++ znode->parent = zi; ++ znode->iip = 0; ++ ++ return 0; ++} ++ ++/** ++ * ubifs_tnc_add - add a node to TNC. ++ * @c: UBIFS file-system description object ++ * @key: key to add ++ * @lnum: LEB number of node ++ * @offs: node offset ++ * @len: node length ++ * ++ * This function adds a node with key @key to TNC. The node may be new or it may ++ * obsolete some existing one. Returns %0 on success or negative error code on ++ * failure. ++ */ ++int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum, ++ int offs, int len) ++{ ++ int found, n, err = 0; ++ struct ubifs_znode *znode; ++ ++ mutex_lock(&c->tnc_mutex); ++ dbg_tnc("%d:%d, len %d, key %s", lnum, offs, len, DBGKEY(key)); ++ found = lookup_level0_dirty(c, key, &znode, &n); ++ if (!found) { ++ struct ubifs_zbranch zbr; ++ ++ zbr.znode = NULL; ++ zbr.lnum = lnum; ++ zbr.offs = offs; ++ zbr.len = len; ++ key_copy(c, key, &zbr.key); ++ err = tnc_insert(c, znode, &zbr, n + 1); ++ } else if (found == 1) { ++ struct ubifs_zbranch *zbr = &znode->zbranch[n]; ++ ++ lnc_free(zbr); ++ err = ubifs_add_dirt(c, zbr->lnum, zbr->len); ++ zbr->lnum = lnum; ++ zbr->offs = offs; ++ zbr->len = len; ++ } else ++ err = found; ++ if (!err) ++ err = dbg_check_tnc(c, 0); ++ mutex_unlock(&c->tnc_mutex); ++ ++ return err; ++} ++ ++/** ++ * ubifs_tnc_replace - replace a node in the TNC only if the old node is found. ++ * @c: UBIFS file-system description object ++ * @key: key to add ++ * @old_lnum: LEB number of old node ++ * @old_offs: old node offset ++ * @lnum: LEB number of node ++ * @offs: node offset ++ * @len: node length ++ * ++ * This function replaces a node with key @key in the TNC only if the old node ++ * is found. This function is called by garbage collection when node are moved. ++ * Returns %0 on success or negative error code on failure. ++ */ ++int ubifs_tnc_replace(struct ubifs_info *c, const union ubifs_key *key, ++ int old_lnum, int old_offs, int lnum, int offs, int len) ++{ ++ int found, n, err = 0; ++ struct ubifs_znode *znode; ++ ++ mutex_lock(&c->tnc_mutex); ++ dbg_tnc("old LEB %d:%d, new LEB %d:%d, len %d, key %s", old_lnum, ++ old_offs, lnum, offs, len, DBGKEY(key)); ++ found = lookup_level0_dirty(c, key, &znode, &n); ++ if (found < 0) { ++ err = found; ++ goto out_unlock; ++ } ++ ++ if (found == 1) { ++ struct ubifs_zbranch *zbr = &znode->zbranch[n]; ++ ++ found = 0; ++ if (zbr->lnum == old_lnum && zbr->offs == old_offs) { ++ lnc_free(zbr); ++ err = ubifs_add_dirt(c, zbr->lnum, zbr->len); ++ if (err) ++ goto out_unlock; ++ zbr->lnum = lnum; ++ zbr->offs = offs; ++ zbr->len = len; ++ found = 1; ++ } else if (is_hash_key(c, key)) { ++ found = resolve_collision_directly(c, key, &znode, &n, ++ old_lnum, old_offs); ++ dbg_tnc("rc returned %d, znode %p, n %d, LEB %d:%d", ++ found, znode, n, old_lnum, old_offs); ++ if (found < 0) { ++ err = found; ++ goto out_unlock; ++ } ++ ++ if (found) { ++ /* Ensure the znode is dirtied */ ++ if (znode->cnext || !ubifs_zn_dirty(znode)) { ++ znode = dirty_cow_bottom_up(c, ++ znode); ++ if (IS_ERR(znode)) { ++ err = PTR_ERR(znode); ++ goto out_unlock; ++ } ++ } ++ zbr = &znode->zbranch[n]; ++ lnc_free(zbr); ++ err = ubifs_add_dirt(c, zbr->lnum, ++ zbr->len); ++ if (err) ++ goto out_unlock; ++ zbr->lnum = lnum; ++ zbr->offs = offs; ++ zbr->len = len; ++ } ++ } ++ } ++ ++ if (!found) ++ err = ubifs_add_dirt(c, lnum, len); ++ ++ if (!err) ++ err = dbg_check_tnc(c, 0); ++ ++out_unlock: ++ mutex_unlock(&c->tnc_mutex); ++ return err; ++} ++ ++/** ++ * ubifs_tnc_add_nm - add a "hashed" node to TNC. ++ * @c: UBIFS file-system description object ++ * @key: key to add ++ * @lnum: LEB number of node ++ * @offs: node offset ++ * @len: node length ++ * @nm: node name ++ * ++ * This is the same as 'ubifs_tnc_add()' but it should be used with keys which ++ * may have collisions, like directory entry keys. ++ */ ++int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key, ++ int lnum, int offs, int len, const struct qstr *nm) ++{ ++ int found, n, err = 0; ++ struct ubifs_znode *znode; ++ ++ mutex_lock(&c->tnc_mutex); ++ dbg_tnc("LEB %d:%d, name '%.*s', key %s", lnum, offs, nm->len, nm->name, ++ DBGKEY(key)); ++ found = lookup_level0_dirty(c, key, &znode, &n); ++ if (found < 0) { ++ err = found; ++ goto out_unlock; ++ } ++ ++ if (found == 1) { ++ if (c->replaying) ++ found = fallible_resolve_collision(c, key, &znode, &n, ++ nm, 1); ++ else ++ found = resolve_collision(c, key, &znode, &n, nm); ++ dbg_tnc("rc returned %d, znode %p, n %d", found, znode, n); ++ if (found < 0) { ++ err = found; ++ goto out_unlock; ++ } ++ ++ /* Ensure the znode is dirtied */ ++ if (znode->cnext || !ubifs_zn_dirty(znode)) { ++ znode = dirty_cow_bottom_up(c, znode); ++ if (IS_ERR(znode)) { ++ err = PTR_ERR(znode); ++ goto out_unlock; ++ } ++ } ++ ++ if (found == 1) { ++ struct ubifs_zbranch *zbr = &znode->zbranch[n]; ++ ++ lnc_free(zbr); ++ err = ubifs_add_dirt(c, zbr->lnum, zbr->len); ++ zbr->lnum = lnum; ++ zbr->offs = offs; ++ zbr->len = len; ++ goto out_unlock; ++ } ++ } ++ ++ if (!found) { ++ struct ubifs_zbranch zbr; ++ ++ zbr.znode = NULL; ++ zbr.lnum = lnum; ++ zbr.offs = offs; ++ zbr.len = len; ++ key_copy(c, key, &zbr.key); ++ err = tnc_insert(c, znode, &zbr, n + 1); ++ if (err) ++ goto out_unlock; ++ if (c->replaying && c->replay_sqnum < c->cs_sqnum) { ++ /* ++ * This node was moved by garbage collection. We can ++ * tell because it is in the journal but it has a ++ * sequence number earlier than the last commit-start. ++ * We did not find it in the index so there may be a ++ * dangling branch still in the index. So we remove it ++ * by passing 'ubifs_tnc_remove_nm()' the same key but ++ * an unmatchable name. ++ */ ++ struct qstr noname = { .len = 0, .name = "" }; ++ ++ err = dbg_check_tnc(c, 0); ++ mutex_unlock(&c->tnc_mutex); ++ if (err) ++ return err; ++ return ubifs_tnc_remove_nm(c, key, &noname); ++ } ++ } ++ ++out_unlock: ++ if (!err) ++ err = dbg_check_tnc(c, 0); ++ mutex_unlock(&c->tnc_mutex); ++ return err; ++} ++ ++/** ++ * tnc_delete - delete a znode form TNC. ++ * @c: UBIFS file-system description object ++ * @znode: znode to delete from ++ * @n: zbranch slot number to delete ++ * ++ * This function deletes a leaf node from @n-th slot of @znode. Returns zero in ++ * case of success and a negative error code in case of failure. ++ */ ++static int tnc_delete(struct ubifs_info *c, struct ubifs_znode *znode, int n) ++{ ++ struct ubifs_zbranch *zbr; ++ struct ubifs_znode *zp; ++ int i, err; ++ ++ /* Delete without merge for now */ ++ ubifs_assert(znode->level == 0); ++ ubifs_assert(n >= 0 && n < c->fanout); ++ dbg_tnc("deleting %s", DBGKEY(&znode->zbranch[n].key)); ++ ++ zbr = &znode->zbranch[n]; ++ lnc_free(zbr); ++ ++ err = ubifs_add_dirt(c, zbr->lnum, zbr->len); ++ if (err) { ++ dbg_dump_znode(c, znode); ++ return err; ++ } ++ ++ /* We do not "gap" zbranch slots */ ++ for (i = n; i < znode->child_cnt - 1; i++) ++ znode->zbranch[i] = znode->zbranch[i + 1]; ++ znode->child_cnt -= 1; ++ ++ if (znode->child_cnt > 0) ++ return 0; ++ ++ /* ++ * This was the last zbranch, we have to delete this znode from the ++ * parent. ++ */ ++ ++ do { ++ ubifs_assert(!test_bit(OBSOLETE_ZNODE, &znode->flags)); ++ ubifs_assert(ubifs_zn_dirty(znode)); ++ ++ zp = znode->parent; ++ n = znode->iip; ++ ++ atomic_long_dec(&c->dirty_zn_cnt); ++ ++ err = insert_old_idx_znode(c, znode); ++ if (err) ++ return err; ++ ++ if (znode->cnext) { ++ __set_bit(OBSOLETE_ZNODE, &znode->flags); ++ atomic_long_inc(&c->clean_zn_cnt); ++ atomic_long_inc(&ubifs_clean_zn_cnt); ++ } else ++ kfree(znode); ++ znode = zp; ++ } while (znode->child_cnt == 1); /* while removing last child */ ++ ++ /* Remove from znode, entry n - 1 */ ++ znode->child_cnt -= 1; ++ ubifs_assert(znode->level != 0); ++ for (i = n; i < znode->child_cnt; i++) { ++ znode->zbranch[i] = znode->zbranch[i + 1]; ++ if (znode->zbranch[i].znode) ++ znode->zbranch[i].znode->iip = i; ++ } ++ ++ /* ++ * If this is the root and it has only 1 child then ++ * collapse the tree. ++ */ ++ if (!znode->parent) { ++ while (znode->child_cnt == 1 && znode->level != 0) { ++ zp = znode; ++ zbr = &znode->zbranch[0]; ++ znode = get_znode(c, znode, 0); ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ znode = dirty_cow_znode(c, zbr); ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ znode->parent = NULL; ++ znode->iip = 0; ++ if (c->zroot.len) { ++ err = insert_old_idx(c, c->zroot.lnum, ++ c->zroot.offs); ++ if (err) ++ return err; ++ } ++ c->zroot.lnum = zbr->lnum; ++ c->zroot.offs = zbr->offs; ++ c->zroot.len = zbr->len; ++ c->zroot.znode = znode; ++ ubifs_assert(!test_bit(OBSOLETE_ZNODE, ++ &zp->flags)); ++ ubifs_assert(test_bit(DIRTY_ZNODE, &zp->flags)); ++ atomic_long_dec(&c->dirty_zn_cnt); ++ ++ if (zp->cnext) { ++ __set_bit(OBSOLETE_ZNODE, &zp->flags); ++ atomic_long_inc(&c->clean_zn_cnt); ++ atomic_long_inc(&ubifs_clean_zn_cnt); ++ } else ++ kfree(zp); ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * ubifs_tnc_remove - remove an index entry of a node. ++ * @c: UBIFS file-system description object ++ * @key: key of node ++ * ++ * Returns %0 on success or negative error code on failure. ++ */ ++int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key) ++{ ++ int found, n, err = 0; ++ struct ubifs_znode *znode; ++ ++ mutex_lock(&c->tnc_mutex); ++ dbg_tnc("key %s", DBGKEY(key)); ++ found = lookup_level0_dirty(c, key, &znode, &n); ++ if (found < 0) { ++ err = found; ++ goto out_unlock; ++ } ++ if (found == 1) ++ err = tnc_delete(c, znode, n); ++ if (!err) ++ err = dbg_check_tnc(c, 0); ++ ++out_unlock: ++ mutex_unlock(&c->tnc_mutex); ++ return err; ++} ++ ++/** ++ * ubifs_tnc_remove_nm - remove an index entry for a "hashed" node. ++ * @c: UBIFS file-system description object ++ * @key: key of node ++ * @nm: directory entry name ++ * ++ * Returns %0 on success or negative error code on failure. ++ */ ++int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key, ++ const struct qstr *nm) ++{ ++ int n, err; ++ struct ubifs_znode *znode; ++ ++ mutex_lock(&c->tnc_mutex); ++ dbg_tnc("%.*s, key %s", nm->len, nm->name, DBGKEY(key)); ++ err = lookup_level0_dirty(c, key, &znode, &n); ++ if (err < 0) ++ goto out_unlock; ++ ++ if (err) { ++ if (c->replaying) ++ err = fallible_resolve_collision(c, key, &znode, &n, ++ nm, 0); ++ else ++ err = resolve_collision(c, key, &znode, &n, nm); ++ dbg_tnc("rc returned %d, znode %p, n %d", err, znode, n); ++ if (err < 0) ++ goto out_unlock; ++ if (err) { ++ /* Ensure the znode is dirtied */ ++ if (znode->cnext || !ubifs_zn_dirty(znode)) { ++ znode = dirty_cow_bottom_up(c, znode); ++ if (IS_ERR(znode)) { ++ err = PTR_ERR(znode); ++ goto out_unlock; ++ } ++ } ++ err = tnc_delete(c, znode, n); ++ } ++ } ++ ++out_unlock: ++ if (!err) ++ err = dbg_check_tnc(c, 0); ++ mutex_unlock(&c->tnc_mutex); ++ return err; ++} ++ ++/** ++ * key_in_range - determine if a key falls within a range of keys. ++ * @c: UBIFS file-system description object ++ * @key: key to check ++ * @from_key: lowest key in range ++ * @to_key: highest key in range ++ * ++ * This function returns %1 if the key is in range and %0 otherwise. ++ */ ++static int key_in_range(struct ubifs_info *c, union ubifs_key *key, ++ union ubifs_key *from_key, union ubifs_key *to_key) ++{ ++ if (keys_cmp(c, key, from_key) < 0) ++ return 0; ++ if (keys_cmp(c, key, to_key) > 0) ++ return 0; ++ return 1; ++} ++ ++/** ++ * ubifs_tnc_remove_range - remove index entries in range. ++ * @c: UBIFS file-system description object ++ * @from_key: lowest key to remove ++ * @to_key: highest key to remove ++ * ++ * This function removes index entries starting at @from_key and ending at ++ * @to_key. This function returns zero in case of success and a negative error ++ * code in case of failure. ++ */ ++int ubifs_tnc_remove_range(struct ubifs_info *c, union ubifs_key *from_key, ++ union ubifs_key *to_key) ++{ ++ int i, n, k, err = 0; ++ struct ubifs_znode *znode; ++ union ubifs_key *key; ++ ++ mutex_lock(&c->tnc_mutex); ++ while (1) { ++ /* Find first level 0 znode that contains keys to remove */ ++ err = lookup_level0(c, from_key, &znode, &n); ++ if (err < 0) ++ goto out_unlock; ++ ++ if (err) ++ key = from_key; ++ else { ++ err = tnc_next(c, &znode, &n); ++ if (err == -ENOENT) { ++ err = 0; ++ goto out_unlock; ++ } ++ if (err < 0) ++ goto out_unlock; ++ key = &znode->zbranch[n].key; ++ if (!key_in_range(c, key, from_key, to_key)) { ++ err = 0; ++ goto out_unlock; ++ } ++ } ++ ++ /* Ensure the znode is dirtied */ ++ if (znode->cnext || !ubifs_zn_dirty(znode)) { ++ znode = dirty_cow_bottom_up(c, znode); ++ if (IS_ERR(znode)) { ++ err = PTR_ERR(znode); ++ goto out_unlock; ++ } ++ } ++ ++ /* Remove all keys in range except the first */ ++ for (i = n + 1, k = 0; i < znode->child_cnt; i++, k++) { ++ key = &znode->zbranch[i].key; ++ if (!key_in_range(c, key, from_key, to_key)) ++ break; ++ lnc_free(&znode->zbranch[i]); ++ err = ubifs_add_dirt(c, znode->zbranch[i].lnum, ++ znode->zbranch[i].len); ++ if (err) { ++ dbg_dump_znode(c, znode); ++ goto out_unlock; ++ } ++ dbg_tnc("removing %s", DBGKEY(key)); ++ } ++ if (k) { ++ for (i = n + 1 + k; i < znode->child_cnt; i++) ++ znode->zbranch[i - k] = znode->zbranch[i]; ++ znode->child_cnt -= k; ++ } ++ ++ /* Now delete the first */ ++ err = tnc_delete(c, znode, n); ++ if (err) ++ goto out_unlock; ++ } ++ ++out_unlock: ++ if (!err) ++ err = dbg_check_tnc(c, 0); ++ mutex_unlock(&c->tnc_mutex); ++ return err; ++} ++ ++/** ++ * ubifs_tnc_remove_ino - remove an inode from TNC. ++ * @c: UBIFS file-system description object ++ * @inum: inode number to remove ++ * ++ * This function remove inode @inum and all the extended attributes associated ++ * with the anode from TNC and returns zero in case of success or a negative ++ * error code in case of failure. ++ */ ++int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum) ++{ ++ union ubifs_key key1, key2; ++ struct ubifs_dent_node *xent, *pxent = NULL; ++ struct qstr nm = { .name = NULL }; ++ ++ dbg_tnc("ino %lu", inum); ++ ++ /* ++ * Walk all extended attribute entries and remove them together with ++ * corresponding extended attribute inodes. ++ */ ++ lowest_xent_key(c, &key1, inum); ++ while (1) { ++ ino_t xattr_inum; ++ int err; ++ ++ xent = ubifs_tnc_next_ent(c, &key1, &nm); ++ if (IS_ERR(xent)) { ++ err = PTR_ERR(xent); ++ if (err == -ENOENT) ++ break; ++ return err; ++ } ++ ++ xattr_inum = le64_to_cpu(xent->inum); ++ dbg_tnc("xent '%s', ino %lu", xent->name, xattr_inum); ++ ++ nm.name = xent->name; ++ nm.len = le16_to_cpu(xent->nlen); ++ err = ubifs_tnc_remove_nm(c, &key1, &nm); ++ if (err) { ++ kfree(xent); ++ return err; ++ } ++ ++ lowest_ino_key(c, &key1, xattr_inum); ++ highest_ino_key(c, &key2, xattr_inum); ++ err = ubifs_tnc_remove_range(c, &key1, &key2); ++ if (err) { ++ kfree(xent); ++ return err; ++ } ++ ++ kfree(pxent); ++ pxent = xent; ++ key_read(c, &xent->key, &key1); ++ } ++ ++ kfree(pxent); ++ lowest_ino_key(c, &key1, inum); ++ highest_ino_key(c, &key2, inum); ++ ++ return ubifs_tnc_remove_range(c, &key1, &key2); ++} ++ ++/** ++ * ubifs_tnc_next_ent - walk directory or extended attribute entries. ++ * @c: UBIFS file-system description object ++ * @key: key of last entry ++ * @nm: name of last entry found or %NULL ++ * ++ * This function finds and reads the next directory or extended attribute entry ++ * after the given key (@key) if there is one. @nm is used to resolve ++ * collisions. ++ * ++ * If the name of the current entry is not known and only the key is known, ++ * @nm->name has to be %NULL. In this case the semantics of this function is a ++ * little bit different and it returns the entry corresponding to this key, not ++ * the next one. If the key was not found, the closest "right" entry is ++ * returned. ++ * ++ * If the fist entry has to be found, @key has to contain the lowest possible ++ * key value for this inode and @name has to be %NULL. ++ * ++ * This function returns the found directory or extended attribute entry node ++ * in case of success, %-ENOENT is returned if no entry was found, and a ++ * negative error code is returned in case of failure. ++ */ ++struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c, ++ union ubifs_key *key, ++ const struct qstr *nm) ++{ ++ int n, err, type = key_type(c, key); ++ struct ubifs_znode *znode; ++ struct ubifs_dent_node *dent; ++ struct ubifs_zbranch *zbr; ++ union ubifs_key *dkey; ++ ++ dbg_tnc("%s %s", nm->name ? (char *)nm->name : "(lowest)", DBGKEY(key)); ++ ubifs_assert(is_hash_key(c, key)); ++ ++ mutex_lock(&c->tnc_mutex); ++ err = lookup_level0(c, key, &znode, &n); ++ if (unlikely(err < 0)) ++ goto out_unlock; ++ ++ if (nm->name) { ++ if (err) { ++ /* Handle collisions */ ++ err = resolve_collision(c, key, &znode, &n, nm); ++ dbg_tnc("rc returned %d, znode %p, n %d", ++ err, znode, n); ++ if (unlikely(err < 0)) ++ goto out_unlock; ++ } ++ ++ /* Now find next entry */ ++ err = tnc_next(c, &znode, &n); ++ if (unlikely(err)) ++ goto out_unlock; ++ } else { ++ /* ++ * The full name of the entry was not given, in which case the ++ * behavior of this function is a little different and it ++ * returns current entry, not the next one. ++ */ ++ if (!err) { ++ /* ++ * However, the given key does not exist in the TNC ++ * tree and @znode/@n variables contain the closest ++ * "preceding" element. Switch to the next one. ++ */ ++ err = tnc_next(c, &znode, &n); ++ if (err) ++ goto out_unlock; ++ } ++ } ++ ++ zbr = &znode->zbranch[n]; ++ dent = kmalloc(zbr->len, GFP_NOFS); ++ if (unlikely(!dent)) { ++ err = -ENOMEM; ++ goto out_unlock; ++ } ++ ++ /* ++ * The above 'tnc_next()' call could lead us to the next inode, check ++ * this. ++ */ ++ dkey = &zbr->key; ++ if (key_ino(c, dkey) != key_ino(c, key) || ++ key_type(c, dkey) != type) { ++ err = -ENOENT; ++ goto out_free; ++ } ++ ++ err = tnc_read_node_nm(c, zbr, dent); ++ if (unlikely(err)) ++ goto out_free; ++ ++ mutex_unlock(&c->tnc_mutex); ++ return dent; ++ ++out_free: ++ kfree(dent); ++out_unlock: ++ mutex_unlock(&c->tnc_mutex); ++ return ERR_PTR(err); ++} ++ ++/** ++ * tnc_destroy_cnext - destroy left-over obsolete znodes from a failed commit. ++ * @c: UBIFS file-system description object ++ * ++ * Destroy left-over obsolete znodes from a failed commit. ++ */ ++static void tnc_destroy_cnext(struct ubifs_info *c) ++{ ++ struct ubifs_znode *cnext; ++ ++ if (!c->cnext) ++ return; ++ ubifs_assert(c->cmt_state == COMMIT_BROKEN); ++ cnext = c->cnext; ++ do { ++ struct ubifs_znode *znode = cnext; ++ ++ cnext = cnext->cnext; ++ if (test_bit(OBSOLETE_ZNODE, &znode->flags)) ++ kfree(znode); ++ } while (cnext && cnext != c->cnext); ++} ++ ++/** ++ * ubifs_tnc_close - close TNC subsystem and free all related resources. ++ * @c: UBIFS file-system description object ++ */ ++void ubifs_tnc_close(struct ubifs_info *c) ++{ ++ long clean_freed; ++ ++ tnc_destroy_cnext(c); ++ if (c->zroot.znode) { ++ clean_freed = ubifs_destroy_tnc_subtree(c->zroot.znode); ++ atomic_long_sub(clean_freed, &ubifs_clean_zn_cnt); ++ } ++ kfree(c->gap_lebs); ++ kfree(c->ilebs); ++ destroy_old_idx(c); ++} ++ ++/** ++ * left_znode - get the znode to the left. ++ * @c: UBIFS file-system description object ++ * @znode: znode ++ * ++ * This function returns a pointer to the znode to the left of @znode or NULL if ++ * there is not one. A negative error code is returned on failure. ++ */ ++static struct ubifs_znode *left_znode(struct ubifs_info *c, ++ struct ubifs_znode *znode) ++{ ++ int level = znode->level; ++ ++ while (1) { ++ int n = znode->iip - 1; ++ ++ /* Go up until we can go left */ ++ znode = znode->parent; ++ if (!znode) ++ return NULL; ++ if (n >= 0) { ++ /* Now go down the rightmost branch to 'level' */ ++ znode = get_znode(c, znode, n); ++ if (IS_ERR(znode)) ++ return znode; ++ while (znode->level != level) { ++ n = znode->child_cnt - 1; ++ znode = get_znode(c, znode, n); ++ if (IS_ERR(znode)) ++ return znode; ++ } ++ break; ++ } ++ } ++ return znode; ++} ++ ++/** ++ * right_znode - get the znode to the right. ++ * @c: UBIFS file-system description object ++ * @znode: znode ++ * ++ * This function returns a pointer to the znode to the right of @znode or NULL ++ * if there is not one. A negative error code is returned on failure. ++ */ ++static struct ubifs_znode *right_znode(struct ubifs_info *c, ++ struct ubifs_znode *znode) ++{ ++ int level = znode->level; ++ ++ while (1) { ++ int n = znode->iip + 1; ++ ++ /* Go up until we can go right */ ++ znode = znode->parent; ++ if (!znode) ++ return NULL; ++ if (n < znode->child_cnt) { ++ /* Now go down the leftmost branch to 'level' */ ++ znode = get_znode(c, znode, n); ++ if (IS_ERR(znode)) ++ return znode; ++ while (znode->level != level) { ++ znode = get_znode(c, znode, 0); ++ if (IS_ERR(znode)) ++ return znode; ++ } ++ break; ++ } ++ } ++ return znode; ++} ++ ++/** ++ * lookup_znode - find a particular indexing node from TNC. ++ * @c: UBIFS file-system description object ++ * @key: index node key to lookup ++ * @level: index node level ++ * @lnum: index node LEB number ++ * @offs: index node offset ++ * ++ * This function searches an indexing node by its first key @key and its ++ * address @lnum:@offs. It looks up the indexing tree by pulling all indexing ++ * nodes it traverses to TNC. This function is called fro indexing nodes which ++ * were found on the media by scanning, for example when garbage-collecting or ++ * when doing in-the-gaps commit. This means that the indexing node which is ++ * looked for does not have to have exactly the same leftmost key @key, because ++ * the leftmost key may have been changed, in which case TNC will contain a ++ * dirty znode which still refers the same @lnum:@offs. This function is clever ++ * enough to recognize such indexing nodes. ++ * ++ * Note, if a znode was deleted or changed too much, then this function will ++ * not find it. For situations like this UBIFS has the old index RB-tree ++ * (indexed by @lnum:@offs). ++ * ++ * This function returns a pointer to the znode found or %NULL if it is not ++ * found. A negative error code is returned on failure. ++ */ ++static struct ubifs_znode *lookup_znode(struct ubifs_info *c, ++ union ubifs_key *key, int level, ++ int lnum, int offs) ++{ ++ struct ubifs_znode *znode, *zn; ++ int n, nn; ++ ++ /* ++ * The arguments have probably been read off flash, so don't assume ++ * they are valid. ++ */ ++ if (level < 0) ++ return ERR_PTR(-EINVAL); ++ ++ /* Get the root znode */ ++ znode = c->zroot.znode; ++ if (!znode) { ++ znode = load_znode(c, &c->zroot, NULL, 0); ++ if (IS_ERR(znode)) ++ return znode; ++ } ++ /* Check if it is the one we are looking for */ ++ if (c->zroot.lnum == lnum && c->zroot.offs == offs) ++ return znode; ++ /* Descend to the parent level i.e. (level + 1) */ ++ if (level >= znode->level) ++ return NULL; ++ while (1) { ++ ubifs_search_zbranch(c, znode, key, &n); ++ if (n < 0) { ++ /* ++ * We reached a znode where the leftmost key is greater ++ * than the key we are searching for. This is the same ++ * situation as the one described in a huge comment at ++ * the end of the 'lookup_level0()' function. And for ++ * exactly the same reasons we have to try to look left ++ * before giving up. ++ */ ++ znode = left_znode(c, znode); ++ if (!znode) ++ return NULL; ++ if (IS_ERR(znode)) ++ return znode; ++ ubifs_search_zbranch(c, znode, key, &n); ++ ubifs_assert(n >= 0); ++ } ++ if (znode->level == level + 1) ++ break; ++ znode = get_znode(c, znode, n); ++ if (IS_ERR(znode)) ++ return znode; ++ } ++ /* Check if the child is the one we are looking for */ ++ if (znode->zbranch[n].lnum == lnum && znode->zbranch[n].offs == offs) ++ return get_znode(c, znode, n); ++ /* If the key is unique, there is nowhere else to look */ ++ if (!is_hash_key(c, key)) ++ return NULL; ++ /* ++ * The key is not unique and so may be also in the znodes to either ++ * side. ++ */ ++ zn = znode; ++ nn = n; ++ /* Look left */ ++ while (1) { ++ /* Move one branch to the left */ ++ if (n) ++ n -= 1; ++ else { ++ znode = left_znode(c, znode); ++ if (!znode) ++ break; ++ if (IS_ERR(znode)) ++ return znode; ++ n = znode->child_cnt - 1; ++ } ++ /* Check it */ ++ if (znode->zbranch[n].lnum == lnum && ++ znode->zbranch[n].offs == offs) ++ return get_znode(c, znode, n); ++ /* Stop if the key is less than the one we are looking for */ ++ if (keys_cmp(c, &znode->zbranch[n].key, key) < 0) ++ break; ++ } ++ /* Back to the middle */ ++ znode = zn; ++ n = nn; ++ /* Look right */ ++ while (1) { ++ /* Move one branch to the right */ ++ if (++n >= znode->child_cnt) { ++ znode = right_znode(c, znode); ++ if (!znode) ++ break; ++ if (IS_ERR(znode)) ++ return znode; ++ n = 0; ++ } ++ /* Check it */ ++ if (znode->zbranch[n].lnum == lnum && ++ znode->zbranch[n].offs == offs) ++ return get_znode(c, znode, n); ++ /* Stop if the key is greater than the one we are looking for */ ++ if (keys_cmp(c, &znode->zbranch[n].key, key) > 0) ++ break; ++ } ++ return NULL; ++} ++ ++/** ++ * is_idx_node_in_tnc - determine if an index node is in the TNC. ++ * @c: UBIFS file-system description object ++ * @key: key of index node ++ * @level: index node level ++ * @lnum: LEB number of index node ++ * @offs: offset of index node ++ * ++ * This function returns %0 if the index node is not referred to in the TNC, %1 ++ * if the index node is referred to in the TNC and the corresponding znode is ++ * dirty, %2 if an index node is referred to in the TNC and the corresponding ++ * znode is clean, and a negative error code in case of failure. ++ * ++ * Note, the @key argument has to be the key of the first child. Also note, ++ * this function relies on the fact that 0:0 is never a valid LEB number and ++ * offset for a main-area node. ++ */ ++int is_idx_node_in_tnc(struct ubifs_info *c, union ubifs_key *key, int level, ++ int lnum, int offs) ++{ ++ struct ubifs_znode *znode; ++ ++ znode = lookup_znode(c, key, level, lnum, offs); ++ if (!znode) ++ return 0; ++ if (IS_ERR(znode)) ++ return PTR_ERR(znode); ++ ++ return ubifs_zn_dirty(znode) ? 1 : 2; ++} ++ ++/** ++ * is_leaf_node_in_tnc - determine if a non-indexing not is in the TNC. ++ * @c: UBIFS file-system description object ++ * @key: node key ++ * @lnum: node LEB number ++ * @offs: node offset ++ * ++ * This function returns %1 if the node is referred to in the TNC, %0 if it is ++ * not, and a negative error code in case of failure. ++ * ++ * Note, this function relies on the fact that 0:0 is never a valid LEB number ++ * and offset for a main-area node. ++ */ ++static int is_leaf_node_in_tnc(struct ubifs_info *c, union ubifs_key *key, ++ int lnum, int offs) ++{ ++ struct ubifs_zbranch *zbr; ++ struct ubifs_znode *znode, *zn; ++ int n, found, err, nn; ++ const int unique = !is_hash_key(c, key); ++ ++ found = lookup_level0(c, key, &znode, &n); ++ if (found < 0) ++ return found; /* Error code */ ++ if (!found) ++ return 0; ++ zbr = &znode->zbranch[n]; ++ if (lnum == zbr->lnum && offs == zbr->offs) ++ return 1; /* Found it */ ++ if (unique) ++ return 0; ++ /* ++ * Because the key is not unique, we have to look left ++ * and right as well ++ */ ++ zn = znode; ++ nn = n; ++ /* Look left */ ++ while (1) { ++ err = tnc_prev(c, &znode, &n); ++ if (err == -ENOENT) ++ break; ++ if (err) ++ return err; ++ if (keys_cmp(c, key, &znode->zbranch[n].key)) ++ break; ++ zbr = &znode->zbranch[n]; ++ if (lnum == zbr->lnum && offs == zbr->offs) ++ return 1; /* Found it */ ++ } ++ /* Look right */ ++ znode = zn; ++ n = nn; ++ while (1) { ++ err = tnc_next(c, &znode, &n); ++ if (err) { ++ if (err == -ENOENT) ++ return 0; ++ return err; ++ } ++ if (keys_cmp(c, key, &znode->zbranch[n].key)) ++ break; ++ zbr = &znode->zbranch[n]; ++ if (lnum == zbr->lnum && offs == zbr->offs) ++ return 1; /* Found it */ ++ } ++ return 0; ++} ++ ++/** ++ * ubifs_tnc_has_node - determine whether a node is in the TNC. ++ * @c: UBIFS file-system description object ++ * @key: node key ++ * @level: index node level (if it is an index node) ++ * @lnum: node LEB number ++ * @offs: node offset ++ * @is_idx: non-zero if the node is an index node ++ * ++ * This function returns %1 if the node is in the TNC, %0 if it is not, and a ++ * negative error code in case of failure. For index nodes, @key has to be the ++ * key of the first child. An index node is considered to be in the TNC only if ++ * the corresponding znode is clean or has not been loaded. ++ */ ++int ubifs_tnc_has_node(struct ubifs_info *c, union ubifs_key *key, int level, ++ int lnum, int offs, int is_idx) ++{ ++ int err; ++ ++ mutex_lock(&c->tnc_mutex); ++ if (is_idx) { ++ err = is_idx_node_in_tnc(c, key, level, lnum, offs); ++ if (err < 0) ++ goto out_unlock; ++ if (err == 1) ++ /* The index node was found but it was dirty */ ++ err = 0; ++ else if (err == 2) ++ /* The index node was found and it was clean */ ++ err = 1; ++ else ++ BUG_ON(err != 0); ++ } else ++ err = is_leaf_node_in_tnc(c, key, lnum, offs); ++ ++out_unlock: ++ mutex_unlock(&c->tnc_mutex); ++ return err; ++} ++ ++/** ++ * ubifs_dirty_idx_node - dirty an index node. ++ * @c: UBIFS file-system description object ++ * @key: index node key ++ * @level: index node level ++ * @lnum: index node LEB number ++ * @offs: index node offset ++ * ++ * This function loads and dirties an index node so that it can be garbage ++ * collected. The @key argument has to be the key of the first child. This ++ * function relies on the fact that 0:0 is never a valid LEB number and offset ++ * for a main-area node. Returns %0 on success and a negative error code on ++ * failure. ++ */ ++int ubifs_dirty_idx_node(struct ubifs_info *c, union ubifs_key *key, int level, ++ int lnum, int offs) ++{ ++ struct ubifs_znode *znode; ++ int err = 0; ++ ++ mutex_lock(&c->tnc_mutex); ++ znode = lookup_znode(c, key, level, lnum, offs); ++ if (!znode) ++ goto out_unlock; ++ if (IS_ERR(znode)) { ++ err = PTR_ERR(znode); ++ goto out_unlock; ++ } ++ znode = dirty_cow_bottom_up(c, znode); ++ if (IS_ERR(znode)) { ++ err = PTR_ERR(znode); ++ goto out_unlock; ++ } ++ ++out_unlock: ++ mutex_unlock(&c->tnc_mutex); ++ return err; ++} ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ ++/** ++ * dbg_walk_sub_tree - walk index subtree. ++ * @c: UBIFS file-system description object ++ * @znode: root znode of the subtree to walk ++ * @leaf_cb: called for each leaf node ++ * @znode_cb: called for each indexing node ++ * @priv: private date which is passed to callbacks ++ * ++ * This is a helper function which recursively walks the UBIFS index, reading ++ * each indexing node from the media if needed. Returns zero in case of success ++ * and a negative error code in case of failure. ++ */ ++static int dbg_walk_sub_tree(struct ubifs_info *c, struct ubifs_znode *znode, ++ dbg_leaf_callback leaf_cb, ++ dbg_znode_callback znode_cb, void *priv) ++{ ++ int n, err; ++ ++ cond_resched(); ++ ++ if (znode_cb) { ++ err = znode_cb(c, znode, priv); ++ if (err) ++ return err; ++ } ++ ++ if (znode->level == 0) { ++ if (!leaf_cb) ++ return 0; ++ ++ for (n = 0; n < znode->child_cnt; n++) { ++ struct ubifs_zbranch *zbr = &znode->zbranch[n]; ++ ++ err = leaf_cb(c, zbr, priv); ++ if (err) ++ return err; ++ } ++ } else ++ for (n = 0; n < znode->child_cnt; n++) { ++ struct ubifs_znode *zn; ++ ++ zn = get_znode(c, znode, n); ++ if (IS_ERR(zn)) ++ return PTR_ERR(zn); ++ err = dbg_walk_sub_tree(c, zn, leaf_cb, znode_cb, priv); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/** ++ * dbg_walk_index - walk the on-flash index. ++ * @c: UBIFS file-system description object ++ * @leaf_cb: called for each leaf node ++ * @znode_cb: called for each indexing node ++ * @priv: private date which is passed to callbacks ++ * ++ * This function walks the UBIFS index and calls the @leaf_cb for each leaf ++ * node and @znode_cb for each indexing node. Returns zero in case of success ++ * and a negative error code in case of failure. ++ * ++ * Because 'dbg_walk_sub_tree()' is recursive, it runs the risk of exceeding the ++ * stack space. ++ * ++ * It would be better if this function removed every znode it pulled to into ++ * the TNC, so that the behavior more closely matched the non-debugging ++ * behavior. ++ */ ++int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb, ++ dbg_znode_callback znode_cb, void *priv) ++{ ++ int err = 0; ++ ++ mutex_lock(&c->tnc_mutex); ++ if (!c->zroot.znode) { ++ c->zroot.znode = load_znode(c, &c->zroot, NULL, 0); ++ if (IS_ERR(c->zroot.znode)) { ++ err = PTR_ERR(c->zroot.znode); ++ c->zroot.znode = NULL; ++ goto out_unlock; ++ } ++ } ++ ++ err = dbg_walk_sub_tree(c, c->zroot.znode, leaf_cb, znode_cb, priv); ++ ++out_unlock: ++ mutex_unlock(&c->tnc_mutex); ++ return err; ++} ++ ++int dbg_read_leaf_nolock(struct ubifs_info *c, struct ubifs_zbranch *zbr, ++ void *node) ++{ ++ ubifs_assert(mutex_is_locked(&c->tnc_mutex)); ++ if (is_hash_key(c, &zbr->key)) ++ return tnc_read_node_nm(c, zbr, node); ++ return tnc_read_node(c, zbr, node); ++} ++ ++#endif /* CONFIG_UBIFS_FS_DEBUG */ +diff -urN linux-2.6.24.7.old/fs/ubifs/tnc_commit.c linux-2.6.24.7/fs/ubifs/tnc_commit.c +--- linux-2.6.24.7.old/fs/ubifs/tnc_commit.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/tnc_commit.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1105 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy (Битюцкий Ðртём) ++ */ ++ ++/* This file implements TNC functions for committing */ ++ ++#include "ubifs.h" ++ ++/** ++ * make_idx_node - make an index node for fill-the-gaps method of TNC commit. ++ * @c: UBIFS file-system description object ++ * @idx: buffer in which to place new index node ++ * @znode: znode from which to make new index node ++ * @lnum: LEB number where new index node will be written ++ * @offs: offset where new index node will be written ++ * @len: length of new index node ++ */ ++static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx, ++ struct ubifs_znode *znode, int lnum, int offs, int len) ++{ ++ struct ubifs_znode *zp; ++ int i, err; ++ ++ /* Make index node */ ++ idx->ch.node_type = UBIFS_IDX_NODE; ++ idx->child_cnt = cpu_to_le16(znode->child_cnt); ++ idx->level = cpu_to_le16(znode->level); ++ for (i = 0; i < znode->child_cnt; i++) { ++ struct ubifs_branch *br = ubifs_idx_branch(c, idx, i); ++ struct ubifs_zbranch *zbr = &znode->zbranch[i]; ++ ++ key_write_idx(c, &zbr->key, &br->key); ++ br->lnum = cpu_to_le32(zbr->lnum); ++ br->offs = cpu_to_le32(zbr->offs); ++ br->len = cpu_to_le32(zbr->len); ++ if (!zbr->lnum || !zbr->len) { ++ ubifs_err("bad ref in znode"); ++ dbg_dump_znode(c, znode); ++ if (zbr->znode) ++ dbg_dump_znode(c, zbr->znode); ++ } ++ } ++ ubifs_prepare_node(c, idx, len, 0); ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ znode->lnum = lnum; ++ znode->offs = offs; ++ znode->len = len; ++#endif ++ ++ err = insert_old_idx_znode(c, znode); ++ ++ /* Update the parent */ ++ zp = znode->parent; ++ if (zp) { ++ struct ubifs_zbranch *zbr; ++ ++ zbr = &zp->zbranch[znode->iip]; ++ zbr->lnum = lnum; ++ zbr->offs = offs; ++ zbr->len = len; ++ } else { ++ c->zroot.lnum = lnum; ++ c->zroot.offs = offs; ++ c->zroot.len = len; ++ } ++ c->calc_idx_sz += ALIGN(len, 8); ++ ++ atomic_long_dec(&c->dirty_zn_cnt); ++ ++ ubifs_assert(ubifs_zn_dirty(znode)); ++ ubifs_assert(test_bit(COW_ZNODE, &znode->flags)); ++ ++ __clear_bit(DIRTY_ZNODE, &znode->flags); ++ __clear_bit(COW_ZNODE, &znode->flags); ++ ++ return err; ++} ++ ++/** ++ * fill_gap - make index nodes in gaps in dirty index LEBs. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number that gap appears in ++ * @gap_start: offset of start of gap ++ * @gap_end: offset of end of gap ++ * @dirt: adds dirty space to this ++ * ++ * This function returns the number of index nodes written into the gap. ++ */ ++static int fill_gap(struct ubifs_info *c, int lnum, int gap_start, int gap_end, ++ int *dirt) ++{ ++ int len, gap_remains, gap_pos, written, pad_len; ++ ++ ubifs_assert((gap_start & 7) == 0); ++ ubifs_assert((gap_end & 7) == 0); ++ ubifs_assert(gap_end >= gap_start); ++ ++ gap_remains = gap_end - gap_start; ++ if (!gap_remains) ++ return 0; ++ gap_pos = gap_start; ++ written = 0; ++ while (c->enext) { ++ len = ubifs_idx_node_sz(c, c->enext->child_cnt); ++ if (len < gap_remains) { ++ struct ubifs_znode *znode = c->enext; ++ const int alen = ALIGN(len, 8); ++ int err; ++ ++ ubifs_assert(alen <= gap_remains); ++ err = make_idx_node(c, c->ileb_buf + gap_pos, znode, ++ lnum, gap_pos, len); ++ if (err) ++ return err; ++ gap_remains -= alen; ++ gap_pos += alen; ++ c->enext = znode->cnext; ++ if (c->enext == c->cnext) ++ c->enext = NULL; ++ written += 1; ++ } else ++ break; ++ } ++ if (gap_end == c->leb_size) { ++ c->ileb_len = ALIGN(gap_pos, c->min_io_size); ++ /* Pad to end of min_io_size */ ++ pad_len = c->ileb_len - gap_pos; ++ } else ++ /* Pad to end of gap */ ++ pad_len = gap_remains; ++ dbg_gc("LEB %d:%d to %d len %d nodes written %d wasted bytes %d", ++ lnum, gap_start, gap_end, gap_end - gap_start, written, pad_len); ++ ubifs_pad(c, c->ileb_buf + gap_pos, pad_len); ++ *dirt += pad_len; ++ return written; ++} ++ ++/** ++ * find_old_idx - find an index node obsoleted since the last commit start. ++ * @c: UBIFS file-system description object ++ * @lnum: LEB number of obsoleted index node ++ * @offs: offset of obsoleted index node ++ * ++ * Returns %1 if found and %0 otherwise. ++ */ ++static int find_old_idx(struct ubifs_info *c, int lnum, int offs) ++{ ++ struct ubifs_old_idx *o; ++ struct rb_node *p; ++ ++ p = c->old_idx.rb_node; ++ while (p) { ++ o = rb_entry(p, struct ubifs_old_idx, rb); ++ if (lnum < o->lnum) ++ p = p->rb_left; ++ else if (lnum > o->lnum) ++ p = p->rb_right; ++ else if (offs < o->offs) ++ p = p->rb_left; ++ else if (offs > o->offs) ++ p = p->rb_right; ++ else ++ return 1; ++ } ++ return 0; ++} ++ ++/** ++ * is_idx_node_in_use - determine if an index node can be overwritten. ++ * @c: UBIFS file-system description object ++ * @key: key of index node ++ * @level: index node level ++ * @lnum: LEB number of index node ++ * @offs: offset of index node ++ * ++ * If @key / @lnum / @offs identify an index node that was not part of the old ++ * index, then this function returns %0 (obsolete). Else if the index node was ++ * part of the old index but is now dirty %1 is returned, else if it is clean %2 ++ * is returned. A negative error code is returned on failure. ++ */ ++static int is_idx_node_in_use(struct ubifs_info *c, union ubifs_key *key, ++ int level, int lnum, int offs) ++{ ++ int ret; ++ ++ ret = is_idx_node_in_tnc(c, key, level, lnum, offs); ++ if (ret < 0) ++ return ret; /* Error code */ ++ if (ret == 0) ++ if (find_old_idx(c, lnum, offs)) ++ return 1; ++ return ret; ++} ++ ++/** ++ * layout_leb_in_gaps - layout index nodes using in-the-gaps method. ++ * @c: UBIFS file-system description object ++ * @p: return LEB number here ++ * ++ * This function lays out new index nodes for dirty znodes using in-the-gaps ++ * method of TNC commit. ++ * This function merely puts the next znode into the next gap, making no attempt ++ * to try to maximise the number of znodes that fit. ++ * This function returns the number of index nodes written into the gaps, or a ++ * negative error code on failure. ++ */ ++static int layout_leb_in_gaps(struct ubifs_info *c, int *p) ++{ ++ struct ubifs_scan_leb *sleb; ++ struct ubifs_scan_node *snod; ++ int lnum, dirt = 0, gap_start, gap_end, err, written, tot_written; ++ ++ tot_written = 0; ++ /* Get an index LEB with lots of obsolete index nodes */ ++ lnum = ubifs_find_dirty_idx_leb(c); ++ if (lnum < 0) ++ /* ++ * There also may be dirt in the index head that could be ++ * filled, however we do not check there at present. ++ */ ++ return lnum; /* Error code */ ++ *p = lnum; ++ dbg_gc("LEB %d", lnum); ++ /* ++ * Scan the index LEB. We use the generic scan for this even though ++ * it is more comprehensive and less efficient than is needed for this ++ * purpose. ++ */ ++ sleb = ubifs_scan(c, lnum, 0, c->ileb_buf); ++ c->ileb_len = 0; ++ if (IS_ERR(sleb)) ++ return PTR_ERR(sleb); ++ gap_start = 0; ++ list_for_each_entry(snod, &sleb->nodes, list) { ++ struct ubifs_idx_node *idx; ++ int in_use, level; ++ ++ ubifs_assert(snod->type == UBIFS_IDX_NODE); ++ idx = snod->node; ++ key_read(c, ubifs_idx_key(c, idx), &snod->key); ++ level = le16_to_cpu(idx->level); ++ /* Determine if the index node is in use (not obsolete) */ ++ in_use = is_idx_node_in_use(c, &snod->key, level, lnum, ++ snod->offs); ++ if (in_use < 0) { ++ ubifs_scan_destroy(sleb); ++ return in_use; /* Error code */ ++ } ++ if (in_use) { ++ if (in_use == 1) ++ dirt += ALIGN(snod->len, 8); ++ /* ++ * The obsolete index nodes form gaps that can be ++ * overwritten. This gap has ended because we have ++ * found an index node that is still in use ++ * i.e. not obsolete ++ */ ++ gap_end = snod->offs; ++ /* Try to fill gap */ ++ written = fill_gap(c, lnum, gap_start, gap_end, &dirt); ++ if (written < 0) { ++ ubifs_scan_destroy(sleb); ++ return written; /* Error code */ ++ } ++ tot_written += written; ++ gap_start = ALIGN(snod->offs + snod->len, 8); ++ } ++ } ++ ubifs_scan_destroy(sleb); ++ c->ileb_len = c->leb_size; ++ gap_end = c->leb_size; ++ /* Try to fill gap */ ++ written = fill_gap(c, lnum, gap_start, gap_end, &dirt); ++ if (written < 0) ++ return written; /* Error code */ ++ tot_written += written; ++ if (tot_written == 0) { ++ struct ubifs_lprops lp; ++ ++ dbg_gc("LEB %d wrote %d index nodes", lnum, tot_written); ++ err = ubifs_read_one_lp(c, lnum, &lp); ++ if (err) ++ return err; ++ if (lp.free == c->leb_size) { ++ /* ++ * We must have snatched this LEB from the idx_gc list ++ * so we need to correct the free and dirty space. ++ */ ++ err = ubifs_change_one_lp(c, lnum, ++ c->leb_size - c->ileb_len, ++ dirt, 0, 0, 0); ++ if (err) ++ return err; ++ } ++ return 0; ++ } ++ err = ubifs_change_one_lp(c, lnum, c->leb_size - c->ileb_len, dirt, ++ 0, 0, 0); ++ if (err) ++ return err; ++ err = ubi_leb_change(c->ubi, lnum, c->ileb_buf, c->ileb_len, ++ UBI_SHORTTERM); ++ if (err) { ++ ubifs_err("ubi_leb_change failed, error %d", err); ++ return err; ++ } ++ dbg_gc("LEB %d wrote %d index nodes", lnum, tot_written); ++ return tot_written; ++} ++ ++/** ++ * get_leb_cnt - calculate the number of empty LEBs needed to commit. ++ * @c: UBIFS file-system description object ++ * @cnt: number of znodes to commit ++ * ++ * This function returns the number of empty LEBs needed to commit @cnt znodes ++ * to the current index head. The number is not exact and may be more than ++ * needed. ++ */ ++static int get_leb_cnt(struct ubifs_info *c, int cnt) ++{ ++ int d; ++ ++ /* Assume maximum index node size (i.e. overestimate space needed) */ ++ cnt -= (c->leb_size - c->ihead_offs) / c->max_idx_node_sz; ++ if (cnt < 0) ++ cnt = 0; ++ d = c->leb_size / c->max_idx_node_sz; ++ return DIV_ROUND_UP(cnt, d); ++} ++ ++/** ++ * layout_in_gaps - in-the-gaps method of committing TNC. ++ * @c: UBIFS file-system description object ++ * @cnt: number of dirty znodes to commit. ++ * ++ * This function lays out new index nodes for dirty znodes using in-the-gaps ++ * method of TNC commit. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int layout_in_gaps(struct ubifs_info *c, int cnt) ++{ ++ int err, leb_needed_cnt, written, *p; ++ ++ dbg_gc("%d znodes to write", cnt); ++ ++ c->gap_lebs = kmalloc(sizeof(int) * (c->lst.idx_lebs + 1), GFP_NOFS); ++ if (!c->gap_lebs) ++ return -ENOMEM; ++ ++ p = c->gap_lebs; ++ do { ++ ubifs_assert(p < c->gap_lebs + sizeof(int) * c->lst.idx_lebs); ++ written = layout_leb_in_gaps(c, p); ++ if (written < 0) { ++ err = written; ++ if (err == -ENOSPC) { ++ if (!dbg_force_in_the_gaps_enabled) { ++ /* ++ * Do not print scary warnings if the ++ * debugging option which forces ++ * in-the-gaps is enabled. ++ */ ++ ubifs_err("out of space"); ++ spin_lock(&c->space_lock); ++ dbg_dump_budg(c); ++ spin_unlock(&c->space_lock); ++ dbg_dump_lprops(c); ++ } ++ /* Try to commit anyway */ ++ err = 0; ++ break; ++ } ++ kfree(c->gap_lebs); ++ c->gap_lebs = NULL; ++ return err; ++ } ++ p++; ++ cnt -= written; ++ leb_needed_cnt = get_leb_cnt(c, cnt); ++ dbg_gc("%d znodes remaining, need %d LEBs, have %d", cnt, ++ leb_needed_cnt, c->ileb_cnt); ++ } while (leb_needed_cnt > c->ileb_cnt); ++ ++ *p = -1; ++ return 0; ++} ++ ++/** ++ * layout_in_empty_space - layout index nodes in empty space. ++ * @c: UBIFS file-system description object ++ * ++ * This function lays out new index nodes for dirty znodes using empty LEBs. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int layout_in_empty_space(struct ubifs_info *c) ++{ ++ struct ubifs_znode *znode, *cnext, *zp; ++ int lnum, offs, len, next_len, buf_len, buf_offs, used, avail; ++ int wlen, blen, err; ++ ++ cnext = c->enext; ++ if (!cnext) ++ return 0; ++ ++ lnum = c->ihead_lnum; ++ buf_offs = c->ihead_offs; ++ ++ buf_len = ubifs_idx_node_sz(c, c->fanout); ++ buf_len = ALIGN(buf_len, c->min_io_size); ++ used = 0; ++ avail = buf_len; ++ ++ /* Ensure there is enough room for first write */ ++ next_len = ubifs_idx_node_sz(c, cnext->child_cnt); ++ if (buf_offs + next_len > c->leb_size) ++ lnum = -1; ++ ++ while (1) { ++ znode = cnext; ++ ++ len = ubifs_idx_node_sz(c, znode->child_cnt); ++ ++ /* Determine the index node position */ ++ if (lnum == -1) { ++ if (c->ileb_nxt >= c->ileb_cnt) { ++ ubifs_err("out of space"); ++ return -ENOSPC; ++ } ++ lnum = c->ilebs[c->ileb_nxt++]; ++ buf_offs = 0; ++ used = 0; ++ avail = buf_len; ++ } ++ ++ offs = buf_offs + used; ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ znode->lnum = lnum; ++ znode->offs = offs; ++ znode->len = len; ++#endif ++ ++ /* Update the parent */ ++ zp = znode->parent; ++ if (zp) { ++ struct ubifs_zbranch *zbr; ++ int i; ++ ++ i = znode->iip; ++ zbr = &zp->zbranch[i]; ++ zbr->lnum = lnum; ++ zbr->offs = offs; ++ zbr->len = len; ++ } else { ++ c->zroot.lnum = lnum; ++ c->zroot.offs = offs; ++ c->zroot.len = len; ++ } ++ c->calc_idx_sz += ALIGN(len, 8); ++ ++ /* ++ * Once lprops is updated, we can decrease the dirty znode count ++ * but it is easier to just do it here. ++ */ ++ atomic_long_dec(&c->dirty_zn_cnt); ++ ++ /* ++ * Calculate the next index node length to see if there is ++ * enough room for it ++ */ ++ cnext = znode->cnext; ++ if (cnext == c->cnext) ++ next_len = 0; ++ else ++ next_len = ubifs_idx_node_sz(c, cnext->child_cnt); ++ ++ if (c->min_io_size == 1) { ++ buf_offs += ALIGN(len, 8); ++ if (next_len) { ++ if (buf_offs + next_len <= c->leb_size) ++ continue; ++ err = ubifs_update_one_lp(c, lnum, 0, ++ c->leb_size - buf_offs, 0, 0); ++ if (err) ++ return err; ++ lnum = -1; ++ continue; ++ } ++ err = ubifs_update_one_lp(c, lnum, ++ c->leb_size - buf_offs, 0, 0, 0); ++ if (err) ++ return err; ++ break; ++ } ++ ++ /* Update buffer positions */ ++ wlen = used + len; ++ used += ALIGN(len, 8); ++ avail -= ALIGN(len, 8); ++ ++ if (next_len != 0 && ++ buf_offs + used + next_len <= c->leb_size && ++ avail > 0) ++ continue; ++ ++ if (avail <= 0 && next_len && ++ buf_offs + used + next_len <= c->leb_size) ++ blen = buf_len; ++ else ++ blen = ALIGN(wlen, c->min_io_size); ++ ++ /* The buffer is full or there are no more znodes to do */ ++ buf_offs += blen; ++ if (next_len) { ++ if (buf_offs + next_len > c->leb_size) { ++ err = ubifs_update_one_lp(c, lnum, ++ c->leb_size - buf_offs, blen - used, ++ 0, 0); ++ if (err) ++ return err; ++ lnum = -1; ++ } ++ used -= blen; ++ if (used < 0) ++ used = 0; ++ avail = buf_len - used; ++ continue; ++ } ++ err = ubifs_update_one_lp(c, lnum, c->leb_size - buf_offs, ++ blen - used, 0, 0); ++ if (err) ++ return err; ++ break; ++ } ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ c->new_ihead_lnum = lnum; ++ c->new_ihead_offs = buf_offs; ++#endif ++ ++ return 0; ++} ++ ++/** ++ * layout_commit - determine positions of index nodes to commit. ++ * @c: UBIFS file-system description object ++ * @no_space: indicates that insufficient empty LEBs were allocated ++ * @cnt: number of znodes to commit ++ * ++ * Calculate and update the positions of index nodes to commit. If there were ++ * an insufficient number of empty LEBs allocated, then index nodes are placed ++ * into the gaps created by obsolete index nodes in non-empty index LEBs. For ++ * this purpose, an obsolete index node is one that was not in the index as at ++ * the end of the last commit. To write "in-the-gaps" requires that those index ++ * LEBs are updated atomically in-place. ++ */ ++static int layout_commit(struct ubifs_info *c, int no_space, int cnt) ++{ ++ int err; ++ ++ if (no_space) { ++ err = layout_in_gaps(c, cnt); ++ if (err) ++ return err; ++ } ++ err = layout_in_empty_space(c); ++ return err; ++} ++ ++/** ++ * find_first_dirty - find first dirty znode. ++ * @znode: znode to begin searching from ++ */ ++static struct ubifs_znode *find_first_dirty(struct ubifs_znode *znode) ++{ ++ int i, cont; ++ ++ if (!znode) ++ return NULL; ++ ++ while (1) { ++ if (znode->level == 0) { ++ if (ubifs_zn_dirty(znode)) ++ return znode; ++ return NULL; ++ } ++ cont = 0; ++ for (i = 0; i < znode->child_cnt; i++) { ++ struct ubifs_zbranch *zbr = &znode->zbranch[i]; ++ ++ if (zbr->znode && ubifs_zn_dirty(zbr->znode)) { ++ znode = zbr->znode; ++ cont = 1; ++ break; ++ } ++ } ++ if (!cont) { ++ if (ubifs_zn_dirty(znode)) ++ return znode; ++ return NULL; ++ } ++ } ++} ++ ++/** ++ * find_next_dirty - find next dirty znode. ++ * @znode: znode to begin searching from ++ */ ++static struct ubifs_znode *find_next_dirty(struct ubifs_znode *znode) ++{ ++ int n = znode->iip + 1; ++ ++ znode = znode->parent; ++ if (!znode) ++ return NULL; ++ for (; n < znode->child_cnt; n++) { ++ struct ubifs_zbranch *zbr = &znode->zbranch[n]; ++ ++ if (zbr->znode && ubifs_zn_dirty(zbr->znode)) ++ return find_first_dirty(zbr->znode); ++ } ++ return znode; ++} ++ ++/** ++ * get_znodes_to_commit - create list of dirty znodes to commit. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns the number of znodes to commit. ++ */ ++static int get_znodes_to_commit(struct ubifs_info *c) ++{ ++ struct ubifs_znode *znode, *cnext; ++ int cnt = 0; ++ ++ c->cnext = find_first_dirty(c->zroot.znode); ++ znode = c->enext = c->cnext; ++ if (!znode) { ++ dbg_cmt("no znodes to commit"); ++ return 0; ++ } ++ cnt += 1; ++ while (1) { ++ ubifs_assert(!test_bit(COW_ZNODE, &znode->flags)); ++ __set_bit(COW_ZNODE, &znode->flags); ++ znode->alt = 0; ++ cnext = find_next_dirty(znode); ++ if (!cnext) { ++ znode->cnext = c->cnext; ++ break; ++ } ++ znode->cnext = cnext; ++ znode = cnext; ++ cnt += 1; ++ } ++ dbg_cmt("committing %d znodes", cnt); ++ ubifs_assert(cnt == atomic_long_read(&c->dirty_zn_cnt)); ++ return cnt; ++} ++ ++/** ++ * alloc_idx_lebs - allocate empty LEBs to be used to commit. ++ * @c: UBIFS file-system description object ++ * @cnt: number of znodes to commit ++ * ++ * This function returns %-ENOSPC if it cannot allocate a sufficient number of ++ * empty LEBs. %0 is returned on success, otherwise a negative error code ++ * is returned. ++ */ ++static int alloc_idx_lebs(struct ubifs_info *c, int cnt) ++{ ++ int i, leb_cnt, lnum; ++ ++ c->ileb_cnt = 0; ++ c->ileb_nxt = 0; ++ leb_cnt = get_leb_cnt(c, cnt); ++ dbg_cmt("need about %d empty LEBS for TNC commit", leb_cnt); ++ if (!leb_cnt) ++ return 0; ++ c->ilebs = kmalloc(leb_cnt * sizeof(int), GFP_NOFS); ++ if (!c->ilebs) ++ return -ENOMEM; ++ for (i = 0; i < leb_cnt; i++) { ++ lnum = ubifs_find_free_leb_for_idx(c); ++ if (lnum < 0) ++ return lnum; ++ c->ilebs[c->ileb_cnt++] = lnum; ++ dbg_cmt("LEB %d", lnum); ++ } ++ if (dbg_force_in_the_gaps()) ++ return -ENOSPC; ++ return 0; ++} ++ ++/** ++ * free_unused_idx_lebs - free unused LEBs that were allocated for the commit. ++ * @c: UBIFS file-system description object ++ * ++ * It is possible that we allocate more empty LEBs for the commit than we need. ++ * This functions frees the surplus. ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int free_unused_idx_lebs(struct ubifs_info *c) ++{ ++ int i, err = 0, lnum, er; ++ ++ for (i = c->ileb_nxt; i < c->ileb_cnt; i++) { ++ lnum = c->ilebs[i]; ++ dbg_cmt("LEB %d", lnum); ++ er = ubifs_change_one_lp(c, lnum, -1, -1, 0, ++ LPROPS_INDEX | LPROPS_TAKEN, 0); ++ if (!err) ++ err = er; ++ } ++ return err; ++} ++ ++/** ++ * free_idx_lebs - free unused LEBs after commit end. ++ * @c: UBIFS file-system description object ++ * ++ * This function returns %0 on success and a negative error code on failure. ++ */ ++static int free_idx_lebs(struct ubifs_info *c) ++{ ++ int err; ++ ++ err = free_unused_idx_lebs(c); ++ kfree(c->ilebs); ++ c->ilebs = NULL; ++ return err; ++} ++ ++/** ++ * ubifs_tnc_start_commit - start TNC commit. ++ * @c: UBIFS file-system description object ++ * @zroot: new index root position is returned here ++ * ++ * This function prepares the list of indexing nodes to commit and lays out ++ * their positions on flash. If there is not enough free space it uses the ++ * in-gap commit method. Returns zero in case of success and a negative error ++ * code in case of failure. ++ */ ++int ubifs_tnc_start_commit(struct ubifs_info *c, struct ubifs_zbranch *zroot) ++{ ++ int err = 0, cnt; ++ ++ mutex_lock(&c->tnc_mutex); ++ err = dbg_check_tnc(c, 1); ++ if (err) ++ goto out; ++ cnt = get_znodes_to_commit(c); ++ if (cnt != 0) { ++ int no_space = 0; ++ ++ err = alloc_idx_lebs(c, cnt); ++ if (err == -ENOSPC) ++ no_space = 1; ++ else if (err) ++ goto out_free; ++ err = layout_commit(c, no_space, cnt); ++ if (err) ++ goto out_free; ++ ubifs_assert(atomic_long_read(&c->dirty_zn_cnt) == 0); ++ err = free_unused_idx_lebs(c); ++ if (err) ++ goto out; ++ } ++ destroy_old_idx(c); ++ memcpy(zroot, &c->zroot, sizeof(struct ubifs_zbranch)); ++ ++ err = ubifs_save_dirty_idx_lnums(c); ++ if (err) ++ goto out; ++ ++ spin_lock(&c->space_lock); ++ /* ++ * Although we have not finished committing yet, update size of the ++ * committed index ('c->old_idx_sz') and zero out the index growth ++ * budget. It is OK to do this now, because we've reserved all the ++ * space which is needed to commit the index, and it is save for the ++ * budgeting subsystem to assume the index is already committed, ++ * even though it is not. ++ */ ++ c->old_idx_sz = c->calc_idx_sz; ++ c->budg_uncommitted_idx = 0; ++ spin_unlock(&c->space_lock); ++ mutex_unlock(&c->tnc_mutex); ++ ++ dbg_cmt("number of index LEBs %d", c->lst.idx_lebs); ++ dbg_cmt("size of index %llu", c->calc_idx_sz); ++ return err; ++ ++out_free: ++ free_idx_lebs(c); ++out: ++ mutex_unlock(&c->tnc_mutex); ++ return err; ++} ++ ++/** ++ * write_index - write index nodes. ++ * @c: UBIFS file-system description object ++ * ++ * This function writes the index nodes whose positions were laid out in the ++ * layout_in_empty_space function. ++ */ ++static int write_index(struct ubifs_info *c) ++{ ++ struct ubifs_idx_node *idx; ++ struct ubifs_znode *znode, *cnext; ++ int i, lnum, offs, len, next_len, buf_len, buf_offs, used; ++ int avail, wlen, err, lnum_pos = 0; ++ ++ cnext = c->enext; ++ if (!cnext) ++ return 0; ++ ++ /* ++ * Always write index nodes to the index head so that index nodes and ++ * other types of nodes are never mixed in the same erase block. ++ */ ++ lnum = c->ihead_lnum; ++ buf_offs = c->ihead_offs; ++ ++ /* Allocate commit buffer */ ++ buf_len = ALIGN(c->max_idx_node_sz, c->min_io_size); ++ used = 0; ++ avail = buf_len; ++ ++ /* Ensure there is enough room for first write */ ++ next_len = ubifs_idx_node_sz(c, cnext->child_cnt); ++ if (buf_offs + next_len > c->leb_size) { ++ err = ubifs_update_one_lp(c, lnum, -1, -1, 0, LPROPS_TAKEN); ++ if (err) ++ return err; ++ lnum = -1; ++ } ++ ++ while (1) { ++ cond_resched(); ++ ++ znode = cnext; ++ idx = c->cbuf + used; ++ ++ /* Make index node */ ++ idx->ch.node_type = UBIFS_IDX_NODE; ++ idx->child_cnt = cpu_to_le16(znode->child_cnt); ++ idx->level = cpu_to_le16(znode->level); ++ for (i = 0; i < znode->child_cnt; i++) { ++ struct ubifs_branch *br = ubifs_idx_branch(c, idx, i); ++ struct ubifs_zbranch *zbr = &znode->zbranch[i]; ++ ++ key_write_idx(c, &zbr->key, &br->key); ++ br->lnum = cpu_to_le32(zbr->lnum); ++ br->offs = cpu_to_le32(zbr->offs); ++ br->len = cpu_to_le32(zbr->len); ++ if (!zbr->lnum || !zbr->len) { ++ ubifs_err("bad ref in znode"); ++ dbg_dump_znode(c, znode); ++ if (zbr->znode) ++ dbg_dump_znode(c, zbr->znode); ++ } ++ } ++ len = ubifs_idx_node_sz(c, znode->child_cnt); ++ ubifs_prepare_node(c, idx, len, 0); ++ ++ /* Determine the index node position */ ++ if (lnum == -1) { ++ lnum = c->ilebs[lnum_pos++]; ++ buf_offs = 0; ++ used = 0; ++ avail = buf_len; ++ } ++ offs = buf_offs + used; ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ if (lnum != znode->lnum || offs != znode->offs || ++ len != znode->len) { ++ ubifs_err("inconsistent znode posn"); ++ return -EINVAL; ++ } ++#endif ++ ++ /* Grab some stuff from znode while we still can */ ++ cnext = znode->cnext; ++ ++ ubifs_assert(ubifs_zn_dirty(znode)); ++ ubifs_assert(test_bit(COW_ZNODE, &znode->flags)); ++ ++ /* ++ * It is important that other threads should see %DIRTY_ZNODE ++ * flag cleared before %COW_ZNODE. Specifically, it matters in ++ * the 'dirty_cow_znode()' function. This is the reason for the ++ * first barrier. Also, we want the bit changes to be seen to ++ * other threads ASAP, to avoid unnecesarry copying, which is ++ * the reason for the second barrier. ++ */ ++ clear_bit(DIRTY_ZNODE, &znode->flags); ++ smp_mb__before_clear_bit(); ++ clear_bit(COW_ZNODE, &znode->flags); ++ smp_mb__after_clear_bit(); ++ ++ /* Do not access znode from this point on */ ++ ++ /* Update buffer positions */ ++ wlen = used + len; ++ used += ALIGN(len, 8); ++ avail -= ALIGN(len, 8); ++ ++ /* ++ * Calculate the next index node length to see if there is ++ * enough room for it ++ */ ++ if (cnext == c->cnext) ++ next_len = 0; ++ else ++ next_len = ubifs_idx_node_sz(c, cnext->child_cnt); ++ ++ if (c->min_io_size == 1) { ++ /* ++ * Write the prepared index node immediately if there is ++ * no minimum IO size ++ */ ++ err = ubifs_leb_write(c, lnum, c->cbuf, buf_offs, ++ wlen, UBI_SHORTTERM); ++ if (err) ++ return err; ++ buf_offs += ALIGN(wlen, 8); ++ if (next_len) { ++ used = 0; ++ avail = buf_len; ++ if (buf_offs + next_len > c->leb_size) { ++ err = ubifs_update_one_lp(c, lnum, -1, ++ -1, 0, ++ LPROPS_TAKEN); ++ if (err) ++ return err; ++ lnum = -1; ++ } ++ continue; ++ } ++ } else { ++ int blen, nxt_offs = buf_offs + used + next_len; ++ ++ if (next_len && nxt_offs <= c->leb_size) { ++ if (avail > 0) ++ continue; ++ else ++ blen = buf_len; ++ } else { ++ wlen = ALIGN(wlen, 8); ++ blen = ALIGN(wlen, c->min_io_size); ++ ubifs_pad(c, c->cbuf + wlen, blen - wlen); ++ } ++ /* ++ * The buffer is full or there are no more znodes ++ * to do ++ */ ++ err = ubifs_leb_write(c, lnum, c->cbuf, buf_offs, ++ blen, UBI_SHORTTERM); ++ if (err) ++ return err; ++ buf_offs += blen; ++ if (next_len) { ++ if (nxt_offs > c->leb_size) { ++ err = ubifs_update_one_lp(c, lnum, -1, ++ -1, 0, ++ LPROPS_TAKEN); ++ if (err) ++ return err; ++ lnum = -1; ++ } ++ used -= blen; ++ if (used < 0) ++ used = 0; ++ avail = buf_len - used; ++ memmove(c->cbuf, c->cbuf + blen, used); ++ continue; ++ } ++ } ++ break; ++ } ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ if (lnum != c->new_ihead_lnum || buf_offs != c->new_ihead_offs) { ++ ubifs_err("inconsistent ihead"); ++ return -EINVAL; ++ } ++#endif ++ ++ c->ihead_lnum = lnum; ++ c->ihead_offs = buf_offs; ++ ++ return 0; ++} ++ ++/** ++ * free_obsolete_znodes - free obsolete znodes. ++ * @c: UBIFS file-system description object ++ * ++ * At the end of commit end, obsolete znodes are freed. ++ */ ++static void free_obsolete_znodes(struct ubifs_info *c) ++{ ++ struct ubifs_znode *znode, *cnext; ++ ++ cnext = c->cnext; ++ do { ++ znode = cnext; ++ cnext = znode->cnext; ++ if (test_bit(OBSOLETE_ZNODE, &znode->flags)) ++ kfree(znode); ++ else { ++ znode->cnext = NULL; ++ atomic_long_inc(&c->clean_zn_cnt); ++ atomic_long_inc(&ubifs_clean_zn_cnt); ++ } ++ } while (cnext != c->cnext); ++} ++ ++/** ++ * return_gap_lebs - return LEBs used by the in-gap commit method. ++ * @c: UBIFS file-system description object ++ * ++ * This function clears the "taken" flag for the LEBs which were used by the ++ * "commit in-the-gaps" method. ++ */ ++static int return_gap_lebs(struct ubifs_info *c) ++{ ++ int *p, err; ++ ++ if (!c->gap_lebs) ++ return 0; ++ ++ dbg_cmt(""); ++ for (p = c->gap_lebs; *p != -1; p++) { ++ err = ubifs_change_one_lp(c, *p, -1, -1, 0, LPROPS_TAKEN, 0); ++ if (err) ++ return err; ++ } ++ ++ kfree(c->gap_lebs); ++ c->gap_lebs = NULL; ++ return 0; ++} ++ ++/** ++ * ubifs_tnc_end_commit - update the TNC for commit end. ++ * @c: UBIFS file-system description object ++ * ++ * Write the dirty znodes. ++ */ ++int ubifs_tnc_end_commit(struct ubifs_info *c) ++{ ++ int err; ++ ++ if (!c->cnext) ++ return 0; ++ ++ err = return_gap_lebs(c); ++ if (err) ++ return err; ++ ++ err = write_index(c); ++ if (err) ++ return err; ++ ++ mutex_lock(&c->tnc_mutex); ++ ++ dbg_cmt("TNC height is %d", c->zroot.znode->level + 1); ++ ++ free_obsolete_znodes(c); ++ ++ c->cnext = NULL; ++ kfree(c->ilebs); ++ c->ilebs = NULL; ++ ++ mutex_unlock(&c->tnc_mutex); ++ ++ return 0; ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/tnc_misc.c linux-2.6.24.7/fs/ubifs/tnc_misc.c +--- linux-2.6.24.7.old/fs/ubifs/tnc_misc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/tnc_misc.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,259 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Adrian Hunter ++ * Artem Bityutskiy (Битюцкий Ðртём) ++ */ ++ ++/* ++ * This file contains miscelanious TNC-related functions shared betweend ++ * different files. This file does not form any logically separate TNC ++ * sub-system. The file was created because there is a lot of TNC code and ++ * putting it all in one file would make that file too big and unreadable. ++ * Nonetheless, this contains more or less "generic" code which may be used for ++ * more then one specific task. ++ */ ++ ++#include "ubifs.h" ++ ++/** ++ * ubifs_tnc_levelorder_next - next TNC tree element in levelorder traversal. ++ * @zr: root of the subtree to traverse ++ * @znode: previous znode ++ * ++ * This function implements levelorder TNC traversal. The LNC is ignored. ++ * Returns the next element or %NULL if @znode is already the last one. ++ */ ++struct ubifs_znode *ubifs_tnc_levelorder_next(struct ubifs_znode *zr, ++ struct ubifs_znode *znode) ++{ ++ int level, iip, level_search = 0; ++ struct ubifs_znode *zn; ++ ++ ubifs_assert(zr); ++ ++ if (unlikely(!znode)) ++ return zr; ++ ++ if (unlikely(znode == zr)) { ++ if (znode->level == 0) ++ return NULL; ++ return ubifs_tnc_find_child(zr, 0); ++ } ++ ++ level = znode->level; ++ ++ iip = znode->iip; ++ while (1) { ++ ubifs_assert(znode->level <= zr->level); ++ ++ /* ++ * First walk up until there is a znode with next branch to ++ * look at. ++ */ ++ while (znode->parent != zr && iip >= znode->parent->child_cnt) { ++ znode = znode->parent; ++ iip = znode->iip; ++ } ++ ++ if (unlikely(znode->parent == zr && ++ iip >= znode->parent->child_cnt)) { ++ /* This level is done, switch to the lower one */ ++ level -= 1; ++ if (level_search || level < 0) ++ /* ++ * We were already looking for znode at lower ++ * level ('level_search'). As we are here ++ * again, it just does not exist. Or all levels ++ * were finished ('level < 0'). ++ */ ++ return NULL; ++ ++ level_search = 1; ++ iip = -1; ++ znode = ubifs_tnc_find_child(zr, 0); ++ ubifs_assert(znode); ++ } ++ ++ /* Switch to the next index */ ++ zn = ubifs_tnc_find_child(znode->parent, iip + 1); ++ if (!zn) { ++ /* No more children to look at, we have walk up */ ++ iip = znode->parent->child_cnt; ++ continue; ++ } ++ ++ /* Walk back down to the level we came from ('level') */ ++ while (zn->level != level) { ++ znode = zn; ++ zn = ubifs_tnc_find_child(zn, 0); ++ if (!zn) { ++ /* ++ * This path is not too deep so it does not ++ * reach 'level'. Try next path. ++ */ ++ iip = znode->iip; ++ break; ++ } ++ } ++ ++ if (zn) { ++ ubifs_assert(zn->level >= 0); ++ return zn; ++ } ++ } ++} ++ ++/** ++ * ubifs_search_zbranch - search znode branch. ++ * @c: UBIFS file-system description object ++ * @znode: znode to search in ++ * @key: key to search for ++ * @n: znode branch slot number is returned here ++ * ++ * This is a helper function which search branch with key @key in @znode using ++ * binary search. The result of the search may be: ++ * o exact match, then %1 is returned, and the slot number of the branch is ++ * stored in @n; ++ * o no exact match, then %0 is returned and the slot number of the left ++ * closest branch is returned in @n; the slot if all keys in this znode are ++ * greater than @key, then %-1 is returned in @n. ++ */ ++int ubifs_search_zbranch(const struct ubifs_info *c, ++ const struct ubifs_znode *znode, ++ const union ubifs_key *key, int *n) ++{ ++ int beg = 0, end = znode->child_cnt, uninitialized_var(mid); ++ int uninitialized_var(cmp); ++ const struct ubifs_zbranch *zbr = &znode->zbranch[0]; ++ ++ ubifs_assert(end > beg); ++ ++ while (end > beg) { ++ mid = (beg + end) >> 1; ++ cmp = keys_cmp(c, key, &zbr[mid].key); ++ if (cmp > 0) ++ beg = mid + 1; ++ else if (cmp < 0) ++ end = mid; ++ else { ++ *n = mid; ++ return 1; ++ } ++ } ++ ++ *n = end - 1; ++ ++ /* The insert point is after *n */ ++ ubifs_assert(*n >= -1 && *n < znode->child_cnt); ++ if (*n == -1) ++ ubifs_assert(keys_cmp(c, key, &zbr[0].key) < 0); ++ else ++ ubifs_assert(keys_cmp(c, key, &zbr[*n].key) > 0); ++ if (*n + 1 < znode->child_cnt) ++ ubifs_assert(keys_cmp(c, key, &zbr[*n + 1].key) < 0); ++ ++ return 0; ++} ++ ++/** ++ * ubifs_tnc_postorder_first - find first znode to do postorder tree traversal. ++ * @znode: znode to start at (root of the sub-tree to traverse) ++ * ++ * Find the lowest leftmost znode in a subtree of the TNC tree. The LNC is ++ * ignored. ++ */ ++struct ubifs_znode *ubifs_tnc_postorder_first(struct ubifs_znode *znode) ++{ ++ if (unlikely(!znode)) ++ return NULL; ++ ++ while (znode->level > 0) { ++ struct ubifs_znode *child; ++ ++ child = ubifs_tnc_find_child(znode, 0); ++ if (!child) ++ return znode; ++ znode = child; ++ } ++ ++ return znode; ++} ++ ++/** ++ * ubifs_tnc_postorder_next - next TNC tree element in postorder traversal. ++ * @znode: previous znode ++ * ++ * This function implements postorder TNC traversal. The LNC is ignored. ++ * Returns the next element or %NULL if @znode is already the last one. ++ */ ++struct ubifs_znode *ubifs_tnc_postorder_next(struct ubifs_znode *znode) ++{ ++ struct ubifs_znode *zn; ++ ++ ubifs_assert(znode); ++ if (unlikely(!znode->parent)) ++ return NULL; ++ ++ /* Switch to the next index in the parent */ ++ zn = ubifs_tnc_find_child(znode->parent, znode->iip + 1); ++ if (!zn) ++ /* This is in fact the last child, return parent */ ++ return znode->parent; ++ ++ /* Go to the first znode in this new subtree */ ++ return ubifs_tnc_postorder_first(zn); ++} ++ ++/** ++ * ubifs_destroy_tnc_subtree - destroy all znodes connected to a subtree. ++ * @znode: znode defining subtree to destroy ++ * ++ * This function destroys subtree of the TNC tree. Returns number of clean ++ * znodes in the subtree. ++ */ ++long ubifs_destroy_tnc_subtree(struct ubifs_znode *znode) ++{ ++ struct ubifs_znode *zn = ubifs_tnc_postorder_first(znode); ++ long clean_freed = 0; ++ int n; ++ ++ ubifs_assert(zn); ++ while (1) { ++ for (n = 0; n < zn->child_cnt; n++) { ++ if (!zn->zbranch[n].znode) ++ continue; ++ ++ if (zn->level > 0 && ++ !ubifs_zn_dirty(zn->zbranch[n].znode)) ++ clean_freed += 1; ++ ++ cond_resched(); ++ kfree(zn->zbranch[n].znode); ++ } ++ ++ if (zn == znode) { ++ if (!ubifs_zn_dirty(zn)) ++ clean_freed += 1; ++ kfree(zn); ++ return clean_freed; ++ } ++ ++ zn = ubifs_tnc_postorder_next(zn); ++ } ++} +diff -urN linux-2.6.24.7.old/fs/ubifs/ubifs-media.h linux-2.6.24.7/fs/ubifs/ubifs-media.h +--- linux-2.6.24.7.old/fs/ubifs/ubifs-media.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/ubifs-media.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,731 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This file describes UBIFS on-flash format and contains definitions of all the ++ * relevant data structures and constants. ++ * ++ * All UBIFS on-flash objects are stored in the form of nodes. All nodes start ++ * with the UBIFS node magic number and have the same common header. Nodes ++ * always sit at 8-byte aligned positions on the media and node header sizes are ++ * also 8-byte aligned (except for the indexing node and the padding node). ++ */ ++ ++#ifndef __UBIFS_MEDIA_H__ ++#define __UBIFS_MEDIA_H__ ++ ++/* UBIFS node magic number (must not have the padding byte first or last) */ ++#define UBIFS_NODE_MAGIC 0x06101831 ++ ++/* UBIFS on-flash format version */ ++#define UBIFS_FORMAT_VERSION 3 ++ ++/* Minimum logical eraseblock size in bytes */ ++#define UBIFS_MIN_LEB_SZ (15*1024) ++ ++/* Initial CRC32 value used when calculating CRC checksums */ ++#define UBIFS_CRC32_INIT 0xFFFFFFFFU ++ ++/* ++ * UBIFS does not try to compress data if its length is less than the below ++ * constant. ++ */ ++#define UBIFS_MIN_COMPR_LEN 128 ++ ++/* Root inode number */ ++#define UBIFS_ROOT_INO 1 ++ ++/* Lowest inode number used for regular inodes (not UBIFS-only internal ones) */ ++#define UBIFS_FIRST_INO 64 ++ ++/* ++ * Maximum file name and extended attribute length (must be a multiple of 8, ++ * minus 1). ++ */ ++#define UBIFS_MAX_NLEN 255 ++ ++/* Maximum number of data journal heads */ ++#define UBIFS_MAX_JHEADS 1 ++ ++/* ++ * Size of UBIFS data block. Note, UBIFS is not a block oriented file-system, ++ * which means that it does not treat the underlying media as consisting of ++ * blocks like in case of hard drives. Do not be confused. UBIFS block is just ++ * the maximum amount of data which one data node can have or which can be ++ * attached to an inode node. ++ */ ++#define UBIFS_BLOCK_SIZE 4096 ++#define UBIFS_BLOCK_SHIFT 12 ++#define UBIFS_BLOCK_MASK 0x00000FFF ++ ++/* UBIFS padding byte pattern (must not be first or last byte of node magic) */ ++#define UBIFS_PADDING_BYTE 0xCE ++ ++/* Maximum possible key length */ ++#define UBIFS_MAX_KEY_LEN 16 ++ ++/* Key length ("simple" format) */ ++#define UBIFS_SK_LEN 8 ++ ++/* Minimum index tree fanout */ ++#define UBIFS_MIN_FANOUT 2 ++ ++/* Maximum number of levels in UBIFS indexing B-tree */ ++#define UBIFS_MAX_LEVELS 512 ++ ++/* Maximum amount of data attached to an inode in bytes */ ++#define UBIFS_MAX_INO_DATA UBIFS_BLOCK_SIZE ++ ++/* LEB Properties Tree fanout (must be power of 2) and fanout shift */ ++#define UBIFS_LPT_FANOUT 4 ++#define UBIFS_LPT_FANOUT_SHIFT 2 ++ ++/* LEB Properties Tree bit field sizes */ ++#define UBIFS_LPT_CRC_BITS 16 ++#define UBIFS_LPT_CRC_BYTES 2 ++#define UBIFS_LPT_TYPE_BITS 4 ++ ++/* The key is always at the same position in all keyed nodes */ ++#define UBIFS_KEY_OFFSET offsetof(struct ubifs_ino_node, key) ++ ++/* ++ * LEB Properties Tree node types. ++ * ++ * UBIFS_LPT_PNODE: LPT leaf node (contains LEB properties) ++ * UBIFS_LPT_NNODE: LPT internal node ++ * UBIFS_LPT_LTAB: LPT's own lprops table ++ * UBIFS_LPT_LSAVE: LPT's save table (big model only) ++ * UBIFS_LPT_NODE_CNT: count of LPT node types ++ * UBIFS_LPT_NOT_A_NODE: all ones (15 for 4 bits) is never a valid node type ++ */ ++enum { ++ UBIFS_LPT_PNODE, ++ UBIFS_LPT_NNODE, ++ UBIFS_LPT_LTAB, ++ UBIFS_LPT_LSAVE, ++ UBIFS_LPT_NODE_CNT, ++ UBIFS_LPT_NOT_A_NODE = (1 << UBIFS_LPT_TYPE_BITS) - 1, ++}; ++ ++/* ++ * UBIFS inode types. ++ * ++ * UBIFS_ITYPE_REG: regular file ++ * UBIFS_ITYPE_DIR: directory ++ * UBIFS_ITYPE_LNK: soft link ++ * UBIFS_ITYPE_BLK: block device node ++ * UBIFS_ITYPE_CHR: character device node ++ * UBIFS_ITYPE_FIFO: fifo ++ * UBIFS_ITYPE_SOCK: socket ++ * UBIFS_ITYPES_CNT: count of supported file types ++ */ ++enum { ++ UBIFS_ITYPE_REG, ++ UBIFS_ITYPE_DIR, ++ UBIFS_ITYPE_LNK, ++ UBIFS_ITYPE_BLK, ++ UBIFS_ITYPE_CHR, ++ UBIFS_ITYPE_FIFO, ++ UBIFS_ITYPE_SOCK, ++ UBIFS_ITYPES_CNT, ++}; ++ ++/* ++ * Supported key hash functions. ++ * ++ * UBIFS_KEY_HASH_R5: R5 hash ++ * UBIFS_KEY_HASH_TEST: test hash which just returns first 4 bytes of the name ++ */ ++enum { ++ UBIFS_KEY_HASH_R5, ++ UBIFS_KEY_HASH_TEST, ++}; ++ ++/* ++ * Supported key formats. ++ * ++ * UBIFS_SIMPLE_KEY_FMT: simple key format ++ */ ++enum { ++ UBIFS_SIMPLE_KEY_FMT, ++}; ++ ++/* ++ * The simple key format uses 29 bits for storing UBIFS block number and hash ++ * value. ++ */ ++#define UBIFS_S_KEY_BLOCK_BITS 29 ++/* ++ * TODO: this is a temporary hach which was added to support old format and to ++ * avoid breaking binary compatibility. It is temporaty and should go. The ++ * define should be ++ * #define UBIFS_S_KEY_BLOCK_MASK 0x1FFFFFFF ++ */ ++#define UBIFS_S_KEY_BLOCK_MASK (c->fmt_version == 2 ? 0x01FFFFFF : 0x1FFFFFFF) ++#define UBIFS_S_KEY_HASH_BITS UBIFS_S_KEY_BLOCK_BITS ++#define UBIFS_S_KEY_HASH_MASK UBIFS_S_KEY_BLOCK_MASK ++ ++/* ++ * Key types. ++ * ++ * UBIFS_INO_KEY: inode node key ++ * UBIFS_DATA_KEY: data node key ++ * UBIFS_DENT_KEY: directory entry node key ++ * UBIFS_XENT_KEY: extended attribute entry key ++ * UBIFS_TRUN_KEY: truncation node key ++ * UBIFS_KEY_TYPES_CNT: number of supported key types ++ */ ++enum { ++ UBIFS_INO_KEY, ++ UBIFS_DATA_KEY, ++ UBIFS_DENT_KEY, ++ UBIFS_XENT_KEY, ++ UBIFS_TRUN_KEY, ++ UBIFS_KEY_TYPES_CNT, ++}; ++ ++/* Count of LEBs reserved for the superblock area */ ++#define UBIFS_SB_LEBS 1 ++/* Count of LEBs reserved for the master area */ ++#define UBIFS_MST_LEBS 2 ++ ++/* First LEB of the superblock area */ ++#define UBIFS_SB_LNUM 0 ++/* First LEB of the master area */ ++#define UBIFS_MST_LNUM (UBIFS_SB_LNUM + UBIFS_SB_LEBS) ++/* First LEB of the log area */ ++#define UBIFS_LOG_LNUM (UBIFS_MST_LNUM + UBIFS_MST_LEBS) ++ ++/* Minimum number of logical eraseblocks in the log */ ++#define UBIFS_MIN_LOG_LEBS 2 ++/* Minimum number of bud logical eraseblocks */ ++#define UBIFS_MIN_BUD_LEBS 2 ++/* Minimum number of journal logical eraseblocks */ ++#define UBIFS_MIN_JNL_LEBS (UBIFS_MIN_LOG_LEBS + UBIFS_MIN_BUD_LEBS) ++/* Minimum number of LPT area logical eraseblocks */ ++#define UBIFS_MIN_LPT_LEBS 2 ++/* Minimum number of orphan area logical eraseblocks */ ++#define UBIFS_MIN_ORPH_LEBS 1 ++/* Minimum number of main area logical eraseblocks */ ++#define UBIFS_MIN_MAIN_LEBS 8 ++ ++/* Minimum number of logical eraseblocks */ ++#define UBIFS_MIN_LEB_CNT (UBIFS_SB_LEBS + UBIFS_MST_LEBS + \ ++ UBIFS_MIN_LOG_LEBS + UBIFS_MIN_BUD_LEBS + \ ++ UBIFS_MIN_LPT_LEBS + UBIFS_MIN_ORPH_LEBS + \ ++ UBIFS_MIN_MAIN_LEBS) ++ ++/* Node sizes (N.B. these are guaranteed to be multiples of 8) */ ++#define UBIFS_CH_SZ sizeof(struct ubifs_ch) ++#define UBIFS_INO_NODE_SZ sizeof(struct ubifs_ino_node) ++#define UBIFS_DATA_NODE_SZ sizeof(struct ubifs_data_node) ++#define UBIFS_DENT_NODE_SZ sizeof(struct ubifs_dent_node) ++#define UBIFS_TRUN_NODE_SZ sizeof(struct ubifs_trun_node) ++#define UBIFS_PAD_NODE_SZ sizeof(struct ubifs_pad_node) ++#define UBIFS_SB_NODE_SZ sizeof(struct ubifs_sb_node) ++#define UBIFS_MST_NODE_SZ sizeof(struct ubifs_mst_node) ++#define UBIFS_REF_NODE_SZ sizeof(struct ubifs_ref_node) ++#define UBIFS_IDX_NODE_SZ sizeof(struct ubifs_idx_node) ++#define UBIFS_CS_NODE_SZ sizeof(struct ubifs_cs_node) ++#define UBIFS_ORPH_NODE_SZ sizeof(struct ubifs_orph_node) ++/* Extended attribute entry nodes are identical to directory entry nodes */ ++#define UBIFS_XENT_NODE_SZ UBIFS_DENT_NODE_SZ ++/* Only this does not have to be multiple of 8 bytes */ ++#define UBIFS_BRANCH_SZ sizeof(struct ubifs_branch) ++ ++/* Maximum node sizes (N.B. these are guaranteed to be multiples of 8) */ ++#define UBIFS_MAX_DATA_NODE_SZ (UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE) ++#define UBIFS_MAX_INO_NODE_SZ (UBIFS_INO_NODE_SZ + UBIFS_MAX_INO_DATA) ++#define UBIFS_MAX_DENT_NODE_SZ (UBIFS_DENT_NODE_SZ + UBIFS_MAX_NLEN + 1) ++#define UBIFS_MAX_XENT_NODE_SZ UBIFS_MAX_DENT_NODE_SZ ++ ++/* The largest UBIFS node */ ++#define UBIFS_MAX_NODE_SZ UBIFS_MAX_INO_NODE_SZ ++ ++/* ++ * On-flash inode flags. ++ * ++ * UBIFS_COMPR_FL: use compression for this inode ++ * UBIFS_SYNC_FL: I/O on this inode has to be synchronous ++ * UBIFS_IMMUTABLE_FL: inode is immutable ++ * UBIFS_APPEND_FL: writes to the inode may only append data ++ * UBIFS_DIRSYNC_FL: I/O on this directory inode has to be synchronous ++ * ++ * Note, these are on-flash flags which correspond to ioctl flags ++ * (@FS_COMPR_FL, etc). They have the same values now, but generally, do not ++ * have to be the same. ++ */ ++enum { ++ UBIFS_COMPR_FL = 0x01, ++ UBIFS_SYNC_FL = 0x02, ++ UBIFS_IMMUTABLE_FL = 0x04, ++ UBIFS_APPEND_FL = 0x08, ++ UBIFS_DIRSYNC_FL = 0x10, ++}; ++ ++/* Inode flag bits used by UBIFS */ ++#define UBIFS_FL_MASK 0x0000001F ++ ++/* ++ * UBIFS compression types. ++ * ++ * UBIFS_COMPR_NONE: no compression ++ * UBIFS_COMPR_LZO: LZO compression ++ * UBIFS_COMPR_ZLIB: ZLIB compression ++ * UBIFS_COMPR_TYPES_CNT: count of supported compression types ++ */ ++enum { ++ UBIFS_COMPR_NONE, ++ UBIFS_COMPR_LZO, ++ UBIFS_COMPR_ZLIB, ++ UBIFS_COMPR_TYPES_CNT, ++}; ++ ++/* ++ * UBIFS node types. ++ * ++ * UBIFS_INO_NODE: inode node ++ * UBIFS_DATA_NODE: data node ++ * UBIFS_DENT_NODE: directory entry node ++ * UBIFS_XENT_NODE: extended attribute node ++ * UBIFS_TRUN_NODE: truncation node ++ * UBIFS_PAD_NODE: padding node ++ * UBIFS_SB_NODE: superblock node ++ * UBIFS_MST_NODE: master node ++ * UBIFS_REF_NODE: LEB reference node ++ * UBIFS_IDX_NODE: index node ++ * UBIFS_CS_NODE: commit start node ++ * UBIFS_ORPH_NODE: orphan node ++ * UBIFS_NODE_TYPES_CNT: count of supported node types ++ * ++ * Note, we index arrays by these numbers, so keep them low and contiguous. ++ * Node type constants for inodes, direntries and so on have to be the same as ++ * corresponding key type constants. ++ */ ++enum { ++ UBIFS_INO_NODE, ++ UBIFS_DATA_NODE, ++ UBIFS_DENT_NODE, ++ UBIFS_XENT_NODE, ++ UBIFS_TRUN_NODE, ++ UBIFS_PAD_NODE, ++ UBIFS_SB_NODE, ++ UBIFS_MST_NODE, ++ UBIFS_REF_NODE, ++ UBIFS_IDX_NODE, ++ UBIFS_CS_NODE, ++ UBIFS_ORPH_NODE, ++ UBIFS_NODE_TYPES_CNT, ++}; ++ ++/* ++ * Master node flags. ++ * ++ * UBIFS_MST_DIRTY: rebooted uncleanly - master node is dirty ++ * UBIFS_MST_NO_ORPHS: no orphan inodes present ++ * UBIFS_MST_RCVRY: written by recovery ++ */ ++enum { ++ UBIFS_MST_DIRTY = 1, ++ UBIFS_MST_NO_ORPHS = 2, ++ UBIFS_MST_RCVRY = 4, ++}; ++ ++/* ++ * Node group type (used by recovery to recover whole group or none). ++ * ++ * UBIFS_NO_NODE_GROUP: this node is not part of a group ++ * UBIFS_IN_NODE_GROUP: this node is a part of a group ++ * UBIFS_LAST_OF_NODE_GROUP: this node is the last in a group ++ */ ++enum { ++ UBIFS_NO_NODE_GROUP = 0, ++ UBIFS_IN_NODE_GROUP, ++ UBIFS_LAST_OF_NODE_GROUP, ++}; ++ ++/* ++ * Superblock flags. ++ * ++ * UBIFS_FLG_BIGLPT: if "big" LPT model is used if set ++ */ ++enum { ++ UBIFS_FLG_BIGLPT = 0x02, ++}; ++ ++/** ++ * struct ubifs_ch - common header node. ++ * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC) ++ * @crc: CRC-32 checksum of the node header ++ * @sqnum: sequence number ++ * @len: full node length ++ * @node_type: node type ++ * @group_type: node group type ++ * @padding: reserved for future, zeroes ++ * ++ * Every UBIFS node starts with this common part. If the node has a key, the ++ * key always goes next. ++ */ ++struct ubifs_ch { ++ __le32 magic; ++ __le32 crc; ++ __le64 sqnum; ++ __le32 len; ++ __u8 node_type; ++ __u8 group_type; ++ __u8 padding[2]; ++} __attribute__ ((packed)); ++ ++/** ++ * union ubifs_dev_desc - device node descriptor. ++ * @new: new type device descriptor ++ * @huge: huge type device descriptor ++ * ++ * This data structure describes major/minor numbers of a device node. In an ++ * inode is a device node then its data contains an object of this type. UBIFS ++ * uses standard Linux "new" and "huge" device node encodings. ++ */ ++union ubifs_dev_desc { ++ __le32 new; ++ __le64 huge; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_ino_node - inode node. ++ * @ch: common header ++ * @key: node key ++ * @creat_sqnum: sequence number at time of creation ++ * @size: inode size in bytes (amount of uncompressed data) ++ * @atime_sec: access time seconds ++ * @ctime_sec: creation time seconds ++ * @mtime_sec: modification time seconds ++ * @atime_nsec: access time nanoseconds ++ * @ctime_nsec: creation time nanoseconds ++ * @mtime_nsec: modification time nanoseconds ++ * @nlink: number of hard links ++ * @uid: owner ID ++ * @gid: group ID ++ * @mode: access flags ++ * @flags: per-inode flags (%UBIFS_COMPR_FL, %UBIFS_SYNC_FL, etc) ++ * @data_len: inode data length ++ * @xattr_cnt: count of extended attributes this inode has ++ * @xattr_size: summarized size of all extended attributes in bytes ++ * @xattr_names: sum of lengths of all extended attribute names belonging to ++ * this inode ++ * @compr_type: compression type used for this inode ++ * @padding: reserved for future, zeroes ++ * @data: data attached to the inode ++ * ++ * Note, even though inode compression type is defined by @compr_type, some ++ * nodes of this inode may be compressed with different compressor - this ++ * happens if compression type is changed while the inode already has data ++ * nodes. But @compr_type will be use for further writes to the inode. ++ * ++ * Note, do not forget to amend 'zero_ino_node_unused()' function when changing ++ * the padding fields. ++ */ ++struct ubifs_ino_node { ++ struct ubifs_ch ch; ++ __u8 key[UBIFS_MAX_KEY_LEN]; ++ __le64 creat_sqnum; ++ __le64 size; ++ __le64 atime_sec; ++ __le64 ctime_sec; ++ __le64 mtime_sec; ++ __le32 atime_nsec; ++ __le32 ctime_nsec; ++ __le32 mtime_nsec; ++ __le32 nlink; ++ __le32 uid; ++ __le32 gid; ++ __le32 mode; ++ __le32 flags; ++ __le32 data_len; ++ __le32 xattr_cnt; ++ __le64 xattr_size; ++ __le32 xattr_names; ++ __le16 compr_type; ++ __u8 padding[26]; /* Watch 'zero_ino_node_unused()' if changing! */ ++ __u8 data[]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_dent_node - directory entry node. ++ * @ch: common header ++ * @key: node key ++ * @inum: target inode number ++ * @padding1: reserved for future, zeroes ++ * @type: type of the target inode (%UBIFS_ITYPE_REG, %UBIFS_ITYPE_DIR, etc) ++ * @nlen: name length ++ * @padding2: reserved for future, zeroes ++ * @name: zero-terminated name ++ * ++ * Note, do not forget to amend 'zero_dent_node_unused()' function when ++ * changing the padding fields. ++ */ ++struct ubifs_dent_node { ++ struct ubifs_ch ch; ++ __u8 key[UBIFS_MAX_KEY_LEN]; ++ __le64 inum; ++ __u8 padding1; ++ __u8 type; ++ __le16 nlen; ++ __u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */ ++ __u8 name[]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_data_node - data node. ++ * @ch: common header ++ * @key: node key ++ * @size: uncompressed data size in bytes ++ * @compr_type: compression type (%UBIFS_COMPR_NONE, %UBIFS_COMPR_LZO, etc) ++ * @padding: reserved for future, zeroes ++ * @data: data ++ * ++ * Note, do not forget to amend 'zero_data_node_unused()' function when ++ * changing the padding fields. ++ */ ++struct ubifs_data_node { ++ struct ubifs_ch ch; ++ __u8 key[UBIFS_MAX_KEY_LEN]; ++ __le32 size; ++ __le16 compr_type; ++ __u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */ ++ __u8 data[]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_trun_node - truncation node. ++ * @ch: common header ++ * @key: truncation node key ++ * @old_size: size before truncation ++ * @new_size: size after truncation ++ * ++ * This node exists only in the journal and never goes to the main area. ++ */ ++struct ubifs_trun_node { ++ struct ubifs_ch ch; ++ __u8 key[UBIFS_MAX_KEY_LEN]; ++ __le64 old_size; ++ __le64 new_size; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_pad_node - padding node. ++ * @ch: common header ++ * @pad_len: how many bytes after this node are unused (because padded) ++ * @padding: reserved for future, zeroes ++ */ ++struct ubifs_pad_node { ++ struct ubifs_ch ch; ++ __le32 pad_len; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_sb_node - superblock node. ++ * @ch: common header ++ * @padding: reserved for future, zeroes ++ * @key_hash: type of hash function used in keys ++ * @key_fmt: format of the key ++ * @flags: file-system flags (%UBIFS_FLG_BIGLPT, etc) ++ * @min_io_size: minimal input/output unit size ++ * @leb_size: logical eraseblock size in bytes ++ * @leb_cnt: count of LEBs used by filesystem ++ * @max_leb_cnt: maximum count of LEBs used by filesystem ++ * @max_bud_bytes: maximum amount of data stored in buds ++ * @log_lebs: log size in logical eraseblocks ++ * @lpt_lebs: number of LEBs used for lprops table ++ * @orph_lebs: number of LEBs used for recording orphans ++ * @jhead_cnt: count of journal heads ++ * @fanout: tree fanout (max. number of links per indexing node) ++ * @lsave_cnt: number of LEB numbers in LPT's save table ++ * @fmt_version: UBIFS on-flash format version ++ * @default_compr: default compression ++ * @padding1: reserved for future, zeroes ++ * @rp_uid: reserve pool UID ++ * @rp_gid: reserve pool GID ++ * @rp_size: size of the reserved pool in bytes ++ * @padding2: reserved for future, zeroes ++ * @time_gran: time granularity in nanoseconds ++ */ ++struct ubifs_sb_node { ++ struct ubifs_ch ch; ++ __u8 padding[2]; ++ __u8 key_hash; ++ __u8 key_fmt; ++ __le32 flags; ++ __le32 min_io_size; ++ __le32 leb_size; ++ __le32 leb_cnt; ++ __le32 max_leb_cnt; ++ __le64 max_bud_bytes; ++ __le32 log_lebs; ++ __le32 lpt_lebs; ++ __le32 orph_lebs; ++ __le32 jhead_cnt; ++ __le32 fanout; ++ __le32 lsave_cnt; ++ __le32 fmt_version; ++ __le16 default_compr; ++ __u8 padding1[2]; ++ __le32 rp_uid; ++ __le32 rp_gid; ++ __le64 rp_size; ++ __le32 time_gran; ++ __u8 padding2[3988]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_mst_node - master node. ++ * @ch: common header ++ * @highest_inum: highest inode number in the committed index ++ * @cmt_no: commit number ++ * @flags: various flags (%UBIFS_MST_DIRTY, etc) ++ * @log_lnum: start of the log ++ * @root_lnum: LEB number of the root indexing node ++ * @root_offs: offset within @root_lnum ++ * @root_len: root indexing node length ++ * @gc_lnum: LEB reserved for garbage collection (%-1 value means the LEB was ++ * not reserved and should be reserved on mount) ++ * @ihead_lnum: LEB number of index head ++ * @ihead_offs: offset of index head ++ * @index_size: size of index on flash ++ * @total_free: total free space in bytes ++ * @total_dirty: total dirty space in bytes ++ * @total_used: total used space in bytes (includes only data LEBs) ++ * @total_dead: total dead space in bytes (includes only data LEBs) ++ * @total_dark: total dark space in bytes (includes only data LEBs) ++ * @lpt_lnum: LEB number of LPT root nnode ++ * @lpt_offs: offset of LPT root nnode ++ * @nhead_lnum: LEB number of LPT head ++ * @nhead_offs: offset of LPT head ++ * @ltab_lnum: LEB number of LPT's own lprops table ++ * @ltab_offs: offset of LPT's own lprops table ++ * @lsave_lnum: LEB number of LPT's save table (big model only) ++ * @lsave_offs: offset of LPT's save table (big model only) ++ * @lscan_lnum: LEB number of last LPT scan ++ * @empty_lebs: number of empty logical eraseblocks ++ * @idx_lebs: number of indexing logical eraseblocks ++ * @leb_cnt: count of LEBs used by filesystem ++ * @padding: reserved for future, zeroes ++ */ ++struct ubifs_mst_node { ++ struct ubifs_ch ch; ++ __le64 highest_inum; ++ __le64 cmt_no; ++ __le32 flags; ++ __le32 log_lnum; ++ __le32 root_lnum; ++ __le32 root_offs; ++ __le32 root_len; ++ __le32 gc_lnum; ++ __le32 ihead_lnum; ++ __le32 ihead_offs; ++ __le64 index_size; ++ __le64 total_free; ++ __le64 total_dirty; ++ __le64 total_used; ++ __le64 total_dead; ++ __le64 total_dark; ++ __le32 lpt_lnum; ++ __le32 lpt_offs; ++ __le32 nhead_lnum; ++ __le32 nhead_offs; ++ __le32 ltab_lnum; ++ __le32 ltab_offs; ++ __le32 lsave_lnum; ++ __le32 lsave_offs; ++ __le32 lscan_lnum; ++ __le32 empty_lebs; ++ __le32 idx_lebs; ++ __le32 leb_cnt; ++ __u8 padding[344]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_ref_node - logical eraseblock reference node. ++ * @ch: common header ++ * @lnum: the referred logical eraseblock number ++ * @offs: start offset in the referred LEB ++ * @jhead: journal head number ++ * @padding: reserved for future, zeroes ++ */ ++struct ubifs_ref_node { ++ struct ubifs_ch ch; ++ __le32 lnum; ++ __le32 offs; ++ __le32 jhead; ++ __u8 padding[28]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_branch - key/reference/length branch ++ * @lnum: LEB number of the target node ++ * @offs: offset within @lnum ++ * @len: target node length ++ * @key: key ++ */ ++struct ubifs_branch { ++ __le32 lnum; ++ __le32 offs; ++ __le32 len; ++ __u8 key[]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_idx_node - indexing node. ++ * @ch: common header ++ * @child_cnt: number of child index nodes ++ * @level: tree level ++ * @branches: LEB number / offset / length / key branches ++ */ ++struct ubifs_idx_node { ++ struct ubifs_ch ch; ++ __le16 child_cnt; ++ __le16 level; ++ __u8 branches[]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_cs_node - commit start node. ++ * @ch: common header ++ * @cmt_no: commit number ++ */ ++struct ubifs_cs_node { ++ struct ubifs_ch ch; ++ __le64 cmt_no; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubifs_orph_node - orphan node. ++ * @ch: common header ++ * @cmt_no: commit number (also top bit is set on the last node of the commit) ++ * @inos: inode numbers of orphans ++ */ ++struct ubifs_orph_node { ++ struct ubifs_ch ch; ++ __le64 cmt_no; ++ __le64 inos[]; ++} __attribute__ ((packed)); ++ ++#endif /* __UBIFS_MEDIA_H__ */ +diff -urN linux-2.6.24.7.old/fs/ubifs/ubifs.h linux-2.6.24.7/fs/ubifs/ubifs.h +--- linux-2.6.24.7.old/fs/ubifs/ubifs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/ubifs.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1589 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* Implementation version 0.6 */ ++ ++#ifndef __UBIFS_H__ ++#define __UBIFS_H__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ubifs-media.h" ++#include "compat.h" ++ ++/* Version of this UBIFS implementation */ ++#define UBIFS_VERSION 1 ++ ++/* Normal UBIFS messages */ ++#define ubifs_msg(fmt, ...) \ ++ printk(KERN_NOTICE "UBIFS: " fmt "\n", ##__VA_ARGS__) ++/* UBIFS error messages */ ++#define ubifs_err(fmt, ...) \ ++ printk(KERN_ERR "UBIFS error (pid %d): %s: " fmt "\n", current->pid, \ ++ __func__, ##__VA_ARGS__) ++/* UBIFS warning messages */ ++#define ubifs_warn(fmt, ...) \ ++ printk(KERN_WARNING "UBIFS warning (pid %d): %s: " fmt "\n", \ ++ current->pid, __func__, ##__VA_ARGS__) ++ ++/* UBIFS file system VFS magic number */ ++#define UBIFS_SUPER_MAGIC 0x24051905 ++ ++/* Number of UBIFS blocks per VFS page */ ++#define UBIFS_BLOCKS_PER_PAGE (PAGE_CACHE_SIZE / UBIFS_BLOCK_SIZE) ++#define UBIFS_BLOCKS_PER_PAGE_SHIFT (PAGE_CACHE_SHIFT - UBIFS_BLOCK_SHIFT) ++ ++/* "File system end of life" sequence number watermark */ ++#define SQNUM_WARN_WATERMARK 0xFFFFFFFF00000000ULL ++#define SQNUM_WATERMARK 0xFFFFFFFFFF000000ULL ++ ++/* Minimum amount of data UBIFS writes to the flash */ ++#define MIN_WRITE_SZ (UBIFS_DATA_NODE_SZ + 8) ++ ++/* ++ * Currently we do not support inode number overlapping and re-using, so this ++ * watermark defines dangerous inode number level. This should be fixed later, ++ * although it is difficult to exceed current limit. Another option is to use ++ * 64-bit inode numbers, but this means more overhead. ++ */ ++#define INUM_WARN_WATERMARK 0xFFF00000 ++#define INUM_WATERMARK 0xFFFFFF00 ++ ++/* Largest key size supported in this implementation */ ++#define CUR_MAX_KEY_LEN UBIFS_SK_LEN ++ ++/* Maximum number of entries in each LPT (LEB category) heap */ ++#define LPT_HEAP_SZ 256 ++ ++/* ++ * Background thread name pattern. The numbers are UBI device and volume ++ * numbers. ++ */ ++#define BGT_NAME_PATTERN "ubifs_bgt%d_%d" ++ ++/* Default write-buffer synchronization timeout (5 secs) */ ++#define DEFAULT_WBUF_TIMEOUT (5 * HZ) ++ ++/* Maximum possible inode number (only 32-bit inodes are supported now) */ ++#define MAX_INUM 0xFFFFFFFF ++ ++/* Number of non-data journal heads */ ++#define NONDATA_JHEADS_CNT 2 ++ ++/* Garbage collector head */ ++#define GCHD 0 ++/* Base journal head number */ ++#define BASEHD 1 ++/* First "general purpose" journal head */ ++#define DATAHD 2 ++ ++/* ++ * How much a directory entry/extended attribute entry adds to the parent/host ++ * inode. ++ */ ++#define CALC_DENT_SIZE(name_len) ALIGN(UBIFS_DENT_NODE_SZ + (name_len) + 1, 8) ++ ++/* ++ * Znodes which were not touched for 'OLD_ZNODE_AGE' seconds are considered ++ * "old", and znode which were touched last 'YOUNG_ZNODE_AGE' seconds ago are ++ * considered "young". This is used by shrinker when selecting znode to trim ++ * off. ++ */ ++#define OLD_ZNODE_AGE 20 ++#define YOUNG_ZNODE_AGE 5 ++ ++/* ++ * Some compressors, like LZO, may end up with more data then the input buffer. ++ * So UBIFS always allocates larger output buffer, to be sure the compressor ++ * will not corrupt memory in case of worst case compression. ++ */ ++#define WORST_COMPR_FACTOR 2 ++ ++/* Maximum expected tree height for use by bottom_up_buf */ ++#define BOTTOM_UP_HEIGHT 64 ++ ++/* ++ * Znode flags (actually, bit numbers which store the flags). ++ * ++ * DIRTY_ZNODE: znode is dirty ++ * COW_ZNODE: znode is being committed and a new instance of this znode has to ++ * be created before changing this znode ++ * OBSOLETE_ZNODE: znode is obsolete, which means it was deleted, but it is ++ * still in the commit list and the ongoing commit operation ++ * will commit it, and delete this znode after it is done ++ */ ++enum { ++ DIRTY_ZNODE = 0, ++ COW_ZNODE = 1, ++ OBSOLETE_ZNODE = 2 ++}; ++ ++/* ++ * Commit states. ++ * ++ * COMMIT_RESTING: commit is not wanted ++ * COMMIT_BACKGROUND: background commit has been requested ++ * COMMIT_REQUIRED: commit is required ++ * COMMIT_RUNNING_BACKGROUND: background commit is running ++ * COMMIT_RUNNING_REQUIRED: commit is running and it is required ++ * COMMIT_BROKEN: commit failed ++ */ ++enum { ++ COMMIT_RESTING = 0, ++ COMMIT_BACKGROUND, ++ COMMIT_REQUIRED, ++ COMMIT_RUNNING_BACKGROUND, ++ COMMIT_RUNNING_REQUIRED, ++ COMMIT_BROKEN, ++}; ++ ++/* ++ * 'ubifs_scan_a_node()' return values. ++ * ++ * SCANNED_GARBAGE: scanned garbage ++ * SCANNED_EMPTY_SPACE: scanned empty space ++ * SCANNED_A_NODE: scanned a valid node ++ * SCANNED_A_CORRUPT_NODE: scanned a corrupted node ++ * SCANNED_A_BAD_PAD_NODE: scanned a padding node with invalid pad length ++ * ++ * Greater than zero means: 'scanned that number of padding bytes' ++ */ ++enum { ++ SCANNED_GARBAGE = 0, ++ SCANNED_EMPTY_SPACE = -1, ++ SCANNED_A_NODE = -2, ++ SCANNED_A_CORRUPT_NODE = -3, ++ SCANNED_A_BAD_PAD_NODE = -4, ++}; ++ ++/* ++ * LPT cnode flag bits. ++ * ++ * DIRTY_CNODE: cnode is dirty ++ * COW_CNODE: cnode is being committed and must be copied before writing ++ * OBSOLETE_CNODE: cnode is being committed and has been copied (or deleted), ++ * so it can (and must) be freed when the commit is finished ++ */ ++enum { ++ DIRTY_CNODE = 0, ++ COW_CNODE = 1, ++ OBSOLETE_CNODE = 2, ++}; ++ ++/* ++ * Dirty flag bits (lpt_drty_flgs) for LPT special nodes. ++ * ++ * LTAB_DIRTY: ltab node is dirty ++ * LSAVE_DIRTY: lsave node is dirty ++ */ ++enum { ++ LTAB_DIRTY = 1, ++ LSAVE_DIRTY = 2, ++}; ++ ++/* ++ * Return codes used by the garbage collector. ++ * @LEB_FREED: the logical eraseblock was freed and is ready to use ++ * @LEB_FREED_IDX: indexing LEB was freed and can be used only after the commit ++ * @LEB_RETAINED: the logical eraseblock was freed and retained for GC purposes ++ */ ++enum { ++ LEB_FREED, ++ LEB_FREED_IDX, ++ LEB_RETAINED, ++}; ++ ++/** ++ * struct ubifs_old_idx - index node obsoleted since last commit start. ++ * @rb: rb-tree node ++ * @lnum: LEB number of obsoleted index node ++ * @offs: offset of obsoleted index node ++ */ ++struct ubifs_old_idx { ++ struct rb_node rb; ++ int lnum; ++ int offs; ++}; ++ ++/* The below union makes it easier to deal with keys */ ++union ubifs_key { ++ uint8_t u8[CUR_MAX_KEY_LEN]; ++ uint32_t u32[CUR_MAX_KEY_LEN/4]; ++ uint64_t u64[CUR_MAX_KEY_LEN/8]; ++ __le32 j32[CUR_MAX_KEY_LEN/4]; ++}; ++ ++/** ++ * struct ubifs_scan_node - UBIFS scanned node information. ++ * @list: list of scanned nodes ++ * @key: key of node scanned (if it has one) ++ * @sqnum: sequence number ++ * @type: type of node scanned ++ * @offs: offset with LEB of node scanned ++ * @len: length of node scanned ++ * @node: raw node ++ */ ++struct ubifs_scan_node { ++ struct list_head list; ++ union ubifs_key key; ++ unsigned long long sqnum; ++ int type; ++ int offs; ++ int len; ++ void *node; ++}; ++ ++/** ++ * struct ubifs_scan_leb - UBIFS scanned LEB information. ++ * @lnum: logical eraseblock number ++ * @nodes_cnt: number of nodes scanned ++ * @nodes: list of struct ubifs_scan_node ++ * @endpt: end point (and therefore the start of empty space) ++ * @ecc: read returned -EBADMSG ++ * @buf: buffer containing entire LEB scanned ++ */ ++struct ubifs_scan_leb { ++ int lnum; ++ int nodes_cnt; ++ struct list_head nodes; ++ int endpt; ++ int ecc; ++ void *buf; ++}; ++ ++/** ++ * struct ubifs_gced_idx_leb - garbage-collected indexing LEB. ++ * @list: list ++ * @lnum: LEB number ++ * @unmap: OK to unmap this LEB ++ * ++ * This data structure is used to temporary store garbage-collected indexing ++ * LEBs - they are not released immediately, but only after the next commit. ++ * This is needed to guarantee recoverability. ++ */ ++struct ubifs_gced_idx_leb { ++ struct list_head list; ++ int lnum; ++ int unmap; ++}; ++ ++/** ++ * struct ubifs_inode - UBIFS in-memory inode description. ++ * @vfs_inode: VFS inode description object ++ * @creat_sqnum: sequence number at time of creation ++ * @xattr_size: summarized size of all extended attributes in bytes, protected ++ * by @inode->i_lock ++ * @xattr_cnt: count of extended attributes this inode has ++ * @xattr_names: sum of lengths of all extended attribute names belonging to ++ * this inode ++ * @dirty: non-zero if the inode is dirty ++ * @xattr: non-zero if this is an extended attribute inode ++ * @budgeted: non-zero if the inode has been budgeted (used for debugging) ++ * @budg_mutex: serializes inode budgeting and write-back ++ * @flags: inode flags (@UBIFS_COMPR_FL, etc) ++ * @compr_type: default compression type used for this inode ++ * @data_len: length of the data attached to the inode ++ * @data: inode's data ++ * ++ * UBIFS has its own inode mutex, besides the VFS 'i_mutex'. The reason for ++ * this is budgeting - UBIFS has to budget each operation. So, if an operation ++ * is going to mark an inode dirty, it has to allocate budget for this. It ++ * cannot just mark it dirty because there is no guarantee there will be enough ++ * flash space when it is time to write the inode back. This means that UBIFS ++ * has to have full control over "clean <-> dirty" transitions of inodes (and ++ * pages actually, but it is easy for pages, because we have ++ * 'ubifs_prepare_write()' which is called _before_ every page change). But ++ * unfortunately, VFS marks inodes dirty in many places, and it does not ask ++ * the file-system if it is allowed to do so (there is a notifier, but it is ++ * not enough), i.e., there is no mechanism to synchronize with this. So we ++ * introduce our own dirty flag to UBIFS inodes and our own inode mutex to ++ * serialize "clean <-> dirty" transitions. ++ */ ++struct ubifs_inode { ++ struct inode vfs_inode; ++ unsigned long long creat_sqnum; ++ long long xattr_size; ++ int xattr_cnt; ++ int xattr_names; ++ unsigned int dirty:1; ++ unsigned int xattr:1; ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ unsigned int budgeted:1; ++#endif ++ struct mutex budg_mutex; ++ int flags; ++ int compr_type; ++ int data_len; ++ void *data; ++}; ++ ++/** ++ * struct ubifs_unclean_leb - records a LEB recovered under read-only mode. ++ * @list: list ++ * @lnum: LEB number of recovered LEB ++ * @endpt: offset where recovery ended ++ * ++ * This structure records a LEB identified during recovery that needs to be ++ * cleaned but was not because UBIFS was mounted read-only. The information ++ * is used to clean the LEB when remounting to read-write mode. ++ */ ++struct ubifs_unclean_leb { ++ struct list_head list; ++ int lnum; ++ int endpt; ++}; ++ ++/* ++ * LEB properties flags. ++ * ++ * LPROPS_UNCAT: not categorized ++ * LPROPS_DIRTY: dirty > 0, not index ++ * LPROPS_DIRTY_IDX: dirty + free > UBIFS_CH_SZ and index ++ * LPROPS_FREE: free > 0, not empty, not index ++ * LPROPS_HEAP_CNT: number of heaps used for storing categorized LEBs ++ * LPROPS_EMPTY: LEB is empty, not taken ++ * LPROPS_FREEABLE: free + dirty == leb_size, not index, not taken ++ * LPROPS_FRDI_IDX: free + dirty == leb_size and index, may be taken ++ * LPROPS_CAT_MASK: mask for the LEB categories above ++ * LPROPS_TAKEN: LEB was taken (this flag is not saved on the media) ++ * LPROPS_INDEX: LEB contains indexing nodes (this flag also exists on flash) ++ */ ++enum { ++ LPROPS_UNCAT = 0, ++ LPROPS_DIRTY = 1, ++ LPROPS_DIRTY_IDX = 2, ++ LPROPS_FREE = 3, ++ LPROPS_HEAP_CNT = 3, ++ LPROPS_EMPTY = 4, ++ LPROPS_FREEABLE = 5, ++ LPROPS_FRDI_IDX = 6, ++ LPROPS_CAT_MASK = 15, ++ LPROPS_TAKEN = 16, ++ LPROPS_INDEX = 32, ++}; ++ ++/** ++ * struct ubifs_lprops - logical eraseblock properties. ++ * @free: amount of free space in bytes ++ * @dirty: amount of dirty space in bytes ++ * @flags: LEB properties flags (see above) ++ * @lnum: LEB number ++ * @list: list of same-category lprops (for LPROPS_EMPTY and LPROPS_FREEABLE) ++ * @hpos: heap position in heap of same-category lprops (other categories) ++ */ ++struct ubifs_lprops { ++ int free; ++ int dirty; ++ int flags; ++ int lnum; ++ union { ++ struct list_head list; ++ int hpos; ++ }; ++}; ++ ++/** ++ * struct ubifs_lpt_lprops - LPT logical eraseblock properties. ++ * @free: amount of free space in bytes ++ * @dirty: amount of dirty space in bytes ++ * @tgc: trivial GC flag (1 => unmap after commit end) ++ * @cmt: commit flag (1 => reserved for commit) ++ */ ++struct ubifs_lpt_lprops { ++ int free; ++ int dirty; ++ unsigned tgc : 1; ++ unsigned cmt : 1; ++}; ++ ++/** ++ * struct ubifs_lp_stats - statistics of eraseblocks in the main area. ++ * @empty_lebs: number of empty LEBs ++ * @taken_empty_lebs: number of taken LEBs ++ * @idx_lebs: number of indexing LEBs ++ * @total_free: total free space in bytes ++ * @total_dirty: total dirty space in bytes ++ * @total_used: total used space in bytes (includes only data LEBs) ++ * @total_dead: total dead space in bytes (includes only data LEBs) ++ * @total_dark: total dark space in bytes (includes only data LEBs) ++ * ++ * N.B. total_dirty and total_used are different to other total_* fields, ++ * because they account _all_ LEBs, not just data LEBs. ++ * ++ * 'taken_empty_lebs' counts the LEBs that are in the transient state of having ++ * been 'taken' for use but not yet written to. 'taken_empty_lebs' is needed ++ * to account correctly for gc_lnum, otherwise 'empty_lebs' could be used ++ * by itself (in which case 'unused_lebs' would be a better name). In the case ++ * of gc_lnum, it is 'taken' at mount time or whenever a LEB is retained by GC, ++ * but unlike other empty LEBs that are 'taken', it may not be written straight ++ * away (i.e. before the next commit start or unmount), so either gc_lnum must ++ * be specially accounted for, or the current approach followed i.e. count it ++ * under 'taken_empty_lebs'. ++ */ ++struct ubifs_lp_stats { ++ int empty_lebs; ++ int taken_empty_lebs; ++ int idx_lebs; ++ long long total_free; ++ long long total_dirty; ++ long long total_used; ++ long long total_dead; ++ long long total_dark; ++}; ++ ++struct ubifs_nnode; ++ ++/** ++ * struct ubifs_cnode - LEB Properties Tree common node. ++ * @parent: parent nnode ++ * @cnext: next cnode to commit ++ * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) ++ * @iip: index in parent ++ * @level: level in the tree (zero for pnodes, greater than zero for nnodes) ++ * @num: node number ++ */ ++struct ubifs_cnode { ++ struct ubifs_nnode *parent; ++ struct ubifs_cnode *cnext; ++ unsigned long flags; ++ int iip; ++ int level; ++ int num; ++}; ++ ++/** ++ * struct ubifs_pnode - LEB Properties Tree leaf node. ++ * @parent: parent nnode ++ * @cnext: next cnode to commit ++ * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) ++ * @iip: index in parent ++ * @level: level in the tree (always zero for pnodes) ++ * @num: node number ++ * @lprops: LEB properties array ++ */ ++struct ubifs_pnode { ++ struct ubifs_nnode *parent; ++ struct ubifs_cnode *cnext; ++ unsigned long flags; ++ int iip; ++ int level; ++ int num; ++ struct ubifs_lprops lprops[UBIFS_LPT_FANOUT]; ++}; ++ ++/** ++ * struct ubifs_nbranch - LEB Properties Tree internal node branch. ++ * @lnum: LEB number of child ++ * @offs: offset of child ++ * @nnode: nnode child ++ * @pnode: pnode child ++ * @cnode: cnode child ++ */ ++struct ubifs_nbranch { ++ int lnum; ++ int offs; ++ union { ++ struct ubifs_nnode *nnode; ++ struct ubifs_pnode *pnode; ++ struct ubifs_cnode *cnode; ++ }; ++}; ++ ++/** ++ * struct ubifs_nnode - LEB Properties Tree internal node. ++ * @parent: parent nnode ++ * @cnext: next cnode to commit ++ * @flags: flags (%DIRTY_LPT_NODE or %OBSOLETE_LPT_NODE) ++ * @iip: index in parent ++ * @level: level in the tree (always greater than zero for nnodes) ++ * @num: node number ++ * @nbranch: branches to child nodes ++ */ ++struct ubifs_nnode { ++ struct ubifs_nnode *parent; ++ struct ubifs_cnode *cnext; ++ unsigned long flags; ++ int iip; ++ int level; ++ int num; ++ struct ubifs_nbranch nbranch[UBIFS_LPT_FANOUT]; ++}; ++ ++/** ++ * struct ubifs_lpt_heap - heap of categorized lprops. ++ * @arr: heap array ++ * @cnt: number in heap ++ * @max_cnt: maximum number allowed in heap ++ * ++ * There are %LPROPS_HEAP_CNT heaps. ++ */ ++struct ubifs_lpt_heap { ++ struct ubifs_lprops **arr; ++ int cnt; ++ int max_cnt; ++}; ++ ++/* ++ * Return codes for LPT scan callback function. ++ * ++ * LPT_SCAN_CONTINUE: continue scanning ++ * LPT_SCAN_ADD: add the LEB properties scanned to the tree in memory ++ * LPT_SCAN_STOP: stop scanning ++ */ ++enum { ++ LPT_SCAN_CONTINUE = 0, ++ LPT_SCAN_ADD = 1, ++ LPT_SCAN_STOP = 2, ++}; ++ ++struct ubifs_info; ++ ++/* Callback used by the 'ubifs_lpt_scan_nolock()' function */ ++typedef int (*ubifs_lpt_scan_callback)(struct ubifs_info *c, ++ const struct ubifs_lprops *lprops, ++ int in_tree, void *data); ++ ++/** ++ * struct ubifs_wbuf - UBIFS write-buffer. ++ * @c: UBIFS file-system description object ++ * @buf: write-buffer (of min. flash I/O unit size) ++ * @lnum: logical eraseblock number the write-buffer points to ++ * @offs: write-buffer offset in this logical eraseblock ++ * @avail: number of bytes available in the write-buffer ++ * @used: number of used bytes in the write-buffer ++ * @dtype: type of data stored in this LEB (%UBI_LONGTERM, %UBI_SHORTTERM, ++ * %UBI_UNKNOWN) ++ * @jhead: journal head the mutex belongs to (note, needed only to shut lockdep ++ * up by 'mutex_lock_nested()). ++ * @sync_callback: write-buffer synchronization callback ++ * @io_mutex: serializes write-buffer I/O ++ * @lock: serializes @buf, @lnum, @offs, @avail, @used, @next_ino and @inodes ++ * fields ++ * @timer: write-buffer timer ++ * @timeout: timer expire interval in jiffies ++ * @need_sync: it is set if its timer expired and needs sync ++ * @next_ino: points to the next position of the following inode number ++ * @inodes: stores the inode numbers of the nodes which are in wbuf ++ * ++ * The write-buffer synchronization callback is called when the write-buffer is ++ * synchronized in order to notify how much space was wasted due to ++ * write-buffer padding and how much free space is left in the LEB. ++ * ++ * Note: the fields @buf, @lnum, @offs, @avail and @used can be read under ++ * spin-lock or mutex because they are written under both mutex and spin-lock. ++ * @buf is appended to under mutex but overwritten under both mutex and ++ * spin-lock. Thus the data between @buf and @buf + @used can be read under ++ * spinlock. ++ */ ++struct ubifs_wbuf { ++ struct ubifs_info *c; ++ void *buf; ++ int lnum; ++ int offs; ++ int avail; ++ int used; ++ int dtype; ++ int jhead; ++ int (*sync_callback)(struct ubifs_info *c, int lnum, int free, int pad); ++ struct mutex io_mutex; ++ spinlock_t lock; ++ struct timer_list timer; ++ int timeout; ++ int need_sync; ++ int next_ino; ++ ino_t *inodes; ++}; ++ ++/** ++ * struct ubifs_bud - bud logical eraseblock. ++ * @lnum: logical eraseblock number ++ * @start: where the (uncommitted) bud data starts ++ * @jhead: journal head number this bud belongs to ++ * @list: link in the list buds belonging to the same journal head ++ * @rb: link in the tree of all buds ++ */ ++struct ubifs_bud { ++ int lnum; ++ int start; ++ int jhead; ++ struct list_head list; ++ struct rb_node rb; ++}; ++ ++/** ++ * struct ubifs_jhead - journal head. ++ * @wbuf: head's write-buffer ++ * @buds_list: list of bud LEBs belonging to this journal head ++ * ++ * Note, the @buds list is protected by the @c->buds_lock. ++ */ ++struct ubifs_jhead { ++ struct ubifs_wbuf wbuf; ++ struct list_head buds_list; ++}; ++ ++/** ++ * struct ubifs_zbranch - key/coordinate/length branch stored in znodes. ++ * @key: key ++ * @znode: znode address in memory ++ * @lnum: LEB number of the indexing node ++ * @offs: offset of the indexing node within @lnum ++ * @len: target node length ++ */ ++struct ubifs_zbranch { ++ union ubifs_key key; ++ union { ++ struct ubifs_znode *znode; ++ void *leaf; ++ }; ++ int lnum; ++ int offs; ++ int len; ++}; ++ ++/** ++ * struct ubifs_znode - in-memory representation of an indexing node. ++ * @parent: parent znode or NULL if it is the root ++ * @cnext: next znode to commit ++ * @flags: znode flags (%DIRTY_ZNODE, %COW_ZNODE or %OBSOLETE_ZNODE) ++ * @time: last access time (seconds) ++ * @level: level of the entry in the TNC tree ++ * @child_cnt: count of child znodes ++ * @iip: index in parent's zbranch array ++ * @alt: lower bound of key range has altered i.e. child inserted at slot 0 ++ * @lnum: LEB number of the corresponding indexing node ++ * @offs: offset of the corresponding indexing node ++ * @len: length of the corresponding indexing node ++ * @zbranch: array of znode branches (@c->fanout elements) ++ */ ++struct ubifs_znode { ++ struct ubifs_znode *parent; ++ struct ubifs_znode *cnext; ++ unsigned long flags; ++ unsigned long time; ++ int level; ++ int child_cnt; ++ int iip; ++ int alt; ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ int lnum, offs, len; ++#endif ++ struct ubifs_zbranch zbranch[]; ++}; ++ ++/** ++ * struct ubifs_node_range - node length range description data structure. ++ * @len: fixed node length ++ * @min_len: minimum possible node length ++ * @max_len: maximum possible node length ++ * ++ * If @max_len is %0, the node has fixed length @len. ++ */ ++struct ubifs_node_range { ++ union { ++ int len; ++ int min_len; ++ }; ++ int max_len; ++}; ++ ++/** ++ * struct ubifs_compressor - UBIFS compressor description structure. ++ * @compr_type: compressor type (%UBIFS_COMPR_LZO, etc) ++ * @cc: cryptoapi compressor handle ++ * @comp_mutex: mutex used during compression ++ * @decomp_mutex: mutex used during decompression ++ * @name: compressor name ++ * @capi_name: cryptoapi compressor name ++ */ ++struct ubifs_compressor { ++ int compr_type; ++ struct crypto_comp *cc; ++ struct mutex *comp_mutex; ++ struct mutex *decomp_mutex; ++ const char *name; ++ const char *capi_name; ++}; ++ ++/** ++ * struct ubifs_budget_req - budget requirements of an operation. ++ * ++ * @new_ino: non-zero if the operation adds a new inode ++ * @dirtied_ino: how many inodes the operation makes dirty ++ * @new_page: non-zero if the operation adds a new page ++ * @dirtied_page: non-zero if the operation makes a page dirty ++ * @new_dent: non-zero if the operation adds a new directory entry ++ * @mod_dent: non-zero if the operation removes or modifies an existing ++ * directory entry ++ * @new_ino_d: now much data newly created inode contains ++ * @dirtied_ino_d: now much data dirtied inode contains ++ * @idx_growth: how much the index will supposedly grow ++ * @data_growth: how much new data the operation will supposedly add ++ * @dd_growth: how much data that makes other data dirty the operation will ++ * supposedly add ++ * ++ * @idx_growth, @data_growth and @dd_growth are not used in budget request. The ++ * budgeting subsystem caches index and data growth values there to avoid ++ * re-calculating them when the budget is released. However, if @idx_growth is ++ * %-1, it is calculated by the release function using other fields. ++ * ++ * An inode may contain 4KiB of data at max., thus the widths of @new_ino_d ++ * is 13 bits, and @dirtied_ino_d - 15, because up to 4 inodes may be made ++ * dirty by the re-name operation. ++ */ ++struct ubifs_budget_req { ++ unsigned int new_ino:1; ++ unsigned int dirtied_ino:4; ++ unsigned int new_page:1; ++ unsigned int dirtied_page:1; ++ unsigned int new_dent:1; ++ unsigned int mod_dent:1; ++/* TODO: remove compatibility stuff as late as possible */ ++#ifdef UBIFS_COMPAT_USE_OLD_PREPARE_WRITE ++ unsigned int locked_pg:1; ++#endif ++ unsigned int new_ino_d:13; ++ unsigned int dirtied_ino_d:15; ++ int idx_growth; ++ int data_growth; ++ int dd_growth; ++}; ++ ++/** ++ * struct ubifs_orphan - stores the inode number of an orphan. ++ * @rb: rb-tree node of rb-tree of orphans sorted by inode number ++ * @list: list head of list of orphans in order added ++ * @new_list: list head of list of orphans added since the last commit ++ * @cnext: next orphan to commit ++ * @dnext: next orphan to delete ++ * @inum: inode number ++ * @new: %1 => added since the last commit, otherwise %0 ++ */ ++struct ubifs_orphan { ++ struct rb_node rb; ++ struct list_head list; ++ struct list_head new_list; ++ struct ubifs_orphan *cnext; ++ struct ubifs_orphan *dnext; ++ ino_t inum; ++ int new; ++}; ++ ++/** ++ * struct ubifs_mount_opts - UBIFS-specific mount options information. ++ * @unmount_mode: selected unmount mode (%0 default, %1 normal, %2 fast) ++ */ ++struct ubifs_mount_opts { ++ unsigned int unmount_mode:2; ++}; ++ ++/** ++ * struct ubifs_info - UBIFS file-system description data structure ++ * (per-superblock). ++ * @vfs_sb: VFS @struct super_block object ++ * ++ * @highest_inum: highest used inode number ++ * @vfs_gen: VFS inode generation counter ++ * @max_sqnum: current global sequence number ++ * @cmt_no: commit number (last successfully completed commit) ++ * @cnt_lock: protects @highest_inum, @vfs_gen, and @max_sqnum counters ++ * @fmt_version: UBIFS on-flash format version ++ * ++ * @lhead_lnum: log head logical eraseblock number ++ * @lhead_offs: log head offset ++ * @ltail_lnum: log tail logical eraseblock number (offset is always 0) ++ * @log_mutex: protects the log, @lhead_lnum, @lhead_offs, @ltail_lnum, and ++ * @bud_bytes ++ * @min_log_bytes: minimum required number of bytes in the log ++ * @cmt_bud_bytes: used during commit to temporarily amount of bytes in ++ * committed buds ++ * ++ * @buds: tree of all buds indexed by bud LEB number ++ * @bud_bytes: how many bytes of flash is used by buds ++ * @buds_lock: protects the @buds tree, @bud_bytes, and per-journal head bud ++ * lists ++ * @jhead_cnt: count of journal heads ++ * @jheads: journal heads (head zero is base head) ++ * @max_bud_bytes: maximum number of bytes allowed in buds ++ * @bg_bud_bytes: number of bud bytes when background commit is initiated ++ * @old_buds: buds to be released after commit ends ++ * @max_bud_cnt: maximum number of buds ++ * ++ * @commit_sem: synchronizes committer with other processes ++ * @cmt_state: commit state ++ * @cs_lock: commit state lock ++ * @cmt_wq: wait queue to sleep on if the log is full and a commit is running ++ * @fast_unmount: do not run journal commit before unmounting ++ * @big_lpt: flag that LPT is too big to write whole during commit ++ * ++ * @tnc_mutex: protects the Tree Node Cache (TNC), @zroot, @cnext, @enext, and ++ * @calc_idx_sz ++ * @zroot: zbranch which points to the root index node and znode ++ * @cnext: next znode to commit ++ * @enext: next znode to commit to empty space ++ * @gap_lebs: array of LEBs used by the in-gaps commit method ++ * @cbuf: commit buffer ++ * @ileb_buf: buffer for commit in-the-gaps method ++ * @ileb_len: length of data in ileb_buf ++ * @ihead_lnum: LEB number of index head ++ * @ihead_offs: offset of index head ++ * @ilebs: pre-allocated index LEBs ++ * @ileb_cnt: number of pre-allocated index LEBs ++ * @ileb_nxt: next pre-allocated index LEBs ++ * @old_idx: tree of index nodes obsoleted since the last commit start ++ * @bottom_up_buf: a buffer which is used by 'dirty_cow_bottom_up()' in tnc.c ++ * @new_ihead_lnum: used by debugging to check ihead_lnum ++ * @new_ihead_offs: used by debugging to check ihead_offs ++ * ++ * @mst_node: master node ++ * @mst_offs: offset of valid master node ++ * @mst_mutex: protects the master node area, @mst_node, and @mst_offs ++ * ++ * @log_lebs: number of logical eraseblocks in the log ++ * @log_bytes: log size in bytes ++ * @log_last: last LEB of the log ++ * @lpt_lebs: number of LEBs used for lprops table ++ * @lpt_first: first LEB of the lprops table area ++ * @lpt_last: last LEB of the lprops table area ++ * @orph_lebs: number of LEBs used for the orphan area ++ * @orph_first: first LEB of the orphan area ++ * @orph_last: last LEB of the orphan area ++ * @main_lebs: count of LEBs in the main area ++ * @main_first: first LEB of the main area ++ * @main_bytes: main area size in bytes ++ * @default_compr: default compression type ++ * ++ * @key_hash_type: type of the key hash ++ * @key_hash: direntry key hash function ++ * @key_fmt: key format ++ * @key_len: key length ++ * @fanout: fanout of the index tree (number of links per indexing node) ++ * ++ * @min_io_size: minimal input/output unit size ++ * @min_io_shift: number of bits in @min_io_size minus one ++ * @leb_size: logical eraseblock size in bytes ++ * @half_leb_size: half LEB size ++ * @leb_cnt: count of logical eraseblocks ++ * @max_leb_cnt: maximum count of logical eraseblocks ++ * @old_leb_cnt: count of logical eraseblocks before resize ++ * @ro_media: the underlying UBI volume is read-only ++ * ++ * @dirty_pg_cnt: number of dirty pages (not used) ++ * @dirty_ino_cnt: number of dirty inodes (not used) ++ * @dirty_zn_cnt: number of dirty znodes ++ * @clean_zn_cnt: number of clean znodes ++ * ++ * @budg_idx_growth: amount of bytes budgeted for index growth ++ * @budg_data_growth: amount of bytes budgeted for cached data ++ * @budg_dd_growth: amount of bytes budgeted for cached data that will make ++ * other data dirty ++ * @budg_uncommitted_idx: amount of bytes were budgeted for growth of the index, ++ * but which still have to be taken into account because ++ * the index has not been committed so far ++ * @space_lock: protects @budg_idx_growth, @budg_data_growth, @budg_dd_growth, ++ * @budg_uncommited_idx, @min_idx_lebs, @old_idx_sz, and @lst; ++ * @min_idx_lebs: minimum number of LEBs required for the index ++ * @old_idx_sz: size of index on flash ++ * @calc_idx_sz: temporary variable which is used to calculate new index size ++ * (contains accurate new index size at end of TNC commit start) ++ * @lst: lprops statistics ++ * ++ * @page_budget: budget for a page ++ * @inode_budget: budget for an inode ++ * @dent_budget: budget for a directory entry ++ * ++ * @ref_node_alsz: size of the LEB reference node aligned to the min. flash ++ * I/O unit ++ * @mst_node_alsz: master node aligned size ++ * @min_idx_node_sz: minimum indexing node aligned on 8-bytes boundary ++ * @max_idx_node_sz: maximum indexing node aligned on 8-bytes boundary ++ * @max_inode_sz: maximum possible inode size in bytes ++ * @max_znode_sz: size of znode in bytes ++ * @dead_wm: LEB dead space watermark ++ * @dark_wm: LEB dark space watermark ++ * @block_cnt: count of 4KiB blocks on the FS ++ * ++ * @ranges: UBIFS node length ranges ++ * @ubi: UBI volume descriptor ++ * @di: UBI device information ++ * @vi: UBI volume information ++ * ++ * @orph_tree: rb-tree of orphan inode numbers ++ * @orph_list: list of orphan inode numbers in order added ++ * @orph_new: list of orphan inode numbers added since last commit ++ * @orph_cnext: next orphan to commit ++ * @orph_dnext: next orphan to delete ++ * @orphan_lock: lock for orph_tree and orph_new ++ * @orph_buf: buffer for orphan nodes ++ * @new_orphans: number of orphans since last commit ++ * @cmt_orphans: number of orphans being committed ++ * @tot_orphans: number of orphans in the rb_tree ++ * @max_orphans: maximum number of orphans allowed ++ * @ohead_lnum: orphan head LEB number ++ * @ohead_offs: orphan head offset ++ * @no_orphs: non-zero if there are no orphans ++ * ++ * @bgt: UBIFS background thread ++ * @bgt_name: background thread name ++ * @need_bgt: if background thread should run ++ * @need_wbuf_sync: if write-buffers have to be synchronized ++ * ++ * @gc_lnum: LEB number used for garbage collection ++ * @sbuf: a buffer of LEB size used by GC and replay for scanning ++ * @idx_gc: list of index LEBs that have been garbage collected ++ * @idx_gc_cnt: number of elements on the idx_gc list ++ * ++ * @infos_list: links all 'ubifs_info' objects ++ * @umount_mutex: serializes shrinker and un-mount ++ * @shrinker_run_no: shrinker run number ++ * ++ * @space_bits: number of bits needed to record free or dirty space ++ * @lpt_lnum_bits: number of bits needed to record a LEB number in the LPT ++ * @lpt_offs_bits: number of bits needed to record an offset in the LPT ++ * @lpt_spc_bits: number of bits needed to space in the LPT ++ * @pcnt_bits: number of bits needed to record pnode or nnode number ++ * @lnum_bits: number of bits needed to record LEB number ++ * @nnode_sz: size of on-flash nnode ++ * @pnode_sz: size of on-flash pnode ++ * @ltab_sz: size of on-flash LPT lprops table ++ * @lsave_sz: size of on-flash LPT save table ++ * @pnode_cnt: number of pnodes ++ * @nnode_cnt: number of nnodes ++ * @lpt_hght: height of the LPT ++ * @pnodes_have: number of pnodes in memory ++ * ++ * @lp_mutex: protects lprops table and all the other lprops-related fields ++ * @lpt_lnum: LEB number of the root nnode of the LPT ++ * @lpt_offs: offset of the root nnode of the LPT ++ * @nhead_lnum: LEB number of LPT head ++ * @nhead_offs: offset of LPT head ++ * @lpt_drty_flgs: dirty flags for LPT special nodes e.g. ltab ++ * @dirty_nn_cnt: number of dirty nnodes ++ * @dirty_pn_cnt: number of dirty pnodes ++ * @lpt_sz: LPT size ++ * @lpt_nod_buf: buffer for an on-flash nnode or pnode ++ * @lpt_buf: buffer of LEB size used by LPT ++ * @nroot: address in memory of the root nnode of the LPT ++ * @lpt_cnext: next LPT node to commit ++ * @lpt_heap: array of heaps of categorized lprops ++ * @dirty_idx: a (reverse sorted) copy of the LPROPS_DIRTY_IDX heap as at ++ * previous commit start ++ * @uncat_list: list of un-categorized LEBs ++ * @empty_list: list of empty LEBs ++ * @freeable_list: list of freeable non-index LEBs (free + dirty == leb_size) ++ * @frdi_idx_list: list of freeable index LEBs (free + dirty == leb_size) ++ * @freeable_cnt: number of freeable LEBs in @freeable_list ++ * ++ * @ltab_lnum: LEB number of LPT's own lprops table ++ * @ltab_offs: offset of LPT's own lprops table ++ * @ltab: LPT's own lprops table ++ * @ltab_cmt: LPT's own lprops table (commit copy) ++ * @lsave_cnt: number of LEB numbers in LPT's save table ++ * @lsave_lnum: LEB number of LPT's save table ++ * @lsave_offs: offset of LPT's save table ++ * @lsave: LPT's save table ++ * @lscan_lnum: LEB number of last LPT scan ++ * ++ * @rp_size: size of the reserved pool in bytes ++ * @report_rp_size: size of the reserved pool reported to userspace ++ * @rp_uid: reserved pool user ID ++ * @rp_gid: reserved pool group ID ++ * ++ * @empty: if the UBI device is empty ++ * @replay_tree: temporary tree used during journal replay ++ * @replay_list: temporary list used during journal replay ++ * @replay_buds: list of buds to replay ++ * @cs_sqnum: sequence number of first node in the log (commit start node) ++ * @replay_sqnum: sequence number of node currently being replayed ++ * @need_recovery: file-system needs recovery ++ * @replaying: set to %1 during journal replay ++ * @unclean_leb_list: LEBs to recover when mounting ro to rw ++ * @rcvrd_mst_node: recovered master node to write when mounting ro to rw ++ * @size_tree: inode size information for recovery ++ * @recovery_needs_commit: a commit must be done before unmounting ++ * @remounting_rw: set while remounting from ro to rw (sb flags have MS_RDONLY) ++ * @mount_opts: UBIFS-specific mount options ++ * ++ * @dbg_buf: a buffer of LEB size used for debugging purposes ++ * @old_zroot: old index root - used by 'dbg_check_old_index()' ++ * @old_zroot_level: old index root level - used by 'dbg_check_old_index()' ++ * @old_zroot_sqnum: old index root sqnum - used by 'dbg_check_old_index()' ++ * @failure_mode: failure mode for recovery testing ++ * @fail_delay: 0=>don't delay, 1=>delay a time, 2=>delay a number of calls ++ * @fail_timeout: time in jiffies when delay of failure mode expires ++ * @fail_cnt: current number of calls to failure mode I/O functions ++ * @fail_cnt_max: number of calls by which to delay failure mode ++ */ ++struct ubifs_info { ++ struct super_block *vfs_sb; ++ ++ ino_t highest_inum; ++ unsigned int vfs_gen; ++ unsigned long long max_sqnum; ++ unsigned long long cmt_no; ++ spinlock_t cnt_lock; ++ int fmt_version; ++ ++ int lhead_lnum; ++ int lhead_offs; ++ int ltail_lnum; ++ struct mutex log_mutex; ++ int min_log_bytes; ++ long long cmt_bud_bytes; ++ ++ struct rb_root buds; ++ long long bud_bytes; ++ spinlock_t buds_lock; ++ int jhead_cnt; ++ struct ubifs_jhead *jheads; ++ long long max_bud_bytes; ++ long long bg_bud_bytes; ++ struct list_head old_buds; ++ int max_bud_cnt; ++ ++ struct rw_semaphore commit_sem; ++ int cmt_state; ++ spinlock_t cs_lock; ++ wait_queue_head_t cmt_wq; ++ unsigned int fast_unmount:1; ++ unsigned int big_lpt:1; ++ ++ struct mutex tnc_mutex; ++ struct ubifs_zbranch zroot; ++ struct ubifs_znode *cnext; ++ struct ubifs_znode *enext; ++ int *gap_lebs; ++ void *cbuf; ++ void *ileb_buf; ++ int ileb_len; ++ int ihead_lnum; ++ int ihead_offs; ++ int *ilebs; ++ int ileb_cnt; ++ int ileb_nxt; ++ struct rb_root old_idx; ++ int *bottom_up_buf; ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ int new_ihead_lnum; ++ int new_ihead_offs; ++#endif ++ ++ struct ubifs_mst_node *mst_node; ++ int mst_offs; ++ struct mutex mst_mutex; ++ ++ int log_lebs; ++ long long log_bytes; ++ int log_last; ++ int lpt_lebs; ++ int lpt_first; ++ int lpt_last; ++ int orph_lebs; ++ int orph_first; ++ int orph_last; ++ int main_lebs; ++ int main_first; ++ long long main_bytes; ++ int default_compr; ++ ++ uint8_t key_hash_type; ++ uint32_t (*key_hash)(const char *str, int len); ++ int key_fmt; ++ int key_len; ++ int fanout; ++ ++ int min_io_size; ++ int min_io_shift; ++ int leb_size; ++ int half_leb_size; ++ int leb_cnt; ++ int max_leb_cnt; ++ int old_leb_cnt; ++ int ro_media; ++ ++ atomic_long_t dirty_pg_cnt; ++ atomic_long_t dirty_ino_cnt; ++ atomic_long_t dirty_zn_cnt; ++ atomic_long_t clean_zn_cnt; ++ ++ long long budg_idx_growth; ++ long long budg_data_growth; ++ long long budg_dd_growth; ++ long long budg_uncommitted_idx; ++ spinlock_t space_lock; ++ int min_idx_lebs; ++ unsigned long long old_idx_sz; ++ unsigned long long calc_idx_sz; ++ struct ubifs_lp_stats lst; ++ ++ int page_budget; ++ int inode_budget; ++ int dent_budget; ++ ++ int ref_node_alsz; ++ int mst_node_alsz; ++ int min_idx_node_sz; ++ int max_idx_node_sz; ++ long long max_inode_sz; ++ int max_znode_sz; ++ int dead_wm; ++ int dark_wm; ++ int block_cnt; ++ ++ struct ubifs_node_range ranges[UBIFS_NODE_TYPES_CNT]; ++ struct ubi_volume_desc *ubi; ++ struct ubi_device_info di; ++ struct ubi_volume_info vi; ++ ++ struct rb_root orph_tree; ++ struct list_head orph_list; ++ struct list_head orph_new; ++ struct ubifs_orphan *orph_cnext; ++ struct ubifs_orphan *orph_dnext; ++ spinlock_t orphan_lock; ++ void *orph_buf; ++ int new_orphans; ++ int cmt_orphans; ++ int tot_orphans; ++ int max_orphans; ++ int ohead_lnum; ++ int ohead_offs; ++ int no_orphs; ++ ++ struct task_struct *bgt; ++ char bgt_name[sizeof(BGT_NAME_PATTERN) + 9]; ++ int need_bgt; ++ int need_wbuf_sync; ++ ++ int gc_lnum; ++ void *sbuf; ++ struct list_head idx_gc; ++ int idx_gc_cnt; ++ ++ struct list_head infos_list; ++ struct mutex umount_mutex; ++ unsigned int shrinker_run_no; ++ ++ int space_bits; ++ int lpt_lnum_bits; ++ int lpt_offs_bits; ++ int lpt_spc_bits; ++ int pcnt_bits; ++ int lnum_bits; ++ int nnode_sz; ++ int pnode_sz; ++ int ltab_sz; ++ int lsave_sz; ++ int pnode_cnt; ++ int nnode_cnt; ++ int lpt_hght; ++ int pnodes_have; ++ ++ struct mutex lp_mutex; ++ int lpt_lnum; ++ int lpt_offs; ++ int nhead_lnum; ++ int nhead_offs; ++ int lpt_drty_flgs; ++ int dirty_nn_cnt; ++ int dirty_pn_cnt; ++ long long lpt_sz; ++ void *lpt_nod_buf; ++ void *lpt_buf; ++ struct ubifs_nnode *nroot; ++ struct ubifs_cnode *lpt_cnext; ++ struct ubifs_lpt_heap lpt_heap[LPROPS_HEAP_CNT]; ++ struct ubifs_lpt_heap dirty_idx; ++ struct list_head uncat_list; ++ struct list_head empty_list; ++ struct list_head freeable_list; ++ struct list_head frdi_idx_list; ++ int freeable_cnt; ++ ++ int ltab_lnum; ++ int ltab_offs; ++ struct ubifs_lpt_lprops *ltab; ++ struct ubifs_lpt_lprops *ltab_cmt; ++ int lsave_cnt; ++ int lsave_lnum; ++ int lsave_offs; ++ int *lsave; ++ int lscan_lnum; ++ ++ long long rp_size; ++ long long report_rp_size; ++ uid_t rp_uid; ++ gid_t rp_gid; ++ ++ /* The below fields are used only during mounting and re-mounting */ ++ int empty; ++ struct rb_root replay_tree; ++ struct list_head replay_list; ++ struct list_head replay_buds; ++ unsigned long long cs_sqnum; ++ unsigned long long replay_sqnum; ++ int need_recovery; ++ int replaying; ++ struct list_head unclean_leb_list; ++ struct ubifs_mst_node *rcvrd_mst_node; ++ struct rb_root size_tree; ++ int recovery_needs_commit; ++ int remounting_rw; ++ struct ubifs_mount_opts mount_opts; ++ ++#ifdef CONFIG_UBIFS_FS_DEBUG ++ void *dbg_buf; ++ struct ubifs_zbranch old_zroot; ++ int old_zroot_level; ++ unsigned long long old_zroot_sqnum; ++ int failure_mode; ++ int fail_delay; ++ unsigned long fail_timeout; ++ unsigned int fail_cnt; ++ unsigned int fail_cnt_max; ++#endif ++}; ++ ++extern struct list_head ubifs_infos; ++extern spinlock_t ubifs_infos_lock; ++extern atomic_long_t ubifs_clean_zn_cnt; ++extern struct kmem_cache *ubifs_inode_slab; ++extern struct super_operations ubifs_super_operations; ++extern struct address_space_operations ubifs_file_address_operations; ++extern struct file_operations ubifs_file_operations; ++extern struct inode_operations ubifs_file_inode_operations; ++extern struct file_operations ubifs_dir_operations; ++extern struct inode_operations ubifs_dir_inode_operations; ++extern struct inode_operations ubifs_symlink_inode_operations; ++extern struct backing_dev_info ubifs_backing_dev_info; ++extern struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT]; ++ ++/* io.c */ ++int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len); ++int ubifs_wbuf_seek_nolock(struct ubifs_wbuf *wbuf, int lnum, int offs, ++ int dtype); ++int ubifs_wbuf_init(struct ubifs_info *c, struct ubifs_wbuf *wbuf); ++int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len, ++ int lnum, int offs); ++int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len, ++ int lnum, int offs); ++int ubifs_write_node(struct ubifs_info *c, void *node, int len, int lnum, ++ int offs, int dtype); ++int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum, ++ int offs, int quiet); ++void ubifs_prepare_node(struct ubifs_info *c, void *buf, int len, int pad); ++void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last); ++int ubifs_io_init(struct ubifs_info *c); ++void ubifs_pad(const struct ubifs_info *c, void *buf, int pad); ++int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf); ++int ubifs_bg_wbufs_sync(struct ubifs_info *c); ++void ubifs_wbuf_add_ino_nolock(struct ubifs_wbuf *wbuf, ino_t inum); ++int ubifs_sync_wbufs_by_inodes(struct ubifs_info *c, ++ struct inode * const *inodes, int count); ++ ++/* scan.c */ ++struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum, ++ int offs, void *sbuf); ++void ubifs_scan_destroy(struct ubifs_scan_leb *sleb); ++int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum, ++ int offs, int quiet); ++struct ubifs_scan_leb *ubifs_start_scan(const struct ubifs_info *c, int lnum, ++ int offs, void *sbuf); ++void ubifs_end_scan(const struct ubifs_info *c, struct ubifs_scan_leb *sleb, ++ int lnum, int offs); ++int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb, ++ void *buf, int offs); ++void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs, ++ void *buf); ++ ++/* log.c */ ++void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud); ++void ubifs_create_buds_lists(struct ubifs_info *c); ++int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs); ++struct ubifs_bud *ubifs_search_bud(struct ubifs_info *c, int lnum); ++struct ubifs_wbuf *ubifs_get_wbuf(struct ubifs_info *c, int lnum); ++int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum); ++int ubifs_log_end_commit(struct ubifs_info *c, int new_ltail_lnum); ++int ubifs_log_post_commit(struct ubifs_info *c, int old_ltail_lnum); ++int ubifs_consolidate_log(struct ubifs_info *c); ++ ++/* journal.c */ ++int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir, ++ const struct qstr *nm, const struct inode *inode, ++ int deletion, int sync, int xent); ++int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode, ++ const union ubifs_key *key, const void *buf, int len); ++int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode, ++ int last_reference, int sync); ++int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, ++ const struct dentry *old_dentry, ++ const struct inode *new_dir, ++ const struct dentry *new_dentry, int sync); ++int ubifs_jnl_truncate(struct ubifs_info *c, ino_t inum, ++ loff_t old_size, loff_t new_size); ++int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host, ++ const struct inode *inode, const struct qstr *nm, ++ int sync); ++int ubifs_jnl_write_2_inodes(struct ubifs_info *c, const struct inode *inode1, ++ const struct inode *inode2, int sync); ++ ++/* budget.c */ ++int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req); ++void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req); ++int ubifs_budget_inode_op(struct ubifs_info *c, struct inode *inode, ++ struct ubifs_budget_req *req); ++void ubifs_release_ino_dirty(struct ubifs_info *c, struct inode *inode, ++ struct ubifs_budget_req *req); ++void ubifs_cancel_ino_op(struct ubifs_info *c, struct inode *inode, ++ struct ubifs_budget_req *req); ++int ubifs_budget_ino_cleaning(struct ubifs_info *c, struct inode *inode, ++ struct ubifs_budget_req *req); ++void ubifs_release_ino_clean(struct ubifs_info *c, struct inode *inode, ++ struct ubifs_budget_req *req); ++long long ubifs_budg_get_free_space(struct ubifs_info *c); ++int ubifs_calc_min_idx_lebs(struct ubifs_info *c); ++void ubifs_convert_page_budget(struct ubifs_info *c); ++void ubifs_release_new_page_budget(struct ubifs_info *c); ++long long ubifs_calc_available(const struct ubifs_info *c); ++ ++/* find.c */ ++int ubifs_find_free_space(struct ubifs_info *c, int min_space, int *free, ++ int squeeze); ++int ubifs_find_free_leb_for_idx(struct ubifs_info *c); ++int ubifs_find_dirty_leb(struct ubifs_info *c, struct ubifs_lprops *ret_lp, ++ int min_space, int pick_free); ++int ubifs_find_dirty_idx_leb(struct ubifs_info *c); ++int ubifs_save_dirty_idx_lnums(struct ubifs_info *c); ++ ++/* tnc.c */ ++int ubifs_tnc_lookup(struct ubifs_info *c, const union ubifs_key *key, ++ void *node); ++int ubifs_tnc_locate(struct ubifs_info *c, const union ubifs_key *key, ++ void *node, int *lnum, int *offs); ++int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key, ++ void *node, const struct qstr *nm); ++int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum, ++ int offs, int len); ++int ubifs_tnc_replace(struct ubifs_info *c, const union ubifs_key *key, ++ int old_lnum, int old_offs, int lnum, int offs, int len); ++int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key, ++ int lnum, int offs, int len, const struct qstr *nm); ++int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key); ++int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key, ++ const struct qstr *nm); ++int ubifs_tnc_remove_range(struct ubifs_info *c, union ubifs_key *from_key, ++ union ubifs_key *to_key); ++int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum); ++struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c, ++ union ubifs_key *key, ++ const struct qstr *nm); ++void ubifs_tnc_close(struct ubifs_info *c); ++int ubifs_tnc_has_node(struct ubifs_info *c, union ubifs_key *key, int level, ++ int lnum, int offs, int is_idx); ++int ubifs_dirty_idx_node(struct ubifs_info *c, union ubifs_key *key, int level, ++ int lnum, int offs); ++/* Shared by tnc.c for tnc_commit.c */ ++void destroy_old_idx(struct ubifs_info *c); ++int is_idx_node_in_tnc(struct ubifs_info *c, union ubifs_key *key, int level, ++ int lnum, int offs); ++int insert_old_idx_znode(struct ubifs_info *c, struct ubifs_znode *znode); ++ ++/* tnc_misc.c */ ++struct ubifs_znode *ubifs_tnc_levelorder_next(struct ubifs_znode *zr, ++ struct ubifs_znode *znode); ++int ubifs_search_zbranch(const struct ubifs_info *c, ++ const struct ubifs_znode *znode, ++ const union ubifs_key *key, int *n); ++struct ubifs_znode *ubifs_tnc_postorder_first(struct ubifs_znode *znode); ++struct ubifs_znode *ubifs_tnc_postorder_next(struct ubifs_znode *znode); ++long ubifs_destroy_tnc_subtree(struct ubifs_znode *zr); ++ ++/* tnc_commit.c */ ++int ubifs_tnc_start_commit(struct ubifs_info *c, struct ubifs_zbranch *zroot); ++int ubifs_tnc_end_commit(struct ubifs_info *c); ++ ++/* shrinker.c */ ++int ubifs_shrinker(int nr_to_scan, gfp_t gfp_mask); ++ ++/* commit.c */ ++int ubifs_bg_thread(void *info); ++void ubifs_commit_required(struct ubifs_info *c); ++void ubifs_request_bg_commit(struct ubifs_info *c); ++int ubifs_run_commit(struct ubifs_info *c); ++void ubifs_recovery_commit(struct ubifs_info *c); ++int ubifs_gc_should_commit(struct ubifs_info *c); ++void ubifs_wait_for_commit(struct ubifs_info *c); ++ ++/* master.c */ ++int ubifs_read_master(struct ubifs_info *c); ++int ubifs_write_master(struct ubifs_info *c); ++ ++/* sb.c */ ++int ubifs_read_superblock(struct ubifs_info *c); ++struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c); ++int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup); ++ ++/* replay.c */ ++int ubifs_validate_entry(struct ubifs_info *c, ++ const struct ubifs_dent_node *dent); ++int ubifs_replay_journal(struct ubifs_info *c); ++ ++/* gc.c */ ++int ubifs_garbage_collect(struct ubifs_info *c, int anyway); ++int ubifs_gc_start_commit(struct ubifs_info *c); ++int ubifs_gc_end_commit(struct ubifs_info *c); ++void ubifs_destroy_idx_gc(struct ubifs_info *c); ++int ubifs_get_idx_gc_leb(struct ubifs_info *c); ++int ubifs_garbage_collect_leb(struct ubifs_info *c, struct ubifs_lprops *lp); ++ ++/* orphan.c */ ++int ubifs_add_orphan(struct ubifs_info *c, ino_t inum); ++void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum); ++int ubifs_orphan_start_commit(struct ubifs_info *c); ++int ubifs_orphan_end_commit(struct ubifs_info *c); ++int ubifs_mount_orphans(struct ubifs_info *c, int unclean); ++ ++/* lpt.c */ ++int ubifs_calc_lpt_geom(struct ubifs_info *c); ++int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first, ++ int *lpt_lebs, int *big_lpt); ++int ubifs_lpt_init(struct ubifs_info *c, int rd, int wr); ++struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum); ++struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum); ++int ubifs_lpt_scan_nolock(struct ubifs_info *c, int start_lnum, int end_lnum, ++ ubifs_lpt_scan_callback scan_cb, void *data); ++ ++/* Shared by lpt.c for lpt_commit.c */ ++void ubifs_pack_lsave(struct ubifs_info *c, void *buf, int *lsave); ++void ubifs_pack_ltab(struct ubifs_info *c, void *buf, ++ struct ubifs_lpt_lprops *ltab); ++void ubifs_pack_pnode(struct ubifs_info *c, void *buf, ++ struct ubifs_pnode *pnode); ++void ubifs_pack_nnode(struct ubifs_info *c, void *buf, ++ struct ubifs_nnode *nnode); ++struct ubifs_pnode *ubifs_get_pnode(struct ubifs_info *c, ++ struct ubifs_nnode *parent, int iip); ++struct ubifs_nnode *ubifs_get_nnode(struct ubifs_info *c, ++ struct ubifs_nnode *parent, int iip); ++int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip); ++void ubifs_add_lpt_dirt(struct ubifs_info *c, int lnum, int dirty); ++void ubifs_add_nnode_dirt(struct ubifs_info *c, struct ubifs_nnode *nnode); ++uint32_t ubifs_unpack_bits(uint8_t **addr, int *pos, int nrbits); ++struct ubifs_nnode *ubifs_first_nnode(struct ubifs_info *c, int *hght); ++ ++/* lpt_commit.c */ ++int ubifs_lpt_start_commit(struct ubifs_info *c); ++int ubifs_lpt_end_commit(struct ubifs_info *c); ++int ubifs_lpt_post_commit(struct ubifs_info *c); ++void ubifs_lpt_free(struct ubifs_info *c, int wr_only); ++ ++/* lprops.c */ ++void ubifs_get_lprops(struct ubifs_info *c); ++const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c, ++ const struct ubifs_lprops *lp, ++ int free, int dirty, int flags, ++ int idx_gc_cnt); ++void ubifs_release_lprops(struct ubifs_info *c); ++void ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *stats); ++void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops, ++ int cat); ++void ubifs_replace_cat(struct ubifs_info *c, struct ubifs_lprops *old_lprops, ++ struct ubifs_lprops *new_lprops); ++void ubifs_ensure_cat(struct ubifs_info *c, struct ubifs_lprops *lprops); ++int ubifs_categorize_lprops(const struct ubifs_info *c, ++ const struct ubifs_lprops *lprops); ++int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty, ++ int flags_set, int flags_clean, int idx_gc_cnt); ++int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty, ++ int flags_set, int flags_clean); ++int ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp); ++const struct ubifs_lprops *ubifs_fast_find_free(struct ubifs_info *c); ++const struct ubifs_lprops *ubifs_fast_find_empty(struct ubifs_info *c); ++const struct ubifs_lprops *ubifs_fast_find_freeable(struct ubifs_info *c); ++const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c); ++ ++/* file.c */ ++int ubifs_fsync(struct file *file, struct dentry *dentry, int datasync); ++int ubifs_setattr(struct dentry *dentry, struct iattr *attr); ++ ++/* dir.c */ ++struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir, ++ int mode); ++int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry, ++ struct kstat *stat); ++ ++/* xattr.c */ ++int ubifs_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags); ++ssize_t ubifs_getxattr(struct dentry *dentry, const char *name, void *buf, ++ size_t size); ++ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size); ++int ubifs_removexattr(struct dentry *dentry, const char *name); ++ ++/* super.c */ ++struct inode *ubifs_iget(struct super_block *sb, unsigned long inum); ++ ++/* recovery.c */ ++int ubifs_recover_master_node(struct ubifs_info *c); ++int ubifs_write_rcvrd_mst_node(struct ubifs_info *c); ++struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum, ++ int offs, void *sbuf, int grouped); ++struct ubifs_scan_leb *ubifs_recover_log_leb(struct ubifs_info *c, int lnum, ++ int offs, void *sbuf); ++int ubifs_recover_inl_heads(const struct ubifs_info *c, void *sbuf); ++int ubifs_clean_lebs(const struct ubifs_info *c, void *sbuf); ++int ubifs_recover_gc_lnum(struct ubifs_info *c); ++int ubifs_recover_size_accum(struct ubifs_info *c, union ubifs_key *key, ++ int deletion, loff_t new_size); ++int ubifs_recover_size(struct ubifs_info *c); ++void ubifs_destroy_size_tree(struct ubifs_info *c); ++ ++/* ioctl.c */ ++long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); ++void ubifs_set_inode_flags(struct inode *inode); ++#ifdef CONFIG_COMPAT ++long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); ++#endif ++ ++/* compressor.c */ ++int __init ubifs_compressors_init(void); ++void __exit ubifs_compressors_exit(void); ++void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len, ++ int *compr_type); ++int ubifs_decompress(const void *buf, int len, void *out, int *out_len, ++ int compr_type); ++ ++#include "debug.h" ++#include "misc.h" ++#include "key.h" ++ ++#endif /* !__UBIFS_H__ */ +diff -urN linux-2.6.24.7.old/fs/ubifs/xattr.c linux-2.6.24.7/fs/ubifs/xattr.c +--- linux-2.6.24.7.old/fs/ubifs/xattr.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/fs/ubifs/xattr.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,582 @@ ++/* ++ * This file is part of UBIFS. ++ * ++ * Copyright (C) 2006-2008 Nokia Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 51 ++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Authors: Artem Bityutskiy (Битюцкий Ðртём) ++ * Adrian Hunter ++ */ ++ ++/* ++ * This file implements UBIFS extended attributes support. ++ * ++ * Extended attributes are implemented as regular inodes with attached data, ++ * which limits extended attribute size to UBIFS block size (4KiB). Names of ++ * extended attributes are described by extended attribute entries (xentries), ++ * which are almost identical to directory entries, but have different key type. ++ * ++ * In other words, the situation with extended attributes is very similar to ++ * directories. Indeed, any inode (but of course not xattr inodes) may have a ++ * number of associated xentries, just like directory inodes have associated ++ * directory entries. Extended attribute entries store the name of the extended ++ * attribute, the host inode number, and the extended attribute inode number. ++ * Similarly, direntries store the name, the parent and the target inode ++ * numbers. Thus, most of the common UBIFS mechanisms may be re-used for ++ * extended attributes. ++ * ++ * The number of extended attributes is not limited, but there is Linux ++ * limitation on the maximum possible size of the list of all extended ++ * attributes associated with an inode (%XATTR_LIST_MAX), so UBIFS makes sure ++ * the sum of all extended attribute names of the inode does not exceed that ++ * limit. ++ * ++ * Extended attributes are synchronous, which means they are written to the ++ * flash media synchronously and there is no write-back for extended attribute ++ * inodes. The extended attribute values are not stored in compressed form on ++ * the media. ++ * ++ * Since extended attributes are represented by regular inodes, they are cached ++ * in the VFS inode cache. The xentries are cached in the LNC cache (see ++ * tnc.c). ++ * ++ * ACL support is not implemented. ++ */ ++ ++#include ++#include ++#include "ubifs.h" ++ ++/* How many bytes an extended attribute adds to the host inode */ ++#define CALC_XATTR_BYTES(data_len) ALIGN(UBIFS_INO_NODE_SZ + (data_len) + 1, 8) ++ ++/* ++ * Extended attribute type constants. ++ * ++ * USER_XATTR: user extended attribute ("user.*") ++ * TRUSTED_XATTR: trusted extended attribute ("trusted.*) ++ * SECURITY_XATTR: security extended attribute ("security.*") ++ */ ++enum { ++ USER_XATTR, ++ TRUSTED_XATTR, ++ SECURITY_XATTR, ++}; ++ ++static struct inode_operations none_inode_operations; ++static struct address_space_operations none_address_operations; ++static struct file_operations none_file_operations; ++ ++/** ++ * create_xattr - create an extended attribute. ++ * @c: UBIFS file-system description object ++ * @host: host inode ++ * @nm: extended attribute name ++ * @value: extended attribute value ++ * @size: size of extended attribute value ++ * ++ * This is a helper function which creates an extended attribute of name @nm ++ * and value @value for inode @host. The host inode is also updated on flash ++ * because the ctime and extended attribute accounting data changes. This ++ * function returns zero in case of success and a negative error code in case ++ * of failure. ++ */ ++static int create_xattr(struct ubifs_info *c, struct inode *host, ++ const struct qstr *nm, const void *value, int size) ++{ ++ struct ubifs_inode *ui, *host_ui = ubifs_inode(host); ++ struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1, ++ .new_ino_d = size }; ++ struct inode *inode; ++ int err; ++ ++ /* ++ * Linux limits the maximum size of the extended attribute names list ++ * to %XATTR_LIST_MAX. This means we should not allow creating more* ++ * extended attributes if the name list becomes larger. This limitation ++ * is artificial for UBIFS, though. ++ */ ++ if (host_ui->xattr_names + host_ui->xattr_cnt + ++ nm->len + 1 > XATTR_LIST_MAX) ++ return -ENOSPC; ++ ++ err = ubifs_budget_inode_op(c, host, &req); ++ if (err) ++ return err; ++ ++ inode = ubifs_new_inode(c, host, S_IFREG | S_IRWXUGO); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ goto out_budg; ++ } ++ ++ /* Re-define all operations to be "nothing" */ ++ inode->i_mapping->a_ops = &none_address_operations; ++ inode->i_op = &none_inode_operations; ++ inode->i_fop = &none_file_operations; ++ ++ inode->i_flags |= S_SYNC | S_NOATIME | S_NOCMTIME | S_NOQUOTA; ++ ui = ubifs_inode(inode); ++ ui->xattr = 1; ++ ui->data = kmalloc(size, GFP_NOFS); ++ if (!ui->data) { ++ err = -ENOMEM; ++ goto out_inode; ++ } ++ ++ memcpy(ui->data, value, size); ++ host->i_ctime = ubifs_current_time(host); ++ host_ui->xattr_cnt += 1; ++ spin_lock(&host->i_lock); ++ host_ui->xattr_size += CALC_DENT_SIZE(nm->len); ++ host_ui->xattr_size += CALC_XATTR_BYTES(size); ++ spin_unlock(&host->i_lock); ++ host_ui->xattr_names += nm->len; ++ ++ /* ++ * We do not use i_size_write() because nobody can race with us as we ++ * are holding host @host->i_mutex - every xattr operation for this ++ * inode is serialized by it. ++ */ ++ inode->i_size = size; ++ ui->data_len = size; ++ ++ /* ++ * Note, it is important that 'ubifs_jnl_update()' writes the @host ++ * inode last, so when it gets synchronized and the write-buffer is ++ * flushed, the extended attribute is flushed as well. ++ */ ++ err = ubifs_jnl_update(c, host, nm, inode, 0, IS_DIRSYNC(host), 1); ++ if (err) ++ goto out_cancel; ++ ++ ubifs_release_ino_clean(c, host, &req); ++ insert_inode_hash(inode); ++ iput(inode); ++ return 0; ++ ++out_cancel: ++ host_ui->xattr_cnt -= 1; ++ spin_lock(&host->i_lock); ++ host_ui->xattr_size -= CALC_DENT_SIZE(nm->len); ++ host_ui->xattr_size -= CALC_XATTR_BYTES(size); ++ spin_unlock(&host->i_lock); ++out_inode: ++ make_bad_inode(inode); ++ iput(inode); ++out_budg: ++ ubifs_cancel_ino_op(c, host, &req); ++ return err; ++} ++ ++/** ++ * change_xattr - change an extended attribute. ++ * @c: UBIFS file-system description object ++ * @host: host inode ++ * @inode: extended attribute inode ++ * @value: extended attribute value ++ * @size: size of extended attribute value ++ * ++ * This helper function changes the value of extended attribute @inode with new ++ * data from @value. Returns zero in case of success and a negative error code ++ * in case of failure. ++ */ ++static int change_xattr(struct ubifs_info *c, struct inode *host, ++ struct inode *inode, const void *value, int size) ++{ ++ struct ubifs_inode *host_ui = ubifs_inode(host); ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ struct ubifs_budget_req req = { .dirtied_ino = 1, ++ .dirtied_ino_d = ui->data_len }; ++ int err; ++ ++ ubifs_assert(ui->data_len == inode->i_size); ++ ++ err = ubifs_budget_inode_op(c, host, &req); ++ if (err) ++ return err; ++ ++ host->i_ctime = ubifs_current_time(host); ++ spin_lock(&host->i_lock); ++ host_ui->xattr_size -= CALC_XATTR_BYTES(ui->data_len); ++ host_ui->xattr_size += CALC_XATTR_BYTES(size); ++ spin_unlock(&host->i_lock); ++ ++ kfree(ui->data); ++ ui->data = kmalloc(size, GFP_NOFS); ++ if (!ui->data) { ++ err = -ENOMEM; ++ goto out_budg; ++ } ++ ++ memcpy(ui->data, value, size); ++ inode->i_size = size; ++ ui->data_len = size; ++ ++ /* ++ * It is important to write the host inode after the xattr inode ++ * because if the host inode gets synchronized, then the extended ++ * attribute inode gets synchronized, because it goes before the host ++ * inode in the write-buffer. ++ */ ++ err = ubifs_jnl_write_2_inodes(c, inode, host, IS_DIRSYNC(host)); ++ if (err) ++ goto out_cancel; ++ ++ ubifs_release_ino_clean(c, host, &req); ++ return 0; ++ ++out_cancel: ++ spin_lock(&host->i_lock); ++ host_ui->xattr_size -= CALC_XATTR_BYTES(size); ++ host_ui->xattr_size += CALC_XATTR_BYTES(ui->data_len); ++ spin_unlock(&host->i_lock); ++ make_bad_inode(inode); ++out_budg: ++ ubifs_cancel_ino_op(c, host, &req); ++ return err; ++} ++ ++/** ++ * check_namespace - check extended attribute name-space. ++ * @nm: extended attribute name ++ * ++ * This function makes sure the extended attribute name belongs to one of the ++ * supported extended attribute name-spaces. Returns name-space index in case ++ * of success and a negative error code in case of failure. ++ */ ++static int check_namespace(const struct qstr *nm) ++{ ++ int type; ++ ++ if (nm->len > UBIFS_MAX_NLEN) ++ return -ENAMETOOLONG; ++ ++ if (!strncmp(nm->name, XATTR_TRUSTED_PREFIX, ++ XATTR_TRUSTED_PREFIX_LEN)) { ++ if (nm->name[sizeof(XATTR_TRUSTED_PREFIX) - 1] == '\0') ++ return -EINVAL; ++ type = TRUSTED_XATTR; ++ } else if (!strncmp(nm->name, XATTR_USER_PREFIX, ++ XATTR_USER_PREFIX_LEN)) { ++ if (nm->name[XATTR_USER_PREFIX_LEN] == '\0') ++ return -EINVAL; ++ type = USER_XATTR; ++ } else if (!strncmp(nm->name, XATTR_SECURITY_PREFIX, ++ XATTR_SECURITY_PREFIX_LEN)) { ++ if (nm->name[sizeof(XATTR_SECURITY_PREFIX) - 1] == '\0') ++ return -EINVAL; ++ type = SECURITY_XATTR; ++ } else ++ return -EOPNOTSUPP; ++ ++ return type; ++} ++ ++int ubifs_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++{ ++ struct inode *inode, *host = dentry->d_inode; ++ struct ubifs_info *c = host->i_sb->s_fs_info; ++ struct qstr nm = { .name = name, .len = strlen(name) }; ++ struct ubifs_dent_node *xent; ++ union ubifs_key key; ++ int err, type; ++ ++ dbg_gen("xattr '%s', host ino %lu ('%.*s'), size %zd", name, ++ host->i_ino, dentry->d_name.len, dentry->d_name.name, size); ++ ubifs_assert(ubifs_inode(host)->xattr_cnt >= 0); ++ ubifs_assert(ubifs_inode(host)->xattr_size >= 0); ++ ubifs_assert(ubifs_inode(host)->xattr_names >= 0); ++ ++ if (size > UBIFS_MAX_INO_DATA) ++ return -ERANGE; ++ ++ type = check_namespace(&nm); ++ if (type < 0) ++ return type; ++ ++ xent = kmalloc(UBIFS_MAX_XENT_NODE_SZ, GFP_NOFS); ++ if (!xent) ++ return -ENOMEM; ++ ++ /* ++ * The extended attribute entries are stored in LNC, so multiple ++ * look-ups do not involve reading the flash. ++ */ ++ xent_key_init(c, &key, host->i_ino, &nm); ++ err = ubifs_tnc_lookup_nm(c, &key, xent, &nm); ++ if (err) { ++ if (err != -ENOENT) ++ goto out_free; ++ ++ if (flags & XATTR_REPLACE) ++ /* We are asked not to create the xattr */ ++ err = -ENODATA; ++ else ++ err = create_xattr(c, host, &nm, value, size); ++ goto out_free; ++ } ++ ++ if (flags & XATTR_CREATE) { ++ /* We are asked not to replace the xattr */ ++ err = -EEXIST; ++ goto out_free; ++ } ++ ++ inode = ubifs_iget(c->vfs_sb, le64_to_cpu(xent->inum)); ++ if (IS_ERR(inode)) { ++ ubifs_err("dead extended attribute entry, error %d", err); ++ ubifs_ro_mode(c, err); ++ err = PTR_ERR(inode); ++ goto out_free; ++ } ++ ++ err = change_xattr(c, host, inode, value, size); ++ iput(inode); ++ ++out_free: ++ kfree(xent); ++ return err; ++} ++ ++ssize_t ubifs_getxattr(struct dentry *dentry, const char *name, void *buf, ++ size_t size) ++{ ++ struct inode *inode, *host = dentry->d_inode; ++ struct ubifs_info *c = host->i_sb->s_fs_info; ++ struct qstr nm = { .name = name, .len = strlen(name) }; ++ struct ubifs_inode *ui; ++ struct ubifs_dent_node *xent; ++ union ubifs_key key; ++ int err; ++ ++ dbg_gen("xattr '%s', ino %lu ('%.*s'), buf size %zd", name, ++ host->i_ino, dentry->d_name.len, dentry->d_name.name, size); ++ ubifs_assert(ubifs_inode(host)->xattr_cnt >= 0); ++ ubifs_assert(ubifs_inode(host)->xattr_size >= 0); ++ ubifs_assert(ubifs_inode(host)->xattr_names >= 0); ++ ++ err = check_namespace(&nm); ++ if (err < 0) ++ return err; ++ ++ xent = kmalloc(UBIFS_MAX_XENT_NODE_SZ, GFP_NOFS); ++ if (!xent) ++ return -ENOMEM; ++ ++ mutex_lock(&host->i_mutex); ++ xent_key_init(c, &key, host->i_ino, &nm); ++ err = ubifs_tnc_lookup_nm(c, &key, xent, &nm); ++ if (err) { ++ if (err == -ENOENT) ++ err = -ENODATA; ++ goto out_unlock; ++ } ++ ++ inode = ubifs_iget(c->vfs_sb, le64_to_cpu(xent->inum)); ++ if (IS_ERR(inode)) { ++ ubifs_err("dead extended attribute entry, error %d", err); ++ ubifs_ro_mode(c, err); ++ err = PTR_ERR(inode); ++ goto out_unlock; ++ } ++ ++ ui = ubifs_inode(inode); ++ ubifs_assert(inode->i_size == ui->data_len); ++ ubifs_assert(ubifs_inode(host)->xattr_size > ui->data_len); ++ ++ if (buf) { ++ /* If @buf is %NULL we are supposed to return the length */ ++ if (ui->data_len > size) { ++ dbg_err("buffer size %zd, xattr len %d", ++ size, ui->data_len); ++ err = -ERANGE; ++ goto out_iput; ++ } ++ ++ memcpy(buf, ui->data, ui->data_len); ++ } ++ err = ui->data_len; ++ ++out_iput: ++ iput(inode); ++out_unlock: ++ mutex_unlock(&host->i_mutex); ++ kfree(xent); ++ return err; ++} ++ ++ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size) ++{ ++ struct inode *host = dentry->d_inode; ++ struct ubifs_info *c = host->i_sb->s_fs_info; ++ struct ubifs_inode *host_ui = ubifs_inode(host); ++ union ubifs_key key; ++ struct ubifs_dent_node *xent, *pxent = NULL; ++ int err, len, written = 0; ++ struct qstr nm = { .name = NULL }; ++ ++ dbg_gen("ino %lu ('%.*s'), buffer size %zd", host->i_ino, ++ dentry->d_name.len, dentry->d_name.name, size); ++ ubifs_assert(host_ui->xattr_cnt >= 0); ++ ubifs_assert(host_ui->xattr_size >= 0); ++ ubifs_assert(host_ui->xattr_names >= 0); ++ ++ len = host_ui->xattr_names + host_ui->xattr_cnt; ++ if (!buffer) ++ /* ++ * We should return the minimum buffer size which will fit a ++ * null-terminated list of all the extended attribute names. ++ */ ++ return len; ++ ++ if (len > size) ++ return -ERANGE; ++ ++ lowest_xent_key(c, &key, host->i_ino); ++ ++ mutex_lock(&host->i_mutex); ++ while (1) { ++ int type; ++ ++ xent = ubifs_tnc_next_ent(c, &key, &nm); ++ if (unlikely(IS_ERR(xent))) { ++ err = PTR_ERR(xent); ++ break; ++ } ++ ++ nm.name = xent->name; ++ nm.len = le16_to_cpu(xent->nlen); ++ ++ type = check_namespace(&nm); ++ if (unlikely(type < 0)) { ++ err = type; ++ break; ++ } ++ ++ /* Show trusted namespace only for "power" users */ ++ if (type != TRUSTED_XATTR || capable(CAP_SYS_ADMIN)) { ++ memcpy(buffer + written, nm.name, nm.len + 1); ++ written += nm.len + 1; ++ } ++ ++ kfree(pxent); ++ pxent = xent; ++ key_read(c, &xent->key, &key); ++ } ++ mutex_unlock(&host->i_mutex); ++ ++ kfree(pxent); ++ if (err != -ENOENT) { ++ ubifs_err("cannot find next direntry, error %d", err); ++ return err; ++ } ++ ++ ubifs_assert(written <= size); ++ return written; ++} ++ ++static int remove_xattr(struct ubifs_info *c, struct inode *host, ++ struct inode *inode, const struct qstr *nm) ++{ ++ struct ubifs_inode *host_ui = ubifs_inode(host); ++ struct ubifs_inode *ui = ubifs_inode(inode); ++ struct ubifs_budget_req req = { .dirtied_ino = 1, .mod_dent = 1 }; ++ int err; ++ ++ ubifs_assert(ui->data_len == inode->i_size); ++ ++ err = ubifs_budget_inode_op(c, host, &req); ++ if (err) ++ return err; ++ ++ host->i_ctime = ubifs_current_time(host); ++ host_ui->xattr_cnt -= 1; ++ spin_lock(&host->i_lock); ++ host_ui->xattr_size -= CALC_XATTR_BYTES(ui->data_len); ++ spin_unlock(&host->i_lock); ++ host_ui->xattr_names -= nm->len; ++ ++ err = ubifs_jnl_delete_xattr(c, host, inode, nm, IS_DIRSYNC(host)); ++ if (err) ++ goto out_cancel; ++ ++ ubifs_release_ino_clean(c, host, &req); ++ return 0; ++ ++out_cancel: ++ ubifs_cancel_ino_op(c, host, &req); ++ host_ui->xattr_cnt += 1; ++ spin_lock(&host->i_lock); ++ host_ui->xattr_size += CALC_XATTR_BYTES(ui->data_len); ++ spin_unlock(&host->i_lock); ++ make_bad_inode(inode); ++ return err; ++} ++ ++int ubifs_removexattr(struct dentry *dentry, const char *name) ++{ ++ struct inode *inode, *host = dentry->d_inode; ++ struct ubifs_info *c = host->i_sb->s_fs_info; ++ struct qstr nm = { .name = name, .len = strlen(name) }; ++ struct ubifs_dent_node *xent; ++ union ubifs_key key; ++ int err; ++ ++ dbg_gen("xattr '%s', ino %lu ('%.*s')", name, ++ host->i_ino, dentry->d_name.len, dentry->d_name.name); ++ ubifs_assert(mutex_is_locked(&host->i_mutex)); ++ ubifs_assert(ubifs_inode(host)->xattr_cnt >= 0); ++ ubifs_assert(ubifs_inode(host)->xattr_size >= 0); ++ ubifs_assert(ubifs_inode(host)->xattr_names >= 0); ++ ++ err = check_namespace(&nm); ++ if (err < 0) ++ return err; ++ ++ xent = kmalloc(UBIFS_MAX_XENT_NODE_SZ, GFP_NOFS); ++ if (!xent) ++ return -ENOMEM; ++ ++ xent_key_init(c, &key, host->i_ino, &nm); ++ err = ubifs_tnc_lookup_nm(c, &key, xent, &nm); ++ if (err) { ++ if (err == -ENOENT) ++ err = -ENODATA; ++ goto out_free; ++ } ++ ++ inode = ubifs_iget(c->vfs_sb, le64_to_cpu(xent->inum)); ++ if (IS_ERR(inode)) { ++ ubifs_err("dead extended attribute node entry"); ++ ubifs_ro_mode(c, err); ++ err = PTR_ERR(inode); ++ goto out_free; ++ } ++ ++ ubifs_assert(inode->i_nlink == 1); ++ inode->i_nlink = 0; ++ err = remove_xattr(c, host, inode, &nm); ++ if (err) ++ inode->i_nlink = 1; ++ ++ /* If @i_nlink is 0, 'iput()' will delete the inode */ ++ iput(inode); ++ ++out_free: ++ kfree(xent); ++ return err; ++} +diff -urN linux-2.6.24.7.old/include/asm-mips/bootinfo.h linux-2.6.24.7/include/asm-mips/bootinfo.h +--- linux-2.6.24.7.old/include/asm-mips/bootinfo.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/include/asm-mips/bootinfo.h 2009-04-12 18:13:57.000000000 +0200 +@@ -198,6 +198,14 @@ + #define MACH_GROUP_BRCM 23 /* Broadcom */ + #define MACH_BCM47XX 1 /* Broadcom BCM47XX */ + ++/* ++ * Valid machtype for group INGENIC ++ */ ++#define MACH_INGENIC_JZ4730 0 /* JZ4730 SOC */ ++#define MACH_INGENIC_JZ4740 1 /* JZ4740 SOC */ ++#define MACH_INGENIC_JZ4750 2 /* JZ4750 SOC */ ++#define MACH_INGENIC_JZ4750D 3 /* JZ4750D SOC */ ++ + #define CL_SIZE COMMAND_LINE_SIZE + + const char *get_system_type(void); +diff -urN linux-2.6.24.7.old/include/asm-mips/cpu.h linux-2.6.24.7/include/asm-mips/cpu.h +--- linux-2.6.24.7.old/include/asm-mips/cpu.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/include/asm-mips/cpu.h 2009-04-12 18:13:57.000000000 +0200 +@@ -33,6 +33,7 @@ + #define PRID_COMP_TOSHIBA 0x070000 + #define PRID_COMP_LSI 0x080000 + #define PRID_COMP_LEXRA 0x0b0000 ++#define PRID_COMP_INGENIC 0xd00000 + + + /* +@@ -113,6 +114,12 @@ + #define PRID_IMP_BCM3302 0x9000 + + /* ++ * These are the PRID's for when 23:16 == PRID_COMP_INGENIC ++ */ ++ ++#define PRID_IMP_JZRISC 0x0200 ++ ++/* + * Definitions for 7:0 on legacy processors + */ + +@@ -203,6 +210,11 @@ + */ + CPU_5KC, CPU_20KC, CPU_25KF, CPU_SB1, CPU_SB1A, CPU_LOONGSON2, + ++ /* ++ * Ingenic class processors ++ */ ++ CPU_JZRISC, CPU_XBURST, ++ + CPU_LAST + }; + +diff -urN linux-2.6.24.7.old/include/asm-mips/jzsoc.h linux-2.6.24.7/include/asm-mips/jzsoc.h +--- linux-2.6.24.7.old/include/asm-mips/jzsoc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/jzsoc.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,49 @@ ++/* ++ * linux/include/asm-mips/jzsoc.h ++ * ++ * Ingenic's JZXXXX SoC common include. ++ * ++ * Copyright (C) 2006 - 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZSOC_H__ ++#define __ASM_JZSOC_H__ ++ ++/* ++ * SoC include ++ */ ++ ++#ifdef CONFIG_SOC_JZ4730 ++#include ++#endif ++ ++#ifdef CONFIG_SOC_JZ4740 ++#include ++#endif ++ ++#ifdef CONFIG_SOC_JZ4750 ++#include ++#endif ++ ++#ifdef CONFIG_SOC_JZ4750D ++#include ++#endif ++ ++/* ++ * Generic I/O routines ++ */ ++#define readb(addr) (*(volatile unsigned char *)(addr)) ++#define readw(addr) (*(volatile unsigned short *)(addr)) ++#define readl(addr) (*(volatile unsigned int *)(addr)) ++ ++#define writeb(b,addr) ((*(volatile unsigned char *)(addr)) = (b)) ++#define writew(b,addr) ((*(volatile unsigned short *)(addr)) = (b)) ++#define writel(b,addr) ((*(volatile unsigned int *)(addr)) = (b)) ++ ++#endif /* __ASM_JZSOC_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-generic/irq.h linux-2.6.24.7/include/asm-mips/mach-generic/irq.h +--- linux-2.6.24.7.old/include/asm-mips/mach-generic/irq.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/include/asm-mips/mach-generic/irq.h 2009-04-12 18:13:57.000000000 +0200 +@@ -9,7 +9,7 @@ + #define __ASM_MACH_GENERIC_IRQ_H + + #ifndef NR_IRQS +-#define NR_IRQS 128 ++#define NR_IRQS 256 + #endif + + #ifdef CONFIG_I8259 +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4730/board-pmp.h linux-2.6.24.7/include/asm-mips/mach-jz4730/board-pmp.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4730/board-pmp.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4730/board-pmp.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,83 @@ ++/* ++ * linux/include/asm-mips/mach-jz4730/board-pmp.h ++ * ++ * JZ4730-based PMP board ver 2.x definition. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4730_PMP_H__ ++#define __ASM_JZ4730_PMP_H__ ++ ++/*====================================================================== ++ * EXTAL frequency ++ */ ++#define JZ_EXTAL 12000000 /* EXTAL: 12 MHz */ ++#define JZ_EXTAL2 32768 /* EXTAL2: 32.768 KHz */ ++ ++ ++/*====================================================================== ++ * GPIO ++ */ ++#define GPIO_PW_I 97 ++#define GPIO_PW_O 66 ++#define GPIO_LED_EN 92 ++#define GPIO_DISP_OFF_N 93 ++#define GPIO_PWM0 94 ++#define GPIO_RTC_IRQ 96 ++#define GPIO_USB_CLK_EN 29 ++#define GPIO_CHARG_STAT 125 ++#define GPIO_TS_PENIRQ 98 ++#define GPIO_UDC_HOTPLUG 86 ++ ++/*====================================================================== ++ * MMC/SD ++ */ ++#define MSC_WP_PIN 82 ++#define MSC_POWEREN_PIN 91 ++#define MSC_HOTPLUG_PIN 90 ++#define MSC_HOTPLUG_IRQ (IRQ_GPIO_0 + MSC_HOTPLUG_PIN) ++ ++/* enable slot power */ ++#define __msc_init_io() \ ++do { \ ++ __gpio_as_input(MSC_WP_PIN); \ ++ __gpio_as_output(MSC_POWEREN_PIN); \ ++} while (0) ++ ++/* enable slot power */ ++#define __msc_enable_power() \ ++do { \ ++ __gpio_clear_pin(MSC_POWEREN_PIN); \ ++} while (0) ++ ++/* disable slot power */ ++#define __msc_disable_power() \ ++do { \ ++ __gpio_set_pin(MSC_POWEREN_PIN); \ ++} while (0) ++ ++/* detect card insertion or not */ ++#define __msc_card_detected(slot) \ ++({ \ ++ int ret; \ ++ if (slot == 0) { \ ++ __gpio_mask_irq(MSC_HOTPLUG_IRQ); \ ++ __gpio_as_input(MSC_HOTPLUG_PIN); \ ++ ret = __gpio_get_pin(MSC_HOTPLUG_PIN); \ ++ __gpio_unmask_irq(MSC_HOTPLUG_IRQ); \ ++ } \ ++ else { \ ++ ret = 1; \ ++ } \ ++ ret = !ret; \ ++ ret; \ ++}) ++ ++#endif /* __ASM_JZ4730_PMP_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4730/clock.h linux-2.6.24.7/include/asm-mips/mach-jz4730/clock.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4730/clock.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4730/clock.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,184 @@ ++/* ++ * linux/include/asm-mips/mach-jz4730/clock.h ++ * ++ * JZ4730 clocks definition. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4730_CLOCK_H__ ++#define __ASM_JZ4730_CLOCK_H__ ++ ++#ifndef JZ_EXTAL ++#define JZ_EXTAL 3686400 ++#endif ++ ++#ifndef JZ_EXTAL2 ++#define JZ_EXTAL2 32768 ++#endif ++ ++/* ++ * JZ4730 clocks structure ++ */ ++typedef struct { ++ unsigned int iclk; /* CPU core clock */ ++ unsigned int sclk; /* AHB bus clock */ ++ unsigned int mclk; /* Memory bus clock */ ++ unsigned int pclk; /* APB bus clock */ ++ unsigned int devclk; /* Devcie clock to specific modules */ ++ unsigned int rtcclk; /* RTC module clock */ ++ unsigned int uartclk; /* UART module clock */ ++ unsigned int lcdclk; /* LCD module clock */ ++ unsigned int pixclk; /* LCD pixel clock */ ++ unsigned int usbclk; /* USB module clock */ ++ unsigned int i2sclk; /* I2S module clock */ ++ unsigned int mscclk; /* MMC/SD module clock */ ++} jz_clocks_t; ++ ++extern jz_clocks_t jz_clocks; ++ ++ ++static __inline__ unsigned int __cpm_get_pllout(void) ++{ ++ unsigned int nf, nr, no, pllout; ++ unsigned long plcr = REG_CPM_PLCR1; ++ unsigned long od[4] = {1, 2, 2, 4}; ++ if (plcr & CPM_PLCR1_PLL1EN) { ++ nf = (plcr & CPM_PLCR1_PLL1FD_MASK) >> CPM_PLCR1_PLL1FD_BIT; ++ nr = (plcr & CPM_PLCR1_PLL1RD_MASK) >> CPM_PLCR1_PLL1RD_BIT; ++ no = od[((plcr & CPM_PLCR1_PLL1OD_MASK) >> CPM_PLCR1_PLL1OD_BIT)]; ++ pllout = (JZ_EXTAL) / ((nr+2) * no) * (nf+2); ++ } else ++ pllout = JZ_EXTAL; ++ return pllout; ++} ++ ++static __inline__ unsigned int __cpm_get_iclk(void) ++{ ++ unsigned int iclk; ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ unsigned long cfcr = REG_CPM_CFCR; ++ unsigned long plcr = REG_CPM_PLCR1; ++ if (plcr & CPM_PLCR1_PLL1EN) ++ iclk = __cpm_get_pllout() / ++ div[(cfcr & CPM_CFCR_IFR_MASK) >> CPM_CFCR_IFR_BIT]; ++ else ++ iclk = JZ_EXTAL; ++ return iclk; ++} ++ ++static __inline__ unsigned int __cpm_get_sclk(void) ++{ ++ unsigned int sclk; ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ unsigned long cfcr = REG_CPM_CFCR; ++ unsigned long plcr = REG_CPM_PLCR1; ++ if (plcr & CPM_PLCR1_PLL1EN) ++ sclk = __cpm_get_pllout() / ++ div[(cfcr & CPM_CFCR_SFR_MASK) >> CPM_CFCR_SFR_BIT]; ++ else ++ sclk = JZ_EXTAL; ++ return sclk; ++} ++ ++static __inline__ unsigned int __cpm_get_mclk(void) ++{ ++ unsigned int mclk; ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ unsigned long cfcr = REG_CPM_CFCR; ++ unsigned long plcr = REG_CPM_PLCR1; ++ if (plcr & CPM_PLCR1_PLL1EN) ++ mclk = __cpm_get_pllout() / ++ div[(cfcr & CPM_CFCR_MFR_MASK) >> CPM_CFCR_MFR_BIT]; ++ else ++ mclk = JZ_EXTAL; ++ return mclk; ++} ++ ++static __inline__ unsigned int __cpm_get_pclk(void) ++{ ++ unsigned int devclk; ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ unsigned long cfcr = REG_CPM_CFCR; ++ unsigned long plcr = REG_CPM_PLCR1; ++ if (plcr & CPM_PLCR1_PLL1EN) ++ devclk = __cpm_get_pllout() / ++ div[(cfcr & CPM_CFCR_PFR_MASK) >> CPM_CFCR_PFR_BIT]; ++ else ++ devclk = JZ_EXTAL; ++ return devclk; ++} ++ ++static __inline__ unsigned int __cpm_get_lcdclk(void) ++{ ++ unsigned int lcdclk; ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ unsigned long cfcr = REG_CPM_CFCR; ++ unsigned long plcr = REG_CPM_PLCR1; ++ if (plcr & CPM_PLCR1_PLL1EN) ++ lcdclk = __cpm_get_pllout() / ++ div[(cfcr & CPM_CFCR_LFR_MASK) >> CPM_CFCR_LFR_BIT]; ++ else ++ lcdclk = JZ_EXTAL; ++ return lcdclk; ++} ++ ++static __inline__ unsigned int __cpm_get_pixclk(void) ++{ ++ unsigned int pixclk; ++ unsigned long cfcr2 = REG_CPM_CFCR2; ++ pixclk = __cpm_get_pllout() / (cfcr2 + 1); ++ return pixclk; ++} ++ ++static __inline__ unsigned int __cpm_get_devclk(void) ++{ ++ return JZ_EXTAL; ++} ++ ++static __inline__ unsigned int __cpm_get_rtcclk(void) ++{ ++ return JZ_EXTAL2; ++} ++ ++static __inline__ unsigned int __cpm_get_uartclk(void) ++{ ++ return JZ_EXTAL; ++} ++ ++static __inline__ unsigned int __cpm_get_usbclk(void) ++{ ++ unsigned int usbclk; ++ unsigned long cfcr = REG_CPM_CFCR; ++ if (cfcr & CPM_CFCR_UCS) ++ usbclk = 48000000; ++ else ++ usbclk = __cpm_get_pllout() / ++ (((cfcr &CPM_CFCR_UFR_MASK) >> CPM_CFCR_UFR_BIT) + 1); ++ return usbclk; ++} ++ ++static __inline__ unsigned int __cpm_get_i2sclk(void) ++{ ++ unsigned int i2sclk; ++ unsigned long cfcr = REG_CPM_CFCR; ++ i2sclk = __cpm_get_pllout() / ++ ((cfcr & CPM_CFCR_I2S) ? 2: 1); ++ return i2sclk; ++} ++ ++static __inline__ unsigned int __cpm_get_mscclk(void) ++{ ++ if (REG_CPM_CFCR & CPM_CFCR_MSC) ++ return 24000000; ++ else ++ return 16000000; ++} ++ ++#endif /* __ASM_JZ4730_CLOCK_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4730/dma.h linux-2.6.24.7/include/asm-mips/mach-jz4730/dma.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4730/dma.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4730/dma.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,272 @@ ++/* ++ * linux/include/asm-mips/mach-jz4730/dma.h ++ * ++ * JZ4730 DMA definition. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4730_DMA_H__ ++#define __ASM_JZ4730_DMA_H__ ++ ++#include ++#include /* need byte IO */ ++#include /* And spinlocks */ ++#include ++#include ++ ++#define DMA_UNIT_32 32 ++#define DMA_UNIT_16 16 ++ ++ ++/* block-mode EOP: high DREQ: high DACK: low*/ ++#define DMA_BLOCK_CONF \ ++ DMAC_DCCSR_TM | \ ++ DMAC_DCCSR_DS_8b | DMAC_DCCSR_RDIL_IGN | \ ++ DMAC_DCCSR_ERDM_HLEVEL | DMAC_DCCSR_EACKS ++ ++/* single-mode EOP: high DREQ: high DACK: low */ ++#define DMA_SINGLE_CONF \ ++ DMAC_DCCSR_DS_8b | DMAC_DCCSR_RDIL_IGN | \ ++ DMAC_DCCSR_ERDM_HLEVEL | DMAC_DCCSR_EACKS ++ ++#define DMA_8bit_RX_CONF \ ++ DMAC_DCCSR_DAM | \ ++ DMAC_DCCSR_SWDH_8 | DMAC_DCCSR_DWDH_32 | \ ++ DMAC_DCCSR_DS_8b | DMAC_DCCSR_RDIL_IGN ++ ++#define DMA_8bit_TX_CONF \ ++ DMAC_DCCSR_SAM | \ ++ DMAC_DCCSR_SWDH_32 | DMAC_DCCSR_DWDH_8 | \ ++ DMAC_DCCSR_DS_8b | DMAC_DCCSR_RDIL_IGN ++ ++#define DMA_16bit_RX_CONF \ ++ DMAC_DCCSR_DAM | \ ++ DMAC_DCCSR_SWDH_16 | DMAC_DCCSR_DWDH_32 | \ ++ DMAC_DCCSR_DS_16b | DMAC_DCCSR_RDIL_IGN ++ ++#define DMA_16bit_TX_CONF \ ++ DMAC_DCCSR_SAM | \ ++ DMAC_DCCSR_SWDH_32 | DMAC_DCCSR_DWDH_16 | \ ++ DMAC_DCCSR_DS_16b | DMAC_DCCSR_RDIL_IGN ++ ++#define DMA_32bit_RX_CONF \ ++ DMAC_DCCSR_DAM | \ ++ DMAC_DCCSR_SWDH_32 | DMAC_DCCSR_DWDH_32 | \ ++ DMAC_DCCSR_DS_32b | DMAC_DCCSR_RDIL_IGN ++ ++#define DMA_32bit_TX_CONF \ ++ DMAC_DCCSR_SAM | \ ++ DMAC_DCCSR_SWDH_32 | DMAC_DCCSR_DWDH_32 | \ ++ DMAC_DCCSR_DS_32b | DMAC_DCCSR_RDIL_IGN ++ ++#define DMA_16BYTE_RX_CONF \ ++ DMAC_DCCSR_DAM | \ ++ DMAC_DCCSR_SWDH_8 | DMAC_DCCSR_DWDH_32 | \ ++ DMAC_DCCSR_DS_16B | DMAC_DCCSR_RDIL_IGN ++ ++#define DMA_16BYTE_TX_CONF \ ++ DMAC_DCCSR_SAM | \ ++ DMAC_DCCSR_SWDH_32 | DMAC_DCCSR_DWDH_8 | \ ++ DMAC_DCCSR_DS_16B | DMAC_DCCSR_RDIL_IGN ++ ++#define DMA_AIC_32_16BYTE_TX_CMD \ ++ DMAC_DCCSR_SAM | \ ++ DMAC_DCCSR_SWDH_32 | DMAC_DCCSR_DWDH_32 | \ ++ DMAC_DCCSR_DS_16B | DMAC_DCCSR_RDIL_IGN ++ ++#define DMA_AIC_32_16BYTE_RX_CMD \ ++ DMAC_DCCSR_DAM | \ ++ DMAC_DCCSR_SWDH_32 | DMAC_DCCSR_DWDH_32 | \ ++ DMAC_DCCSR_DS_16B | DMAC_DCCSR_RDIL_IGN ++ ++#define DMA_AIC_16BIT_TX_CMD \ ++ DMAC_DCCSR_SAM | \ ++ DMAC_DCCSR_SWDH_16 | DMAC_DCCSR_DWDH_16 | \ ++ DMAC_DCCSR_DS_16b | DMAC_DCCSR_RDIL_IGN ++ ++#define DMA_AIC_16BIT_RX_CMD \ ++ DMAC_DCCSR_DAM | \ ++ DMAC_DCCSR_SWDH_16 | DMAC_DCCSR_DWDH_16 | \ ++ DMAC_DCCSR_DS_16b | DMAC_DCCSR_RDIL_IGN ++ ++#define DMA_AIC_16BYTE_RX_CMD \ ++ DMAC_DCCSR_DAM | \ ++ DMAC_DCCSR_SWDH_16 | DMAC_DCCSR_DWDH_16 | \ ++ DMAC_DCCSR_DS_16B | DMAC_DCCSR_RDIL_IGN ++ ++#define DMA_AIC_16BYTE_TX_CMD \ ++ DMAC_DCCSR_SAM | \ ++ DMAC_DCCSR_SWDH_16 | DMAC_DCCSR_DWDH_16 | \ ++ DMAC_DCCSR_DS_16B | DMAC_DCCSR_RDIL_IGN ++ ++/* DMA Device ID's follow */ ++enum { ++ DMA_ID_UART0_TX = 0, ++ DMA_ID_UART0_RX, ++ DMA_ID_UART1_TX, ++ DMA_ID_UART1_RX, ++ DMA_ID_UART2_TX, ++ DMA_ID_UART2_RX, ++ DMA_ID_UART3_TX, ++ DMA_ID_UART3_RX, ++ DMA_ID_SSI_TX, ++ DMA_ID_SSI_RX, ++ DMA_ID_MSC_TX, ++ DMA_ID_MSC_RX, ++ DMA_ID_AIC_TX, ++ DMA_ID_AIC_RX, ++ DMA_ID_BLOCK, /* DREQ */ ++ DMA_ID_SINGLE, /* DREQ */ ++ DMA_ID_PCMCIA0_TX, ++ DMA_ID_PCMCIA0_RX, ++ DMA_ID_PCMCIA1_TX, ++ DMA_ID_PCMCIA2_RX, ++ DMA_ID_AUTO, ++ DMA_ID_RAW_SET, ++ NUM_DMA_DEV ++}; ++ ++/* dummy DCCSR bit, i386 style DMA macros compitable */ ++#define DMA_MODE_READ 0 /* I/O to memory, no autoinit, ++ * increment, single mode */ ++#define DMA_MODE_WRITE 1 /* memory to I/O, no autoinit, ++ * increment, single mode */ ++#define DMA_MODE_CASCADE 2 /* pass thru DREQ->HRQ, ++ * DACK<-HLDA only */ ++#define DMA_AUTOINIT 3 ++#define DMA_MODE_MASK 3 ++ ++struct jz_dma_chan { ++ int dev_id; /* this channel is allocated if >=0, ++ * free otherwise */ ++ unsigned int io; ++ const char *dev_str; ++ int irq; ++ void *irq_dev; ++ unsigned int fifo_addr; ++ unsigned int mode; ++ unsigned int source; ++}; ++ ++extern struct jz_dma_chan jz_dma_table[]; ++ ++extern int jz_request_dma(int dev_id, ++ const char *dev_str, ++ irqreturn_t (*irqhandler)(int, void *), ++ unsigned long irqflags, ++ void *irq_dev_id); ++extern void jz_free_dma(unsigned int dmanr); ++ ++extern int jz_dma_read_proc(char *buf, char **start, off_t fpos, ++ int length, int *eof, void *data); ++extern void dump_jz_dma_channel(unsigned int dmanr); ++ ++extern void enable_dma(unsigned int dmanr); ++extern void disable_dma(unsigned int dmanr); ++extern void set_dma_addr(unsigned int dmanr, unsigned int a); ++extern void set_dma_count(unsigned int dmanr, unsigned int count); ++extern void set_dma_mode(unsigned int dmanr, unsigned int mode); ++extern void jz_set_oss_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt); ++extern void jz_set_alsa_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt); ++extern int get_dma_residue(unsigned int dmanr); ++ ++extern spinlock_t dma_spin_lock; ++ ++static __inline__ unsigned long claim_dma_lock(void) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&dma_spin_lock, flags); ++ return flags; ++} ++ ++static __inline__ void release_dma_lock(unsigned long flags) ++{ ++ spin_unlock_irqrestore(&dma_spin_lock, flags); ++} ++ ++/* Clear the 'DMA Pointer Flip Flop'. ++ * Write 0 for LSB/MSB, 1 for MSB/LSB access. ++ */ ++#define clear_dma_ff(channel) ++ ++static __inline__ struct jz_dma_chan *get_dma_chan(unsigned int dmanr) ++{ ++ if (dmanr > NUM_DMA ++ || jz_dma_table[dmanr].dev_id < 0) ++ return NULL; ++ return &jz_dma_table[dmanr]; ++} ++ ++static __inline__ int dma_halted(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 1; ++ return __dmac_channel_transmit_halt_detected(dmanr) ? 1 : 0; ++} ++ ++static __inline__ unsigned int get_dma_mode(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 0; ++ return chan->mode; ++} ++ ++static __inline__ void clear_dma_done(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TC | DMAC_DCCSR_AR); ++} ++ ++static __inline__ void clear_dma_halt(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT); ++ REG_DMAC_DMACR &= ~(DMAC_DMACR_HTR); ++} ++static __inline__ void clear_dma_flag(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TC | DMAC_DCCSR_AR); ++ REG_DMAC_DMACR &= ~(DMAC_DMACR_HTR | DMAC_DMACR_AER); ++} ++ ++static __inline__ void set_dma_page(unsigned int dmanr, char pagenr) ++{ ++} ++ ++static __inline__ unsigned int get_dma_done_status(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ unsigned long dccsr; ++ if (!chan) ++ return 0; ++ ++ dccsr = REG_DMAC_DCCSR(chan->io); ++ return dccsr & (DMAC_DCCSR_HLT | DMAC_DCCSR_TC | DMAC_DCCSR_AR); ++} ++ ++static __inline__ int get_dma_done_irq(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return -1; ++ ++ return chan->irq; ++} ++ ++#endif /* __ASM_JZ4730_DMA_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4730/jz4730.h linux-2.6.24.7/include/asm-mips/mach-jz4730/jz4730.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4730/jz4730.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4730/jz4730.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,40 @@ ++/* ++ * linux/include/asm-mips/mach-jz4730/jz4730.h ++ * ++ * JZ4730 common definition. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4730_H__ ++#define __ASM_JZ4730_H__ ++ ++#include ++#include ++#include ++#include ++ ++/*------------------------------------------------------------------ ++ * Platform definitions ++ */ ++#ifdef CONFIG_JZ4730_PMP ++#include ++#endif ++ ++/* Add other platform definition here ... */ ++ ++ ++/*------------------------------------------------------------------ ++ * Follows are related to platform definitions ++ */ ++ ++#include ++#include ++ ++#endif /* __ASM_JZ4730_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4730/misc.h linux-2.6.24.7/include/asm-mips/mach-jz4730/misc.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4730/misc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4730/misc.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,28 @@ ++/* ++ * linux/include/asm-mips/mach-jz4730/misc.h ++ * ++ * JZ4730 miscillaneous definitions. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4730_MISC_H__ ++#define __ASM_JZ4730_MISC_H__ ++ ++/* ++ * I2C routines ++ */ ++ ++extern void i2c_open(void); ++extern void i2c_close(void); ++extern void i2c_setclk(unsigned int i2cclk); ++extern int i2c_read(unsigned char, unsigned char *, unsigned char, int); ++extern int i2c_write(unsigned char, unsigned char *, unsigned char, int); ++ ++#endif /* __ASM_JZ4730_MISC_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4730/ops.h linux-2.6.24.7/include/asm-mips/mach-jz4730/ops.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4730/ops.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4730/ops.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,2541 @@ ++/* ++ * linux/include/asm-mips/mach-jz4730/ops.h ++ * ++ * JZ4730 module operations definition. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * 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 __ASM_JZ4730_OPS_H__ ++#define __ASM_JZ4730_OPS_H__ ++ ++/*************************************************************************** ++ * MSC ++ ***************************************************************************/ ++ ++#define __msc_start_op() \ ++ ( REG_MSC_STRPCL = MSC_STRPCL_START_OP | MSC_STRPCL_CLOCK_CONTROL_START ) ++ ++#define __msc_set_resto(to) ( REG_MSC_RESTO = to ) ++#define __msc_set_rdto(to) ( REG_MSC_RDTO = to ) ++#define __msc_set_cmd(cmd) ( REG_MSC_CMD = cmd ) ++#define __msc_set_arg(arg) ( REG_MSC_ARG = arg ) ++#define __msc_set_nob(nob) ( REG_MSC_NOB = nob ) ++#define __msc_get_nob() ( REG_MSC_NOB ) ++#define __msc_set_blklen(len) ( REG_MSC_BLKLEN = len ) ++#define __msc_set_cmdat(cmdat) ( REG_MSC_CMDAT = cmdat ) ++#define __msc_set_cmdat_ioabort() ( REG_MSC_CMDAT |= MSC_CMDAT_IO_ABORT ) ++#define __msc_clear_cmdat_ioabort() ( REG_MSC_CMDAT &= ~MSC_CMDAT_IO_ABORT ) ++ ++#define __msc_set_cmdat_bus_width1() \ ++do { \ ++ REG_MSC_CMDAT &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ ++ REG_MSC_CMDAT |= MSC_CMDAT_BUS_WIDTH_1BIT; \ ++} while(0) ++ ++#define __msc_set_cmdat_bus_width4() \ ++do { \ ++ REG_MSC_CMDAT &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ ++ REG_MSC_CMDAT |= MSC_CMDAT_BUS_WIDTH_4BIT; \ ++} while(0) ++ ++#define __msc_set_cmdat_dma_en() ( REG_MSC_CMDAT |= MSC_CMDAT_DMA_EN ) ++#define __msc_set_cmdat_init() ( REG_MSC_CMDAT |= MSC_CMDAT_INIT ) ++#define __msc_set_cmdat_busy() ( REG_MSC_CMDAT |= MSC_CMDAT_BUSY ) ++#define __msc_set_cmdat_stream() ( REG_MSC_CMDAT |= MSC_CMDAT_STREAM_BLOCK ) ++#define __msc_set_cmdat_block() ( REG_MSC_CMDAT &= ~MSC_CMDAT_STREAM_BLOCK ) ++#define __msc_set_cmdat_read() ( REG_MSC_CMDAT &= ~MSC_CMDAT_WRITE_READ ) ++#define __msc_set_cmdat_write() ( REG_MSC_CMDAT |= MSC_CMDAT_WRITE_READ ) ++#define __msc_set_cmdat_data_en() ( REG_MSC_CMDAT |= MSC_CMDAT_DATA_EN ) ++ ++/* r is MSC_CMDAT_RESPONSE_FORMAT_Rx or MSC_CMDAT_RESPONSE_FORMAT_NONE */ ++#define __msc_set_cmdat_res_format(r) \ ++do { \ ++ REG_MSC_CMDAT &= ~MSC_CMDAT_RESPONSE_FORMAT_MASK; \ ++ REG_MSC_CMDAT |= (r); \ ++} while(0) ++ ++#define __msc_clear_cmdat() \ ++ REG_MSC_CMDAT &= ~( MSC_CMDAT_IO_ABORT | MSC_CMDAT_DMA_EN | MSC_CMDAT_INIT| \ ++ MSC_CMDAT_BUSY | MSC_CMDAT_STREAM_BLOCK | MSC_CMDAT_WRITE_READ | \ ++ MSC_CMDAT_DATA_EN | MSC_CMDAT_RESPONSE_FORMAT_MASK ) ++ ++#define __msc_get_imask() ( REG_MSC_IMASK ) ++#define __msc_mask_all_intrs() ( REG_MSC_IMASK = 0xff ) ++#define __msc_unmask_all_intrs() ( REG_MSC_IMASK = 0x00 ) ++#define __msc_mask_rd() ( REG_MSC_IMASK |= MSC_IMASK_RXFIFO_RD_REQ ) ++#define __msc_unmask_rd() ( REG_MSC_IMASK &= ~MSC_IMASK_RXFIFO_RD_REQ ) ++#define __msc_mask_wr() ( REG_MSC_IMASK |= MSC_IMASK_TXFIFO_WR_REQ ) ++#define __msc_unmask_wr() ( REG_MSC_IMASK &= ~MSC_IMASK_TXFIFO_WR_REQ ) ++#define __msc_mask_endcmdres() ( REG_MSC_IMASK |= MSC_IMASK_END_CMD_RES ) ++#define __msc_unmask_endcmdres() ( REG_MSC_IMASK &= ~MSC_IMASK_END_CMD_RES ) ++#define __msc_mask_datatrandone() ( REG_MSC_IMASK |= MSC_IMASK_DATA_TRAN_DONE ) ++#define __msc_unmask_datatrandone() ( REG_MSC_IMASK &= ~MSC_IMASK_DATA_TRAN_DONE ) ++#define __msc_mask_prgdone() ( REG_MSC_IMASK |= MSC_IMASK_PRG_DONE ) ++#define __msc_unmask_prgdone() ( REG_MSC_IMASK &= ~MSC_IMASK_PRG_DONE ) ++ ++/* n=0,1,2,3,4,5,6,7 */ ++#define __msc_set_clkrt(n) \ ++do { \ ++ REG_MSC_CLKRT = n; \ ++} while(0) ++ ++#define __msc_get_ireg() ( REG_MSC_IREG ) ++#define __msc_ireg_rd() ( REG_MSC_IREG & MSC_IREG_RXFIFO_RD_REQ ) ++#define __msc_ireg_wr() ( REG_MSC_IREG & MSC_IREG_TXFIFO_WR_REQ ) ++#define __msc_ireg_end_cmd_res() ( REG_MSC_IREG & MSC_IREG_END_CMD_RES ) ++#define __msc_ireg_data_tran_done() ( REG_MSC_IREG & MSC_IREG_DATA_TRAN_DONE ) ++#define __msc_ireg_prg_done() ( REG_MSC_IREG & MSC_IREG_PRG_DONE ) ++#define __msc_ireg_clear_end_cmd_res() ( REG_MSC_IREG = MSC_IREG_END_CMD_RES ) ++#define __msc_ireg_clear_data_tran_done() ( REG_MSC_IREG = MSC_IREG_DATA_TRAN_DONE ) ++#define __msc_ireg_clear_prg_done() ( REG_MSC_IREG = MSC_IREG_PRG_DONE ) ++ ++#define __msc_get_stat() ( REG_MSC_STAT ) ++#define __msc_stat_not_end_cmd_res() ( (REG_MSC_STAT & MSC_STAT_END_CMD_RES) == 0) ++#define __msc_stat_crc_err() \ ++ ( REG_MSC_STAT & (MSC_STAT_CRC_RES_ERR | MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR_YES) ) ++#define __msc_stat_res_crc_err() ( REG_MSC_STAT & MSC_STAT_CRC_RES_ERR ) ++#define __msc_stat_rd_crc_err() ( REG_MSC_STAT & MSC_STAT_CRC_READ_ERROR ) ++#define __msc_stat_wr_crc_err() ( REG_MSC_STAT & MSC_STAT_CRC_WRITE_ERROR_YES ) ++#define __msc_stat_resto_err() ( REG_MSC_STAT & MSC_STAT_TIME_OUT_RES ) ++#define __msc_stat_rdto_err() ( REG_MSC_STAT & MSC_STAT_TIME_OUT_READ ) ++ ++#define __msc_rd_resfifo() ( REG_MSC_RES ) ++#define __msc_rd_rxfifo() ( REG_MSC_RXFIFO ) ++#define __msc_wr_txfifo(v) ( REG_MSC_TXFIFO = v ) ++ ++#define __msc_reset() \ ++do { \ ++ REG_MSC_STRPCL = MSC_STRPCL_RESET; \ ++ while (REG_MSC_STAT & MSC_STAT_IS_RESETTING); \ ++} while (0) ++ ++#define __msc_start_clk() \ ++do { \ ++ REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_START; \ ++} while (0) ++ ++#define __msc_stop_clk() \ ++do { \ ++ REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_STOP; \ ++} while (0) ++ ++#define MMC_CLK 19169200 ++#define SD_CLK 24576000 ++ ++/* msc_clk should little than pclk and little than clk retrieve from card */ ++#define __msc_calc_clk_divisor(type,dev_clk,msc_clk,lv) \ ++do { \ ++ unsigned int rate, pclk, i; \ ++ pclk = dev_clk; \ ++ rate = type?SD_CLK:MMC_CLK; \ ++ if (msc_clk && msc_clk < pclk) \ ++ pclk = msc_clk; \ ++ i = 0; \ ++ while (pclk < rate) \ ++ { \ ++ i ++; \ ++ rate >>= 1; \ ++ } \ ++ lv = i; \ ++} while(0) ++ ++/* divide rate to little than or equal to 400kHz */ ++#define __msc_calc_slow_clk_divisor(type, lv) \ ++do { \ ++ unsigned int rate, i; \ ++ rate = (type?SD_CLK:MMC_CLK)/1000/400; \ ++ i = 0; \ ++ while (rate > 0) \ ++ { \ ++ rate >>= 1; \ ++ i ++; \ ++ } \ ++ lv = i; \ ++} while(0) ++ ++/*************************************************************************** ++ * RTC ++ ***************************************************************************/ ++ ++#define __rtc_start() ( REG_RTC_RCR |= RTC_RCR_START ) ++#define __rtc_stop() ( REG_RTC_RCR &= ~RTC_RCR_START ) ++ ++#define __rtc_enable_alarm() ( REG_RTC_RCR |= RTC_RCR_AE ) ++#define __rtc_disable_alarm() ( REG_RTC_RCR &= ~RTC_RCR_AE ) ++#define __rtc_enable_alarm_irq() ( REG_RTC_RCR |= RTC_RCR_AIE ) ++#define __rtc_disable_alarm_irq() ( REG_RTC_RCR &= ~RTC_RCR_AIE ) ++ ++#define __rtc_enable_1hz_irq() ( REG_RTC_RCR |= RTC_RCR_HZIE ) ++#define __rtc_disable_1hz_irq() ( REG_RTC_RCR &= ~RTC_RCR_HZIE ) ++ ++#define __rtc_is_alarm_flag() ( REG_RTC_RCR & RTC_RCR_AF ) ++#define __rtc_is_1hz_flag() ( REG_RTC_RCR & RTC_RCR_HZ ) ++#define __rtc_clear_alarm_flag() ( REG_RTC_RCR &= ~RTC_RCR_AF ) ++#define __rtc_clear_1hz_flag() ( REG_RTC_RCR &= ~RTC_RCR_HZ ) ++ ++#define __rtc_set_second(s) ( REG_RTC_RSR = (s) ) ++#define __rtc_get_second() REG_RTC_RSR ++#define __rtc_set_alarm(s) ( REG_RTC_RSAR = (s) ) ++#define __rtc_get_alarm() REG_RTC_RSAR ++ ++#define __rtc_adjust_1hz(f32k) \ ++ ( REG_RTC_RGR = (REG_RTC_RGR & ~(RTC_REG_DIV_MASK | RTC_RGR_ADJ_MASK)) | f32k | 0 ) ++#define __rtc_lock_1hz() ( REG_RTC_RGR |= RTC_RGR_LOCK ) ++ ++ ++/*************************************************************************** ++ * FIR ++ ***************************************************************************/ ++ ++/* enable/disable fir unit */ ++#define __fir_enable() ( REG_FIR_CR1 |= FIR_CR1_FIRUE ) ++#define __fir_disable() ( REG_FIR_CR1 &= ~FIR_CR1_FIRUE ) ++ ++/* enable/disable address comparison */ ++#define __fir_enable_ac() ( REG_FIR_CR1 |= FIR_CR1_ACE ) ++#define __fir_disable_ac() ( REG_FIR_CR1 &= ~FIR_CR1_ACE ) ++ ++/* select frame end mode as underrun or normal */ ++#define __fir_set_eous() ( REG_FIR_CR1 |= FIR_CR1_EOUS ) ++#define __fir_clear_eous() ( REG_FIR_CR1 &= ~FIR_CR1_EOUS ) ++ ++/* enable/disable transmitter idle interrupt */ ++#define __fir_enable_tii() ( REG_FIR_CR1 |= FIR_CR1_TIIE ) ++#define __fir_disable_tii() ( REG_FIR_CR1 &= ~FIR_CR1_TIIE ) ++ ++/* enable/disable transmit FIFO service request interrupt */ ++#define __fir_enable_tfi() ( REG_FIR_CR1 |= FIR_CR1_TFIE ) ++#define __fir_disable_tfi() ( REG_FIR_CR1 &= ~FIR_CR1_TFIE ) ++ ++/* enable/disable receive FIFO service request interrupt */ ++#define __fir_enable_rfi() ( REG_FIR_CR1 |= FIR_CR1_RFIE ) ++#define __fir_disable_rfi() ( REG_FIR_CR1 &= ~FIR_CR1_RFIE ) ++ ++/* enable/disable tx function */ ++#define __fir_tx_enable() ( REG_FIR_CR1 |= FIR_CR1_TXE ) ++#define __fir_tx_disable() ( REG_FIR_CR1 &= ~FIR_CR1_TXE ) ++ ++/* enable/disable rx function */ ++#define __fir_rx_enable() ( REG_FIR_CR1 |= FIR_CR1_RXE ) ++#define __fir_rx_disable() ( REG_FIR_CR1 &= ~FIR_CR1_RXE ) ++ ++ ++/* enable/disable serial infrared interaction pulse (SIP) */ ++#define __fir_enable_sip() ( REG_FIR_CR2 |= FIR_CR2_SIPE ) ++#define __fir_disable_sip() ( REG_FIR_CR2 &= ~FIR_CR2_SIPE ) ++ ++/* un-inverted CRC value is sent out */ ++#define __fir_enable_bcrc() ( REG_FIR_CR2 |= FIR_CR2_BCRC ) ++ ++/* inverted CRC value is sent out */ ++#define __fir_disable_bcrc() ( REG_FIR_CR2 &= ~FIR_CR2_BCRC ) ++ ++/* enable/disable Transmit Frame Length Register */ ++#define __fir_enable_tflr() ( REG_FIR_CR2 |= FIR_CR2_TFLRS ) ++#define __fir_disable_tflr() ( REG_FIR_CR2 &= ~FIR_CR2_TFLRS ) ++ ++/* Preamble is transmitted in idle state */ ++#define __fir_set_iss() ( REG_FIR_CR2 |= FIR_CR2_ISS ) ++ ++/* Abort symbol is transmitted in idle state */ ++#define __fir_clear_iss() ( REG_FIR_CR2 &= ~FIR_CR2_ISS ) ++ ++/* enable/disable loopback mode */ ++#define __fir_enable_loopback() ( REG_FIR_CR2 |= FIR_CR2_LMS ) ++#define __fir_disable_loopback() ( REG_FIR_CR2 &= ~FIR_CR2_LMS ) ++ ++/* select transmit pin polarity */ ++#define __fir_tpp_negative() ( REG_FIR_CR2 |= FIR_CR2_TPPS ) ++#define __fir_tpp_positive() ( REG_FIR_CR2 &= ~FIR_CR2_TPPS ) ++ ++/* select receive pin polarity */ ++#define __fir_rpp_negative() ( REG_FIR_CR2 |= FIR_CR2_RPPS ) ++#define __fir_rpp_positive() ( REG_FIR_CR2 &= ~FIR_CR2_RPPS ) ++ ++/* n=16,32,64,128 */ ++#define __fir_set_txfifo_trigger(n) \ ++do { \ ++ REG_FIR_CR2 &= ~FIR_CR2_TTRG_MASK; \ ++ REG_FIR_CR2 |= FIR_CR2_TTRG_##n; \ ++} while (0) ++ ++/* n=16,32,64,128 */ ++#define __fir_set_rxfifo_trigger(n) \ ++do { \ ++ REG_FIR_CR2 &= ~FIR_CR2_RTRG_MASK; \ ++ REG_FIR_CR2 |= FIR_CR2_RTRG_##n; \ ++} while (0) ++ ++ ++/* FIR status checking */ ++ ++#define __fir_test_rfw() ( REG_FIR_SR & FIR_SR_RFW ) ++#define __fir_test_rfa() ( REG_FIR_SR & FIR_SR_RFA ) ++#define __fir_test_tfrtl() ( REG_FIR_SR & FIR_SR_TFRTL ) ++#define __fir_test_rfrtl() ( REG_FIR_SR & FIR_SR_RFRTL ) ++#define __fir_test_urun() ( REG_FIR_SR & FIR_SR_URUN ) ++#define __fir_test_rfte() ( REG_FIR_SR & FIR_SR_RFTE ) ++#define __fir_test_orun() ( REG_FIR_SR & FIR_SR_ORUN ) ++#define __fir_test_crce() ( REG_FIR_SR & FIR_SR_CRCE ) ++#define __fir_test_fend() ( REG_FIR_SR & FIR_SR_FEND ) ++#define __fir_test_tff() ( REG_FIR_SR & FIR_SR_TFF ) ++#define __fir_test_rfe() ( REG_FIR_SR & FIR_SR_RFE ) ++#define __fir_test_tidle() ( REG_FIR_SR & FIR_SR_TIDLE ) ++#define __fir_test_rb() ( REG_FIR_SR & FIR_SR_RB ) ++ ++#define __fir_clear_status() \ ++do { \ ++ REG_FIR_SR |= FIR_SR_RFW | FIR_SR_RFA | FIR_SR_URUN; \ ++} while (0) ++ ++#define __fir_clear_rfw() ( REG_FIR_SR |= FIR_SR_RFW ) ++#define __fir_clear_rfa() ( REG_FIR_SR |= FIR_SR_RFA ) ++#define __fir_clear_urun() ( REG_FIR_SR |= FIR_SR_URUN ) ++ ++#define __fir_set_tflr(len) \ ++do { \ ++ REG_FIR_TFLR = len; \ ++} while (0) ++ ++#define __fir_set_addr(a) ( REG_FIR_AR = (a) ) ++ ++#define __fir_write_data(data) ( REG_FIR_TDR = data ) ++#define __fir_read_data(data) ( data = REG_FIR_RDR ) ++ ++/*************************************************************************** ++ * SCC ++ ***************************************************************************/ ++ ++#define __scc_enable(base) ( REG_SCC_CR(base) |= SCC_CR_SCCE ) ++#define __scc_disable(base) ( REG_SCC_CR(base) &= ~SCC_CR_SCCE ) ++ ++#define __scc_set_tx_mode(base) ( REG_SCC_CR(base) |= SCC_CR_TRS ) ++#define __scc_set_rx_mode(base) ( REG_SCC_CR(base) &= ~SCC_CR_TRS ) ++ ++#define __scc_enable_t2r(base) ( REG_SCC_CR(base) |= SCC_CR_T2R ) ++#define __scc_disable_t2r(base) ( REG_SCC_CR(base) &= ~SCC_CR_T2R ) ++ ++#define __scc_clk_as_devclk(base) \ ++do { \ ++ REG_SCC_CR(base) &= ~SCC_CR_FDIV_MASK; \ ++ REG_SCC_CR(base) |= SCC_CR_FDIV_1; \ ++} while (0) ++ ++#define __scc_clk_as_half_devclk(base) \ ++do { \ ++ REG_SCC_CR(base) &= ~SCC_CR_FDIV_MASK; \ ++ REG_SCC_CR(base) |= SCC_CR_FDIV_2; \ ++} while (0) ++ ++/* n=1,4,8,14 */ ++#define __scc_set_fifo_trigger(base, n) \ ++do { \ ++ REG_SCC_CR(base) &= ~SCC_CR_TRIG_MASK; \ ++ REG_SCC_CR(base) |= SCC_CR_TRIG_##n; \ ++} while (0) ++ ++#define __scc_set_protocol(base, p) \ ++do { \ ++ if (p) \ ++ REG_SCC_CR(base) |= SCC_CR_TP; \ ++ else \ ++ REG_SCC_CR(base) &= ~SCC_CR_TP; \ ++} while (0) ++ ++#define __scc_flush_fifo(base) ( REG_SCC_CR(base) |= SCC_CR_FLUSH ) ++ ++#define __scc_set_invert_mode(base) ( REG_SCC_CR(base) |= SCC_CR_CONV ) ++#define __scc_set_direct_mode(base) ( REG_SCC_CR(base) &= ~SCC_CR_CONV ) ++ ++#define SCC_ERR_INTRS \ ++ ( SCC_CR_ECIE | SCC_CR_EPIE | SCC_CR_RETIE | SCC_CR_EOIE ) ++#define SCC_ALL_INTRS \ ++ ( SCC_CR_TXIE | SCC_CR_RXIE | SCC_CR_TENDIE | SCC_CR_RTOIE | \ ++ SCC_CR_ECIE | SCC_CR_EPIE | SCC_CR_RETIE | SCC_CR_EOIE ) ++ ++#define __scc_enable_err_intrs(base) ( REG_SCC_CR(base) |= SCC_ERR_INTRS ) ++#define __scc_disable_err_intrs(base) ( REG_SCC_CR(base) &= ~SCC_ERR_INTRS ) ++ ++#define SCC_ALL_ERRORS \ ++ ( SCC_SR_ORER | SCC_SR_RTO | SCC_SR_PER | SCC_SR_RETR_3 | SCC_SR_ECNTO) ++ ++#define __scc_clear_errors(base) ( REG_SCC_SR(base) &= ~SCC_ALL_ERRORS ) ++ ++#define __scc_enable_all_intrs(base) ( REG_SCC_CR(base) |= SCC_ALL_INTRS ) ++#define __scc_disable_all_intrs(base) ( REG_SCC_CR(base) &= ~SCC_ALL_INTRS ) ++ ++#define __scc_enable_tx_intr(base) ( REG_SCC_CR(base) |= SCC_CR_TXIE | SCC_CR_TENDIE ) ++#define __scc_disable_tx_intr(base) ( REG_SCC_CR(base) &= ~(SCC_CR_TXIE | SCC_CR_TENDIE) ) ++ ++#define __scc_enable_rx_intr(base) ( REG_SCC_CR(base) |= SCC_CR_RXIE) ++#define __scc_disable_rx_intr(base) ( REG_SCC_CR(base) &= ~SCC_CR_RXIE) ++ ++#define __scc_set_tsend(base) ( REG_SCC_CR(base) |= SCC_CR_TSEND ) ++#define __scc_clear_tsend(base) ( REG_SCC_CR(base) &= ~SCC_CR_TSEND ) ++ ++#define __scc_set_clockstop(base) ( REG_SCC_CR(base) |= SCC_CR_CLKSTP ) ++#define __scc_clear_clockstop(base) ( REG_SCC_CR(base) &= ~SCC_CR_CLKSTP ) ++ ++#define __scc_clockstop_low(base) \ ++do { \ ++ REG_SCC_CR(base) &= ~SCC_CR_PX_MASK; \ ++ REG_SCC_CR(base) |= SCC_CR_PX_STOP_LOW; \ ++} while (0) ++ ++#define __scc_clockstop_high(base) \ ++do { \ ++ REG_SCC_CR(base) &= ~SCC_CR_PX_MASK; \ ++ REG_SCC_CR(base) |= SCC_CR_PX_STOP_HIGH; \ ++} while (0) ++ ++ ++/* SCC status checking */ ++#define __scc_check_transfer_status(base) ( REG_SCC_SR(base) & SCC_SR_TRANS ) ++#define __scc_check_rx_overrun_error(base) ( REG_SCC_SR(base) & SCC_SR_ORER ) ++#define __scc_check_rx_timeout(base) ( REG_SCC_SR(base) & SCC_SR_RTO ) ++#define __scc_check_parity_error(base) ( REG_SCC_SR(base) & SCC_SR_PER ) ++#define __scc_check_txfifo_trigger(base) ( REG_SCC_SR(base) & SCC_SR_TFTG ) ++#define __scc_check_rxfifo_trigger(base) ( REG_SCC_SR(base) & SCC_SR_RFTG ) ++#define __scc_check_tx_end(base) ( REG_SCC_SR(base) & SCC_SR_TEND ) ++#define __scc_check_retx_3(base) ( REG_SCC_SR(base) & SCC_SR_RETR_3 ) ++#define __scc_check_ecnt_overflow(base) ( REG_SCC_SR(base) & SCC_SR_ECNTO ) ++ ++ ++/*************************************************************************** ++ * WDT ++ ***************************************************************************/ ++ ++#define __wdt_set_count(count) ( REG_WDT_WTCNT = (count) ) ++#define __wdt_start() ( REG_WDT_WTCSR |= WDT_WTCSR_START ) ++#define __wdt_stop() ( REG_WDT_WTCSR &= ~WDT_WTCSR_START ) ++ ++ ++/*************************************************************************** ++ * OST ++ ***************************************************************************/ ++ ++#define __ost_enable_all() ( REG_OST_TER |= 0x07 ) ++#define __ost_disable_all() ( REG_OST_TER &= ~0x07 ) ++#define __ost_enable_channel(n) ( REG_OST_TER |= (1 << (n)) ) ++#define __ost_disable_channel(n) ( REG_OST_TER &= ~(1 << (n)) ) ++#define __ost_set_reload(n, val) ( REG_OST_TRDR(n) = (val) ) ++#define __ost_set_count(n, val) ( REG_OST_TCNT(n) = (val) ) ++#define __ost_get_count(n) ( REG_OST_TCNT(n) ) ++#define __ost_set_clock(n, cs) \ ++do { \ ++ REG_OST_TCSR(n) &= ~OST_TCSR_CKS_MASK; \ ++ REG_OST_TCSR(n) |= cs; \ ++} while (0) ++#define __ost_set_mode(n, val) ( REG_OST_TCSR(n) = (val) ) ++#define __ost_enable_interrupt(n) ( REG_OST_TCSR(n) |= OST_TCSR_UIE ) ++#define __ost_disable_interrupt(n) ( REG_OST_TCSR(n) &= ~OST_TCSR_UIE ) ++#define __ost_uf_detected(n) ( REG_OST_TCSR(n) & OST_TCSR_UF ) ++#define __ost_clear_uf(n) ( REG_OST_TCSR(n) &= ~OST_TCSR_UF ) ++#define __ost_is_busy(n) ( REG_OST_TCSR(n) & OST_TCSR_BUSY ) ++#define __ost_clear_busy(n) ( REG_OST_TCSR(n) &= ~OST_TCSR_BUSY ) ++ ++ ++/*************************************************************************** ++ * UART ++ ***************************************************************************/ ++ ++#define __uart_enable(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) |= UARTFCR_UUE | UARTFCR_FE ) ++#define __uart_disable(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) = ~UARTFCR_UUE ) ++ ++#define __uart_enable_transmit_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_TIE ) ++#define __uart_disable_transmit_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~UARTIER_TIE ) ++ ++#define __uart_enable_receive_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE ) ++#define __uart_disable_receive_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~(UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE) ) ++ ++#define __uart_enable_loopback(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) |= UARTMCR_LOOP ) ++#define __uart_disable_loopback(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) &= ~UARTMCR_LOOP ) ++ ++#define __uart_set_8n1(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) = UARTLCR_WLEN_8 ) ++ ++#define __uart_set_baud(n, devclk, baud) \ ++ do { \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) |= UARTLCR_DLAB; \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_DLLR) = (devclk / 16 / baud) & 0xff; \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_DLHR) = ((devclk / 16 / baud) >> 8) & 0xff; \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) &= ~UARTLCR_DLAB; \ ++ } while (0) ++ ++#define __uart_parity_error(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_PER) != 0 ) ++ ++#define __uart_clear_errors(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) &= ~(UARTLSR_ORER | UARTLSR_BRK | UARTLSR_FER | UARTLSR_PER | UARTLSR_RFER) ) ++ ++#define __uart_transmit_fifo_empty(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TDRQ) != 0 ) ++ ++#define __uart_transmit_end(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TEMT) != 0 ) ++ ++#define __uart_transmit_char(n, ch) \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_TDR) = (ch) ++ ++#define __uart_receive_fifo_full(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) ++ ++#define __uart_receive_ready(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) ++ ++#define __uart_receive_char(n) \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_RDR) ++ ++#define __uart_disable_irda() \ ++ ( REG8(IRDA_BASE + OFF_SIRCR) &= ~(SIRCR_TSIRE | SIRCR_RSIRE) ) ++#define __uart_enable_irda() \ ++ /* Tx high pulse as 0, Rx low pulse as 0 */ \ ++ ( REG8(IRDA_BASE + OFF_SIRCR) = SIRCR_TSIRE | SIRCR_RSIRE | SIRCR_RXPL | SIRCR_TPWS ) ++ ++ ++/*************************************************************************** ++ * INTC ++ ***************************************************************************/ ++#define __intc_unmask_irq(n) ( REG_INTC_IMCR = (1 << (n)) ) ++#define __intc_mask_irq(n) ( REG_INTC_IMSR = (1 << (n)) ) ++#define __intc_ack_irq(n) ( REG_INTC_IPR = (1 << (n)) ) ++ ++/*************************************************************************** ++ * CIM ++ ***************************************************************************/ ++ ++#define __cim_enable() ( REG_CIM_CTRL |= CIM_CTRL_ENA ) ++#define __cim_disable() ( REG_CIM_CTRL &= ~CIM_CTRL_ENA ) ++ ++#define __cim_input_data_inverse() ( REG_CIM_CFG |= CIM_CFG_INV_DAT ) ++#define __cim_input_data_normal() ( REG_CIM_CFG &= ~CIM_CFG_INV_DAT ) ++ ++#define __cim_vsync_active_low() ( REG_CIM_CFG |= CIM_CFG_VSP ) ++#define __cim_vsync_active_high() ( REG_CIM_CFG &= ~CIM_CFG_VSP ) ++ ++#define __cim_hsync_active_low() ( REG_CIM_CFG |= CIM_CFG_HSP ) ++#define __cim_hsync_active_high() ( REG_CIM_CFG &= ~CIM_CFG_HSP ) ++ ++#define __cim_sample_data_at_pclk_falling_edge() \ ++ ( REG_CIM_CFG |= CIM_CFG_PCP ) ++#define __cim_sample_data_at_pclk_rising_edge() \ ++ ( REG_CIM_CFG &= ~CIM_CFG_PCP ) ++ ++#define __cim_enable_dummy_zero() ( REG_CIM_CFG |= CIM_CFG_DUMMY_ZERO ) ++#define __cim_disable_dummy_zero() ( REG_CIM_CFG &= ~CIM_CFG_DUMMY_ZERO ) ++ ++#define __cim_select_external_vsync() ( REG_CIM_CFG |= CIM_CFG_EXT_VSYNC ) ++#define __cim_select_internal_vsync() ( REG_CIM_CFG &= ~CIM_CFG_EXT_VSYNC ) ++ ++/* n=0-7 */ ++#define __cim_set_data_packing_mode(n) \ ++do { \ ++ REG_CIM_CFG &= ~CIM_CFG_PACK_MASK; \ ++ REG_CIM_CFG |= (CIM_CFG_PACK_##n); \ ++} while (0) ++ ++#define __cim_enable_ccir656_progressive_mode() \ ++do { \ ++ REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ ++ REG_CIM_CFG |= CIM_CFG_DSM_CPM; \ ++} while (0) ++ ++#define __cim_enable_ccir656_interlace_mode() \ ++do { \ ++ REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ ++ REG_CIM_CFG |= CIM_CFG_DSM_CIM; \ ++} while (0) ++ ++#define __cim_enable_gated_clock_mode() \ ++do { \ ++ REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ ++ REG_CIM_CFG |= CIM_CFG_DSM_GCM; \ ++} while (0) ++ ++#define __cim_enable_nongated_clock_mode() \ ++do { \ ++ REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ ++ REG_CIM_CFG |= CIM_CFG_DSM_NGCM; \ ++} while (0) ++ ++/* sclk:system bus clock ++ * mclk: CIM master clock ++ */ ++#define __cim_set_master_clk(sclk, mclk) \ ++do { \ ++ REG_CIM_CTRL &= ~CIM_CTRL_MCLKDIV_MASK; \ ++ REG_CIM_CTRL |= (((sclk)/(mclk) - 1) << CIM_CTRL_MCLKDIV_BIT); \ ++} while (0) ++ ++#define __cim_enable_sof_intr() \ ++ ( REG_CIM_CTRL |= CIM_CTRL_DMA_SOFM ) ++#define __cim_disable_sof_intr() \ ++ ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_SOFM ) ++ ++#define __cim_enable_eof_intr() \ ++ ( REG_CIM_CTRL |= CIM_CTRL_DMA_EOFM ) ++#define __cim_disable_eof_intr() \ ++ ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_EOFM ) ++ ++#define __cim_enable_stop_intr() \ ++ ( REG_CIM_CTRL |= CIM_CTRL_DMA_STOPM ) ++#define __cim_disable_stop_intr() \ ++ ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_STOPM ) ++ ++#define __cim_enable_trig_intr() \ ++ ( REG_CIM_CTRL |= CIM_CTRL_RXF_TRIGM ) ++#define __cim_disable_trig_intr() \ ++ ( REG_CIM_CTRL &= ~CIM_CTRL_RXF_TRIGM ) ++ ++#define __cim_enable_rxfifo_overflow_intr() \ ++ ( REG_CIM_CTRL |= CIM_CTRL_RXF_OFM ) ++#define __cim_disable_rxfifo_overflow_intr() \ ++ ( REG_CIM_CTRL &= ~CIM_CTRL_RXF_OFM ) ++ ++/* n=1-16 */ ++#define __cim_set_frame_rate(n) \ ++do { \ ++ REG_CIM_CTRL &= ~CIM_CTRL_FRC_MASK; \ ++ REG_CIM_CTRL |= CIM_CTRL_FRC_##n; \ ++} while (0) ++ ++#define __cim_enable_dma() ( REG_CIM_CTRL |= CIM_CTRL_DMA_EN ) ++#define __cim_disable_dma() ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_EN ) ++ ++#define __cim_reset_rxfifo() ( REG_CIM_CTRL |= CIM_CTRL_RXF_RST ) ++#define __cim_unreset_rxfifo() ( REG_CIM_CTRL &= ~CIM_CTRL_RXF_RST ) ++ ++/* n=4,8,12,16,20,24,28,32 */ ++#define __cim_set_rxfifo_trigger(n) \ ++do { \ ++ REG_CIM_CTRL &= ~CIM_CTRL_RXF_TRIG_MASK; \ ++ REG_CIM_CTRL |= CIM_CTRL_RXF_TRIG_##n; \ ++} while (0) ++ ++#define __cim_clear_state() ( REG_CIM_STATE = 0 ) ++ ++#define __cim_disable_done() ( REG_CIM_STATE & CIM_STATE_VDD ) ++#define __cim_rxfifo_empty() ( REG_CIM_STATE & CIM_STATE_RXF_EMPTY ) ++#define __cim_rxfifo_reach_trigger() ( REG_CIM_STATE & CIM_STATE_RXF_TRIG ) ++#define __cim_rxfifo_overflow() ( REG_CIM_STATE & CIM_STATE_RXF_OF ) ++#define __cim_clear_rxfifo_overflow() ( REG_CIM_STATE &= ~CIM_STATE_RXF_OF ) ++#define __cim_dma_stop() ( REG_CIM_STATE & CIM_STATE_DMA_STOP ) ++#define __cim_dma_eof() ( REG_CIM_STATE & CIM_STATE_DMA_EOF ) ++#define __cim_dma_sof() ( REG_CIM_STATE & CIM_STATE_DMA_SOF ) ++ ++#define __cim_get_iid() ( REG_CIM_IID ) ++#define __cim_get_image_data() ( REG_CIM_RXFIFO ) ++#define __cim_get_dam_cmd() ( REG_CIM_CMD ) ++ ++#define __cim_set_da(a) ( REG_CIM_DA = (a) ) ++ ++/*************************************************************************** ++ * PWM ++ ***************************************************************************/ ++ ++/* n is the pwm channel (0,1,..) */ ++#define __pwm_enable_module(n) ( REG_PWM_CTR(n) |= PWM_CTR_EN ) ++#define __pwm_disable_module(n) ( REG_PWM_CTR(n) &= ~PWM_CTR_EN ) ++#define __pwm_graceful_shutdown_mode(n) ( REG_PWM_CTR(n) &= ~PWM_CTR_SD ) ++#define __pwm_abrupt_shutdown_mode(n) ( REG_PWM_CTR(n) |= PWM_CTR_SD ) ++#define __pwm_set_full_duty(n) ( REG_PWM_DUT(n) |= PWM_DUT_FDUTY ) ++ ++#define __pwm_set_prescale(n, p) \ ++ ( REG_PWM_CTR(n) = ((REG_PWM_CTR(n) & ~PWM_CTR_PRESCALE_MASK) | (p) ) ) ++#define __pwm_set_period(n, p) \ ++ ( REG_PWM_PER(n) = ( (REG_PWM_PER(n) & ~PWM_PER_PERIOD_MASK) | (p) ) ) ++#define __pwm_set_duty(n, d) \ ++ ( REG_PWM_DUT(n) = ( (REG_PWM_DUT(n) & ~(PWM_DUT_FDUTY | PWM_DUT_DUTY_MASK)) | (d) ) ) ++ ++/*************************************************************************** ++ * EMC ++ ***************************************************************************/ ++ ++#define __emc_enable_split() ( REG_EMC_BCR = EMC_BCR_BRE ) ++#define __emc_disable_split() ( REG_EMC_BCR = 0 ) ++ ++#define __emc_smem_bus_width(n) /* 8, 16 or 32*/ \ ++ ( REG_EMC_SMCR = (REG_EMC_SMCR & EMC_SMCR_BW_MASK) | \ ++ EMC_SMCR_BW_##n##BIT ) ++#define __emc_smem_byte_control() \ ++ ( REG_EMC_SMCR = (REG_EMC_SMCR | EMC_SMCR_BCM ) ++#define __emc_normal_smem() \ ++ ( REG_EMC_SMCR = (REG_EMC_SMCR & ~EMC_SMCR_SMT ) ++#define __emc_burst_smem() \ ++ ( REG_EMC_SMCR = (REG_EMC_SMCR | EMC_SMCR_SMT ) ++#define __emc_smem_burstlen(n) /* 4, 8, 16 or 32 */ \ ++ ( REG_EMC_SMCR = (REG_EMC_SMCR & EMC_SMCR_BL_MASK) | (EMC_SMCR_BL_##n ) ++ ++/*************************************************************************** ++ * GPIO ++ ***************************************************************************/ ++ ++/* p is the port number (0,1,2,3) ++ * o is the pin offset (0-31) inside the port ++ * n is the absolute number of a pin (0-124), regardless of the port ++ * m is the interrupt manner (low/high/falling/rising) ++ */ ++ ++#define __gpio_port_data(p) ( REG_GPIO_GPDR(p) ) ++ ++#define __gpio_port_as_output(p, o) \ ++do { \ ++ unsigned int tmp; \ ++ REG_GPIO_GPIER(p) &= ~(1 << (o)); \ ++ REG_GPIO_GPDIR(p) |= (1 << (o)); \ ++ if (o < 16) { \ ++ tmp = REG_GPIO_GPALR(p); \ ++ tmp &= ~(3 << ((o) << 1)); \ ++ REG_GPIO_GPALR(p) = tmp; \ ++ } else { \ ++ tmp = REG_GPIO_GPAUR(p); \ ++ tmp &= ~(3 << (((o) - 16)<< 1)); \ ++ REG_GPIO_GPAUR(p) = tmp; \ ++ } \ ++} while (0) ++ ++#define __gpio_port_as_input(p, o) \ ++do { \ ++ unsigned int tmp; \ ++ REG_GPIO_GPIER(p) &= ~(1 << (o)); \ ++ REG_GPIO_GPDIR(p) &= ~(1 << (o)); \ ++ if (o < 16) { \ ++ tmp = REG_GPIO_GPALR(p); \ ++ tmp &= ~(3 << ((o) << 1)); \ ++ REG_GPIO_GPALR(p) = tmp; \ ++ } else { \ ++ tmp = REG_GPIO_GPAUR(p); \ ++ tmp &= ~(3 << (((o) - 16)<< 1)); \ ++ REG_GPIO_GPAUR(p) = tmp; \ ++ } \ ++} while (0) ++ ++#define __gpio_as_output(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ __gpio_port_as_output(p, o); \ ++} while (0) ++ ++#define __gpio_as_input(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ __gpio_port_as_input(p, o); \ ++} while (0) ++ ++#define __gpio_set_pin(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ __gpio_port_data(p) |= (1 << o); \ ++} while (0) ++ ++#define __gpio_clear_pin(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ __gpio_port_data(p) &= ~(1 << o); \ ++} while (0) ++ ++static __inline__ unsigned int __gpio_get_pin(unsigned int n) ++{ ++ unsigned int p, o; ++ p = (n) / 32; ++ o = (n) % 32; ++ if (__gpio_port_data(p) & (1 << o)) ++ return 1; ++ else ++ return 0; ++} ++ ++#define __gpio_set_irq_detect_manner(p, o, m) \ ++do { \ ++ unsigned int tmp; \ ++ if (o < 16) { \ ++ tmp = REG_GPIO_GPIDLR(p); \ ++ tmp &= ~(3 << ((o) << 1)); \ ++ tmp |= ((m) << ((o) << 1)); \ ++ REG_GPIO_GPIDLR(p) = tmp; \ ++ } else { \ ++ tmp = REG_GPIO_GPIDUR(p); \ ++ tmp &= ~(3 << (((o)-16) << 1)); \ ++ tmp |= ((m) << (((o)-16) << 1)); \ ++ REG_GPIO_GPIDUR(p) = tmp; \ ++ } \ ++} while (0) ++ ++#define __gpio_port_as_irq(p, o, m) \ ++do { \ ++ __gpio_port_as_input(p, o); \ ++ __gpio_set_irq_detect_manner(p, o, m); \ ++} while (0) ++ ++#define __gpio_as_irq(n, m) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ __gpio_port_as_irq(p, o, m); \ ++} while (0) ++ ++ ++#define __gpio_as_irq_high_level(n) __gpio_as_irq(n, GPIO_IRQ_HILEVEL) ++#define __gpio_as_irq_low_level(n) __gpio_as_irq(n, GPIO_IRQ_LOLEVEL) ++#define __gpio_as_irq_fall_edge(n) __gpio_as_irq(n, GPIO_IRQ_FALLEDG) ++#define __gpio_as_irq_rise_edge(n) __gpio_as_irq(n, GPIO_IRQ_RAISEDG) ++ ++ ++#define __gpio_mask_irq(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_GPIER(p) &= ~(1 << o); \ ++} while (0) ++ ++#define __gpio_unmask_irq(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_GPIER(p) |= (1 << o); \ ++} while (0) ++ ++#define __gpio_ack_irq(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_GPFR(p) |= (1 << o); \ ++} while (0) ++ ++ ++static __inline__ unsigned int __gpio_get_irq(void) ++{ ++ unsigned int tmp, i; ++ ++ tmp = REG_GPIO_GPFR(3); ++ for (i=0; i<32; i++) ++ if (tmp & (1 << i)) ++ return 0x60 + i; ++ tmp = REG_GPIO_GPFR(2); ++ for (i=0; i<32; i++) ++ if (tmp & (1 << i)) ++ return 0x40 + i; ++ tmp = REG_GPIO_GPFR(1); ++ for (i=0; i<32; i++) ++ if (tmp & (1 << i)) ++ return 0x20 + i; ++ tmp = REG_GPIO_GPFR(0); ++ for (i=0; i<32; i++) ++ if (tmp & (1 << i)) ++ return i; ++ return 0; ++} ++ ++#define __gpio_group_irq(n) \ ++({ \ ++ register int tmp, i; \ ++ tmp = REG_GPIO_GPFR((n)); \ ++ for (i=31;i>=0;i--) \ ++ if (tmp & (1 << i)) \ ++ break; \ ++ i; \ ++}) ++ ++#define __gpio_enable_pull(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_GPPUR(p) |= (1 << o); \ ++} while (0) ++ ++#define __gpio_disable_pull(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_GPPUR(p) &= ~(1 << o); \ ++} while (0) ++ ++ ++/* Init the alternate function pins */ ++ ++ ++#define __gpio_as_ssi() \ ++do { \ ++ REG_GPIO_GPALR(2) &= 0xFC00FFFF; \ ++ REG_GPIO_GPALR(2) |= 0x01550000; \ ++} while (0) ++ ++#define __gpio_as_uart3() \ ++do { \ ++ REG_GPIO_GPAUR(0) &= 0xFFFF0000; \ ++ REG_GPIO_GPAUR(0) |= 0x00005555; \ ++} while (0) ++ ++#define __gpio_as_uart2() \ ++do { \ ++ REG_GPIO_GPALR(3) &= 0x3FFFFFFF; \ ++ REG_GPIO_GPALR(3) |= 0x40000000; \ ++ REG_GPIO_GPAUR(3) &= 0xF3FFFFFF; \ ++ REG_GPIO_GPAUR(3) |= 0x04000000; \ ++} while (0) ++ ++#define __gpio_as_uart1() \ ++do { \ ++ REG_GPIO_GPAUR(0) &= 0xFFF0FFFF; \ ++ REG_GPIO_GPAUR(0) |= 0x00050000; \ ++} while (0) ++ ++#define __gpio_as_uart0() \ ++do { \ ++ REG_GPIO_GPAUR(3) &= 0x0FFFFFFF; \ ++ REG_GPIO_GPAUR(3) |= 0x50000000; \ ++} while (0) ++ ++ ++#define __gpio_as_scc0() \ ++do { \ ++ REG_GPIO_GPALR(2) &= 0xFFFFFFCC; \ ++ REG_GPIO_GPALR(2) |= 0x00000011; \ ++} while (0) ++ ++#define __gpio_as_scc1() \ ++do { \ ++ REG_GPIO_GPALR(2) &= 0xFFFFFF33; \ ++ REG_GPIO_GPALR(2) |= 0x00000044; \ ++} while (0) ++ ++#define __gpio_as_scc() \ ++do { \ ++ __gpio_as_scc0(); \ ++ __gpio_as_scc1(); \ ++} while (0) ++ ++#define __gpio_as_dma() \ ++do { \ ++ REG_GPIO_GPALR(0) &= 0x00FFFFFF; \ ++ REG_GPIO_GPALR(0) |= 0x55000000; \ ++ REG_GPIO_GPAUR(0) &= 0xFF0FFFFF; \ ++ REG_GPIO_GPAUR(0) |= 0x00500000; \ ++} while (0) ++ ++#define __gpio_as_msc() \ ++do { \ ++ REG_GPIO_GPALR(1) &= 0xFFFF000F; \ ++ REG_GPIO_GPALR(1) |= 0x00005550; \ ++} while (0) ++ ++#define __gpio_as_pcmcia() \ ++do { \ ++ REG_GPIO_GPAUR(2) &= 0xF000FFFF; \ ++ REG_GPIO_GPAUR(2) |= 0x05550000; \ ++} while (0) ++ ++#define __gpio_as_emc(csmask) \ ++do { \ ++ REG_GPIO_GPALR(2) &= 0x3FFFFFFF; \ ++ REG_GPIO_GPALR(2) |= 0x40000000; \ ++ REG_GPIO_GPAUR(2) &= 0xFFFF0000; \ ++ REG_GPIO_GPAUR(2) |= 0x00005555; \ ++} while (0) ++ ++#define __gpio_as_lcd_slave() \ ++do { \ ++ REG_GPIO_GPALR(1) &= 0x0000FFFF; \ ++ REG_GPIO_GPALR(1) |= 0x55550000; \ ++ REG_GPIO_GPAUR(1) &= 0x00000000; \ ++ REG_GPIO_GPAUR(1) |= 0x55555555; \ ++} while (0) ++ ++#define __gpio_as_lcd_master() \ ++do { \ ++ REG_GPIO_GPALR(1) &= 0x0000FFFF; \ ++ REG_GPIO_GPALR(1) |= 0x55550000; \ ++ REG_GPIO_GPAUR(1) &= 0x00000000; \ ++ REG_GPIO_GPAUR(1) |= 0x556A5555; \ ++} while (0) ++ ++#define __gpio_as_usb() \ ++do { \ ++ REG_GPIO_GPAUR(0) &= 0x00FFFFFF; \ ++ REG_GPIO_GPAUR(0) |= 0x55000000; \ ++} while (0) ++ ++#define __gpio_as_ac97() \ ++do { \ ++ REG_GPIO_GPALR(2) &= 0xC3FF03FF; \ ++ REG_GPIO_GPALR(2) |= 0x24005400; \ ++} while (0) ++ ++#define __gpio_as_i2s_slave() \ ++do { \ ++ REG_GPIO_GPALR(2) &= 0xC3FF0CFF; \ ++ REG_GPIO_GPALR(2) |= 0x14005100; \ ++} while (0) ++ ++#define __gpio_as_i2s_master() \ ++do { \ ++ REG_GPIO_GPALR(2) &= 0xC3FF0CFF; \ ++ REG_GPIO_GPALR(2) |= 0x28005100; \ ++} while (0) ++ ++#define __gpio_as_eth() \ ++do { \ ++ REG_GPIO_GPAUR(3) &= 0xFC000000; \ ++ REG_GPIO_GPAUR(3) |= 0x01555555; \ ++} while (0) ++ ++#define __gpio_as_pwm() \ ++do { \ ++ REG_GPIO_GPAUR(2) &= 0x0FFFFFFF; \ ++ REG_GPIO_GPAUR(2) |= 0x50000000; \ ++} while (0) ++ ++#define __gpio_as_ps2() \ ++do { \ ++ REG_GPIO_GPALR(1) &= 0xFFFFFFF0; \ ++ REG_GPIO_GPALR(1) |= 0x00000005; \ ++} while (0) ++ ++#define __gpio_as_uprt() \ ++do { \ ++ REG_GPIO_GPALR(1) &= 0x0000000F; \ ++ REG_GPIO_GPALR(1) |= 0x55555550; \ ++ REG_GPIO_GPALR(3) &= 0xC0000000; \ ++ REG_GPIO_GPALR(3) |= 0x15555555; \ ++} while (0) ++ ++#define __gpio_as_cim() \ ++do { \ ++ REG_GPIO_GPALR(0) &= 0xFF000000; \ ++ REG_GPIO_GPALR(0) |= 0x00555555; \ ++} while (0) ++ ++/*************************************************************************** ++ * HARB ++ ***************************************************************************/ ++ ++#define __harb_usb0_udc() \ ++do { \ ++ REG_HARB_HAPOR &= ~HARB_HAPOR_UCHSEL; \ ++} while (0) ++ ++#define __harb_usb0_uhc() \ ++do { \ ++ REG_HARB_HAPOR |= HARB_HAPOR_UCHSEL; \ ++} while (0) ++ ++#define __harb_set_priority(n) \ ++do { \ ++ REG_HARB_HAPOR = ((REG_HARB_HAPOR & ~HARB_HAPOR_PRIO_MASK) | n); \ ++} while (0) ++ ++/*************************************************************************** ++ * I2C ++ ***************************************************************************/ ++ ++#define __i2c_enable() ( REG_I2C_CR |= I2C_CR_I2CE ) ++#define __i2c_disable() ( REG_I2C_CR &= ~I2C_CR_I2CE ) ++ ++#define __i2c_send_start() ( REG_I2C_CR |= I2C_CR_STA ) ++#define __i2c_send_stop() ( REG_I2C_CR |= I2C_CR_STO ) ++#define __i2c_send_ack() ( REG_I2C_CR &= ~I2C_CR_AC ) ++#define __i2c_send_nack() ( REG_I2C_CR |= I2C_CR_AC ) ++ ++#define __i2c_set_drf() ( REG_I2C_SR |= I2C_SR_DRF ) ++#define __i2c_clear_drf() ( REG_I2C_SR &= ~I2C_SR_DRF ) ++#define __i2c_check_drf() ( REG_I2C_SR & I2C_SR_DRF ) ++ ++#define __i2c_received_ack() ( !(REG_I2C_SR & I2C_SR_ACKF) ) ++#define __i2c_is_busy() ( REG_I2C_SR & I2C_SR_BUSY ) ++#define __i2c_transmit_ended() ( REG_I2C_SR & I2C_SR_TEND ) ++ ++#define __i2c_set_clk(dev_clk, i2c_clk) \ ++ ( REG_I2C_GR = (dev_clk) / (16*(i2c_clk)) - 1 ) ++ ++#define __i2c_read() ( REG_I2C_DR ) ++#define __i2c_write(val) ( REG_I2C_DR = (val) ) ++ ++/*************************************************************************** ++ * UDC ++ ***************************************************************************/ ++ ++#define __udc_set_16bit_phy() ( REG_UDC_DevCFGR |= UDC_DevCFGR_PI ) ++#define __udc_set_8bit_phy() ( REG_UDC_DevCFGR &= ~UDC_DevCFGR_PI ) ++ ++#define __udc_enable_sync_frame() ( REG_UDC_DevCFGR |= UDC_DevCFGR_SS ) ++#define __udc_disable_sync_frame() ( REG_UDC_DevCFGR &= ~UDC_DevCFGR_SS ) ++ ++#define __udc_self_powered() ( REG_UDC_DevCFGR |= UDC_DevCFGR_SP ) ++#define __udc_bus_powered() ( REG_UDC_DevCFGR &= ~UDC_DevCFGR_SP ) ++ ++#define __udc_enable_remote_wakeup() ( REG_UDC_DevCFGR |= UDC_DevCFGR_RW ) ++#define __udc_disable_remote_wakeup() ( REG_UDC_DevCFGR &= ~UDC_DevCFGR_RW ) ++ ++#define __udc_set_speed_high() \ ++do { \ ++ REG_UDC_DevCFGR &= ~UDC_DevCFGR_SPD_MASK; \ ++ REG_UDC_DevCFGR |= UDC_DevCFGR_SPD_HS; \ ++} while (0) ++ ++#define __udc_set_speed_full() \ ++do { \ ++ REG_UDC_DevCFGR &= ~UDC_DevCFGR_SPD_MASK; \ ++ REG_UDC_DevCFGR |= UDC_DevCFGR_SPD_FS; \ ++} while (0) ++ ++#define __udc_set_speed_low() \ ++do { \ ++ REG_UDC_DevCFGR &= ~UDC_DevCFGR_SPD_MASK; \ ++ REG_UDC_DevCFGR |= UDC_DevCFGR_SPD_LS; \ ++} while (0) ++ ++ ++#define __udc_set_dma_mode() ( REG_UDC_DevCR |= UDC_DevCR_DM ) ++#define __udc_set_slave_mode() ( REG_UDC_DevCR &= ~UDC_DevCR_DM ) ++#define __udc_set_big_endian() ( REG_UDC_DevCR |= UDC_DevCR_BE ) ++#define __udc_set_little_endian() ( REG_UDC_DevCR &= ~UDC_DevCR_BE ) ++#define __udc_generate_resume() ( REG_UDC_DevCR |= UDC_DevCR_RES ) ++#define __udc_clear_resume() ( REG_UDC_DevCR &= ~UDC_DevCR_RES ) ++ ++ ++#define __udc_get_enumarated_speed() ( REG_UDC_DevSR & UDC_DevSR_ENUMSPD_MASK ) ++#define __udc_suspend_detected() ( REG_UDC_DevSR & UDC_DevSR_SUSP ) ++#define __udc_get_alternate_setting() ( (REG_UDC_DevSR & UDC_DevSR_ALT_MASK) >> UDC_DevSR_ALT_BIT ) ++#define __udc_get_interface_number() ( (REG_UDC_DevSR & UDC_DevSR_INTF_MASK) >> UDC_DevSR_INTF_BIT ) ++#define __udc_get_config_number() ( (REG_UDC_DevSR & UDC_DevSR_CFG_MASK) >> UDC_DevSR_CFG_BIT ) ++ ++ ++#define __udc_sof_detected(r) ( (r) & UDC_DevIntR_SOF ) ++#define __udc_usb_suspend_detected(r) ( (r) & UDC_DevIntR_US ) ++#define __udc_usb_reset_detected(r) ( (r) & UDC_DevIntR_UR ) ++#define __udc_set_interface_detected(r) ( (r) & UDC_DevIntR_SI ) ++#define __udc_set_config_detected(r) ( (r) & UDC_DevIntR_SC ) ++ ++#define __udc_clear_sof() ( REG_UDC_DevIntR |= UDC_DevIntR_SOF ) ++#define __udc_clear_usb_suspend() ( REG_UDC_DevIntR |= UDC_DevIntR_US ) ++#define __udc_clear_usb_reset() ( REG_UDC_DevIntR |= UDC_DevIntR_UR ) ++#define __udc_clear_set_interface() ( REG_UDC_DevIntR |= UDC_DevIntR_SI ) ++#define __udc_clear_set_config() ( REG_UDC_DevIntR |= UDC_DevIntR_SC ) ++ ++#define __udc_mask_sof() ( REG_UDC_DevIntMR |= UDC_DevIntR_SOF ) ++#define __udc_mask_usb_suspend() ( REG_UDC_DevIntMR |= UDC_DevIntR_US ) ++#define __udc_mask_usb_reset() ( REG_UDC_DevIntMR |= UDC_DevIntR_UR ) ++#define __udc_mask_set_interface() ( REG_UDC_DevIntMR |= UDC_DevIntR_SI ) ++#define __udc_mask_set_config() ( REG_UDC_DevIntMR |= UDC_DevIntR_SC ) ++#define __udc_mask_all_dev_intrs() \ ++ ( REG_UDC_DevIntMR = UDC_DevIntR_SOF | UDC_DevIntR_US | \ ++ UDC_DevIntR_UR | UDC_DevIntR_SI | UDC_DevIntR_SC ) ++ ++#define __udc_unmask_sof() ( REG_UDC_DevIntMR &= ~UDC_DevIntR_SOF ) ++#define __udc_unmask_usb_suspend() ( REG_UDC_DevIntMR &= ~UDC_DevIntR_US ) ++#define __udc_unmask_usb_reset() ( REG_UDC_DevIntMR &= ~UDC_DevIntR_UR ) ++#define __udc_unmask_set_interface() ( REG_UDC_DevIntMR &= ~UDC_DevIntR_SI ) ++#define __udc_unmask_set_config() ( REG_UDC_DevIntMR &= ~UDC_DevIntR_SC ) ++#if 0 ++#define __udc_unmask_all_dev_intrs() \ ++ ( REG_UDC_DevIntMR = ~(UDC_DevIntR_SOF | UDC_DevIntR_US | \ ++ UDC_DevIntR_UR | UDC_DevIntR_SI | UDC_DevIntR_SC) ) ++#else ++#define __udc_unmask_all_dev_intrs() \ ++ ( REG_UDC_DevIntMR = 0x00000000 ) ++#endif ++ ++ ++#define __udc_ep0out_irq_detected(epintr) \ ++ ( (((epintr) & UDC_EPIntR_OUTEP_MASK) >> (UDC_EPIntR_OUTEP_BIT + 0)) & 0x1 ) ++#define __udc_ep5out_irq_detected(epintr) \ ++ ( (((epintr) & UDC_EPIntR_OUTEP_MASK) >> (UDC_EPIntR_OUTEP_BIT + 5)) & 0x1 ) ++#define __udc_ep6out_irq_detected(epintr) \ ++ ( (((epintr) & UDC_EPIntR_OUTEP_MASK) >> (UDC_EPIntR_OUTEP_BIT + 6)) & 0x1 ) ++#define __udc_ep7out_irq_detected(epintr) \ ++ ( (((epintr) & UDC_EPIntR_OUTEP_MASK) >> (UDC_EPIntR_OUTEP_BIT + 7)) & 0x1 ) ++ ++#define __udc_ep0in_irq_detected(epintr) \ ++ ( (((epintr) & UDC_EPIntR_INEP_MASK) >> (UDC_EPIntR_INEP_BIT + 0)) & 0x1 ) ++#define __udc_ep1in_irq_detected(epintr) \ ++ ( (((epintr) & UDC_EPIntR_INEP_MASK) >> (UDC_EPIntR_INEP_BIT + 1)) & 0x1 ) ++#define __udc_ep2in_irq_detected(epintr) \ ++ ( (((epintr) & UDC_EPIntR_INEP_MASK) >> (UDC_EPIntR_INEP_BIT + 2)) & 0x1 ) ++#define __udc_ep3in_irq_detected(epintr) \ ++ ( (((epintr) & UDC_EPIntR_INEP_MASK) >> (UDC_EPIntR_INEP_BIT + 3)) & 0x1 ) ++#define __udc_ep4in_irq_detected(epintr) \ ++ ( (((epintr) & UDC_EPIntR_INEP_MASK) >> (UDC_EPIntR_INEP_BIT + 4)) & 0x1 ) ++ ++ ++#define __udc_mask_ep0out_irq() \ ++ ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_OUTEP_BIT + 0)) ) ++#define __udc_mask_ep5out_irq() \ ++ ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_OUTEP_BIT + 5)) ) ++#define __udc_mask_ep6out_irq() \ ++ ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_OUTEP_BIT + 6)) ) ++#define __udc_mask_ep7out_irq() \ ++ ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_OUTEP_BIT + 7)) ) ++ ++#define __udc_unmask_ep0out_irq() \ ++ ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_OUTEP_BIT + 0)) ) ++#define __udc_unmask_ep5out_irq() \ ++ ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_OUTEP_BIT + 5)) ) ++#define __udc_unmask_ep6out_irq() \ ++ ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_OUTEP_BIT + 6)) ) ++#define __udc_unmask_ep7out_irq() \ ++ ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_OUTEP_BIT + 7)) ) ++ ++#define __udc_mask_ep0in_irq() \ ++ ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_INEP_BIT + 0)) ) ++#define __udc_mask_ep1in_irq() \ ++ ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_INEP_BIT + 1)) ) ++#define __udc_mask_ep2in_irq() \ ++ ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_INEP_BIT + 2)) ) ++#define __udc_mask_ep3in_irq() \ ++ ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_INEP_BIT + 3)) ) ++#define __udc_mask_ep4in_irq() \ ++ ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_INEP_BIT + 4)) ) ++ ++#define __udc_unmask_ep0in_irq() \ ++ ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_INEP_BIT + 0)) ) ++#define __udc_unmask_ep1in_irq() \ ++ ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_INEP_BIT + 1)) ) ++#define __udc_unmask_ep2in_irq() \ ++ ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_INEP_BIT + 2)) ) ++#define __udc_unmask_ep3in_irq() \ ++ ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_INEP_BIT + 3)) ) ++#define __udc_unmask_ep4in_irq() \ ++ ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_INEP_BIT + 4)) ) ++ ++#define __udc_mask_all_ep_intrs() \ ++ ( REG_UDC_EPIntMR = 0xffffffff ) ++#define __udc_unmask_all_ep_intrs() \ ++ ( REG_UDC_EPIntMR = 0x00000000 ) ++ ++ ++/* ep0 only CTRL, ep1 only INTR, ep2/3/5/6 only BULK, ep4/7 only ISO */ ++#define __udc_config_endpoint_type() \ ++do { \ ++ REG_UDC_EP0InCR = (REG_UDC_EP0InCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_CTRL; \ ++ REG_UDC_EP0OutCR = (REG_UDC_EP0OutCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_CTRL; \ ++ REG_UDC_EP1InCR = (REG_UDC_EP1InCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_INTR; \ ++ REG_UDC_EP2InCR = (REG_UDC_EP2InCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_BULK; \ ++ REG_UDC_EP3InCR = (REG_UDC_EP3InCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_BULK; \ ++ REG_UDC_EP4InCR = (REG_UDC_EP4InCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_ISO; \ ++ REG_UDC_EP5OutCR = (REG_UDC_EP5OutCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_BULK; \ ++ REG_UDC_EP6OutCR = (REG_UDC_EP6OutCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_BULK; \ ++ REG_UDC_EP7OutCR = (REG_UDC_EP7OutCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_ISO; \ ++} while (0) ++ ++#define __udc_enable_ep0out_snoop_mode() ( REG_UDC_EP0OutCR |= UDC_EPCR_SN ) ++#define __udc_enable_ep5out_snoop_mode() ( REG_UDC_EP5OutCR |= UDC_EPCR_SN ) ++#define __udc_enable_ep6out_snoop_mode() ( REG_UDC_EP6OutCR |= UDC_EPCR_SN ) ++#define __udc_enable_ep7out_snoop_mode() ( REG_UDC_EP7OutCR |= UDC_EPCR_SN ) ++ ++#define __udc_disable_ep0out_snoop_mode() ( REG_UDC_EP0OutCR &= ~UDC_EPCR_SN ) ++#define __udc_disable_ep5out_snoop_mode() ( REG_UDC_EP5OutCR &= ~UDC_EPCR_SN ) ++#define __udc_disable_ep6out_snoop_mode() ( REG_UDC_EP6OutCR &= ~UDC_EPCR_SN ) ++#define __udc_disable_ep7out_snoop_mode() ( REG_UDC_EP7OutCR &= ~UDC_EPCR_SN ) ++ ++#define __udc_flush_ep0in_fifo() ( REG_UDC_EP0InCR |= UDC_EPCR_F ) ++#define __udc_flush_ep1in_fifo() ( REG_UDC_EP1InCR |= UDC_EPCR_F ) ++#define __udc_flush_ep2in_fifo() ( REG_UDC_EP2InCR |= UDC_EPCR_F ) ++#define __udc_flush_ep3in_fifo() ( REG_UDC_EP3InCR |= UDC_EPCR_F ) ++#define __udc_flush_ep4in_fifo() ( REG_UDC_EP4InCR |= UDC_EPCR_F ) ++ ++#define __udc_unflush_ep0in_fifo() ( REG_UDC_EP0InCR &= ~UDC_EPCR_F ) ++#define __udc_unflush_ep1in_fifo() ( REG_UDC_EP1InCR &= ~UDC_EPCR_F ) ++#define __udc_unflush_ep2in_fifo() ( REG_UDC_EP2InCR &= ~UDC_EPCR_F ) ++#define __udc_unflush_ep3in_fifo() ( REG_UDC_EP3InCR &= ~UDC_EPCR_F ) ++#define __udc_unflush_ep4in_fifo() ( REG_UDC_EP4InCR &= ~UDC_EPCR_F ) ++ ++#define __udc_enable_ep0in_stall() ( REG_UDC_EP0InCR |= UDC_EPCR_S ) ++#define __udc_enable_ep0out_stall() ( REG_UDC_EP0OutCR |= UDC_EPCR_S ) ++#define __udc_enable_ep1in_stall() ( REG_UDC_EP1InCR |= UDC_EPCR_S ) ++#define __udc_enable_ep2in_stall() ( REG_UDC_EP2InCR |= UDC_EPCR_S ) ++#define __udc_enable_ep3in_stall() ( REG_UDC_EP3InCR |= UDC_EPCR_S ) ++#define __udc_enable_ep4in_stall() ( REG_UDC_EP4InCR |= UDC_EPCR_S ) ++#define __udc_enable_ep5out_stall() ( REG_UDC_EP5OutCR |= UDC_EPCR_S ) ++#define __udc_enable_ep6out_stall() ( REG_UDC_EP6OutCR |= UDC_EPCR_S ) ++#define __udc_enable_ep7out_stall() ( REG_UDC_EP7OutCR |= UDC_EPCR_S ) ++ ++#define __udc_disable_ep0in_stall() ( REG_UDC_EP0InCR &= ~UDC_EPCR_S ) ++#define __udc_disable_ep0out_stall() ( REG_UDC_EP0OutCR &= ~UDC_EPCR_S ) ++#define __udc_disable_ep1in_stall() ( REG_UDC_EP1InCR &= ~UDC_EPCR_S ) ++#define __udc_disable_ep2in_stall() ( REG_UDC_EP2InCR &= ~UDC_EPCR_S ) ++#define __udc_disable_ep3in_stall() ( REG_UDC_EP3InCR &= ~UDC_EPCR_S ) ++#define __udc_disable_ep4in_stall() ( REG_UDC_EP4InCR &= ~UDC_EPCR_S ) ++#define __udc_disable_ep5out_stall() ( REG_UDC_EP5OutCR &= ~UDC_EPCR_S ) ++#define __udc_disable_ep6out_stall() ( REG_UDC_EP6OutCR &= ~UDC_EPCR_S ) ++#define __udc_disable_ep7out_stall() ( REG_UDC_EP7OutCR &= ~UDC_EPCR_S ) ++ ++ ++#define __udc_ep0out_packet_size() \ ++ ( (REG_UDC_EP0OutSR & UDC_EPSR_RXPKTSIZE_MASK) >> UDC_EPSR_RXPKTSIZE_BIT ) ++#define __udc_ep5out_packet_size() \ ++ ( (REG_UDC_EP5OutSR & UDC_EPSR_RXPKTSIZE_MASK) >> UDC_EPSR_RXPKTSIZE_BIT ) ++#define __udc_ep6out_packet_size() \ ++ ( (REG_UDC_EP6OutSR & UDC_EPSR_RXPKTSIZE_MASK) >> UDC_EPSR_RXPKTSIZE_BIT ) ++#define __udc_ep7out_packet_size() \ ++ ( (REG_UDC_EP7OutSR & UDC_EPSR_RXPKTSIZE_MASK) >> UDC_EPSR_RXPKTSIZE_BIT ) ++ ++#define __udc_ep0in_received_intoken() ( (REG_UDC_EP0InSR & UDC_EPSR_IN) ) ++#define __udc_ep1in_received_intoken() ( (REG_UDC_EP1InSR & UDC_EPSR_IN) ) ++#define __udc_ep2in_received_intoken() ( (REG_UDC_EP2InSR & UDC_EPSR_IN) ) ++#define __udc_ep3in_received_intoken() ( (REG_UDC_EP3InSR & UDC_EPSR_IN) ) ++#define __udc_ep4in_received_intoken() ( (REG_UDC_EP4InSR & UDC_EPSR_IN) ) ++ ++#define __udc_ep0out_received_none() \ ++ ( (REG_UDC_EP0OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_NONE ) ++#define __udc_ep0out_received_data() \ ++ ( (REG_UDC_EP0OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVDATA ) ++#define __udc_ep0out_received_setup() \ ++ ( (REG_UDC_EP0OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVSETUP ) ++ ++#define __udc_ep5out_received_none() \ ++ ( (REG_UDC_EP5OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_NONE ) ++#define __udc_ep5out_received_data() \ ++ ( (REG_UDC_EP5OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVDATA ) ++#define __udc_ep5out_received_setup() \ ++ ( (REG_UDC_EP5OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVSETUP ) ++ ++#define __udc_ep6out_received_none() \ ++ ( (REG_UDC_EP6OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_NONE ) ++#define __udc_ep6out_received_data() \ ++ ( (REG_UDC_EP6OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVDATA ) ++#define __udc_ep6out_received_setup() \ ++ ( (REG_UDC_EP6OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVSETUP ) ++ ++#define __udc_ep7out_received_none() \ ++ ( (REG_UDC_EP7OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_NONE ) ++#define __udc_ep7out_received_data() \ ++ ( (REG_UDC_EP7OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVDATA ) ++#define __udc_ep7out_received_setup() \ ++ ( (REG_UDC_EP7OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVSETUP ) ++ ++/* ep7out ISO only */ ++#define __udc_ep7out_get_pid() \ ++ ( (REG_UDC_EP7OutSR & UDC_EPSR_PID_MASK) >> UDC_EPSR_PID_BIT ) ++ ++ ++#define __udc_ep0in_set_buffer_size(n) ( REG_UDC_EP0InBSR = (n) ) ++#define __udc_ep1in_set_buffer_size(n) ( REG_UDC_EP1InBSR = (n) ) ++#define __udc_ep2in_set_buffer_size(n) ( REG_UDC_EP2InBSR = (n) ) ++#define __udc_ep3in_set_buffer_size(n) ( REG_UDC_EP3InBSR = (n) ) ++#define __udc_ep4in_set_buffer_size(n) ( REG_UDC_EP4InBSR = (n) ) ++ ++#define __udc_ep0out_get_frame_number(n) ( UDC_EP0OutPFNR ) ++#define __udc_ep5out_get_frame_number(n) ( UDC_EP5OutPFNR ) ++#define __udc_ep6out_get_frame_number(n) ( UDC_EP6OutPFNR ) ++#define __udc_ep7out_get_frame_number(n) ( UDC_EP7OutPFNR ) ++ ++ ++#define __udc_ep0in_set_max_packet_size(n) ( REG_UDC_EP0InMPSR = (n) ) ++#define __udc_ep0out_set_max_packet_size(n) ( REG_UDC_EP0OutMPSR = (n) ) ++#define __udc_ep1in_set_max_packet_size(n) ( REG_UDC_EP1InMPSR = (n) ) ++#define __udc_ep2in_set_max_packet_size(n) ( REG_UDC_EP2InMPSR = (n) ) ++#define __udc_ep3in_set_max_packet_size(n) ( REG_UDC_EP3InMPSR = (n) ) ++#define __udc_ep4in_set_max_packet_size(n) ( REG_UDC_EP4InMPSR = (n) ) ++#define __udc_ep5out_set_max_packet_size(n) ( REG_UDC_EP5OutMPSR = (n) ) ++#define __udc_ep6out_set_max_packet_size(n) ( REG_UDC_EP6OutMPSR = (n) ) ++#define __udc_ep7out_set_max_packet_size(n) ( REG_UDC_EP7OutMPSR = (n) ) ++ ++/* set to 0xFFFF for UDC */ ++#define __udc_set_setup_command_address(n) ( REG_UDC_STCMAR = (n) ) ++ ++/* Init and configure EPxInfR(x=0,1,2,3,4,5,6,7) ++ * c: Configuration number to which this endpoint belongs ++ * i: Interface number to which this endpoint belongs ++ * a: Alternate setting to which this endpoint belongs ++ * p: max Packet size of this endpoint ++ */ ++ ++#define __udc_ep0info_init(c,i,a,p) \ ++do { \ ++ REG_UDC_EP0InfR &= ~UDC_EPInfR_MPS_MASK; \ ++ REG_UDC_EP0InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ ++ REG_UDC_EP0InfR &= ~UDC_EPInfR_ALTS_MASK; \ ++ REG_UDC_EP0InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ ++ REG_UDC_EP0InfR &= ~UDC_EPInfR_IFN_MASK; \ ++ REG_UDC_EP0InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ ++ REG_UDC_EP0InfR &= ~UDC_EPInfR_CGN_MASK; \ ++ REG_UDC_EP0InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ ++ REG_UDC_EP0InfR &= ~UDC_EPInfR_EPT_MASK; \ ++ REG_UDC_EP0InfR |= UDC_EPInfR_EPT_CTRL; \ ++ REG_UDC_EP0InfR &= ~UDC_EPInfR_EPD; \ ++ REG_UDC_EP0InfR |= UDC_EPInfR_EPD_OUT; \ ++ REG_UDC_EP0InfR &= ~UDC_EPInfR_EPN_MASK; \ ++ REG_UDC_EP0InfR |= (0 << UDC_EPInfR_EPN_BIT); \ ++} while (0) ++ ++#define __udc_ep1info_init(c,i,a,p) \ ++do { \ ++ REG_UDC_EP1InfR &= ~UDC_EPInfR_MPS_MASK; \ ++ REG_UDC_EP1InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ ++ REG_UDC_EP1InfR &= ~UDC_EPInfR_ALTS_MASK; \ ++ REG_UDC_EP1InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ ++ REG_UDC_EP1InfR &= ~UDC_EPInfR_IFN_MASK; \ ++ REG_UDC_EP1InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ ++ REG_UDC_EP1InfR &= ~UDC_EPInfR_CGN_MASK; \ ++ REG_UDC_EP1InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ ++ REG_UDC_EP1InfR &= ~UDC_EPInfR_EPT_MASK; \ ++ REG_UDC_EP1InfR |= UDC_EPInfR_EPT_INTR; \ ++ REG_UDC_EP1InfR &= ~UDC_EPInfR_EPD; \ ++ REG_UDC_EP1InfR |= UDC_EPInfR_EPD_IN; \ ++ REG_UDC_EP1InfR &= ~UDC_EPInfR_EPN_MASK; \ ++ REG_UDC_EP1InfR |= (1 << UDC_EPInfR_EPN_BIT); \ ++} while (0) ++ ++#define __udc_ep2info_init(c,i,a,p) \ ++do { \ ++ REG_UDC_EP2InfR &= ~UDC_EPInfR_MPS_MASK; \ ++ REG_UDC_EP2InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ ++ REG_UDC_EP2InfR &= ~UDC_EPInfR_ALTS_MASK; \ ++ REG_UDC_EP2InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ ++ REG_UDC_EP2InfR &= ~UDC_EPInfR_IFN_MASK; \ ++ REG_UDC_EP2InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ ++ REG_UDC_EP2InfR &= ~UDC_EPInfR_CGN_MASK; \ ++ REG_UDC_EP2InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ ++ REG_UDC_EP2InfR &= ~UDC_EPInfR_EPT_MASK; \ ++ REG_UDC_EP2InfR |= UDC_EPInfR_EPT_BULK; \ ++ REG_UDC_EP2InfR &= ~UDC_EPInfR_EPD; \ ++ REG_UDC_EP2InfR |= UDC_EPInfR_EPD_IN; \ ++ REG_UDC_EP2InfR &= ~UDC_EPInfR_EPN_MASK; \ ++ REG_UDC_EP2InfR |= (2 << UDC_EPInfR_EPN_BIT); \ ++} while (0) ++ ++#define __udc_ep3info_init(c,i,a,p) \ ++do { \ ++ REG_UDC_EP3InfR &= ~UDC_EPInfR_MPS_MASK; \ ++ REG_UDC_EP3InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ ++ REG_UDC_EP3InfR &= ~UDC_EPInfR_ALTS_MASK; \ ++ REG_UDC_EP3InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ ++ REG_UDC_EP3InfR &= ~UDC_EPInfR_IFN_MASK; \ ++ REG_UDC_EP3InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ ++ REG_UDC_EP3InfR &= ~UDC_EPInfR_CGN_MASK; \ ++ REG_UDC_EP3InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ ++ REG_UDC_EP3InfR &= ~UDC_EPInfR_EPT_MASK; \ ++ REG_UDC_EP3InfR |= UDC_EPInfR_EPT_BULK; \ ++ REG_UDC_EP3InfR &= ~UDC_EPInfR_EPD; \ ++ REG_UDC_EP3InfR |= UDC_EPInfR_EPD_IN; \ ++ REG_UDC_EP3InfR &= ~UDC_EPInfR_EPN_MASK; \ ++ REG_UDC_EP3InfR |= (3 << UDC_EPInfR_EPN_BIT); \ ++} while (0) ++ ++#define __udc_ep4info_init(c,i,a,p) \ ++do { \ ++ REG_UDC_EP4InfR &= ~UDC_EPInfR_MPS_MASK; \ ++ REG_UDC_EP4InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ ++ REG_UDC_EP4InfR &= ~UDC_EPInfR_ALTS_MASK; \ ++ REG_UDC_EP4InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ ++ REG_UDC_EP4InfR &= ~UDC_EPInfR_IFN_MASK; \ ++ REG_UDC_EP4InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ ++ REG_UDC_EP4InfR &= ~UDC_EPInfR_CGN_MASK; \ ++ REG_UDC_EP4InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ ++ REG_UDC_EP4InfR &= ~UDC_EPInfR_EPT_MASK; \ ++ REG_UDC_EP4InfR |= UDC_EPInfR_EPT_ISO; \ ++ REG_UDC_EP4InfR &= ~UDC_EPInfR_EPD; \ ++ REG_UDC_EP4InfR |= UDC_EPInfR_EPD_IN; \ ++ REG_UDC_EP4InfR &= ~UDC_EPInfR_EPN_MASK; \ ++ REG_UDC_EP4InfR |= (4 << UDC_EPInfR_EPN_BIT); \ ++} while (0) ++ ++#define __udc_ep5info_init(c,i,a,p) \ ++do { \ ++ REG_UDC_EP5InfR &= ~UDC_EPInfR_MPS_MASK; \ ++ REG_UDC_EP5InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ ++ REG_UDC_EP5InfR &= ~UDC_EPInfR_ALTS_MASK; \ ++ REG_UDC_EP5InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ ++ REG_UDC_EP5InfR &= ~UDC_EPInfR_IFN_MASK; \ ++ REG_UDC_EP5InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ ++ REG_UDC_EP5InfR &= ~UDC_EPInfR_CGN_MASK; \ ++ REG_UDC_EP5InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ ++ REG_UDC_EP5InfR &= ~UDC_EPInfR_EPT_MASK; \ ++ REG_UDC_EP5InfR |= UDC_EPInfR_EPT_BULK; \ ++ REG_UDC_EP5InfR &= ~UDC_EPInfR_EPD; \ ++ REG_UDC_EP5InfR |= UDC_EPInfR_EPD_OUT; \ ++ REG_UDC_EP5InfR &= ~UDC_EPInfR_EPN_MASK; \ ++ REG_UDC_EP5InfR |= (5 << UDC_EPInfR_EPN_BIT); \ ++} while (0) ++ ++#define __udc_ep6info_init(c,i,a,p) \ ++do { \ ++ REG_UDC_EP6InfR &= ~UDC_EPInfR_MPS_MASK; \ ++ REG_UDC_EP6InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ ++ REG_UDC_EP6InfR &= ~UDC_EPInfR_ALTS_MASK; \ ++ REG_UDC_EP6InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ ++ REG_UDC_EP6InfR &= ~UDC_EPInfR_IFN_MASK; \ ++ REG_UDC_EP6InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ ++ REG_UDC_EP6InfR &= ~UDC_EPInfR_CGN_MASK; \ ++ REG_UDC_EP6InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ ++ REG_UDC_EP6InfR &= ~UDC_EPInfR_EPT_MASK; \ ++ REG_UDC_EP6InfR |= UDC_EPInfR_EPT_BULK; \ ++ REG_UDC_EP6InfR &= ~UDC_EPInfR_EPD; \ ++ REG_UDC_EP6InfR |= UDC_EPInfR_EPD_OUT; \ ++ REG_UDC_EP6InfR &= ~UDC_EPInfR_EPN_MASK; \ ++ REG_UDC_EP6InfR |= (6 << UDC_EPInfR_EPN_BIT); \ ++} while (0) ++ ++#define __udc_ep7info_init(c,i,a,p) \ ++do { \ ++ REG_UDC_EP7InfR &= ~UDC_EPInfR_MPS_MASK; \ ++ REG_UDC_EP7InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ ++ REG_UDC_EP7InfR &= ~UDC_EPInfR_ALTS_MASK; \ ++ REG_UDC_EP7InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ ++ REG_UDC_EP7InfR &= ~UDC_EPInfR_IFN_MASK; \ ++ REG_UDC_EP7InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ ++ REG_UDC_EP7InfR &= ~UDC_EPInfR_CGN_MASK; \ ++ REG_UDC_EP7InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ ++ REG_UDC_EP7InfR &= ~UDC_EPInfR_EPT_MASK; \ ++ REG_UDC_EP7InfR |= UDC_EPInfR_EPT_ISO; \ ++ REG_UDC_EP7InfR &= ~UDC_EPInfR_EPD; \ ++ REG_UDC_EP7InfR |= UDC_EPInfR_EPD_OUT; \ ++ REG_UDC_EP7InfR &= ~UDC_EPInfR_EPN_MASK; \ ++ REG_UDC_EP7InfR |= (7 << UDC_EPInfR_EPN_BIT); \ ++} while (0) ++ ++ ++/*************************************************************************** ++ * DMAC ++ ***************************************************************************/ ++ ++/* n is the DMA channel (0 - 7) */ ++ ++#define __dmac_enable_all_channels() \ ++ ( REG_DMAC_DMACR |= DMAC_DMACR_DME | DMAC_DMACR_PR_ROUNDROBIN ) ++#define __dmac_disable_all_channels() \ ++ ( REG_DMAC_DMACR &= ~DMAC_DMACR_DME ) ++ ++/* p=0,1,2,3 */ ++#define __dmac_set_priority(p) \ ++do { \ ++ REG_DMAC_DMACR &= ~DMAC_DMACR_PR_MASK; \ ++ REG_DMAC_DMACR |= ((p) << DMAC_DMACR_PR_BIT); \ ++} while (0) ++ ++#define __dmac_test_halt_error() ( REG_DMAC_DMACR & DMAC_DMACR_HTR ) ++#define __dmac_test_addr_error() ( REG_DMAC_DMACR & DMAC_DMACR_AER ) ++ ++#define __dmac_enable_channel(n) \ ++ ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_CHDE ) ++#define __dmac_disable_channel(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_CHDE ) ++#define __dmac_channel_enabled(n) \ ++ ( REG_DMAC_DCCSR(n) & DMAC_DCCSR_CHDE ) ++ ++#define __dmac_channel_enable_irq(n) \ ++ ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_TCIE ) ++#define __dmac_channel_disable_irq(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_TCIE ) ++ ++#define __dmac_channel_transmit_halt_detected(n) \ ++ ( REG_DMAC_DCCSR(n) & DMAC_DCCSR_HLT ) ++#define __dmac_channel_transmit_end_detected(n) \ ++ ( REG_DMAC_DCCSR(n) & DMAC_DCCSR_TC ) ++#define __dmac_channel_address_error_detected(n) \ ++ ( REG_DMAC_DCCSR(n) & DMAC_DCCSR_AR ) ++ ++#define __dmac_channel_clear_transmit_halt(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_HLT ) ++#define __dmac_channel_clear_transmit_end(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_TC ) ++#define __dmac_channel_clear_address_error(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_AR ) ++ ++#define __dmac_channel_set_single_mode(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_TM ) ++#define __dmac_channel_set_block_mode(n) \ ++ ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_TM ) ++ ++#define __dmac_channel_set_transfer_unit_32bit(n) \ ++do { \ ++ REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_DS_MASK; \ ++ REG_DMAC_DCCSR(n) |= DMAC_DCCSR_DS_32b; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_16bit(n) \ ++do { \ ++ REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_DS_MASK; \ ++ REG_DMAC_DCCSR(n) |= DMAC_DCCSR_DS_16b; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_8bit(n) \ ++do { \ ++ REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_DS_MASK; \ ++ REG_DMAC_DCCSR(n) |= DMAC_DCCSR_DS_8b; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_16byte(n) \ ++do { \ ++ REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_DS_MASK; \ ++ REG_DMAC_DCCSR(n) |= DMAC_DCCSR_DS_16B; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_32byte(n) \ ++do { \ ++ REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_DS_MASK; \ ++ REG_DMAC_DCCSR(n) |= DMAC_DCCSR_DS_32B; \ ++} while (0) ++ ++/* w=8,16,32 */ ++#define __dmac_channel_set_dest_port_width(n,w) \ ++do { \ ++ REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_DWDH_MASK; \ ++ REG_DMAC_DCCSR(n) |= DMAC_DCCSR_DWDH_##w; \ ++} while (0) ++ ++/* w=8,16,32 */ ++#define __dmac_channel_set_src_port_width(n,w) \ ++do { \ ++ REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_SWDH_MASK; \ ++ REG_DMAC_DCCSR(n) |= DMAC_DCCSR_SWDH_##w; \ ++} while (0) ++ ++/* v=0-15 */ ++#define __dmac_channel_set_rdil(n,v) \ ++do { \ ++ REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_RDIL_MASK; \ ++ REG_DMAC_DCCSR(n) |= ((v) << DMAC_DCCSR_RDIL_BIT); \ ++} while (0) ++ ++#define __dmac_channel_dest_addr_fixed(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_DAM ) ++#define __dmac_channel_dest_addr_increment(n) \ ++ ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_DAM ) ++ ++#define __dmac_channel_src_addr_fixed(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_SAM ) ++#define __dmac_channel_src_addr_increment(n) \ ++ ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_SAM ) ++ ++#define __dmac_channel_set_eop_high(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_EOPM ) ++#define __dmac_channel_set_eop_low(n) \ ++ ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_EOPM ) ++ ++#define __dmac_channel_set_erdm(n,m) \ ++do { \ ++ REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_SWDH_MASK; \ ++ REG_DMAC_DCCSR(n) |= ((m) << DMAC_DCCSR_ERDM_BIT); \ ++} while (0) ++ ++#define __dmac_channel_set_eackm(n) \ ++ ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_EACKM ) ++#define __dmac_channel_clear_eackm(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_EACKM ) ++ ++#define __dmac_channel_set_eacks(n) \ ++ ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_EACKS ) ++#define __dmac_channel_clear_eacks(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_EACKS ) ++ ++ ++#define __dmac_channel_irq_detected(n) \ ++ ( REG_DMAC_DCCSR(n) & (DMAC_DCCSR_TC | DMAC_DCCSR_AR) ) ++ ++static __inline__ int __dmac_get_irq(void) ++{ ++ int i; ++ for (i=0;i> AIC_SR_TFL_BIT ) ++#define __aic_get_receive_count() \ ++ ( (REG_AIC_SR & AIC_SR_RFL_MASK) >> AIC_SR_RFL_BIT ) ++ ++#define __ac97_command_transmitted() ( REG_AIC_ACSR & AIC_ACSR_CADT ) ++#define __ac97_status_received() ( REG_AIC_ACSR & AIC_ACSR_SADR ) ++#define __ac97_status_receive_timeout() ( REG_AIC_ACSR & AIC_ACSR_RSTO ) ++#define __ac97_codec_is_low_power_mode() ( REG_AIC_ACSR & AIC_ACSR_CLPM ) ++#define __ac97_codec_is_ready() ( REG_AIC_ACSR & AIC_ACSR_CRDY ) ++ ++#define __i2s_is_busy() ( REG_AIC_I2SSR & AIC_I2SSR_BSY ) ++ ++#define CODEC_READ_CMD (1 << 19) ++#define CODEC_WRITE_CMD (0 << 19) ++#define CODEC_REG_INDEX_BIT 12 ++#define CODEC_REG_INDEX_MASK (0x7f << CODEC_REG_INDEX_BIT) /* 18:12 */ ++#define CODEC_REG_DATA_BIT 4 ++#define CODEC_REG_DATA_MASK (0x0ffff << 4) /* 19:4 */ ++ ++#define __ac97_out_rcmd_addr(reg) \ ++do { \ ++ REG_AIC_ACCAR = CODEC_READ_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ ++} while (0) ++ ++#define __ac97_out_wcmd_addr(reg) \ ++do { \ ++ REG_AIC_ACCAR = CODEC_WRITE_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ ++} while (0) ++ ++#define __ac97_out_data(value) \ ++do { \ ++ REG_AIC_ACCDR = ((value) << CODEC_REG_DATA_BIT); \ ++} while (0) ++ ++#define __ac97_in_data() \ ++ ( (REG_AIC_ACSDR & CODEC_REG_DATA_MASK) >> CODEC_REG_DATA_BIT ) ++ ++#define __ac97_in_status_addr() \ ++ ( (REG_AIC_ACSAR & CODEC_REG_INDEX_MASK) >> CODEC_REG_INDEX_BIT ) ++ ++#define __i2s_set_sample_rate(i2sclk, sync) \ ++ ( REG_AIC_I2SDIV = ((i2sclk) / (4*64)) / (sync) ) ++ ++#define __aic_write_tfifo(v) ( REG_AIC_DR = (v) ) ++#define __aic_read_rfifo() ( REG_AIC_DR ) ++ ++// ++// Define next ops for AC97 compatible ++// ++ ++#define AC97_ACSR AIC_ACSR ++ ++#define __ac97_enable() __aic_enable(); __aic_select_ac97() ++#define __ac97_disable() __aic_disable() ++#define __ac97_reset() __aic_reset() ++ ++#define __ac97_set_transmit_trigger(n) __aic_set_transmit_trigger(n) ++#define __ac97_set_receive_trigger(n) __aic_set_receive_trigger(n) ++ ++#define __ac97_enable_record() __aic_enable_record() ++#define __ac97_disable_record() __aic_disable_record() ++#define __ac97_enable_replay() __aic_enable_replay() ++#define __ac97_disable_replay() __aic_disable_replay() ++#define __ac97_enable_loopback() __aic_enable_loopback() ++#define __ac97_disable_loopback() __aic_disable_loopback() ++ ++#define __ac97_enable_transmit_dma() __aic_enable_transmit_dma() ++#define __ac97_disable_transmit_dma() __aic_disable_transmit_dma() ++#define __ac97_enable_receive_dma() __aic_enable_receive_dma() ++#define __ac97_disable_receive_dma() __aic_disable_receive_dma() ++ ++#define __ac97_transmit_request() __aic_transmit_request() ++#define __ac97_receive_request() __aic_receive_request() ++#define __ac97_transmit_underrun() __aic_transmit_underrun() ++#define __ac97_receive_overrun() __aic_receive_overrun() ++ ++#define __ac97_clear_errors() __aic_clear_errors() ++ ++#define __ac97_get_transmit_resident() __aic_get_transmit_resident() ++#define __ac97_get_receive_count() __aic_get_receive_count() ++ ++#define __ac97_enable_transmit_intr() __aic_enable_transmit_intr() ++#define __ac97_disable_transmit_intr() __aic_disable_transmit_intr() ++#define __ac97_enable_receive_intr() __aic_enable_receive_intr() ++#define __ac97_disable_receive_intr() __aic_disable_receive_intr() ++ ++#define __ac97_write_tfifo(v) __aic_write_tfifo(v) ++#define __ac97_read_rfifo() __aic_read_rfifo() ++ ++// ++// Define next ops for I2S compatible ++// ++ ++#define I2S_ACSR AIC_I2SSR ++ ++#define __i2s_enable() __aic_enable(); __aic_select_i2s() ++#define __i2s_disable() __aic_disable() ++#define __i2s_reset() __aic_reset() ++ ++#define __i2s_set_transmit_trigger(n) __aic_set_transmit_trigger(n) ++#define __i2s_set_receive_trigger(n) __aic_set_receive_trigger(n) ++ ++#define __i2s_enable_record() __aic_enable_record() ++#define __i2s_disable_record() __aic_disable_record() ++#define __i2s_enable_replay() __aic_enable_replay() ++#define __i2s_disable_replay() __aic_disable_replay() ++#define __i2s_enable_loopback() __aic_enable_loopback() ++#define __i2s_disable_loopback() __aic_disable_loopback() ++ ++#define __i2s_enable_transmit_dma() __aic_enable_transmit_dma() ++#define __i2s_disable_transmit_dma() __aic_disable_transmit_dma() ++#define __i2s_enable_receive_dma() __aic_enable_receive_dma() ++#define __i2s_disable_receive_dma() __aic_disable_receive_dma() ++ ++#define __i2s_transmit_request() __aic_transmit_request() ++#define __i2s_receive_request() __aic_receive_request() ++#define __i2s_transmit_underrun() __aic_transmit_underrun() ++#define __i2s_receive_overrun() __aic_receive_overrun() ++ ++#define __i2s_clear_errors() __aic_clear_errors() ++ ++#define __i2s_get_transmit_resident() __aic_get_transmit_resident() ++#define __i2s_get_receive_count() __aic_get_receive_count() ++ ++#define __i2s_enable_transmit_intr() __aic_enable_transmit_intr() ++#define __i2s_disable_transmit_intr() __aic_disable_transmit_intr() ++#define __i2s_enable_receive_intr() __aic_enable_receive_intr() ++#define __i2s_disable_receive_intr() __aic_disable_receive_intr() ++ ++#define __i2s_write_tfifo(v) __aic_write_tfifo(v) ++#define __i2s_read_rfifo() __aic_read_rfifo() ++ ++#define __i2s_reset_codec() \ ++ do { \ ++ __gpio_as_output(70); /* SDATA_OUT */ \ ++ __gpio_as_input(71); /* SDATA_IN */ \ ++ __gpio_as_output(78); /* SYNC */ \ ++ __gpio_as_output(69); /* RESET# */ \ ++ __gpio_clear_pin(70); \ ++ __gpio_clear_pin(71); \ ++ __gpio_clear_pin(78); \ ++ __gpio_clear_pin(69); \ ++ __gpio_as_i2s_master(); \ ++ } while (0) ++ ++ ++/*************************************************************************** ++ * LCD ++ ***************************************************************************/ ++ ++#define __lcd_set_dis() ( REG_LCD_CTRL |= LCD_CTRL_DIS ) ++#define __lcd_clr_dis() ( REG_LCD_CTRL &= ~LCD_CTRL_DIS ) ++ ++#define __lcd_set_ena() ( REG_LCD_CTRL |= LCD_CTRL_ENA ) ++#define __lcd_clr_ena() ( REG_LCD_CTRL &= ~LCD_CTRL_ENA ) ++ ++/* n=1,2,4,8,16 */ ++#define __lcd_set_bpp(n) \ ++ ( REG_LCD_CTRL = (REG_LCD_CTRL & ~LCD_CTRL_BPP_MASK) | LCD_CTRL_BPP_##n ) ++ ++/* n=4,8,16 */ ++#define __lcd_set_burst_length(n) \ ++do { \ ++ REG_LCD_CTRL &= ~LCD_CTRL_BST_MASK; \ ++ REG_LCD_CTRL |= LCD_CTRL_BST_n##; \ ++} while (0) ++ ++#define __lcd_select_rgb565() ( REG_LCD_CTRL &= ~LCD_CTRL_RGB555 ) ++#define __lcd_select_rgb555() ( REG_LCD_CTRL |= LCD_CTRL_RGB555 ) ++ ++#define __lcd_set_ofup() ( REG_LCD_CTRL |= LCD_CTRL_OFUP ) ++#define __lcd_clr_ofup() ( REG_LCD_CTRL &= ~LCD_CTRL_OFUP ) ++ ++/* n=2,4,16 */ ++#define __lcd_set_stn_frc(n) \ ++do { \ ++ REG_LCD_CTRL &= ~LCD_CTRL_FRC_MASK; \ ++ REG_LCD_CTRL |= LCD_CTRL_FRC_n##; \ ++} while (0) ++ ++ ++#define __lcd_pixel_endian_little() ( REG_LCD_CTRL |= LCD_CTRL_PEDN ) ++#define __lcd_pixel_endian_big() ( REG_LCD_CTRL &= ~LCD_CTRL_PEDN ) ++ ++#define __lcd_reverse_byte_endian() ( REG_LCD_CTRL |= LCD_CTRL_BEDN ) ++#define __lcd_normal_byte_endian() ( REG_LCD_CTRL &= ~LCD_CTRL_BEDN ) ++ ++#define __lcd_enable_eof_intr() ( REG_LCD_CTRL |= LCD_CTRL_EOFM ) ++#define __lcd_disable_eof_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_EOFM ) ++ ++#define __lcd_enable_sof_intr() ( REG_LCD_CTRL |= LCD_CTRL_SOFM ) ++#define __lcd_disable_sof_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_SOFM ) ++ ++#define __lcd_enable_ofu_intr() ( REG_LCD_CTRL |= LCD_CTRL_OFUM ) ++#define __lcd_disable_ofu_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_OFUM ) ++ ++#define __lcd_enable_ifu0_intr() ( REG_LCD_CTRL |= LCD_CTRL_IFUM0 ) ++#define __lcd_disable_ifu0_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_IFUM0 ) ++ ++#define __lcd_enable_ifu1_intr() ( REG_LCD_CTRL |= LCD_CTRL_IFUM1 ) ++#define __lcd_disable_ifu1_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_IFUM1 ) ++ ++#define __lcd_enable_ldd_intr() ( REG_LCD_CTRL |= LCD_CTRL_LDDM ) ++#define __lcd_disable_ldd_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_LDDM ) ++ ++#define __lcd_enable_qd_intr() ( REG_LCD_CTRL |= LCD_CTRL_QDM ) ++#define __lcd_disable_qd_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_QDM ) ++ ++ ++/* LCD status register indication */ ++ ++#define __lcd_quick_disable_done() ( REG_LCD_STATE & LCD_STATE_QD ) ++#define __lcd_disable_done() ( REG_LCD_STATE & LCD_STATE_LDD ) ++#define __lcd_infifo0_underrun() ( REG_LCD_STATE & LCD_STATE_IFU0 ) ++#define __lcd_infifo1_underrun() ( REG_LCD_STATE & LCD_STATE_IFU1 ) ++#define __lcd_outfifo_underrun() ( REG_LCD_STATE & LCD_STATE_OFU ) ++#define __lcd_start_of_frame() ( REG_LCD_STATE & LCD_STATE_SOF ) ++#define __lcd_end_of_frame() ( REG_LCD_STATE & LCD_STATE_EOF ) ++ ++#define __lcd_clr_outfifounderrun() ( REG_LCD_STATE &= ~LCD_STATE_OFU ) ++#define __lcd_clr_sof() ( REG_LCD_STATE &= ~LCD_STATE_SOF ) ++#define __lcd_clr_eof() ( REG_LCD_STATE &= ~LCD_STATE_EOF ) ++ ++#define __lcd_panel_white() ( REG_LCD_DEV |= LCD_DEV_WHITE ) ++#define __lcd_panel_black() ( REG_LCD_DEV &= ~LCD_DEV_WHITE ) ++ ++/* n=1,2,4,8 for single mono-STN ++ * n=4,8 for dual mono-STN ++ */ ++#define __lcd_set_panel_datawidth(n) \ ++do { \ ++ REG_LCD_DEV &= ~LCD_DEV_PDW_MASK; \ ++ REG_LCD_DEV |= LCD_DEV_PDW_n##; \ ++} while (0) ++ ++/* m=LCD_DEV_MODE_GENERUIC_TFT_xxx */ ++#define __lcd_set_panel_mode(m) \ ++do { \ ++ REG_LCD_DEV &= ~LCD_DEV_MODE_MASK; \ ++ REG_LCD_DEV |= (m); \ ++} while(0) ++ ++/* n = 0-255 */ ++#define __lcd_disable_ac_bias() ( REG_LCD_IO = 0xff ) ++#define __lcd_set_ac_bias(n) \ ++do { \ ++ REG_LCD_IO &= ~LCD_IO_ACB_MASK; \ ++ REG_LCD_IO |= ((n) << LCD_IO_ACB_BIT); \ ++} while(0) ++ ++#define __lcd_io_set_dir() ( REG_LCD_IO |= LCD_IO_DIR ) ++#define __lcd_io_clr_dir() ( REG_LCD_IO &= ~LCD_IO_DIR ) ++ ++#define __lcd_io_set_dep() ( REG_LCD_IO |= LCD_IO_DEP ) ++#define __lcd_io_clr_dep() ( REG_LCD_IO &= ~LCD_IO_DEP ) ++ ++#define __lcd_io_set_vsp() ( REG_LCD_IO |= LCD_IO_VSP ) ++#define __lcd_io_clr_vsp() ( REG_LCD_IO &= ~LCD_IO_VSP ) ++ ++#define __lcd_io_set_hsp() ( REG_LCD_IO |= LCD_IO_HSP ) ++#define __lcd_io_clr_hsp() ( REG_LCD_IO &= ~LCD_IO_HSP ) ++ ++#define __lcd_io_set_pcp() ( REG_LCD_IO |= LCD_IO_PCP ) ++#define __lcd_io_clr_pcp() ( REG_LCD_IO &= ~LCD_IO_PCP ) ++ ++#define __lcd_vsync_get_vps() \ ++ ( (REG_LCD_VSYNC & LCD_VSYNC_VPS_MASK) >> LCD_VSYNC_VPS_BIT ) ++ ++#define __lcd_vsync_get_vpe() \ ++ ( (REG_LCD_VSYNC & LCD_VSYNC_VPE_MASK) >> LCD_VSYNC_VPE_BIT ) ++#define __lcd_vsync_set_vpe(n) \ ++do { \ ++ REG_LCD_VSYNC &= ~LCD_VSYNC_VPE_MASK; \ ++ REG_LCD_VSYNC |= (n) << LCD_VSYNC_VPE_BIT; \ ++} while (0) ++ ++#define __lcd_hsync_get_hps() \ ++ ( (REG_LCD_HSYNC & LCD_HSYNC_HPS_MASK) >> LCD_HSYNC_HPS_BIT ) ++#define __lcd_hsync_set_hps(n) \ ++do { \ ++ REG_LCD_HSYNC &= ~LCD_HSYNC_HPS_MASK; \ ++ REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPS_BIT; \ ++} while (0) ++ ++#define __lcd_hsync_get_hpe() \ ++ ( (REG_LCD_HSYNC & LCD_HSYNC_HPE_MASK) >> LCD_VSYNC_HPE_BIT ) ++#define __lcd_hsync_set_hpe(n) \ ++do { \ ++ REG_LCD_HSYNC &= ~LCD_HSYNC_HPE_MASK; \ ++ REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPE_BIT; \ ++} while (0) ++ ++#define __lcd_vat_get_ht() \ ++ ( (REG_LCD_VAT & LCD_VAT_HT_MASK) >> LCD_VAT_HT_BIT ) ++#define __lcd_vat_set_ht(n) \ ++do { \ ++ REG_LCD_VAT &= ~LCD_VAT_HT_MASK; \ ++ REG_LCD_VAT |= (n) << LCD_VAT_HT_BIT; \ ++} while (0) ++ ++#define __lcd_vat_get_vt() \ ++ ( (REG_LCD_VAT & LCD_VAT_VT_MASK) >> LCD_VAT_VT_BIT ) ++#define __lcd_vat_set_vt(n) \ ++do { \ ++ REG_LCD_VAT &= ~LCD_VAT_VT_MASK; \ ++ REG_LCD_VAT |= (n) << LCD_VAT_VT_BIT; \ ++} while (0) ++ ++#define __lcd_dah_get_hds() \ ++ ( (REG_LCD_DAH & LCD_DAH_HDS_MASK) >> LCD_DAH_HDS_BIT ) ++#define __lcd_dah_set_hds(n) \ ++do { \ ++ REG_LCD_DAH &= ~LCD_DAH_HDS_MASK; \ ++ REG_LCD_DAH |= (n) << LCD_DAH_HDS_BIT; \ ++} while (0) ++ ++#define __lcd_dah_get_hde() \ ++ ( (REG_LCD_DAH & LCD_DAH_HDE_MASK) >> LCD_DAH_HDE_BIT ) ++#define __lcd_dah_set_hde(n) \ ++do { \ ++ REG_LCD_DAH &= ~LCD_DAH_HDE_MASK; \ ++ REG_LCD_DAH |= (n) << LCD_DAH_HDE_BIT; \ ++} while (0) ++ ++#define __lcd_dav_get_vds() \ ++ ( (REG_LCD_DAV & LCD_DAV_VDS_MASK) >> LCD_DAV_VDS_BIT ) ++#define __lcd_dav_set_vds(n) \ ++do { \ ++ REG_LCD_DAV &= ~LCD_DAV_VDS_MASK; \ ++ REG_LCD_DAV |= (n) << LCD_DAV_VDS_BIT; \ ++} while (0) ++ ++#define __lcd_dav_get_vde() \ ++ ( (REG_LCD_DAV & LCD_DAV_VDE_MASK) >> LCD_DAV_VDE_BIT ) ++#define __lcd_dav_set_vde(n) \ ++do { \ ++ REG_LCD_DAV &= ~LCD_DAV_VDE_MASK; \ ++ REG_LCD_DAV |= (n) << LCD_DAV_VDE_BIT; \ ++} while (0) ++ ++#define __lcd_cmd0_set_sofint() ( REG_LCD_CMD0 |= LCD_CMD_SOFINT ) ++#define __lcd_cmd0_clr_sofint() ( REG_LCD_CMD0 &= ~LCD_CMD_SOFINT ) ++#define __lcd_cmd1_set_sofint() ( REG_LCD_CMD1 |= LCD_CMD_SOFINT ) ++#define __lcd_cmd1_clr_sofint() ( REG_LCD_CMD1 &= ~LCD_CMD_SOFINT ) ++ ++#define __lcd_cmd0_set_eofint() ( REG_LCD_CMD0 |= LCD_CMD_EOFINT ) ++#define __lcd_cmd0_clr_eofint() ( REG_LCD_CMD0 &= ~LCD_CMD_EOFINT ) ++#define __lcd_cmd1_set_eofint() ( REG_LCD_CMD1 |= LCD_CMD_EOFINT ) ++#define __lcd_cmd1_clr_eofint() ( REG_LCD_CMD1 &= ~LCD_CMD_EOFINT ) ++ ++#define __lcd_cmd0_set_pal() ( REG_LCD_CMD0 |= LCD_CMD_PAL ) ++#define __lcd_cmd0_clr_pal() ( REG_LCD_CMD0 &= ~LCD_CMD_PAL ) ++ ++#define __lcd_cmd0_get_len() \ ++ ( (REG_LCD_CMD0 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) ++#define __lcd_cmd1_get_len() \ ++ ( (REG_LCD_CMD1 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) ++ ++ ++ ++/*************************************************************************** ++ * DES ++ ***************************************************************************/ ++ ++ ++/*************************************************************************** ++ * CPM ++ ***************************************************************************/ ++#define __cpm_plcr1_fd() \ ++ ((REG_CPM_PLCR1 & CPM_PLCR1_PLL1FD_MASK) >> CPM_PLCR1_PLL1FD_BIT) ++#define __cpm_plcr1_rd() \ ++ ((REG_CPM_PLCR1 & CPM_PLCR1_PLL1RD_MASK) >> CPM_PLCR1_PLL1RD_BIT) ++#define __cpm_plcr1_od() \ ++ ((REG_CPM_PLCR1 & CPM_PLCR1_PLL1OD_MASK) >> CPM_PLCR1_PLL1OD_BIT) ++#define __cpm_cfcr_mfr() \ ++ ((REG_CPM_CFCR & CPM_CFCR_MFR_MASK) >> CPM_CFCR_MFR_BIT) ++#define __cpm_cfcr_pfr() \ ++ ((REG_CPM_CFCR & CPM_CFCR_PFR_MASK) >> CPM_CFCR_PFR_BIT) ++#define __cpm_cfcr_sfr() \ ++ ((REG_CPM_CFCR & CPM_CFCR_SFR_MASK) >> CPM_CFCR_SFR_BIT) ++#define __cpm_cfcr_ifr() \ ++ ((REG_CPM_CFCR & CPM_CFCR_IFR_MASK) >> CPM_CFCR_IFR_BIT) ++ ++static __inline__ unsigned int __cpm_divisor_encode(unsigned int n) ++{ ++ unsigned int encode[10] = {1,2,3,4,6,8,12,16,24,32}; ++ int i; ++ for (i=0;i<10;i++) ++ if (n < encode[i]) ++ break; ++ return i; ++} ++ ++#define __cpm_set_mclk_div(n) \ ++do { \ ++ REG_CPM_CFCR = (REG_CPM_CFCR & ~CPM_CFCR_MFR_MASK) | \ ++ ((n) << (CPM_CFCR_MFR_BIT)); \ ++} while (0) ++ ++#define __cpm_set_pclk_div(n) \ ++do { \ ++ REG_CPM_CFCR = (REG_CPM_CFCR & ~CPM_CFCR_PFR_MASK) | \ ++ ((n) << (CPM_CFCR_PFR_BIT)); \ ++} while (0) ++ ++#define __cpm_set_sclk_div(n) \ ++do { \ ++ REG_CPM_CFCR = (REG_CPM_CFCR & ~CPM_CFCR_SFR_MASK) | \ ++ ((n) << (CPM_CFCR_SFR_BIT)); \ ++} while (0) ++ ++#define __cpm_set_iclk_div(n) \ ++do { \ ++ REG_CPM_CFCR = (REG_CPM_CFCR & ~CPM_CFCR_IFR_MASK) | \ ++ ((n) << (CPM_CFCR_IFR_BIT)); \ ++} while (0) ++ ++#define __cpm_set_lcdclk_div(n) \ ++do { \ ++ REG_CPM_CFCR = (REG_CPM_CFCR & ~CPM_CFCR_LFR_MASK) | \ ++ ((n) << (CPM_CFCR_LFR_BIT)); \ ++} while (0) ++ ++#define __cpm_enable_cko1() (REG_CPM_CFCR |= CPM_CFCR_CKOEN1) ++#define __cpm_enable_cko2() (REG_CPM_CFCR |= CPM_CFCR_CKOEN2) ++#define __cpm_disable_cko1() (REG_CPM_CFCR &= ~CPM_CFCR_CKOEN1) ++#define __cpm_disable_cko2() (REG_CPM_CFCR &= ~CPM_CFCR_CKOEN2) ++ ++#define __cpm_select_msc_clk(type) \ ++do { \ ++ if (type == 0) \ ++ REG_CPM_CFCR &= ~CPM_CFCR_MSC; \ ++ else \ ++ REG_CPM_CFCR |= CPM_CFCR_MSC; \ ++ REG_CPM_CFCR |= CPM_CFCR_UPE; \ ++} while(0) ++ ++#define __cpm_idle_mode() \ ++ (REG_CPM_LPCR = (REG_CPM_LPCR & ~CPM_LPCR_LPM_MASK) | \ ++ CPM_LPCR_LPM_IDLE) ++#define __cpm_sleep_mode() \ ++ (REG_CPM_LPCR = (REG_CPM_LPCR & ~CPM_LPCR_LPM_MASK) | \ ++ CPM_LPCR_LPM_SLEEP) ++#define __cpm_hibernate_mode() \ ++ (REG_CPM_LPCR = (REG_CPM_LPCR & ~CPM_LPCR_LPM_MASK) | \ ++ CPM_LPCR_LPM_HIBERNATE) ++ ++#define __cpm_start_uart0() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_UART0)) ++#define __cpm_start_uart1() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_UART1)) ++#define __cpm_start_uart2() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_UART2)) ++#define __cpm_start_uart3() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_UART3)) ++#define __cpm_start_ost() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_OST)) ++#define __cpm_start_dmac() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_DMAC)) ++#define __cpm_start_uhc() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_UHC)) ++#define __cpm_start_lcd() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_LCD)) ++#define __cpm_start_i2c() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_I2C)) ++#define __cpm_start_aic_pclk() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_AICPCLK)) ++#define __cpm_start_aic_bitclk() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_AICBCLK)) ++#define __cpm_start_pwm0() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_PWM0)) ++#define __cpm_start_pwm1() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_PWM1)) ++#define __cpm_start_ssi() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_SSI)) ++#define __cpm_start_msc() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_MSC)) ++#define __cpm_start_scc() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_SCC)) ++#define __cpm_start_eth() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_ETH)) ++#define __cpm_start_kbc() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_KBC)) ++#define __cpm_start_cim() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_CIM)) ++#define __cpm_start_udc() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_UDC)) ++#define __cpm_start_uprt() \ ++ (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_UPRT)) ++#define __cpm_start_all() (REG_CPM_MSCR = 0) ++ ++#define __cpm_stop_uart0() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_UART0)) ++#define __cpm_stop_uart1() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_UART1)) ++#define __cpm_stop_uart2() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_UART2)) ++#define __cpm_stop_uart3() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_UART3)) ++#define __cpm_stop_ost() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_OST)) ++#define __cpm_stop_dmac() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_DMAC)) ++#define __cpm_stop_uhc() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_UHC)) ++#define __cpm_stop_lcd() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_LCD)) ++#define __cpm_stop_i2c() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_I2C)) ++#define __cpm_stop_aic_pclk() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_AICPCLK)) ++#define __cpm_stop_aic_bitclk() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_AICBCLK)) ++#define __cpm_stop_pwm0() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_PWM0)) ++#define __cpm_stop_pwm1() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_PWM1)) ++#define __cpm_stop_ssi() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_SSI)) ++#define __cpm_stop_msc() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_MSC)) ++#define __cpm_stop_scc() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_SCC)) ++#define __cpm_stop_eth() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_ETH)) ++#define __cpm_stop_kbc() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_KBC)) ++#define __cpm_stop_cim() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_CIM)) ++#define __cpm_stop_udc() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_UDC)) ++#define __cpm_stop_uprt() \ ++ (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_UPRT)) ++#define __cpm_stop_all() (REG_CPM_MSCR = 0xffffffff) ++ ++#define __cpm_set_pin(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ if (p == 0) \ ++ REG_CPM_GSR0 |= (1 << o); \ ++ else if (p == 1) \ ++ REG_CPM_GSR1 |= (1 << o); \ ++ else if (p == 2) \ ++ REG_CPM_GSR2 |= (1 << o); \ ++ else if (p == 3) \ ++ REG_CPM_GSR3 |= (1 << o); \ ++} while (0) ++ ++#define __cpm_clear_pin(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ if (p == 0) \ ++ REG_CPM_GSR0 &= ~(1 << o); \ ++ else if (p == 1) \ ++ REG_CPM_GSR1 &= ~(1 << o); \ ++ else if (p == 2) \ ++ REG_CPM_GSR2 &= ~(1 << o); \ ++ else if (p == 3) \ ++ REG_CPM_GSR3 &= ~(1 << o); \ ++} while (0) ++ ++ ++/*************************************************************************** ++ * SSI ++ ***************************************************************************/ ++ ++#define __ssi_enable() ( REG_SSI_CR0 |= SSI_CR0_SSIE ) ++#define __ssi_disable() ( REG_SSI_CR0 &= ~SSI_CR0_SSIE ) ++#define __ssi_select_ce() ( REG_SSI_CR0 &= ~SSI_CR0_FSEL ) ++ ++#define __ssi_normal_mode() ( REG_SSI_ITR &= ~SSI_ITR_IVLTM_MASK ) ++ ++#define __ssi_select_ce2() \ ++do { \ ++ REG_SSI_CR0 |= SSI_CR0_FSEL; \ ++ REG_SSI_CR1 &= ~SSI_CR1_MULTS; \ ++} while (0) ++ ++#define __ssi_select_gpc() \ ++do { \ ++ REG_SSI_CR0 &= ~SSI_CR0_FSEL; \ ++ REG_SSI_CR1 |= SSI_CR1_MULTS; \ ++} while (0) ++ ++#define __ssi_enable_tx_intr() \ ++ ( REG_SSI_CR0 |= SSI_CR0_TIE | SSI_CR0_TEIE ) ++ ++#define __ssi_disable_tx_intr() \ ++ ( REG_SSI_CR0 &= ~(SSI_CR0_TIE | SSI_CR0_TEIE) ) ++ ++#define __ssi_enable_rx_intr() \ ++ ( REG_SSI_CR0 |= SSI_CR0_RIE | SSI_CR0_REIE ) ++ ++#define __ssi_disable_rx_intr() \ ++ ( REG_SSI_CR0 &= ~(SSI_CR0_RIE | SSI_CR0_REIE) ) ++ ++#define __ssi_enable_loopback() ( REG_SSI_CR0 |= SSI_CR0_LOOP ) ++#define __ssi_disable_loopback() ( REG_SSI_CR0 &= ~SSI_CR0_LOOP ) ++ ++#define __ssi_enable_receive() ( REG_SSI_CR0 &= ~SSI_CR0_DISREV ) ++#define __ssi_disable_receive() ( REG_SSI_CR0 |= SSI_CR0_DISREV ) ++ ++#define __ssi_finish_receive() \ ++ ( REG_SSI_CR0 |= (SSI_CR0_RFINE | SSI_CR0_RFINC) ) ++ ++#define __ssi_disable_recvfinish() \ ++ ( REG_SSI_CR0 &= ~(SSI_CR0_RFINE | SSI_CR0_RFINC) ) ++ ++#define __ssi_flush_txfifo() ( REG_SSI_CR0 |= SSI_CR0_TFLUSH ) ++#define __ssi_flush_rxfifo() ( REG_SSI_CR0 |= SSI_CR0_RFLUSH ) ++ ++#define __ssi_flush_fifo() \ ++ ( REG_SSI_CR0 |= SSI_CR0_TFLUSH | SSI_CR0_RFLUSH ) ++ ++#define __ssi_finish_transmit() ( REG_SSI_CR1 &= ~SSI_CR1_UNFIN ) ++ ++#define __ssi_spi_format() \ ++do { \ ++ REG_SSI_CR1 &= ~SSI_CR1_FMAT_MASK; \ ++ REG_SSI_CR1 |= SSI_CR1_FMAT_SPI; \ ++ REG_SSI_CR1 &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK);\ ++ REG_SSI_CR1 |= (SSI_CR1_TFVCK_1 | SSI_CR1_TCKFI_1); \ ++} while (0) ++ ++/* TI's SSP format, must clear SSI_CR1.UNFIN */ ++#define __ssi_ssp_format() \ ++do { \ ++ REG_SSI_CR1 &= ~(SSI_CR1_FMAT_MASK | SSI_CR1_UNFIN); \ ++ REG_SSI_CR1 |= SSI_CR1_FMAT_SSP; \ ++} while (0) ++ ++/* National's Microwire format, must clear SSI_CR0.RFINE, and set max delay */ ++#define __ssi_microwire_format() \ ++do { \ ++ REG_SSI_CR1 &= ~SSI_CR1_FMAT_MASK; \ ++ REG_SSI_CR1 |= SSI_CR1_FMAT_MW1; \ ++ REG_SSI_CR1 &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK);\ ++ REG_SSI_CR1 |= (SSI_CR1_TFVCK_3 | SSI_CR1_TCKFI_3); \ ++ REG_SSI_CR0 &= ~SSI_CR0_RFINE; \ ++} while (0) ++ ++/* CE# level (FRMHL), CE# in interval time (ITFRM), ++ clock phase and polarity (PHA POL), ++ interval time (SSIITR), interval characters/frame (SSIICR) */ ++ ++ /* frmhl,endian,mcom,flen,pha,pol MASK */ ++#define SSICR1_MISC_MASK \ ++ ( SSI_CR1_FRMHL_MASK | SSI_CR1_LFST | SSI_CR1_MCOM_MASK \ ++ | SSI_CR1_FLEN_MASK | SSI_CR1_PHA | SSI_CR1_POL ) \ ++ ++#define __ssi_spi_set_misc(frmhl,endian,flen,mcom,pha,pol) \ ++do { \ ++ REG_SSI_CR1 &= ~SSICR1_MISC_MASK; \ ++ REG_SSI_CR1 |= ((frmhl) << 30) | ((endian) << 25) | \ ++ (((mcom) - 1) << 12) | (((flen) - 2) << 4) | \ ++ ((pha) << 1) | (pol); \ ++} while(0) ++ ++/* Transfer with MSB or LSB first */ ++#define __ssi_set_msb() ( REG_SSI_CR1 &= ~SSI_CR1_LFST ) ++#define __ssi_set_lsb() ( REG_SSI_CR1 |= SSI_CR1_LFST ) ++ ++#define __ssi_set_frame_length(n) \ ++ REG_SSI_CR1 = (REG_SSI_CR1 & ~SSI_CR1_FLEN_MASK) | (((n) - 2) << 4) ++ ++/* n = 1 - 16 */ ++#define __ssi_set_microwire_command_length(n) \ ++ ( REG_SSI_CR1 = ((REG_SSI_CR1 & ~SSI_CR1_MCOM_MASK) | SSI_CR1_MCOM_##n##BIT) ) ++ ++/* Set the clock phase for SPI */ ++#define __ssi_set_spi_clock_phase(n) \ ++ ( REG_SSI_CR1 = ((REG_SSI_CR1 & ~SSI_CR1_PHA) | ((n&0x1) << 1 ))) ++ ++/* Set the clock polarity for SPI */ ++#define __ssi_set_spi_clock_polarity(n) \ ++ ( REG_SSI_CR1 = ((REG_SSI_CR1 & ~SSI_CR1_POL) | ((n&0x1) << 0 ))) ++ ++/* n = 1,4,8,14 */ ++#define __ssi_set_tx_trigger(n) \ ++do { \ ++ REG_SSI_CR1 &= ~SSI_CR1_TTRG_MASK; \ ++ REG_SSI_CR1 |= SSI_CR1_TTRG_##n; \ ++} while (0) ++ ++/* n = 1,4,8,14 */ ++#define __ssi_set_rx_trigger(n) \ ++do { \ ++ REG_SSI_CR1 &= ~SSI_CR1_RTRG_MASK; \ ++ REG_SSI_CR1 |= SSI_CR1_RTRG_##n; \ ++} while (0) ++ ++#define __ssi_get_txfifo_count() \ ++ ( (REG_SSI_SR & SSI_SR_TFIFONUM_MASK) >> SSI_SR_TFIFONUM_BIT ) ++ ++#define __ssi_get_rxfifo_count() \ ++ ( (REG_SSI_SR & SSI_SR_RFIFONUM_MASK) >> SSI_SR_RFIFONUM_BIT ) ++ ++#define __ssi_clear_errors() \ ++ ( REG_SSI_SR &= ~(SSI_SR_UNDR | SSI_SR_OVER) ) ++ ++#define __ssi_transfer_end() ( REG_SSI_SR & SSI_SR_END ) ++#define __ssi_is_busy() ( REG_SSI_SR & SSI_SR_BUSY ) ++ ++#define __ssi_txfifo_full() ( REG_SSI_SR & SSI_SR_TFF ) ++#define __ssi_rxfifo_empty() ( REG_SSI_SR & SSI_SR_RFE ) ++#define __ssi_rxfifo_noempty() ( REG_SSI_SR & SSI_SR_RFHF ) ++#define __ssi_rxfifo_half_full() ( REG_SSI_SR & SSI_SR_RFHF ) ++#define __ssi_txfifo_half_empty() ( REG_SSI_SR & SSI_SR_TFHE ) ++#define __ssi_underrun() ( REG_SSI_SR & SSI_SR_UNDR ) ++#define __ssi_overrun() ( REG_SSI_SR & SSI_SR_OVER ) ++ ++#define __ssi_set_clk(dev_clk, ssi_clk) \ ++ ( REG_SSI_GR = (dev_clk) / (2*(ssi_clk)) - 1 ) ++ ++#define __ssi_receive_data() REG_SSI_DR ++#define __ssi_transmit_data(v) ( REG_SSI_DR = (v) ) ++ ++#endif /* __ASM_JZ4730_OPS_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4730/regs.h linux-2.6.24.7/include/asm-mips/mach-jz4730/regs.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4730/regs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4730/regs.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,2550 @@ ++/* ++ * linux/include/asm-mips/mach-jz4730/regs.h ++ * ++ * JZ4730 registers definition. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * 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 __ASM_JZ4730_REGS_H__ ++#define __ASM_JZ4730_REGS_H__ ++ ++#if defined(__ASSEMBLY__) || defined(__LANGUAGE_ASSEMBLY) ++#define REG8(addr) (addr) ++#define REG16(addr) (addr) ++#define REG32(addr) (addr) ++#else ++#define REG8(addr) *((volatile unsigned char *)(addr)) ++#define REG16(addr) *((volatile unsigned short *)(addr)) ++#define REG32(addr) *((volatile unsigned int *)(addr)) ++#endif ++ ++#define HARB_BASE 0xB3000000 ++#define EMC_BASE 0xB3010000 ++#define DMAC_BASE 0xB3020000 ++#define UHC_BASE 0xB3030000 ++#define UDC_BASE 0xB3040000 ++#define LCD_BASE 0xB3050000 ++#define CIM_BASE 0xB3060000 ++#define ETH_BASE 0xB3100000 ++#define NBM_BASE 0xB3F00000 ++ ++#define CPM_BASE 0xB0000000 ++#define INTC_BASE 0xB0001000 ++#define OST_BASE 0xB0002000 ++#define RTC_BASE 0xB0003000 ++#define WDT_BASE 0xB0004000 ++#define GPIO_BASE 0xB0010000 ++#define AIC_BASE 0xB0020000 ++#define MSC_BASE 0xB0021000 ++#define UART0_BASE 0xB0030000 ++#define UART1_BASE 0xB0031000 ++#define UART2_BASE 0xB0032000 ++#define UART3_BASE 0xB0033000 ++#define FIR_BASE 0xB0040000 ++#define SCC_BASE 0xB0041000 ++#define SCC0_BASE 0xB0041000 ++#define I2C_BASE 0xB0042000 ++#define SSI_BASE 0xB0043000 ++#define SCC1_BASE 0xB0044000 ++#define PWM0_BASE 0xB0050000 ++#define PWM1_BASE 0xB0051000 ++#define DES_BASE 0xB0060000 ++#define UPRT_BASE 0xB0061000 ++#define KBC_BASE 0xB0062000 ++ ++ ++ ++ ++/************************************************************************* ++ * MSC ++ *************************************************************************/ ++#define MSC_STRPCL (MSC_BASE + 0x000) ++#define MSC_STAT (MSC_BASE + 0x004) ++#define MSC_CLKRT (MSC_BASE + 0x008) ++#define MSC_CMDAT (MSC_BASE + 0x00C) ++#define MSC_RESTO (MSC_BASE + 0x010) ++#define MSC_RDTO (MSC_BASE + 0x014) ++#define MSC_BLKLEN (MSC_BASE + 0x018) ++#define MSC_NOB (MSC_BASE + 0x01C) ++#define MSC_SNOB (MSC_BASE + 0x020) ++#define MSC_IMASK (MSC_BASE + 0x024) ++#define MSC_IREG (MSC_BASE + 0x028) ++#define MSC_CMD (MSC_BASE + 0x02C) ++#define MSC_ARG (MSC_BASE + 0x030) ++#define MSC_RES (MSC_BASE + 0x034) ++#define MSC_RXFIFO (MSC_BASE + 0x038) ++#define MSC_TXFIFO (MSC_BASE + 0x03C) ++ ++#define REG_MSC_STRPCL REG16(MSC_STRPCL) ++#define REG_MSC_STAT REG32(MSC_STAT) ++#define REG_MSC_CLKRT REG16(MSC_CLKRT) ++#define REG_MSC_CMDAT REG32(MSC_CMDAT) ++#define REG_MSC_RESTO REG16(MSC_RESTO) ++#define REG_MSC_RDTO REG16(MSC_RDTO) ++#define REG_MSC_BLKLEN REG16(MSC_BLKLEN) ++#define REG_MSC_NOB REG16(MSC_NOB) ++#define REG_MSC_SNOB REG16(MSC_SNOB) ++#define REG_MSC_IMASK REG16(MSC_IMASK) ++#define REG_MSC_IREG REG16(MSC_IREG) ++#define REG_MSC_CMD REG8(MSC_CMD) ++#define REG_MSC_ARG REG32(MSC_ARG) ++#define REG_MSC_RES REG16(MSC_RES) ++#define REG_MSC_RXFIFO REG32(MSC_RXFIFO) ++#define REG_MSC_TXFIFO REG32(MSC_TXFIFO) ++ ++/* MSC Clock and Control Register (MSC_STRPCL) */ ++ ++#define MSC_STRPCL_EXIT_MULTIPLE (1 << 7) ++#define MSC_STRPCL_EXIT_TRANSFER (1 << 6) ++#define MSC_STRPCL_START_READWAIT (1 << 5) ++#define MSC_STRPCL_STOP_READWAIT (1 << 4) ++#define MSC_STRPCL_RESET (1 << 3) ++#define MSC_STRPCL_START_OP (1 << 2) ++#define MSC_STRPCL_CLOCK_CONTROL_BIT 0 ++#define MSC_STRPCL_CLOCK_CONTROL_MASK (0x3 << MSC_STRPCL_CLOCK_CONTROL_BIT) ++ #define MSC_STRPCL_CLOCK_CONTROL_STOP (0x1 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Stop MMC/SD clock */ ++ #define MSC_STRPCL_CLOCK_CONTROL_START (0x2 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Start MMC/SD clock */ ++ ++/* MSC Status Register (MSC_STAT) */ ++ ++#define MSC_STAT_IS_RESETTING (1 << 15) ++#define MSC_STAT_SDIO_INT_ACTIVE (1 << 14) ++#define MSC_STAT_PRG_DONE (1 << 13) ++#define MSC_STAT_DATA_TRAN_DONE (1 << 12) ++#define MSC_STAT_END_CMD_RES (1 << 11) ++#define MSC_STAT_DATA_FIFO_AFULL (1 << 10) ++#define MSC_STAT_IS_READWAIT (1 << 9) ++#define MSC_STAT_CLK_EN (1 << 8) ++#define MSC_STAT_DATA_FIFO_FULL (1 << 7) ++#define MSC_STAT_DATA_FIFO_EMPTY (1 << 6) ++#define MSC_STAT_CRC_RES_ERR (1 << 5) ++#define MSC_STAT_CRC_READ_ERROR (1 << 4) ++#define MSC_STAT_CRC_WRITE_ERROR_BIT 2 ++#define MSC_STAT_CRC_WRITE_ERROR_MASK (0x3 << MSC_STAT_CRC_WRITE_ERROR_BIT) ++ #define MSC_STAT_CRC_WRITE_ERROR_NO (0 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No error on transmission of data */ ++ #define MSC_STAT_CRC_WRITE_ERROR (1 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* Card observed erroneous transmission of data */ ++ #define MSC_STAT_CRC_WRITE_ERROR_NOSTS (2 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No CRC status is sent back */ ++#define MSC_STAT_TIME_OUT_RES (1 << 1) ++#define MSC_STAT_TIME_OUT_READ (1 << 0) ++ ++/* MSC Bus Clock Control Register (MSC_CLKRT) */ ++ ++#define MSC_CLKRT_CLK_RATE_BIT 0 ++#define MSC_CLKRT_CLK_RATE_MASK (0x7 << MSC_CLKRT_CLK_RATE_BIT) ++ #define MSC_CLKRT_CLK_RATE_DIV_1 (0x0 << MSC_CLKRT_CLK_RATE_BIT) /* CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_2 (0x1 << MSC_CLKRT_CLK_RATE_BIT) /* 1/2 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_4 (0x2 << MSC_CLKRT_CLK_RATE_BIT) /* 1/4 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_8 (0x3 << MSC_CLKRT_CLK_RATE_BIT) /* 1/8 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_16 (0x4 << MSC_CLKRT_CLK_RATE_BIT) /* 1/16 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_32 (0x5 << MSC_CLKRT_CLK_RATE_BIT) /* 1/32 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_64 (0x6 << MSC_CLKRT_CLK_RATE_BIT) /* 1/64 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_128 (0x7 << MSC_CLKRT_CLK_RATE_BIT) /* 1/128 of CLK_SRC */ ++ ++/* MSC Command Sequence Control Register (MSC_CMDAT) */ ++ ++#define MSC_CMDAT_IO_ABORT (1 << 11) ++#define MSC_CMDAT_BUS_WIDTH_BIT 9 ++#define MSC_CMDAT_BUS_WIDTH_MASK (0x3 << MSC_CMDAT_BUS_WIDTH_BIT) ++ #define MSC_CMDAT_BUS_WIDTH_1BIT (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) /* 1-bit data bus */ ++ #define MSC_CMDAT_BUS_WIDTH_4BIT (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) /* 4-bit data bus */ ++ #define CMDAT_BUS_WIDTH1 (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) ++ #define CMDAT_BUS_WIDTH4 (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) ++#define MSC_CMDAT_DMA_EN (1 << 8) ++#define MSC_CMDAT_INIT (1 << 7) ++#define MSC_CMDAT_BUSY (1 << 6) ++#define MSC_CMDAT_STREAM_BLOCK (1 << 5) ++#define MSC_CMDAT_WRITE (1 << 4) ++#define MSC_CMDAT_READ (0 << 4) ++#define MSC_CMDAT_DATA_EN (1 << 3) ++#define MSC_CMDAT_RESPONSE_BIT 0 ++#define MSC_CMDAT_RESPONSE_MASK (0x7 << MSC_CMDAT_RESPONSE_BIT) ++ #define MSC_CMDAT_RESPONSE_NONE (0x0 << MSC_CMDAT_RESPONSE_BIT) /* No response */ ++ #define MSC_CMDAT_RESPONSE_R1 (0x1 << MSC_CMDAT_RESPONSE_BIT) /* Format R1 and R1b */ ++ #define MSC_CMDAT_RESPONSE_R2 (0x2 << MSC_CMDAT_RESPONSE_BIT) /* Format R2 */ ++ #define MSC_CMDAT_RESPONSE_R3 (0x3 << MSC_CMDAT_RESPONSE_BIT) /* Format R3 */ ++ #define MSC_CMDAT_RESPONSE_R4 (0x4 << MSC_CMDAT_RESPONSE_BIT) /* Format R4 */ ++ #define MSC_CMDAT_RESPONSE_R5 (0x5 << MSC_CMDAT_RESPONSE_BIT) /* Format R5 */ ++ #define MSC_CMDAT_RESPONSE_R6 (0x6 << MSC_CMDAT_RESPONSE_BIT) /* Format R6 */ ++ ++#define CMDAT_DMA_EN (1 << 8) ++#define CMDAT_INIT (1 << 7) ++#define CMDAT_BUSY (1 << 6) ++#define CMDAT_STREAM (1 << 5) ++#define CMDAT_WRITE (1 << 4) ++#define CMDAT_DATA_EN (1 << 3) ++ ++/* MSC Interrupts Mask Register (MSC_IMASK) */ ++ ++#define MSC_IMASK_SDIO (1 << 7) ++#define MSC_IMASK_TXFIFO_WR_REQ (1 << 6) ++#define MSC_IMASK_RXFIFO_RD_REQ (1 << 5) ++#define MSC_IMASK_END_CMD_RES (1 << 2) ++#define MSC_IMASK_PRG_DONE (1 << 1) ++#define MSC_IMASK_DATA_TRAN_DONE (1 << 0) ++ ++ ++/* MSC Interrupts Status Register (MSC_IREG) */ ++ ++#define MSC_IREG_SDIO (1 << 7) ++#define MSC_IREG_TXFIFO_WR_REQ (1 << 6) ++#define MSC_IREG_RXFIFO_RD_REQ (1 << 5) ++#define MSC_IREG_END_CMD_RES (1 << 2) ++#define MSC_IREG_PRG_DONE (1 << 1) ++#define MSC_IREG_DATA_TRAN_DONE (1 << 0) ++ ++ ++ ++ ++/************************************************************************* ++ * RTC ++ *************************************************************************/ ++#define RTC_RCR (RTC_BASE + 0x00) ++#define RTC_RSR (RTC_BASE + 0x04) ++#define RTC_RSAR (RTC_BASE + 0x08) ++#define RTC_RGR (RTC_BASE + 0x0c) ++ ++#define REG_RTC_RCR REG32(RTC_RCR) ++#define REG_RTC_RSR REG32(RTC_RSR) ++#define REG_RTC_RSAR REG32(RTC_RSAR) ++#define REG_RTC_RGR REG32(RTC_RGR) ++ ++#define RTC_RCR_HZ (1 << 6) ++#define RTC_RCR_HZIE (1 << 5) ++#define RTC_RCR_AF (1 << 4) ++#define RTC_RCR_AIE (1 << 3) ++#define RTC_RCR_AE (1 << 2) ++#define RTC_RCR_START (1 << 0) ++ ++#define RTC_RGR_LOCK (1 << 31) ++#define RTC_RGR_ADJ_BIT 16 ++#define RTC_RGR_ADJ_MASK (0x3ff << RTC_RGR_ADJ_BIT) ++#define RTC_RGR_DIV_BIT 0 ++#define RTC_REG_DIV_MASK (0xff << RTC_RGR_DIV_BIT) ++ ++ ++ ++ ++/************************************************************************* ++ * FIR ++ *************************************************************************/ ++#define FIR_TDR (FIR_BASE + 0x000) ++#define FIR_RDR (FIR_BASE + 0x004) ++#define FIR_TFLR (FIR_BASE + 0x008) ++#define FIR_AR (FIR_BASE + 0x00C) ++#define FIR_CR1 (FIR_BASE + 0x010) ++#define FIR_CR2 (FIR_BASE + 0x014) ++#define FIR_SR (FIR_BASE + 0x018) ++ ++#define REG_FIR_TDR REG8(FIR_TDR) ++#define REG_FIR_RDR REG8(FIR_RDR) ++#define REG_FIR_TFLR REG16(FIR_TFLR) ++#define REG_FIR_AR REG8(FIR_AR) ++#define REG_FIR_CR1 REG8(FIR_CR1) ++#define REG_FIR_CR2 REG16(FIR_CR2) ++#define REG_FIR_SR REG16(FIR_SR) ++ ++/* FIR Control Register 1 (FIR_CR1) */ ++ ++#define FIR_CR1_FIRUE (1 << 7) ++#define FIR_CR1_ACE (1 << 6) ++#define FIR_CR1_EOUS (1 << 5) ++#define FIR_CR1_TIIE (1 << 4) ++#define FIR_CR1_TFIE (1 << 3) ++#define FIR_CR1_RFIE (1 << 2) ++#define FIR_CR1_TXE (1 << 1) ++#define FIR_CR1_RXE (1 << 0) ++ ++/* FIR Control Register 2 (FIR_CR2) */ ++ ++#define FIR_CR2_SIPE (1 << 10) ++#define FIR_CR2_BCRC (1 << 9) ++#define FIR_CR2_TFLRS (1 << 8) ++#define FIR_CR2_ISS (1 << 7) ++#define FIR_CR2_LMS (1 << 6) ++#define FIR_CR2_TPPS (1 << 5) ++#define FIR_CR2_RPPS (1 << 4) ++#define FIR_CR2_TTRG_BIT 2 ++#define FIR_CR2_TTRG_MASK (0x3 << FIR_CR2_TTRG_BIT) ++ #define FIR_CR2_TTRG_16 (0 << FIR_CR2_TTRG_BIT) /* Transmit Trigger Level is 16 */ ++ #define FIR_CR2_TTRG_32 (1 << FIR_CR2_TTRG_BIT) /* Transmit Trigger Level is 32 */ ++ #define FIR_CR2_TTRG_64 (2 << FIR_CR2_TTRG_BIT) /* Transmit Trigger Level is 64 */ ++ #define FIR_CR2_TTRG_128 (3 << FIR_CR2_TTRG_BIT) /* Transmit Trigger Level is 128 */ ++#define FIR_CR2_RTRG_BIT 0 ++#define FIR_CR2_RTRG_MASK (0x3 << FIR_CR2_RTRG_BIT) ++ #define FIR_CR2_RTRG_16 (0 << FIR_CR2_RTRG_BIT) /* Receive Trigger Level is 16 */ ++ #define FIR_CR2_RTRG_32 (1 << FIR_CR2_RTRG_BIT) /* Receive Trigger Level is 32 */ ++ #define FIR_CR2_RTRG_64 (2 << FIR_CR2_RTRG_BIT) /* Receive Trigger Level is 64 */ ++ #define FIR_CR2_RTRG_128 (3 << FIR_CR2_RTRG_BIT) /* Receive Trigger Level is 128 */ ++ ++/* FIR Status Register (FIR_SR) */ ++ ++#define FIR_SR_RFW (1 << 12) ++#define FIR_SR_RFA (1 << 11) ++#define FIR_SR_TFRTL (1 << 10) ++#define FIR_SR_RFRTL (1 << 9) ++#define FIR_SR_URUN (1 << 8) ++#define FIR_SR_RFTE (1 << 7) ++#define FIR_SR_ORUN (1 << 6) ++#define FIR_SR_CRCE (1 << 5) ++#define FIR_SR_FEND (1 << 4) ++#define FIR_SR_TFF (1 << 3) ++#define FIR_SR_RFE (1 << 2) ++#define FIR_SR_TIDLE (1 << 1) ++#define FIR_SR_RB (1 << 0) ++ ++ ++ ++ ++/************************************************************************* ++ * SCC ++ *************************************************************************/ ++#define SCC_DR(base) ((base) + 0x000) ++#define SCC_FDR(base) ((base) + 0x004) ++#define SCC_CR(base) ((base) + 0x008) ++#define SCC1_CR(base) ((base) + 0x008) ++#define SCC_SR(base) ((base) + 0x00C) ++#define SCC_TFR(base) ((base) + 0x010) ++#define SCC_EGTR(base) ((base) + 0x014) ++#define SCC_ECR(base) ((base) + 0x018) ++#define SCC_RTOR(base) ((base) + 0x01C) ++ ++#define REG_SCC_DR(base) REG8(SCC_DR(base)) ++#define REG_SCC_FDR(base) REG8(SCC_FDR(base)) ++#define REG_SCC_CR(base) REG32(SCC_CR(base)) ++#define REG_SCC1_CR(base) REG32(SCC1_CR(base)) ++#define REG_SCC_SR(base) REG16(SCC_SR(base)) ++#define REG_SCC_TFR(base) REG16(SCC_TFR(base)) ++#define REG_SCC_EGTR(base) REG8(SCC_EGTR(base)) ++#define REG_SCC_ECR(base) REG32(SCC_ECR(base)) ++#define REG_SCC_RTOR(base) REG8(SCC_RTOR(base)) ++ ++/* SCC FIFO Data Count Register (SCC_FDR) */ ++ ++#define SCC_FDR_EMPTY 0x00 ++#define SCC_FDR_FULL 0x10 ++ ++/* SCC Control Register (SCC_CR) */ ++ ++#define SCC_CR_SCCE (1 << 31) ++#define SCC_CR_TRS (1 << 30) ++#define SCC_CR_T2R (1 << 29) ++#define SCC_CR_FDIV_BIT 24 ++#define SCC_CR_FDIV_MASK (0x3 << SCC_CR_FDIV_BIT) ++ #define SCC_CR_FDIV_1 (0 << SCC_CR_FDIV_BIT) /* SCC_CLK frequency is the same as device clock */ ++ #define SCC_CR_FDIV_2 (1 << SCC_CR_FDIV_BIT) /* SCC_CLK frequency is half of device clock */ ++#define SCC_CR_FLUSH (1 << 23) ++#define SCC_CR_TRIG_BIT 16 ++#define SCC_CR_TRIG_MASK (0x3 << SCC_CR_TRIG_BIT) ++ #define SCC_CR_TRIG_1 (0 << SCC_CR_TRIG_BIT) /* Receive/Transmit-FIFO Trigger is 1 */ ++ #define SCC_CR_TRIG_4 (1 << SCC_CR_TRIG_BIT) /* Receive/Transmit-FIFO Trigger is 4 */ ++ #define SCC_CR_TRIG_8 (2 << SCC_CR_TRIG_BIT) /* Receive/Transmit-FIFO Trigger is 8 */ ++ #define SCC_CR_TRIG_14 (3 << SCC_CR_TRIG_BIT) /* Receive/Transmit-FIFO Trigger is 14 */ ++#define SCC_CR_TP (1 << 15) ++#define SCC_CR_CONV (1 << 14) ++#define SCC_CR_TXIE (1 << 13) ++#define SCC_CR_RXIE (1 << 12) ++#define SCC_CR_TENDIE (1 << 11) ++#define SCC_CR_RTOIE (1 << 10) ++#define SCC_CR_ECIE (1 << 9) ++#define SCC_CR_EPIE (1 << 8) ++#define SCC_CR_RETIE (1 << 7) ++#define SCC_CR_EOIE (1 << 6) ++#define SCC_CR_TSEND (1 << 3) ++#define SCC_CR_PX_BIT 1 ++#define SCC_CR_PX_MASK (0x3 << SCC_CR_PX_BIT) ++ #define SCC_CR_PX_NOT_SUPPORT (0 << SCC_CR_PX_BIT) /* SCC does not support clock stop */ ++ #define SCC_CR_PX_STOP_LOW (1 << SCC_CR_PX_BIT) /* SCC_CLK stops at state low */ ++ #define SCC_CR_PX_STOP_HIGH (2 << SCC_CR_PX_BIT) /* SCC_CLK stops at state high */ ++#define SCC_CR_CLKSTP (1 << 0) ++ ++/* SCC Status Register (SCC_SR) */ ++ ++#define SCC_SR_TRANS (1 << 15) ++#define SCC_SR_ORER (1 << 12) ++#define SCC_SR_RTO (1 << 11) ++#define SCC_SR_PER (1 << 10) ++#define SCC_SR_TFTG (1 << 9) ++#define SCC_SR_RFTG (1 << 8) ++#define SCC_SR_TEND (1 << 7) ++#define SCC_SR_RETR_3 (1 << 4) ++#define SCC_SR_ECNTO (1 << 0) ++ ++ ++ ++ ++/************************************************************************* ++ * ETH ++ *************************************************************************/ ++#define ETH_BMR (ETH_BASE + 0x1000) ++#define ETH_TPDR (ETH_BASE + 0x1004) ++#define ETH_RPDR (ETH_BASE + 0x1008) ++#define ETH_RAR (ETH_BASE + 0x100C) ++#define ETH_TAR (ETH_BASE + 0x1010) ++#define ETH_SR (ETH_BASE + 0x1014) ++#define ETH_CR (ETH_BASE + 0x1018) ++#define ETH_IER (ETH_BASE + 0x101C) ++#define ETH_MFCR (ETH_BASE + 0x1020) ++#define ETH_CTAR (ETH_BASE + 0x1050) ++#define ETH_CRAR (ETH_BASE + 0x1054) ++#define ETH_MCR (ETH_BASE + 0x0000) ++#define ETH_MAHR (ETH_BASE + 0x0004) ++#define ETH_MALR (ETH_BASE + 0x0008) ++#define ETH_HTHR (ETH_BASE + 0x000C) ++#define ETH_HTLR (ETH_BASE + 0x0010) ++#define ETH_MIAR (ETH_BASE + 0x0014) ++#define ETH_MIDR (ETH_BASE + 0x0018) ++#define ETH_FCR (ETH_BASE + 0x001C) ++#define ETH_VTR1 (ETH_BASE + 0x0020) ++#define ETH_VTR2 (ETH_BASE + 0x0024) ++#define ETH_WKFR (ETH_BASE + 0x0028) ++#define ETH_PMTR (ETH_BASE + 0x002C) ++ ++#define REG_ETH_BMR REG32(ETH_BMR) ++#define REG_ETH_TPDR REG32(ETH_TPDR) ++#define REG_ETH_RPDR REG32(ETH_RPDR) ++#define REG_ETH_RAR REG32(ETH_RAR) ++#define REG_ETH_TAR REG32(ETH_TAR) ++#define REG_ETH_SR REG32(ETH_SR) ++#define REG_ETH_CR REG32(ETH_CR) ++#define REG_ETH_IER REG32(ETH_IER) ++#define REG_ETH_MFCR REG32(ETH_MFCR) ++#define REG_ETH_CTAR REG32(ETH_CTAR) ++#define REG_ETH_CRAR REG32(ETH_CRAR) ++#define REG_ETH_MCR REG32(ETH_MCR) ++#define REG_ETH_MAHR REG32(ETH_MAHR) ++#define REG_ETH_MALR REG32(ETH_MALR) ++#define REG_ETH_HTHR REG32(ETH_HTHR) ++#define REG_ETH_HTLR REG32(ETH_HTLR) ++#define REG_ETH_MIAR REG32(ETH_MIAR) ++#define REG_ETH_MIDR REG32(ETH_MIDR) ++#define REG_ETH_FCR REG32(ETH_FCR) ++#define REG_ETH_VTR1 REG32(ETH_VTR1) ++#define REG_ETH_VTR2 REG32(ETH_VTR2) ++#define REG_ETH_WKFR REG32(ETH_WKFR) ++#define REG_ETH_PMTR REG32(ETH_PMTR) ++ ++/* Bus Mode Register (ETH_BMR) */ ++ ++#define ETH_BMR_DBO (1 << 20) ++#define ETH_BMR_PBL_BIT 8 ++#define ETH_BMR_PBL_MASK (0x3f << ETH_BMR_PBL_BIT) ++ #define ETH_BMR_PBL_1 (0x1 << ETH_BMR_PBL_BIT) ++ #define ETH_BMR_PBL_4 (0x4 << ETH_BMR_PBL_BIT) ++#define ETH_BMR_BLE (1 << 7) ++#define ETH_BMR_DSL_BIT 2 ++#define ETH_BMR_DSL_MASK (0x1f << ETH_BMR_DSL_BIT) ++ #define ETH_BMR_DSL_0 (0x0 << ETH_BMR_DSL_BIT) ++ #define ETH_BMR_DSL_1 (0x1 << ETH_BMR_DSL_BIT) ++ #define ETH_BMR_DSL_2 (0x2 << ETH_BMR_DSL_BIT) ++ #define ETH_BMR_DSL_4 (0x4 << ETH_BMR_DSL_BIT) ++ #define ETH_BMR_DSL_8 (0x8 << ETH_BMR_DSL_BIT) ++#define ETH_BMR_SWR (1 << 0) ++ ++/* DMA Status Register (ETH_SR) */ ++ ++#define ETH_SR_EB_BIT 23 ++#define ETH_SR_EB_MASK (0x7 << ETH_SR_EB_BIT) ++ #define ETH_SR_EB_TX_ABORT (0x1 << ETH_SR_EB_BIT) ++ #define ETH_SR_EB_RX_ABORT (0x2 << ETH_SR_EB_BIT) ++#define ETH_SR_TS_BIT 20 ++#define ETH_SR_TS_MASK (0x7 << ETH_SR_TS_BIT) ++ #define ETH_SR_TS_STOP (0x0 << ETH_SR_TS_BIT) ++ #define ETH_SR_TS_FTD (0x1 << ETH_SR_TS_BIT) ++ #define ETH_SR_TS_WEOT (0x2 << ETH_SR_TS_BIT) ++ #define ETH_SR_TS_QDAT (0x3 << ETH_SR_TS_BIT) ++ #define ETH_SR_TS_SUSPEND (0x6 << ETH_SR_TS_BIT) ++ #define ETH_SR_TS_CTD (0x7 << ETH_SR_TS_BIT) ++#define ETH_SR_RS_BIT 17 ++#define ETH_SR_RS_MASK (0x7 << ETH_SR_RS_BIT) ++ #define ETH_SR_RS_STOP (0x0 << ETH_SR_RS_BIT) ++ #define ETH_SR_RS_FRD (0x1 << ETH_SR_RS_BIT) ++ #define ETH_SR_RS_CEOR (0x2 << ETH_SR_RS_BIT) ++ #define ETH_SR_RS_WRP (0x3 << ETH_SR_RS_BIT) ++ #define ETH_SR_RS_SUSPEND (0x4 << ETH_SR_RS_BIT) ++ #define ETH_SR_RS_CRD (0x5 << ETH_SR_RS_BIT) ++ #define ETH_SR_RS_FCF (0x6 << ETH_SR_RS_BIT) ++ #define ETH_SR_RS_QRF (0x7 << ETH_SR_RS_BIT) ++#define ETH_SR_NIS (1 << 16) ++#define ETH_SR_AIS (1 << 15) ++#define ETH_SR_ERI (1 << 14) ++#define ETH_SR_FBE (1 << 13) ++#define ETH_SR_ETI (1 << 10) ++#define ETH_SR_RWT (1 << 9) ++#define ETH_SR_RPS (1 << 8) ++#define ETH_SR_RU (1 << 7) ++#define ETH_SR_RI (1 << 6) ++#define ETH_SR_UNF (1 << 5) ++#define ETH_SR_TJT (1 << 3) ++#define ETH_SR_TU (1 << 2) ++#define ETH_SR_TPS (1 << 1) ++#define ETH_SR_TI (1 << 0) ++ ++/* Control (Operation Mode) Register (ETH_CR) */ ++ ++#define ETH_CR_TTM (1 << 22) ++#define ETH_CR_SF (1 << 21) ++#define ETH_CR_TR_BIT 14 ++#define ETH_CR_TR_MASK (0x3 << ETH_CR_TR_BIT) ++#define ETH_CR_ST (1 << 13) ++#define ETH_CR_OSF (1 << 2) ++#define ETH_CR_SR (1 << 1) ++ ++/* Interrupt Enable Register (ETH_IER) */ ++ ++#define ETH_IER_NI (1 << 16) ++#define ETH_IER_AI (1 << 15) ++#define ETH_IER_ERE (1 << 14) ++#define ETH_IER_FBE (1 << 13) ++#define ETH_IER_ET (1 << 10) ++#define ETH_IER_RWE (1 << 9) ++#define ETH_IER_RS (1 << 8) ++#define ETH_IER_RU (1 << 7) ++#define ETH_IER_RI (1 << 6) ++#define ETH_IER_UN (1 << 5) ++#define ETH_IER_TJ (1 << 3) ++#define ETH_IER_TU (1 << 2) ++#define ETH_IER_TS (1 << 1) ++#define ETH_IER_TI (1 << 0) ++ ++/* Missed Frame and Buffer Overflow Counter Register (ETH_MFCR) */ ++ ++#define ETH_MFCR_OVERFLOW_BIT 17 ++#define ETH_MFCR_OVERFLOW_MASK (0x7ff << ETH_MFCR_OVERFLOW_BIT) ++#define ETH_MFCR_MFC_BIT 0 ++#define ETH_MFCR_MFC_MASK (0xffff << ETH_MFCR_MFC_BIT) ++ ++/* MAC Control Register (ETH_MCR) */ ++ ++#define ETH_MCR_RA (1 << 31) ++#define ETH_MCR_HBD (1 << 28) ++#define ETH_MCR_PS (1 << 27) ++#define ETH_MCR_DRO (1 << 23) ++#define ETH_MCR_OM_BIT 21 ++#define ETH_MCR_OM_MASK (0x3 << ETH_MCR_OM_BIT) ++ #define ETH_MCR_OM_NORMAL (0x0 << ETH_MCR_OM_BIT) ++ #define ETH_MCR_OM_INTERNAL (0x1 << ETH_MCR_OM_BIT) ++ #define ETH_MCR_OM_EXTERNAL (0x2 << ETH_MCR_OM_BIT) ++#define ETH_MCR_F (1 << 20) ++#define ETH_MCR_PM (1 << 19) ++#define ETH_MCR_PR (1 << 18) ++#define ETH_MCR_IF (1 << 17) ++#define ETH_MCR_PB (1 << 16) ++#define ETH_MCR_HO (1 << 15) ++#define ETH_MCR_HP (1 << 13) ++#define ETH_MCR_LCC (1 << 12) ++#define ETH_MCR_DBF (1 << 11) ++#define ETH_MCR_DTRY (1 << 10) ++#define ETH_MCR_ASTP (1 << 8) ++#define ETH_MCR_BOLMT_BIT 6 ++#define ETH_MCR_BOLMT_MASK (0x3 << ETH_MCR_BOLMT_BIT) ++ #define ETH_MCR_BOLMT_10 (0 << ETH_MCR_BOLMT_BIT) ++ #define ETH_MCR_BOLMT_8 (1 << ETH_MCR_BOLMT_BIT) ++ #define ETH_MCR_BOLMT_4 (2 << ETH_MCR_BOLMT_BIT) ++ #define ETH_MCR_BOLMT_1 (3 << ETH_MCR_BOLMT_BIT) ++#define ETH_MCR_DC (1 << 5) ++#define ETH_MCR_TE (1 << 3) ++#define ETH_MCR_RE (1 << 2) ++ ++/* MII Address Register (ETH_MIAR) */ ++ ++#define ETH_MIAR_PHY_ADDR_BIT 11 ++#define ETH_MIAR_PHY_ADDR_MASK (0x1f << ETH_MIAR_PHY_ADDR_BIT) ++#define ETH_MIAR_MII_REG_BIT 6 ++#define ETH_MIAR_MII_REG_MASK (0x1f << ETH_MIAR_MII_REG_BIT) ++#define ETH_MIAR_MII_WRITE (1 << 1) ++#define ETH_MIAR_MII_BUSY (1 << 0) ++ ++/* Flow Control Register (ETH_FCR) */ ++ ++#define ETH_FCR_PAUSE_TIME_BIT 16 ++#define ETH_FCR_PAUSE_TIME_MASK (0xffff << ETH_FCR_PAUSE_TIME_BIT) ++#define ETH_FCR_PCF (1 << 2) ++#define ETH_FCR_FCE (1 << 1) ++#define ETH_FCR_BUSY (1 << 0) ++ ++/* PMT Control and Status Register (ETH_PMTR) */ ++ ++#define ETH_PMTR_GU (1 << 9) ++#define ETH_PMTR_RF (1 << 6) ++#define ETH_PMTR_MF (1 << 5) ++#define ETH_PMTR_RWK (1 << 2) ++#define ETH_PMTR_MPK (1 << 1) ++ ++/* Receive Descriptor 0 (ETH_RD0) Bits */ ++ ++#define ETH_RD0_OWN (1 << 31) ++#define ETH_RD0_FF (1 << 30) ++#define ETH_RD0_FL_BIT 16 ++#define ETH_RD0_FL_MASK (0x3fff << ETH_RD0_FL_BIT) ++#define ETH_RD0_ES (1 << 15) ++#define ETH_RD0_DE (1 << 14) ++#define ETH_RD0_LE (1 << 12) ++#define ETH_RD0_RF (1 << 11) ++#define ETH_RD0_MF (1 << 10) ++#define ETH_RD0_FD (1 << 9) ++#define ETH_RD0_LD (1 << 8) ++#define ETH_RD0_TL (1 << 7) ++#define ETH_RD0_CS (1 << 6) ++#define ETH_RD0_FT (1 << 5) ++#define ETH_RD0_WT (1 << 4) ++#define ETH_RD0_ME (1 << 3) ++#define ETH_RD0_DB (1 << 2) ++#define ETH_RD0_CE (1 << 1) ++ ++/* Receive Descriptor 1 (ETH_RD1) Bits */ ++ ++#define ETH_RD1_RER (1 << 25) ++#define ETH_RD1_RCH (1 << 24) ++#define ETH_RD1_RBS2_BIT 11 ++#define ETH_RD1_RBS2_MASK (0x7ff << ETH_RD1_RBS2_BIT) ++#define ETH_RD1_RBS1_BIT 0 ++#define ETH_RD1_RBS1_MASK (0x7ff << ETH_RD1_RBS1_BIT) ++ ++/* Transmit Descriptor 0 (ETH_TD0) Bits */ ++ ++#define ETH_TD0_OWN (1 << 31) ++#define ETH_TD0_FA (1 << 15) ++#define ETH_TD0_LOC (1 << 11) ++#define ETH_TD0_NC (1 << 10) ++#define ETH_TD0_LC (1 << 9) ++#define ETH_TD0_EC (1 << 8) ++#define ETH_TD0_HBF (1 << 7) ++#define ETH_TD0_CC_BIT 3 ++#define ETH_TD0_CC_MASK (0xf << ETH_TD0_CC_BIT) ++#define ETH_TD0_ED (1 << 2) ++#define ETH_TD0_UF (1 << 1) ++#define ETH_TD0_DF (1 << 0) ++ ++/* Transmit Descriptor 1 (ETH_TD1) Bits */ ++ ++#define ETH_TD1_IC (1 << 31) ++#define ETH_TD1_LS (1 << 30) ++#define ETH_TD1_FS (1 << 29) ++#define ETH_TD1_AC (1 << 26) ++#define ETH_TD1_TER (1 << 25) ++#define ETH_TD1_TCH (1 << 24) ++#define ETH_TD1_DPD (1 << 23) ++#define ETH_TD1_TBS2_BIT 11 ++#define ETH_TD1_TBS2_MASK (0x7ff << ETH_TD1_TBS2_BIT) ++#define ETH_TD1_TBS1_BIT 0 ++#define ETH_TD1_TBS1_MASK (0x7ff << ETH_TD1_TBS1_BIT) ++ ++ ++ ++ ++/************************************************************************* ++ * WDT ++ *************************************************************************/ ++#define WDT_WTCSR (WDT_BASE + 0x00) ++#define WDT_WTCNT (WDT_BASE + 0x04) ++ ++#define REG_WDT_WTCSR REG8(WDT_WTCSR) ++#define REG_WDT_WTCNT REG32(WDT_WTCNT) ++ ++#define WDT_WTCSR_START (1 << 4) ++ ++ ++ ++ ++/************************************************************************* ++ * OST ++ *************************************************************************/ ++#define OST_TER (OST_BASE + 0x00) ++#define OST_TRDR(n) (OST_BASE + 0x10 + ((n) * 0x20)) ++#define OST_TCNT(n) (OST_BASE + 0x14 + ((n) * 0x20)) ++#define OST_TCSR(n) (OST_BASE + 0x18 + ((n) * 0x20)) ++#define OST_TCRB(n) (OST_BASE + 0x1c + ((n) * 0x20)) ++ ++#define REG_OST_TER REG8(OST_TER) ++#define REG_OST_TRDR(n) REG32(OST_TRDR((n))) ++#define REG_OST_TCNT(n) REG32(OST_TCNT((n))) ++#define REG_OST_TCSR(n) REG16(OST_TCSR((n))) ++#define REG_OST_TCRB(n) REG32(OST_TCRB((n))) ++ ++#define OST_TCSR_BUSY (1 << 7) ++#define OST_TCSR_UF (1 << 6) ++#define OST_TCSR_UIE (1 << 5) ++#define OST_TCSR_CKS_BIT 0 ++#define OST_TCSR_CKS_MASK (0x07 << OST_TCSR_CKS_BIT) ++ #define OST_TCSR_CKS_PCLK_4 (0 << OST_TCSR_CKS_BIT) ++ #define OST_TCSR_CKS_PCLK_16 (1 << OST_TCSR_CKS_BIT) ++ #define OST_TCSR_CKS_PCLK_64 (2 << OST_TCSR_CKS_BIT) ++ #define OST_TCSR_CKS_PCLK_256 (3 << OST_TCSR_CKS_BIT) ++ #define OST_TCSR_CKS_RTCCLK (4 << OST_TCSR_CKS_BIT) ++ #define OST_TCSR_CKS_EXTAL (5 << OST_TCSR_CKS_BIT) ++ ++#define OST_TCSR0 OST_TCSR(0) ++#define OST_TCSR1 OST_TCSR(1) ++#define OST_TCSR2 OST_TCSR(2) ++#define OST_TRDR0 OST_TRDR(0) ++#define OST_TRDR1 OST_TRDR(1) ++#define OST_TRDR2 OST_TRDR(2) ++#define OST_TCNT0 OST_TCNT(0) ++#define OST_TCNT1 OST_TCNT(1) ++#define OST_TCNT2 OST_TCNT(2) ++#define OST_TCRB0 OST_TCRB(0) ++#define OST_TCRB1 OST_TCRB(1) ++#define OST_TCRB2 OST_TCRB(2) ++ ++/************************************************************************* ++ * UART ++ *************************************************************************/ ++ ++#define IRDA_BASE UART0_BASE ++#define UART_BASE UART0_BASE ++#define UART_OFF 0x1000 ++ ++/* register offset */ ++#define OFF_RDR (0x00) /* R 8b H'xx */ ++#define OFF_TDR (0x00) /* W 8b H'xx */ ++#define OFF_DLLR (0x00) /* RW 8b H'00 */ ++#define OFF_DLHR (0x04) /* RW 8b H'00 */ ++#define OFF_IER (0x04) /* RW 8b H'00 */ ++#define OFF_ISR (0x08) /* R 8b H'01 */ ++#define OFF_FCR (0x08) /* W 8b H'00 */ ++#define OFF_LCR (0x0C) /* RW 8b H'00 */ ++#define OFF_MCR (0x10) /* RW 8b H'00 */ ++#define OFF_LSR (0x14) /* R 8b H'00 */ ++#define OFF_MSR (0x18) /* R 8b H'00 */ ++#define OFF_SPR (0x1C) /* RW 8b H'00 */ ++#define OFF_MCR (0x10) /* RW 8b H'00 */ ++#define OFF_SIRCR (0x20) /* RW 8b H'00, UART0 */ ++ ++/* register address */ ++#define UART0_RDR (UART0_BASE + OFF_RDR) ++#define UART0_TDR (UART0_BASE + OFF_TDR) ++#define UART0_DLLR (UART0_BASE + OFF_DLLR) ++#define UART0_DLHR (UART0_BASE + OFF_DLHR) ++#define UART0_IER (UART0_BASE + OFF_IER) ++#define UART0_ISR (UART0_BASE + OFF_ISR) ++#define UART0_FCR (UART0_BASE + OFF_FCR) ++#define UART0_LCR (UART0_BASE + OFF_LCR) ++#define UART0_MCR (UART0_BASE + OFF_MCR) ++#define UART0_LSR (UART0_BASE + OFF_LSR) ++#define UART0_MSR (UART0_BASE + OFF_MSR) ++#define UART0_SPR (UART0_BASE + OFF_SPR) ++#define UART0_SIRCR (UART0_BASE + OFF_SIRCR) ++ ++#define UART1_RDR (UART1_BASE + OFF_RDR) ++#define UART1_TDR (UART1_BASE + OFF_TDR) ++#define UART1_DLLR (UART1_BASE + OFF_DLLR) ++#define UART1_DLHR (UART1_BASE + OFF_DLHR) ++#define UART1_IER (UART1_BASE + OFF_IER) ++#define UART1_ISR (UART1_BASE + OFF_ISR) ++#define UART1_FCR (UART1_BASE + OFF_FCR) ++#define UART1_LCR (UART1_BASE + OFF_LCR) ++#define UART1_MCR (UART1_BASE + OFF_MCR) ++#define UART1_LSR (UART1_BASE + OFF_LSR) ++#define UART1_MSR (UART1_BASE + OFF_MSR) ++#define UART1_SPR (UART1_BASE + OFF_SPR) ++#define UART1_SIRCR (UART1_BASE + OFF_SIRCR) ++ ++#define UART2_RDR (UART2_BASE + OFF_RDR) ++#define UART2_TDR (UART2_BASE + OFF_TDR) ++#define UART2_DLLR (UART2_BASE + OFF_DLLR) ++#define UART2_DLHR (UART2_BASE + OFF_DLHR) ++#define UART2_IER (UART2_BASE + OFF_IER) ++#define UART2_ISR (UART2_BASE + OFF_ISR) ++#define UART2_FCR (UART2_BASE + OFF_FCR) ++#define UART2_LCR (UART2_BASE + OFF_LCR) ++#define UART2_MCR (UART2_BASE + OFF_MCR) ++#define UART2_LSR (UART2_BASE + OFF_LSR) ++#define UART2_MSR (UART2_BASE + OFF_MSR) ++#define UART2_SPR (UART2_BASE + OFF_SPR) ++#define UART2_SIRCR (UART2_BASE + OFF_SIRCR) ++ ++#define UART3_RDR (UART3_BASE + OFF_RDR) ++#define UART3_TDR (UART3_BASE + OFF_TDR) ++#define UART3_DLLR (UART3_BASE + OFF_DLLR) ++#define UART3_DLHR (UART3_BASE + OFF_DLHR) ++#define UART3_IER (UART3_BASE + OFF_IER) ++#define UART3_ISR (UART3_BASE + OFF_ISR) ++#define UART3_FCR (UART3_BASE + OFF_FCR) ++#define UART3_LCR (UART3_BASE + OFF_LCR) ++#define UART3_MCR (UART3_BASE + OFF_MCR) ++#define UART3_LSR (UART3_BASE + OFF_LSR) ++#define UART3_MSR (UART3_BASE + OFF_MSR) ++#define UART3_SPR (UART3_BASE + OFF_SPR) ++#define UART3_SIRCR (UART3_BASE + OFF_SIRCR) ++ ++/* ++ * Define macros for UARTIER ++ * UART Interrupt Enable Register ++ */ ++#define UARTIER_RIE (1 << 0) /* 0: receive fifo "full" interrupt disable */ ++#define UARTIER_TIE (1 << 1) /* 0: transmit fifo "empty" interrupt disable */ ++#define UARTIER_RLIE (1 << 2) /* 0: receive line status interrupt disable */ ++#define UARTIER_MIE (1 << 3) /* 0: modem status interrupt disable */ ++#define UARTIER_RTIE (1 << 4) /* 0: receive timeout interrupt disable */ ++ ++/* ++ * Define macros for UARTISR ++ * UART Interrupt Status Register ++ */ ++#define UARTISR_IP (1 << 0) /* 0: interrupt is pending 1: no interrupt */ ++#define UARTISR_IID (7 << 1) /* Source of Interrupt */ ++#define UARTISR_IID_MSI (0 << 1) /* Modem status interrupt */ ++#define UARTISR_IID_THRI (1 << 1) /* Transmitter holding register empty */ ++#define UARTISR_IID_RDI (2 << 1) /* Receiver data interrupt */ ++#define UARTISR_IID_RLSI (3 << 1) /* Receiver line status interrupt */ ++#define UARTISR_FFMS (3 << 6) /* FIFO mode select, set when UARTFCR.FE is set to 1 */ ++#define UARTISR_FFMS_NO_FIFO (0 << 6) ++#define UARTISR_FFMS_FIFO_MODE (3 << 6) ++ ++/* ++ * Define macros for UARTFCR ++ * UART FIFO Control Register ++ */ ++#define UARTFCR_FE (1 << 0) /* 0: non-FIFO mode 1: FIFO mode */ ++#define UARTFCR_RFLS (1 << 1) /* write 1 to flush receive FIFO */ ++#define UARTFCR_TFLS (1 << 2) /* write 1 to flush transmit FIFO */ ++#define UARTFCR_DMS (1 << 3) /* 0: disable DMA mode */ ++#define UARTFCR_UUE (1 << 4) /* 0: disable UART */ ++#define UARTFCR_RTRG (3 << 6) /* Receive FIFO Data Trigger */ ++#define UARTFCR_RTRG_1 (0 << 6) ++#define UARTFCR_RTRG_4 (1 << 6) ++#define UARTFCR_RTRG_8 (2 << 6) ++#define UARTFCR_RTRG_15 (3 << 6) ++ ++/* ++ * Define macros for UARTLCR ++ * UART Line Control Register ++ */ ++#define UARTLCR_WLEN (3 << 0) /* word length */ ++#define UARTLCR_WLEN_5 (0 << 0) ++#define UARTLCR_WLEN_6 (1 << 0) ++#define UARTLCR_WLEN_7 (2 << 0) ++#define UARTLCR_WLEN_8 (3 << 0) ++#define UARTLCR_STOP (1 << 2) /* 0: 1 stop bit when word length is 5,6,7,8 ++ 1: 1.5 stop bits when 5; 2 stop bits when 6,7,8 */ ++#define UARTLCR_PE (1 << 3) /* 0: parity disable */ ++#define UARTLCR_PROE (1 << 4) /* 0: even parity 1: odd parity */ ++#define UARTLCR_SPAR (1 << 5) /* 0: sticky parity disable */ ++#define UARTLCR_SBRK (1 << 6) /* write 0 normal, write 1 send break */ ++#define UARTLCR_DLAB (1 << 7) /* 0: access UARTRDR/TDR/IER 1: access UARTDLLR/DLHR */ ++ ++/* ++ * Define macros for UARTLSR ++ * UART Line Status Register ++ */ ++#define UARTLSR_DR (1 << 0) /* 0: receive FIFO is empty 1: receive data is ready */ ++#define UARTLSR_ORER (1 << 1) /* 0: no overrun error */ ++#define UARTLSR_PER (1 << 2) /* 0: no parity error */ ++#define UARTLSR_FER (1 << 3) /* 0; no framing error */ ++#define UARTLSR_BRK (1 << 4) /* 0: no break detected 1: receive a break signal */ ++#define UARTLSR_TDRQ (1 << 5) /* 1: transmit FIFO half "empty" */ ++#define UARTLSR_TEMT (1 << 6) /* 1: transmit FIFO and shift registers empty */ ++#define UARTLSR_RFER (1 << 7) /* 0: no receive error 1: receive error in FIFO mode */ ++ ++/* ++ * Define macros for UARTMCR ++ * UART Modem Control Register ++ */ ++#define UARTMCR_DTR (1 << 0) /* 0: DTR_ ouput high */ ++#define UARTMCR_RTS (1 << 1) /* 0: RTS_ output high */ ++#define UARTMCR_OUT1 (1 << 2) /* 0: UARTMSR.RI is set to 0 and RI_ input high */ ++#define UARTMCR_OUT2 (1 << 3) /* 0: UARTMSR.DCD is set to 0 and DCD_ input high */ ++#define UARTMCR_LOOP (1 << 4) /* 0: normal 1: loopback mode */ ++#define UARTMCR_MCE (1 << 7) /* 0: modem function is disable */ ++ ++/* ++ * Define macros for UARTMSR ++ * UART Modem Status Register ++ */ ++#define UARTMSR_DCTS (1 << 0) /* 0: no change on CTS_ pin since last read of UARTMSR */ ++#define UARTMSR_DDSR (1 << 1) /* 0: no change on DSR_ pin since last read of UARTMSR */ ++#define UARTMSR_DRI (1 << 2) /* 0: no change on RI_ pin since last read of UARTMSR */ ++#define UARTMSR_DDCD (1 << 3) /* 0: no change on DCD_ pin since last read of UARTMSR */ ++#define UARTMSR_CTS (1 << 4) /* 0: CTS_ pin is high */ ++#define UARTMSR_DSR (1 << 5) /* 0: DSR_ pin is high */ ++#define UARTMSR_RI (1 << 6) /* 0: RI_ pin is high */ ++#define UARTMSR_DCD (1 << 7) /* 0: DCD_ pin is high */ ++ ++/* ++ * Define macros for SIRCR ++ * Slow IrDA Control Register ++ */ ++#define SIRCR_TSIRE (1 << 0) /* 0: transmitter is in UART mode 1: IrDA mode */ ++#define SIRCR_RSIRE (1 << 1) /* 0: receiver is in UART mode 1: IrDA mode */ ++#define SIRCR_TPWS (1 << 2) /* 0: transmit 0 pulse width is 3/16 of bit length ++ 1: 0 pulse width is 1.6us for 115.2Kbps */ ++#define SIRCR_TXPL (1 << 3) /* 0: encoder generates a positive pulse for 0 */ ++#define SIRCR_RXPL (1 << 4) /* 0: decoder interprets positive pulse as 0 */ ++ ++ ++ ++/************************************************************************* ++ * INTC ++ *************************************************************************/ ++#define INTC_ISR (INTC_BASE + 0x00) ++#define INTC_IMR (INTC_BASE + 0x04) ++#define INTC_IMSR (INTC_BASE + 0x08) ++#define INTC_IMCR (INTC_BASE + 0x0c) ++#define INTC_IPR (INTC_BASE + 0x10) ++ ++#define REG_INTC_ISR REG32(INTC_ISR) ++#define REG_INTC_IMR REG32(INTC_IMR) ++#define REG_INTC_IMSR REG32(INTC_IMSR) ++#define REG_INTC_IMCR REG32(INTC_IMCR) ++#define REG_INTC_IPR REG32(INTC_IPR) ++ ++#define IRQ_I2C 1 ++#define IRQ_PS2 2 ++#define IRQ_UPRT 3 ++#define IRQ_CORE 4 ++#define IRQ_UART3 6 ++#define IRQ_UART2 7 ++#define IRQ_UART1 8 ++#define IRQ_UART0 9 ++#define IRQ_SCC1 10 ++#define IRQ_SCC0 11 ++#define IRQ_UDC 12 ++#define IRQ_UHC 13 ++#define IRQ_MSC 14 ++#define IRQ_RTC 15 ++#define IRQ_FIR 16 ++#define IRQ_SSI 17 ++#define IRQ_CIM 18 ++#define IRQ_ETH 19 ++#define IRQ_AIC 20 ++#define IRQ_DMAC 21 ++#define IRQ_OST2 22 ++#define IRQ_OST1 23 ++#define IRQ_OST0 24 ++#define IRQ_GPIO3 25 ++#define IRQ_GPIO2 26 ++#define IRQ_GPIO1 27 ++#define IRQ_GPIO0 28 ++#define IRQ_LCD 30 ++ ++ ++ ++ ++/************************************************************************* ++ * CIM ++ *************************************************************************/ ++#define CIM_CFG (CIM_BASE + 0x0000) ++#define CIM_CTRL (CIM_BASE + 0x0004) ++#define CIM_STATE (CIM_BASE + 0x0008) ++#define CIM_IID (CIM_BASE + 0x000C) ++#define CIM_RXFIFO (CIM_BASE + 0x0010) ++#define CIM_DA (CIM_BASE + 0x0020) ++#define CIM_FA (CIM_BASE + 0x0024) ++#define CIM_FID (CIM_BASE + 0x0028) ++#define CIM_CMD (CIM_BASE + 0x002C) ++ ++#define REG_CIM_CFG REG32(CIM_CFG) ++#define REG_CIM_CTRL REG32(CIM_CTRL) ++#define REG_CIM_STATE REG32(CIM_STATE) ++#define REG_CIM_IID REG32(CIM_IID) ++#define REG_CIM_RXFIFO REG32(CIM_RXFIFO) ++#define REG_CIM_DA REG32(CIM_DA) ++#define REG_CIM_FA REG32(CIM_FA) ++#define REG_CIM_FID REG32(CIM_FID) ++#define REG_CIM_CMD REG32(CIM_CMD) ++ ++/* CIM Configuration Register (CIM_CFG) */ ++ ++#define CIM_CFG_INV_DAT (1 << 15) ++#define CIM_CFG_VSP (1 << 14) ++#define CIM_CFG_HSP (1 << 13) ++#define CIM_CFG_PCP (1 << 12) ++#define CIM_CFG_DUMMY_ZERO (1 << 9) ++#define CIM_CFG_EXT_VSYNC (1 << 8) ++#define CIM_CFG_PACK_BIT 4 ++#define CIM_CFG_PACK_MASK (0x7 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_0 (0 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_1 (1 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_2 (2 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_3 (3 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_4 (4 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_5 (5 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_6 (6 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_7 (7 << CIM_CFG_PACK_BIT) ++#define CIM_CFG_DSM_BIT 0 ++#define CIM_CFG_DSM_MASK (0x3 << CIM_CFG_DSM_BIT) ++ #define CIM_CFG_DSM_CPM (0 << CIM_CFG_DSM_BIT) /* CCIR656 Progressive Mode */ ++ #define CIM_CFG_DSM_CIM (1 << CIM_CFG_DSM_BIT) /* CCIR656 Interlace Mode */ ++ #define CIM_CFG_DSM_GCM (2 << CIM_CFG_DSM_BIT) /* Gated Clock Mode */ ++ #define CIM_CFG_DSM_NGCM (3 << CIM_CFG_DSM_BIT) /* Non-Gated Clock Mode */ ++ ++/* CIM Control Register (CIM_CTRL) */ ++ ++#define CIM_CTRL_MCLKDIV_BIT 24 ++#define CIM_CTRL_MCLKDIV_MASK (0xff << CIM_CTRL_MCLKDIV_BIT) ++#define CIM_CTRL_FRC_BIT 16 ++#define CIM_CTRL_FRC_MASK (0xf << CIM_CTRL_FRC_BIT) ++ #define CIM_CTRL_FRC_1 (0x0 << CIM_CTRL_FRC_BIT) /* Sample every frame */ ++ #define CIM_CTRL_FRC_2 (0x1 << CIM_CTRL_FRC_BIT) /* Sample 1/2 frame */ ++ #define CIM_CTRL_FRC_3 (0x2 << CIM_CTRL_FRC_BIT) /* Sample 1/3 frame */ ++ #define CIM_CTRL_FRC_4 (0x3 << CIM_CTRL_FRC_BIT) /* Sample 1/4 frame */ ++ #define CIM_CTRL_FRC_5 (0x4 << CIM_CTRL_FRC_BIT) /* Sample 1/5 frame */ ++ #define CIM_CTRL_FRC_6 (0x5 << CIM_CTRL_FRC_BIT) /* Sample 1/6 frame */ ++ #define CIM_CTRL_FRC_7 (0x6 << CIM_CTRL_FRC_BIT) /* Sample 1/7 frame */ ++ #define CIM_CTRL_FRC_8 (0x7 << CIM_CTRL_FRC_BIT) /* Sample 1/8 frame */ ++ #define CIM_CTRL_FRC_9 (0x8 << CIM_CTRL_FRC_BIT) /* Sample 1/9 frame */ ++ #define CIM_CTRL_FRC_10 (0x9 << CIM_CTRL_FRC_BIT) /* Sample 1/10 frame */ ++ #define CIM_CTRL_FRC_11 (0xA << CIM_CTRL_FRC_BIT) /* Sample 1/11 frame */ ++ #define CIM_CTRL_FRC_12 (0xB << CIM_CTRL_FRC_BIT) /* Sample 1/12 frame */ ++ #define CIM_CTRL_FRC_13 (0xC << CIM_CTRL_FRC_BIT) /* Sample 1/13 frame */ ++ #define CIM_CTRL_FRC_14 (0xD << CIM_CTRL_FRC_BIT) /* Sample 1/14 frame */ ++ #define CIM_CTRL_FRC_15 (0xE << CIM_CTRL_FRC_BIT) /* Sample 1/15 frame */ ++ #define CIM_CTRL_FRC_16 (0xF << CIM_CTRL_FRC_BIT) /* Sample 1/16 frame */ ++#define CIM_CTRL_VDDM (1 << 13) ++#define CIM_CTRL_DMA_SOFM (1 << 12) ++#define CIM_CTRL_DMA_EOFM (1 << 11) ++#define CIM_CTRL_DMA_STOPM (1 << 10) ++#define CIM_CTRL_RXF_TRIGM (1 << 9) ++#define CIM_CTRL_RXF_OFM (1 << 8) ++#define CIM_CTRL_RXF_TRIG_BIT 4 ++#define CIM_CTRL_RXF_TRIG_MASK (0x7 << CIM_CTRL_RXF_TRIG_BIT) ++ #define CIM_CTRL_RXF_TRIG_4 (0 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 4 */ ++ #define CIM_CTRL_RXF_TRIG_8 (1 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 8 */ ++ #define CIM_CTRL_RXF_TRIG_12 (2 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 12 */ ++ #define CIM_CTRL_RXF_TRIG_16 (3 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 16 */ ++ #define CIM_CTRL_RXF_TRIG_20 (4 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 20 */ ++ #define CIM_CTRL_RXF_TRIG_24 (5 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 24 */ ++ #define CIM_CTRL_RXF_TRIG_28 (6 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 28 */ ++ #define CIM_CTRL_RXF_TRIG_32 (7 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 32 */ ++#define CIM_CTRL_DMA_EN (1 << 2) ++#define CIM_CTRL_RXF_RST (1 << 1) ++#define CIM_CTRL_ENA (1 << 0) ++ ++/* CIM State Register (CIM_STATE) */ ++ ++#define CIM_STATE_DMA_SOF (1 << 6) ++#define CIM_STATE_DMA_EOF (1 << 5) ++#define CIM_STATE_DMA_STOP (1 << 4) ++#define CIM_STATE_RXF_OF (1 << 3) ++#define CIM_STATE_RXF_TRIG (1 << 2) ++#define CIM_STATE_RXF_EMPTY (1 << 1) ++#define CIM_STATE_VDD (1 << 0) ++ ++/* CIM DMA Command Register (CIM_CMD) */ ++ ++#define CIM_CMD_SOFINT (1 << 31) ++#define CIM_CMD_EOFINT (1 << 30) ++#define CIM_CMD_STOP (1 << 28) ++#define CIM_CMD_LEN_BIT 0 ++#define CIM_CMD_LEN_MASK (0xffffff << CIM_CMD_LEN_BIT) ++ ++ ++ ++ ++/************************************************************************* ++ * PWM ++ *************************************************************************/ ++#define PWM_CTR(n) (PWM##n##_BASE + 0x000) ++#define PWM_PER(n) (PWM##n##_BASE + 0x004) ++#define PWM_DUT(n) (PWM##n##_BASE + 0x008) ++ ++#define REG_PWM_CTR(n) REG8(PWM_CTR(n)) ++#define REG_PWM_PER(n) REG16(PWM_PER(n)) ++#define REG_PWM_DUT(n) REG16(PWM_DUT(n)) ++ ++/* PWM Control Register (PWM_CTR) */ ++ ++#define PWM_CTR_EN (1 << 7) ++#define PWM_CTR_SD (1 << 6) ++#define PWM_CTR_PRESCALE_BIT 0 ++#define PWM_CTR_PRESCALE_MASK (0x3f << PWM_CTR_PRESCALE_BIT) ++ ++/* PWM Period Register (PWM_PER) */ ++ ++#define PWM_PER_PERIOD_BIT 0 ++#define PWM_PER_PERIOD_MASK (0x3ff << PWM_PER_PERIOD_BIT) ++ ++/* PWM Duty Register (PWM_DUT) */ ++ ++#define PWM_DUT_FDUTY (1 << 10) ++#define PWM_DUT_DUTY_BIT 0 ++#define PWM_DUT_DUTY_MASK (0x3ff << PWM_DUT_DUTY_BIT) ++ ++ ++ ++ ++/************************************************************************* ++ * EMC ++ *************************************************************************/ ++#define EMC_BCR (EMC_BASE + 0x00) ++#define EMC_SMCR0 (EMC_BASE + 0x10) ++#define EMC_SMCR1 (EMC_BASE + 0x14) ++#define EMC_SMCR2 (EMC_BASE + 0x18) ++#define EMC_SMCR3 (EMC_BASE + 0x1c) ++#define EMC_SMCR4 (EMC_BASE + 0x20) ++#define EMC_SMCR5 (EMC_BASE + 0x24) ++#define EMC_SMCR6 (EMC_BASE + 0x28) ++#define EMC_SMCR7 (EMC_BASE + 0x2c) ++#define EMC_SACR0 (EMC_BASE + 0x30) ++#define EMC_SACR1 (EMC_BASE + 0x34) ++#define EMC_SACR2 (EMC_BASE + 0x38) ++#define EMC_SACR3 (EMC_BASE + 0x3c) ++#define EMC_SACR4 (EMC_BASE + 0x40) ++#define EMC_SACR5 (EMC_BASE + 0x44) ++#define EMC_SACR6 (EMC_BASE + 0x48) ++#define EMC_SACR7 (EMC_BASE + 0x4c) ++#define EMC_NFCSR (EMC_BASE + 0x50) ++#define EMC_NFECC (EMC_BASE + 0x54) ++#define EMC_PCCR1 (EMC_BASE + 0x60) ++#define EMC_PCCR2 (EMC_BASE + 0x64) ++#define EMC_PCCR3 (EMC_BASE + 0x68) ++#define EMC_PCCR4 (EMC_BASE + 0x6c) ++#define EMC_DMCR (EMC_BASE + 0x80) ++#define EMC_RTCSR (EMC_BASE + 0x84) ++#define EMC_RTCNT (EMC_BASE + 0x88) ++#define EMC_RTCOR (EMC_BASE + 0x8c) ++#define EMC_DMAR1 (EMC_BASE + 0x90) ++#define EMC_DMAR2 (EMC_BASE + 0x94) ++#define EMC_DMAR3 (EMC_BASE + 0x98) ++#define EMC_DMAR4 (EMC_BASE + 0x9c) ++ ++#define EMC_SDMR0 (EMC_BASE + 0xa000) ++#define EMC_SDMR1 (EMC_BASE + 0xb000) ++#define EMC_SDMR2 (EMC_BASE + 0xc000) ++#define EMC_SDMR3 (EMC_BASE + 0xd000) ++ ++#define REG_EMC_BCR REG32(EMC_BCR) ++#define REG_EMC_SMCR0 REG32(EMC_SMCR0) ++#define REG_EMC_SMCR1 REG32(EMC_SMCR1) ++#define REG_EMC_SMCR2 REG32(EMC_SMCR2) ++#define REG_EMC_SMCR3 REG32(EMC_SMCR3) ++#define REG_EMC_SMCR4 REG32(EMC_SMCR4) ++#define REG_EMC_SMCR5 REG32(EMC_SMCR5) ++#define REG_EMC_SMCR6 REG32(EMC_SMCR6) ++#define REG_EMC_SMCR7 REG32(EMC_SMCR7) ++#define REG_EMC_SACR0 REG32(EMC_SACR0) ++#define REG_EMC_SACR1 REG32(EMC_SACR1) ++#define REG_EMC_SACR2 REG32(EMC_SACR2) ++#define REG_EMC_SACR3 REG32(EMC_SACR3) ++#define REG_EMC_SACR4 REG32(EMC_SACR4) ++#define REG_EMC_SACR5 REG32(EMC_SACR5) ++#define REG_EMC_SACR6 REG32(EMC_SACR6) ++#define REG_EMC_SACR7 REG32(EMC_SACR7) ++#define REG_EMC_NFCSR REG32(EMC_NFCSR) ++#define REG_EMC_NFECC REG32(EMC_NFECC) ++#define REG_EMC_DMCR REG32(EMC_DMCR) ++#define REG_EMC_RTCSR REG16(EMC_RTCSR) ++#define REG_EMC_RTCNT REG16(EMC_RTCNT) ++#define REG_EMC_RTCOR REG16(EMC_RTCOR) ++#define REG_EMC_DMAR1 REG32(EMC_DMAR1) ++#define REG_EMC_DMAR2 REG32(EMC_DMAR2) ++#define REG_EMC_DMAR3 REG32(EMC_DMAR3) ++#define REG_EMC_DMAR4 REG32(EMC_DMAR4) ++#define REG_EMC_PCCR1 REG32(EMC_PCCR1) ++#define REG_EMC_PCCR2 REG32(EMC_PCCR2) ++#define REG_EMC_PCCR3 REG32(EMC_PCCR3) ++#define REG_EMC_PCCR4 REG32(EMC_PCCR4) ++ ++ ++#define EMC_BCR_BRE (1 << 1) ++ ++#define EMC_SMCR_STRV_BIT 24 ++#define EMC_SMCR_STRV_MASK (0x0f << EMC_SMCR_STRV_BIT) ++#define EMC_SMCR_TAW_BIT 20 ++#define EMC_SMCR_TAW_MASK (0x0f << EMC_SMCR_TAW_BIT) ++#define EMC_SMCR_TBP_BIT 16 ++#define EMC_SMCR_TBP_MASK (0x0f << EMC_SMCR_TBP_BIT) ++#define EMC_SMCR_TAH_BIT 12 ++#define EMC_SMCR_TAH_MASK (0x07 << EMC_SMCR_TAH_BIT) ++#define EMC_SMCR_TAS_BIT 8 ++#define EMC_SMCR_TAS_MASK (0x07 << EMC_SMCR_TAS_BIT) ++#define EMC_SMCR_BW_BIT 6 ++#define EMC_SMCR_BW_MASK (0x03 << EMC_SMCR_BW_BIT) ++ #define EMC_SMCR_BW_8BIT (0 << EMC_SMCR_BW_BIT) ++ #define EMC_SMCR_BW_16BIT (1 << EMC_SMCR_BW_BIT) ++ #define EMC_SMCR_BW_32BIT (2 << EMC_SMCR_BW_BIT) ++#define EMC_SMCR_BCM (1 << 3) ++#define EMC_SMCR_BL_BIT 1 ++#define EMC_SMCR_BL_MASK (0x03 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_4 (0 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_8 (1 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_16 (2 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_32 (3 << EMC_SMCR_BL_BIT) ++#define EMC_SMCR_SMT (1 << 0) ++ ++#define EMC_SACR_BASE_BIT 8 ++#define EMC_SACR_BASE_MASK (0xff << EMC_SACR_BASE_BIT) ++#define EMC_SACR_MASK_BIT 0 ++#define EMC_SACR_MASK_MASK (0xff << EMC_SACR_MASK_BIT) ++ ++#define EMC_NFCSR_RB (1 << 7) ++#define EMC_NFCSR_BOOT_SEL_BIT 4 ++#define EMC_NFCSR_BOOT_SEL_MASK (0x07 << EMC_NFCSR_BOOT_SEL_BIT) ++#define EMC_NFCSR_ERST (1 << 3) ++#define EMC_NFCSR_ECCE (1 << 2) ++#define EMC_NFCSR_FCE (1 << 1) ++#define EMC_NFCSR_NFE (1 << 0) ++ ++#define EMC_NFECC_ECC2_BIT 16 ++#define EMC_NFECC_ECC2_MASK (0xff << EMC_NFECC_ECC2_BIT) ++#define EMC_NFECC_ECC1_BIT 8 ++#define EMC_NFECC_ECC1_MASK (0xff << EMC_NFECC_ECC1_BIT) ++#define EMC_NFECC_ECC0_BIT 0 ++#define EMC_NFECC_ECC0_MASK (0xff << EMC_NFECC_ECC0_BIT) ++ ++#define EMC_DMCR_BW_BIT 31 ++#define EMC_DMCR_BW (1 << EMC_DMCR_BW_BIT) ++#define EMC_DMCR_CA_BIT 26 ++#define EMC_DMCR_CA_MASK (0x07 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_8 (0 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_9 (1 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_10 (2 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_11 (3 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_12 (4 << EMC_DMCR_CA_BIT) ++#define EMC_DMCR_RMODE (1 << 25) ++#define EMC_DMCR_RFSH (1 << 24) ++#define EMC_DMCR_MRSET (1 << 23) ++#define EMC_DMCR_RA_BIT 20 ++#define EMC_DMCR_RA_MASK (0x03 << EMC_DMCR_RA_BIT) ++ #define EMC_DMCR_RA_11 (0 << EMC_DMCR_RA_BIT) ++ #define EMC_DMCR_RA_12 (1 << EMC_DMCR_RA_BIT) ++ #define EMC_DMCR_RA_13 (2 << EMC_DMCR_RA_BIT) ++#define EMC_DMCR_BA_BIT 19 ++#define EMC_DMCR_BA (1 << EMC_DMCR_BA_BIT) ++#define EMC_DMCR_PDM (1 << 18) ++#define EMC_DMCR_EPIN (1 << 17) ++#define EMC_DMCR_TRAS_BIT 13 ++#define EMC_DMCR_TRAS_MASK (0x07 << EMC_DMCR_TRAS_BIT) ++#define EMC_DMCR_RCD_BIT 11 ++#define EMC_DMCR_RCD_MASK (0x03 << EMC_DMCR_RCD_BIT) ++#define EMC_DMCR_TPC_BIT 8 ++#define EMC_DMCR_TPC_MASK (0x07 << EMC_DMCR_TPC_BIT) ++#define EMC_DMCR_TRWL_BIT 5 ++#define EMC_DMCR_TRWL_MASK (0x03 << EMC_DMCR_TRWL_BIT) ++#define EMC_DMCR_TRC_BIT 2 ++#define EMC_DMCR_TRC_MASK (0x07 << EMC_DMCR_TRC_BIT) ++#define EMC_DMCR_TCL_BIT 0 ++#define EMC_DMCR_TCL_MASK (0x03 << EMC_DMCR_TCL_BIT) ++ ++#define EMC_RTCSR_CMF (1 << 7) ++#define EMC_RTCSR_CKS_BIT 0 ++#define EMC_RTCSR_CKS_MASK (0x07 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_DISABLE (0 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_4 (1 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_16 (2 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_64 (3 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_256 (4 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_1024 (5 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_2048 (6 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_4096 (7 << EMC_RTCSR_CKS_BIT) ++ ++#define EMC_DMAR_BASE_BIT 8 ++#define EMC_DMAR_BASE_MASK (0xff << EMC_DMAR_BASE_BIT) ++#define EMC_DMAR_MASK_BIT 0 ++#define EMC_DMAR_MASK_MASK (0xff << EMC_DMAR_MASK_BIT) ++ ++#define EMC_SDMR_BM (1 << 9) ++#define EMC_SDMR_OM_BIT 7 ++#define EMC_SDMR_OM_MASK (3 << EMC_SDMR_OM_BIT) ++ #define EMC_SDMR_OM_NORMAL (0 << EMC_SDMR_OM_BIT) ++#define EMC_SDMR_CAS_BIT 4 ++#define EMC_SDMR_CAS_MASK (7 << EMC_SDMR_CAS_BIT) ++ #define EMC_SDMR_CAS_1 (1 << EMC_SDMR_CAS_BIT) ++ #define EMC_SDMR_CAS_2 (2 << EMC_SDMR_CAS_BIT) ++ #define EMC_SDMR_CAS_3 (3 << EMC_SDMR_CAS_BIT) ++#define EMC_SDMR_BT_BIT 3 ++#define EMC_SDMR_BT_MASK (1 << EMC_SDMR_BT_BIT) ++ #define EMC_SDMR_BT_SEQ (0 << EMC_SDMR_BT_BIT) ++ #define EMC_SDMR_BT_INTR (1 << EMC_SDMR_BT_BIT) ++#define EMC_SDMR_BL_BIT 0 ++#define EMC_SDMR_BL_MASK (7 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_1 (0 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_2 (1 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_4 (2 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_8 (3 << EMC_SDMR_BL_BIT) ++ ++#define EMC_SDMR_CAS2_16BIT \ ++ (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) ++#define EMC_SDMR_CAS2_32BIT \ ++ (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) ++#define EMC_SDMR_CAS3_16BIT \ ++ (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) ++#define EMC_SDMR_CAS3_32BIT \ ++ (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) ++ ++#define EMC_PCCR12_AMW (1 << 31) ++#define EMC_PCCR12_AMAS_BIT 28 ++#define EMC_PCCR12_AMAS_MASK (0x07 << EMC_PCCR12_AMAS_BIT) ++#define EMC_PCCR12_AMAH_BIT 24 ++#define EMC_PCCR12_AMAH_MASK (0x07 << EMC_PCCR12_AMAH_BIT) ++#define EMC_PCCR12_AMPW_BIT 20 ++#define EMC_PCCR12_AMPW_MASK (0x0f << EMC_PCCR12_AMPW_BIT) ++#define EMC_PCCR12_AMRT_BIT 16 ++#define EMC_PCCR12_AMRT_MASK (0x0f << EMC_PCCR12_AMRT_BIT) ++#define EMC_PCCR12_CMW (1 << 15) ++#define EMC_PCCR12_CMAS_BIT 12 ++#define EMC_PCCR12_CMAS_MASK (0x07 << EMC_PCCR12_CMAS_BIT) ++#define EMC_PCCR12_CMAH_BIT 8 ++#define EMC_PCCR12_CMAH_MASK (0x07 << EMC_PCCR12_CMAH_BIT) ++#define EMC_PCCR12_CMPW_BIT 4 ++#define EMC_PCCR12_CMPW_MASK (0x0f << EMC_PCCR12_CMPW_BIT) ++#define EMC_PCCR12_CMRT_BIT 0 ++#define EMC_PCCR12_CMRT_MASK (0x07 << EMC_PCCR12_CMRT_BIT) ++ ++#define EMC_PCCR34_DRS_BIT 16 ++#define EMC_PCCR34_DRS_MASK (0x03 << EMC_PCCR34_DRS_BIT) ++ #define EMC_PCCR34_DRS_SPKR (1 << EMC_PCCR34_DRS_BIT) ++ #define EMC_PCCR34_DRS_IOIS16 (2 << EMC_PCCR34_DRS_BIT) ++ #define EMC_PCCR34_DRS_INPACK (3 << EMC_PCCR34_DRS_BIT) ++#define EMC_PCCR34_IOIS16 (1 << 15) ++#define EMC_PCCR34_IOW (1 << 14) ++#define EMC_PCCR34_TCB_BIT 12 ++#define EMC_PCCR34_TCB_MASK (0x03 << EMC_PCCR34_TCB_BIT) ++#define EMC_PCCR34_IORT_BIT 8 ++#define EMC_PCCR34_IORT_MASK (0x07 << EMC_PCCR34_IORT_BIT) ++#define EMC_PCCR34_IOAE_BIT 6 ++#define EMC_PCCR34_IOAE_MASK (0x03 << EMC_PCCR34_IOAE_BIT) ++ #define EMC_PCCR34_IOAE_NONE (0 << EMC_PCCR34_IOAE_BIT) ++ #define EMC_PCCR34_IOAE_1 (1 << EMC_PCCR34_IOAE_BIT) ++ #define EMC_PCCR34_IOAE_2 (2 << EMC_PCCR34_IOAE_BIT) ++ #define EMC_PCCR34_IOAE_5 (3 << EMC_PCCR34_IOAE_BIT) ++#define EMC_PCCR34_IOAH_BIT 4 ++#define EMC_PCCR34_IOAH_MASK (0x03 << EMC_PCCR34_IOAH_BIT) ++ #define EMC_PCCR34_IOAH_NONE (0 << EMC_PCCR34_IOAH_BIT) ++ #define EMC_PCCR34_IOAH_1 (1 << EMC_PCCR34_IOAH_BIT) ++ #define EMC_PCCR34_IOAH_2 (2 << EMC_PCCR34_IOAH_BIT) ++ #define EMC_PCCR34_IOAH_5 (3 << EMC_PCCR34_IOAH_BIT) ++#define EMC_PCCR34_IOPW_BIT 0 ++#define EMC_PCCR34_IOPW_MASK (0x0f << EMC_PCCR34_IOPW_BIT) ++ ++ ++ ++ ++/************************************************************************* ++ * GPIO ++ *************************************************************************/ ++#define GPIO_GPDR(n) (GPIO_BASE + (0x00 + (n)*0x30)) ++#define GPIO_GPDIR(n) (GPIO_BASE + (0x04 + (n)*0x30)) ++#define GPIO_GPODR(n) (GPIO_BASE + (0x08 + (n)*0x30)) ++#define GPIO_GPPUR(n) (GPIO_BASE + (0x0c + (n)*0x30)) ++#define GPIO_GPALR(n) (GPIO_BASE + (0x10 + (n)*0x30)) ++#define GPIO_GPAUR(n) (GPIO_BASE + (0x14 + (n)*0x30)) ++#define GPIO_GPIDLR(n) (GPIO_BASE + (0x18 + (n)*0x30)) ++#define GPIO_GPIDUR(n) (GPIO_BASE + (0x1c + (n)*0x30)) ++#define GPIO_GPIER(n) (GPIO_BASE + (0x20 + (n)*0x30)) ++#define GPIO_GPIMR(n) (GPIO_BASE + (0x24 + (n)*0x30)) ++#define GPIO_GPFR(n) (GPIO_BASE + (0x28 + (n)*0x30)) ++ ++#define REG_GPIO_GPDR(n) REG32(GPIO_GPDR((n))) ++#define REG_GPIO_GPDIR(n) REG32(GPIO_GPDIR((n))) ++#define REG_GPIO_GPODR(n) REG32(GPIO_GPODR((n))) ++#define REG_GPIO_GPPUR(n) REG32(GPIO_GPPUR((n))) ++#define REG_GPIO_GPALR(n) REG32(GPIO_GPALR((n))) ++#define REG_GPIO_GPAUR(n) REG32(GPIO_GPAUR((n))) ++#define REG_GPIO_GPIDLR(n) REG32(GPIO_GPIDLR((n))) ++#define REG_GPIO_GPIDUR(n) REG32(GPIO_GPIDUR((n))) ++#define REG_GPIO_GPIER(n) REG32(GPIO_GPIER((n))) ++#define REG_GPIO_GPIMR(n) REG32(GPIO_GPIMR((n))) ++#define REG_GPIO_GPFR(n) REG32(GPIO_GPFR((n))) ++ ++#define GPIO_IRQ_LOLEVEL 0 ++#define GPIO_IRQ_HILEVEL 1 ++#define GPIO_IRQ_FALLEDG 2 ++#define GPIO_IRQ_RAISEDG 3 ++ ++#define IRQ_GPIO_0 48 ++#define NUM_GPIO 128 ++ ++#define GPIO_GPDR0 GPIO_GPDR(0) ++#define GPIO_GPDR1 GPIO_GPDR(1) ++#define GPIO_GPDR2 GPIO_GPDR(2) ++#define GPIO_GPDR3 GPIO_GPDR(3) ++#define GPIO_GPDIR0 GPIO_GPDIR(0) ++#define GPIO_GPDIR1 GPIO_GPDIR(1) ++#define GPIO_GPDIR2 GPIO_GPDIR(2) ++#define GPIO_GPDIR3 GPIO_GPDIR(3) ++#define GPIO_GPODR0 GPIO_GPODR(0) ++#define GPIO_GPODR1 GPIO_GPODR(1) ++#define GPIO_GPODR2 GPIO_GPODR(2) ++#define GPIO_GPODR3 GPIO_GPODR(3) ++#define GPIO_GPPUR0 GPIO_GPPUR(0) ++#define GPIO_GPPUR1 GPIO_GPPUR(1) ++#define GPIO_GPPUR2 GPIO_GPPUR(2) ++#define GPIO_GPPUR3 GPIO_GPPUR(3) ++#define GPIO_GPALR0 GPIO_GPALR(0) ++#define GPIO_GPALR1 GPIO_GPALR(1) ++#define GPIO_GPALR2 GPIO_GPALR(2) ++#define GPIO_GPALR3 GPIO_GPALR(3) ++#define GPIO_GPAUR0 GPIO_GPAUR(0) ++#define GPIO_GPAUR1 GPIO_GPAUR(1) ++#define GPIO_GPAUR2 GPIO_GPAUR(2) ++#define GPIO_GPAUR3 GPIO_GPAUR(3) ++#define GPIO_GPIDLR0 GPIO_GPIDLR(0) ++#define GPIO_GPIDLR1 GPIO_GPIDLR(1) ++#define GPIO_GPIDLR2 GPIO_GPIDLR(2) ++#define GPIO_GPIDLR3 GPIO_GPIDLR(3) ++#define GPIO_GPIDUR0 GPIO_GPIDUR(0) ++#define GPIO_GPIDUR1 GPIO_GPIDUR(1) ++#define GPIO_GPIDUR2 GPIO_GPIDUR(2) ++#define GPIO_GPIDUR3 GPIO_GPIDUR(3) ++#define GPIO_GPIER0 GPIO_GPIER(0) ++#define GPIO_GPIER1 GPIO_GPIER(1) ++#define GPIO_GPIER2 GPIO_GPIER(2) ++#define GPIO_GPIER3 GPIO_GPIER(3) ++#define GPIO_GPIMR0 GPIO_GPIMR(0) ++#define GPIO_GPIMR1 GPIO_GPIMR(1) ++#define GPIO_GPIMR2 GPIO_GPIMR(2) ++#define GPIO_GPIMR3 GPIO_GPIMR(3) ++#define GPIO_GPFR0 GPIO_GPFR(0) ++#define GPIO_GPFR1 GPIO_GPFR(1) ++#define GPIO_GPFR2 GPIO_GPFR(2) ++#define GPIO_GPFR3 GPIO_GPFR(3) ++ ++ ++/************************************************************************* ++ * HARB ++ *************************************************************************/ ++#define HARB_HAPOR (HARB_BASE + 0x000) ++#define HARB_HMCTR (HARB_BASE + 0x010) ++#define HARB_HME8H (HARB_BASE + 0x014) ++#define HARB_HMCR1 (HARB_BASE + 0x018) ++#define HARB_HMER2 (HARB_BASE + 0x01C) ++#define HARB_HMER3 (HARB_BASE + 0x020) ++#define HARB_HMLTR (HARB_BASE + 0x024) ++ ++#define REG_HARB_HAPOR REG32(HARB_HAPOR) ++#define REG_HARB_HMCTR REG32(HARB_HMCTR) ++#define REG_HARB_HME8H REG32(HARB_HME8H) ++#define REG_HARB_HMCR1 REG32(HARB_HMCR1) ++#define REG_HARB_HMER2 REG32(HARB_HMER2) ++#define REG_HARB_HMER3 REG32(HARB_HMER3) ++#define REG_HARB_HMLTR REG32(HARB_HMLTR) ++ ++/* HARB Priority Order Register (HARB_HAPOR) */ ++ ++#define HARB_HAPOR_UCHSEL (1 << 7) ++#define HARB_HAPOR_PRIO_BIT 0 ++#define HARB_HAPOR_PRIO_MASK (0xf << HARB_HAPOR_PRIO_BIT) ++ ++/* AHB Monitor Control Register (HARB_HMCTR) */ ++ ++#define HARB_HMCTR_HET3_BIT 20 ++#define HARB_HMCTR_HET3_MASK (0xf << HARB_HMCTR_HET3_BIT) ++#define HARB_HMCTR_HMS3_BIT 16 ++#define HARB_HMCTR_HMS3_MASK (0xf << HARB_HMCTR_HMS3_BIT) ++#define HARB_HMCTR_HET2_BIT 12 ++#define HARB_HMCTR_HET2_MASK (0xf << HARB_HMCTR_HET2_BIT) ++#define HARB_HMCTR_HMS2_BIT 8 ++#define HARB_HMCTR_HMS2_MASK (0xf << HARB_HMCTR_HMS2_BIT) ++#define HARB_HMCTR_HOVF3 (1 << 7) ++#define HARB_HMCTR_HOVF2 (1 << 6) ++#define HARB_HMCTR_HOVF1 (1 << 5) ++#define HARB_HMCTR_HRST (1 << 4) ++#define HARB_HMCTR_HEE3 (1 << 2) ++#define HARB_HMCTR_HEE2 (1 << 1) ++#define HARB_HMCTR_HEE1 (1 << 0) ++ ++/* AHB Monitor Event 8bits High Register (HARB_HME8H) */ ++ ++#define HARB_HME8H_HC8H1_BIT 16 ++#define HARB_HME8H_HC8H1_MASK (0xff << HARB_HME8H_HC8H1_BIT) ++#define HARB_HME8H_HC8H2_BIT 8 ++#define HARB_HME8H_HC8H2_MASK (0xff << HARB_HME8H_HC8H2_BIT) ++#define HARB_HME8H_HC8H3_BIT 0 ++#define HARB_HME8H_HC8H3_MASK (0xff << HARB_HME8H_HC8H3_BIT) ++ ++/* AHB Monitor Latency Register (HARB_HMLTR) */ ++ ++#define HARB_HMLTR_HLT2_BIT 16 ++#define HARB_HMLTR_HLT2_MASK (0xffff << HARB_HMLTR_HLT2_BIT) ++#define HARB_HMLTR_HLT3_BIT 0 ++#define HARB_HMLTR_HLT3_MASK (0xffff << HARB_HMLTR_HLT3_BIT) ++ ++ ++ ++ ++/************************************************************************* ++ * I2C ++ *************************************************************************/ ++#define I2C_DR (I2C_BASE + 0x000) ++#define I2C_CR (I2C_BASE + 0x004) ++#define I2C_SR (I2C_BASE + 0x008) ++#define I2C_GR (I2C_BASE + 0x00C) ++ ++#define REG_I2C_DR REG8(I2C_DR) ++#define REG_I2C_CR REG8(I2C_CR) ++#define REG_I2C_SR REG8(I2C_SR) ++#define REG_I2C_GR REG16(I2C_GR) ++ ++/* I2C Control Register (I2C_CR) */ ++ ++#define I2C_CR_IEN (1 << 4) ++#define I2C_CR_STA (1 << 3) ++#define I2C_CR_STO (1 << 2) ++#define I2C_CR_AC (1 << 1) ++#define I2C_CR_I2CE (1 << 0) ++ ++/* I2C Status Register (I2C_SR) */ ++ ++#define I2C_SR_STX (1 << 4) ++#define I2C_SR_BUSY (1 << 3) ++#define I2C_SR_TEND (1 << 2) ++#define I2C_SR_DRF (1 << 1) ++#define I2C_SR_ACKF (1 << 0) ++ ++ ++ ++ ++/************************************************************************* ++ * UDC ++ *************************************************************************/ ++#define UDC_EP0InCR (UDC_BASE + 0x00) ++#define UDC_EP0InSR (UDC_BASE + 0x04) ++#define UDC_EP0InBSR (UDC_BASE + 0x08) ++#define UDC_EP0InMPSR (UDC_BASE + 0x0c) ++#define UDC_EP0InDesR (UDC_BASE + 0x14) ++#define UDC_EP1InCR (UDC_BASE + 0x20) ++#define UDC_EP1InSR (UDC_BASE + 0x24) ++#define UDC_EP1InBSR (UDC_BASE + 0x28) ++#define UDC_EP1InMPSR (UDC_BASE + 0x2c) ++#define UDC_EP1InDesR (UDC_BASE + 0x34) ++#define UDC_EP2InCR (UDC_BASE + 0x40) ++#define UDC_EP2InSR (UDC_BASE + 0x44) ++#define UDC_EP2InBSR (UDC_BASE + 0x48) ++#define UDC_EP2InMPSR (UDC_BASE + 0x4c) ++#define UDC_EP2InDesR (UDC_BASE + 0x54) ++#define UDC_EP3InCR (UDC_BASE + 0x60) ++#define UDC_EP3InSR (UDC_BASE + 0x64) ++#define UDC_EP3InBSR (UDC_BASE + 0x68) ++#define UDC_EP3InMPSR (UDC_BASE + 0x6c) ++#define UDC_EP3InDesR (UDC_BASE + 0x74) ++#define UDC_EP4InCR (UDC_BASE + 0x80) ++#define UDC_EP4InSR (UDC_BASE + 0x84) ++#define UDC_EP4InBSR (UDC_BASE + 0x88) ++#define UDC_EP4InMPSR (UDC_BASE + 0x8c) ++#define UDC_EP4InDesR (UDC_BASE + 0x94) ++ ++#define UDC_EP0OutCR (UDC_BASE + 0x200) ++#define UDC_EP0OutSR (UDC_BASE + 0x204) ++#define UDC_EP0OutPFNR (UDC_BASE + 0x208) ++#define UDC_EP0OutMPSR (UDC_BASE + 0x20c) ++#define UDC_EP0OutSBPR (UDC_BASE + 0x210) ++#define UDC_EP0OutDesR (UDC_BASE + 0x214) ++#define UDC_EP5OutCR (UDC_BASE + 0x2a0) ++#define UDC_EP5OutSR (UDC_BASE + 0x2a4) ++#define UDC_EP5OutPFNR (UDC_BASE + 0x2a8) ++#define UDC_EP5OutMPSR (UDC_BASE + 0x2ac) ++#define UDC_EP5OutDesR (UDC_BASE + 0x2b4) ++#define UDC_EP6OutCR (UDC_BASE + 0x2c0) ++#define UDC_EP6OutSR (UDC_BASE + 0x2c4) ++#define UDC_EP6OutPFNR (UDC_BASE + 0x2c8) ++#define UDC_EP6OutMPSR (UDC_BASE + 0x2cc) ++#define UDC_EP6OutDesR (UDC_BASE + 0x2d4) ++#define UDC_EP7OutCR (UDC_BASE + 0x2e0) ++#define UDC_EP7OutSR (UDC_BASE + 0x2e4) ++#define UDC_EP7OutPFNR (UDC_BASE + 0x2e8) ++#define UDC_EP7OutMPSR (UDC_BASE + 0x2ec) ++#define UDC_EP7OutDesR (UDC_BASE + 0x2f4) ++ ++#define UDC_DevCFGR (UDC_BASE + 0x400) ++#define UDC_DevCR (UDC_BASE + 0x404) ++#define UDC_DevSR (UDC_BASE + 0x408) ++#define UDC_DevIntR (UDC_BASE + 0x40c) ++#define UDC_DevIntMR (UDC_BASE + 0x410) ++#define UDC_EPIntR (UDC_BASE + 0x414) ++#define UDC_EPIntMR (UDC_BASE + 0x418) ++ ++#define UDC_STCMAR (UDC_BASE + 0x500) ++#define UDC_EP0InfR (UDC_BASE + 0x504) ++#define UDC_EP1InfR (UDC_BASE + 0x508) ++#define UDC_EP2InfR (UDC_BASE + 0x50c) ++#define UDC_EP3InfR (UDC_BASE + 0x510) ++#define UDC_EP4InfR (UDC_BASE + 0x514) ++#define UDC_EP5InfR (UDC_BASE + 0x518) ++#define UDC_EP6InfR (UDC_BASE + 0x51c) ++#define UDC_EP7InfR (UDC_BASE + 0x520) ++ ++#define UDC_TXCONFIRM (UDC_BASE + 0x41C) ++#define UDC_TXZLP (UDC_BASE + 0x420) ++#define UDC_RXCONFIRM (UDC_BASE + 0x41C) ++ ++#define UDC_RXFIFO (UDC_BASE + 0x800) ++#define UDC_TXFIFOEP0 (UDC_BASE + 0x840) ++ ++#define REG_UDC_EP0InCR REG32(UDC_EP0InCR) ++#define REG_UDC_EP0InSR REG32(UDC_EP0InSR) ++#define REG_UDC_EP0InBSR REG32(UDC_EP0InBSR) ++#define REG_UDC_EP0InMPSR REG32(UDC_EP0InMPSR) ++#define REG_UDC_EP0InDesR REG32(UDC_EP0InDesR) ++#define REG_UDC_EP1InCR REG32(UDC_EP1InCR) ++#define REG_UDC_EP1InSR REG32(UDC_EP1InSR) ++#define REG_UDC_EP1InBSR REG32(UDC_EP1InBSR) ++#define REG_UDC_EP1InMPSR REG32(UDC_EP1InMPSR) ++#define REG_UDC_EP1InDesR REG32(UDC_EP1InDesR) ++#define REG_UDC_EP2InCR REG32(UDC_EP2InCR) ++#define REG_UDC_EP2InSR REG32(UDC_EP2InSR) ++#define REG_UDC_EP2InBSR REG32(UDC_EP2InBSR) ++#define REG_UDC_EP2InMPSR REG32(UDC_EP2InMPSR) ++#define REG_UDC_EP2InDesR REG32(UDC_EP2InDesR) ++#define REG_UDC_EP3InCR REG32(UDC_EP3InCR) ++#define REG_UDC_EP3InSR REG32(UDC_EP3InSR) ++#define REG_UDC_EP3InBSR REG32(UDC_EP3InBSR) ++#define REG_UDC_EP3InMPSR REG32(UDC_EP3InMPSR) ++#define REG_UDC_EP3InDesR REG32(UDC_EP3InDesR) ++#define REG_UDC_EP4InCR REG32(UDC_EP4InCR) ++#define REG_UDC_EP4InSR REG32(UDC_EP4InSR) ++#define REG_UDC_EP4InBSR REG32(UDC_EP4InBSR) ++#define REG_UDC_EP4InMPSR REG32(UDC_EP4InMPSR) ++#define REG_UDC_EP4InDesR REG32(UDC_EP4InDesR) ++ ++#define REG_UDC_EP0OutCR REG32(UDC_EP0OutCR) ++#define REG_UDC_EP0OutSR REG32(UDC_EP0OutSR) ++#define REG_UDC_EP0OutPFNR REG32(UDC_EP0OutPFNR) ++#define REG_UDC_EP0OutMPSR REG32(UDC_EP0OutMPSR) ++#define REG_UDC_EP0OutSBPR REG32(UDC_EP0OutSBPR) ++#define REG_UDC_EP0OutDesR REG32(UDC_EP0OutDesR) ++#define REG_UDC_EP5OutCR REG32(UDC_EP5OutCR) ++#define REG_UDC_EP5OutSR REG32(UDC_EP5OutSR) ++#define REG_UDC_EP5OutPFNR REG32(UDC_EP5OutPFNR) ++#define REG_UDC_EP5OutMPSR REG32(UDC_EP5OutMPSR) ++#define REG_UDC_EP5OutDesR REG32(UDC_EP5OutDesR) ++#define REG_UDC_EP6OutCR REG32(UDC_EP6OutCR) ++#define REG_UDC_EP6OutSR REG32(UDC_EP6OutSR) ++#define REG_UDC_EP6OutPFNR REG32(UDC_EP6OutPFNR) ++#define REG_UDC_EP6OutMPSR REG32(UDC_EP6OutMPSR) ++#define REG_UDC_EP6OutDesR REG32(UDC_EP6OutDesR) ++#define REG_UDC_EP7OutCR REG32(UDC_EP7OutCR) ++#define REG_UDC_EP7OutSR REG32(UDC_EP7OutSR) ++#define REG_UDC_EP7OutPFNR REG32(UDC_EP7OutPFNR) ++#define REG_UDC_EP7OutMPSR REG32(UDC_EP7OutMPSR) ++#define REG_UDC_EP7OutDesR REG32(UDC_EP7OutDesR) ++ ++#define REG_UDC_DevCFGR REG32(UDC_DevCFGR) ++#define REG_UDC_DevCR REG32(UDC_DevCR) ++#define REG_UDC_DevSR REG32(UDC_DevSR) ++#define REG_UDC_DevIntR REG32(UDC_DevIntR) ++#define REG_UDC_DevIntMR REG32(UDC_DevIntMR) ++#define REG_UDC_EPIntR REG32(UDC_EPIntR) ++#define REG_UDC_EPIntMR REG32(UDC_EPIntMR) ++ ++#define REG_UDC_STCMAR REG32(UDC_STCMAR) ++#define REG_UDC_EP0InfR REG32(UDC_EP0InfR) ++#define REG_UDC_EP1InfR REG32(UDC_EP1InfR) ++#define REG_UDC_EP2InfR REG32(UDC_EP2InfR) ++#define REG_UDC_EP3InfR REG32(UDC_EP3InfR) ++#define REG_UDC_EP4InfR REG32(UDC_EP4InfR) ++#define REG_UDC_EP5InfR REG32(UDC_EP5InfR) ++#define REG_UDC_EP6InfR REG32(UDC_EP6InfR) ++#define REG_UDC_EP7InfR REG32(UDC_EP7InfR) ++ ++#define UDC_DevCFGR_PI (1 << 5) ++#define UDC_DevCFGR_SS (1 << 4) ++#define UDC_DevCFGR_SP (1 << 3) ++#define UDC_DevCFGR_RW (1 << 2) ++#define UDC_DevCFGR_SPD_BIT 0 ++#define UDC_DevCFGR_SPD_MASK (0x03 << UDC_DevCFGR_SPD_BIT) ++ #define UDC_DevCFGR_SPD_HS (0 << UDC_DevCFGR_SPD_BIT) ++ #define UDC_DevCFGR_SPD_LS (2 << UDC_DevCFGR_SPD_BIT) ++ #define UDC_DevCFGR_SPD_FS (3 << UDC_DevCFGR_SPD_BIT) ++ ++#define UDC_DevCR_DM (1 << 9) ++#define UDC_DevCR_BE (1 << 5) ++#define UDC_DevCR_RES (1 << 0) ++ ++#define UDC_DevSR_ENUMSPD_BIT 13 ++#define UDC_DevSR_ENUMSPD_MASK (0x03 << UDC_DevSR_ENUMSPD_BIT) ++ #define UDC_DevSR_ENUMSPD_HS (0 << UDC_DevSR_ENUMSPD_BIT) ++ #define UDC_DevSR_ENUMSPD_LS (2 << UDC_DevSR_ENUMSPD_BIT) ++ #define UDC_DevSR_ENUMSPD_FS (3 << UDC_DevSR_ENUMSPD_BIT) ++#define UDC_DevSR_SUSP (1 << 12) ++#define UDC_DevSR_ALT_BIT 8 ++#define UDC_DevSR_ALT_MASK (0x0f << UDC_DevSR_ALT_BIT) ++#define UDC_DevSR_INTF_BIT 4 ++#define UDC_DevSR_INTF_MASK (0x0f << UDC_DevSR_INTF_BIT) ++#define UDC_DevSR_CFG_BIT 0 ++#define UDC_DevSR_CFG_MASK (0x0f << UDC_DevSR_CFG_BIT) ++ ++#define UDC_DevIntR_ENUM (1 << 6) ++#define UDC_DevIntR_SOF (1 << 5) ++#define UDC_DevIntR_US (1 << 4) ++#define UDC_DevIntR_UR (1 << 3) ++#define UDC_DevIntR_SI (1 << 1) ++#define UDC_DevIntR_SC (1 << 0) ++ ++#define UDC_EPIntR_OUTEP_BIT 16 ++#define UDC_EPIntR_OUTEP_MASK (0xffff << UDC_EPIntR_OUTEP_BIT) ++#define UDC_EPIntR_OUTEP0 0x00010000 ++#define UDC_EPIntR_OUTEP5 0x00200000 ++#define UDC_EPIntR_OUTEP6 0x00400000 ++#define UDC_EPIntR_OUTEP7 0x00800000 ++#define UDC_EPIntR_INEP_BIT 0 ++#define UDC_EPIntR_INEP_MASK (0xffff << UDC_EPIntR_INEP_BIT) ++#define UDC_EPIntR_INEP0 0x00000001 ++#define UDC_EPIntR_INEP1 0x00000002 ++#define UDC_EPIntR_INEP2 0x00000004 ++#define UDC_EPIntR_INEP3 0x00000008 ++#define UDC_EPIntR_INEP4 0x00000010 ++ ++ ++#define UDC_EPIntMR_OUTEP_BIT 16 ++#define UDC_EPIntMR_OUTEP_MASK (0xffff << UDC_EPIntMR_OUTEP_BIT) ++#define UDC_EPIntMR_INEP_BIT 0 ++#define UDC_EPIntMR_INEP_MASK (0xffff << UDC_EPIntMR_INEP_BIT) ++ ++#define UDC_EPCR_ET_BIT 4 ++#define UDC_EPCR_ET_MASK (0x03 << UDC_EPCR_ET_BIT) ++ #define UDC_EPCR_ET_CTRL (0 << UDC_EPCR_ET_BIT) ++ #define UDC_EPCR_ET_ISO (1 << UDC_EPCR_ET_BIT) ++ #define UDC_EPCR_ET_BULK (2 << UDC_EPCR_ET_BIT) ++ #define UDC_EPCR_ET_INTR (3 << UDC_EPCR_ET_BIT) ++#define UDC_EPCR_SN (1 << 2) ++#define UDC_EPCR_F (1 << 1) ++#define UDC_EPCR_S (1 << 0) ++ ++#define UDC_EPSR_RXPKTSIZE_BIT 11 ++#define UDC_EPSR_RXPKTSIZE_MASK (0x7ff << UDC_EPSR_RXPKTSIZE_BIT) ++#define UDC_EPSR_IN (1 << 6) ++#define UDC_EPSR_OUT_BIT 4 ++#define UDC_EPSR_OUT_MASK (0x03 << UDC_EPSR_OUT_BIT) ++ #define UDC_EPSR_OUT_NONE (0 << UDC_EPSR_OUT_BIT) ++ #define UDC_EPSR_OUT_RCVDATA (1 << UDC_EPSR_OUT_BIT) ++ #define UDC_EPSR_OUT_RCVSETUP (2 << UDC_EPSR_OUT_BIT) ++#define UDC_EPSR_PID_BIT 0 ++#define UDC_EPSR_PID_MASK (0x0f << UDC_EPSR_PID_BIT) ++ ++#define UDC_EPInfR_MPS_BIT 19 ++#define UDC_EPInfR_MPS_MASK (0x3ff << UDC_EPInfR_MPS_BIT) ++#define UDC_EPInfR_ALTS_BIT 15 ++#define UDC_EPInfR_ALTS_MASK (0x0f << UDC_EPInfR_ALTS_BIT) ++#define UDC_EPInfR_IFN_BIT 11 ++#define UDC_EPInfR_IFN_MASK (0x0f << UDC_EPInfR_IFN_BIT) ++#define UDC_EPInfR_CGN_BIT 7 ++#define UDC_EPInfR_CGN_MASK (0x0f << UDC_EPInfR_CGN_BIT) ++#define UDC_EPInfR_EPT_BIT 5 ++#define UDC_EPInfR_EPT_MASK (0x03 << UDC_EPInfR_EPT_BIT) ++ #define UDC_EPInfR_EPT_CTRL (0 << UDC_EPInfR_EPT_BIT) ++ #define UDC_EPInfR_EPT_ISO (1 << UDC_EPInfR_EPT_BIT) ++ #define UDC_EPInfR_EPT_BULK (2 << UDC_EPInfR_EPT_BIT) ++ #define UDC_EPInfR_EPT_INTR (3 << UDC_EPInfR_EPT_BIT) ++#define UDC_EPInfR_EPD (1 << 4) ++ #define UDC_EPInfR_EPD_OUT (0 << 4) ++ #define UDC_EPInfR_EPD_IN (1 << 4) ++ ++#define UDC_EPInfR_EPN_BIT 0 ++#define UDC_EPInfR_EPN_MASK (0xf << UDC_EPInfR_EPN_BIT) ++ ++ ++ ++ ++/************************************************************************* ++ * DMAC ++ *************************************************************************/ ++#define DMAC_DSAR(n) (DMAC_BASE + (0x00 + (n) * 0x20)) ++#define DMAC_DDAR(n) (DMAC_BASE + (0x04 + (n) * 0x20)) ++#define DMAC_DTCR(n) (DMAC_BASE + (0x08 + (n) * 0x20)) ++#define DMAC_DRSR(n) (DMAC_BASE + (0x0c + (n) * 0x20)) ++#define DMAC_DCCSR(n) (DMAC_BASE + (0x10 + (n) * 0x20)) ++#define DMAC_DMAIPR (DMAC_BASE + 0xf8) ++#define DMAC_DMACR (DMAC_BASE + 0xfc) ++ ++#define REG_DMAC_DSAR(n) REG32(DMAC_DSAR((n))) ++#define REG_DMAC_DDAR(n) REG32(DMAC_DDAR((n))) ++#define REG_DMAC_DTCR(n) REG32(DMAC_DTCR((n))) ++#define REG_DMAC_DRSR(n) REG32(DMAC_DRSR((n))) ++#define REG_DMAC_DCCSR(n) REG32(DMAC_DCCSR((n))) ++#define REG_DMAC_DMAIPR REG32(DMAC_DMAIPR) ++#define REG_DMAC_DMACR REG32(DMAC_DMACR) ++ ++#define DMAC_DRSR_RS_BIT 0 ++#define DMAC_DRSR_RS_MASK (0x1f << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_EXTREXTR (0 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_PCMCIAOUT (4 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_PCMCIAIN (5 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_AUTO (8 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_DESOUT (10 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_DESIN (11 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_UART3OUT (14 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_UART3IN (15 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_UART2OUT (16 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_UART2IN (17 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_UART1OUT (18 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_UART1IN (19 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_UART0OUT (20 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_UART0IN (21 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_SSIOUT (22 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_SSIIN (23 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_AICOUT (24 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_AICIN (25 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_MSCOUT (26 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_MSCIN (27 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_OST2 (28 << DMAC_DRSR_RS_BIT) ++ ++#define DMAC_DCCSR_EACKS (1 << 31) ++#define DMAC_DCCSR_EACKM (1 << 30) ++#define DMAC_DCCSR_ERDM_BIT 28 ++#define DMAC_DCCSR_ERDM_MASK (0x03 << DMAC_DCCSR_ERDM_BIT) ++ #define DMAC_DCCSR_ERDM_LLEVEL (0 << DMAC_DCCSR_ERDM_BIT) ++ #define DMAC_DCCSR_ERDM_FEDGE (1 << DMAC_DCCSR_ERDM_BIT) ++ #define DMAC_DCCSR_ERDM_HLEVEL (2 << DMAC_DCCSR_ERDM_BIT) ++ #define DMAC_DCCSR_ERDM_REDGE (3 << DMAC_DCCSR_ERDM_BIT) ++#define DMAC_DCCSR_EOPM (1 << 27) ++#define DMAC_DCCSR_SAM (1 << 23) ++#define DMAC_DCCSR_DAM (1 << 22) ++#define DMAC_DCCSR_RDIL_BIT 16 ++#define DMAC_DCCSR_RDIL_MASK (0x0f << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_IGN (0 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_2 (1 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_4 (2 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_8 (3 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_12 (4 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_16 (5 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_20 (6 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_24 (7 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_28 (8 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_32 (9 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_48 (10 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_60 (11 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_64 (12 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_124 (13 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_128 (14 << DMAC_DCCSR_RDIL_BIT) ++ #define DMAC_DCCSR_RDIL_200 (15 << DMAC_DCCSR_RDIL_BIT) ++#define DMAC_DCCSR_SWDH_BIT 14 ++#define DMAC_DCCSR_SWDH_MASK (0x03 << DMAC_DCCSR_SWDH_BIT) ++ #define DMAC_DCCSR_SWDH_32 (0 << DMAC_DCCSR_SWDH_BIT) ++ #define DMAC_DCCSR_SWDH_8 (1 << DMAC_DCCSR_SWDH_BIT) ++ #define DMAC_DCCSR_SWDH_16 (2 << DMAC_DCCSR_SWDH_BIT) ++#define DMAC_DCCSR_DWDH_BIT 12 ++#define DMAC_DCCSR_DWDH_MASK (0x03 << DMAC_DCCSR_DWDH_BIT) ++ #define DMAC_DCCSR_DWDH_32 (0 << DMAC_DCCSR_DWDH_BIT) ++ #define DMAC_DCCSR_DWDH_8 (1 << DMAC_DCCSR_DWDH_BIT) ++ #define DMAC_DCCSR_DWDH_16 (2 << DMAC_DCCSR_DWDH_BIT) ++#define DMAC_DCCSR_DS_BIT 8 ++#define DMAC_DCCSR_DS_MASK (0x07 << DMAC_DCCSR_DS_BIT) ++ #define DMAC_DCCSR_DS_32b (0 << DMAC_DCCSR_DS_BIT) ++ #define DMAC_DCCSR_DS_8b (1 << DMAC_DCCSR_DS_BIT) ++ #define DMAC_DCCSR_DS_16b (2 << DMAC_DCCSR_DS_BIT) ++ #define DMAC_DCCSR_DS_16B (3 << DMAC_DCCSR_DS_BIT) ++ #define DMAC_DCCSR_DS_32B (4 << DMAC_DCCSR_DS_BIT) ++#define DMAC_DCCSR_TM (1 << 7) ++#define DMAC_DCCSR_AR (1 << 4) ++#define DMAC_DCCSR_TC (1 << 3) ++#define DMAC_DCCSR_HLT (1 << 2) ++#define DMAC_DCCSR_TCIE (1 << 1) ++#define DMAC_DCCSR_CHDE (1 << 0) ++ ++#define DMAC_DMAIPR_CINT_BIT 8 ++#define DMAC_DMAIPR_CINT_MASK (0xff << DMAC_DMAIPR_CINT_BIT) ++ ++#define DMAC_DMACR_PR_BIT 8 ++#define DMAC_DMACR_PR_MASK (0x03 << DMAC_DMACR_PR_BIT) ++ #define DMAC_DMACR_PR_01234567 (0 << DMAC_DMACR_PR_BIT) ++ #define DMAC_DMACR_PR_02314675 (1 << DMAC_DMACR_PR_BIT) ++ #define DMAC_DMACR_PR_20136457 (2 << DMAC_DMACR_PR_BIT) ++ #define DMAC_DMACR_PR_ROUNDROBIN (3 << DMAC_DMACR_PR_BIT) ++#define DMAC_DMACR_HTR (1 << 3) ++#define DMAC_DMACR_AER (1 << 2) ++#define DMAC_DMACR_DME (1 << 0) ++ ++#define IRQ_DMA_0 32 ++#define NUM_DMA 6 ++ ++#define DMAC_DSAR0 DMAC_DSAR(0) ++#define DMAC_DDAR0 DMAC_DDAR(0) ++#define DMAC_DTCR0 DMAC_DTCR(0) ++#define DMAC_DRSR0 DMAC_DRSR(0) ++#define DMAC_DCCSR0 DMAC_DCCSR(0) ++ ++#define DMAC_DSAR1 DMAC_DSAR(1) ++#define DMAC_DDAR1 DMAC_DDAR(1) ++#define DMAC_DTCR1 DMAC_DTCR(1) ++#define DMAC_DRSR1 DMAC_DRSR(1) ++#define DMAC_DCCSR1 DMAC_DCCSR(1) ++ ++#define DMAC_DSAR2 DMAC_DSAR(2) ++#define DMAC_DDAR2 DMAC_DDAR(2) ++#define DMAC_DTCR2 DMAC_DTCR(2) ++#define DMAC_DRSR2 DMAC_DRSR(2) ++#define DMAC_DCCSR2 DMAC_DCCSR(2) ++ ++#define DMAC_DSAR3 DMAC_DSAR(3) ++#define DMAC_DDAR3 DMAC_DDAR(3) ++#define DMAC_DTCR3 DMAC_DTCR(3) ++#define DMAC_DRSR3 DMAC_DRSR(3) ++#define DMAC_DCCSR3 DMAC_DCCSR(3) ++ ++#define DMAC_DSAR4 DMAC_DSAR(4) ++#define DMAC_DDAR4 DMAC_DDAR(4) ++#define DMAC_DTCR4 DMAC_DTCR(4) ++#define DMAC_DRSR4 DMAC_DRSR(4) ++#define DMAC_DCCSR4 DMAC_DCCSR(4) ++ ++#define DMAC_DSAR5 DMAC_DSAR(5) ++#define DMAC_DDAR5 DMAC_DDAR(5) ++#define DMAC_DTCR5 DMAC_DTCR(5) ++#define DMAC_DRSR5 DMAC_DRSR(5) ++#define DMAC_DCCSR5 DMAC_DCCSR(5) ++ ++#define DMAC_DSAR6 DMAC_DSAR(6) ++#define DMAC_DDAR6 DMAC_DDAR(6) ++#define DMAC_DTCR6 DMAC_DTCR(6) ++#define DMAC_DRSR6 DMAC_DRSR(6) ++#define DMAC_DCCSR6 DMAC_DCCSR(6) ++ ++#define DMAC_DSAR7 DMAC_DSAR(7) ++#define DMAC_DDAR7 DMAC_DDAR(7) ++#define DMAC_DTCR7 DMAC_DTCR(7) ++#define DMAC_DRSR7 DMAC_DRSR(7) ++#define DMAC_DCCSR7 DMAC_DCCSR(7) ++ ++ ++ ++/************************************************************************* ++ * AIC ++ *************************************************************************/ ++#define AIC_FR (AIC_BASE + 0x000) ++#define AIC_CR (AIC_BASE + 0x004) ++#define AIC_ACCR1 (AIC_BASE + 0x008) ++#define AIC_ACCR2 (AIC_BASE + 0x00C) ++#define AIC_I2SCR (AIC_BASE + 0x010) ++#define AIC_SR (AIC_BASE + 0x014) ++#define AIC_ACSR (AIC_BASE + 0x018) ++#define AIC_I2SSR (AIC_BASE + 0x01C) ++#define AIC_ACCAR (AIC_BASE + 0x020) ++#define AIC_ACCDR (AIC_BASE + 0x024) ++#define AIC_ACSAR (AIC_BASE + 0x028) ++#define AIC_ACSDR (AIC_BASE + 0x02C) ++#define AIC_I2SDIV (AIC_BASE + 0x030) ++#define AIC_DR (AIC_BASE + 0x034) ++ ++#define REG_AIC_FR REG32(AIC_FR) ++#define REG_AIC_CR REG32(AIC_CR) ++#define REG_AIC_ACCR1 REG32(AIC_ACCR1) ++#define REG_AIC_ACCR2 REG32(AIC_ACCR2) ++#define REG_AIC_I2SCR REG32(AIC_I2SCR) ++#define REG_AIC_SR REG32(AIC_SR) ++#define REG_AIC_ACSR REG32(AIC_ACSR) ++#define REG_AIC_I2SSR REG32(AIC_I2SSR) ++#define REG_AIC_ACCAR REG32(AIC_ACCAR) ++#define REG_AIC_ACCDR REG32(AIC_ACCDR) ++#define REG_AIC_ACSAR REG32(AIC_ACSAR) ++#define REG_AIC_ACSDR REG32(AIC_ACSDR) ++#define REG_AIC_I2SDIV REG32(AIC_I2SDIV) ++#define REG_AIC_DR REG32(AIC_DR) ++ ++/* AIC Controller Configuration Register (AIC_FR) */ ++ ++#define AIC_FR_RFTH_BIT 12 ++#define AIC_FR_RFTH_MASK (0xf << AIC_FR_RFTH_BIT) ++#define AIC_FR_TFTH_BIT 8 ++#define AIC_FR_TFTH_MASK (0xf << AIC_FR_TFTH_BIT) ++#define AIC_FR_AUSEL (1 << 4) ++#define AIC_FR_RST (1 << 3) ++#define AIC_FR_BCKD (1 << 2) ++#define AIC_FR_SYNCD (1 << 1) ++#define AIC_FR_ENB (1 << 0) ++ ++/* AIC Controller Common Control Register (AIC_CR) */ ++ ++#define AIC_CR_RDMS (1 << 15) ++#define AIC_CR_TDMS (1 << 14) ++#define AIC_CR_FLUSH (1 << 8) ++#define AIC_CR_EROR (1 << 6) ++#define AIC_CR_ETUR (1 << 5) ++#define AIC_CR_ERFS (1 << 4) ++#define AIC_CR_ETFS (1 << 3) ++#define AIC_CR_ENLBF (1 << 2) ++#define AIC_CR_ERPL (1 << 1) ++#define AIC_CR_EREC (1 << 0) ++ ++/* AIC Controller AC-link Control Register 1 (AIC_ACCR1) */ ++ ++#define AIC_ACCR1_RS_BIT 16 ++#define AIC_ACCR1_RS_MASK (0x3ff << AIC_ACCR1_RS_BIT) ++ #define AIC_ACCR1_RS_SLOT12 (1 << 25) /* Slot 12 valid bit */ ++ #define AIC_ACCR1_RS_SLOT11 (1 << 24) /* Slot 11 valid bit */ ++ #define AIC_ACCR1_RS_SLOT10 (1 << 23) /* Slot 10 valid bit */ ++ #define AIC_ACCR1_RS_SLOT9 (1 << 22) /* Slot 9 valid bit */ ++ #define AIC_ACCR1_RS_SLOT8 (1 << 21) /* Slot 8 valid bit */ ++ #define AIC_ACCR1_RS_SLOT7 (1 << 20) /* Slot 7 valid bit */ ++ #define AIC_ACCR1_RS_SLOT6 (1 << 19) /* Slot 6 valid bit */ ++ #define AIC_ACCR1_RS_SLOT5 (1 << 18) /* Slot 5 valid bit */ ++ #define AIC_ACCR1_RS_SLOT4 (1 << 17) /* Slot 4 valid bit */ ++ #define AIC_ACCR1_RS_SLOT3 (1 << 16) /* Slot 3 valid bit */ ++#define AIC_ACCR1_XS_BIT 0 ++#define AIC_ACCR1_XS_MASK (0x3ff << AIC_ACCR1_XS_BIT) ++ #define AIC_ACCR1_XS_SLOT12 (1 << 9) /* Slot 12 valid bit */ ++ #define AIC_ACCR1_XS_SLOT11 (1 << 8) /* Slot 11 valid bit */ ++ #define AIC_ACCR1_XS_SLOT10 (1 << 7) /* Slot 10 valid bit */ ++ #define AIC_ACCR1_XS_SLOT9 (1 << 6) /* Slot 9 valid bit */ ++ #define AIC_ACCR1_XS_SLOT8 (1 << 5) /* Slot 8 valid bit */ ++ #define AIC_ACCR1_XS_SLOT7 (1 << 4) /* Slot 7 valid bit */ ++ #define AIC_ACCR1_XS_SLOT6 (1 << 3) /* Slot 6 valid bit */ ++ #define AIC_ACCR1_XS_SLOT5 (1 << 2) /* Slot 5 valid bit */ ++ #define AIC_ACCR1_XS_SLOT4 (1 << 1) /* Slot 4 valid bit */ ++ #define AIC_ACCR1_XS_SLOT3 (1 << 0) /* Slot 3 valid bit */ ++ ++/* AIC Controller AC-link Control Register 2 (AIC_ACCR2) */ ++ ++#define AIC_ACCR2_ERSTO (1 << 18) ++#define AIC_ACCR2_ESADR (1 << 17) ++#define AIC_ACCR2_ECADT (1 << 16) ++#define AIC_ACCR2_OASS_BIT 8 ++#define AIC_ACCR2_OASS_MASK (0x3 << AIC_ACCR2_OASS_BIT) ++ #define AIC_ACCR2_OASS_20BIT (0 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 20-bit */ ++ #define AIC_ACCR2_OASS_18BIT (1 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 18-bit */ ++ #define AIC_ACCR2_OASS_16BIT (2 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 16-bit */ ++ #define AIC_ACCR2_OASS_8BIT (3 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 8-bit */ ++#define AIC_ACCR2_IASS_BIT 6 ++#define AIC_ACCR2_IASS_MASK (0x3 << AIC_ACCR2_IASS_BIT) ++ #define AIC_ACCR2_IASS_20BIT (0 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 20-bit */ ++ #define AIC_ACCR2_IASS_18BIT (1 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 18-bit */ ++ #define AIC_ACCR2_IASS_16BIT (2 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 16-bit */ ++ #define AIC_ACCR2_IASS_8BIT (3 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 8-bit */ ++#define AIC_ACCR2_SO (1 << 3) ++#define AIC_ACCR2_SR (1 << 2) ++#define AIC_ACCR2_SS (1 << 1) ++#define AIC_ACCR2_SA (1 << 0) ++ ++/* AIC Controller I2S/MSB-justified Control Register (AIC_I2SCR) */ ++ ++#define AIC_I2SCR_STPBK (1 << 12) ++#define AIC_I2SCR_WL_BIT 1 ++#define AIC_I2SCR_WL_MASK (0x7 << AIC_I2SCR_WL_BIT) ++ #define AIC_I2SCR_WL_24BIT (0 << AIC_I2SCR_WL_BIT) /* Word Length is 24 bit */ ++ #define AIC_I2SCR_WL_20BIT (1 << AIC_I2SCR_WL_BIT) /* Word Length is 20 bit */ ++ #define AIC_I2SCR_WL_18BIT (2 << AIC_I2SCR_WL_BIT) /* Word Length is 18 bit */ ++ #define AIC_I2SCR_WL_16BIT (3 << AIC_I2SCR_WL_BIT) /* Word Length is 16 bit */ ++ #define AIC_I2SCR_WL_8BIT (4 << AIC_I2SCR_WL_BIT) /* Word Length is 8 bit */ ++#define AIC_I2SCR_AMSL (1 << 0) ++ ++/* AIC Controller FIFO Status Register (AIC_SR) */ ++ ++#define AIC_SR_RFL_BIT 24 ++#define AIC_SR_RFL_MASK (0x1f << AIC_SR_RFL_BIT) ++#define AIC_SR_TFL_BIT 8 ++#define AIC_SR_TFL_MASK (0x1f << AIC_SR_TFL_BIT) ++#define AIC_SR_ROR (1 << 6) ++#define AIC_SR_TUR (1 << 5) ++#define AIC_SR_RFS (1 << 4) ++#define AIC_SR_TFS (1 << 3) ++ ++/* AIC Controller AC-link Status Register (AIC_ACSR) */ ++ ++#define AIC_ACSR_CRDY (1 << 20) ++#define AIC_ACSR_CLPM (1 << 19) ++#define AIC_ACSR_RSTO (1 << 18) ++#define AIC_ACSR_SADR (1 << 17) ++#define AIC_ACSR_CADT (1 << 16) ++ ++/* AIC Controller I2S/MSB-justified Status Register (AIC_I2SSR) */ ++ ++#define AIC_I2SSR_BSY (1 << 2) ++ ++/* AIC Controller AC97 codec Command Address Register (AIC_ACCAR) */ ++ ++#define AIC_ACCAR_CAR_BIT 0 ++#define AIC_ACCAR_CAR_MASK (0xfffff << AIC_ACCAR_CAR_BIT) ++ ++/* AIC Controller AC97 codec Command Data Register (AIC_ACCDR) */ ++ ++#define AIC_ACCDR_CDR_BIT 0 ++#define AIC_ACCDR_CDR_MASK (0xfffff << AIC_ACCDR_CDR_BIT) ++ ++/* AIC Controller AC97 codec Status Address Register (AIC_ACSAR) */ ++ ++#define AIC_ACSAR_SAR_BIT 0 ++#define AIC_ACSAR_SAR_MASK (0xfffff << AIC_ACSAR_SAR_BIT) ++ ++/* AIC Controller AC97 codec Status Data Register (AIC_ACSDR) */ ++ ++#define AIC_ACSDR_SDR_BIT 0 ++#define AIC_ACSDR_SDR_MASK (0xfffff << AIC_ACSDR_SDR_BIT) ++ ++/* AIC Controller I2S/MSB-justified Clock Divider Register (AIC_I2SDIV) */ ++ ++#define AIC_I2SDIV_DIV_BIT 0 ++#define AIC_I2SDIV_DIV_MASK (0x7f << AIC_I2SDIV_DIV_BIT) ++ #define AIC_I2SDIV_BITCLK_3072KHZ (0x0C << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 3.072MHz */ ++ #define AIC_I2SDIV_BITCLK_2836KHZ (0x0D << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 2.836MHz */ ++ #define AIC_I2SDIV_BITCLK_1418KHZ (0x1A << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.418MHz */ ++ #define AIC_I2SDIV_BITCLK_1024KHZ (0x24 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.024MHz */ ++ #define AIC_I2SDIV_BITCLK_7089KHZ (0x34 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 708.92KHz */ ++ #define AIC_I2SDIV_BITCLK_512KHZ (0x48 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 512.00KHz */ ++ ++ ++ ++ ++/************************************************************************* ++ * LCD ++ *************************************************************************/ ++#define LCD_CFG (LCD_BASE + 0x00) ++#define LCD_VSYNC (LCD_BASE + 0x04) ++#define LCD_HSYNC (LCD_BASE + 0x08) ++#define LCD_VAT (LCD_BASE + 0x0c) ++#define LCD_DAH (LCD_BASE + 0x10) ++#define LCD_DAV (LCD_BASE + 0x14) ++#define LCD_PS (LCD_BASE + 0x18) ++#define LCD_CLS (LCD_BASE + 0x1c) ++#define LCD_SPL (LCD_BASE + 0x20) ++#define LCD_REV (LCD_BASE + 0x24) ++#define LCD_CTRL (LCD_BASE + 0x30) ++#define LCD_STATE (LCD_BASE + 0x34) ++#define LCD_IID (LCD_BASE + 0x38) ++#define LCD_DA0 (LCD_BASE + 0x40) ++#define LCD_SA0 (LCD_BASE + 0x44) ++#define LCD_FID0 (LCD_BASE + 0x48) ++#define LCD_CMD0 (LCD_BASE + 0x4c) ++#define LCD_DA1 (LCD_BASE + 0x50) ++#define LCD_SA1 (LCD_BASE + 0x54) ++#define LCD_FID1 (LCD_BASE + 0x58) ++#define LCD_CMD1 (LCD_BASE + 0x5c) ++ ++#define REG_LCD_CFG REG32(LCD_CFG) ++#define REG_LCD_VSYNC REG32(LCD_VSYNC) ++#define REG_LCD_HSYNC REG32(LCD_HSYNC) ++#define REG_LCD_VAT REG32(LCD_VAT) ++#define REG_LCD_DAH REG32(LCD_DAH) ++#define REG_LCD_DAV REG32(LCD_DAV) ++#define REG_LCD_PS REG32(LCD_PS) ++#define REG_LCD_CLS REG32(LCD_CLS) ++#define REG_LCD_SPL REG32(LCD_SPL) ++#define REG_LCD_REV REG32(LCD_REV) ++#define REG_LCD_CTRL REG32(LCD_CTRL) ++#define REG_LCD_STATE REG32(LCD_STATE) ++#define REG_LCD_IID REG32(LCD_IID) ++#define REG_LCD_DA0 REG32(LCD_DA0) ++#define REG_LCD_SA0 REG32(LCD_SA0) ++#define REG_LCD_FID0 REG32(LCD_FID0) ++#define REG_LCD_CMD0 REG32(LCD_CMD0) ++#define REG_LCD_DA1 REG32(LCD_DA1) ++#define REG_LCD_SA1 REG32(LCD_SA1) ++#define REG_LCD_FID1 REG32(LCD_FID1) ++#define REG_LCD_CMD1 REG32(LCD_CMD1) ++ ++#define LCD_CFG_PDW_BIT 4 ++#define LCD_CFG_PDW_MASK (0x03 << LCD_DEV_PDW_BIT) ++ #define LCD_CFG_PDW_1 (0 << LCD_DEV_PDW_BIT) ++ #define LCD_CFG_PDW_2 (1 << LCD_DEV_PDW_BIT) ++ #define LCD_CFG_PDW_4 (2 << LCD_DEV_PDW_BIT) ++ #define LCD_CFG_PDW_8 (3 << LCD_DEV_PDW_BIT) ++#define LCD_CFG_MODE_BIT 0 ++#define LCD_CFG_MODE_MASK (0x0f << LCD_DEV_MODE_BIT) ++ #define LCD_CFG_MODE_GENERIC_TFT (0 << LCD_DEV_MODE_BIT) ++ #define LCD_CFG_MODE_SHARP_HR (1 << LCD_DEV_MODE_BIT) ++ #define LCD_CFG_MODE_CASIO_TFT (2 << LCD_DEV_MODE_BIT) ++ #define LCD_CFG_MODE_SAMSUNG_ALPHA (3 << LCD_DEV_MODE_BIT) ++ #define LCD_CFG_MODE_NONINTER_CCIR656 (4 << LCD_DEV_MODE_BIT) ++ #define LCD_CFG_MODE_INTER_CCIR656 (6 << LCD_DEV_MODE_BIT) ++ #define LCD_CFG_MODE_SINGLE_CSTN (8 << LCD_DEV_MODE_BIT) ++ #define LCD_CFG_MODE_SINGLE_MSTN (9 << LCD_DEV_MODE_BIT) ++ #define LCD_CFG_MODE_DUAL_CSTN (10 << LCD_DEV_MODE_BIT) ++ #define LCD_CFG_MODE_DUAL_MSTN (11 << LCD_DEV_MODE_BIT) ++ ++#define LCD_VSYNC_VPS_BIT 16 ++#define LCD_VSYNC_VPS_MASK (0xffff << LCD_VSYNC_VPS_BIT) ++#define LCD_VSYNC_VPE_BIT 0 ++#define LCD_VSYNC_VPE_MASK (0xffff << LCD_VSYNC_VPS_BIT) ++ ++#define LCD_HSYNC_HPS_BIT 16 ++#define LCD_HSYNC_HPS_MASK (0xffff << LCD_HSYNC_HPS_BIT) ++#define LCD_HSYNC_HPE_BIT 0 ++#define LCD_HSYNC_HPE_MASK (0xffff << LCD_HSYNC_HPE_BIT) ++ ++#define LCD_VAT_HT_BIT 16 ++#define LCD_VAT_HT_MASK (0xffff << LCD_VAT_HT_BIT) ++#define LCD_VAT_VT_BIT 0 ++#define LCD_VAT_VT_MASK (0xffff << LCD_VAT_VT_BIT) ++ ++#define LCD_DAH_HDS_BIT 16 ++#define LCD_DAH_HDS_MASK (0xffff << LCD_DAH_HDS_BIT) ++#define LCD_DAH_HDE_BIT 0 ++#define LCD_DAH_HDE_MASK (0xffff << LCD_DAH_HDE_BIT) ++ ++#define LCD_DAV_VDS_BIT 16 ++#define LCD_DAV_VDS_MASK (0xffff << LCD_DAV_VDS_BIT) ++#define LCD_DAV_VDE_BIT 0 ++#define LCD_DAV_VDE_MASK (0xffff << LCD_DAV_VDE_BIT) ++ ++#define LCD_CTRL_BST_BIT 28 ++#define LCD_CTRL_BST_MASK (0x03 << LCD_CTRL_BST_BIT) ++ #define LCD_CTRL_BST_4 (0 << LCD_CTRL_BST_BIT) ++ #define LCD_CTRL_BST_8 (1 << LCD_CTRL_BST_BIT) ++ #define LCD_CTRL_BST_16 (2 << LCD_CTRL_BST_BIT) ++#define LCD_CTRL_RGB555 (1 << 27) ++#define LCD_CTRL_OFUP (1 << 26) ++#define LCD_CTRL_FRC_BIT 24 ++#define LCD_CTRL_FRC_MASK (0x03 << LCD_CTRL_FRC_BIT) ++ #define LCD_CTRL_FRC_16 (0 << LCD_CTRL_FRC_BIT) ++ #define LCD_CTRL_FRC_4 (1 << LCD_CTRL_FRC_BIT) ++ #define LCD_CTRL_FRC_2 (2 << LCD_CTRL_FRC_BIT) ++#define LCD_CTRL_PDD_BIT 16 ++#define LCD_CTRL_PDD_MASK (0xff << LCD_CTRL_PDD_BIT) ++#define LCD_CTRL_EOFM (1 << 13) ++#define LCD_CTRL_SOFM (1 << 12) ++#define LCD_CTRL_OFUM (1 << 11) ++#define LCD_CTRL_IFUM0 (1 << 10) ++#define LCD_CTRL_IFUM1 (1 << 9) ++#define LCD_CTRL_LDDM (1 << 8) ++#define LCD_CTRL_QDM (1 << 7) ++#define LCD_CTRL_BEDN (1 << 6) ++#define LCD_CTRL_PEDN (1 << 5) ++#define LCD_CTRL_DIS (1 << 4) ++#define LCD_CTRL_ENA (1 << 3) ++#define LCD_CTRL_BPP_BIT 0 ++#define LCD_CTRL_BPP_MASK (0x07 << LCD_CTRL_BPP_BIT) ++ #define LCD_CTRL_BPP_1 (0 << LCD_CTRL_BPP_BIT) ++ #define LCD_CTRL_BPP_2 (1 << LCD_CTRL_BPP_BIT) ++ #define LCD_CTRL_BPP_4 (2 << LCD_CTRL_BPP_BIT) ++ #define LCD_CTRL_BPP_8 (3 << LCD_CTRL_BPP_BIT) ++ #define LCD_CTRL_BPP_16 (4 << LCD_CTRL_BPP_BIT) ++ ++#define LCD_STATE_QD (1 << 7) ++#define LCD_STATE_EOF (1 << 5) ++#define LCD_STATE_SOF (1 << 4) ++#define LCD_STATE_OFU (1 << 3) ++#define LCD_STATE_IFU0 (1 << 2) ++#define LCD_STATE_IFU1 (1 << 1) ++#define LCD_STATE_LDD (1 << 0) ++ ++#define LCD_CMD_SOFINT (1 << 31) ++#define LCD_CMD_EOFINT (1 << 30) ++#define LCD_CMD_PAL (1 << 28) ++#define LCD_CMD_LEN_BIT 0 ++#define LCD_CMD_LEN_MASK (0xffffff << LCD_CMD_LEN_BIT) ++ ++ ++ ++ ++/************************************************************************* ++ * DES ++ *************************************************************************/ ++#define DES_CR1 (DES_BASE + 0x000) ++#define DES_CR2 (DES_BASE + 0x004) ++#define DES_SR (DES_BASE + 0x008) ++#define DES_K1L (DES_BASE + 0x010) ++#define DES_K1R (DES_BASE + 0x014) ++#define DES_K2L (DES_BASE + 0x018) ++#define DES_K2R (DES_BASE + 0x01C) ++#define DES_K3L (DES_BASE + 0x020) ++#define DES_K3R (DES_BASE + 0x024) ++#define DES_IVL (DES_BASE + 0x028) ++#define DES_IVR (DES_BASE + 0x02C) ++#define DES_DIN (DES_BASE + 0x030) ++#define DES_DOUT (DES_BASE + 0x034) ++ ++#define REG_DES_CR1 REG32(DES_CR1) ++#define REG_DES_CR2 REG32(DES_CR2) ++#define REG_DES_SR REG32(DES_SR) ++#define REG_DES_K1L REG32(DES_K1L) ++#define REG_DES_K1R REG32(DES_K1R) ++#define REG_DES_K2L REG32(DES_K2L) ++#define REG_DES_K2R REG32(DES_K2R) ++#define REG_DES_K3L REG32(DES_K3L) ++#define REG_DES_K3R REG32(DES_K3R) ++#define REG_DES_IVL REG32(DES_IVL) ++#define REG_DES_IVR REG32(DES_IVR) ++#define REG_DES_DIN REG32(DES_DIN) ++#define REG_DES_DOUT REG32(DES_DOUT) ++ ++/* DES Control Register 1 (DES_CR1) */ ++ ++#define DES_CR1_EN (1 << 0) ++ ++/* DES Control Register 2 (DES_CR2) */ ++ ++#define DES_CR2_ENDEC (1 << 3) ++#define DES_CR2_MODE (1 << 2) ++#define DES_CR2_ALG (1 << 1) ++#define DES_CR2_DMAE (1 << 0) ++ ++/* DES State Register (DES_SR) */ ++ ++#define DES_SR_IN_FULL (1 << 5) ++#define DES_SR_IN_LHF (1 << 4) ++#define DES_SR_IN_EMPTY (1 << 3) ++#define DES_SR_OUT_FULL (1 << 2) ++#define DES_SR_OUT_GHF (1 << 1) ++#define DES_SR_OUT_EMPTY (1 << 0) ++ ++ ++ ++ ++/************************************************************************* ++ * CPM ++ *************************************************************************/ ++#define CPM_CFCR (CPM_BASE+0x00) ++#define CPM_PLCR1 (CPM_BASE+0x10) ++#define CPM_OCR (CPM_BASE+0x1c) ++#define CPM_CFCR2 (CPM_BASE+0x60) ++#define CPM_LPCR (CPM_BASE+0x04) ++#define CPM_RSTR (CPM_BASE+0x08) ++#define CPM_MSCR (CPM_BASE+0x20) ++#define CPM_SCR (CPM_BASE+0x24) ++#define CPM_WRER (CPM_BASE+0x28) ++#define CPM_WFER (CPM_BASE+0x2c) ++#define CPM_WER (CPM_BASE+0x30) ++#define CPM_WSR (CPM_BASE+0x34) ++#define CPM_GSR0 (CPM_BASE+0x38) ++#define CPM_GSR1 (CPM_BASE+0x3c) ++#define CPM_GSR2 (CPM_BASE+0x40) ++#define CPM_SPR (CPM_BASE+0x44) ++#define CPM_GSR3 (CPM_BASE+0x48) ++ ++#define REG_CPM_CFCR REG32(CPM_CFCR) ++#define REG_CPM_PLCR1 REG32(CPM_PLCR1) ++#define REG_CPM_OCR REG32(CPM_OCR) ++#define REG_CPM_CFCR2 REG32(CPM_CFCR2) ++#define REG_CPM_LPCR REG32(CPM_LPCR) ++#define REG_CPM_RSTR REG32(CPM_RSTR) ++#define REG_CPM_MSCR REG32(CPM_MSCR) ++#define REG_CPM_SCR REG32(CPM_SCR) ++#define REG_CPM_WRER REG32(CPM_WRER) ++#define REG_CPM_WFER REG32(CPM_WFER) ++#define REG_CPM_WER REG32(CPM_WER) ++#define REG_CPM_WSR REG32(CPM_WSR) ++#define REG_CPM_GSR0 REG32(CPM_GSR0) ++#define REG_CPM_GSR1 REG32(CPM_GSR1) ++#define REG_CPM_GSR2 REG32(CPM_GSR2) ++#define REG_CPM_SPR REG32(CPM_SPR) ++#define REG_CPM_GSR3 REG32(CPM_GSR3) ++ ++#define CPM_CFCR_SSI (1 << 31) ++#define CPM_CFCR_LCD (1 << 30) ++#define CPM_CFCR_I2S (1 << 29) ++#define CPM_CFCR_UCS (1 << 28) ++#define CPM_CFCR_UFR_BIT 25 ++#define CPM_CFCR_UFR_MASK (0x07 << CPM_CFCR_UFR_BIT) ++#define CPM_CFCR_MSC (1 << 24) ++#define CPM_CFCR_CKOEN2 (1 << 23) ++#define CPM_CFCR_CKOEN1 (1 << 22) ++#define CPM_CFCR_UPE (1 << 20) ++#define CPM_CFCR_MFR_BIT 16 ++#define CPM_CFCR_MFR_MASK (0x0f << CPM_CFCR_MFR_BIT) ++#define CPM_CFCR_LFR_BIT 12 ++#define CPM_CFCR_LFR_MASK (0x0f << CPM_CFCR_LFR_BIT) ++#define CPM_CFCR_PFR_BIT 8 ++#define CPM_CFCR_PFR_MASK (0x0f << CPM_CFCR_PFR_BIT) ++#define CPM_CFCR_SFR_BIT 4 ++#define CPM_CFCR_SFR_MASK (0x0f << CPM_CFCR_SFR_BIT) ++#define CPM_CFCR_IFR_BIT 0 ++#define CPM_CFCR_IFR_MASK (0x0f << CPM_CFCR_IFR_BIT) ++ ++#define CPM_PLCR1_PLL1FD_BIT 23 ++#define CPM_PLCR1_PLL1FD_MASK (0x1ff << CPM_PLCR1_PLL1FD_BIT) ++#define CPM_PLCR1_PLL1RD_BIT 18 ++#define CPM_PLCR1_PLL1RD_MASK (0x1f << CPM_PLCR1_PLL1RD_BIT) ++#define CPM_PLCR1_PLL1OD_BIT 16 ++#define CPM_PLCR1_PLL1OD_MASK (0x03 << CPM_PLCR1_PLL1OD_BIT) ++#define CPM_PLCR1_PLL1S (1 << 10) ++#define CPM_PLCR1_PLL1BP (1 << 9) ++#define CPM_PLCR1_PLL1EN (1 << 8) ++#define CPM_PLCR1_PLL1ST_BIT 0 ++#define CPM_PLCR1_PLL1ST_MASK (0xff << CPM_PLCR1_PLL1ST_BIT) ++ ++#define CPM_OCR_O1ST_BIT 16 ++#define CPM_OCR_O1ST_MASK (0xff << CPM_OCR_O1ST_BIT) ++#define CPM_OCR_EXT_RTC_CLK (1<<8) ++#define CPM_OCR_SUSPEND_PHY1 (1<<7) ++#define CPM_OCR_SUSPEND_PHY0 (1<<6) ++ ++#define CPM_CFCR2_PXFR_BIT 0 ++#define CPM_CFCR2_PXFR_MASK (0x1ff << CPM_CFCR2_PXFR_BIT) ++ ++#define CPM_LPCR_DUTY_BIT 3 ++#define CPM_LPCR_DUTY_MASK (0x1f << CPM_LPCR_DUTY_BIT) ++#define CPM_LPCR_DOZE (1 << 2) ++#define CPM_LPCR_LPM_BIT 0 ++#define CPM_LPCR_LPM_MASK (0x03 << CPM_LPCR_LPM_BIT) ++ #define CPM_LPCR_LPM_IDLE (0 << CPM_LPCR_LPM_BIT) ++ #define CPM_LPCR_LPM_SLEEP (1 << CPM_LPCR_LPM_BIT) ++ #define CPM_LPCR_LPM_HIBERNATE (2 << CPM_LPCR_LPM_BIT) ++ ++#define CPM_RSTR_SR (1 << 2) ++#define CPM_RSTR_WR (1 << 1) ++#define CPM_RSTR_HR (1 << 0) ++ ++#define CPM_MSCR_MSTP_BIT 0 ++#define CPM_MSCR_MSTP_MASK (0x1ffffff << CPM_MSCR_MSTP_BIT) ++ #define CPM_MSCR_MSTP_UART0 0 ++ #define CPM_MSCR_MSTP_UART1 1 ++ #define CPM_MSCR_MSTP_UART2 2 ++ #define CPM_MSCR_MSTP_OST 3 ++ #define CPM_MSCR_MSTP_DMAC 5 ++ #define CPM_MSCR_MSTP_UHC 6 ++ #define CPM_MSCR_MSTP_LCD 7 ++ #define CPM_MSCR_MSTP_I2C 8 ++ #define CPM_MSCR_MSTP_AICPCLK 9 ++ #define CPM_MSCR_MSTP_PWM0 10 ++ #define CPM_MSCR_MSTP_PWM1 11 ++ #define CPM_MSCR_MSTP_SSI 12 ++ #define CPM_MSCR_MSTP_MSC 13 ++ #define CPM_MSCR_MSTP_SCC 14 ++ #define CPM_MSCR_MSTP_AICBCLK 18 ++ #define CPM_MSCR_MSTP_UART3 20 ++ #define CPM_MSCR_MSTP_ETH 21 ++ #define CPM_MSCR_MSTP_KBC 22 ++ #define CPM_MSCR_MSTP_CIM 23 ++ #define CPM_MSCR_MSTP_UDC 24 ++ #define CPM_MSCR_MSTP_UPRT 25 ++ ++#define CPM_SCR_O1SE (1 << 4) ++#define CPM_SCR_HGP (1 << 3) ++#define CPM_SCR_HZP (1 << 2) ++#define CPM_SCR_HZM (1 << 1) ++ ++#define CPM_WRER_RE_BIT 0 ++#define CPM_WRER_RE_MASK (0xffff << CPM_WRER_RE_BIT) ++ ++#define CPM_WFER_FE_BIT 0 ++#define CPM_WFER_FE_MASK (0xffff << CPM_WFER_FE_BIT) ++ ++#define CPM_WER_WERTC (1 << 31) ++#define CPM_WER_WEETH (1 << 30) ++#define CPM_WER_WE_BIT 0 ++#define CPM_WER_WE_MASK (0xffff << CPM_WER_WE_BIT) ++ ++#define CPM_WSR_WSRTC (1 << 31) ++#define CPM_WSR_WSETH (1 << 30) ++#define CPM_WSR_WS_BIT 0 ++#define CPM_WSR_WS_MASK (0xffff << CPM_WSR_WS_BIT) ++ ++ ++ ++ ++/************************************************************************* ++ * SSI ++ *************************************************************************/ ++#define SSI_DR (SSI_BASE + 0x000) ++#define SSI_CR0 (SSI_BASE + 0x004) ++#define SSI_CR1 (SSI_BASE + 0x008) ++#define SSI_SR (SSI_BASE + 0x00C) ++#define SSI_ITR (SSI_BASE + 0x010) ++#define SSI_ICR (SSI_BASE + 0x014) ++#define SSI_GR (SSI_BASE + 0x018) ++ ++#define REG_SSI_DR REG32(SSI_DR) ++#define REG_SSI_CR0 REG16(SSI_CR0) ++#define REG_SSI_CR1 REG32(SSI_CR1) ++#define REG_SSI_SR REG32(SSI_SR) ++#define REG_SSI_ITR REG16(SSI_ITR) ++#define REG_SSI_ICR REG8(SSI_ICR) ++#define REG_SSI_GR REG16(SSI_GR) ++ ++/* SSI Data Register (SSI_DR) */ ++ ++#define SSI_DR_GPC_BIT 0 ++#define SSI_DR_GPC_MASK (0x1ff << SSI_DR_GPC_BIT) ++ ++/* SSI Control Register 0 (SSI_CR0) */ ++ ++#define SSI_CR0_SSIE (1 << 15) ++#define SSI_CR0_TIE (1 << 14) ++#define SSI_CR0_RIE (1 << 13) ++#define SSI_CR0_TEIE (1 << 12) ++#define SSI_CR0_REIE (1 << 11) ++#define SSI_CR0_LOOP (1 << 10) ++#define SSI_CR0_RFINE (1 << 9) ++#define SSI_CR0_RFINC (1 << 8) ++#define SSI_CR0_FSEL (1 << 6) ++#define SSI_CR0_TFLUSH (1 << 2) ++#define SSI_CR0_RFLUSH (1 << 1) ++#define SSI_CR0_DISREV (1 << 0) ++ ++/* SSI Control Register 1 (SSI_CR1) */ ++ ++#define SSI_CR1_FRMHL_BIT 30 ++#define SSI_CR1_FRMHL_MASK (0x3 << SSI_CR1_FRMHL_BIT) ++ #define SSI_CR1_FRMHL_CELOW_CE2LOW (0 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is low valid */ ++ #define SSI_CR1_FRMHL_CEHIGH_CE2LOW (1 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is low valid */ ++ #define SSI_CR1_FRMHL_CELOW_CE2HIGH (2 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is high valid */ ++ #define SSI_CR1_FRMHL_CEHIGH_CE2HIGH (3 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is high valid */ ++#define SSI_CR1_TFVCK_BIT 28 ++#define SSI_CR1_TFVCK_MASK (0x3 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_0 (0 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_1 (1 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_2 (2 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_3 (3 << SSI_CR1_TFVCK_BIT) ++#define SSI_CR1_TCKFI_BIT 26 ++#define SSI_CR1_TCKFI_MASK (0x3 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_0 (0 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_1 (1 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_2 (2 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_3 (3 << SSI_CR1_TCKFI_BIT) ++#define SSI_CR1_LFST (1 << 25) ++#define SSI_CR1_ITFRM (1 << 24) ++#define SSI_CR1_UNFIN (1 << 23) ++#define SSI_CR1_MULTS (1 << 22) ++#define SSI_CR1_FMAT_BIT 20 ++#define SSI_CR1_FMAT_MASK (0x3 << SSI_CR1_FMAT_BIT) ++ #define SSI_CR1_FMAT_SPI (0 << SSI_CR1_FMAT_BIT) /* Motorola¡¯s SPI format */ ++ #define SSI_CR1_FMAT_SSP (1 << SSI_CR1_FMAT_BIT) /* TI's SSP format */ ++ #define SSI_CR1_FMAT_MW1 (2 << SSI_CR1_FMAT_BIT) /* National Microwire 1 format */ ++ #define SSI_CR1_FMAT_MW2 (3 << SSI_CR1_FMAT_BIT) /* National Microwire 2 format */ ++#define SSI_CR1_MCOM_BIT 12 ++#define SSI_CR1_MCOM_MASK (0xf << SSI_CR1_MCOM_BIT) ++ #define SSI_CR1_MCOM_1BIT (0x0 << SSI_CR1_MCOM_BIT) /* 1-bit command selected */ ++ #define SSI_CR1_MCOM_2BIT (0x1 << SSI_CR1_MCOM_BIT) /* 2-bit command selected */ ++ #define SSI_CR1_MCOM_3BIT (0x2 << SSI_CR1_MCOM_BIT) /* 3-bit command selected */ ++ #define SSI_CR1_MCOM_4BIT (0x3 << SSI_CR1_MCOM_BIT) /* 4-bit command selected */ ++ #define SSI_CR1_MCOM_5BIT (0x4 << SSI_CR1_MCOM_BIT) /* 5-bit command selected */ ++ #define SSI_CR1_MCOM_6BIT (0x5 << SSI_CR1_MCOM_BIT) /* 6-bit command selected */ ++ #define SSI_CR1_MCOM_7BIT (0x6 << SSI_CR1_MCOM_BIT) /* 7-bit command selected */ ++ #define SSI_CR1_MCOM_8BIT (0x7 << SSI_CR1_MCOM_BIT) /* 8-bit command selected */ ++ #define SSI_CR1_MCOM_9BIT (0x8 << SSI_CR1_MCOM_BIT) /* 9-bit command selected */ ++ #define SSI_CR1_MCOM_10BIT (0x9 << SSI_CR1_MCOM_BIT) /* 10-bit command selected */ ++ #define SSI_CR1_MCOM_11BIT (0xA << SSI_CR1_MCOM_BIT) /* 11-bit command selected */ ++ #define SSI_CR1_MCOM_12BIT (0xB << SSI_CR1_MCOM_BIT) /* 12-bit command selected */ ++ #define SSI_CR1_MCOM_13BIT (0xC << SSI_CR1_MCOM_BIT) /* 13-bit command selected */ ++ #define SSI_CR1_MCOM_14BIT (0xD << SSI_CR1_MCOM_BIT) /* 14-bit command selected */ ++ #define SSI_CR1_MCOM_15BIT (0xE << SSI_CR1_MCOM_BIT) /* 15-bit command selected */ ++ #define SSI_CR1_MCOM_16BIT (0xF << SSI_CR1_MCOM_BIT) /* 16-bit command selected */ ++#define SSI_CR1_TTRG_BIT 10 ++#define SSI_CR1_TTRG_MASK (0x3 << SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_1 (0 << SSI_CR1_TTRG_BIT)/* Less than or equal to 1 */ ++ #define SSI_CR1_TTRG_4 (1 << SSI_CR1_TTRG_BIT) /* Less than or equal to 4 */ ++ #define SSI_CR1_TTRG_8 (2 << SSI_CR1_TTRG_BIT) /* Less than or equal to 8 */ ++ #define SSI_CR1_TTRG_14 (3 << SSI_CR1_TTRG_BIT) /* Less than or equal to 14 */ ++#define SSI_CR1_RTRG_BIT 8 ++#define SSI_CR1_RTRG_MASK (0x3 << SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_1 (0 << SSI_CR1_RTRG_BIT) /* More than or equal to 1 */ ++ #define SSI_CR1_RTRG_4 (1 << SSI_CR1_RTRG_BIT) /* More than or equal to 4 */ ++ #define SSI_CR1_RTRG_8 (2 << SSI_CR1_RTRG_BIT) /* More than or equal to 8 */ ++ #define SSI_CR1_RTRG_14 (3 << SSI_CR1_RTRG_BIT) /* More than or equal to 14 */ ++#define SSI_CR1_FLEN_BIT 4 ++#define SSI_CR1_FLEN_MASK (0xf << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_2BIT (0x0 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_3BIT (0x1 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_4BIT (0x2 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_5BIT (0x3 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_6BIT (0x4 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_7BIT (0x5 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_8BIT (0x6 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_9BIT (0x7 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_10BIT (0x8 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_11BIT (0x9 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_12BIT (0xA << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_13BIT (0xB << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_14BIT (0xC << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_15BIT (0xD << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_16BIT (0xE << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_17BIT (0xF << SSI_CR1_FLEN_BIT) ++#define SSI_CR1_PHA (1 << 1) ++#define SSI_CR1_POL (1 << 0) ++ ++/* SSI Status Register (SSI_SR) */ ++ ++#define SSI_SR_TFIFONUM_BIT 13 ++#define SSI_SR_TFIFONUM_MASK (0x1f << SSI_SR_TFIFONUM_BIT) ++#define SSI_SR_RFIFONUM_BIT 8 ++#define SSI_SR_RFIFONUM_MASK (0x1f << SSI_SR_RFIFONUM_BIT) ++#define SSI_SR_END (1 << 7) ++#define SSI_SR_BUSY (1 << 6) ++#define SSI_SR_TFF (1 << 5) ++#define SSI_SR_RFE (1 << 4) ++#define SSI_SR_TFHE (1 << 3) ++#define SSI_SR_RFHF (1 << 2) ++#define SSI_SR_UNDR (1 << 1) ++#define SSI_SR_OVER (1 << 0) ++ ++/* SSI Interval Time Control Register (SSI_ITR) */ ++ ++#define SSI_ITR_CNTCLK (1 << 15) ++#define SSI_ITR_IVLTM_BIT 0 ++#define SSI_ITR_IVLTM_MASK (0x7fff << SSI_ITR_IVLTM_BIT) ++ ++#endif /* __ASM_JZ4730_REGS_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4730/serial.h linux-2.6.24.7/include/asm-mips/mach-jz4730/serial.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4730/serial.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4730/serial.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,33 @@ ++/* ++ * linux/include/asm-mips/mach-jz4730/serial.h ++ * ++ * JZ4730 serial port definition. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4730_SERIAL_H__ ++#define __ASM_JZ4730_SERIAL_H__ ++ ++#define JZ_BASE_BAUD (JZ_EXTAL/16) ++#define JZ_SERIAL_PORT_DEFNS \ ++ { .baud_base = JZ_BASE_BAUD, .irq = IRQ_UART0, \ ++ .flags = STD_COM_FLAGS, .iomem_base = (u8 *)UART0_BASE, \ ++ .iomem_reg_shift = 2, .io_type = SERIAL_IO_MEM }, \ ++ { .baud_base = JZ_BASE_BAUD, .irq = IRQ_UART1, \ ++ .flags = STD_COM_FLAGS, .iomem_base = (u8 *)UART1_BASE, \ ++ .iomem_reg_shift = 2, .io_type = SERIAL_IO_MEM }, \ ++ { .baud_base = JZ_BASE_BAUD, .irq = IRQ_UART2, \ ++ .flags = STD_COM_FLAGS, .iomem_base = (u8 *)UART2_BASE, \ ++ .iomem_reg_shift = 2, .io_type = SERIAL_IO_MEM }, \ ++ { .baud_base = JZ_BASE_BAUD, .irq = IRQ_UART3, \ ++ .flags = STD_COM_FLAGS, .iomem_base = (u8 *)UART3_BASE, \ ++ .iomem_reg_shift = 2, .io_type = SERIAL_IO_MEM }, ++ ++#endif /* __ASM_JZ4730_SERIAL_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4730/war.h linux-2.6.24.7/include/asm-mips/mach-jz4730/war.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4730/war.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4730/war.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,25 @@ ++/* ++ * 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) 2002, 2004, 2007 by Ralf Baechle ++ */ ++#ifndef __ASM_MIPS_MACH_JZ4740_WAR_H ++#define __ASM_MIPS_MACH_JZ4740_WAR_H ++ ++#define R4600_V1_INDEX_ICACHEOP_WAR 0 ++#define R4600_V1_HIT_CACHEOP_WAR 0 ++#define R4600_V2_HIT_CACHEOP_WAR 0 ++#define R5432_CP0_INTERRUPT_WAR 0 ++#define BCM1250_M3_WAR 0 ++#define SIBYTE_1956_WAR 0 ++#define MIPS4K_ICACHE_REFILL_WAR 0 ++#define MIPS_CACHE_SYNC_WAR 0 ++#define TX49XX_ICACHE_INDEX_INV_WAR 0 ++#define RM9000_CDEX_SMP_WAR 0 ++#define ICACHE_REFILLS_WORKAROUND_WAR 0 ++#define R10000_LLSC_WAR 0 ++#define MIPS34K_MISSED_ITLB_WAR 0 ++ ++#endif /* __ASM_MIPS_MACH_JZ4740_WAR_H */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4740/board-dipper.h linux-2.6.24.7/include/asm-mips/mach-jz4740/board-dipper.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4740/board-dipper.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4740/board-dipper.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,69 @@ ++/* ++ * linux/include/asm-mips/mach-jz4740/board-dipper.h ++ * ++ * JZ4725-based (16bit) Dipper board ver 1.x definition. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4725_DIPPER_H__ ++#define __ASM_JZ4725_DIPPER_H__ ++ ++/*====================================================================== ++ * Frequencies of on-board oscillators ++ */ ++#define JZ_EXTAL 12000000 /* Main extal freq: 12 MHz */ ++#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */ ++ ++/*====================================================================== ++ * GPIO JZ4725 ++ */ ++#define GPIO_SD_VCC_EN_N 85 /* GPC21 */ ++#define GPIO_SD_CD_N 91 /* GPC27 */ ++#define GPIO_SD_WP 112 /* GPD16 */ ++#define GPIO_USB_DETE 124 /* GPD28 */ ++#define GPIO_DC_DETE_N 103 /* GPD7 */ ++#define GPIO_CHARG_STAT_N 86 /* GPC22 */ ++#define GPIO_DISP_OFF_N 118 /* GPD22 */ ++ ++#define GPIO_UDC_HOTPLUG GPIO_USB_DETE ++ ++/*====================================================================== ++ * MMC/SD ++ */ ++ ++#define MSC_WP_PIN GPIO_SD_WP ++#define MSC_HOTPLUG_PIN GPIO_SD_CD_N ++#define MSC_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD_CD_N) ++ ++#define __msc_init_io() \ ++do { \ ++ __gpio_as_output(GPIO_SD_VCC_EN_N); \ ++ __gpio_as_input(GPIO_SD_CD_N); \ ++} while (0) ++ ++#define __msc_enable_power() \ ++do { \ ++ __gpio_clear_pin(GPIO_SD_VCC_EN_N); \ ++} while (0) ++ ++#define __msc_disable_power() \ ++do { \ ++ __gpio_set_pin(GPIO_SD_VCC_EN_N); \ ++} while (0) ++ ++#define __msc_card_detected(s) \ ++({ \ ++ int detected = 1; \ ++ if (__gpio_get_pin(GPIO_SD_CD_N)) \ ++ detected = 0; \ ++ detected; \ ++}) ++ ++#endif /* __ASM_JZ4740_DIPPER_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4740/board-leo.h linux-2.6.24.7/include/asm-mips/mach-jz4740/board-leo.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4740/board-leo.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4740/board-leo.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,56 @@ ++#ifndef __ASM_JZ4740_LEO_H__ ++#define __ASM_JZ4740_LEO_H__ ++ ++/* ++ * Define your board specific codes here !!! ++ */ ++ ++/*====================================================================== ++ * Frequencies of on-board oscillators ++ */ ++#define JZ_EXTAL 12000000 /* Main extal freq: 12 MHz */ ++#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */ ++ ++ ++/*====================================================================== ++ * GPIO ++ */ ++#define GPIO_DISP_OFF_N 100 ++#define GPIO_SD_VCC_EN_N 119 ++#define GPIO_SD_CD_N 120 ++#define GPIO_SD_WP 111 ++ ++/*====================================================================== ++ * MMC/SD ++ */ ++ ++#define MSC_WP_PIN GPIO_SD_WP ++#define MSC_HOTPLUG_PIN GPIO_SD_CD_N ++#define MSC_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD_CD_N) ++ ++#define __msc_init_io() \ ++do { \ ++ __gpio_as_output(GPIO_SD_VCC_EN_N); \ ++ __gpio_as_input(GPIO_SD_CD_N); \ ++} while (0) ++ ++#define __msc_enable_power() \ ++do { \ ++ __gpio_clear_pin(GPIO_SD_VCC_EN_N); \ ++} while (0) ++ ++#define __msc_disable_power() \ ++do { \ ++ __gpio_set_pin(GPIO_SD_VCC_EN_N); \ ++} while (0) ++ ++#define __msc_card_detected(s) \ ++({ \ ++ int detected = 1; \ ++ __gpio_as_input(GPIO_SD_CD_N); \ ++ if (__gpio_get_pin(GPIO_SD_CD_N)) \ ++ detected = 0; \ ++ detected; \ ++}) ++ ++#endif /* __ASM_JZ4740_BOARD_LEO_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4740/board-lyra.h linux-2.6.24.7/include/asm-mips/mach-jz4740/board-lyra.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4740/board-lyra.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4740/board-lyra.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,70 @@ ++/* ++ * linux/include/asm-mips/mach-jz4740/board-lyra.h ++ * ++ * JZ4740-based LYRA board ver 2.x definition. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4740_LYRA_H__ ++#define __ASM_JZ4740_LYRA_H__ ++ ++/*====================================================================== ++ * Frequencies of on-board oscillators ++ */ ++#define JZ_EXTAL 12000000 /* Main extal freq: 12 MHz */ ++#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */ ++ ++ ++/*====================================================================== ++ * GPIO ++ */ ++#define GPIO_SD_VCC_EN_N 113 /* GPD17 */ ++#define GPIO_SD_CD_N 110 /* GPD14 */ ++#define GPIO_SD_WP 112 /* GPD16 */ ++#define GPIO_USB_DETE 102 /* GPD6 */ ++#define GPIO_DC_DETE_N 103 /* GPD7 */ ++#define GPIO_CHARG_STAT_N 111 /* GPD15 */ ++#define GPIO_DISP_OFF_N 118 /* GPD22 */ ++#define GPIO_LED_EN 124 /* GPD28 */ ++ ++#define GPIO_UDC_HOTPLUG GPIO_USB_DETE ++/*====================================================================== ++ * MMC/SD ++ */ ++ ++#define MSC_WP_PIN GPIO_SD_WP ++#define MSC_HOTPLUG_PIN GPIO_SD_CD_N ++#define MSC_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD_CD_N) ++ ++#define __msc_init_io() \ ++do { \ ++ __gpio_as_output(GPIO_SD_VCC_EN_N); \ ++ __gpio_as_input(GPIO_SD_CD_N); \ ++} while (0) ++ ++#define __msc_enable_power() \ ++do { \ ++ __gpio_clear_pin(GPIO_SD_VCC_EN_N); \ ++} while (0) ++ ++#define __msc_disable_power() \ ++do { \ ++ __gpio_set_pin(GPIO_SD_VCC_EN_N); \ ++} while (0) ++ ++#define __msc_card_detected(s) \ ++({ \ ++ int detected = 1; \ ++ if (!(__gpio_get_pin(GPIO_SD_CD_N))) \ ++ detected = 0; \ ++ detected; \ ++}) ++ ++#endif /* __ASM_JZ4740_LYRA_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4740/board-pavo.h linux-2.6.24.7/include/asm-mips/mach-jz4740/board-pavo.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4740/board-pavo.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4740/board-pavo.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,70 @@ ++/* ++ * linux/include/asm-mips/mach-jz4740/board-pavo.h ++ * ++ * JZ4730-based PAVO board ver 2.x definition. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4740_PAVO_H__ ++#define __ASM_JZ4740_PAVO_H__ ++ ++/*====================================================================== ++ * Frequencies of on-board oscillators ++ */ ++#define JZ_EXTAL 12000000 /* Main extal freq: 12 MHz */ ++#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */ ++ ++ ++/*====================================================================== ++ * GPIO ++ */ ++#define GPIO_SD_VCC_EN_N 113 /* GPD17 */ ++#define GPIO_SD_CD_N 110 /* GPD14 */ ++#define GPIO_SD_WP 112 /* GPD16 */ ++#define GPIO_USB_DETE 102 /* GPD6 */ ++#define GPIO_DC_DETE_N 103 /* GPD7 */ ++#define GPIO_CHARG_STAT_N 111 /* GPD15 */ ++#define GPIO_DISP_OFF_N 118 /* GPD22 */ ++#define GPIO_LED_EN 124 /* GPD28 */ ++ ++#define GPIO_UDC_HOTPLUG GPIO_USB_DETE ++/*====================================================================== ++ * MMC/SD ++ */ ++ ++#define MSC_WP_PIN GPIO_SD_WP ++#define MSC_HOTPLUG_PIN GPIO_SD_CD_N ++#define MSC_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD_CD_N) ++ ++#define __msc_init_io() \ ++do { \ ++ __gpio_as_output(GPIO_SD_VCC_EN_N); \ ++ __gpio_as_input(GPIO_SD_CD_N); \ ++} while (0) ++ ++#define __msc_enable_power() \ ++do { \ ++ __gpio_clear_pin(GPIO_SD_VCC_EN_N); \ ++} while (0) ++ ++#define __msc_disable_power() \ ++do { \ ++ __gpio_set_pin(GPIO_SD_VCC_EN_N); \ ++} while (0) ++ ++#define __msc_card_detected(s) \ ++({ \ ++ int detected = 1; \ ++ if (__gpio_get_pin(GPIO_SD_CD_N)) \ ++ detected = 0; \ ++ detected; \ ++}) ++ ++#endif /* __ASM_JZ4740_PAVO_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4740/board-virgo.h linux-2.6.24.7/include/asm-mips/mach-jz4740/board-virgo.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4740/board-virgo.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4740/board-virgo.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,67 @@ ++/* ++ * linux/include/asm-mips/mach-jz4740/board-virgo.h ++ * ++ * JZ4720-based VIRGO board ver 1.x definition. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4720_VIRGO_H__ ++#define __ASM_JZ4720_VIRGO_H__ ++ ++/*====================================================================== ++ * Frequencies of on-board oscillators ++ */ ++#define JZ_EXTAL 12000000 /* Main extal freq: 12 MHz */ ++#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */ ++ ++/*====================================================================== ++ * GPIO VIRGO(JZ4720) ++ */ ++#define GPIO_SD_VCC_EN_N 115 /* GPD19 */ ++#define GPIO_SD_CD_N 116 /* GPD20 */ ++#define GPIO_USB_DETE 114 /* GPD18 */ ++#define GPIO_DC_DETE_N 120 /* GPD24 */ ++#define GPIO_DISP_OFF_N 118 /* GPD22 */ ++#define GPIO_LED_EN 117 /* GPD21 */ ++ ++#define GPIO_UDC_HOTPLUG GPIO_USB_DETE ++ ++/*====================================================================== ++ * MMC/SD ++ */ ++ ++#define MSC_HOTPLUG_PIN GPIO_SD_CD_N ++#define MSC_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD_CD_N) ++ ++#define __msc_init_io() \ ++do { \ ++ __gpio_as_output(GPIO_SD_VCC_EN_N); \ ++ __gpio_as_input(GPIO_SD_CD_N); \ ++} while (0) ++ ++#define __msc_enable_power() \ ++do { \ ++ __gpio_clear_pin(GPIO_SD_VCC_EN_N); \ ++} while (0) ++ ++#define __msc_disable_power() \ ++do { \ ++ __gpio_set_pin(GPIO_SD_VCC_EN_N); \ ++} while (0) ++ ++#define __msc_card_detected(s) \ ++({ \ ++ int detected = 1; \ ++ if (__gpio_get_pin(GPIO_SD_CD_N)) \ ++ detected = 0; \ ++ detected; \ ++}) ++ ++#endif /* __ASM_JZ4720_VIRGO_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4740/clock.h linux-2.6.24.7/include/asm-mips/mach-jz4740/clock.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4740/clock.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4740/clock.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,173 @@ ++/* ++ * linux/include/asm-mips/mach-jz4740/clock.h ++ * ++ * JZ4740 clocks definition. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4740_CLOCK_H__ ++#define __ASM_JZ4740_CLOCK_H__ ++ ++#ifndef JZ_EXTAL ++//#define JZ_EXTAL 3686400 /* 3.6864 MHz */ ++#define JZ_EXTAL 12000000 /* 3.6864 MHz */ ++#endif ++#ifndef JZ_EXTAL2 ++#define JZ_EXTAL2 32768 /* 32.768 KHz */ ++#endif ++ ++/* ++ * JZ4740 clocks structure ++ */ ++typedef struct { ++ unsigned int cclk; /* CPU clock */ ++ unsigned int hclk; /* System bus clock */ ++ unsigned int pclk; /* Peripheral bus clock */ ++ unsigned int mclk; /* Flash/SRAM/SDRAM clock */ ++ unsigned int lcdclk; /* LCDC module clock */ ++ unsigned int pixclk; /* LCD pixel clock */ ++ unsigned int i2sclk; /* AIC module clock */ ++ unsigned int usbclk; /* USB module clock */ ++ unsigned int mscclk; /* MSC module clock */ ++ unsigned int extalclk; /* EXTAL clock for UART,I2C,SSI,TCU,USB-PHY */ ++ unsigned int rtcclk; /* RTC clock for CPM,INTC,RTC,TCU,WDT */ ++} jz_clocks_t; ++ ++extern jz_clocks_t jz_clocks; ++ ++ ++/* PLL output frequency */ ++static __inline__ unsigned int __cpm_get_pllout(void) ++{ ++ unsigned long m, n, no, pllout; ++ unsigned long cppcr = REG_CPM_CPPCR; ++ unsigned long od[4] = {1, 2, 2, 4}; ++ if ((cppcr & CPM_CPPCR_PLLEN) && !(cppcr & CPM_CPPCR_PLLBP)) { ++ m = __cpm_get_pllm() + 2; ++ n = __cpm_get_plln() + 2; ++ no = od[__cpm_get_pllod()]; ++ pllout = ((JZ_EXTAL) / (n * no)) * m; ++ } else ++ pllout = JZ_EXTAL; ++ return pllout; ++} ++ ++/* PLL output frequency for MSC/I2S/LCD/USB */ ++static __inline__ unsigned int __cpm_get_pllout2(void) ++{ ++ if (REG_CPM_CPCCR & CPM_CPCCR_PCS) ++ return __cpm_get_pllout(); ++ else ++ return __cpm_get_pllout()/2; ++} ++ ++/* CPU core clock */ ++static __inline__ unsigned int __cpm_get_cclk(void) ++{ ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ ++ return __cpm_get_pllout() / div[__cpm_get_cdiv()]; ++} ++ ++/* AHB system bus clock */ ++static __inline__ unsigned int __cpm_get_hclk(void) ++{ ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ ++ return __cpm_get_pllout() / div[__cpm_get_hdiv()]; ++} ++ ++/* Memory bus clock */ ++static __inline__ unsigned int __cpm_get_mclk(void) ++{ ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ ++ return __cpm_get_pllout() / div[__cpm_get_mdiv()]; ++} ++ ++/* APB peripheral bus clock */ ++static __inline__ unsigned int __cpm_get_pclk(void) ++{ ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ ++ return __cpm_get_pllout() / div[__cpm_get_pdiv()]; ++} ++ ++/* LCDC module clock */ ++static __inline__ unsigned int __cpm_get_lcdclk(void) ++{ ++ return __cpm_get_pllout2() / (__cpm_get_ldiv() + 1); ++} ++ ++/* LCD pixel clock */ ++static __inline__ unsigned int __cpm_get_pixclk(void) ++{ ++ return __cpm_get_pllout2() / (__cpm_get_pixdiv() + 1); ++} ++ ++/* I2S clock */ ++static __inline__ unsigned int __cpm_get_i2sclk(void) ++{ ++ if (REG_CPM_CPCCR & CPM_CPCCR_I2CS) { ++ return __cpm_get_pllout2() / (__cpm_get_i2sdiv() + 1); ++ } ++ else { ++ return JZ_EXTAL; ++ } ++} ++ ++/* USB clock */ ++static __inline__ unsigned int __cpm_get_usbclk(void) ++{ ++ if (REG_CPM_CPCCR & CPM_CPCCR_UCS) { ++ return __cpm_get_pllout2() / (__cpm_get_udiv() + 1); ++ } ++ else { ++ return JZ_EXTAL; ++ } ++} ++ ++/* MSC clock */ ++static __inline__ unsigned int __cpm_get_mscclk(void) ++{ ++ return __cpm_get_pllout2() / (__cpm_get_mscdiv() + 1); ++} ++ ++/* EXTAL clock for UART,I2C,SSI,TCU,USB-PHY */ ++static __inline__ unsigned int __cpm_get_extalclk(void) ++{ ++ return JZ_EXTAL; ++} ++ ++/* RTC clock for CPM,INTC,RTC,TCU,WDT */ ++static __inline__ unsigned int __cpm_get_rtcclk(void) ++{ ++ return JZ_EXTAL2; ++} ++ ++/* ++ * Output 24MHz for SD and 16MHz for MMC. ++ */ ++static inline void __cpm_select_msc_clk(int sd) ++{ ++ unsigned int pllout2 = __cpm_get_pllout2(); ++ unsigned int div = 0; ++ ++ if (sd) { ++ div = pllout2 / 24000000; ++ } ++ else { ++ div = pllout2 / 16000000; ++ } ++ ++ REG_CPM_MSCCDR = div - 1; ++} ++ ++#endif /* __ASM_JZ4740_CLOCK_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4740/dma.h linux-2.6.24.7/include/asm-mips/mach-jz4740/dma.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4740/dma.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4740/dma.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,265 @@ ++/* ++ * linux/include/asm-mips/mach-jz4740/dma.h ++ * ++ * JZ4740 DMA definition. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4740_DMA_H__ ++#define __ASM_JZ4740_DMA_H__ ++ ++#include ++#include /* need byte IO */ ++#include /* And spinlocks */ ++#include ++#include ++ ++/* ++ * Descriptor structure for JZ4740 DMA engine ++ * Note: this structure must always be aligned to a 16-bytes boundary. ++ */ ++ ++typedef struct { ++ volatile u32 dcmd; /* DCMD value for the current transfer */ ++ volatile u32 dsadr; /* DSAR value for the current transfer */ ++ volatile u32 dtadr; /* DTAR value for the current transfer */ ++ volatile u32 ddadr; /* Points to the next descriptor + transfer count */ ++} jz_dma_desc; ++ ++ ++/* DMA Device ID's follow */ ++enum { ++ DMA_ID_UART0_TX = 0, ++ DMA_ID_UART0_RX, ++ DMA_ID_SSI_TX, ++ DMA_ID_SSI_RX, ++ DMA_ID_AIC_TX, ++ DMA_ID_AIC_RX, ++ DMA_ID_MSC_TX, ++ DMA_ID_MSC_RX, ++ DMA_ID_TCU_OVERFLOW, ++ DMA_ID_AUTO, ++ DMA_ID_RAW_SET, ++ DMA_ID_MAX ++}; ++ ++/* DMA modes, simulated by sw */ ++#define DMA_MODE_READ 0x0 /* I/O to memory, no autoinit, increment, single mode */ ++#define DMA_MODE_WRITE 0x1 /* memory to I/O, no autoinit, increment, single mode */ ++#define DMA_AUTOINIT 0x2 ++#define DMA_MODE_MASK 0x3 ++ ++struct jz_dma_chan { ++ int dev_id; /* DMA ID: this channel is allocated if >=0, free otherwise */ ++ unsigned int io; /* DMA channel number */ ++ const char *dev_str; /* string describes the DMA channel */ ++ int irq; /* DMA irq number */ ++ void *irq_dev; /* DMA private device structure */ ++ unsigned int fifo_addr; /* physical fifo address of the requested device */ ++ unsigned int cntl; /* DMA controll */ ++ unsigned int mode; /* DMA configuration */ ++ unsigned int source; /* DMA request source */ ++}; ++ ++extern struct jz_dma_chan jz_dma_table[]; ++ ++ ++#define DMA_8BIT_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_8BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_8BIT_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \ ++ DMAC_DCMD_DS_8BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_16BIT_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_16BIT_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_32BIT_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_32BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_32BIT_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_32BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_16BYTE_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_16BYTE_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_32BYTE_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_32BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_32BYTE_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \ ++ DMAC_DCMD_DS_32BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_32_16BYTE_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_32_16BYTE_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_16BIT_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_16BIT_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_16BYTE_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_16BYTE_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++extern int jz_request_dma(int dev_id, ++ const char *dev_str, ++ irqreturn_t (*irqhandler)(int, void *), ++ unsigned long irqflags, ++ void *irq_dev_id); ++extern void jz_free_dma(unsigned int dmanr); ++ ++extern int jz_dma_read_proc(char *buf, char **start, off_t fpos, ++ int length, int *eof, void *data); ++extern void dump_jz_dma_channel(unsigned int dmanr); ++ ++extern void enable_dma(unsigned int dmanr); ++extern void disable_dma(unsigned int dmanr); ++extern void set_dma_addr(unsigned int dmanr, unsigned int phyaddr); ++extern void set_dma_count(unsigned int dmanr, unsigned int bytecnt); ++extern void set_dma_mode(unsigned int dmanr, unsigned int mode); ++extern void jz_set_oss_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt); ++extern void jz_set_alsa_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt); ++extern unsigned int get_dma_residue(unsigned int dmanr); ++ ++extern spinlock_t dma_spin_lock; ++ ++static __inline__ unsigned long claim_dma_lock(void) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&dma_spin_lock, flags); ++ return flags; ++} ++ ++static __inline__ void release_dma_lock(unsigned long flags) ++{ ++ spin_unlock_irqrestore(&dma_spin_lock, flags); ++} ++ ++/* Clear the 'DMA Pointer Flip Flop'. ++ * Write 0 for LSB/MSB, 1 for MSB/LSB access. ++ */ ++#define clear_dma_ff(channel) ++ ++static __inline__ struct jz_dma_chan *get_dma_chan(unsigned int dmanr) ++{ ++ if (dmanr > MAX_DMA_NUM ++ || jz_dma_table[dmanr].dev_id < 0) ++ return NULL; ++ return &jz_dma_table[dmanr]; ++} ++ ++static __inline__ int dma_halted(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 1; ++ return __dmac_channel_transmit_halt_detected(dmanr) ? 1 : 0; ++} ++ ++static __inline__ unsigned int get_dma_mode(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 0; ++ return chan->mode; ++} ++ ++static __inline__ void clear_dma_done(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); ++} ++ ++static __inline__ void clear_dma_halt(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT); ++ REG_DMAC_DMACR &= ~(DMAC_DMACR_HLT); ++} ++ ++static __inline__ void clear_dma_flag(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); ++ REG_DMAC_DMACR &= ~(DMAC_DMACR_HLT | DMAC_DMACR_AR); ++} ++ ++static __inline__ void set_dma_page(unsigned int dmanr, char pagenr) ++{ ++} ++ ++static __inline__ unsigned int get_dma_done_status(unsigned int dmanr) ++{ ++ unsigned long dccsr; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 0; ++ dccsr = REG_DMAC_DCCSR(chan->io); ++ return dccsr & (DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); ++} ++ ++static __inline__ int get_dma_done_irq(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return -1; ++ return chan->irq; ++} ++ ++#endif /* __ASM_JZ4740_DMA_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4740/jz4740.h linux-2.6.24.7/include/asm-mips/mach-jz4740/jz4740.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4740/jz4740.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4740/jz4740.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,56 @@ ++/* ++ * linux/include/asm-mips/mach-jz4740/jz4740.h ++ * ++ * JZ4740 common definition. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4740_H__ ++#define __ASM_JZ4740_H__ ++ ++#include ++#include ++#include ++#include ++ ++/*------------------------------------------------------------------ ++ * Platform definitions ++ */ ++#ifdef CONFIG_JZ4740_PAVO ++#include ++#endif ++ ++#ifdef CONFIG_JZ4740_LEO ++#include ++#endif ++ ++#ifdef CONFIG_JZ4740_LYRA ++#include ++#endif ++ ++#ifdef CONFIG_JZ4725_DIPPER ++#include ++#endif ++ ++#ifdef CONFIG_JZ4720_VIRGO ++#include ++#endif ++ ++/* Add other platform definition here ... */ ++ ++ ++/*------------------------------------------------------------------ ++ * Follows are related to platform definitions ++ */ ++ ++#include ++#include ++ ++#endif /* __ASM_JZ4740_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4740/misc.h linux-2.6.24.7/include/asm-mips/mach-jz4740/misc.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4740/misc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4740/misc.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,43 @@ ++/* ++ * linux/include/asm-mips/mach-jz4740/misc.h ++ * ++ * Ingenic's JZ4740 common include. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4740_MISC_H__ ++#define __ASM_JZ4740_MISC_H__ ++ ++/*========================================================== ++ * I2C ++ *===========================================================*/ ++ ++#define I2C_EEPROM_DEV 0xA /* b'1010 */ ++#define I2C_RTC_DEV 0xD /* b'1101 */ ++#define DIMM0_SPD_ADDR 0 ++#define DIMM1_SPD_ADDR 1 ++#define DIMM2_SPD_ADDR 2 ++#define DIMM3_SPD_ADDR 3 ++#define JZ_HCI_ADDR 7 ++ ++#define DIMM_SPD_LEN 128 ++#define JZ_HCI_LEN 512 /* 4K bits E2PROM */ ++#define I2C_RTC_LEN 16 ++#define HCI_MAC_OFFSET 64 ++ ++extern void i2c_open(void); ++extern void i2c_close(void); ++extern void i2c_setclk(unsigned int i2cclk); ++extern int i2c_read(unsigned char device, unsigned char *buf, ++ unsigned char address, int count); ++extern int i2c_write(unsigned char device, unsigned char *buf, ++ unsigned char address, int count); ++ ++#endif /* __ASM_JZ4740_MISC_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4740/ops.h linux-2.6.24.7/include/asm-mips/mach-jz4740/ops.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4740/ops.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4740/ops.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,2224 @@ ++/* ++ * linux/include/asm-mips/mach-jz4740/ops.h ++ * ++ * Ingenic's JZ4740 common include. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __JZ4740_OPS_H__ ++#define __JZ4740_OPS_H__ ++ ++/* ++ * Definition of Module Operations ++ */ ++ ++/*************************************************************************** ++ * GPIO ++ ***************************************************************************/ ++ ++//------------------------------------------------------ ++// GPIO Pins Description ++// ++// PORT 0: ++// ++// PIN/BIT N FUNC0 FUNC1 ++// 0 D0 - ++// 1 D1 - ++// 2 D2 - ++// 3 D3 - ++// 4 D4 - ++// 5 D5 - ++// 6 D6 - ++// 7 D7 - ++// 8 D8 - ++// 9 D9 - ++// 10 D10 - ++// 11 D11 - ++// 12 D12 - ++// 13 D13 - ++// 14 D14 - ++// 15 D15 - ++// 16 D16 - ++// 17 D17 - ++// 18 D18 - ++// 19 D19 - ++// 20 D20 - ++// 21 D21 - ++// 22 D22 - ++// 23 D23 - ++// 24 D24 - ++// 25 D25 - ++// 26 D26 - ++// 27 D27 - ++// 28 D28 - ++// 29 D29 - ++// 30 D30 - ++// 31 D31 - ++// ++//------------------------------------------------------ ++// PORT 1: ++// ++// PIN/BIT N FUNC0 FUNC1 ++// 0 A0 - ++// 1 A1 - ++// 2 A2 - ++// 3 A3 - ++// 4 A4 - ++// 5 A5 - ++// 6 A6 - ++// 7 A7 - ++// 8 A8 - ++// 9 A9 - ++// 10 A10 - ++// 11 A11 - ++// 12 A12 - ++// 13 A13 - ++// 14 A14 - ++// 15 A15/CL - ++// 16 A16/AL - ++// 17 LCD_CLS A21 ++// 18 LCD_SPL A22 ++// 19 DCS# - ++// 20 RAS# - ++// 21 CAS# - ++// 22 RDWE#/BUFD# - ++// 23 CKE - ++// 24 CKO - ++// 25 CS1# - ++// 26 CS2# - ++// 27 CS3# - ++// 28 CS4# - ++// 29 RD# - ++// 30 WR# - ++// 31 WE0# - ++// ++// Note: PIN15&16 are CL&AL when connecting to NAND flash. ++//------------------------------------------------------ ++// PORT 2: ++// ++// PIN/BIT N FUNC0 FUNC1 ++// 0 LCD_D0 - ++// 1 LCD_D1 - ++// 2 LCD_D2 - ++// 3 LCD_D3 - ++// 4 LCD_D4 - ++// 5 LCD_D5 - ++// 6 LCD_D6 - ++// 7 LCD_D7 - ++// 8 LCD_D8 - ++// 9 LCD_D9 - ++// 10 LCD_D10 - ++// 11 LCD_D11 - ++// 12 LCD_D12 - ++// 13 LCD_D13 - ++// 14 LCD_D14 - ++// 15 LCD_D15 - ++// 16 LCD_D16 - ++// 17 LCD_D17 - ++// 18 LCD_PCLK - ++// 19 LCD_HSYNC - ++// 20 LCD_VSYNC - ++// 21 LCD_DE - ++// 22 LCD_PS A19 ++// 23 LCD_REV A20 ++// 24 WE1# - ++// 25 WE2# - ++// 26 WE3# - ++// 27 WAIT# - ++// 28 FRE# - ++// 29 FWE# - ++// 30(NOTE:FRB#) - - ++// 31 - - ++// ++// NOTE(1): PIN30 is used for FRB# when connecting to NAND flash. ++//------------------------------------------------------ ++// PORT 3: ++// ++// PIN/BIT N FUNC0 FUNC1 ++// 0 CIM_D0 - ++// 1 CIM_D1 - ++// 2 CIM_D2 - ++// 3 CIM_D3 - ++// 4 CIM_D4 - ++// 5 CIM_D5 - ++// 6 CIM_D6 - ++// 7 CIM_D7 - ++// 8 MSC_CMD - ++// 9 MSC_CLK - ++// 10 MSC_D0 - ++// 11 MSC_D1 - ++// 12 MSC_D2 - ++// 13 MSC_D3 - ++// 14 CIM_MCLK - ++// 15 CIM_PCLK - ++// 16 CIM_VSYNC - ++// 17 CIM_HSYNC - ++// 18 SSI_CLK SCLK_RSTN ++// 19 SSI_CE0# BIT_CLK(AIC) ++// 20 SSI_DT SDATA_OUT(AIC) ++// 21 SSI_DR SDATA_IN(AIC) ++// 22 SSI_CE1#&GPC SYNC(AIC) ++// 23 PWM0 I2C_SDA ++// 24 PWM1 I2C_SCK ++// 25 PWM2 UART0_TxD ++// 26 PWM3 UART0_RxD ++// 27 PWM4 A17 ++// 28 PWM5 A18 ++// 29 - - ++// 30 PWM6 UART0_CTS/UART1_RxD ++// 31 PWM7 UART0_RTS/UART1_TxD ++// ++////////////////////////////////////////////////////////// ++ ++/* ++ * p is the port number (0,1,2,3) ++ * o is the pin offset (0-31) inside the port ++ * n is the absolute number of a pin (0-127), regardless of the port ++ */ ++ ++//------------------------------------------- ++// Function Pins Mode ++ ++#define __gpio_as_func0(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXFUNS(p) = (1 << o); \ ++ REG_GPIO_PXSELC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_as_func1(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXFUNS(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++} while (0) ++ ++/* ++ * D0 ~ D31, A0 ~ A16, DCS#, RAS#, CAS#, CKE#, ++ * RDWE#, CKO#, WE0#, WE1#, WE2#, WE3# ++ */ ++#define __gpio_as_sdram_32bit() \ ++do { \ ++ REG_GPIO_PXFUNS(0) = 0xffffffff; \ ++ REG_GPIO_PXSELC(0) = 0xffffffff; \ ++ REG_GPIO_PXPES(0) = 0xffffffff; \ ++ REG_GPIO_PXFUNS(1) = 0x81f9ffff; \ ++ REG_GPIO_PXSELC(1) = 0x81f9ffff; \ ++ REG_GPIO_PXPES(1) = 0x81f9ffff; \ ++ REG_GPIO_PXFUNS(2) = 0x07000000; \ ++ REG_GPIO_PXSELC(2) = 0x07000000; \ ++ REG_GPIO_PXPES(2) = 0x07000000; \ ++} while (0) ++ ++/* ++ * D0 ~ D15, A0 ~ A16, DCS#, RAS#, CAS#, CKE#, ++ * RDWE#, CKO#, WE0#, WE1# ++ */ ++#define __gpio_as_sdram_16bit() \ ++do { \ ++ REG_GPIO_PXFUNS(0) = 0x5442bfaa; \ ++ REG_GPIO_PXSELC(0) = 0x5442bfaa; \ ++ REG_GPIO_PXPES(0) = 0x5442bfaa; \ ++ REG_GPIO_PXFUNS(1) = 0x81f9ffff; \ ++ REG_GPIO_PXSELC(1) = 0x81f9ffff; \ ++ REG_GPIO_PXPES(1) = 0x81f9ffff; \ ++ REG_GPIO_PXFUNS(2) = 0x01000000; \ ++ REG_GPIO_PXSELC(2) = 0x01000000; \ ++ REG_GPIO_PXPES(2) = 0x01000000; \ ++} while (0) ++ ++/* ++ * CS1#, CLE, ALE, FRE#, FWE#, FRB#, RDWE#/BUFD# ++ */ ++#define __gpio_as_nand() \ ++do { \ ++ REG_GPIO_PXFUNS(1) = 0x02018000; \ ++ REG_GPIO_PXSELC(1) = 0x02018000; \ ++ REG_GPIO_PXPES(1) = 0x02018000; \ ++ REG_GPIO_PXFUNS(2) = 0x30000000; \ ++ REG_GPIO_PXSELC(2) = 0x30000000; \ ++ REG_GPIO_PXPES(2) = 0x30000000; \ ++ REG_GPIO_PXFUNC(2) = 0x40000000; \ ++ REG_GPIO_PXSELC(2) = 0x40000000; \ ++ REG_GPIO_PXDIRC(2) = 0x40000000; \ ++ REG_GPIO_PXPES(2) = 0x40000000; \ ++ REG_GPIO_PXFUNS(1) = 0x00400000; \ ++ REG_GPIO_PXSELC(1) = 0x00400000; \ ++} while (0) ++ ++/* ++ * CS4#, RD#, WR#, WAIT#, A0 ~ A22, D0 ~ D7 ++ */ ++#define __gpio_as_nor_8bit() \ ++do { \ ++ REG_GPIO_PXFUNS(0) = 0x000000ff; \ ++ REG_GPIO_PXSELC(0) = 0x000000ff; \ ++ REG_GPIO_PXPES(0) = 0x000000ff; \ ++ REG_GPIO_PXFUNS(1) = 0x7041ffff; \ ++ REG_GPIO_PXSELC(1) = 0x7041ffff; \ ++ REG_GPIO_PXPES(1) = 0x7041ffff; \ ++ REG_GPIO_PXFUNS(1) = 0x00060000; \ ++ REG_GPIO_PXSELS(1) = 0x00060000; \ ++ REG_GPIO_PXPES(1) = 0x00060000; \ ++ REG_GPIO_PXFUNS(2) = 0x08000000; \ ++ REG_GPIO_PXSELC(2) = 0x08000000; \ ++ REG_GPIO_PXPES(2) = 0x08000000; \ ++ REG_GPIO_PXFUNS(2) = 0x00c00000; \ ++ REG_GPIO_PXSELS(2) = 0x00c00000; \ ++ REG_GPIO_PXPES(2) = 0x00c00000; \ ++ REG_GPIO_PXFUNS(3) = 0x18000000; \ ++ REG_GPIO_PXSELS(3) = 0x18000000; \ ++ REG_GPIO_PXPES(3) = 0x18000000; \ ++} while (0) ++ ++/* ++ * CS4#, RD#, WR#, WAIT#, A0 ~ A22, D0 ~ D15 ++ */ ++#define __gpio_as_nor_16bit() \ ++do { \ ++ REG_GPIO_PXFUNS(0) = 0x0000ffff; \ ++ REG_GPIO_PXSELC(0) = 0x0000ffff; \ ++ REG_GPIO_PXPES(0) = 0x0000ffff; \ ++ REG_GPIO_PXFUNS(1) = 0x7041ffff; \ ++ REG_GPIO_PXSELC(1) = 0x7041ffff; \ ++ REG_GPIO_PXPES(1) = 0x7041ffff; \ ++ REG_GPIO_PXFUNS(1) = 0x00060000; \ ++ REG_GPIO_PXSELS(1) = 0x00060000; \ ++ REG_GPIO_PXPES(1) = 0x00060000; \ ++ REG_GPIO_PXFUNS(2) = 0x08000000; \ ++ REG_GPIO_PXSELC(2) = 0x08000000; \ ++ REG_GPIO_PXPES(2) = 0x08000000; \ ++ REG_GPIO_PXFUNS(2) = 0x00c00000; \ ++ REG_GPIO_PXSELS(2) = 0x00c00000; \ ++ REG_GPIO_PXPES(2) = 0x00c00000; \ ++ REG_GPIO_PXFUNS(3) = 0x18000000; \ ++ REG_GPIO_PXSELS(3) = 0x18000000; \ ++ REG_GPIO_PXPES(3) = 0x18000000; \ ++} while (0) ++ ++/* ++ * UART0_TxD, UART_RxD0 ++ */ ++#define __gpio_as_uart0() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x06000000; \ ++ REG_GPIO_PXSELS(3) = 0x06000000; \ ++ REG_GPIO_PXPES(3) = 0x06000000; \ ++} while (0) ++ ++/* ++ * UART0_CTS, UART0_RTS ++ */ ++#define __gpio_as_ctsrts() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0xc0000000; \ ++ REG_GPIO_PXSELS(3) = 0xc0000000; \ ++ REG_GPIO_PXTRGC(3) = 0xc0000000; \ ++ REG_GPIO_PXPES(3) = 0xc0000000; \ ++} while (0) ++ ++/* ++ * UART1_TxD, UART1_RxD1 ++ */ ++#define __gpio_as_uart1() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0xc0000000; \ ++ REG_GPIO_PXSELC(3) = 0xc0000000; \ ++ REG_GPIO_PXTRGS(3) = 0xc0000000; \ ++ REG_GPIO_PXPES(3) = 0xc0000000; \ ++} while (0) ++ ++/* ++ * LCD_D0~LCD_D15, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE ++ */ ++#define __gpio_as_lcd_16bit() \ ++do { \ ++ REG_GPIO_PXFUNS(2) = 0x003cffff; \ ++ REG_GPIO_PXSELC(2) = 0x003cffff; \ ++ REG_GPIO_PXPES(2) = 0x003cffff; \ ++} while (0) ++ ++/* ++ * LCD_D0~LCD_D17, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE ++ */ ++#define __gpio_as_lcd_18bit() \ ++do { \ ++ REG_GPIO_PXFUNS(2) = 0x003fffff; \ ++ REG_GPIO_PXSELC(2) = 0x003fffff; \ ++ REG_GPIO_PXPES(2) = 0x003fffff; \ ++} while (0) ++ ++/* ++ * LCD_PS, LCD_REV, LCD_CLS, LCD_SPL ++ */ ++#define __gpio_as_lcd_special() \ ++do { \ ++ REG_GPIO_PXFUNS(1) = 0x00060000; \ ++ REG_GPIO_PXSELC(1) = 0x00060000; \ ++ REG_GPIO_PXPES(1) = 0x00060000; \ ++ REG_GPIO_PXFUNS(2) = 0x00c00000; \ ++ REG_GPIO_PXSELC(2) = 0x00c00000; \ ++ REG_GPIO_PXPES(2) = 0x00c00000; \ ++} while (0) ++ ++/* LCD_D0~LCD_D7, SLCD_RS, SLCD_CS */ ++#define __gpio_as_slcd_8bit() \ ++do { \ ++ REG_GPIO_PXFUNS(2) = 0x001800ff; \ ++ REG_GPIO_PXSELC(2) = 0x001800ff; \ ++} while (0) ++ ++/* LCD_D0~LCD_D7, SLCD_RS, SLCD_CS */ ++#define __gpio_as_slcd_9bit() \ ++do { \ ++ REG_GPIO_PXFUNS(2) = 0x001801ff; \ ++ REG_GPIO_PXSELC(2) = 0x001801ff; \ ++} while (0) ++ ++/* LCD_D0~LCD_D15, SLCD_RS, SLCD_CS */ ++#define __gpio_as_slcd_16bit() \ ++do { \ ++ REG_GPIO_PXFUNS(2) = 0x0018ffff; \ ++ REG_GPIO_PXSELC(2) = 0x0018ffff; \ ++} while (0) ++ ++/* LCD_D0~LCD_D17, SLCD_RS, SLCD_CS */ ++#define __gpio_as_slcd_18bit() \ ++do { \ ++ REG_GPIO_PXFUNS(2) = 0x001bffff; \ ++ REG_GPIO_PXSELC(2) = 0x001bffff; \ ++} while (0) ++ ++/* ++ * CIM_D0~CIM_D7, CIM_MCLK, CIM_PCLK, CIM_VSYNC, CIM_HSYNC ++ */ ++#define __gpio_as_cim() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x0003c0ff; \ ++ REG_GPIO_PXSELC(3) = 0x0003c0ff; \ ++ REG_GPIO_PXPES(3) = 0x0003c0ff; \ ++} while (0) ++ ++/* ++ * SDATA_OUT, SDATA_IN, BIT_CLK, SYNC, SCLK_RESET ++ */ ++#define __gpio_as_aic() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x007c0000; \ ++ REG_GPIO_PXSELS(3) = 0x007c0000; \ ++ REG_GPIO_PXPES(3) = 0x007c0000; \ ++} while (0) ++ ++/* ++ * MSC_CMD, MSC_CLK, MSC_D0 ~ MSC_D3 ++ */ ++#define __gpio_as_msc() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x00003f00; \ ++ REG_GPIO_PXSELC(3) = 0x00003f00; \ ++ REG_GPIO_PXPES(3) = 0x00003f00; \ ++} while (0) ++ ++/* ++ * SSI_CS0, SSI_CLK, SSI_DT, SSI_DR ++ */ ++#define __gpio_as_ssi() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x003c0000; \ ++ REG_GPIO_PXSELC(3) = 0x003c0000; \ ++ REG_GPIO_PXPES(3) = 0x003c0000; \ ++} while (0) ++ ++/* ++ * I2C_SCK, I2C_SDA ++ */ ++#define __gpio_as_i2c() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x01800000; \ ++ REG_GPIO_PXSELS(3) = 0x01800000; \ ++ REG_GPIO_PXPES(3) = 0x01800000; \ ++} while (0) ++ ++/* ++ * PWM0 ++ */ ++#define __gpio_as_pwm0() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x00800000; \ ++ REG_GPIO_PXSELC(3) = 0x00800000; \ ++ REG_GPIO_PXPES(3) = 0x00800000; \ ++} while (0) ++ ++/* ++ * PWM1 ++ */ ++#define __gpio_as_pwm1() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x01000000; \ ++ REG_GPIO_PXSELC(3) = 0x01000000; \ ++ REG_GPIO_PXPES(3) = 0x01000000; \ ++} while (0) ++ ++/* ++ * PWM2 ++ */ ++#define __gpio_as_pwm2() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x02000000; \ ++ REG_GPIO_PXSELC(3) = 0x02000000; \ ++ REG_GPIO_PXPES(3) = 0x02000000; \ ++} while (0) ++ ++/* ++ * PWM3 ++ */ ++#define __gpio_as_pwm3() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x04000000; \ ++ REG_GPIO_PXSELC(3) = 0x04000000; \ ++ REG_GPIO_PXPES(3) = 0x04000000; \ ++} while (0) ++ ++/* ++ * PWM4 ++ */ ++#define __gpio_as_pwm4() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x08000000; \ ++ REG_GPIO_PXSELC(3) = 0x08000000; \ ++ REG_GPIO_PXPES(3) = 0x08000000; \ ++} while (0) ++ ++/* ++ * PWM5 ++ */ ++#define __gpio_as_pwm5() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x10000000; \ ++ REG_GPIO_PXSELC(3) = 0x10000000; \ ++ REG_GPIO_PXPES(3) = 0x10000000; \ ++} while (0) ++ ++/* ++ * PWM6 ++ */ ++#define __gpio_as_pwm6() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x40000000; \ ++ REG_GPIO_PXSELC(3) = 0x40000000; \ ++ REG_GPIO_PXPES(3) = 0x40000000; \ ++} while (0) ++ ++/* ++ * PWM7 ++ */ ++#define __gpio_as_pwm7() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x80000000; \ ++ REG_GPIO_PXSELC(3) = 0x80000000; \ ++ REG_GPIO_PXPES(3) = 0x80000000; \ ++} while (0) ++ ++/* ++ * n = 0 ~ 7 ++ */ ++#define __gpio_as_pwm(n) __gpio_as_pwm##n() ++ ++//------------------------------------------- ++// GPIO or Interrupt Mode ++ ++#define __gpio_get_port(p) (REG_GPIO_PXPIN(p)) ++ ++#define __gpio_port_as_output(p, o) \ ++do { \ ++ REG_GPIO_PXFUNC(p) = (1 << (o)); \ ++ REG_GPIO_PXSELC(p) = (1 << (o)); \ ++ REG_GPIO_PXDIRS(p) = (1 << (o)); \ ++} while (0) ++ ++#define __gpio_port_as_input(p, o) \ ++do { \ ++ REG_GPIO_PXFUNC(p) = (1 << (o)); \ ++ REG_GPIO_PXSELC(p) = (1 << (o)); \ ++ REG_GPIO_PXDIRC(p) = (1 << (o)); \ ++} while (0) ++ ++#define __gpio_as_output(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ __gpio_port_as_output(p, o); \ ++} while (0) ++ ++#define __gpio_as_input(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ __gpio_port_as_input(p, o); \ ++} while (0) ++ ++#define __gpio_set_pin(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXDATS(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_clear_pin(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXDATC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_get_pin(n) \ ++({ \ ++ unsigned int p, o, v; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ if (__gpio_get_port(p) & (1 << o)) \ ++ v = 1; \ ++ else \ ++ v = 0; \ ++ v; \ ++}) ++ ++#define __gpio_as_irq_high_level(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++ REG_GPIO_PXTRGC(p) = (1 << o); \ ++ REG_GPIO_PXFUNC(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++ REG_GPIO_PXDIRS(p) = (1 << o); \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_as_irq_low_level(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++ REG_GPIO_PXTRGC(p) = (1 << o); \ ++ REG_GPIO_PXFUNC(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++ REG_GPIO_PXDIRC(p) = (1 << o); \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_as_irq_rise_edge(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++ REG_GPIO_PXTRGS(p) = (1 << o); \ ++ REG_GPIO_PXFUNC(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++ REG_GPIO_PXDIRS(p) = (1 << o); \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_as_irq_fall_edge(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++ REG_GPIO_PXTRGS(p) = (1 << o); \ ++ REG_GPIO_PXFUNC(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++ REG_GPIO_PXDIRC(p) = (1 << o); \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_mask_irq(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_unmask_irq(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_ack_irq(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_get_irq() \ ++({ \ ++ unsigned int p, i, tmp, v = 0; \ ++ for (p = 3; p >= 0; p--) { \ ++ tmp = REG_GPIO_PXFLG(p); \ ++ for (i = 0; i < 32; i++) \ ++ if (tmp & (1 << i)) \ ++ v = (32*p + i); \ ++ } \ ++ v; \ ++}) ++ ++#define __gpio_group_irq(n) \ ++({ \ ++ register int tmp, i; \ ++ tmp = REG_GPIO_PXFLG((n)); \ ++ for (i=31;i>=0;i--) \ ++ if (tmp & (1 << i)) \ ++ break; \ ++ i; \ ++}) ++ ++#define __gpio_enable_pull(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXPEC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_disable_pull(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXPES(p) = (1 << o); \ ++} while (0) ++ ++ ++/*************************************************************************** ++ * CPM ++ ***************************************************************************/ ++#define __cpm_get_pllm() \ ++ ((REG_CPM_CPPCR & CPM_CPPCR_PLLM_MASK) >> CPM_CPPCR_PLLM_BIT) ++#define __cpm_get_plln() \ ++ ((REG_CPM_CPPCR & CPM_CPPCR_PLLN_MASK) >> CPM_CPPCR_PLLN_BIT) ++#define __cpm_get_pllod() \ ++ ((REG_CPM_CPPCR & CPM_CPPCR_PLLOD_MASK) >> CPM_CPPCR_PLLOD_BIT) ++ ++#define __cpm_get_cdiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_CDIV_MASK) >> CPM_CPCCR_CDIV_BIT) ++#define __cpm_get_hdiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_HDIV_MASK) >> CPM_CPCCR_HDIV_BIT) ++#define __cpm_get_pdiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_PDIV_MASK) >> CPM_CPCCR_PDIV_BIT) ++#define __cpm_get_mdiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_MDIV_MASK) >> CPM_CPCCR_MDIV_BIT) ++#define __cpm_get_ldiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_LDIV_MASK) >> CPM_CPCCR_LDIV_BIT) ++#define __cpm_get_udiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_UDIV_MASK) >> CPM_CPCCR_UDIV_BIT) ++#define __cpm_get_i2sdiv() \ ++ ((REG_CPM_I2SCDR & CPM_I2SCDR_I2SDIV_MASK) >> CPM_I2SCDR_I2SDIV_BIT) ++#define __cpm_get_pixdiv() \ ++ ((REG_CPM_LPCDR & CPM_LPCDR_PIXDIV_MASK) >> CPM_LPCDR_PIXDIV_BIT) ++#define __cpm_get_mscdiv() \ ++ ((REG_CPM_MSCCDR & CPM_MSCCDR_MSCDIV_MASK) >> CPM_MSCCDR_MSCDIV_BIT) ++#define __cpm_get_uhcdiv() \ ++ ((REG_CPM_UHCCDR & CPM_UHCCDR_UHCDIV_MASK) >> CPM_UHCCDR_UHCDIV_BIT) ++#define __cpm_get_ssidiv() \ ++ ((REG_CPM_SSICCDR & CPM_SSICDR_SSICDIV_MASK) >> CPM_SSICDR_SSIDIV_BIT) ++ ++#define __cpm_set_cdiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_CDIV_MASK) | ((v) << (CPM_CPCCR_CDIV_BIT))) ++#define __cpm_set_hdiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_HDIV_MASK) | ((v) << (CPM_CPCCR_HDIV_BIT))) ++#define __cpm_set_pdiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_PDIV_MASK) | ((v) << (CPM_CPCCR_PDIV_BIT))) ++#define __cpm_set_mdiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_MDIV_MASK) | ((v) << (CPM_CPCCR_MDIV_BIT))) ++#define __cpm_set_ldiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_LDIV_MASK) | ((v) << (CPM_CPCCR_LDIV_BIT))) ++#define __cpm_set_udiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_UDIV_MASK) | ((v) << (CPM_CPCCR_UDIV_BIT))) ++#define __cpm_set_i2sdiv(v) \ ++ (REG_CPM_I2SCDR = (REG_CPM_I2SCDR & ~CPM_I2SCDR_I2SDIV_MASK) | ((v) << (CPM_I2SCDR_I2SDIV_BIT))) ++#define __cpm_set_pixdiv(v) \ ++ (REG_CPM_LPCDR = (REG_CPM_LPCDR & ~CPM_LPCDR_PIXDIV_MASK) | ((v) << (CPM_LPCDR_PIXDIV_BIT))) ++#define __cpm_set_mscdiv(v) \ ++ (REG_CPM_MSCCDR = (REG_CPM_MSCCDR & ~CPM_MSCCDR_MSCDIV_MASK) | ((v) << (CPM_MSCCDR_MSCDIV_BIT))) ++#define __cpm_set_uhcdiv(v) \ ++ (REG_CPM_UHCCDR = (REG_CPM_UHCCDR & ~CPM_UHCCDR_UHCDIV_MASK) | ((v) << (CPM_UHCCDR_UHCDIV_BIT))) ++#define __cpm_ssiclk_select_exclk() \ ++ (REG_CPM_SSICDR &= ~CPM_SSICDR_SCS) ++#define __cpm_ssiclk_select_pllout() \ ++ (REG_CPM_SSICDR |= CPM_SSICDR_SCS) ++#define __cpm_set_ssidiv(v) \ ++ (REG_CPM_SSICDR = (REG_CPM_SSICDR & ~CPM_SSICDR_SSIDIV_MASK) | ((v) << (CPM_SSICDR_SSIDIV_BIT))) ++ ++#define __cpm_select_i2sclk_exclk() (REG_CPM_CPCCR &= ~CPM_CPCCR_I2CS) ++#define __cpm_select_i2sclk_pll() (REG_CPM_CPCCR |= CPM_CPCCR_I2CS) ++#define __cpm_enable_cko() (REG_CPM_CPCCR |= CPM_CPCCR_CLKOEN) ++#define __cpm_select_usbclk_exclk() (REG_CPM_CPCCR &= ~CPM_CPCCR_UCS) ++#define __cpm_select_usbclk_pll() (REG_CPM_CPCCR |= CPM_CPCCR_UCS) ++#define __cpm_enable_pll_change() (REG_CPM_CPCCR |= CPM_CPCCR_CE) ++#define __cpm_pllout_direct() (REG_CPM_CPCCR |= CPM_CPCCR_PCS) ++#define __cpm_pllout_div2() (REG_CPM_CPCCR &= ~CPM_CPCCR_PCS) ++ ++#define __cpm_pll_is_on() (REG_CPM_CPPCR & CPM_CPPCR_PLLS) ++#define __cpm_pll_bypass() (REG_CPM_CPPCR |= CPM_CPPCR_PLLBP) ++#define __cpm_pll_enable() (REG_CPM_CPPCR |= CPM_CPPCR_PLLEN) ++ ++#define __cpm_get_cclk_doze_duty() \ ++ ((REG_CPM_LCR & CPM_LCR_DOZE_DUTY_MASK) >> CPM_LCR_DOZE_DUTY_BIT) ++#define __cpm_set_cclk_doze_duty(v) \ ++ (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_DOZE_DUTY_MASK) | ((v) << (CPM_LCR_DOZE_DUTY_BIT))) ++ ++#define __cpm_doze_mode() (REG_CPM_LCR |= CPM_LCR_DOZE_ON) ++#define __cpm_idle_mode() \ ++ (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_LPM_MASK) | CPM_LCR_LPM_IDLE) ++#define __cpm_sleep_mode() \ ++ (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_LPM_MASK) | CPM_LCR_LPM_SLEEP) ++ ++#define __cpm_stop_all() (REG_CPM_CLKGR = 0x7fff) ++#define __cpm_stop_uart1() (REG_CPM_CLKGR |= CPM_CLKGR_UART1) ++#define __cpm_stop_uhc() (REG_CPM_CLKGR |= CPM_CLKGR_UHC) ++#define __cpm_stop_ipu() (REG_CPM_CLKGR |= CPM_CLKGR_IPU) ++#define __cpm_stop_dmac() (REG_CPM_CLKGR |= CPM_CLKGR_DMAC) ++#define __cpm_stop_udc() (REG_CPM_CLKGR |= CPM_CLKGR_UDC) ++#define __cpm_stop_lcd() (REG_CPM_CLKGR |= CPM_CLKGR_LCD) ++#define __cpm_stop_cim() (REG_CPM_CLKGR |= CPM_CLKGR_CIM) ++#define __cpm_stop_sadc() (REG_CPM_CLKGR |= CPM_CLKGR_SADC) ++#define __cpm_stop_msc() (REG_CPM_CLKGR |= CPM_CLKGR_MSC) ++#define __cpm_stop_aic1() (REG_CPM_CLKGR |= CPM_CLKGR_AIC1) ++#define __cpm_stop_aic2() (REG_CPM_CLKGR |= CPM_CLKGR_AIC2) ++#define __cpm_stop_ssi() (REG_CPM_CLKGR |= CPM_CLKGR_SSI) ++#define __cpm_stop_i2c() (REG_CPM_CLKGR |= CPM_CLKGR_I2C) ++#define __cpm_stop_rtc() (REG_CPM_CLKGR |= CPM_CLKGR_RTC) ++#define __cpm_stop_tcu() (REG_CPM_CLKGR |= CPM_CLKGR_TCU) ++#define __cpm_stop_uart0() (REG_CPM_CLKGR |= CPM_CLKGR_UART0) ++ ++#define __cpm_start_all() (REG_CPM_CLKGR = 0x0) ++#define __cpm_start_uart1() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART1) ++#define __cpm_start_uhc() (REG_CPM_CLKGR &= ~CPM_CLKGR_UHC) ++#define __cpm_start_ipu() (REG_CPM_CLKGR &= ~CPM_CLKGR_IPU) ++#define __cpm_start_dmac() (REG_CPM_CLKGR &= ~CPM_CLKGR_DMAC) ++#define __cpm_start_udc() (REG_CPM_CLKGR &= ~CPM_CLKGR_UDC) ++#define __cpm_start_lcd() (REG_CPM_CLKGR &= ~CPM_CLKGR_LCD) ++#define __cpm_start_cim() (REG_CPM_CLKGR &= ~CPM_CLKGR_CIM) ++#define __cpm_start_sadc() (REG_CPM_CLKGR &= ~CPM_CLKGR_SADC) ++#define __cpm_start_msc() (REG_CPM_CLKGR &= ~CPM_CLKGR_MSC) ++#define __cpm_start_aic1() (REG_CPM_CLKGR &= ~CPM_CLKGR_AIC1) ++#define __cpm_start_aic2() (REG_CPM_CLKGR &= ~CPM_CLKGR_AIC2) ++#define __cpm_start_ssi() (REG_CPM_CLKGR &= ~CPM_CLKGR_SSI) ++#define __cpm_start_i2c() (REG_CPM_CLKGR &= ~CPM_CLKGR_I2C) ++#define __cpm_start_rtc() (REG_CPM_CLKGR &= ~CPM_CLKGR_RTC) ++#define __cpm_start_tcu() (REG_CPM_CLKGR &= ~CPM_CLKGR_TCU) ++#define __cpm_start_uart0() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART0) ++ ++#define __cpm_get_o1st() \ ++ ((REG_CPM_SCR & CPM_SCR_O1ST_MASK) >> CPM_SCR_O1ST_BIT) ++#define __cpm_set_o1st(v) \ ++ (REG_CPM_SCR = (REG_CPM_SCR & ~CPM_SCR_O1ST_MASK) | ((v) << (CPM_SCR_O1ST_BIT))) ++#define __cpm_suspend_usbphy() (REG_CPM_SCR |= CPM_SCR_USBPHY_SUSPEND) ++#define __cpm_enable_osc_in_sleep() (REG_CPM_SCR |= CPM_SCR_OSC_ENABLE) ++ ++ ++/*************************************************************************** ++ * TCU ++ ***************************************************************************/ ++// where 'n' is the TCU channel ++#define __tcu_select_extalclk(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_EXT_EN) ++#define __tcu_select_rtcclk(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_RTC_EN) ++#define __tcu_select_pclk(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_PCK_EN) ++ ++#define __tcu_select_clk_div1(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE1) ++#define __tcu_select_clk_div4(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE4) ++#define __tcu_select_clk_div16(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE16) ++#define __tcu_select_clk_div64(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE64) ++#define __tcu_select_clk_div256(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE256) ++#define __tcu_select_clk_div1024(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE1024) ++ ++#define __tcu_enable_pwm_output(n) ( REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_EN ) ++#define __tcu_disable_pwm_output(n) ( REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_EN ) ++ ++#define __tcu_init_pwm_output_high(n) ( REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_INITL_HIGH ) ++#define __tcu_init_pwm_output_low(n) ( REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_INITL_HIGH ) ++ ++#define __tcu_set_pwm_output_shutdown_graceful(n) ( REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_SD ) ++#define __tcu_set_pwm_output_shutdown_abrupt(n) ( REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_SD ) ++ ++#define __tcu_start_counter(n) ( REG_TCU_TESR |= (1 << (n)) ) ++#define __tcu_stop_counter(n) ( REG_TCU_TECR |= (1 << (n)) ) ++ ++#define __tcu_half_match_flag(n) ( REG_TCU_TFR & (1 << ((n) + 16)) ) ++#define __tcu_full_match_flag(n) ( REG_TCU_TFR & (1 << (n)) ) ++#define __tcu_set_half_match_flag(n) ( REG_TCU_TFSR = (1 << ((n) + 16)) ) ++#define __tcu_set_full_match_flag(n) ( REG_TCU_TFSR = (1 << (n)) ) ++#define __tcu_clear_half_match_flag(n) ( REG_TCU_TFCR = (1 << ((n) + 16)) ) ++#define __tcu_clear_full_match_flag(n) ( REG_TCU_TFCR = (1 << (n)) ) ++#define __tcu_mask_half_match_irq(n) ( REG_TCU_TMSR = (1 << ((n) + 16)) ) ++#define __tcu_mask_full_match_irq(n) ( REG_TCU_TMSR = (1 << (n)) ) ++#define __tcu_unmask_half_match_irq(n) ( REG_TCU_TMCR = (1 << ((n) + 16)) ) ++#define __tcu_unmask_full_match_irq(n) ( REG_TCU_TMCR = (1 << (n)) ) ++ ++#define __tcu_wdt_clock_stopped() ( REG_TCU_TSR & TCU_TSSR_WDTSC ) ++#define __tcu_timer_clock_stopped(n) ( REG_TCU_TSR & (1 << (n)) ) ++ ++#define __tcu_start_wdt_clock() ( REG_TCU_TSCR = TCU_TSSR_WDTSC ) ++#define __tcu_start_timer_clock(n) ( REG_TCU_TSCR = (1 << (n)) ) ++ ++#define __tcu_stop_wdt_clock() ( REG_TCU_TSSR = TCU_TSSR_WDTSC ) ++#define __tcu_stop_timer_clock(n) ( REG_TCU_TSSR = (1 << (n)) ) ++ ++#define __tcu_get_count(n) ( REG_TCU_TCNT((n)) ) ++#define __tcu_set_count(n,v) ( REG_TCU_TCNT((n)) = (v) ) ++#define __tcu_set_full_data(n,v) ( REG_TCU_TDFR((n)) = (v) ) ++#define __tcu_set_half_data(n,v) ( REG_TCU_TDHR((n)) = (v) ) ++ ++ ++/*************************************************************************** ++ * WDT ++ ***************************************************************************/ ++#define __wdt_start() ( REG_WDT_TCER |= WDT_TCER_TCEN ) ++#define __wdt_stop() ( REG_WDT_TCER &= ~WDT_TCER_TCEN ) ++#define __wdt_set_count(v) ( REG_WDT_TCNT = (v) ) ++#define __wdt_set_data(v) ( REG_WDT_TDR = (v) ) ++ ++#define __wdt_select_extalclk() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_EXT_EN) ++#define __wdt_select_rtcclk() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_RTC_EN) ++#define __wdt_select_pclk() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_PCK_EN) ++ ++#define __wdt_select_clk_div1() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE1) ++#define __wdt_select_clk_div4() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE4) ++#define __wdt_select_clk_div16() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE16) ++#define __wdt_select_clk_div64() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE64) ++#define __wdt_select_clk_div256() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE256) ++#define __wdt_select_clk_div1024() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE1024) ++ ++ ++/*************************************************************************** ++ * UART ++ ***************************************************************************/ ++ ++#define __uart_enable(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) |= UARTFCR_UUE | UARTFCR_FE ) ++#define __uart_disable(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) = ~UARTFCR_UUE ) ++ ++#define __uart_enable_transmit_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_TIE ) ++#define __uart_disable_transmit_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~UARTIER_TIE ) ++ ++#define __uart_enable_receive_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE ) ++#define __uart_disable_receive_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~(UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE) ) ++ ++#define __uart_enable_loopback(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) |= UARTMCR_LOOP ) ++#define __uart_disable_loopback(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) &= ~UARTMCR_LOOP ) ++ ++#define __uart_set_8n1(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) = UARTLCR_WLEN_8 ) ++ ++#define __uart_set_baud(n, devclk, baud) \ ++ do { \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) |= UARTLCR_DLAB; \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_DLLR) = (devclk / 16 / baud) & 0xff; \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_DLHR) = ((devclk / 16 / baud) >> 8) & 0xff; \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) &= ~UARTLCR_DLAB; \ ++ } while (0) ++ ++#define __uart_parity_error(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_PER) != 0 ) ++ ++#define __uart_clear_errors(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) &= ~(UARTLSR_ORER | UARTLSR_BRK | UARTLSR_FER | UARTLSR_PER | UARTLSR_RFER) ) ++ ++#define __uart_transmit_fifo_empty(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TDRQ) != 0 ) ++ ++#define __uart_transmit_end(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TEMT) != 0 ) ++ ++#define __uart_transmit_char(n, ch) \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_TDR) = (ch) ++ ++#define __uart_receive_fifo_full(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) ++ ++#define __uart_receive_ready(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) ++ ++#define __uart_receive_char(n) \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_RDR) ++ ++#define __uart_disable_irda() \ ++ ( REG8(IRDA_BASE + OFF_SIRCR) &= ~(SIRCR_TSIRE | SIRCR_RSIRE) ) ++#define __uart_enable_irda() \ ++ /* Tx high pulse as 0, Rx low pulse as 0 */ \ ++ ( REG8(IRDA_BASE + OFF_SIRCR) = SIRCR_TSIRE | SIRCR_RSIRE | SIRCR_RXPL | SIRCR_TPWS ) ++ ++ ++/*************************************************************************** ++ * DMAC ++ ***************************************************************************/ ++ ++/* n is the DMA channel (0 - 5) */ ++ ++#define __dmac_enable_module() \ ++ ( REG_DMAC_DMACR |= DMAC_DMACR_DMAE | DMAC_DMACR_PR_RR ) ++#define __dmac_disable_module() \ ++ ( REG_DMAC_DMACR &= ~DMAC_DMACR_DMAE ) ++ ++/* p=0,1,2,3 */ ++#define __dmac_set_priority(p) \ ++do { \ ++ REG_DMAC_DMACR &= ~DMAC_DMACR_PR_MASK; \ ++ REG_DMAC_DMACR |= ((p) << DMAC_DMACR_PR_BIT); \ ++} while (0) ++ ++#define __dmac_test_halt_error() ( REG_DMAC_DMACR & DMAC_DMACR_HLT ) ++#define __dmac_test_addr_error() ( REG_DMAC_DMACR & DMAC_DMACR_AR ) ++ ++#define __dmac_enable_descriptor(n) \ ++ ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_NDES ) ++#define __dmac_disable_descriptor(n) \ ++ ( REG_DMAC_DCCSR((n)) |= DMAC_DCCSR_NDES ) ++ ++#define __dmac_enable_channel(n) \ ++ ( REG_DMAC_DCCSR((n)) |= DMAC_DCCSR_EN ) ++#define __dmac_disable_channel(n) \ ++ ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_EN ) ++#define __dmac_channel_enabled(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_EN ) ++ ++#define __dmac_channel_enable_irq(n) \ ++ ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_TIE ) ++#define __dmac_channel_disable_irq(n) \ ++ ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_TIE ) ++ ++#define __dmac_channel_transmit_halt_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_HLT ) ++#define __dmac_channel_transmit_end_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_TT ) ++#define __dmac_channel_address_error_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_AR ) ++#define __dmac_channel_count_terminated_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_CT ) ++#define __dmac_channel_descriptor_invalid_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_INV ) ++ ++#define __dmac_channel_clear_transmit_halt(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_HLT ) ++#define __dmac_channel_clear_transmit_end(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_TT ) ++#define __dmac_channel_clear_address_error(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_AR ) ++#define __dmac_channel_clear_count_terminated(n) \ ++ ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_CT ) ++#define __dmac_channel_clear_descriptor_invalid(n) \ ++ ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_INV ) ++ ++#define __dmac_channel_set_single_mode(n) \ ++ ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_TM ) ++#define __dmac_channel_set_block_mode(n) \ ++ ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_TM ) ++ ++#define __dmac_channel_set_transfer_unit_32bit(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_32BIT; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_16bit(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_16BIT; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_8bit(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_8BIT; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_16byte(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_16BYTE; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_32byte(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_32BYTE; \ ++} while (0) ++ ++/* w=8,16,32 */ ++#define __dmac_channel_set_dest_port_width(n,w) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DWDH_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DWDH_##w; \ ++} while (0) ++ ++/* w=8,16,32 */ ++#define __dmac_channel_set_src_port_width(n,w) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_SWDH_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_SWDH_##w; \ ++} while (0) ++ ++/* v=0-15 */ ++#define __dmac_channel_set_rdil(n,v) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_RDIL_MASK; \ ++ REG_DMAC_DCMD((n) |= ((v) << DMAC_DCMD_RDIL_BIT); \ ++} while (0) ++ ++#define __dmac_channel_dest_addr_fixed(n) \ ++ ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DAI ) ++#define __dmac_channel_dest_addr_increment(n) \ ++ ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_DAI ) ++ ++#define __dmac_channel_src_addr_fixed(n) \ ++ ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_SAI ) ++#define __dmac_channel_src_addr_increment(n) \ ++ ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_SAI ) ++ ++#define __dmac_channel_set_doorbell(n) \ ++ ( REG_DMAC_DMADBSR = (1 << (n)) ) ++ ++#define __dmac_channel_irq_detected(n) ( REG_DMAC_DMAIPR & (1 << (n)) ) ++#define __dmac_channel_ack_irq(n) ( REG_DMAC_DMAIPR &= ~(1 << (n)) ) ++ ++static __inline__ int __dmac_get_irq(void) ++{ ++ int i; ++ for (i = 0; i < MAX_DMA_NUM; i++) ++ if (__dmac_channel_irq_detected(i)) ++ return i; ++ return -1; ++} ++ ++ ++/*************************************************************************** ++ * AIC (AC'97 & I2S Controller) ++ ***************************************************************************/ ++ ++#define __aic_enable() ( REG_AIC_FR |= AIC_FR_ENB ) ++#define __aic_disable() ( REG_AIC_FR &= ~AIC_FR_ENB ) ++ ++#define __aic_select_ac97() ( REG_AIC_FR &= ~AIC_FR_AUSEL ) ++#define __aic_select_i2s() ( REG_AIC_FR |= AIC_FR_AUSEL ) ++ ++#define __aic_play_zero() ( REG_AIC_FR &= ~AIC_FR_LSMP ) ++#define __aic_play_lastsample() ( REG_AIC_FR |= AIC_FR_LSMP ) ++ ++#define __i2s_as_master() ( REG_AIC_FR |= AIC_FR_BCKD | AIC_FR_SYNCD ) ++#define __i2s_as_slave() ( REG_AIC_FR &= ~(AIC_FR_BCKD | AIC_FR_SYNCD) ) ++#define __aic_reset_status() ( REG_AIC_FR & AIC_FR_RST ) ++ ++#define __aic_reset() \ ++do { \ ++ REG_AIC_FR |= AIC_FR_RST; \ ++} while(0) ++ ++ ++#define __aic_set_transmit_trigger(n) \ ++do { \ ++ REG_AIC_FR &= ~AIC_FR_TFTH_MASK; \ ++ REG_AIC_FR |= ((n) << AIC_FR_TFTH_BIT); \ ++} while(0) ++ ++#define __aic_set_receive_trigger(n) \ ++do { \ ++ REG_AIC_FR &= ~AIC_FR_RFTH_MASK; \ ++ REG_AIC_FR |= ((n) << AIC_FR_RFTH_BIT); \ ++} while(0) ++ ++#define __aic_enable_record() ( REG_AIC_CR |= AIC_CR_EREC ) ++#define __aic_disable_record() ( REG_AIC_CR &= ~AIC_CR_EREC ) ++#define __aic_enable_replay() ( REG_AIC_CR |= AIC_CR_ERPL ) ++#define __aic_disable_replay() ( REG_AIC_CR &= ~AIC_CR_ERPL ) ++#define __aic_enable_loopback() ( REG_AIC_CR |= AIC_CR_ENLBF ) ++#define __aic_disable_loopback() ( REG_AIC_CR &= ~AIC_CR_ENLBF ) ++ ++#define __aic_flush_fifo() ( REG_AIC_CR |= AIC_CR_FLUSH ) ++#define __aic_unflush_fifo() ( REG_AIC_CR &= ~AIC_CR_FLUSH ) ++ ++#define __aic_enable_transmit_intr() \ ++ ( REG_AIC_CR |= (AIC_CR_ETFS | AIC_CR_ETUR) ) ++#define __aic_disable_transmit_intr() \ ++ ( REG_AIC_CR &= ~(AIC_CR_ETFS | AIC_CR_ETUR) ) ++#define __aic_enable_receive_intr() \ ++ ( REG_AIC_CR |= (AIC_CR_ERFS | AIC_CR_EROR) ) ++#define __aic_disable_receive_intr() \ ++ ( REG_AIC_CR &= ~(AIC_CR_ERFS | AIC_CR_EROR) ) ++ ++#define __aic_enable_transmit_dma() ( REG_AIC_CR |= AIC_CR_TDMS ) ++#define __aic_disable_transmit_dma() ( REG_AIC_CR &= ~AIC_CR_TDMS ) ++#define __aic_enable_receive_dma() ( REG_AIC_CR |= AIC_CR_RDMS ) ++#define __aic_disable_receive_dma() ( REG_AIC_CR &= ~AIC_CR_RDMS ) ++ ++#define __aic_enable_mono2stereo() ( REG_AIC_CR |= AIC_CR_M2S ) ++#define __aic_disable_mono2stereo() ( REG_AIC_CR &= ~AIC_CR_M2S ) ++#define __aic_enable_byteswap() ( REG_AIC_CR |= AIC_CR_ENDSW ) ++#define __aic_disable_byteswap() ( REG_AIC_CR &= ~AIC_CR_ENDSW ) ++#define __aic_enable_unsignadj() ( REG_AIC_CR |= AIC_CR_AVSTSU ) ++#define __aic_disable_unsignadj() ( REG_AIC_CR &= ~AIC_CR_AVSTSU ) ++ ++#define AC97_PCM_XS_L_FRONT AIC_ACCR1_XS_SLOT3 ++#define AC97_PCM_XS_R_FRONT AIC_ACCR1_XS_SLOT4 ++#define AC97_PCM_XS_CENTER AIC_ACCR1_XS_SLOT6 ++#define AC97_PCM_XS_L_SURR AIC_ACCR1_XS_SLOT7 ++#define AC97_PCM_XS_R_SURR AIC_ACCR1_XS_SLOT8 ++#define AC97_PCM_XS_LFE AIC_ACCR1_XS_SLOT9 ++ ++#define AC97_PCM_RS_L_FRONT AIC_ACCR1_RS_SLOT3 ++#define AC97_PCM_RS_R_FRONT AIC_ACCR1_RS_SLOT4 ++#define AC97_PCM_RS_CENTER AIC_ACCR1_RS_SLOT6 ++#define AC97_PCM_RS_L_SURR AIC_ACCR1_RS_SLOT7 ++#define AC97_PCM_RS_R_SURR AIC_ACCR1_RS_SLOT8 ++#define AC97_PCM_RS_LFE AIC_ACCR1_RS_SLOT9 ++ ++#define __ac97_set_xs_none() ( REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK ) ++#define __ac97_set_xs_mono() \ ++do { \ ++ REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK; \ ++ REG_AIC_ACCR1 |= AC97_PCM_XS_R_FRONT; \ ++} while(0) ++#define __ac97_set_xs_stereo() \ ++do { \ ++ REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK; \ ++ REG_AIC_ACCR1 |= AC97_PCM_XS_L_FRONT | AC97_PCM_XS_R_FRONT; \ ++} while(0) ++ ++/* In fact, only stereo is support now. */ ++#define __ac97_set_rs_none() ( REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK ) ++#define __ac97_set_rs_mono() \ ++do { \ ++ REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK; \ ++ REG_AIC_ACCR1 |= AC97_PCM_RS_R_FRONT; \ ++} while(0) ++#define __ac97_set_rs_stereo() \ ++do { \ ++ REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK; \ ++ REG_AIC_ACCR1 |= AC97_PCM_RS_L_FRONT | AC97_PCM_RS_R_FRONT; \ ++} while(0) ++ ++#define __ac97_warm_reset_codec() \ ++ do { \ ++ REG_AIC_ACCR2 |= AIC_ACCR2_SA; \ ++ REG_AIC_ACCR2 |= AIC_ACCR2_SS; \ ++ udelay(2); \ ++ REG_AIC_ACCR2 &= ~AIC_ACCR2_SS; \ ++ REG_AIC_ACCR2 &= ~AIC_ACCR2_SA; \ ++ } while (0) ++ ++#define __ac97_cold_reset_codec() \ ++ do { \ ++ REG_AIC_ACCR2 |= AIC_ACCR2_SR; \ ++ udelay(2); \ ++ REG_AIC_ACCR2 &= ~AIC_ACCR2_SR; \ ++ } while (0) ++ ++/* n=8,16,18,20 */ ++#define __ac97_set_iass(n) \ ++ ( REG_AIC_ACCR2 = (REG_AIC_ACCR2 & ~AIC_ACCR2_IASS_MASK) | AIC_ACCR2_IASS_##n##BIT ) ++#define __ac97_set_oass(n) \ ++ ( REG_AIC_ACCR2 = (REG_AIC_ACCR2 & ~AIC_ACCR2_OASS_MASK) | AIC_ACCR2_OASS_##n##BIT ) ++ ++#define __i2s_select_i2s() ( REG_AIC_I2SCR &= ~AIC_I2SCR_AMSL ) ++#define __i2s_select_msbjustified() ( REG_AIC_I2SCR |= AIC_I2SCR_AMSL ) ++ ++/* n=8,16,18,20,24 */ ++/*#define __i2s_set_sample_size(n) \ ++ ( REG_AIC_I2SCR |= (REG_AIC_I2SCR & ~AIC_I2SCR_WL_MASK) | AIC_I2SCR_WL_##n##BIT )*/ ++ ++#define __i2s_set_oss_sample_size(n) \ ++ ( REG_AIC_CR = (REG_AIC_CR & ~AIC_CR_OSS_MASK) | AIC_CR_OSS_##n##BIT ) ++#define __i2s_set_iss_sample_size(n) \ ++ ( REG_AIC_CR = (REG_AIC_CR & ~AIC_CR_ISS_MASK) | AIC_CR_ISS_##n##BIT ) ++ ++#define __i2s_stop_bitclk() ( REG_AIC_I2SCR |= AIC_I2SCR_STPBK ) ++#define __i2s_start_bitclk() ( REG_AIC_I2SCR &= ~AIC_I2SCR_STPBK ) ++ ++#define __aic_transmit_request() ( REG_AIC_SR & AIC_SR_TFS ) ++#define __aic_receive_request() ( REG_AIC_SR & AIC_SR_RFS ) ++#define __aic_transmit_underrun() ( REG_AIC_SR & AIC_SR_TUR ) ++#define __aic_receive_overrun() ( REG_AIC_SR & AIC_SR_ROR ) ++ ++#define __aic_clear_errors() ( REG_AIC_SR &= ~(AIC_SR_TUR | AIC_SR_ROR) ) ++ ++#define __aic_get_transmit_resident() \ ++ ( (REG_AIC_SR & AIC_SR_TFL_MASK) >> AIC_SR_TFL_BIT ) ++#define __aic_get_receive_count() \ ++ ( (REG_AIC_SR & AIC_SR_RFL_MASK) >> AIC_SR_RFL_BIT ) ++ ++#define __ac97_command_transmitted() ( REG_AIC_ACSR & AIC_ACSR_CADT ) ++#define __ac97_status_received() ( REG_AIC_ACSR & AIC_ACSR_SADR ) ++#define __ac97_status_receive_timeout() ( REG_AIC_ACSR & AIC_ACSR_RSTO ) ++#define __ac97_codec_is_low_power_mode() ( REG_AIC_ACSR & AIC_ACSR_CLPM ) ++#define __ac97_codec_is_ready() ( REG_AIC_ACSR & AIC_ACSR_CRDY ) ++#define __ac97_slot_error_detected() ( REG_AIC_ACSR & AIC_ACSR_SLTERR ) ++#define __ac97_clear_slot_error() ( REG_AIC_ACSR &= ~AIC_ACSR_SLTERR ) ++ ++#define __i2s_is_busy() ( REG_AIC_I2SSR & AIC_I2SSR_BSY ) ++ ++#define CODEC_READ_CMD (1 << 19) ++#define CODEC_WRITE_CMD (0 << 19) ++#define CODEC_REG_INDEX_BIT 12 ++#define CODEC_REG_INDEX_MASK (0x7f << CODEC_REG_INDEX_BIT) /* 18:12 */ ++#define CODEC_REG_DATA_BIT 4 ++#define CODEC_REG_DATA_MASK (0x0ffff << 4) /* 19:4 */ ++ ++#define __ac97_out_rcmd_addr(reg) \ ++do { \ ++ REG_AIC_ACCAR = CODEC_READ_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ ++} while (0) ++ ++#define __ac97_out_wcmd_addr(reg) \ ++do { \ ++ REG_AIC_ACCAR = CODEC_WRITE_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ ++} while (0) ++ ++#define __ac97_out_data(value) \ ++do { \ ++ REG_AIC_ACCDR = ((value) << CODEC_REG_DATA_BIT); \ ++} while (0) ++ ++#define __ac97_in_data() \ ++ ( (REG_AIC_ACSDR & CODEC_REG_DATA_MASK) >> CODEC_REG_DATA_BIT ) ++ ++#define __ac97_in_status_addr() \ ++ ( (REG_AIC_ACSAR & CODEC_REG_INDEX_MASK) >> CODEC_REG_INDEX_BIT ) ++ ++#define __i2s_set_sample_rate(i2sclk, sync) \ ++ ( REG_AIC_I2SDIV = ((i2sclk) / (4*64)) / (sync) ) ++ ++#define __aic_write_tfifo(v) ( REG_AIC_DR = (v) ) ++#define __aic_read_rfifo() ( REG_AIC_DR ) ++ ++#define __aic_internal_codec() ( REG_AIC_FR |= AIC_FR_ICDC ) ++#define __aic_external_codec() ( REG_AIC_FR &= ~AIC_FR_ICDC ) ++ ++// ++// Define next ops for AC97 compatible ++// ++ ++#define AC97_ACSR AIC_ACSR ++ ++#define __ac97_enable() __aic_enable(); __aic_select_ac97() ++#define __ac97_disable() __aic_disable() ++#define __ac97_reset() __aic_reset() ++ ++#define __ac97_set_transmit_trigger(n) __aic_set_transmit_trigger(n) ++#define __ac97_set_receive_trigger(n) __aic_set_receive_trigger(n) ++ ++#define __ac97_enable_record() __aic_enable_record() ++#define __ac97_disable_record() __aic_disable_record() ++#define __ac97_enable_replay() __aic_enable_replay() ++#define __ac97_disable_replay() __aic_disable_replay() ++#define __ac97_enable_loopback() __aic_enable_loopback() ++#define __ac97_disable_loopback() __aic_disable_loopback() ++ ++#define __ac97_enable_transmit_dma() __aic_enable_transmit_dma() ++#define __ac97_disable_transmit_dma() __aic_disable_transmit_dma() ++#define __ac97_enable_receive_dma() __aic_enable_receive_dma() ++#define __ac97_disable_receive_dma() __aic_disable_receive_dma() ++ ++#define __ac97_transmit_request() __aic_transmit_request() ++#define __ac97_receive_request() __aic_receive_request() ++#define __ac97_transmit_underrun() __aic_transmit_underrun() ++#define __ac97_receive_overrun() __aic_receive_overrun() ++ ++#define __ac97_clear_errors() __aic_clear_errors() ++ ++#define __ac97_get_transmit_resident() __aic_get_transmit_resident() ++#define __ac97_get_receive_count() __aic_get_receive_count() ++ ++#define __ac97_enable_transmit_intr() __aic_enable_transmit_intr() ++#define __ac97_disable_transmit_intr() __aic_disable_transmit_intr() ++#define __ac97_enable_receive_intr() __aic_enable_receive_intr() ++#define __ac97_disable_receive_intr() __aic_disable_receive_intr() ++ ++#define __ac97_write_tfifo(v) __aic_write_tfifo(v) ++#define __ac97_read_rfifo() __aic_read_rfifo() ++ ++// ++// Define next ops for I2S compatible ++// ++ ++#define I2S_ACSR AIC_I2SSR ++ ++#define __i2s_enable() __aic_enable(); __aic_select_i2s() ++#define __i2s_disable() __aic_disable() ++#define __i2s_reset() __aic_reset() ++ ++#define __i2s_set_transmit_trigger(n) __aic_set_transmit_trigger(n) ++#define __i2s_set_receive_trigger(n) __aic_set_receive_trigger(n) ++ ++#define __i2s_enable_record() __aic_enable_record() ++#define __i2s_disable_record() __aic_disable_record() ++#define __i2s_enable_replay() __aic_enable_replay() ++#define __i2s_disable_replay() __aic_disable_replay() ++#define __i2s_enable_loopback() __aic_enable_loopback() ++#define __i2s_disable_loopback() __aic_disable_loopback() ++ ++#define __i2s_enable_transmit_dma() __aic_enable_transmit_dma() ++#define __i2s_disable_transmit_dma() __aic_disable_transmit_dma() ++#define __i2s_enable_receive_dma() __aic_enable_receive_dma() ++#define __i2s_disable_receive_dma() __aic_disable_receive_dma() ++ ++#define __i2s_transmit_request() __aic_transmit_request() ++#define __i2s_receive_request() __aic_receive_request() ++#define __i2s_transmit_underrun() __aic_transmit_underrun() ++#define __i2s_receive_overrun() __aic_receive_overrun() ++ ++#define __i2s_clear_errors() __aic_clear_errors() ++ ++#define __i2s_get_transmit_resident() __aic_get_transmit_resident() ++#define __i2s_get_receive_count() __aic_get_receive_count() ++ ++#define __i2s_enable_transmit_intr() __aic_enable_transmit_intr() ++#define __i2s_disable_transmit_intr() __aic_disable_transmit_intr() ++#define __i2s_enable_receive_intr() __aic_enable_receive_intr() ++#define __i2s_disable_receive_intr() __aic_disable_receive_intr() ++ ++#define __i2s_write_tfifo(v) __aic_write_tfifo(v) ++#define __i2s_read_rfifo() __aic_read_rfifo() ++ ++#define __i2s_reset_codec() \ ++ do { \ ++ } while (0) ++ ++ ++/*************************************************************************** ++ * ICDC ++ ***************************************************************************/ ++#define __i2s_internal_codec() __aic_internal_codec() ++#define __i2s_external_codec() __aic_external_codec() ++ ++/*************************************************************************** ++ * INTC ++ ***************************************************************************/ ++#define __intc_unmask_irq(n) ( REG_INTC_IMCR = (1 << (n)) ) ++#define __intc_mask_irq(n) ( REG_INTC_IMSR = (1 << (n)) ) ++#define __intc_ack_irq(n) ( REG_INTC_IPR = (1 << (n)) ) ++ ++ ++/*************************************************************************** ++ * I2C ++ ***************************************************************************/ ++ ++#define __i2c_enable() ( REG_I2C_CR |= I2C_CR_I2CE ) ++#define __i2c_disable() ( REG_I2C_CR &= ~I2C_CR_I2CE ) ++ ++#define __i2c_send_start() ( REG_I2C_CR |= I2C_CR_STA ) ++#define __i2c_send_stop() ( REG_I2C_CR |= I2C_CR_STO ) ++#define __i2c_send_ack() ( REG_I2C_CR &= ~I2C_CR_AC ) ++#define __i2c_send_nack() ( REG_I2C_CR |= I2C_CR_AC ) ++ ++#define __i2c_set_drf() ( REG_I2C_SR |= I2C_SR_DRF ) ++#define __i2c_clear_drf() ( REG_I2C_SR &= ~I2C_SR_DRF ) ++#define __i2c_check_drf() ( REG_I2C_SR & I2C_SR_DRF ) ++ ++#define __i2c_received_ack() ( !(REG_I2C_SR & I2C_SR_ACKF) ) ++#define __i2c_is_busy() ( REG_I2C_SR & I2C_SR_BUSY ) ++#define __i2c_transmit_ended() ( REG_I2C_SR & I2C_SR_TEND ) ++ ++#define __i2c_set_clk(dev_clk, i2c_clk) \ ++ ( REG_I2C_GR = (dev_clk) / (16*(i2c_clk)) - 1 ) ++ ++#define __i2c_read() ( REG_I2C_DR ) ++#define __i2c_write(val) ( REG_I2C_DR = (val) ) ++ ++ ++/*************************************************************************** ++ * MSC ++ ***************************************************************************/ ++ ++#define __msc_start_op() \ ++ ( REG_MSC_STRPCL = MSC_STRPCL_START_OP | MSC_STRPCL_CLOCK_CONTROL_START ) ++ ++#define __msc_set_resto(to) ( REG_MSC_RESTO = to ) ++#define __msc_set_rdto(to) ( REG_MSC_RDTO = to ) ++#define __msc_set_cmd(cmd) ( REG_MSC_CMD = cmd ) ++#define __msc_set_arg(arg) ( REG_MSC_ARG = arg ) ++#define __msc_set_nob(nob) ( REG_MSC_NOB = nob ) ++#define __msc_get_nob() ( REG_MSC_NOB ) ++#define __msc_set_blklen(len) ( REG_MSC_BLKLEN = len ) ++#define __msc_set_cmdat(cmdat) ( REG_MSC_CMDAT = cmdat ) ++#define __msc_set_cmdat_ioabort() ( REG_MSC_CMDAT |= MSC_CMDAT_IO_ABORT ) ++#define __msc_clear_cmdat_ioabort() ( REG_MSC_CMDAT &= ~MSC_CMDAT_IO_ABORT ) ++ ++#define __msc_set_cmdat_bus_width1() \ ++do { \ ++ REG_MSC_CMDAT &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ ++ REG_MSC_CMDAT |= MSC_CMDAT_BUS_WIDTH_1BIT; \ ++} while(0) ++ ++#define __msc_set_cmdat_bus_width4() \ ++do { \ ++ REG_MSC_CMDAT &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ ++ REG_MSC_CMDAT |= MSC_CMDAT_BUS_WIDTH_4BIT; \ ++} while(0) ++ ++#define __msc_set_cmdat_dma_en() ( REG_MSC_CMDAT |= MSC_CMDAT_DMA_EN ) ++#define __msc_set_cmdat_init() ( REG_MSC_CMDAT |= MSC_CMDAT_INIT ) ++#define __msc_set_cmdat_busy() ( REG_MSC_CMDAT |= MSC_CMDAT_BUSY ) ++#define __msc_set_cmdat_stream() ( REG_MSC_CMDAT |= MSC_CMDAT_STREAM_BLOCK ) ++#define __msc_set_cmdat_block() ( REG_MSC_CMDAT &= ~MSC_CMDAT_STREAM_BLOCK ) ++#define __msc_set_cmdat_read() ( REG_MSC_CMDAT &= ~MSC_CMDAT_WRITE_READ ) ++#define __msc_set_cmdat_write() ( REG_MSC_CMDAT |= MSC_CMDAT_WRITE_READ ) ++#define __msc_set_cmdat_data_en() ( REG_MSC_CMDAT |= MSC_CMDAT_DATA_EN ) ++ ++/* r is MSC_CMDAT_RESPONSE_FORMAT_Rx or MSC_CMDAT_RESPONSE_FORMAT_NONE */ ++#define __msc_set_cmdat_res_format(r) \ ++do { \ ++ REG_MSC_CMDAT &= ~MSC_CMDAT_RESPONSE_FORMAT_MASK; \ ++ REG_MSC_CMDAT |= (r); \ ++} while(0) ++ ++#define __msc_clear_cmdat() \ ++ REG_MSC_CMDAT &= ~( MSC_CMDAT_IO_ABORT | MSC_CMDAT_DMA_EN | MSC_CMDAT_INIT| \ ++ MSC_CMDAT_BUSY | MSC_CMDAT_STREAM_BLOCK | MSC_CMDAT_WRITE_READ | \ ++ MSC_CMDAT_DATA_EN | MSC_CMDAT_RESPONSE_FORMAT_MASK ) ++ ++#define __msc_get_imask() ( REG_MSC_IMASK ) ++#define __msc_mask_all_intrs() ( REG_MSC_IMASK = 0xff ) ++#define __msc_unmask_all_intrs() ( REG_MSC_IMASK = 0x00 ) ++#define __msc_mask_rd() ( REG_MSC_IMASK |= MSC_IMASK_RXFIFO_RD_REQ ) ++#define __msc_unmask_rd() ( REG_MSC_IMASK &= ~MSC_IMASK_RXFIFO_RD_REQ ) ++#define __msc_mask_wr() ( REG_MSC_IMASK |= MSC_IMASK_TXFIFO_WR_REQ ) ++#define __msc_unmask_wr() ( REG_MSC_IMASK &= ~MSC_IMASK_TXFIFO_WR_REQ ) ++#define __msc_mask_endcmdres() ( REG_MSC_IMASK |= MSC_IMASK_END_CMD_RES ) ++#define __msc_unmask_endcmdres() ( REG_MSC_IMASK &= ~MSC_IMASK_END_CMD_RES ) ++#define __msc_mask_datatrandone() ( REG_MSC_IMASK |= MSC_IMASK_DATA_TRAN_DONE ) ++#define __msc_unmask_datatrandone() ( REG_MSC_IMASK &= ~MSC_IMASK_DATA_TRAN_DONE ) ++#define __msc_mask_prgdone() ( REG_MSC_IMASK |= MSC_IMASK_PRG_DONE ) ++#define __msc_unmask_prgdone() ( REG_MSC_IMASK &= ~MSC_IMASK_PRG_DONE ) ++ ++/* n=0,1,2,3,4,5,6,7 */ ++#define __msc_set_clkrt(n) \ ++do { \ ++ REG_MSC_CLKRT = n; \ ++} while(0) ++ ++#define __msc_get_ireg() ( REG_MSC_IREG ) ++#define __msc_ireg_rd() ( REG_MSC_IREG & MSC_IREG_RXFIFO_RD_REQ ) ++#define __msc_ireg_wr() ( REG_MSC_IREG & MSC_IREG_TXFIFO_WR_REQ ) ++#define __msc_ireg_end_cmd_res() ( REG_MSC_IREG & MSC_IREG_END_CMD_RES ) ++#define __msc_ireg_data_tran_done() ( REG_MSC_IREG & MSC_IREG_DATA_TRAN_DONE ) ++#define __msc_ireg_prg_done() ( REG_MSC_IREG & MSC_IREG_PRG_DONE ) ++#define __msc_ireg_clear_end_cmd_res() ( REG_MSC_IREG = MSC_IREG_END_CMD_RES ) ++#define __msc_ireg_clear_data_tran_done() ( REG_MSC_IREG = MSC_IREG_DATA_TRAN_DONE ) ++#define __msc_ireg_clear_prg_done() ( REG_MSC_IREG = MSC_IREG_PRG_DONE ) ++ ++#define __msc_get_stat() ( REG_MSC_STAT ) ++#define __msc_stat_not_end_cmd_res() ( (REG_MSC_STAT & MSC_STAT_END_CMD_RES) == 0) ++#define __msc_stat_crc_err() \ ++ ( REG_MSC_STAT & (MSC_STAT_CRC_RES_ERR | MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR_YES) ) ++#define __msc_stat_res_crc_err() ( REG_MSC_STAT & MSC_STAT_CRC_RES_ERR ) ++#define __msc_stat_rd_crc_err() ( REG_MSC_STAT & MSC_STAT_CRC_READ_ERROR ) ++#define __msc_stat_wr_crc_err() ( REG_MSC_STAT & MSC_STAT_CRC_WRITE_ERROR_YES ) ++#define __msc_stat_resto_err() ( REG_MSC_STAT & MSC_STAT_TIME_OUT_RES ) ++#define __msc_stat_rdto_err() ( REG_MSC_STAT & MSC_STAT_TIME_OUT_READ ) ++ ++#define __msc_rd_resfifo() ( REG_MSC_RES ) ++#define __msc_rd_rxfifo() ( REG_MSC_RXFIFO ) ++#define __msc_wr_txfifo(v) ( REG_MSC_TXFIFO = v ) ++ ++#define __msc_reset() \ ++do { \ ++ REG_MSC_STRPCL = MSC_STRPCL_RESET; \ ++ while (REG_MSC_STAT & MSC_STAT_IS_RESETTING); \ ++} while (0) ++ ++#define __msc_start_clk() \ ++do { \ ++ REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_START; \ ++} while (0) ++ ++#define __msc_stop_clk() \ ++do { \ ++ REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_STOP; \ ++} while (0) ++ ++#define MMC_CLK 19169200 ++#define SD_CLK 24576000 ++ ++/* msc_clk should little than pclk and little than clk retrieve from card */ ++#define __msc_calc_clk_divisor(type,dev_clk,msc_clk,lv) \ ++do { \ ++ unsigned int rate, pclk, i; \ ++ pclk = dev_clk; \ ++ rate = type?SD_CLK:MMC_CLK; \ ++ if (msc_clk && msc_clk < pclk) \ ++ pclk = msc_clk; \ ++ i = 0; \ ++ while (pclk < rate) \ ++ { \ ++ i ++; \ ++ rate >>= 1; \ ++ } \ ++ lv = i; \ ++} while(0) ++ ++/* divide rate to little than or equal to 400kHz */ ++#define __msc_calc_slow_clk_divisor(type, lv) \ ++do { \ ++ unsigned int rate, i; \ ++ rate = (type?SD_CLK:MMC_CLK)/1000/400; \ ++ i = 0; \ ++ while (rate > 0) \ ++ { \ ++ rate >>= 1; \ ++ i ++; \ ++ } \ ++ lv = i; \ ++} while(0) ++ ++ ++/*************************************************************************** ++ * SSI ++ ***************************************************************************/ ++ ++#define __ssi_enable() ( REG_SSI_CR0 |= SSI_CR0_SSIE ) ++#define __ssi_disable() ( REG_SSI_CR0 &= ~SSI_CR0_SSIE ) ++#define __ssi_select_ce() ( REG_SSI_CR0 &= ~SSI_CR0_FSEL ) ++ ++#define __ssi_normal_mode() ( REG_SSI_ITR &= ~SSI_ITR_IVLTM_MASK ) ++ ++#define __ssi_select_ce2() \ ++do { \ ++ REG_SSI_CR0 |= SSI_CR0_FSEL; \ ++ REG_SSI_CR1 &= ~SSI_CR1_MULTS; \ ++} while (0) ++ ++#define __ssi_select_gpc() \ ++do { \ ++ REG_SSI_CR0 &= ~SSI_CR0_FSEL; \ ++ REG_SSI_CR1 |= SSI_CR1_MULTS; \ ++} while (0) ++ ++#define __ssi_enable_tx_intr() \ ++ ( REG_SSI_CR0 |= SSI_CR0_TIE | SSI_CR0_TEIE ) ++ ++#define __ssi_disable_tx_intr() \ ++ ( REG_SSI_CR0 &= ~(SSI_CR0_TIE | SSI_CR0_TEIE) ) ++ ++#define __ssi_enable_rx_intr() \ ++ ( REG_SSI_CR0 |= SSI_CR0_RIE | SSI_CR0_REIE ) ++ ++#define __ssi_disable_rx_intr() \ ++ ( REG_SSI_CR0 &= ~(SSI_CR0_RIE | SSI_CR0_REIE) ) ++ ++#define __ssi_enable_txfifo_half_empty_intr() \ ++ ( REG_SSI_CR0 |= SSI_CR0_TIE ) ++#define __ssi_disable_txfifo_half_empty_intr() \ ++ ( REG_SSI_CR0 &= ~SSI_CR0_TIE ) ++#define __ssi_enable_tx_error_intr() \ ++ ( REG_SSI_CR0 |= SSI_CR0_TEIE ) ++#define __ssi_disable_tx_error_intr() \ ++ ( REG_SSI_CR0 &= ~SSI_CR0_TEIE ) ++ ++#define __ssi_enable_rxfifo_half_full_intr() \ ++ ( REG_SSI_CR0 |= SSI_CR0_RIE ) ++#define __ssi_disable_rxfifo_half_full_intr() \ ++ ( REG_SSI_CR0 &= ~SSI_CR0_RIE ) ++#define __ssi_enable_rx_error_intr() \ ++ ( REG_SSI_CR0 |= SSI_CR0_REIE ) ++#define __ssi_disable_rx_error_intr() \ ++ ( REG_SSI_CR0 &= ~SSI_CR0_REIE ) ++ ++#define __ssi_enable_loopback() ( REG_SSI_CR0 |= SSI_CR0_LOOP ) ++#define __ssi_disable_loopback() ( REG_SSI_CR0 &= ~SSI_CR0_LOOP ) ++ ++#define __ssi_enable_receive() ( REG_SSI_CR0 &= ~SSI_CR0_DISREV ) ++#define __ssi_disable_receive() ( REG_SSI_CR0 |= SSI_CR0_DISREV ) ++ ++#define __ssi_finish_receive() \ ++ ( REG_SSI_CR0 |= (SSI_CR0_RFINE | SSI_CR0_RFINC) ) ++ ++#define __ssi_disable_recvfinish() \ ++ ( REG_SSI_CR0 &= ~(SSI_CR0_RFINE | SSI_CR0_RFINC) ) ++ ++#define __ssi_flush_txfifo() ( REG_SSI_CR0 |= SSI_CR0_TFLUSH ) ++#define __ssi_flush_rxfifo() ( REG_SSI_CR0 |= SSI_CR0_RFLUSH ) ++ ++#define __ssi_flush_fifo() \ ++ ( REG_SSI_CR0 |= SSI_CR0_TFLUSH | SSI_CR0_RFLUSH ) ++ ++#define __ssi_finish_transmit() ( REG_SSI_CR1 &= ~SSI_CR1_UNFIN ) ++#define __ssi_wait_transmit() ( REG_SSI_CR1 |= SSI_CR1_UNFIN ) ++ ++#define __ssi_spi_format() \ ++do { \ ++ REG_SSI_CR1 &= ~SSI_CR1_FMAT_MASK; \ ++ REG_SSI_CR1 |= SSI_CR1_FMAT_SPI; \ ++ REG_SSI_CR1 &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK);\ ++ REG_SSI_CR1 |= (SSI_CR1_TFVCK_1 | SSI_CR1_TCKFI_1); \ ++} while (0) ++ ++/* TI's SSP format, must clear SSI_CR1.UNFIN */ ++#define __ssi_ssp_format() \ ++do { \ ++ REG_SSI_CR1 &= ~(SSI_CR1_FMAT_MASK | SSI_CR1_UNFIN); \ ++ REG_SSI_CR1 |= SSI_CR1_FMAT_SSP; \ ++} while (0) ++ ++/* National's Microwire format, must clear SSI_CR0.RFINE, and set max delay */ ++#define __ssi_microwire_format() \ ++do { \ ++ REG_SSI_CR1 &= ~SSI_CR1_FMAT_MASK; \ ++ REG_SSI_CR1 |= SSI_CR1_FMAT_MW1; \ ++ REG_SSI_CR1 &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK);\ ++ REG_SSI_CR1 |= (SSI_CR1_TFVCK_3 | SSI_CR1_TCKFI_3); \ ++ REG_SSI_CR0 &= ~SSI_CR0_RFINE; \ ++} while (0) ++ ++/* CE# level (FRMHL), CE# in interval time (ITFRM), ++ clock phase and polarity (PHA POL), ++ interval time (SSIITR), interval characters/frame (SSIICR) */ ++ ++ /* frmhl,endian,mcom,flen,pha,pol MASK */ ++#define SSICR1_MISC_MASK \ ++ ( SSI_CR1_FRMHL_MASK | SSI_CR1_LFST | SSI_CR1_MCOM_MASK \ ++ | SSI_CR1_FLEN_MASK | SSI_CR1_PHA | SSI_CR1_POL ) \ ++ ++#define __ssi_spi_set_misc(frmhl,endian,flen,mcom,pha,pol) \ ++do { \ ++ REG_SSI_CR1 &= ~SSICR1_MISC_MASK; \ ++ REG_SSI_CR1 |= ((frmhl) << 30) | ((endian) << 25) | \ ++ (((mcom) - 1) << 12) | (((flen) - 2) << 4) | \ ++ ((pha) << 1) | (pol); \ ++} while(0) ++ ++/* Transfer with MSB or LSB first */ ++#define __ssi_set_msb() ( REG_SSI_CR1 &= ~SSI_CR1_LFST ) ++#define __ssi_set_lsb() ( REG_SSI_CR1 |= SSI_CR1_LFST ) ++ ++#define __ssi_set_frame_length(n) \ ++ REG_SSI_CR1 = (REG_SSI_CR1 & ~SSI_CR1_FLEN_MASK) | (((n) - 2) << 4) ++ ++/* n = 1 - 16 */ ++#define __ssi_set_microwire_command_length(n) \ ++ ( REG_SSI_CR1 = ((REG_SSI_CR1 & ~SSI_CR1_MCOM_MASK) | SSI_CR1_MCOM_##n##BIT) ) ++ ++/* Set the clock phase for SPI */ ++#define __ssi_set_spi_clock_phase(n) \ ++ ( REG_SSI_CR1 = ((REG_SSI_CR1 & ~SSI_CR1_PHA) | ((n&0x1)<< 1))) ++ ++/* Set the clock polarity for SPI */ ++#define __ssi_set_spi_clock_polarity(n) \ ++ ( REG_SSI_CR1 = ((REG_SSI_CR1 & ~SSI_CR1_POL) | (n&0x1)) ) ++ ++/* n = ix8 */ ++#define __ssi_set_tx_trigger(n) \ ++do { \ ++ REG_SSI_CR1 &= ~SSI_CR1_TTRG_MASK; \ ++ REG_SSI_CR1 |= (n/8)<> SSI_SR_TFIFONUM_BIT ) ++ ++#define __ssi_get_rxfifo_count() \ ++ ( (REG_SSI_SR & SSI_SR_RFIFONUM_MASK) >> SSI_SR_RFIFONUM_BIT ) ++ ++#define __ssi_transfer_end() ( REG_SSI_SR & SSI_SR_END ) ++#define __ssi_is_busy() ( REG_SSI_SR & SSI_SR_BUSY ) ++ ++#define __ssi_txfifo_full() ( REG_SSI_SR & SSI_SR_TFF ) ++#define __ssi_rxfifo_empty() ( REG_SSI_SR & SSI_SR_RFE ) ++#define __ssi_rxfifo_half_full() ( REG_SSI_SR & SSI_SR_RFHF ) ++#define __ssi_txfifo_half_empty() ( REG_SSI_SR & SSI_SR_TFHE ) ++#define __ssi_underrun() ( REG_SSI_SR & SSI_SR_UNDR ) ++#define __ssi_overrun() ( REG_SSI_SR & SSI_SR_OVER ) ++#define __ssi_clear_underrun() ( REG_SSI_SR = ~SSI_SR_UNDR ) ++#define __ssi_clear_overrun() ( REG_SSI_SR = ~SSI_SR_OVER ) ++#define __ssi_clear_errors() \ ++ ( REG_SSI_SR &= ~(SSI_SR_UNDR | SSI_SR_OVER) ) ++ ++ ++#define __ssi_set_clk(dev_clk, ssi_clk) \ ++ ( REG_SSI_GR = (dev_clk) / (2*(ssi_clk)) - 1 ) ++ ++#define __ssi_receive_data() REG_SSI_DR ++#define __ssi_transmit_data(v) ( REG_SSI_DR = (v) ) ++ ++ ++/*************************************************************************** ++ * CIM ++ ***************************************************************************/ ++ ++#define __cim_enable() ( REG_CIM_CTRL |= CIM_CTRL_ENA ) ++#define __cim_disable() ( REG_CIM_CTRL &= ~CIM_CTRL_ENA ) ++ ++#define __cim_input_data_inverse() ( REG_CIM_CFG |= CIM_CFG_INV_DAT ) ++#define __cim_input_data_normal() ( REG_CIM_CFG &= ~CIM_CFG_INV_DAT ) ++ ++#define __cim_vsync_active_low() ( REG_CIM_CFG |= CIM_CFG_VSP ) ++#define __cim_vsync_active_high() ( REG_CIM_CFG &= ~CIM_CFG_VSP ) ++ ++#define __cim_hsync_active_low() ( REG_CIM_CFG |= CIM_CFG_HSP ) ++#define __cim_hsync_active_high() ( REG_CIM_CFG &= ~CIM_CFG_HSP ) ++ ++#define __cim_sample_data_at_pclk_falling_edge() \ ++ ( REG_CIM_CFG |= CIM_CFG_PCP ) ++#define __cim_sample_data_at_pclk_rising_edge() \ ++ ( REG_CIM_CFG &= ~CIM_CFG_PCP ) ++ ++#define __cim_enable_dummy_zero() ( REG_CIM_CFG |= CIM_CFG_DUMMY_ZERO ) ++#define __cim_disable_dummy_zero() ( REG_CIM_CFG &= ~CIM_CFG_DUMMY_ZERO ) ++ ++#define __cim_select_external_vsync() ( REG_CIM_CFG |= CIM_CFG_EXT_VSYNC ) ++#define __cim_select_internal_vsync() ( REG_CIM_CFG &= ~CIM_CFG_EXT_VSYNC ) ++ ++/* n=0-7 */ ++#define __cim_set_data_packing_mode(n) \ ++do { \ ++ REG_CIM_CFG &= ~CIM_CFG_PACK_MASK; \ ++ REG_CIM_CFG |= (CIM_CFG_PACK_##n); \ ++} while (0) ++ ++#define __cim_enable_ccir656_progressive_mode() \ ++do { \ ++ REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ ++ REG_CIM_CFG |= CIM_CFG_DSM_CPM; \ ++} while (0) ++ ++#define __cim_enable_ccir656_interlace_mode() \ ++do { \ ++ REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ ++ REG_CIM_CFG |= CIM_CFG_DSM_CIM; \ ++} while (0) ++ ++#define __cim_enable_gated_clock_mode() \ ++do { \ ++ REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ ++ REG_CIM_CFG |= CIM_CFG_DSM_GCM; \ ++} while (0) ++ ++#define __cim_enable_nongated_clock_mode() \ ++do { \ ++ REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ ++ REG_CIM_CFG |= CIM_CFG_DSM_NGCM; \ ++} while (0) ++ ++/* sclk:system bus clock ++ * mclk: CIM master clock ++ */ ++#define __cim_set_master_clk(sclk, mclk) \ ++do { \ ++ REG_CIM_CTRL &= ~CIM_CTRL_MCLKDIV_MASK; \ ++ REG_CIM_CTRL |= (((sclk)/(mclk) - 1) << CIM_CTRL_MCLKDIV_BIT); \ ++} while (0) ++ ++#define __cim_enable_sof_intr() \ ++ ( REG_CIM_CTRL |= CIM_CTRL_DMA_SOFM ) ++#define __cim_disable_sof_intr() \ ++ ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_SOFM ) ++ ++#define __cim_enable_eof_intr() \ ++ ( REG_CIM_CTRL |= CIM_CTRL_DMA_EOFM ) ++#define __cim_disable_eof_intr() \ ++ ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_EOFM ) ++ ++#define __cim_enable_stop_intr() \ ++ ( REG_CIM_CTRL |= CIM_CTRL_DMA_STOPM ) ++#define __cim_disable_stop_intr() \ ++ ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_STOPM ) ++ ++#define __cim_enable_trig_intr() \ ++ ( REG_CIM_CTRL |= CIM_CTRL_RXF_TRIGM ) ++#define __cim_disable_trig_intr() \ ++ ( REG_CIM_CTRL &= ~CIM_CTRL_RXF_TRIGM ) ++ ++#define __cim_enable_rxfifo_overflow_intr() \ ++ ( REG_CIM_CTRL |= CIM_CTRL_RXF_OFM ) ++#define __cim_disable_rxfifo_overflow_intr() \ ++ ( REG_CIM_CTRL &= ~CIM_CTRL_RXF_OFM ) ++ ++/* n=1-16 */ ++#define __cim_set_frame_rate(n) \ ++do { \ ++ REG_CIM_CTRL &= ~CIM_CTRL_FRC_MASK; \ ++ REG_CIM_CTRL |= CIM_CTRL_FRC_##n; \ ++} while (0) ++ ++#define __cim_enable_dma() ( REG_CIM_CTRL |= CIM_CTRL_DMA_EN ) ++#define __cim_disable_dma() ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_EN ) ++ ++#define __cim_reset_rxfifo() ( REG_CIM_CTRL |= CIM_CTRL_RXF_RST ) ++#define __cim_unreset_rxfifo() ( REG_CIM_CTRL &= ~CIM_CTRL_RXF_RST ) ++ ++/* n=4,8,12,16,20,24,28,32 */ ++#define __cim_set_rxfifo_trigger(n) \ ++do { \ ++ REG_CIM_CTRL &= ~CIM_CTRL_RXF_TRIG_MASK; \ ++ REG_CIM_CTRL |= CIM_CTRL_RXF_TRIG_##n; \ ++} while (0) ++ ++#define __cim_clear_state() ( REG_CIM_STATE = 0 ) ++ ++#define __cim_disable_done() ( REG_CIM_STATE & CIM_STATE_VDD ) ++#define __cim_rxfifo_empty() ( REG_CIM_STATE & CIM_STATE_RXF_EMPTY ) ++#define __cim_rxfifo_reach_trigger() ( REG_CIM_STATE & CIM_STATE_RXF_TRIG ) ++#define __cim_rxfifo_overflow() ( REG_CIM_STATE & CIM_STATE_RXF_OF ) ++#define __cim_clear_rxfifo_overflow() ( REG_CIM_STATE &= ~CIM_STATE_RXF_OF ) ++#define __cim_dma_stop() ( REG_CIM_STATE & CIM_STATE_DMA_STOP ) ++#define __cim_dma_eof() ( REG_CIM_STATE & CIM_STATE_DMA_EOF ) ++#define __cim_dma_sof() ( REG_CIM_STATE & CIM_STATE_DMA_SOF ) ++ ++#define __cim_get_iid() ( REG_CIM_IID ) ++#define __cim_get_image_data() ( REG_CIM_RXFIFO ) ++#define __cim_get_dam_cmd() ( REG_CIM_CMD ) ++ ++#define __cim_set_da(a) ( REG_CIM_DA = (a) ) ++ ++/*************************************************************************** ++ * LCD ++ ***************************************************************************/ ++#define __lcd_as_smart_lcd() ( REG_LCD_CFG |= (1<> LCD_VSYNC_VPS_BIT ) ++ ++#define __lcd_vsync_get_vpe() \ ++ ( (REG_LCD_VSYNC & LCD_VSYNC_VPE_MASK) >> LCD_VSYNC_VPE_BIT ) ++#define __lcd_vsync_set_vpe(n) \ ++do { \ ++ REG_LCD_VSYNC &= ~LCD_VSYNC_VPE_MASK; \ ++ REG_LCD_VSYNC |= (n) << LCD_VSYNC_VPE_BIT; \ ++} while (0) ++ ++#define __lcd_hsync_get_hps() \ ++ ( (REG_LCD_HSYNC & LCD_HSYNC_HPS_MASK) >> LCD_HSYNC_HPS_BIT ) ++#define __lcd_hsync_set_hps(n) \ ++do { \ ++ REG_LCD_HSYNC &= ~LCD_HSYNC_HPS_MASK; \ ++ REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPS_BIT; \ ++} while (0) ++ ++#define __lcd_hsync_get_hpe() \ ++ ( (REG_LCD_HSYNC & LCD_HSYNC_HPE_MASK) >> LCD_VSYNC_HPE_BIT ) ++#define __lcd_hsync_set_hpe(n) \ ++do { \ ++ REG_LCD_HSYNC &= ~LCD_HSYNC_HPE_MASK; \ ++ REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPE_BIT; \ ++} while (0) ++ ++#define __lcd_vat_get_ht() \ ++ ( (REG_LCD_VAT & LCD_VAT_HT_MASK) >> LCD_VAT_HT_BIT ) ++#define __lcd_vat_set_ht(n) \ ++do { \ ++ REG_LCD_VAT &= ~LCD_VAT_HT_MASK; \ ++ REG_LCD_VAT |= (n) << LCD_VAT_HT_BIT; \ ++} while (0) ++ ++#define __lcd_vat_get_vt() \ ++ ( (REG_LCD_VAT & LCD_VAT_VT_MASK) >> LCD_VAT_VT_BIT ) ++#define __lcd_vat_set_vt(n) \ ++do { \ ++ REG_LCD_VAT &= ~LCD_VAT_VT_MASK; \ ++ REG_LCD_VAT |= (n) << LCD_VAT_VT_BIT; \ ++} while (0) ++ ++#define __lcd_dah_get_hds() \ ++ ( (REG_LCD_DAH & LCD_DAH_HDS_MASK) >> LCD_DAH_HDS_BIT ) ++#define __lcd_dah_set_hds(n) \ ++do { \ ++ REG_LCD_DAH &= ~LCD_DAH_HDS_MASK; \ ++ REG_LCD_DAH |= (n) << LCD_DAH_HDS_BIT; \ ++} while (0) ++ ++#define __lcd_dah_get_hde() \ ++ ( (REG_LCD_DAH & LCD_DAH_HDE_MASK) >> LCD_DAH_HDE_BIT ) ++#define __lcd_dah_set_hde(n) \ ++do { \ ++ REG_LCD_DAH &= ~LCD_DAH_HDE_MASK; \ ++ REG_LCD_DAH |= (n) << LCD_DAH_HDE_BIT; \ ++} while (0) ++ ++#define __lcd_dav_get_vds() \ ++ ( (REG_LCD_DAV & LCD_DAV_VDS_MASK) >> LCD_DAV_VDS_BIT ) ++#define __lcd_dav_set_vds(n) \ ++do { \ ++ REG_LCD_DAV &= ~LCD_DAV_VDS_MASK; \ ++ REG_LCD_DAV |= (n) << LCD_DAV_VDS_BIT; \ ++} while (0) ++ ++#define __lcd_dav_get_vde() \ ++ ( (REG_LCD_DAV & LCD_DAV_VDE_MASK) >> LCD_DAV_VDE_BIT ) ++#define __lcd_dav_set_vde(n) \ ++do { \ ++ REG_LCD_DAV &= ~LCD_DAV_VDE_MASK; \ ++ REG_LCD_DAV |= (n) << LCD_DAV_VDE_BIT; \ ++} while (0) ++ ++#define __lcd_cmd0_set_sofint() ( REG_LCD_CMD0 |= LCD_CMD_SOFINT ) ++#define __lcd_cmd0_clr_sofint() ( REG_LCD_CMD0 &= ~LCD_CMD_SOFINT ) ++#define __lcd_cmd1_set_sofint() ( REG_LCD_CMD1 |= LCD_CMD_SOFINT ) ++#define __lcd_cmd1_clr_sofint() ( REG_LCD_CMD1 &= ~LCD_CMD_SOFINT ) ++ ++#define __lcd_cmd0_set_eofint() ( REG_LCD_CMD0 |= LCD_CMD_EOFINT ) ++#define __lcd_cmd0_clr_eofint() ( REG_LCD_CMD0 &= ~LCD_CMD_EOFINT ) ++#define __lcd_cmd1_set_eofint() ( REG_LCD_CMD1 |= LCD_CMD_EOFINT ) ++#define __lcd_cmd1_clr_eofint() ( REG_LCD_CMD1 &= ~LCD_CMD_EOFINT ) ++ ++#define __lcd_cmd0_set_pal() ( REG_LCD_CMD0 |= LCD_CMD_PAL ) ++#define __lcd_cmd0_clr_pal() ( REG_LCD_CMD0 &= ~LCD_CMD_PAL ) ++ ++#define __lcd_cmd0_get_len() \ ++ ( (REG_LCD_CMD0 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) ++#define __lcd_cmd1_get_len() \ ++ ( (REG_LCD_CMD1 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) ++ ++/******************************************************* ++ * SMART LCD ++ *******************************************************/ ++ ++#define __slcd_dma_enable() (REG_SLCD_CTRL |= SLCD_CTRL_DMA_EN) ++#define __slcd_dma_disable() \ ++do {\ ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); \ ++ REG_SLCD_CTRL &= ~SLCD_CTRL_DMA_EN; \ ++} while(0) ++ ++/******************************************************* ++ * SMART LCD ++ *******************************************************/ ++ ++#define __slcd_dma_enable() (REG_SLCD_CTRL |= SLCD_CTRL_DMA_EN) ++#define __slcd_dma_disable() \ ++do {\ ++ while (REG_SLCD_STATE & SLCD_STATE_BUSY); \ ++ REG_SLCD_CTRL &= ~SLCD_CTRL_DMA_EN; \ ++} while(0) ++ ++/*************************************************************************** ++ * RTC ops ++ ***************************************************************************/ ++ ++#define __rtc_write_ready() ( (REG_RTC_RCR & RTC_RCR_WRDY) >> RTC_RCR_WRDY_BIT ) ++#define __rtc_enabled() ( REG_RTC_RCR |= RTC_RCR_RTCE ) ++#define __rtc_disabled() ( REG_RTC_RCR &= ~RTC_RCR_RTCE ) ++#define __rtc_enable_alarm() ( REG_RTC_RCR |= RTC_RCR_AE ) ++#define __rtc_disable_alarm() ( REG_RTC_RCR &= ~RTC_RCR_AE ) ++#define __rtc_enable_alarm_irq() ( REG_RTC_RCR |= RTC_RCR_AIE ) ++#define __rtc_disable_alarm_irq() ( REG_RTC_RCR &= ~RTC_RCR_AIE ) ++#define __rtc_enable_1Hz_irq() ( REG_RTC_RCR |= RTC_RCR_1HZIE ) ++#define __rtc_disable_1Hz_irq() ( REG_RTC_RCR &= ~RTC_RCR_1HZIE ) ++ ++#define __rtc_get_1Hz_flag() ( (REG_RTC_RCR >> RTC_RCR_1HZ_BIT) & 0x1 ) ++#define __rtc_clear_1Hz_flag() ( REG_RTC_RCR &= ~RTC_RCR_1HZ ) ++#define __rtc_get_alarm_flag() ( (REG_RTC_RCR >> RTC_RCR_AF_BIT) & 0x1 ) ++#define __rtc_clear_alarm_flag() ( REG_RTC_RCR &= ~RTC_RCR_AF ) ++ ++#define __rtc_get_second() ( REG_RTC_RSR ) ++#define __rtc_set_second(v) ( REG_RTC_RSR = v ) ++ ++#define __rtc_get_alarm_second() ( REG_RTC_RSAR ) ++#define __rtc_set_alarm_second(v) ( REG_RTC_RSAR = v ) ++ ++#define __rtc_RGR_is_locked() ( (REG_RTC_RGR >> RTC_RGR_LOCK) ) ++#define __rtc_lock_RGR() ( REG_RTC_RGR |= RTC_RGR_LOCK ) ++#define __rtc_unlock_RGR() ( REG_RTC_RGR &= ~RTC_RGR_LOCK ) ++#define __rtc_get_adjc_val() ( (REG_RTC_RGR & RTC_RGR_ADJC_MASK) >> RTC_RGR_ADJC_BIT ) ++#define __rtc_set_adjc_val(v) \ ++ ( REG_RTC_RGR = ( (REG_RTC_RGR & ~RTC_RGR_ADJC_MASK) | (v << RTC_RGR_ADJC_BIT) )) ++#define __rtc_get_nc1Hz_val() ( (REG_RTC_RGR & RTC_RGR_NC1HZ_MASK) >> RTC_RGR_NC1HZ_BIT ) ++#define __rtc_set_nc1Hz_val(v) \ ++ ( REG_RTC_RGR = ( (REG_RTC_RGR & ~RTC_RGR_NC1HZ_MASK) | (v << RTC_RGR_NC1HZ_BIT) )) ++ ++#define __rtc_power_down() ( REG_RTC_HCR |= RTC_HCR_PD ) ++ ++#define __rtc_get_hwfcr_val() ( REG_RTC_HWFCR & RTC_HWFCR_MASK ) ++#define __rtc_set_hwfcr_val(v) ( REG_RTC_HWFCR = (v) & RTC_HWFCR_MASK ) ++#define __rtc_get_hrcr_val() ( REG_RTC_HRCR & RTC_HRCR_MASK ) ++#define __rtc_set_hrcr_val(v) ( REG_RTC_HRCR = (v) & RTC_HRCR_MASK ) ++ ++#define __rtc_enable_alarm_wakeup() ( REG_RTC_HWCR |= RTC_HWCR_EALM ) ++#define __rtc_disable_alarm_wakeup() ( REG_RTC_HWCR &= ~RTC_HWCR_EALM ) ++ ++#define __rtc_status_hib_reset_occur() ( (REG_RTC_HWRSR >> RTC_HWRSR_HR) & 0x1 ) ++#define __rtc_status_ppr_reset_occur() ( (REG_RTC_HWRSR >> RTC_HWRSR_PPR) & 0x1 ) ++#define __rtc_status_wakeup_pin_waken_up() ( (REG_RTC_HWRSR >> RTC_HWRSR_PIN) & 0x1 ) ++#define __rtc_status_alarm_waken_up() ( (REG_RTC_HWRSR >> RTC_HWRSR_ALM) & 0x1 ) ++#define __rtc_clear_hib_stat_all() ( REG_RTC_HWRSR = 0 ) ++ ++#define __rtc_get_scratch_pattern() (REG_RTC_HSPR) ++#define __rtc_set_scratch_pattern(n) (REG_RTC_HSPR = n ) ++ ++ ++ ++#endif /* __JZ4740_OPS_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4740/regs.h linux-2.6.24.7/include/asm-mips/mach-jz4740/regs.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4740/regs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4740/regs.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,2392 @@ ++/* ++ * linux/include/asm-mips/mach-jz4740/regs.h ++ * ++ * Ingenic's JZ4740 common include. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __JZ4740_REGS_H__ ++#define __JZ4740_REGS_H__ ++ ++#if defined(__ASSEMBLY__) || defined(__LANGUAGE_ASSEMBLY) ++#define REG8(addr) (addr) ++#define REG16(addr) (addr) ++#define REG32(addr) (addr) ++#else ++#define REG8(addr) *((volatile unsigned char *)(addr)) ++#define REG16(addr) *((volatile unsigned short *)(addr)) ++#define REG32(addr) *((volatile unsigned int *)(addr)) ++#endif ++ ++/* ++ * Define the module base addresses ++ */ ++#define CPM_BASE 0xB0000000 ++#define INTC_BASE 0xB0001000 ++#define TCU_BASE 0xB0002000 ++#define WDT_BASE 0xB0002000 ++#define RTC_BASE 0xB0003000 ++#define GPIO_BASE 0xB0010000 ++#define AIC_BASE 0xB0020000 ++#define ICDC_BASE 0xB0020000 ++#define MSC_BASE 0xB0021000 ++#define UART0_BASE 0xB0030000 ++#define UART1_BASE 0xB0031000 ++#define I2C_BASE 0xB0042000 ++#define SSI_BASE 0xB0043000 ++#define SADC_BASE 0xB0070000 ++#define EMC_BASE 0xB3010000 ++#define DMAC_BASE 0xB3020000 ++#define UHC_BASE 0xB3030000 ++#define UDC_BASE 0xB3040000 ++#define LCD_BASE 0xB3050000 ++#define SLCD_BASE 0xB3050000 ++#define CIM_BASE 0xB3060000 ++#define IPU_BASE 0xB3080000 ++#define ETH_BASE 0xB3100000 ++ ++ ++/************************************************************************* ++ * INTC (Interrupt Controller) ++ *************************************************************************/ ++#define INTC_ISR (INTC_BASE + 0x00) ++#define INTC_IMR (INTC_BASE + 0x04) ++#define INTC_IMSR (INTC_BASE + 0x08) ++#define INTC_IMCR (INTC_BASE + 0x0c) ++#define INTC_IPR (INTC_BASE + 0x10) ++ ++#define REG_INTC_ISR REG32(INTC_ISR) ++#define REG_INTC_IMR REG32(INTC_IMR) ++#define REG_INTC_IMSR REG32(INTC_IMSR) ++#define REG_INTC_IMCR REG32(INTC_IMCR) ++#define REG_INTC_IPR REG32(INTC_IPR) ++ ++// 1st-level interrupts ++#define IRQ_I2C 1 ++#define IRQ_UHC 3 ++#define IRQ_UART1 8 ++#define IRQ_UART0 9 ++#define IRQ_SADC 12 ++#define IRQ_MSC 14 ++#define IRQ_RTC 15 ++#define IRQ_SSI 16 ++#define IRQ_CIM 17 ++#define IRQ_AIC 18 ++#define IRQ_ETH 19 ++#define IRQ_DMAC 20 ++#define IRQ_TCU2 21 ++#define IRQ_TCU1 22 ++#define IRQ_TCU0 23 ++#define IRQ_UDC 24 ++#define IRQ_GPIO3 25 ++#define IRQ_GPIO2 26 ++#define IRQ_GPIO1 27 ++#define IRQ_GPIO0 28 ++#define IRQ_IPU 29 ++#define IRQ_LCD 30 ++ ++// 2nd-level interrupts ++#define IRQ_DMA_0 32 /* 32 to 37 for DMAC channel 0 to 5 */ ++#define IRQ_GPIO_0 48 /* 48 to 175 for GPIO pin 0 to 127 */ ++ ++#define NUM_DMA 6 ++#define NUM_GPIO 128 ++/************************************************************************* ++ * RTC ++ *************************************************************************/ ++#define RTC_RCR (RTC_BASE + 0x00) /* RTC Control Register */ ++#define RTC_RSR (RTC_BASE + 0x04) /* RTC Second Register */ ++#define RTC_RSAR (RTC_BASE + 0x08) /* RTC Second Alarm Register */ ++#define RTC_RGR (RTC_BASE + 0x0c) /* RTC Regulator Register */ ++ ++#define RTC_HCR (RTC_BASE + 0x20) /* Hibernate Control Register */ ++#define RTC_HWFCR (RTC_BASE + 0x24) /* Hibernate Wakeup Filter Counter Reg */ ++#define RTC_HRCR (RTC_BASE + 0x28) /* Hibernate Reset Counter Register */ ++#define RTC_HWCR (RTC_BASE + 0x2c) /* Hibernate Wakeup Control Register */ ++#define RTC_HWRSR (RTC_BASE + 0x30) /* Hibernate Wakeup Status Register */ ++#define RTC_HSPR (RTC_BASE + 0x34) /* Hibernate Scratch Pattern Register */ ++ ++#define REG_RTC_RCR REG32(RTC_RCR) ++#define REG_RTC_RSR REG32(RTC_RSR) ++#define REG_RTC_RSAR REG32(RTC_RSAR) ++#define REG_RTC_RGR REG32(RTC_RGR) ++#define REG_RTC_HCR REG32(RTC_HCR) ++#define REG_RTC_HWFCR REG32(RTC_HWFCR) ++#define REG_RTC_HRCR REG32(RTC_HRCR) ++#define REG_RTC_HWCR REG32(RTC_HWCR) ++#define REG_RTC_HWRSR REG32(RTC_HWRSR) ++#define REG_RTC_HSPR REG32(RTC_HSPR) ++ ++/* RTC Control Register */ ++#define RTC_RCR_WRDY_BIT 7 ++#define RTC_RCR_WRDY (1 << 7) /* Write Ready Flag */ ++#define RTC_RCR_1HZ_BIT 6 ++#define RTC_RCR_1HZ (1 << RTC_RCR_1HZ_BIT) /* 1Hz Flag */ ++#define RTC_RCR_1HZIE (1 << 5) /* 1Hz Interrupt Enable */ ++#define RTC_RCR_AF_BIT 4 ++#define RTC_RCR_AF (1 << RTC_RCR_AF_BIT) /* Alarm Flag */ ++#define RTC_RCR_AIE (1 << 3) /* Alarm Interrupt Enable */ ++#define RTC_RCR_AE (1 << 2) /* Alarm Enable */ ++#define RTC_RCR_RTCE (1 << 0) /* RTC Enable */ ++ ++/* RTC Regulator Register */ ++#define RTC_RGR_LOCK (1 << 31) /* Lock Bit */ ++#define RTC_RGR_ADJC_BIT 16 ++#define RTC_RGR_ADJC_MASK (0x3ff << RTC_RGR_ADJC_BIT) ++#define RTC_RGR_NC1HZ_BIT 0 ++#define RTC_RGR_NC1HZ_MASK (0xffff << RTC_RGR_NC1HZ_BIT) ++ ++/* Hibernate Control Register */ ++#define RTC_HCR_PD (1 << 0) /* Power Down */ ++ ++/* Hibernate Wakeup Filter Counter Register */ ++#define RTC_HWFCR_BIT 5 ++#define RTC_HWFCR_MASK (0x7ff << RTC_HWFCR_BIT) ++ ++/* Hibernate Reset Counter Register */ ++#define RTC_HRCR_BIT 5 ++#define RTC_HRCR_MASK (0x7f << RTC_HRCR_BIT) ++ ++/* Hibernate Wakeup Control Register */ ++#define RTC_HWCR_EALM (1 << 0) /* RTC alarm wakeup enable */ ++ ++/* Hibernate Wakeup Status Register */ ++#define RTC_HWRSR_HR (1 << 5) /* Hibernate reset */ ++#define RTC_HWRSR_PPR (1 << 4) /* PPR reset */ ++#define RTC_HWRSR_PIN (1 << 1) /* Wakeup pin status bit */ ++#define RTC_HWRSR_ALM (1 << 0) /* RTC alarm status bit */ ++ ++ ++/************************************************************************* ++ * CPM (Clock reset and Power control Management) ++ *************************************************************************/ ++#define CPM_CPCCR (CPM_BASE+0x00) ++#define CPM_CPPCR (CPM_BASE+0x10) ++#define CPM_I2SCDR (CPM_BASE+0x60) ++#define CPM_LPCDR (CPM_BASE+0x64) ++#define CPM_MSCCDR (CPM_BASE+0x68) ++#define CPM_UHCCDR (CPM_BASE+0x6C) ++#define CPM_SSICDR (CPM_BASE+0x74) ++ ++#define CPM_LCR (CPM_BASE+0x04) ++#define CPM_CLKGR (CPM_BASE+0x20) ++#define CPM_SCR (CPM_BASE+0x24) ++ ++#define CPM_HCR (CPM_BASE+0x30) ++#define CPM_HWFCR (CPM_BASE+0x34) ++#define CPM_HRCR (CPM_BASE+0x38) ++#define CPM_HWCR (CPM_BASE+0x3c) ++#define CPM_HWSR (CPM_BASE+0x40) ++#define CPM_HSPR (CPM_BASE+0x44) ++ ++#define CPM_RSR (CPM_BASE+0x08) ++ ++ ++#define REG_CPM_CPCCR REG32(CPM_CPCCR) ++#define REG_CPM_CPPCR REG32(CPM_CPPCR) ++#define REG_CPM_I2SCDR REG32(CPM_I2SCDR) ++#define REG_CPM_LPCDR REG32(CPM_LPCDR) ++#define REG_CPM_MSCCDR REG32(CPM_MSCCDR) ++#define REG_CPM_UHCCDR REG32(CPM_UHCCDR) ++#define REG_CPM_SSICDR REG32(CPM_SSICDR) ++ ++#define REG_CPM_LCR REG32(CPM_LCR) ++#define REG_CPM_CLKGR REG32(CPM_CLKGR) ++#define REG_CPM_SCR REG32(CPM_SCR) ++#define REG_CPM_HCR REG32(CPM_HCR) ++#define REG_CPM_HWFCR REG32(CPM_HWFCR) ++#define REG_CPM_HRCR REG32(CPM_HRCR) ++#define REG_CPM_HWCR REG32(CPM_HWCR) ++#define REG_CPM_HWSR REG32(CPM_HWSR) ++#define REG_CPM_HSPR REG32(CPM_HSPR) ++ ++#define REG_CPM_RSR REG32(CPM_RSR) ++ ++ ++/* Clock Control Register */ ++#define CPM_CPCCR_I2CS (1 << 31) ++#define CPM_CPCCR_CLKOEN (1 << 30) ++#define CPM_CPCCR_UCS (1 << 29) ++#define CPM_CPCCR_UDIV_BIT 23 ++#define CPM_CPCCR_UDIV_MASK (0x3f << CPM_CPCCR_UDIV_BIT) ++#define CPM_CPCCR_CE (1 << 22) ++#define CPM_CPCCR_PCS (1 << 21) ++#define CPM_CPCCR_LDIV_BIT 16 ++#define CPM_CPCCR_LDIV_MASK (0x1f << CPM_CPCCR_LDIV_BIT) ++#define CPM_CPCCR_MDIV_BIT 12 ++#define CPM_CPCCR_MDIV_MASK (0x0f << CPM_CPCCR_MDIV_BIT) ++#define CPM_CPCCR_PDIV_BIT 8 ++#define CPM_CPCCR_PDIV_MASK (0x0f << CPM_CPCCR_PDIV_BIT) ++#define CPM_CPCCR_HDIV_BIT 4 ++#define CPM_CPCCR_HDIV_MASK (0x0f << CPM_CPCCR_HDIV_BIT) ++#define CPM_CPCCR_CDIV_BIT 0 ++#define CPM_CPCCR_CDIV_MASK (0x0f << CPM_CPCCR_CDIV_BIT) ++ ++/* I2S Clock Divider Register */ ++#define CPM_I2SCDR_I2SDIV_BIT 0 ++#define CPM_I2SCDR_I2SDIV_MASK (0x1ff << CPM_I2SCDR_I2SDIV_BIT) ++ ++/* LCD Pixel Clock Divider Register */ ++#define CPM_LPCDR_PIXDIV_BIT 0 ++#define CPM_LPCDR_PIXDIV_MASK (0x7ff << CPM_LPCDR_PIXDIV_BIT) ++ ++/* MSC Clock Divider Register */ ++#define CPM_MSCCDR_MSCDIV_BIT 0 ++#define CPM_MSCCDR_MSCDIV_MASK (0x1f << CPM_MSCCDR_MSCDIV_BIT) ++ ++/* UHC Clock Divider Register */ ++#define CPM_UHCCDR_UHCDIV_BIT 0 ++#define CPM_UHCCDR_UHCDIV_MASK (0xf << CPM_UHCCDR_UHCDIV_BIT) ++ ++/* SSI Clock Divider Register */ ++#define CPM_SSICDR_SCS (1<<31) /* SSI clock source selection, 0:EXCLK, 1: PLL */ ++#define CPM_SSICDR_SSIDIV_BIT 0 ++#define CPM_SSICDR_SSIDIV_MASK (0xf << CPM_SSICDR_SSIDIV_BIT) ++ ++/* PLL Control Register */ ++#define CPM_CPPCR_PLLM_BIT 23 ++#define CPM_CPPCR_PLLM_MASK (0x1ff << CPM_CPPCR_PLLM_BIT) ++#define CPM_CPPCR_PLLN_BIT 18 ++#define CPM_CPPCR_PLLN_MASK (0x1f << CPM_CPPCR_PLLN_BIT) ++#define CPM_CPPCR_PLLOD_BIT 16 ++#define CPM_CPPCR_PLLOD_MASK (0x03 << CPM_CPPCR_PLLOD_BIT) ++#define CPM_CPPCR_PLLS (1 << 10) ++#define CPM_CPPCR_PLLBP (1 << 9) ++#define CPM_CPPCR_PLLEN (1 << 8) ++#define CPM_CPPCR_PLLST_BIT 0 ++#define CPM_CPPCR_PLLST_MASK (0xff << CPM_CPPCR_PLLST_BIT) ++ ++/* Low Power Control Register */ ++#define CPM_LCR_DOZE_DUTY_BIT 3 ++#define CPM_LCR_DOZE_DUTY_MASK (0x1f << CPM_LCR_DOZE_DUTY_BIT) ++#define CPM_LCR_DOZE_ON (1 << 2) ++#define CPM_LCR_LPM_BIT 0 ++#define CPM_LCR_LPM_MASK (0x3 << CPM_LCR_LPM_BIT) ++ #define CPM_LCR_LPM_IDLE (0x0 << CPM_LCR_LPM_BIT) ++ #define CPM_LCR_LPM_SLEEP (0x1 << CPM_LCR_LPM_BIT) ++ ++/* Clock Gate Register */ ++#define CPM_CLKGR_UART1 (1 << 15) ++#define CPM_CLKGR_UHC (1 << 14) ++#define CPM_CLKGR_IPU (1 << 13) ++#define CPM_CLKGR_DMAC (1 << 12) ++#define CPM_CLKGR_UDC (1 << 11) ++#define CPM_CLKGR_LCD (1 << 10) ++#define CPM_CLKGR_CIM (1 << 9) ++#define CPM_CLKGR_SADC (1 << 8) ++#define CPM_CLKGR_MSC (1 << 7) ++#define CPM_CLKGR_AIC1 (1 << 6) ++#define CPM_CLKGR_AIC2 (1 << 5) ++#define CPM_CLKGR_SSI (1 << 4) ++#define CPM_CLKGR_I2C (1 << 3) ++#define CPM_CLKGR_RTC (1 << 2) ++#define CPM_CLKGR_TCU (1 << 1) ++#define CPM_CLKGR_UART0 (1 << 0) ++ ++/* Sleep Control Register */ ++#define CPM_SCR_O1ST_BIT 8 ++#define CPM_SCR_O1ST_MASK (0xff << CPM_SCR_O1ST_BIT) ++#define CPM_SCR_USBPHY_ENABLE (1 << 6) ++#define CPM_SCR_OSC_ENABLE (1 << 4) ++ ++/* Hibernate Control Register */ ++#define CPM_HCR_PD (1 << 0) ++ ++/* Wakeup Filter Counter Register in Hibernate Mode */ ++#define CPM_HWFCR_TIME_BIT 0 ++#define CPM_HWFCR_TIME_MASK (0x3ff << CPM_HWFCR_TIME_BIT) ++ ++/* Reset Counter Register in Hibernate Mode */ ++#define CPM_HRCR_TIME_BIT 0 ++#define CPM_HRCR_TIME_MASK (0x7f << CPM_HRCR_TIME_BIT) ++ ++/* Wakeup Control Register in Hibernate Mode */ ++#define CPM_HWCR_WLE_LOW (0 << 2) ++#define CPM_HWCR_WLE_HIGH (1 << 2) ++#define CPM_HWCR_PIN_WAKEUP (1 << 1) ++#define CPM_HWCR_RTC_WAKEUP (1 << 0) ++ ++/* Wakeup Status Register in Hibernate Mode */ ++#define CPM_HWSR_WSR_PIN (1 << 1) ++#define CPM_HWSR_WSR_RTC (1 << 0) ++ ++/* Reset Status Register */ ++#define CPM_RSR_HR (1 << 2) ++#define CPM_RSR_WR (1 << 1) ++#define CPM_RSR_PR (1 << 0) ++ ++ ++/************************************************************************* ++ * TCU (Timer Counter Unit) ++ *************************************************************************/ ++#define TCU_TSR (TCU_BASE + 0x1C) /* Timer Stop Register */ ++#define TCU_TSSR (TCU_BASE + 0x2C) /* Timer Stop Set Register */ ++#define TCU_TSCR (TCU_BASE + 0x3C) /* Timer Stop Clear Register */ ++#define TCU_TER (TCU_BASE + 0x10) /* Timer Counter Enable Register */ ++#define TCU_TESR (TCU_BASE + 0x14) /* Timer Counter Enable Set Register */ ++#define TCU_TECR (TCU_BASE + 0x18) /* Timer Counter Enable Clear Register */ ++#define TCU_TFR (TCU_BASE + 0x20) /* Timer Flag Register */ ++#define TCU_TFSR (TCU_BASE + 0x24) /* Timer Flag Set Register */ ++#define TCU_TFCR (TCU_BASE + 0x28) /* Timer Flag Clear Register */ ++#define TCU_TMR (TCU_BASE + 0x30) /* Timer Mask Register */ ++#define TCU_TMSR (TCU_BASE + 0x34) /* Timer Mask Set Register */ ++#define TCU_TMCR (TCU_BASE + 0x38) /* Timer Mask Clear Register */ ++#define TCU_TDFR0 (TCU_BASE + 0x40) /* Timer Data Full Register */ ++#define TCU_TDHR0 (TCU_BASE + 0x44) /* Timer Data Half Register */ ++#define TCU_TCNT0 (TCU_BASE + 0x48) /* Timer Counter Register */ ++#define TCU_TCSR0 (TCU_BASE + 0x4C) /* Timer Control Register */ ++#define TCU_TDFR1 (TCU_BASE + 0x50) ++#define TCU_TDHR1 (TCU_BASE + 0x54) ++#define TCU_TCNT1 (TCU_BASE + 0x58) ++#define TCU_TCSR1 (TCU_BASE + 0x5C) ++#define TCU_TDFR2 (TCU_BASE + 0x60) ++#define TCU_TDHR2 (TCU_BASE + 0x64) ++#define TCU_TCNT2 (TCU_BASE + 0x68) ++#define TCU_TCSR2 (TCU_BASE + 0x6C) ++#define TCU_TDFR3 (TCU_BASE + 0x70) ++#define TCU_TDHR3 (TCU_BASE + 0x74) ++#define TCU_TCNT3 (TCU_BASE + 0x78) ++#define TCU_TCSR3 (TCU_BASE + 0x7C) ++#define TCU_TDFR4 (TCU_BASE + 0x80) ++#define TCU_TDHR4 (TCU_BASE + 0x84) ++#define TCU_TCNT4 (TCU_BASE + 0x88) ++#define TCU_TCSR4 (TCU_BASE + 0x8C) ++#define TCU_TDFR5 (TCU_BASE + 0x90) ++#define TCU_TDHR5 (TCU_BASE + 0x94) ++#define TCU_TCNT5 (TCU_BASE + 0x98) ++#define TCU_TCSR5 (TCU_BASE + 0x9C) ++ ++#define REG_TCU_TSR REG32(TCU_TSR) ++#define REG_TCU_TSSR REG32(TCU_TSSR) ++#define REG_TCU_TSCR REG32(TCU_TSCR) ++#define REG_TCU_TER REG8(TCU_TER) ++#define REG_TCU_TESR REG8(TCU_TESR) ++#define REG_TCU_TECR REG8(TCU_TECR) ++#define REG_TCU_TFR REG32(TCU_TFR) ++#define REG_TCU_TFSR REG32(TCU_TFSR) ++#define REG_TCU_TFCR REG32(TCU_TFCR) ++#define REG_TCU_TMR REG32(TCU_TMR) ++#define REG_TCU_TMSR REG32(TCU_TMSR) ++#define REG_TCU_TMCR REG32(TCU_TMCR) ++#define REG_TCU_TDFR0 REG16(TCU_TDFR0) ++#define REG_TCU_TDHR0 REG16(TCU_TDHR0) ++#define REG_TCU_TCNT0 REG16(TCU_TCNT0) ++#define REG_TCU_TCSR0 REG16(TCU_TCSR0) ++#define REG_TCU_TDFR1 REG16(TCU_TDFR1) ++#define REG_TCU_TDHR1 REG16(TCU_TDHR1) ++#define REG_TCU_TCNT1 REG16(TCU_TCNT1) ++#define REG_TCU_TCSR1 REG16(TCU_TCSR1) ++#define REG_TCU_TDFR2 REG16(TCU_TDFR2) ++#define REG_TCU_TDHR2 REG16(TCU_TDHR2) ++#define REG_TCU_TCNT2 REG16(TCU_TCNT2) ++#define REG_TCU_TCSR2 REG16(TCU_TCSR2) ++#define REG_TCU_TDFR3 REG16(TCU_TDFR3) ++#define REG_TCU_TDHR3 REG16(TCU_TDHR3) ++#define REG_TCU_TCNT3 REG16(TCU_TCNT3) ++#define REG_TCU_TCSR3 REG16(TCU_TCSR3) ++#define REG_TCU_TDFR4 REG16(TCU_TDFR4) ++#define REG_TCU_TDHR4 REG16(TCU_TDHR4) ++#define REG_TCU_TCNT4 REG16(TCU_TCNT4) ++#define REG_TCU_TCSR4 REG16(TCU_TCSR4) ++ ++// n = 0,1,2,3,4,5 ++#define TCU_TDFR(n) (TCU_BASE + (0x40 + (n)*0x10)) /* Timer Data Full Reg */ ++#define TCU_TDHR(n) (TCU_BASE + (0x44 + (n)*0x10)) /* Timer Data Half Reg */ ++#define TCU_TCNT(n) (TCU_BASE + (0x48 + (n)*0x10)) /* Timer Counter Reg */ ++#define TCU_TCSR(n) (TCU_BASE + (0x4C + (n)*0x10)) /* Timer Control Reg */ ++ ++#define REG_TCU_TDFR(n) REG16(TCU_TDFR((n))) ++#define REG_TCU_TDHR(n) REG16(TCU_TDHR((n))) ++#define REG_TCU_TCNT(n) REG16(TCU_TCNT((n))) ++#define REG_TCU_TCSR(n) REG16(TCU_TCSR((n))) ++ ++// Register definitions ++#define TCU_TCSR_PWM_SD (1 << 9) ++#define TCU_TCSR_PWM_INITL_HIGH (1 << 8) ++#define TCU_TCSR_PWM_EN (1 << 7) ++#define TCU_TCSR_PRESCALE_BIT 3 ++#define TCU_TCSR_PRESCALE_MASK (0x7 << TCU_TCSR_PRESCALE_BIT) ++ #define TCU_TCSR_PRESCALE1 (0x0 << TCU_TCSR_PRESCALE_BIT) ++ #define TCU_TCSR_PRESCALE4 (0x1 << TCU_TCSR_PRESCALE_BIT) ++ #define TCU_TCSR_PRESCALE16 (0x2 << TCU_TCSR_PRESCALE_BIT) ++ #define TCU_TCSR_PRESCALE64 (0x3 << TCU_TCSR_PRESCALE_BIT) ++ #define TCU_TCSR_PRESCALE256 (0x4 << TCU_TCSR_PRESCALE_BIT) ++ #define TCU_TCSR_PRESCALE1024 (0x5 << TCU_TCSR_PRESCALE_BIT) ++#define TCU_TCSR_EXT_EN (1 << 2) ++#define TCU_TCSR_RTC_EN (1 << 1) ++#define TCU_TCSR_PCK_EN (1 << 0) ++ ++#define TCU_TER_TCEN5 (1 << 5) ++#define TCU_TER_TCEN4 (1 << 4) ++#define TCU_TER_TCEN3 (1 << 3) ++#define TCU_TER_TCEN2 (1 << 2) ++#define TCU_TER_TCEN1 (1 << 1) ++#define TCU_TER_TCEN0 (1 << 0) ++ ++#define TCU_TESR_TCST5 (1 << 5) ++#define TCU_TESR_TCST4 (1 << 4) ++#define TCU_TESR_TCST3 (1 << 3) ++#define TCU_TESR_TCST2 (1 << 2) ++#define TCU_TESR_TCST1 (1 << 1) ++#define TCU_TESR_TCST0 (1 << 0) ++ ++#define TCU_TECR_TCCL5 (1 << 5) ++#define TCU_TECR_TCCL4 (1 << 4) ++#define TCU_TECR_TCCL3 (1 << 3) ++#define TCU_TECR_TCCL2 (1 << 2) ++#define TCU_TECR_TCCL1 (1 << 1) ++#define TCU_TECR_TCCL0 (1 << 0) ++ ++#define TCU_TFR_HFLAG5 (1 << 21) ++#define TCU_TFR_HFLAG4 (1 << 20) ++#define TCU_TFR_HFLAG3 (1 << 19) ++#define TCU_TFR_HFLAG2 (1 << 18) ++#define TCU_TFR_HFLAG1 (1 << 17) ++#define TCU_TFR_HFLAG0 (1 << 16) ++#define TCU_TFR_FFLAG5 (1 << 5) ++#define TCU_TFR_FFLAG4 (1 << 4) ++#define TCU_TFR_FFLAG3 (1 << 3) ++#define TCU_TFR_FFLAG2 (1 << 2) ++#define TCU_TFR_FFLAG1 (1 << 1) ++#define TCU_TFR_FFLAG0 (1 << 0) ++ ++#define TCU_TFSR_HFLAG5 (1 << 21) ++#define TCU_TFSR_HFLAG4 (1 << 20) ++#define TCU_TFSR_HFLAG3 (1 << 19) ++#define TCU_TFSR_HFLAG2 (1 << 18) ++#define TCU_TFSR_HFLAG1 (1 << 17) ++#define TCU_TFSR_HFLAG0 (1 << 16) ++#define TCU_TFSR_FFLAG5 (1 << 5) ++#define TCU_TFSR_FFLAG4 (1 << 4) ++#define TCU_TFSR_FFLAG3 (1 << 3) ++#define TCU_TFSR_FFLAG2 (1 << 2) ++#define TCU_TFSR_FFLAG1 (1 << 1) ++#define TCU_TFSR_FFLAG0 (1 << 0) ++ ++#define TCU_TFCR_HFLAG5 (1 << 21) ++#define TCU_TFCR_HFLAG4 (1 << 20) ++#define TCU_TFCR_HFLAG3 (1 << 19) ++#define TCU_TFCR_HFLAG2 (1 << 18) ++#define TCU_TFCR_HFLAG1 (1 << 17) ++#define TCU_TFCR_HFLAG0 (1 << 16) ++#define TCU_TFCR_FFLAG5 (1 << 5) ++#define TCU_TFCR_FFLAG4 (1 << 4) ++#define TCU_TFCR_FFLAG3 (1 << 3) ++#define TCU_TFCR_FFLAG2 (1 << 2) ++#define TCU_TFCR_FFLAG1 (1 << 1) ++#define TCU_TFCR_FFLAG0 (1 << 0) ++ ++#define TCU_TMR_HMASK5 (1 << 21) ++#define TCU_TMR_HMASK4 (1 << 20) ++#define TCU_TMR_HMASK3 (1 << 19) ++#define TCU_TMR_HMASK2 (1 << 18) ++#define TCU_TMR_HMASK1 (1 << 17) ++#define TCU_TMR_HMASK0 (1 << 16) ++#define TCU_TMR_FMASK5 (1 << 5) ++#define TCU_TMR_FMASK4 (1 << 4) ++#define TCU_TMR_FMASK3 (1 << 3) ++#define TCU_TMR_FMASK2 (1 << 2) ++#define TCU_TMR_FMASK1 (1 << 1) ++#define TCU_TMR_FMASK0 (1 << 0) ++ ++#define TCU_TMSR_HMST5 (1 << 21) ++#define TCU_TMSR_HMST4 (1 << 20) ++#define TCU_TMSR_HMST3 (1 << 19) ++#define TCU_TMSR_HMST2 (1 << 18) ++#define TCU_TMSR_HMST1 (1 << 17) ++#define TCU_TMSR_HMST0 (1 << 16) ++#define TCU_TMSR_FMST5 (1 << 5) ++#define TCU_TMSR_FMST4 (1 << 4) ++#define TCU_TMSR_FMST3 (1 << 3) ++#define TCU_TMSR_FMST2 (1 << 2) ++#define TCU_TMSR_FMST1 (1 << 1) ++#define TCU_TMSR_FMST0 (1 << 0) ++ ++#define TCU_TMCR_HMCL5 (1 << 21) ++#define TCU_TMCR_HMCL4 (1 << 20) ++#define TCU_TMCR_HMCL3 (1 << 19) ++#define TCU_TMCR_HMCL2 (1 << 18) ++#define TCU_TMCR_HMCL1 (1 << 17) ++#define TCU_TMCR_HMCL0 (1 << 16) ++#define TCU_TMCR_FMCL5 (1 << 5) ++#define TCU_TMCR_FMCL4 (1 << 4) ++#define TCU_TMCR_FMCL3 (1 << 3) ++#define TCU_TMCR_FMCL2 (1 << 2) ++#define TCU_TMCR_FMCL1 (1 << 1) ++#define TCU_TMCR_FMCL0 (1 << 0) ++ ++#define TCU_TSR_WDTS (1 << 16) ++#define TCU_TSR_STOP5 (1 << 5) ++#define TCU_TSR_STOP4 (1 << 4) ++#define TCU_TSR_STOP3 (1 << 3) ++#define TCU_TSR_STOP2 (1 << 2) ++#define TCU_TSR_STOP1 (1 << 1) ++#define TCU_TSR_STOP0 (1 << 0) ++ ++#define TCU_TSSR_WDTSS (1 << 16) ++#define TCU_TSSR_STPS5 (1 << 5) ++#define TCU_TSSR_STPS4 (1 << 4) ++#define TCU_TSSR_STPS3 (1 << 3) ++#define TCU_TSSR_STPS2 (1 << 2) ++#define TCU_TSSR_STPS1 (1 << 1) ++#define TCU_TSSR_STPS0 (1 << 0) ++ ++#define TCU_TSSR_WDTSC (1 << 16) ++#define TCU_TSSR_STPC5 (1 << 5) ++#define TCU_TSSR_STPC4 (1 << 4) ++#define TCU_TSSR_STPC3 (1 << 3) ++#define TCU_TSSR_STPC2 (1 << 2) ++#define TCU_TSSR_STPC1 (1 << 1) ++#define TCU_TSSR_STPC0 (1 << 0) ++ ++ ++/************************************************************************* ++ * WDT (WatchDog Timer) ++ *************************************************************************/ ++#define WDT_TDR (WDT_BASE + 0x00) ++#define WDT_TCER (WDT_BASE + 0x04) ++#define WDT_TCNT (WDT_BASE + 0x08) ++#define WDT_TCSR (WDT_BASE + 0x0C) ++ ++#define REG_WDT_TDR REG16(WDT_TDR) ++#define REG_WDT_TCER REG8(WDT_TCER) ++#define REG_WDT_TCNT REG16(WDT_TCNT) ++#define REG_WDT_TCSR REG16(WDT_TCSR) ++ ++// Register definition ++#define WDT_TCSR_PRESCALE_BIT 3 ++#define WDT_TCSR_PRESCALE_MASK (0x7 << WDT_TCSR_PRESCALE_BIT) ++ #define WDT_TCSR_PRESCALE1 (0x0 << WDT_TCSR_PRESCALE_BIT) ++ #define WDT_TCSR_PRESCALE4 (0x1 << WDT_TCSR_PRESCALE_BIT) ++ #define WDT_TCSR_PRESCALE16 (0x2 << WDT_TCSR_PRESCALE_BIT) ++ #define WDT_TCSR_PRESCALE64 (0x3 << WDT_TCSR_PRESCALE_BIT) ++ #define WDT_TCSR_PRESCALE256 (0x4 << WDT_TCSR_PRESCALE_BIT) ++ #define WDT_TCSR_PRESCALE1024 (0x5 << WDT_TCSR_PRESCALE_BIT) ++#define WDT_TCSR_EXT_EN (1 << 2) ++#define WDT_TCSR_RTC_EN (1 << 1) ++#define WDT_TCSR_PCK_EN (1 << 0) ++ ++#define WDT_TCER_TCEN (1 << 0) ++ ++ ++/************************************************************************* ++ * DMAC (DMA Controller) ++ *************************************************************************/ ++ ++#define MAX_DMA_NUM 6 /* max 6 channels */ ++ ++#define DMAC_DSAR(n) (DMAC_BASE + (0x00 + (n) * 0x20)) /* DMA source address */ ++#define DMAC_DTAR(n) (DMAC_BASE + (0x04 + (n) * 0x20)) /* DMA target address */ ++#define DMAC_DTCR(n) (DMAC_BASE + (0x08 + (n) * 0x20)) /* DMA transfer count */ ++#define DMAC_DRSR(n) (DMAC_BASE + (0x0c + (n) * 0x20)) /* DMA request source */ ++#define DMAC_DCCSR(n) (DMAC_BASE + (0x10 + (n) * 0x20)) /* DMA control/status */ ++#define DMAC_DCMD(n) (DMAC_BASE + (0x14 + (n) * 0x20)) /* DMA command */ ++#define DMAC_DDA(n) (DMAC_BASE + (0x18 + (n) * 0x20)) /* DMA descriptor address */ ++#define DMAC_DMACR (DMAC_BASE + 0x0300) /* DMA control register */ ++#define DMAC_DMAIPR (DMAC_BASE + 0x0304) /* DMA interrupt pending */ ++#define DMAC_DMADBR (DMAC_BASE + 0x0308) /* DMA doorbell */ ++#define DMAC_DMADBSR (DMAC_BASE + 0x030C) /* DMA doorbell set */ ++ ++// channel 0 ++#define DMAC_DSAR0 DMAC_DSAR(0) ++#define DMAC_DTAR0 DMAC_DTAR(0) ++#define DMAC_DTCR0 DMAC_DTCR(0) ++#define DMAC_DRSR0 DMAC_DRSR(0) ++#define DMAC_DCCSR0 DMAC_DCCSR(0) ++#define DMAC_DCMD0 DMAC_DCMD(0) ++#define DMAC_DDA0 DMAC_DDA(0) ++ ++// channel 1 ++#define DMAC_DSAR1 DMAC_DSAR(1) ++#define DMAC_DTAR1 DMAC_DTAR(1) ++#define DMAC_DTCR1 DMAC_DTCR(1) ++#define DMAC_DRSR1 DMAC_DRSR(1) ++#define DMAC_DCCSR1 DMAC_DCCSR(1) ++#define DMAC_DCMD1 DMAC_DCMD(1) ++#define DMAC_DDA1 DMAC_DDA(1) ++ ++// channel 2 ++#define DMAC_DSAR2 DMAC_DSAR(2) ++#define DMAC_DTAR2 DMAC_DTAR(2) ++#define DMAC_DTCR2 DMAC_DTCR(2) ++#define DMAC_DRSR2 DMAC_DRSR(2) ++#define DMAC_DCCSR2 DMAC_DCCSR(2) ++#define DMAC_DCMD2 DMAC_DCMD(2) ++#define DMAC_DDA2 DMAC_DDA(2) ++ ++// channel 3 ++#define DMAC_DSAR3 DMAC_DSAR(3) ++#define DMAC_DTAR3 DMAC_DTAR(3) ++#define DMAC_DTCR3 DMAC_DTCR(3) ++#define DMAC_DRSR3 DMAC_DRSR(3) ++#define DMAC_DCCSR3 DMAC_DCCSR(3) ++#define DMAC_DCMD3 DMAC_DCMD(3) ++#define DMAC_DDA3 DMAC_DDA(3) ++ ++// channel 4 ++#define DMAC_DSAR4 DMAC_DSAR(4) ++#define DMAC_DTAR4 DMAC_DTAR(4) ++#define DMAC_DTCR4 DMAC_DTCR(4) ++#define DMAC_DRSR4 DMAC_DRSR(4) ++#define DMAC_DCCSR4 DMAC_DCCSR(4) ++#define DMAC_DCMD4 DMAC_DCMD(4) ++#define DMAC_DDA4 DMAC_DDA(4) ++ ++// channel 5 ++#define DMAC_DSAR5 DMAC_DSAR(5) ++#define DMAC_DTAR5 DMAC_DTAR(5) ++#define DMAC_DTCR5 DMAC_DTCR(5) ++#define DMAC_DRSR5 DMAC_DRSR(5) ++#define DMAC_DCCSR5 DMAC_DCCSR(5) ++#define DMAC_DCMD5 DMAC_DCMD(5) ++#define DMAC_DDA5 DMAC_DDA(5) ++ ++#define REG_DMAC_DSAR(n) REG32(DMAC_DSAR((n))) ++#define REG_DMAC_DTAR(n) REG32(DMAC_DTAR((n))) ++#define REG_DMAC_DTCR(n) REG32(DMAC_DTCR((n))) ++#define REG_DMAC_DRSR(n) REG32(DMAC_DRSR((n))) ++#define REG_DMAC_DCCSR(n) REG32(DMAC_DCCSR((n))) ++#define REG_DMAC_DCMD(n) REG32(DMAC_DCMD((n))) ++#define REG_DMAC_DDA(n) REG32(DMAC_DDA((n))) ++#define REG_DMAC_DMACR REG32(DMAC_DMACR) ++#define REG_DMAC_DMAIPR REG32(DMAC_DMAIPR) ++#define REG_DMAC_DMADBR REG32(DMAC_DMADBR) ++#define REG_DMAC_DMADBSR REG32(DMAC_DMADBSR) ++ ++// DMA request source register ++#define DMAC_DRSR_RS_BIT 0 ++#define DMAC_DRSR_RS_MASK (0x1f << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_AUTO (8 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_UART0OUT (20 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_UART0IN (21 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_SSIOUT (22 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_SSIIN (23 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_AICOUT (24 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_AICIN (25 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_MSCOUT (26 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_MSCIN (27 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_TCU (28 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_SADC (29 << DMAC_DRSR_RS_BIT) ++ #define DMAC_DRSR_RS_SLCD (30 << DMAC_DRSR_RS_BIT) ++ ++// DMA channel control/status register ++#define DMAC_DCCSR_NDES (1 << 31) /* descriptor (0) or not (1) ? */ ++#define DMAC_DCCSR_CDOA_BIT 16 /* copy of DMA offset address */ ++#define DMAC_DCCSR_CDOA_MASK (0xff << DMAC_DCCSR_CDOA_BIT) ++#define DMAC_DCCSR_INV (1 << 6) /* descriptor invalid */ ++#define DMAC_DCCSR_AR (1 << 4) /* address error */ ++#define DMAC_DCCSR_TT (1 << 3) /* transfer terminated */ ++#define DMAC_DCCSR_HLT (1 << 2) /* DMA halted */ ++#define DMAC_DCCSR_CT (1 << 1) /* count terminated */ ++#define DMAC_DCCSR_EN (1 << 0) /* channel enable bit */ ++ ++// DMA channel command register ++#define DMAC_DCMD_SAI (1 << 23) /* source address increment */ ++#define DMAC_DCMD_DAI (1 << 22) /* dest address increment */ ++#define DMAC_DCMD_RDIL_BIT 16 /* request detection interval length */ ++#define DMAC_DCMD_RDIL_MASK (0x0f << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_IGN (0 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_2 (1 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_4 (2 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_8 (3 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_12 (4 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_16 (5 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_20 (6 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_24 (7 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_28 (8 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_32 (9 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_48 (10 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_60 (11 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_64 (12 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_124 (13 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_128 (14 << DMAC_DCMD_RDIL_BIT) ++ #define DMAC_DCMD_RDIL_200 (15 << DMAC_DCMD_RDIL_BIT) ++#define DMAC_DCMD_SWDH_BIT 14 /* source port width */ ++#define DMAC_DCMD_SWDH_MASK (0x03 << DMAC_DCMD_SWDH_BIT) ++ #define DMAC_DCMD_SWDH_32 (0 << DMAC_DCMD_SWDH_BIT) ++ #define DMAC_DCMD_SWDH_8 (1 << DMAC_DCMD_SWDH_BIT) ++ #define DMAC_DCMD_SWDH_16 (2 << DMAC_DCMD_SWDH_BIT) ++#define DMAC_DCMD_DWDH_BIT 12 /* dest port width */ ++#define DMAC_DCMD_DWDH_MASK (0x03 << DMAC_DCMD_DWDH_BIT) ++ #define DMAC_DCMD_DWDH_32 (0 << DMAC_DCMD_DWDH_BIT) ++ #define DMAC_DCMD_DWDH_8 (1 << DMAC_DCMD_DWDH_BIT) ++ #define DMAC_DCMD_DWDH_16 (2 << DMAC_DCMD_DWDH_BIT) ++#define DMAC_DCMD_DS_BIT 8 /* transfer data size of a data unit */ ++#define DMAC_DCMD_DS_MASK (0x07 << DMAC_DCMD_DS_BIT) ++ #define DMAC_DCMD_DS_32BIT (0 << DMAC_DCMD_DS_BIT) ++ #define DMAC_DCMD_DS_8BIT (1 << DMAC_DCMD_DS_BIT) ++ #define DMAC_DCMD_DS_16BIT (2 << DMAC_DCMD_DS_BIT) ++ #define DMAC_DCMD_DS_16BYTE (3 << DMAC_DCMD_DS_BIT) ++ #define DMAC_DCMD_DS_32BYTE (4 << DMAC_DCMD_DS_BIT) ++#define DMAC_DCMD_TM (1 << 7) /* transfer mode: 0-single 1-block */ ++#define DMAC_DCMD_DES_V (1 << 4) /* descriptor valid flag */ ++#define DMAC_DCMD_DES_VM (1 << 3) /* descriptor valid mask: 1:support V-bit */ ++#define DMAC_DCMD_DES_VIE (1 << 2) /* DMA valid error interrupt enable */ ++#define DMAC_DCMD_TIE (1 << 1) /* DMA transfer interrupt enable */ ++#define DMAC_DCMD_LINK (1 << 0) /* descriptor link enable */ ++ ++// DMA descriptor address register ++#define DMAC_DDA_BASE_BIT 12 /* descriptor base address */ ++#define DMAC_DDA_BASE_MASK (0x0fffff << DMAC_DDA_BASE_BIT) ++#define DMAC_DDA_OFFSET_BIT 4 /* descriptor offset address */ ++#define DMAC_DDA_OFFSET_MASK (0x0ff << DMAC_DDA_OFFSET_BIT) ++ ++// DMA control register ++#define DMAC_DMACR_PR_BIT 8 /* channel priority mode */ ++#define DMAC_DMACR_PR_MASK (0x03 << DMAC_DMACR_PR_BIT) ++ #define DMAC_DMACR_PR_012345 (0 << DMAC_DMACR_PR_BIT) ++ #define DMAC_DMACR_PR_023145 (1 << DMAC_DMACR_PR_BIT) ++ #define DMAC_DMACR_PR_201345 (2 << DMAC_DMACR_PR_BIT) ++ #define DMAC_DMACR_PR_RR (3 << DMAC_DMACR_PR_BIT) /* round robin */ ++#define DMAC_DMACR_HLT (1 << 3) /* DMA halt flag */ ++#define DMAC_DMACR_AR (1 << 2) /* address error flag */ ++#define DMAC_DMACR_DMAE (1 << 0) /* DMA enable bit */ ++ ++// DMA doorbell register ++#define DMAC_DMADBR_DB5 (1 << 5) /* doorbell for channel 5 */ ++#define DMAC_DMADBR_DB4 (1 << 5) /* doorbell for channel 4 */ ++#define DMAC_DMADBR_DB3 (1 << 5) /* doorbell for channel 3 */ ++#define DMAC_DMADBR_DB2 (1 << 5) /* doorbell for channel 2 */ ++#define DMAC_DMADBR_DB1 (1 << 5) /* doorbell for channel 1 */ ++#define DMAC_DMADBR_DB0 (1 << 5) /* doorbell for channel 0 */ ++ ++// DMA doorbell set register ++#define DMAC_DMADBSR_DBS5 (1 << 5) /* enable doorbell for channel 5 */ ++#define DMAC_DMADBSR_DBS4 (1 << 5) /* enable doorbell for channel 4 */ ++#define DMAC_DMADBSR_DBS3 (1 << 5) /* enable doorbell for channel 3 */ ++#define DMAC_DMADBSR_DBS2 (1 << 5) /* enable doorbell for channel 2 */ ++#define DMAC_DMADBSR_DBS1 (1 << 5) /* enable doorbell for channel 1 */ ++#define DMAC_DMADBSR_DBS0 (1 << 5) /* enable doorbell for channel 0 */ ++ ++// DMA interrupt pending register ++#define DMAC_DMAIPR_CIRQ5 (1 << 5) /* irq pending status for channel 5 */ ++#define DMAC_DMAIPR_CIRQ4 (1 << 4) /* irq pending status for channel 4 */ ++#define DMAC_DMAIPR_CIRQ3 (1 << 3) /* irq pending status for channel 3 */ ++#define DMAC_DMAIPR_CIRQ2 (1 << 2) /* irq pending status for channel 2 */ ++#define DMAC_DMAIPR_CIRQ1 (1 << 1) /* irq pending status for channel 1 */ ++#define DMAC_DMAIPR_CIRQ0 (1 << 0) /* irq pending status for channel 0 */ ++ ++ ++/************************************************************************* ++ * GPIO (General-Purpose I/O Ports) ++ *************************************************************************/ ++#define MAX_GPIO_NUM 128 ++ ++//n = 0,1,2,3 ++#define GPIO_PXPIN(n) (GPIO_BASE + (0x00 + (n)*0x100)) /* PIN Level Register */ ++#define GPIO_PXDAT(n) (GPIO_BASE + (0x10 + (n)*0x100)) /* Port Data Register */ ++#define GPIO_PXDATS(n) (GPIO_BASE + (0x14 + (n)*0x100)) /* Port Data Set Register */ ++#define GPIO_PXDATC(n) (GPIO_BASE + (0x18 + (n)*0x100)) /* Port Data Clear Register */ ++#define GPIO_PXIM(n) (GPIO_BASE + (0x20 + (n)*0x100)) /* Interrupt Mask Register */ ++#define GPIO_PXIMS(n) (GPIO_BASE + (0x24 + (n)*0x100)) /* Interrupt Mask Set Reg */ ++#define GPIO_PXIMC(n) (GPIO_BASE + (0x28 + (n)*0x100)) /* Interrupt Mask Clear Reg */ ++#define GPIO_PXPE(n) (GPIO_BASE + (0x30 + (n)*0x100)) /* Pull Enable Register */ ++#define GPIO_PXPES(n) (GPIO_BASE + (0x34 + (n)*0x100)) /* Pull Enable Set Reg. */ ++#define GPIO_PXPEC(n) (GPIO_BASE + (0x38 + (n)*0x100)) /* Pull Enable Clear Reg. */ ++#define GPIO_PXFUN(n) (GPIO_BASE + (0x40 + (n)*0x100)) /* Function Register */ ++#define GPIO_PXFUNS(n) (GPIO_BASE + (0x44 + (n)*0x100)) /* Function Set Register */ ++#define GPIO_PXFUNC(n) (GPIO_BASE + (0x48 + (n)*0x100)) /* Function Clear Register */ ++#define GPIO_PXSEL(n) (GPIO_BASE + (0x50 + (n)*0x100)) /* Select Register */ ++#define GPIO_PXSELS(n) (GPIO_BASE + (0x54 + (n)*0x100)) /* Select Set Register */ ++#define GPIO_PXSELC(n) (GPIO_BASE + (0x58 + (n)*0x100)) /* Select Clear Register */ ++#define GPIO_PXDIR(n) (GPIO_BASE + (0x60 + (n)*0x100)) /* Direction Register */ ++#define GPIO_PXDIRS(n) (GPIO_BASE + (0x64 + (n)*0x100)) /* Direction Set Register */ ++#define GPIO_PXDIRC(n) (GPIO_BASE + (0x68 + (n)*0x100)) /* Direction Clear Register */ ++#define GPIO_PXTRG(n) (GPIO_BASE + (0x70 + (n)*0x100)) /* Trigger Register */ ++#define GPIO_PXTRGS(n) (GPIO_BASE + (0x74 + (n)*0x100)) /* Trigger Set Register */ ++#define GPIO_PXTRGC(n) (GPIO_BASE + (0x78 + (n)*0x100)) /* Trigger Set Register */ ++#define GPIO_PXFLG(n) (GPIO_BASE + (0x80 + (n)*0x100)) /* Port Flag Register */ ++#define GPIO_PXFLGC(n) (GPIO_BASE + (0x14 + (n)*0x100)) /* Port Flag Clear Register */ ++ ++#define REG_GPIO_PXPIN(n) REG32(GPIO_PXPIN((n))) /* PIN level */ ++#define REG_GPIO_PXDAT(n) REG32(GPIO_PXDAT((n))) /* 1: interrupt pending */ ++#define REG_GPIO_PXDATS(n) REG32(GPIO_PXDATS((n))) ++#define REG_GPIO_PXDATC(n) REG32(GPIO_PXDATC((n))) ++#define REG_GPIO_PXIM(n) REG32(GPIO_PXIM((n))) /* 1: mask pin interrupt */ ++#define REG_GPIO_PXIMS(n) REG32(GPIO_PXIMS((n))) ++#define REG_GPIO_PXIMC(n) REG32(GPIO_PXIMC((n))) ++#define REG_GPIO_PXPE(n) REG32(GPIO_PXPE((n))) /* 1: disable pull up/down */ ++#define REG_GPIO_PXPES(n) REG32(GPIO_PXPES((n))) ++#define REG_GPIO_PXPEC(n) REG32(GPIO_PXPEC((n))) ++#define REG_GPIO_PXFUN(n) REG32(GPIO_PXFUN((n))) /* 0:GPIO or intr, 1:FUNC */ ++#define REG_GPIO_PXFUNS(n) REG32(GPIO_PXFUNS((n))) ++#define REG_GPIO_PXFUNC(n) REG32(GPIO_PXFUNC((n))) ++#define REG_GPIO_PXSEL(n) REG32(GPIO_PXSEL((n))) /* 0:GPIO/Fun0,1:intr/fun1*/ ++#define REG_GPIO_PXSELS(n) REG32(GPIO_PXSELS((n))) ++#define REG_GPIO_PXSELC(n) REG32(GPIO_PXSELC((n))) ++#define REG_GPIO_PXDIR(n) REG32(GPIO_PXDIR((n))) /* 0:input/low-level-trig/falling-edge-trig, 1:output/high-level-trig/rising-edge-trig */ ++#define REG_GPIO_PXDIRS(n) REG32(GPIO_PXDIRS((n))) ++#define REG_GPIO_PXDIRC(n) REG32(GPIO_PXDIRC((n))) ++#define REG_GPIO_PXTRG(n) REG32(GPIO_PXTRG((n))) /* 0:level-trigger, 1:edge-trigger */ ++#define REG_GPIO_PXTRGS(n) REG32(GPIO_PXTRGS((n))) ++#define REG_GPIO_PXTRGC(n) REG32(GPIO_PXTRGC((n))) ++#define REG_GPIO_PXFLG(n) REG32(GPIO_PXFLG((n))) /* interrupt flag */ ++#define REG_GPIO_PXFLGC(n) REG32(GPIO_PXFLGC((n))) /* interrupt flag */ ++ ++ ++/************************************************************************* ++ * UART ++ *************************************************************************/ ++ ++#define IRDA_BASE UART0_BASE ++#define UART_BASE UART0_BASE ++#define UART_OFF 0x1000 ++ ++/* Register Offset */ ++#define OFF_RDR (0x00) /* R 8b H'xx */ ++#define OFF_TDR (0x00) /* W 8b H'xx */ ++#define OFF_DLLR (0x00) /* RW 8b H'00 */ ++#define OFF_DLHR (0x04) /* RW 8b H'00 */ ++#define OFF_IER (0x04) /* RW 8b H'00 */ ++#define OFF_ISR (0x08) /* R 8b H'01 */ ++#define OFF_FCR (0x08) /* W 8b H'00 */ ++#define OFF_LCR (0x0C) /* RW 8b H'00 */ ++#define OFF_MCR (0x10) /* RW 8b H'00 */ ++#define OFF_LSR (0x14) /* R 8b H'00 */ ++#define OFF_MSR (0x18) /* R 8b H'00 */ ++#define OFF_SPR (0x1C) /* RW 8b H'00 */ ++#define OFF_SIRCR (0x20) /* RW 8b H'00, UART0 */ ++#define OFF_UMR (0x24) /* RW 8b H'00, UART M Register */ ++#define OFF_UACR (0x28) /* RW 8b H'00, UART Add Cycle Register */ ++ ++/* Register Address */ ++#define UART0_RDR (UART0_BASE + OFF_RDR) ++#define UART0_TDR (UART0_BASE + OFF_TDR) ++#define UART0_DLLR (UART0_BASE + OFF_DLLR) ++#define UART0_DLHR (UART0_BASE + OFF_DLHR) ++#define UART0_IER (UART0_BASE + OFF_IER) ++#define UART0_ISR (UART0_BASE + OFF_ISR) ++#define UART0_FCR (UART0_BASE + OFF_FCR) ++#define UART0_LCR (UART0_BASE + OFF_LCR) ++#define UART0_MCR (UART0_BASE + OFF_MCR) ++#define UART0_LSR (UART0_BASE + OFF_LSR) ++#define UART0_MSR (UART0_BASE + OFF_MSR) ++#define UART0_SPR (UART0_BASE + OFF_SPR) ++#define UART0_SIRCR (UART0_BASE + OFF_SIRCR) ++#define UART0_UMR (UART0_BASE + OFF_UMR) ++#define UART0_UACR (UART0_BASE + OFF_UACR) ++ ++/* ++ * Define macros for UARTIER ++ * UART Interrupt Enable Register ++ */ ++#define UARTIER_RIE (1 << 0) /* 0: receive fifo full interrupt disable */ ++#define UARTIER_TIE (1 << 1) /* 0: transmit fifo empty interrupt disable */ ++#define UARTIER_RLIE (1 << 2) /* 0: receive line status interrupt disable */ ++#define UARTIER_MIE (1 << 3) /* 0: modem status interrupt disable */ ++#define UARTIER_RTIE (1 << 4) /* 0: receive timeout interrupt disable */ ++ ++/* ++ * Define macros for UARTISR ++ * UART Interrupt Status Register ++ */ ++#define UARTISR_IP (1 << 0) /* 0: interrupt is pending 1: no interrupt */ ++#define UARTISR_IID (7 << 1) /* Source of Interrupt */ ++#define UARTISR_IID_MSI (0 << 1) /* Modem status interrupt */ ++#define UARTISR_IID_THRI (1 << 1) /* Transmitter holding register empty */ ++#define UARTISR_IID_RDI (2 << 1) /* Receiver data interrupt */ ++#define UARTISR_IID_RLSI (3 << 1) /* Receiver line status interrupt */ ++#define UARTISR_IID_RTO (6 << 1) /* Receive timeout */ ++#define UARTISR_FFMS (3 << 6) /* FIFO mode select, set when UARTFCR.FE is set to 1 */ ++#define UARTISR_FFMS_NO_FIFO (0 << 6) ++#define UARTISR_FFMS_FIFO_MODE (3 << 6) ++ ++/* ++ * Define macros for UARTFCR ++ * UART FIFO Control Register ++ */ ++#define UARTFCR_FE (1 << 0) /* 0: non-FIFO mode 1: FIFO mode */ ++#define UARTFCR_RFLS (1 << 1) /* write 1 to flush receive FIFO */ ++#define UARTFCR_TFLS (1 << 2) /* write 1 to flush transmit FIFO */ ++#define UARTFCR_DMS (1 << 3) /* 0: disable DMA mode */ ++#define UARTFCR_UUE (1 << 4) /* 0: disable UART */ ++#define UARTFCR_RTRG (3 << 6) /* Receive FIFO Data Trigger */ ++#define UARTFCR_RTRG_1 (0 << 6) ++#define UARTFCR_RTRG_4 (1 << 6) ++#define UARTFCR_RTRG_8 (2 << 6) ++#define UARTFCR_RTRG_15 (3 << 6) ++ ++/* ++ * Define macros for UARTLCR ++ * UART Line Control Register ++ */ ++#define UARTLCR_WLEN (3 << 0) /* word length */ ++#define UARTLCR_WLEN_5 (0 << 0) ++#define UARTLCR_WLEN_6 (1 << 0) ++#define UARTLCR_WLEN_7 (2 << 0) ++#define UARTLCR_WLEN_8 (3 << 0) ++#define UARTLCR_STOP (1 << 2) /* 0: 1 stop bit when word length is 5,6,7,8 ++ 1: 1.5 stop bits when 5; 2 stop bits when 6,7,8 */ ++#define UARTLCR_STOP1 (0 << 2) ++#define UARTLCR_STOP2 (1 << 2) ++#define UARTLCR_PE (1 << 3) /* 0: parity disable */ ++#define UARTLCR_PROE (1 << 4) /* 0: even parity 1: odd parity */ ++#define UARTLCR_SPAR (1 << 5) /* 0: sticky parity disable */ ++#define UARTLCR_SBRK (1 << 6) /* write 0 normal, write 1 send break */ ++#define UARTLCR_DLAB (1 << 7) /* 0: access UARTRDR/TDR/IER 1: access UARTDLLR/DLHR */ ++ ++/* ++ * Define macros for UARTLSR ++ * UART Line Status Register ++ */ ++#define UARTLSR_DR (1 << 0) /* 0: receive FIFO is empty 1: receive data is ready */ ++#define UARTLSR_ORER (1 << 1) /* 0: no overrun error */ ++#define UARTLSR_PER (1 << 2) /* 0: no parity error */ ++#define UARTLSR_FER (1 << 3) /* 0; no framing error */ ++#define UARTLSR_BRK (1 << 4) /* 0: no break detected 1: receive a break signal */ ++#define UARTLSR_TDRQ (1 << 5) /* 1: transmit FIFO half "empty" */ ++#define UARTLSR_TEMT (1 << 6) /* 1: transmit FIFO and shift registers empty */ ++#define UARTLSR_RFER (1 << 7) /* 0: no receive error 1: receive error in FIFO mode */ ++ ++/* ++ * Define macros for UARTMCR ++ * UART Modem Control Register ++ */ ++#define UARTMCR_RTS (1 << 1) /* 0: RTS_ output high, 1: RTS_ output low */ ++#define UARTMCR_LOOP (1 << 4) /* 0: normal 1: loopback mode */ ++#define UARTMCR_MCE (1 << 7) /* 0: modem function is disable */ ++ ++/* ++ * Define macros for UARTMSR ++ * UART Modem Status Register ++ */ ++#define UARTMSR_CCTS (1 << 0) /* 1: a change on CTS_ pin */ ++#define UARTMSR_CTS (1 << 4) /* 0: CTS_ pin is high */ ++ ++/* ++ * Define macros for SIRCR ++ * Slow IrDA Control Register ++ */ ++#define SIRCR_TSIRE (1 << 0) /* 0: transmitter is in UART mode 1: SIR mode */ ++#define SIRCR_RSIRE (1 << 1) /* 0: receiver is in UART mode 1: SIR mode */ ++#define SIRCR_TPWS (1 << 2) /* 0: transmit 0 pulse width is 3/16 of bit length ++ 1: 0 pulse width is 1.6us for 115.2Kbps */ ++#define SIRCR_TDPL (1 << 3) /* 0: encoder generates a positive pulse for 0 */ ++#define SIRCR_RDPL (1 << 4) /* 0: decoder interprets positive pulse as 0 */ ++ ++ ++/************************************************************************* ++ * AIC (AC97/I2S Controller) ++ *************************************************************************/ ++#define AIC_FR (AIC_BASE + 0x000) ++#define AIC_CR (AIC_BASE + 0x004) ++#define AIC_ACCR1 (AIC_BASE + 0x008) ++#define AIC_ACCR2 (AIC_BASE + 0x00C) ++#define AIC_I2SCR (AIC_BASE + 0x010) ++#define AIC_SR (AIC_BASE + 0x014) ++#define AIC_ACSR (AIC_BASE + 0x018) ++#define AIC_I2SSR (AIC_BASE + 0x01C) ++#define AIC_ACCAR (AIC_BASE + 0x020) ++#define AIC_ACCDR (AIC_BASE + 0x024) ++#define AIC_ACSAR (AIC_BASE + 0x028) ++#define AIC_ACSDR (AIC_BASE + 0x02C) ++#define AIC_I2SDIV (AIC_BASE + 0x030) ++#define AIC_DR (AIC_BASE + 0x034) ++ ++#define REG_AIC_FR REG32(AIC_FR) ++#define REG_AIC_CR REG32(AIC_CR) ++#define REG_AIC_ACCR1 REG32(AIC_ACCR1) ++#define REG_AIC_ACCR2 REG32(AIC_ACCR2) ++#define REG_AIC_I2SCR REG32(AIC_I2SCR) ++#define REG_AIC_SR REG32(AIC_SR) ++#define REG_AIC_ACSR REG32(AIC_ACSR) ++#define REG_AIC_I2SSR REG32(AIC_I2SSR) ++#define REG_AIC_ACCAR REG32(AIC_ACCAR) ++#define REG_AIC_ACCDR REG32(AIC_ACCDR) ++#define REG_AIC_ACSAR REG32(AIC_ACSAR) ++#define REG_AIC_ACSDR REG32(AIC_ACSDR) ++#define REG_AIC_I2SDIV REG32(AIC_I2SDIV) ++#define REG_AIC_DR REG32(AIC_DR) ++ ++/* AIC Controller Configuration Register (AIC_FR) */ ++ ++#define AIC_FR_RFTH_BIT 12 /* Receive FIFO Threshold */ ++#define AIC_FR_RFTH_MASK (0xf << AIC_FR_RFTH_BIT) ++#define AIC_FR_TFTH_BIT 8 /* Transmit FIFO Threshold */ ++#define AIC_FR_TFTH_MASK (0xf << AIC_FR_TFTH_BIT) ++#define AIC_FR_LSMP (1 << 6) /* Play Zero sample or last sample */ ++#define AIC_FR_ICDC (1 << 5) /* External(0) or Internal CODEC(1) */ ++#define AIC_FR_AUSEL (1 << 4) /* AC97(0) or I2S/MSB-justified(1) */ ++#define AIC_FR_RST (1 << 3) /* AIC registers reset */ ++#define AIC_FR_BCKD (1 << 2) /* I2S BIT_CLK direction, 0:input,1:output */ ++#define AIC_FR_SYNCD (1 << 1) /* I2S SYNC direction, 0:input,1:output */ ++#define AIC_FR_ENB (1 << 0) /* AIC enable bit */ ++ ++/* AIC Controller Common Control Register (AIC_CR) */ ++ ++#define AIC_CR_OSS_BIT 19 /* Output Sample Size from memory (AIC V2 only) */ ++#define AIC_CR_OSS_MASK (0x7 << AIC_CR_OSS_BIT) ++ #define AIC_CR_OSS_8BIT (0x0 << AIC_CR_OSS_BIT) ++ #define AIC_CR_OSS_16BIT (0x1 << AIC_CR_OSS_BIT) ++ #define AIC_CR_OSS_18BIT (0x2 << AIC_CR_OSS_BIT) ++ #define AIC_CR_OSS_20BIT (0x3 << AIC_CR_OSS_BIT) ++ #define AIC_CR_OSS_24BIT (0x4 << AIC_CR_OSS_BIT) ++#define AIC_CR_ISS_BIT 16 /* Input Sample Size from memory (AIC V2 only) */ ++#define AIC_CR_ISS_MASK (0x7 << AIC_CR_ISS_BIT) ++ #define AIC_CR_ISS_8BIT (0x0 << AIC_CR_ISS_BIT) ++ #define AIC_CR_ISS_16BIT (0x1 << AIC_CR_ISS_BIT) ++ #define AIC_CR_ISS_18BIT (0x2 << AIC_CR_ISS_BIT) ++ #define AIC_CR_ISS_20BIT (0x3 << AIC_CR_ISS_BIT) ++ #define AIC_CR_ISS_24BIT (0x4 << AIC_CR_ISS_BIT) ++#define AIC_CR_RDMS (1 << 15) /* Receive DMA enable */ ++#define AIC_CR_TDMS (1 << 14) /* Transmit DMA enable */ ++#define AIC_CR_M2S (1 << 11) /* Mono to Stereo enable */ ++#define AIC_CR_ENDSW (1 << 10) /* Endian switch enable */ ++#define AIC_CR_AVSTSU (1 << 9) /* Signed <-> Unsigned toggle enable */ ++#define AIC_CR_FLUSH (1 << 8) /* Flush FIFO */ ++#define AIC_CR_EROR (1 << 6) /* Enable ROR interrupt */ ++#define AIC_CR_ETUR (1 << 5) /* Enable TUR interrupt */ ++#define AIC_CR_ERFS (1 << 4) /* Enable RFS interrupt */ ++#define AIC_CR_ETFS (1 << 3) /* Enable TFS interrupt */ ++#define AIC_CR_ENLBF (1 << 2) /* Enable Loopback Function */ ++#define AIC_CR_ERPL (1 << 1) /* Enable Playback Function */ ++#define AIC_CR_EREC (1 << 0) /* Enable Record Function */ ++ ++/* AIC Controller AC-link Control Register 1 (AIC_ACCR1) */ ++ ++#define AIC_ACCR1_RS_BIT 16 /* Receive Valid Slots */ ++#define AIC_ACCR1_RS_MASK (0x3ff << AIC_ACCR1_RS_BIT) ++ #define AIC_ACCR1_RS_SLOT12 (1 << 25) /* Slot 12 valid bit */ ++ #define AIC_ACCR1_RS_SLOT11 (1 << 24) /* Slot 11 valid bit */ ++ #define AIC_ACCR1_RS_SLOT10 (1 << 23) /* Slot 10 valid bit */ ++ #define AIC_ACCR1_RS_SLOT9 (1 << 22) /* Slot 9 valid bit, LFE */ ++ #define AIC_ACCR1_RS_SLOT8 (1 << 21) /* Slot 8 valid bit, Surround Right */ ++ #define AIC_ACCR1_RS_SLOT7 (1 << 20) /* Slot 7 valid bit, Surround Left */ ++ #define AIC_ACCR1_RS_SLOT6 (1 << 19) /* Slot 6 valid bit, PCM Center */ ++ #define AIC_ACCR1_RS_SLOT5 (1 << 18) /* Slot 5 valid bit */ ++ #define AIC_ACCR1_RS_SLOT4 (1 << 17) /* Slot 4 valid bit, PCM Right */ ++ #define AIC_ACCR1_RS_SLOT3 (1 << 16) /* Slot 3 valid bit, PCM Left */ ++#define AIC_ACCR1_XS_BIT 0 /* Transmit Valid Slots */ ++#define AIC_ACCR1_XS_MASK (0x3ff << AIC_ACCR1_XS_BIT) ++ #define AIC_ACCR1_XS_SLOT12 (1 << 9) /* Slot 12 valid bit */ ++ #define AIC_ACCR1_XS_SLOT11 (1 << 8) /* Slot 11 valid bit */ ++ #define AIC_ACCR1_XS_SLOT10 (1 << 7) /* Slot 10 valid bit */ ++ #define AIC_ACCR1_XS_SLOT9 (1 << 6) /* Slot 9 valid bit, LFE */ ++ #define AIC_ACCR1_XS_SLOT8 (1 << 5) /* Slot 8 valid bit, Surround Right */ ++ #define AIC_ACCR1_XS_SLOT7 (1 << 4) /* Slot 7 valid bit, Surround Left */ ++ #define AIC_ACCR1_XS_SLOT6 (1 << 3) /* Slot 6 valid bit, PCM Center */ ++ #define AIC_ACCR1_XS_SLOT5 (1 << 2) /* Slot 5 valid bit */ ++ #define AIC_ACCR1_XS_SLOT4 (1 << 1) /* Slot 4 valid bit, PCM Right */ ++ #define AIC_ACCR1_XS_SLOT3 (1 << 0) /* Slot 3 valid bit, PCM Left */ ++ ++/* AIC Controller AC-link Control Register 2 (AIC_ACCR2) */ ++ ++#define AIC_ACCR2_ERSTO (1 << 18) /* Enable RSTO interrupt */ ++#define AIC_ACCR2_ESADR (1 << 17) /* Enable SADR interrupt */ ++#define AIC_ACCR2_ECADT (1 << 16) /* Enable CADT interrupt */ ++#define AIC_ACCR2_OASS_BIT 8 /* Output Sample Size for AC-link */ ++#define AIC_ACCR2_OASS_MASK (0x3 << AIC_ACCR2_OASS_BIT) ++ #define AIC_ACCR2_OASS_20BIT (0 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 20-bit */ ++ #define AIC_ACCR2_OASS_18BIT (1 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 18-bit */ ++ #define AIC_ACCR2_OASS_16BIT (2 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 16-bit */ ++ #define AIC_ACCR2_OASS_8BIT (3 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 8-bit */ ++#define AIC_ACCR2_IASS_BIT 6 /* Output Sample Size for AC-link */ ++#define AIC_ACCR2_IASS_MASK (0x3 << AIC_ACCR2_IASS_BIT) ++ #define AIC_ACCR2_IASS_20BIT (0 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 20-bit */ ++ #define AIC_ACCR2_IASS_18BIT (1 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 18-bit */ ++ #define AIC_ACCR2_IASS_16BIT (2 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 16-bit */ ++ #define AIC_ACCR2_IASS_8BIT (3 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 8-bit */ ++#define AIC_ACCR2_SO (1 << 3) /* SDATA_OUT output value */ ++#define AIC_ACCR2_SR (1 << 2) /* RESET# pin level */ ++#define AIC_ACCR2_SS (1 << 1) /* SYNC pin level */ ++#define AIC_ACCR2_SA (1 << 0) /* SYNC and SDATA_OUT alternation */ ++ ++/* AIC Controller I2S/MSB-justified Control Register (AIC_I2SCR) */ ++ ++#define AIC_I2SCR_STPBK (1 << 12) /* Stop BIT_CLK for I2S/MSB-justified */ ++#define AIC_I2SCR_WL_BIT 1 /* Input/Output Sample Size for I2S/MSB-justified */ ++#define AIC_I2SCR_WL_MASK (0x7 << AIC_I2SCR_WL_BIT) ++ #define AIC_I2SCR_WL_24BIT (0 << AIC_I2SCR_WL_BIT) /* Word Length is 24 bit */ ++ #define AIC_I2SCR_WL_20BIT (1 << AIC_I2SCR_WL_BIT) /* Word Length is 20 bit */ ++ #define AIC_I2SCR_WL_18BIT (2 << AIC_I2SCR_WL_BIT) /* Word Length is 18 bit */ ++ #define AIC_I2SCR_WL_16BIT (3 << AIC_I2SCR_WL_BIT) /* Word Length is 16 bit */ ++ #define AIC_I2SCR_WL_8BIT (4 << AIC_I2SCR_WL_BIT) /* Word Length is 8 bit */ ++#define AIC_I2SCR_AMSL (1 << 0) /* 0:I2S, 1:MSB-justified */ ++ ++/* AIC Controller FIFO Status Register (AIC_SR) */ ++ ++#define AIC_SR_RFL_BIT 24 /* Receive FIFO Level */ ++#define AIC_SR_RFL_MASK (0x3f << AIC_SR_RFL_BIT) ++#define AIC_SR_TFL_BIT 8 /* Transmit FIFO level */ ++#define AIC_SR_TFL_MASK (0x3f << AIC_SR_TFL_BIT) ++#define AIC_SR_ROR (1 << 6) /* Receive FIFO Overrun */ ++#define AIC_SR_TUR (1 << 5) /* Transmit FIFO Underrun */ ++#define AIC_SR_RFS (1 << 4) /* Receive FIFO Service Request */ ++#define AIC_SR_TFS (1 << 3) /* Transmit FIFO Service Request */ ++ ++/* AIC Controller AC-link Status Register (AIC_ACSR) */ ++ ++#define AIC_ACSR_SLTERR (1 << 21) /* Slot Error Flag */ ++#define AIC_ACSR_CRDY (1 << 20) /* External CODEC Ready Flag */ ++#define AIC_ACSR_CLPM (1 << 19) /* External CODEC low power mode flag */ ++#define AIC_ACSR_RSTO (1 << 18) /* External CODEC regs read status timeout */ ++#define AIC_ACSR_SADR (1 << 17) /* External CODEC regs status addr and data received */ ++#define AIC_ACSR_CADT (1 << 16) /* Command Address and Data Transmitted */ ++ ++/* AIC Controller I2S/MSB-justified Status Register (AIC_I2SSR) */ ++ ++#define AIC_I2SSR_BSY (1 << 2) /* AIC Busy in I2S/MSB-justified format */ ++ ++/* AIC Controller AC97 codec Command Address Register (AIC_ACCAR) */ ++ ++#define AIC_ACCAR_CAR_BIT 0 ++#define AIC_ACCAR_CAR_MASK (0xfffff << AIC_ACCAR_CAR_BIT) ++ ++/* AIC Controller AC97 codec Command Data Register (AIC_ACCDR) */ ++ ++#define AIC_ACCDR_CDR_BIT 0 ++#define AIC_ACCDR_CDR_MASK (0xfffff << AIC_ACCDR_CDR_BIT) ++ ++/* AIC Controller AC97 codec Status Address Register (AIC_ACSAR) */ ++ ++#define AIC_ACSAR_SAR_BIT 0 ++#define AIC_ACSAR_SAR_MASK (0xfffff << AIC_ACSAR_SAR_BIT) ++ ++/* AIC Controller AC97 codec Status Data Register (AIC_ACSDR) */ ++ ++#define AIC_ACSDR_SDR_BIT 0 ++#define AIC_ACSDR_SDR_MASK (0xfffff << AIC_ACSDR_SDR_BIT) ++ ++/* AIC Controller I2S/MSB-justified Clock Divider Register (AIC_I2SDIV) */ ++ ++#define AIC_I2SDIV_DIV_BIT 0 ++#define AIC_I2SDIV_DIV_MASK (0x7f << AIC_I2SDIV_DIV_BIT) ++ #define AIC_I2SDIV_BITCLK_3072KHZ (0x0C << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 3.072MHz */ ++ #define AIC_I2SDIV_BITCLK_2836KHZ (0x0D << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 2.836MHz */ ++ #define AIC_I2SDIV_BITCLK_1418KHZ (0x1A << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.418MHz */ ++ #define AIC_I2SDIV_BITCLK_1024KHZ (0x24 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.024MHz */ ++ #define AIC_I2SDIV_BITCLK_7089KHZ (0x34 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 708.92KHz */ ++ #define AIC_I2SDIV_BITCLK_512KHZ (0x48 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 512.00KHz */ ++ ++ ++/************************************************************************* ++ * ICDC (Internal CODEC) ++ *************************************************************************/ ++#define ICDC_CR (ICDC_BASE + 0x0400) /* ICDC Control Register */ ++#define ICDC_APWAIT (ICDC_BASE + 0x0404) /* Anti-Pop WAIT Stage Timing Control Register */ ++#define ICDC_APPRE (ICDC_BASE + 0x0408) /* Anti-Pop HPEN-PRE Stage Timing Control Register */ ++#define ICDC_APHPEN (ICDC_BASE + 0x040C) /* Anti-Pop HPEN Stage Timing Control Register */ ++#define ICDC_APSR (ICDC_BASE + 0x0410) /* Anti-Pop Status Register */ ++#define ICDC_CDCCR1 (ICDC_BASE + 0x0080) ++#define ICDC_CDCCR2 (ICDC_BASE + 0x0084) ++ ++#define REG_ICDC_CR REG32(ICDC_CR) ++#define REG_ICDC_APWAIT REG32(ICDC_APWAIT) ++#define REG_ICDC_APPRE REG32(ICDC_APPRE) ++#define REG_ICDC_APHPEN REG32(ICDC_APHPEN) ++#define REG_ICDC_APSR REG32(ICDC_APSR) ++#define REG_ICDC_CDCCR1 REG32(ICDC_CDCCR1) ++#define REG_ICDC_CDCCR2 REG32(ICDC_CDCCR2) ++ ++/* ICDC Control Register */ ++#define ICDC_CR_LINVOL_BIT 24 /* LINE Input Volume Gain: GAIN=LINVOL*1.5-34.5 */ ++#define ICDC_CR_LINVOL_MASK (0x1f << ICDC_CR_LINVOL_BIT) ++#define ICDC_CR_ASRATE_BIT 20 /* Audio Sample Rate */ ++#define ICDC_CR_ASRATE_MASK (0x0f << ICDC_CR_ASRATE_BIT) ++ #define ICDC_CR_ASRATE_8000 (0x0 << ICDC_CR_ASRATE_BIT) ++ #define ICDC_CR_ASRATE_11025 (0x1 << ICDC_CR_ASRATE_BIT) ++ #define ICDC_CR_ASRATE_12000 (0x2 << ICDC_CR_ASRATE_BIT) ++ #define ICDC_CR_ASRATE_16000 (0x3 << ICDC_CR_ASRATE_BIT) ++ #define ICDC_CR_ASRATE_22050 (0x4 << ICDC_CR_ASRATE_BIT) ++ #define ICDC_CR_ASRATE_24000 (0x5 << ICDC_CR_ASRATE_BIT) ++ #define ICDC_CR_ASRATE_32000 (0x6 << ICDC_CR_ASRATE_BIT) ++ #define ICDC_CR_ASRATE_44100 (0x7 << ICDC_CR_ASRATE_BIT) ++ #define ICDC_CR_ASRATE_48000 (0x8 << ICDC_CR_ASRATE_BIT) ++#define ICDC_CR_MICBG_BIT 18 /* MIC Boost Gain */ ++#define ICDC_CR_MICBG_MASK (0x3 << ICDC_CR_MICBG_BIT) ++ #define ICDC_CR_MICBG_0DB (0x0 << ICDC_CR_MICBG_BIT) ++ #define ICDC_CR_MICBG_6DB (0x1 << ICDC_CR_MICBG_BIT) ++ #define ICDC_CR_MICBG_12DB (0x2 << ICDC_CR_MICBG_BIT) ++ #define ICDC_CR_MICBG_20DB (0x3 << ICDC_CR_MICBG_BIT) ++#define ICDC_CR_HPVOL_BIT 16 /* Headphone Volume Gain */ ++#define ICDC_CR_HPVOL_MASK (0x3 << ICDC_CR_HPVOL_BIT) ++ #define ICDC_CR_HPVOL_0DB (0x0 << ICDC_CR_HPVOL_BIT) ++ #define ICDC_CR_HPVOL_2DB (0x1 << ICDC_CR_HPVOL_BIT) ++ #define ICDC_CR_HPVOL_4DB (0x2 << ICDC_CR_HPVOL_BIT) ++ #define ICDC_CR_HPVOL_6DB (0x3 << ICDC_CR_HPVOL_BIT) ++#define ICDC_CR_ELINEIN (1 << 13) /* Enable LINE Input */ ++#define ICDC_CR_EMIC (1 << 12) /* Enable MIC Input */ ++#define ICDC_CR_SW1ON (1 << 11) /* Switch 1 in CODEC is on */ ++#define ICDC_CR_EADC (1 << 10) /* Enable ADC */ ++#define ICDC_CR_SW2ON (1 << 9) /* Switch 2 in CODEC is on */ ++#define ICDC_CR_EDAC (1 << 8) /* Enable DAC */ ++#define ICDC_CR_HPMUTE (1 << 5) /* Headphone Mute */ ++#define ICDC_CR_HPTON (1 << 4) /* Headphone Amplifier Trun On */ ++#define ICDC_CR_HPTOFF (1 << 3) /* Headphone Amplifier Trun Off */ ++#define ICDC_CR_TAAP (1 << 2) /* Turn Around of the Anti-Pop Procedure */ ++#define ICDC_CR_EAP (1 << 1) /* Enable Anti-Pop Procedure */ ++#define ICDC_CR_SUSPD (1 << 0) /* CODEC Suspend */ ++ ++/* Anti-Pop WAIT Stage Timing Control Register */ ++#define ICDC_APWAIT_WAITSN_BIT 0 ++#define ICDC_APWAIT_WAITSN_MASK (0x7ff << ICDC_APWAIT_WAITSN_BIT) ++ ++/* Anti-Pop HPEN-PRE Stage Timing Control Register */ ++#define ICDC_APPRE_PRESN_BIT 0 ++#define ICDC_APPRE_PRESN_MASK (0x1ff << ICDC_APPRE_PRESN_BIT) ++ ++/* Anti-Pop HPEN Stage Timing Control Register */ ++#define ICDC_APHPEN_HPENSN_BIT 0 ++#define ICDC_APHPEN_HPENSN_MASK (0x3fff << ICDC_APHPEN_HPENSN_BIT) ++ ++/* Anti-Pop Status Register */ ++#define ICDC_SR_HPST_BIT 14 /* Headphone Amplifier State */ ++#define ICDC_SR_HPST_MASK (0x7 << ICDC_SR_HPST_BIT) ++#define ICDC_SR_HPST_HP_OFF (0x0 << ICDC_SR_HPST_BIT) /* HP amplifier is off */ ++#define ICDC_SR_HPST_TON_WAIT (0x1 << ICDC_SR_HPST_BIT) /* wait state in turn-on */ ++ #define ICDC_SR_HPST_TON_PRE (0x2 << ICDC_SR_HPST_BIT) /* pre-enable state in turn-on */ ++#define ICDC_SR_HPST_TON_HPEN (0x3 << ICDC_SR_HPST_BIT) /* HP enable state in turn-on */ ++ #define ICDC_SR_HPST_TOFF_HPEN (0x4 << ICDC_SR_HPST_BIT) /* HP enable state in turn-off */ ++ #define ICDC_SR_HPST_TOFF_PRE (0x5 << ICDC_SR_HPST_BIT) /* pre-enable state in turn-off */ ++ #define ICDC_SR_HPST_TOFF_WAIT (0x6 << ICDC_SR_HPST_BIT) /* wait state in turn-off */ ++ #define ICDC_SR_HPST_HP_ON (0x7 << ICDC_SR_HPST_BIT) /* HP amplifier is on */ ++#define ICDC_SR_SNCNT_BIT 0 /* Sample Number Counter */ ++#define ICDC_SR_SNCNT_MASK (0x3fff << ICDC_SR_SNCNT_BIT) ++ ++ ++/************************************************************************* ++ * I2C ++ *************************************************************************/ ++#define I2C_DR (I2C_BASE + 0x000) ++#define I2C_CR (I2C_BASE + 0x004) ++#define I2C_SR (I2C_BASE + 0x008) ++#define I2C_GR (I2C_BASE + 0x00C) ++ ++#define REG_I2C_DR REG8(I2C_DR) ++#define REG_I2C_CR REG8(I2C_CR) ++#define REG_I2C_SR REG8(I2C_SR) ++#define REG_I2C_GR REG16(I2C_GR) ++ ++/* I2C Control Register (I2C_CR) */ ++ ++#define I2C_CR_IEN (1 << 4) ++#define I2C_CR_STA (1 << 3) ++#define I2C_CR_STO (1 << 2) ++#define I2C_CR_AC (1 << 1) ++#define I2C_CR_I2CE (1 << 0) ++ ++/* I2C Status Register (I2C_SR) */ ++ ++#define I2C_SR_STX (1 << 4) ++#define I2C_SR_BUSY (1 << 3) ++#define I2C_SR_TEND (1 << 2) ++#define I2C_SR_DRF (1 << 1) ++#define I2C_SR_ACKF (1 << 0) ++ ++ ++/************************************************************************* ++ * SSI ++ *************************************************************************/ ++#define SSI_DR (SSI_BASE + 0x000) ++#define SSI_CR0 (SSI_BASE + 0x004) ++#define SSI_CR1 (SSI_BASE + 0x008) ++#define SSI_SR (SSI_BASE + 0x00C) ++#define SSI_ITR (SSI_BASE + 0x010) ++#define SSI_ICR (SSI_BASE + 0x014) ++#define SSI_GR (SSI_BASE + 0x018) ++ ++#define REG_SSI_DR REG32(SSI_DR) ++#define REG_SSI_CR0 REG16(SSI_CR0) ++#define REG_SSI_CR1 REG32(SSI_CR1) ++#define REG_SSI_SR REG32(SSI_SR) ++#define REG_SSI_ITR REG16(SSI_ITR) ++#define REG_SSI_ICR REG8(SSI_ICR) ++#define REG_SSI_GR REG16(SSI_GR) ++ ++/* SSI Data Register (SSI_DR) */ ++ ++#define SSI_DR_GPC_BIT 0 ++#define SSI_DR_GPC_MASK (0x1ff << SSI_DR_GPC_BIT) ++ ++/* SSI Control Register 0 (SSI_CR0) */ ++ ++#define SSI_CR0_SSIE (1 << 15) ++#define SSI_CR0_TIE (1 << 14) ++#define SSI_CR0_RIE (1 << 13) ++#define SSI_CR0_TEIE (1 << 12) ++#define SSI_CR0_REIE (1 << 11) ++#define SSI_CR0_LOOP (1 << 10) ++#define SSI_CR0_RFINE (1 << 9) ++#define SSI_CR0_RFINC (1 << 8) ++#define SSI_CR0_FSEL (1 << 6) ++#define SSI_CR0_TFLUSH (1 << 2) ++#define SSI_CR0_RFLUSH (1 << 1) ++#define SSI_CR0_DISREV (1 << 0) ++ ++/* SSI Control Register 1 (SSI_CR1) */ ++ ++#define SSI_CR1_FRMHL_BIT 30 ++#define SSI_CR1_FRMHL_MASK (0x3 << SSI_CR1_FRMHL_BIT) ++ #define SSI_CR1_FRMHL_CELOW_CE2LOW (0 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is low valid */ ++ #define SSI_CR1_FRMHL_CEHIGH_CE2LOW (1 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is low valid */ ++ #define SSI_CR1_FRMHL_CELOW_CE2HIGH (2 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is high valid */ ++ #define SSI_CR1_FRMHL_CEHIGH_CE2HIGH (3 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is high valid */ ++#define SSI_CR1_TFVCK_BIT 28 ++#define SSI_CR1_TFVCK_MASK (0x3 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_0 (0 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_1 (1 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_2 (2 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_3 (3 << SSI_CR1_TFVCK_BIT) ++#define SSI_CR1_TCKFI_BIT 26 ++#define SSI_CR1_TCKFI_MASK (0x3 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_0 (0 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_1 (1 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_2 (2 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_3 (3 << SSI_CR1_TCKFI_BIT) ++#define SSI_CR1_LFST (1 << 25) ++#define SSI_CR1_ITFRM (1 << 24) ++#define SSI_CR1_UNFIN (1 << 23) ++#define SSI_CR1_MULTS (1 << 22) ++#define SSI_CR1_FMAT_BIT 20 ++#define SSI_CR1_FMAT_MASK (0x3 << SSI_CR1_FMAT_BIT) ++ #define SSI_CR1_FMAT_SPI (0 << SSI_CR1_FMAT_BIT) /* Motorola¡¯s SPI format */ ++ #define SSI_CR1_FMAT_SSP (1 << SSI_CR1_FMAT_BIT) /* TI's SSP format */ ++ #define SSI_CR1_FMAT_MW1 (2 << SSI_CR1_FMAT_BIT) /* National Microwire 1 format */ ++ #define SSI_CR1_FMAT_MW2 (3 << SSI_CR1_FMAT_BIT) /* National Microwire 2 format */ ++#define SSI_CR1_TTRG_BIT 16 ++#define SSI_CR1_TTRG_MASK (0xf << SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_1 (0 << SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_8 (1 << SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_16 (2 << SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_24 (3 << SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_32 (4 << SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_40 (5 << SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_48 (6 << SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_56 (7 << SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_64 (8 << SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_72 (9 << SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_80 (10<< SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_88 (11<< SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_96 (12<< SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_104 (13<< SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_112 (14<< SSI_CR1_TTRG_BIT) ++ #define SSI_CR1_TTRG_120 (15<< SSI_CR1_TTRG_BIT) ++#define SSI_CR1_MCOM_BIT 12 ++#define SSI_CR1_MCOM_MASK (0xf << SSI_CR1_MCOM_BIT) ++ #define SSI_CR1_MCOM_1BIT (0x0 << SSI_CR1_MCOM_BIT) /* 1-bit command selected */ ++ #define SSI_CR1_MCOM_2BIT (0x1 << SSI_CR1_MCOM_BIT) /* 2-bit command selected */ ++ #define SSI_CR1_MCOM_3BIT (0x2 << SSI_CR1_MCOM_BIT) /* 3-bit command selected */ ++ #define SSI_CR1_MCOM_4BIT (0x3 << SSI_CR1_MCOM_BIT) /* 4-bit command selected */ ++ #define SSI_CR1_MCOM_5BIT (0x4 << SSI_CR1_MCOM_BIT) /* 5-bit command selected */ ++ #define SSI_CR1_MCOM_6BIT (0x5 << SSI_CR1_MCOM_BIT) /* 6-bit command selected */ ++ #define SSI_CR1_MCOM_7BIT (0x6 << SSI_CR1_MCOM_BIT) /* 7-bit command selected */ ++ #define SSI_CR1_MCOM_8BIT (0x7 << SSI_CR1_MCOM_BIT) /* 8-bit command selected */ ++ #define SSI_CR1_MCOM_9BIT (0x8 << SSI_CR1_MCOM_BIT) /* 9-bit command selected */ ++ #define SSI_CR1_MCOM_10BIT (0x9 << SSI_CR1_MCOM_BIT) /* 10-bit command selected */ ++ #define SSI_CR1_MCOM_11BIT (0xA << SSI_CR1_MCOM_BIT) /* 11-bit command selected */ ++ #define SSI_CR1_MCOM_12BIT (0xB << SSI_CR1_MCOM_BIT) /* 12-bit command selected */ ++ #define SSI_CR1_MCOM_13BIT (0xC << SSI_CR1_MCOM_BIT) /* 13-bit command selected */ ++ #define SSI_CR1_MCOM_14BIT (0xD << SSI_CR1_MCOM_BIT) /* 14-bit command selected */ ++ #define SSI_CR1_MCOM_15BIT (0xE << SSI_CR1_MCOM_BIT) /* 15-bit command selected */ ++ #define SSI_CR1_MCOM_16BIT (0xF << SSI_CR1_MCOM_BIT) /* 16-bit command selected */ ++#define SSI_CR1_RTRG_BIT 8 ++#define SSI_CR1_RTRG_MASK (0xf << SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_1 (0 << SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_8 (1 << SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_16 (2 << SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_24 (3 << SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_32 (4 << SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_40 (5 << SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_48 (6 << SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_56 (7 << SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_64 (8 << SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_72 (9 << SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_80 (10<< SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_88 (11<< SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_96 (12<< SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_104 (13<< SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_112 (14<< SSI_CR1_RTRG_BIT) ++ #define SSI_CR1_RTRG_120 (15<< SSI_CR1_RTRG_BIT) ++#define SSI_CR1_FLEN_BIT 4 ++#define SSI_CR1_FLEN_MASK (0xf << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_2BIT (0x0 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_3BIT (0x1 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_4BIT (0x2 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_5BIT (0x3 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_6BIT (0x4 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_7BIT (0x5 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_8BIT (0x6 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_9BIT (0x7 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_10BIT (0x8 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_11BIT (0x9 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_12BIT (0xA << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_13BIT (0xB << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_14BIT (0xC << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_15BIT (0xD << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_16BIT (0xE << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_17BIT (0xF << SSI_CR1_FLEN_BIT) ++#define SSI_CR1_PHA (1 << 1) ++#define SSI_CR1_POL (1 << 0) ++ ++/* SSI Status Register (SSI_SR) */ ++ ++#define SSI_SR_TFIFONUM_BIT 16 ++#define SSI_SR_TFIFONUM_MASK (0xff << SSI_SR_TFIFONUM_BIT) ++#define SSI_SR_RFIFONUM_BIT 8 ++#define SSI_SR_RFIFONUM_MASK (0xff << SSI_SR_RFIFONUM_BIT) ++#define SSI_SR_END (1 << 7) ++#define SSI_SR_BUSY (1 << 6) ++#define SSI_SR_TFF (1 << 5) ++#define SSI_SR_RFE (1 << 4) ++#define SSI_SR_TFHE (1 << 3) ++#define SSI_SR_RFHF (1 << 2) ++#define SSI_SR_UNDR (1 << 1) ++#define SSI_SR_OVER (1 << 0) ++ ++/* SSI Interval Time Control Register (SSI_ITR) */ ++ ++#define SSI_ITR_CNTCLK (1 << 15) ++#define SSI_ITR_IVLTM_BIT 0 ++#define SSI_ITR_IVLTM_MASK (0x7fff << SSI_ITR_IVLTM_BIT) ++ ++ ++/************************************************************************* ++ * MSC ++ *************************************************************************/ ++#define MSC_STRPCL (MSC_BASE + 0x000) ++#define MSC_STAT (MSC_BASE + 0x004) ++#define MSC_CLKRT (MSC_BASE + 0x008) ++#define MSC_CMDAT (MSC_BASE + 0x00C) ++#define MSC_RESTO (MSC_BASE + 0x010) ++#define MSC_RDTO (MSC_BASE + 0x014) ++#define MSC_BLKLEN (MSC_BASE + 0x018) ++#define MSC_NOB (MSC_BASE + 0x01C) ++#define MSC_SNOB (MSC_BASE + 0x020) ++#define MSC_IMASK (MSC_BASE + 0x024) ++#define MSC_IREG (MSC_BASE + 0x028) ++#define MSC_CMD (MSC_BASE + 0x02C) ++#define MSC_ARG (MSC_BASE + 0x030) ++#define MSC_RES (MSC_BASE + 0x034) ++#define MSC_RXFIFO (MSC_BASE + 0x038) ++#define MSC_TXFIFO (MSC_BASE + 0x03C) ++ ++#define REG_MSC_STRPCL REG16(MSC_STRPCL) ++#define REG_MSC_STAT REG32(MSC_STAT) ++#define REG_MSC_CLKRT REG16(MSC_CLKRT) ++#define REG_MSC_CMDAT REG32(MSC_CMDAT) ++#define REG_MSC_RESTO REG16(MSC_RESTO) ++#define REG_MSC_RDTO REG16(MSC_RDTO) ++#define REG_MSC_BLKLEN REG16(MSC_BLKLEN) ++#define REG_MSC_NOB REG16(MSC_NOB) ++#define REG_MSC_SNOB REG16(MSC_SNOB) ++#define REG_MSC_IMASK REG16(MSC_IMASK) ++#define REG_MSC_IREG REG16(MSC_IREG) ++#define REG_MSC_CMD REG8(MSC_CMD) ++#define REG_MSC_ARG REG32(MSC_ARG) ++#define REG_MSC_RES REG16(MSC_RES) ++#define REG_MSC_RXFIFO REG32(MSC_RXFIFO) ++#define REG_MSC_TXFIFO REG32(MSC_TXFIFO) ++ ++/* MSC Clock and Control Register (MSC_STRPCL) */ ++ ++#define MSC_STRPCL_EXIT_MULTIPLE (1 << 7) ++#define MSC_STRPCL_EXIT_TRANSFER (1 << 6) ++#define MSC_STRPCL_START_READWAIT (1 << 5) ++#define MSC_STRPCL_STOP_READWAIT (1 << 4) ++#define MSC_STRPCL_RESET (1 << 3) ++#define MSC_STRPCL_START_OP (1 << 2) ++#define MSC_STRPCL_CLOCK_CONTROL_BIT 0 ++#define MSC_STRPCL_CLOCK_CONTROL_MASK (0x3 << MSC_STRPCL_CLOCK_CONTROL_BIT) ++ #define MSC_STRPCL_CLOCK_CONTROL_STOP (0x1 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Stop MMC/SD clock */ ++ #define MSC_STRPCL_CLOCK_CONTROL_START (0x2 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Start MMC/SD clock */ ++ ++/* MSC Status Register (MSC_STAT) */ ++ ++#define MSC_STAT_IS_RESETTING (1 << 15) ++#define MSC_STAT_SDIO_INT_ACTIVE (1 << 14) ++#define MSC_STAT_PRG_DONE (1 << 13) ++#define MSC_STAT_DATA_TRAN_DONE (1 << 12) ++#define MSC_STAT_END_CMD_RES (1 << 11) ++#define MSC_STAT_DATA_FIFO_AFULL (1 << 10) ++#define MSC_STAT_IS_READWAIT (1 << 9) ++#define MSC_STAT_CLK_EN (1 << 8) ++#define MSC_STAT_DATA_FIFO_FULL (1 << 7) ++#define MSC_STAT_DATA_FIFO_EMPTY (1 << 6) ++#define MSC_STAT_CRC_RES_ERR (1 << 5) ++#define MSC_STAT_CRC_READ_ERROR (1 << 4) ++#define MSC_STAT_CRC_WRITE_ERROR_BIT 2 ++#define MSC_STAT_CRC_WRITE_ERROR_MASK (0x3 << MSC_STAT_CRC_WRITE_ERROR_BIT) ++ #define MSC_STAT_CRC_WRITE_ERROR_NO (0 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No error on transmission of data */ ++ #define MSC_STAT_CRC_WRITE_ERROR (1 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* Card observed erroneous transmission of data */ ++ #define MSC_STAT_CRC_WRITE_ERROR_NOSTS (2 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No CRC status is sent back */ ++#define MSC_STAT_TIME_OUT_RES (1 << 1) ++#define MSC_STAT_TIME_OUT_READ (1 << 0) ++ ++/* MSC Bus Clock Control Register (MSC_CLKRT) */ ++ ++#define MSC_CLKRT_CLK_RATE_BIT 0 ++#define MSC_CLKRT_CLK_RATE_MASK (0x7 << MSC_CLKRT_CLK_RATE_BIT) ++ #define MSC_CLKRT_CLK_RATE_DIV_1 (0x0 << MSC_CLKRT_CLK_RATE_BIT) /* CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_2 (0x1 << MSC_CLKRT_CLK_RATE_BIT) /* 1/2 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_4 (0x2 << MSC_CLKRT_CLK_RATE_BIT) /* 1/4 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_8 (0x3 << MSC_CLKRT_CLK_RATE_BIT) /* 1/8 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_16 (0x4 << MSC_CLKRT_CLK_RATE_BIT) /* 1/16 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_32 (0x5 << MSC_CLKRT_CLK_RATE_BIT) /* 1/32 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_64 (0x6 << MSC_CLKRT_CLK_RATE_BIT) /* 1/64 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_128 (0x7 << MSC_CLKRT_CLK_RATE_BIT) /* 1/128 of CLK_SRC */ ++ ++/* MSC Command Sequence Control Register (MSC_CMDAT) */ ++ ++#define MSC_CMDAT_IO_ABORT (1 << 11) ++#define MSC_CMDAT_BUS_WIDTH_BIT 9 ++#define MSC_CMDAT_BUS_WIDTH_MASK (0x3 << MSC_CMDAT_BUS_WIDTH_BIT) ++ #define MSC_CMDAT_BUS_WIDTH_1BIT (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) /* 1-bit data bus */ ++ #define MSC_CMDAT_BUS_WIDTH_4BIT (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) /* 4-bit data bus */ ++ #define CMDAT_BUS_WIDTH1 (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) ++ #define CMDAT_BUS_WIDTH4 (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) ++#define MSC_CMDAT_DMA_EN (1 << 8) ++#define MSC_CMDAT_INIT (1 << 7) ++#define MSC_CMDAT_BUSY (1 << 6) ++#define MSC_CMDAT_STREAM_BLOCK (1 << 5) ++#define MSC_CMDAT_WRITE (1 << 4) ++#define MSC_CMDAT_READ (0 << 4) ++#define MSC_CMDAT_DATA_EN (1 << 3) ++#define MSC_CMDAT_RESPONSE_BIT 0 ++#define MSC_CMDAT_RESPONSE_MASK (0x7 << MSC_CMDAT_RESPONSE_BIT) ++ #define MSC_CMDAT_RESPONSE_NONE (0x0 << MSC_CMDAT_RESPONSE_BIT) /* No response */ ++ #define MSC_CMDAT_RESPONSE_R1 (0x1 << MSC_CMDAT_RESPONSE_BIT) /* Format R1 and R1b */ ++ #define MSC_CMDAT_RESPONSE_R2 (0x2 << MSC_CMDAT_RESPONSE_BIT) /* Format R2 */ ++ #define MSC_CMDAT_RESPONSE_R3 (0x3 << MSC_CMDAT_RESPONSE_BIT) /* Format R3 */ ++ #define MSC_CMDAT_RESPONSE_R4 (0x4 << MSC_CMDAT_RESPONSE_BIT) /* Format R4 */ ++ #define MSC_CMDAT_RESPONSE_R5 (0x5 << MSC_CMDAT_RESPONSE_BIT) /* Format R5 */ ++ #define MSC_CMDAT_RESPONSE_R6 (0x6 << MSC_CMDAT_RESPONSE_BIT) /* Format R6 */ ++ ++#define CMDAT_DMA_EN (1 << 8) ++#define CMDAT_INIT (1 << 7) ++#define CMDAT_BUSY (1 << 6) ++#define CMDAT_STREAM (1 << 5) ++#define CMDAT_WRITE (1 << 4) ++#define CMDAT_DATA_EN (1 << 3) ++ ++/* MSC Interrupts Mask Register (MSC_IMASK) */ ++ ++#define MSC_IMASK_SDIO (1 << 7) ++#define MSC_IMASK_TXFIFO_WR_REQ (1 << 6) ++#define MSC_IMASK_RXFIFO_RD_REQ (1 << 5) ++#define MSC_IMASK_END_CMD_RES (1 << 2) ++#define MSC_IMASK_PRG_DONE (1 << 1) ++#define MSC_IMASK_DATA_TRAN_DONE (1 << 0) ++ ++ ++/* MSC Interrupts Status Register (MSC_IREG) */ ++ ++#define MSC_IREG_SDIO (1 << 7) ++#define MSC_IREG_TXFIFO_WR_REQ (1 << 6) ++#define MSC_IREG_RXFIFO_RD_REQ (1 << 5) ++#define MSC_IREG_END_CMD_RES (1 << 2) ++#define MSC_IREG_PRG_DONE (1 << 1) ++#define MSC_IREG_DATA_TRAN_DONE (1 << 0) ++ ++ ++/************************************************************************* ++ * EMC (External Memory Controller) ++ *************************************************************************/ ++#define EMC_SMCR0 (EMC_BASE + 0x10) /* Static Memory Control Register 0 */ ++#define EMC_SMCR1 (EMC_BASE + 0x14) /* Static Memory Control Register 1 */ ++#define EMC_SMCR2 (EMC_BASE + 0x18) /* Static Memory Control Register 2 */ ++#define EMC_SMCR3 (EMC_BASE + 0x1c) /* Static Memory Control Register 3 */ ++#define EMC_SMCR4 (EMC_BASE + 0x20) /* Static Memory Control Register 4 */ ++#define EMC_SACR0 (EMC_BASE + 0x30) /* Static Memory Bank 0 Addr Config Reg */ ++#define EMC_SACR1 (EMC_BASE + 0x34) /* Static Memory Bank 1 Addr Config Reg */ ++#define EMC_SACR2 (EMC_BASE + 0x38) /* Static Memory Bank 2 Addr Config Reg */ ++#define EMC_SACR3 (EMC_BASE + 0x3c) /* Static Memory Bank 3 Addr Config Reg */ ++#define EMC_SACR4 (EMC_BASE + 0x40) /* Static Memory Bank 4 Addr Config Reg */ ++ ++#define EMC_NFCSR (EMC_BASE + 0x050) /* NAND Flash Control/Status Register */ ++#define EMC_NFECR (EMC_BASE + 0x100) /* NAND Flash ECC Control Register */ ++#define EMC_NFECC (EMC_BASE + 0x104) /* NAND Flash ECC Data Register */ ++#define EMC_NFPAR0 (EMC_BASE + 0x108) /* NAND Flash RS Parity 0 Register */ ++#define EMC_NFPAR1 (EMC_BASE + 0x10c) /* NAND Flash RS Parity 1 Register */ ++#define EMC_NFPAR2 (EMC_BASE + 0x110) /* NAND Flash RS Parity 2 Register */ ++#define EMC_NFINTS (EMC_BASE + 0x114) /* NAND Flash Interrupt Status Register */ ++#define EMC_NFINTE (EMC_BASE + 0x118) /* NAND Flash Interrupt Enable Register */ ++#define EMC_NFERR0 (EMC_BASE + 0x11c) /* NAND Flash RS Error Report 0 Register */ ++#define EMC_NFERR1 (EMC_BASE + 0x120) /* NAND Flash RS Error Report 1 Register */ ++#define EMC_NFERR2 (EMC_BASE + 0x124) /* NAND Flash RS Error Report 2 Register */ ++#define EMC_NFERR3 (EMC_BASE + 0x128) /* NAND Flash RS Error Report 3 Register */ ++ ++#define EMC_DMCR (EMC_BASE + 0x80) /* DRAM Control Register */ ++#define EMC_RTCSR (EMC_BASE + 0x84) /* Refresh Time Control/Status Register */ ++#define EMC_RTCNT (EMC_BASE + 0x88) /* Refresh Timer Counter */ ++#define EMC_RTCOR (EMC_BASE + 0x8c) /* Refresh Time Constant Register */ ++#define EMC_DMAR0 (EMC_BASE + 0x90) /* SDRAM Bank 0 Addr Config Register */ ++#define EMC_SDMR0 (EMC_BASE + 0xa000) /* Mode Register of SDRAM bank 0 */ ++ ++ ++#define REG_EMC_SMCR0 REG32(EMC_SMCR0) ++#define REG_EMC_SMCR1 REG32(EMC_SMCR1) ++#define REG_EMC_SMCR2 REG32(EMC_SMCR2) ++#define REG_EMC_SMCR3 REG32(EMC_SMCR3) ++#define REG_EMC_SMCR4 REG32(EMC_SMCR4) ++#define REG_EMC_SACR0 REG32(EMC_SACR0) ++#define REG_EMC_SACR1 REG32(EMC_SACR1) ++#define REG_EMC_SACR2 REG32(EMC_SACR2) ++#define REG_EMC_SACR3 REG32(EMC_SACR3) ++#define REG_EMC_SACR4 REG32(EMC_SACR4) ++ ++#define REG_EMC_NFCSR REG32(EMC_NFCSR) ++#define REG_EMC_NFECR REG32(EMC_NFECR) ++#define REG_EMC_NFECC REG32(EMC_NFECC) ++#define REG_EMC_NFPAR0 REG32(EMC_NFPAR0) ++#define REG_EMC_NFPAR1 REG32(EMC_NFPAR1) ++#define REG_EMC_NFPAR2 REG32(EMC_NFPAR2) ++#define REG_EMC_NFINTS REG32(EMC_NFINTS) ++#define REG_EMC_NFINTE REG32(EMC_NFINTE) ++#define REG_EMC_NFERR0 REG32(EMC_NFERR0) ++#define REG_EMC_NFERR1 REG32(EMC_NFERR1) ++#define REG_EMC_NFERR2 REG32(EMC_NFERR2) ++#define REG_EMC_NFERR3 REG32(EMC_NFERR3) ++ ++#define REG_EMC_DMCR REG32(EMC_DMCR) ++#define REG_EMC_RTCSR REG16(EMC_RTCSR) ++#define REG_EMC_RTCNT REG16(EMC_RTCNT) ++#define REG_EMC_RTCOR REG16(EMC_RTCOR) ++#define REG_EMC_DMAR0 REG32(EMC_DMAR0) ++ ++/* Static Memory Control Register */ ++#define EMC_SMCR_STRV_BIT 24 ++#define EMC_SMCR_STRV_MASK (0x0f << EMC_SMCR_STRV_BIT) ++#define EMC_SMCR_TAW_BIT 20 ++#define EMC_SMCR_TAW_MASK (0x0f << EMC_SMCR_TAW_BIT) ++#define EMC_SMCR_TBP_BIT 16 ++#define EMC_SMCR_TBP_MASK (0x0f << EMC_SMCR_TBP_BIT) ++#define EMC_SMCR_TAH_BIT 12 ++#define EMC_SMCR_TAH_MASK (0x07 << EMC_SMCR_TAH_BIT) ++#define EMC_SMCR_TAS_BIT 8 ++#define EMC_SMCR_TAS_MASK (0x07 << EMC_SMCR_TAS_BIT) ++#define EMC_SMCR_BW_BIT 6 ++#define EMC_SMCR_BW_MASK (0x03 << EMC_SMCR_BW_BIT) ++ #define EMC_SMCR_BW_8BIT (0 << EMC_SMCR_BW_BIT) ++ #define EMC_SMCR_BW_16BIT (1 << EMC_SMCR_BW_BIT) ++ #define EMC_SMCR_BW_32BIT (2 << EMC_SMCR_BW_BIT) ++#define EMC_SMCR_BCM (1 << 3) ++#define EMC_SMCR_BL_BIT 1 ++#define EMC_SMCR_BL_MASK (0x03 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_4 (0 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_8 (1 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_16 (2 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_32 (3 << EMC_SMCR_BL_BIT) ++#define EMC_SMCR_SMT (1 << 0) ++ ++/* Static Memory Bank Addr Config Reg */ ++#define EMC_SACR_BASE_BIT 8 ++#define EMC_SACR_BASE_MASK (0xff << EMC_SACR_BASE_BIT) ++#define EMC_SACR_MASK_BIT 0 ++#define EMC_SACR_MASK_MASK (0xff << EMC_SACR_MASK_BIT) ++ ++/* NAND Flash Control/Status Register */ ++#define EMC_NFCSR_NFCE4 (1 << 7) /* NAND Flash Enable */ ++#define EMC_NFCSR_NFE4 (1 << 6) /* NAND Flash FCE# Assertion Enable */ ++#define EMC_NFCSR_NFCE3 (1 << 5) ++#define EMC_NFCSR_NFE3 (1 << 4) ++#define EMC_NFCSR_NFCE2 (1 << 3) ++#define EMC_NFCSR_NFE2 (1 << 2) ++#define EMC_NFCSR_NFCE1 (1 << 1) ++#define EMC_NFCSR_NFE1 (1 << 0) ++ ++/* NAND Flash ECC Control Register */ ++#define EMC_NFECR_PRDY (1 << 4) /* Parity Ready */ ++#define EMC_NFECR_RS_DECODING (0 << 3) /* RS is in decoding phase */ ++#define EMC_NFECR_RS_ENCODING (1 << 3) /* RS is in encoding phase */ ++#define EMC_NFECR_HAMMING (0 << 2) /* Select HAMMING Correction Algorithm */ ++#define EMC_NFECR_RS (1 << 2) /* Select RS Correction Algorithm */ ++#define EMC_NFECR_ERST (1 << 1) /* ECC Reset */ ++#define EMC_NFECR_ECCE (1 << 0) /* ECC Enable */ ++ ++/* NAND Flash ECC Data Register */ ++#define EMC_NFECC_ECC2_BIT 16 ++#define EMC_NFECC_ECC2_MASK (0xff << EMC_NFECC_ECC2_BIT) ++#define EMC_NFECC_ECC1_BIT 8 ++#define EMC_NFECC_ECC1_MASK (0xff << EMC_NFECC_ECC1_BIT) ++#define EMC_NFECC_ECC0_BIT 0 ++#define EMC_NFECC_ECC0_MASK (0xff << EMC_NFECC_ECC0_BIT) ++ ++/* NAND Flash Interrupt Status Register */ ++#define EMC_NFINTS_ERRCNT_BIT 29 /* Error Count */ ++#define EMC_NFINTS_ERRCNT_MASK (0x7 << EMC_NFINTS_ERRCNT_BIT) ++#define EMC_NFINTS_PADF (1 << 4) /* Padding Finished */ ++#define EMC_NFINTS_DECF (1 << 3) /* Decoding Finished */ ++#define EMC_NFINTS_ENCF (1 << 2) /* Encoding Finished */ ++#define EMC_NFINTS_UNCOR (1 << 1) /* Uncorrectable Error Occurred */ ++#define EMC_NFINTS_ERR (1 << 0) /* Error Occurred */ ++ ++/* NAND Flash Interrupt Enable Register */ ++#define EMC_NFINTE_PADFE (1 << 4) /* Padding Finished Interrupt Enable */ ++#define EMC_NFINTE_DECFE (1 << 3) /* Decoding Finished Interrupt Enable */ ++#define EMC_NFINTE_ENCFE (1 << 2) /* Encoding Finished Interrupt Enable */ ++#define EMC_NFINTE_UNCORE (1 << 1) /* Uncorrectable Error Occurred Intr Enable */ ++#define EMC_NFINTE_ERRE (1 << 0) /* Error Occurred Interrupt */ ++ ++/* NAND Flash RS Error Report Register */ ++#define EMC_NFERR_INDEX_BIT 16 /* Error Symbol Index */ ++#define EMC_NFERR_INDEX_MASK (0x1ff << EMC_NFERR_INDEX_BIT) ++#define EMC_NFERR_MASK_BIT 0 /* Error Symbol Value */ ++#define EMC_NFERR_MASK_MASK (0x1ff << EMC_NFERR_MASK_BIT) ++ ++ ++/* DRAM Control Register */ ++#define EMC_DMCR_BW_BIT 31 ++#define EMC_DMCR_BW (1 << EMC_DMCR_BW_BIT) ++#define EMC_DMCR_CA_BIT 26 ++#define EMC_DMCR_CA_MASK (0x07 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_8 (0 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_9 (1 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_10 (2 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_11 (3 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_12 (4 << EMC_DMCR_CA_BIT) ++#define EMC_DMCR_RMODE (1 << 25) ++#define EMC_DMCR_RFSH (1 << 24) ++#define EMC_DMCR_MRSET (1 << 23) ++#define EMC_DMCR_RA_BIT 20 ++#define EMC_DMCR_RA_MASK (0x03 << EMC_DMCR_RA_BIT) ++ #define EMC_DMCR_RA_11 (0 << EMC_DMCR_RA_BIT) ++ #define EMC_DMCR_RA_12 (1 << EMC_DMCR_RA_BIT) ++ #define EMC_DMCR_RA_13 (2 << EMC_DMCR_RA_BIT) ++#define EMC_DMCR_BA_BIT 19 ++#define EMC_DMCR_BA (1 << EMC_DMCR_BA_BIT) ++#define EMC_DMCR_PDM (1 << 18) ++#define EMC_DMCR_EPIN (1 << 17) ++#define EMC_DMCR_TRAS_BIT 13 ++#define EMC_DMCR_TRAS_MASK (0x07 << EMC_DMCR_TRAS_BIT) ++#define EMC_DMCR_RCD_BIT 11 ++#define EMC_DMCR_RCD_MASK (0x03 << EMC_DMCR_RCD_BIT) ++#define EMC_DMCR_TPC_BIT 8 ++#define EMC_DMCR_TPC_MASK (0x07 << EMC_DMCR_TPC_BIT) ++#define EMC_DMCR_TRWL_BIT 5 ++#define EMC_DMCR_TRWL_MASK (0x03 << EMC_DMCR_TRWL_BIT) ++#define EMC_DMCR_TRC_BIT 2 ++#define EMC_DMCR_TRC_MASK (0x07 << EMC_DMCR_TRC_BIT) ++#define EMC_DMCR_TCL_BIT 0 ++#define EMC_DMCR_TCL_MASK (0x03 << EMC_DMCR_TCL_BIT) ++ ++/* Refresh Time Control/Status Register */ ++#define EMC_RTCSR_CMF (1 << 7) ++#define EMC_RTCSR_CKS_BIT 0 ++#define EMC_RTCSR_CKS_MASK (0x07 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_DISABLE (0 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_4 (1 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_16 (2 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_64 (3 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_256 (4 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_1024 (5 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_2048 (6 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_4096 (7 << EMC_RTCSR_CKS_BIT) ++ ++/* SDRAM Bank Address Configuration Register */ ++#define EMC_DMAR_BASE_BIT 8 ++#define EMC_DMAR_BASE_MASK (0xff << EMC_DMAR_BASE_BIT) ++#define EMC_DMAR_MASK_BIT 0 ++#define EMC_DMAR_MASK_MASK (0xff << EMC_DMAR_MASK_BIT) ++ ++/* Mode Register of SDRAM bank 0 */ ++#define EMC_SDMR_BM (1 << 9) /* Write Burst Mode */ ++#define EMC_SDMR_OM_BIT 7 /* Operating Mode */ ++#define EMC_SDMR_OM_MASK (3 << EMC_SDMR_OM_BIT) ++ #define EMC_SDMR_OM_NORMAL (0 << EMC_SDMR_OM_BIT) ++#define EMC_SDMR_CAS_BIT 4 /* CAS Latency */ ++#define EMC_SDMR_CAS_MASK (7 << EMC_SDMR_CAS_BIT) ++ #define EMC_SDMR_CAS_1 (1 << EMC_SDMR_CAS_BIT) ++ #define EMC_SDMR_CAS_2 (2 << EMC_SDMR_CAS_BIT) ++ #define EMC_SDMR_CAS_3 (3 << EMC_SDMR_CAS_BIT) ++#define EMC_SDMR_BT_BIT 3 /* Burst Type */ ++#define EMC_SDMR_BT_MASK (1 << EMC_SDMR_BT_BIT) ++ #define EMC_SDMR_BT_SEQ (0 << EMC_SDMR_BT_BIT) /* Sequential */ ++ #define EMC_SDMR_BT_INT (1 << EMC_SDMR_BT_BIT) /* Interleave */ ++#define EMC_SDMR_BL_BIT 0 /* Burst Length */ ++#define EMC_SDMR_BL_MASK (7 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_1 (0 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_2 (1 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_4 (2 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_8 (3 << EMC_SDMR_BL_BIT) ++ ++#define EMC_SDMR_CAS2_16BIT \ ++ (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) ++#define EMC_SDMR_CAS2_32BIT \ ++ (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) ++#define EMC_SDMR_CAS3_16BIT \ ++ (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) ++#define EMC_SDMR_CAS3_32BIT \ ++ (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) ++ ++ ++/************************************************************************* ++ * CIM ++ *************************************************************************/ ++#define CIM_CFG (CIM_BASE + 0x0000) ++#define CIM_CTRL (CIM_BASE + 0x0004) ++#define CIM_STATE (CIM_BASE + 0x0008) ++#define CIM_IID (CIM_BASE + 0x000C) ++#define CIM_RXFIFO (CIM_BASE + 0x0010) ++#define CIM_DA (CIM_BASE + 0x0020) ++#define CIM_FA (CIM_BASE + 0x0024) ++#define CIM_FID (CIM_BASE + 0x0028) ++#define CIM_CMD (CIM_BASE + 0x002C) ++ ++#define REG_CIM_CFG REG32(CIM_CFG) ++#define REG_CIM_CTRL REG32(CIM_CTRL) ++#define REG_CIM_STATE REG32(CIM_STATE) ++#define REG_CIM_IID REG32(CIM_IID) ++#define REG_CIM_RXFIFO REG32(CIM_RXFIFO) ++#define REG_CIM_DA REG32(CIM_DA) ++#define REG_CIM_FA REG32(CIM_FA) ++#define REG_CIM_FID REG32(CIM_FID) ++#define REG_CIM_CMD REG32(CIM_CMD) ++ ++/* CIM Configuration Register (CIM_CFG) */ ++ ++#define CIM_CFG_INV_DAT (1 << 15) ++#define CIM_CFG_VSP (1 << 14) ++#define CIM_CFG_HSP (1 << 13) ++#define CIM_CFG_PCP (1 << 12) ++#define CIM_CFG_DUMMY_ZERO (1 << 9) ++#define CIM_CFG_EXT_VSYNC (1 << 8) ++#define CIM_CFG_PACK_BIT 4 ++#define CIM_CFG_PACK_MASK (0x7 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_0 (0 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_1 (1 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_2 (2 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_3 (3 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_4 (4 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_5 (5 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_6 (6 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_7 (7 << CIM_CFG_PACK_BIT) ++#define CIM_CFG_DSM_BIT 0 ++#define CIM_CFG_DSM_MASK (0x3 << CIM_CFG_DSM_BIT) ++ #define CIM_CFG_DSM_CPM (0 << CIM_CFG_DSM_BIT) /* CCIR656 Progressive Mode */ ++ #define CIM_CFG_DSM_CIM (1 << CIM_CFG_DSM_BIT) /* CCIR656 Interlace Mode */ ++ #define CIM_CFG_DSM_GCM (2 << CIM_CFG_DSM_BIT) /* Gated Clock Mode */ ++ #define CIM_CFG_DSM_NGCM (3 << CIM_CFG_DSM_BIT) /* Non-Gated Clock Mode */ ++ ++/* CIM Control Register (CIM_CTRL) */ ++ ++#define CIM_CTRL_MCLKDIV_BIT 24 ++#define CIM_CTRL_MCLKDIV_MASK (0xff << CIM_CTRL_MCLKDIV_BIT) ++#define CIM_CTRL_FRC_BIT 16 ++#define CIM_CTRL_FRC_MASK (0xf << CIM_CTRL_FRC_BIT) ++ #define CIM_CTRL_FRC_1 (0x0 << CIM_CTRL_FRC_BIT) /* Sample every frame */ ++ #define CIM_CTRL_FRC_2 (0x1 << CIM_CTRL_FRC_BIT) /* Sample 1/2 frame */ ++ #define CIM_CTRL_FRC_3 (0x2 << CIM_CTRL_FRC_BIT) /* Sample 1/3 frame */ ++ #define CIM_CTRL_FRC_4 (0x3 << CIM_CTRL_FRC_BIT) /* Sample 1/4 frame */ ++ #define CIM_CTRL_FRC_5 (0x4 << CIM_CTRL_FRC_BIT) /* Sample 1/5 frame */ ++ #define CIM_CTRL_FRC_6 (0x5 << CIM_CTRL_FRC_BIT) /* Sample 1/6 frame */ ++ #define CIM_CTRL_FRC_7 (0x6 << CIM_CTRL_FRC_BIT) /* Sample 1/7 frame */ ++ #define CIM_CTRL_FRC_8 (0x7 << CIM_CTRL_FRC_BIT) /* Sample 1/8 frame */ ++ #define CIM_CTRL_FRC_9 (0x8 << CIM_CTRL_FRC_BIT) /* Sample 1/9 frame */ ++ #define CIM_CTRL_FRC_10 (0x9 << CIM_CTRL_FRC_BIT) /* Sample 1/10 frame */ ++ #define CIM_CTRL_FRC_11 (0xA << CIM_CTRL_FRC_BIT) /* Sample 1/11 frame */ ++ #define CIM_CTRL_FRC_12 (0xB << CIM_CTRL_FRC_BIT) /* Sample 1/12 frame */ ++ #define CIM_CTRL_FRC_13 (0xC << CIM_CTRL_FRC_BIT) /* Sample 1/13 frame */ ++ #define CIM_CTRL_FRC_14 (0xD << CIM_CTRL_FRC_BIT) /* Sample 1/14 frame */ ++ #define CIM_CTRL_FRC_15 (0xE << CIM_CTRL_FRC_BIT) /* Sample 1/15 frame */ ++ #define CIM_CTRL_FRC_16 (0xF << CIM_CTRL_FRC_BIT) /* Sample 1/16 frame */ ++#define CIM_CTRL_VDDM (1 << 13) ++#define CIM_CTRL_DMA_SOFM (1 << 12) ++#define CIM_CTRL_DMA_EOFM (1 << 11) ++#define CIM_CTRL_DMA_STOPM (1 << 10) ++#define CIM_CTRL_RXF_TRIGM (1 << 9) ++#define CIM_CTRL_RXF_OFM (1 << 8) ++#define CIM_CTRL_RXF_TRIG_BIT 4 ++#define CIM_CTRL_RXF_TRIG_MASK (0x7 << CIM_CTRL_RXF_TRIG_BIT) ++ #define CIM_CTRL_RXF_TRIG_4 (0 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 4 */ ++ #define CIM_CTRL_RXF_TRIG_8 (1 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 8 */ ++ #define CIM_CTRL_RXF_TRIG_12 (2 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 12 */ ++ #define CIM_CTRL_RXF_TRIG_16 (3 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 16 */ ++ #define CIM_CTRL_RXF_TRIG_20 (4 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 20 */ ++ #define CIM_CTRL_RXF_TRIG_24 (5 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 24 */ ++ #define CIM_CTRL_RXF_TRIG_28 (6 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 28 */ ++ #define CIM_CTRL_RXF_TRIG_32 (7 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 32 */ ++#define CIM_CTRL_DMA_EN (1 << 2) ++#define CIM_CTRL_RXF_RST (1 << 1) ++#define CIM_CTRL_ENA (1 << 0) ++ ++/* CIM State Register (CIM_STATE) */ ++ ++#define CIM_STATE_DMA_SOF (1 << 6) ++#define CIM_STATE_DMA_EOF (1 << 5) ++#define CIM_STATE_DMA_STOP (1 << 4) ++#define CIM_STATE_RXF_OF (1 << 3) ++#define CIM_STATE_RXF_TRIG (1 << 2) ++#define CIM_STATE_RXF_EMPTY (1 << 1) ++#define CIM_STATE_VDD (1 << 0) ++ ++/* CIM DMA Command Register (CIM_CMD) */ ++ ++#define CIM_CMD_SOFINT (1 << 31) ++#define CIM_CMD_EOFINT (1 << 30) ++#define CIM_CMD_STOP (1 << 28) ++#define CIM_CMD_LEN_BIT 0 ++#define CIM_CMD_LEN_MASK (0xffffff << CIM_CMD_LEN_BIT) ++ ++ ++/************************************************************************* ++ * SADC (Smart A/D Controller) ++ *************************************************************************/ ++ ++#define SADC_ENA (SADC_BASE + 0x00) /* ADC Enable Register */ ++#define SADC_CFG (SADC_BASE + 0x04) /* ADC Configure Register */ ++#define SADC_CTRL (SADC_BASE + 0x08) /* ADC Control Register */ ++#define SADC_STATE (SADC_BASE + 0x0C) /* ADC Status Register*/ ++#define SADC_SAMETIME (SADC_BASE + 0x10) /* ADC Same Point Time Register */ ++#define SADC_WAITTIME (SADC_BASE + 0x14) /* ADC Wait Time Register */ ++#define SADC_TSDAT (SADC_BASE + 0x18) /* ADC Touch Screen Data Register */ ++#define SADC_BATDAT (SADC_BASE + 0x1C) /* ADC PBAT Data Register */ ++#define SADC_SADDAT (SADC_BASE + 0x20) /* ADC SADCIN Data Register */ ++ ++#define REG_SADC_ENA REG8(SADC_ENA) ++#define REG_SADC_CFG REG32(SADC_CFG) ++#define REG_SADC_CTRL REG8(SADC_CTRL) ++#define REG_SADC_STATE REG8(SADC_STATE) ++#define REG_SADC_SAMETIME REG16(SADC_SAMETIME) ++#define REG_SADC_WAITTIME REG16(SADC_WAITTIME) ++#define REG_SADC_TSDAT REG32(SADC_TSDAT) ++#define REG_SADC_BATDAT REG16(SADC_BATDAT) ++#define REG_SADC_SADDAT REG16(SADC_SADDAT) ++ ++/* ADC Enable Register */ ++#define SADC_ENA_ADEN (1 << 7) /* Touch Screen Enable */ ++#define SADC_ENA_TSEN (1 << 2) /* Touch Screen Enable */ ++#define SADC_ENA_PBATEN (1 << 1) /* PBAT Enable */ ++#define SADC_ENA_SADCINEN (1 << 0) /* SADCIN Enable */ ++ ++/* ADC Configure Register */ ++#define SADC_CFG_EXIN (1 << 30) ++#define SADC_CFG_CLKOUT_NUM_BIT 16 ++#define SADC_CFG_CLKOUT_NUM_MASK (0x7 << SADC_CFG_CLKOUT_NUM_BIT) ++#define SADC_CFG_TS_DMA (1 << 15) /* Touch Screen DMA Enable */ ++#define SADC_CFG_XYZ_BIT 13 /* XYZ selection */ ++#define SADC_CFG_XYZ_MASK (0x3 << SADC_CFG_XYZ_BIT) ++ #define SADC_CFG_XY (0 << SADC_CFG_XYZ_BIT) ++ #define SADC_CFG_XYZ (1 << SADC_CFG_XYZ_BIT) ++ #define SADC_CFG_XYZ1Z2 (2 << SADC_CFG_XYZ_BIT) ++#define SADC_CFG_SNUM_BIT 10 /* Sample Number */ ++#define SADC_CFG_SNUM_MASK (0x7 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_1 (0x0 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_2 (0x1 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_3 (0x2 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_4 (0x3 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_5 (0x4 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_6 (0x5 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_8 (0x6 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_9 (0x7 << SADC_CFG_SNUM_BIT) ++#define SADC_CFG_CLKDIV_BIT 5 /* AD Converter frequency clock divider */ ++#define SADC_CFG_CLKDIV_MASK (0x1f << SADC_CFG_CLKDIV_BIT) ++#define SADC_CFG_PBAT_HIGH (0 << 4) /* PBAT >= 2.5V */ ++#define SADC_CFG_PBAT_LOW (1 << 4) /* PBAT < 2.5V */ ++#define SADC_CFG_CMD_BIT 0 /* ADC Command */ ++#define SADC_CFG_CMD_MASK (0xf << SADC_CFG_CMD_BIT) ++ #define SADC_CFG_CMD_X_SE (0x0 << SADC_CFG_CMD_BIT) /* X Single-End */ ++ #define SADC_CFG_CMD_Y_SE (0x1 << SADC_CFG_CMD_BIT) /* Y Single-End */ ++ #define SADC_CFG_CMD_X_DIFF (0x2 << SADC_CFG_CMD_BIT) /* X Differential */ ++ #define SADC_CFG_CMD_Y_DIFF (0x3 << SADC_CFG_CMD_BIT) /* Y Differential */ ++ #define SADC_CFG_CMD_Z1_DIFF (0x4 << SADC_CFG_CMD_BIT) /* Z1 Differential */ ++ #define SADC_CFG_CMD_Z2_DIFF (0x5 << SADC_CFG_CMD_BIT) /* Z2 Differential */ ++ #define SADC_CFG_CMD_Z3_DIFF (0x6 << SADC_CFG_CMD_BIT) /* Z3 Differential */ ++ #define SADC_CFG_CMD_Z4_DIFF (0x7 << SADC_CFG_CMD_BIT) /* Z4 Differential */ ++ #define SADC_CFG_CMD_TP_SE (0x8 << SADC_CFG_CMD_BIT) /* Touch Pressure */ ++ #define SADC_CFG_CMD_PBATH_SE (0x9 << SADC_CFG_CMD_BIT) /* PBAT >= 2.5V */ ++ #define SADC_CFG_CMD_PBATL_SE (0xa << SADC_CFG_CMD_BIT) /* PBAT < 2.5V */ ++ #define SADC_CFG_CMD_SADCIN_SE (0xb << SADC_CFG_CMD_BIT) /* Measure SADCIN */ ++ #define SADC_CFG_CMD_INT_PEN (0xc << SADC_CFG_CMD_BIT) /* INT_PEN Enable */ ++ ++/* ADC Control Register */ ++#define SADC_CTRL_PENDM (1 << 4) /* Pen Down Interrupt Mask */ ++#define SADC_CTRL_PENUM (1 << 3) /* Pen Up Interrupt Mask */ ++#define SADC_CTRL_TSRDYM (1 << 2) /* Touch Screen Data Ready Interrupt Mask */ ++#define SADC_CTRL_PBATRDYM (1 << 1) /* PBAT Data Ready Interrupt Mask */ ++#define SADC_CTRL_SRDYM (1 << 0) /* SADCIN Data Ready Interrupt Mask */ ++ ++/* ADC Status Register */ ++#define SADC_STATE_TSBUSY (1 << 7) /* TS A/D is working */ ++#define SADC_STATE_PBATBUSY (1 << 6) /* PBAT A/D is working */ ++#define SADC_STATE_SBUSY (1 << 5) /* SADCIN A/D is working */ ++#define SADC_STATE_PEND (1 << 4) /* Pen Down Interrupt Flag */ ++#define SADC_STATE_PENU (1 << 3) /* Pen Up Interrupt Flag */ ++#define SADC_STATE_TSRDY (1 << 2) /* Touch Screen Data Ready Interrupt Flag */ ++#define SADC_STATE_PBATRDY (1 << 1) /* PBAT Data Ready Interrupt Flag */ ++#define SADC_STATE_SRDY (1 << 0) /* SADCIN Data Ready Interrupt Flag */ ++ ++/* ADC Touch Screen Data Register */ ++#define SADC_TSDAT_DATA0_BIT 0 ++#define SADC_TSDAT_DATA0_MASK (0xfff << SADC_TSDAT_DATA0_BIT) ++#define SADC_TSDAT_TYPE0 (1 << 15) ++#define SADC_TSDAT_DATA1_BIT 16 ++#define SADC_TSDAT_DATA1_MASK (0xfff << SADC_TSDAT_DATA1_BIT) ++#define SADC_TSDAT_TYPE1 (1 << 31) ++ ++ ++/************************************************************************* ++ * SLCD (Smart LCD Controller) ++ *************************************************************************/ ++ ++#define SLCD_CFG (SLCD_BASE + 0xA0) /* SLCD Configure Register */ ++#define SLCD_CTRL (SLCD_BASE + 0xA4) /* SLCD Control Register */ ++#define SLCD_STATE (SLCD_BASE + 0xA8) /* SLCD Status Register */ ++#define SLCD_DATA (SLCD_BASE + 0xAC) /* SLCD Data Register */ ++#define SLCD_FIFO (SLCD_BASE + 0xB0) /* SLCD FIFO Register */ ++ ++#define REG_SLCD_CFG REG32(SLCD_CFG) ++#define REG_SLCD_CTRL REG8(SLCD_CTRL) ++#define REG_SLCD_STATE REG8(SLCD_STATE) ++#define REG_SLCD_DATA REG32(SLCD_DATA) ++#define REG_SLCD_FIFO REG32(SLCD_FIFO) ++ ++/* SLCD Configure Register */ ++#define SLCD_CFG_BURST_BIT 14 ++#define SLCD_CFG_BURST_MASK (0x3 << SLCD_CFG_BURST_BIT) ++ #define SLCD_CFG_BURST_4_WORD (0 << SLCD_CFG_BURST_BIT) ++ #define SLCD_CFG_BURST_8_WORD (1 << SLCD_CFG_BURST_BIT) ++#define SLCD_CFG_DWIDTH_BIT 10 ++#define SLCD_CFG_DWIDTH_MASK (0x7 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_18 (0 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_16 (1 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_8_x3 (2 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_8_x2 (3 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_8_x1 (4 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_9_x2 (7 << SLCD_CFG_DWIDTH_BIT) ++#define SLCD_CFG_CWIDTH_16BIT (0 << 8) ++#define SLCD_CFG_CWIDTH_8BIT (1 << 8) ++#define SLCD_CFG_CWIDTH_18BIT (2 << 8) ++#define SLCD_CFG_CS_ACTIVE_LOW (0 << 4) ++#define SLCD_CFG_CS_ACTIVE_HIGH (1 << 4) ++#define SLCD_CFG_RS_CMD_LOW (0 << 3) ++#define SLCD_CFG_RS_CMD_HIGH (1 << 3) ++#define SLCD_CFG_CLK_ACTIVE_FALLING (0 << 1) ++#define SLCD_CFG_CLK_ACTIVE_RISING (1 << 1) ++#define SLCD_CFG_TYPE_PARALLEL (0 << 0) ++#define SLCD_CFG_TYPE_SERIAL (1 << 0) ++ ++/* SLCD Control Register */ ++#define SLCD_CTRL_DMA_EN (1 << 0) ++ ++/* SLCD Status Register */ ++#define SLCD_STATE_BUSY (1 << 0) ++ ++/* SLCD Data Register */ ++#define SLCD_DATA_RS_DATA (0 << 31) ++#define SLCD_DATA_RS_COMMAND (1 << 31) ++ ++/* SLCD FIFO Register */ ++#define SLCD_FIFO_RS_DATA (0 << 31) ++#define SLCD_FIFO_RS_COMMAND (1 << 31) ++ ++ ++/************************************************************************* ++ * LCD (LCD Controller) ++ *************************************************************************/ ++#define LCD_CFG (LCD_BASE + 0x00) /* LCD Configure Register */ ++#define LCD_VSYNC (LCD_BASE + 0x04) /* Vertical Synchronize Register */ ++#define LCD_HSYNC (LCD_BASE + 0x08) /* Horizontal Synchronize Register */ ++#define LCD_VAT (LCD_BASE + 0x0c) /* Virtual Area Setting Register */ ++#define LCD_DAH (LCD_BASE + 0x10) /* Display Area Horizontal Start/End Point */ ++#define LCD_DAV (LCD_BASE + 0x14) /* Display Area Vertical Start/End Point */ ++#define LCD_PS (LCD_BASE + 0x18) /* PS Signal Setting */ ++#define LCD_CLS (LCD_BASE + 0x1c) /* CLS Signal Setting */ ++#define LCD_SPL (LCD_BASE + 0x20) /* SPL Signal Setting */ ++#define LCD_REV (LCD_BASE + 0x24) /* REV Signal Setting */ ++#define LCD_CTRL (LCD_BASE + 0x30) /* LCD Control Register */ ++#define LCD_STATE (LCD_BASE + 0x34) /* LCD Status Register */ ++#define LCD_IID (LCD_BASE + 0x38) /* Interrupt ID Register */ ++#define LCD_DA0 (LCD_BASE + 0x40) /* Descriptor Address Register 0 */ ++#define LCD_SA0 (LCD_BASE + 0x44) /* Source Address Register 0 */ ++#define LCD_FID0 (LCD_BASE + 0x48) /* Frame ID Register 0 */ ++#define LCD_CMD0 (LCD_BASE + 0x4c) /* DMA Command Register 0 */ ++#define LCD_DA1 (LCD_BASE + 0x50) /* Descriptor Address Register 1 */ ++#define LCD_SA1 (LCD_BASE + 0x54) /* Source Address Register 1 */ ++#define LCD_FID1 (LCD_BASE + 0x58) /* Frame ID Register 1 */ ++#define LCD_CMD1 (LCD_BASE + 0x5c) /* DMA Command Register 1 */ ++ ++#define REG_LCD_CFG REG32(LCD_CFG) ++#define REG_LCD_VSYNC REG32(LCD_VSYNC) ++#define REG_LCD_HSYNC REG32(LCD_HSYNC) ++#define REG_LCD_VAT REG32(LCD_VAT) ++#define REG_LCD_DAH REG32(LCD_DAH) ++#define REG_LCD_DAV REG32(LCD_DAV) ++#define REG_LCD_PS REG32(LCD_PS) ++#define REG_LCD_CLS REG32(LCD_CLS) ++#define REG_LCD_SPL REG32(LCD_SPL) ++#define REG_LCD_REV REG32(LCD_REV) ++#define REG_LCD_CTRL REG32(LCD_CTRL) ++#define REG_LCD_STATE REG32(LCD_STATE) ++#define REG_LCD_IID REG32(LCD_IID) ++#define REG_LCD_DA0 REG32(LCD_DA0) ++#define REG_LCD_SA0 REG32(LCD_SA0) ++#define REG_LCD_FID0 REG32(LCD_FID0) ++#define REG_LCD_CMD0 REG32(LCD_CMD0) ++#define REG_LCD_DA1 REG32(LCD_DA1) ++#define REG_LCD_SA1 REG32(LCD_SA1) ++#define REG_LCD_FID1 REG32(LCD_FID1) ++#define REG_LCD_CMD1 REG32(LCD_CMD1) ++ ++/* LCD Configure Register */ ++#define LCD_CFG_LCDPIN_BIT 31 /* LCD pins selection */ ++#define LCD_CFG_LCDPIN_MASK (0x1 << LCD_CFG_LCDPIN_BIT) ++ #define LCD_CFG_LCDPIN_LCD (0x0 << LCD_CFG_LCDPIN_BIT) ++ #define LCD_CFG_LCDPIN_SLCD (0x1 << LCD_CFG_LCDPIN_BIT) ++#define LCD_CFG_PSM (1 << 23) /* PS signal mode */ ++#define LCD_CFG_CLSM (1 << 22) /* CLS signal mode */ ++#define LCD_CFG_SPLM (1 << 21) /* SPL signal mode */ ++#define LCD_CFG_REVM (1 << 20) /* REV signal mode */ ++#define LCD_CFG_HSYNM (1 << 19) /* HSYNC signal mode */ ++#define LCD_CFG_PCLKM (1 << 18) /* PCLK signal mode */ ++#define LCD_CFG_INVDAT (1 << 17) /* Inverse output data */ ++#define LCD_CFG_SYNDIR_IN (1 << 16) /* VSYNC&HSYNC direction */ ++#define LCD_CFG_PSP (1 << 15) /* PS pin reset state */ ++#define LCD_CFG_CLSP (1 << 14) /* CLS pin reset state */ ++#define LCD_CFG_SPLP (1 << 13) /* SPL pin reset state */ ++#define LCD_CFG_REVP (1 << 12) /* REV pin reset state */ ++#define LCD_CFG_HSP (1 << 11) /* HSYNC pority:0-active high,1-active low */ ++#define LCD_CFG_PCP (1 << 10) /* PCLK pority:0-rising,1-falling */ ++#define LCD_CFG_DEP (1 << 9) /* DE pority:0-active high,1-active low */ ++#define LCD_CFG_VSP (1 << 8) /* VSYNC pority:0-rising,1-falling */ ++#define LCD_CFG_PDW_BIT 4 /* STN pins utilization */ ++#define LCD_CFG_PDW_MASK (0x3 << LCD_DEV_PDW_BIT) ++#define LCD_CFG_PDW_1 (0 << LCD_CFG_PDW_BIT) /* LCD_D[0] */ ++ #define LCD_CFG_PDW_2 (1 << LCD_CFG_PDW_BIT) /* LCD_D[0:1] */ ++ #define LCD_CFG_PDW_4 (2 << LCD_CFG_PDW_BIT) /* LCD_D[0:3]/LCD_D[8:11] */ ++ #define LCD_CFG_PDW_8 (3 << LCD_CFG_PDW_BIT) /* LCD_D[0:7]/LCD_D[8:15] */ ++#define LCD_CFG_MODE_BIT 0 /* Display Device Mode Select */ ++#define LCD_CFG_MODE_MASK (0x0f << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_GENERIC_TFT (0 << LCD_CFG_MODE_BIT) /* 16,18 bit TFT */ ++ #define LCD_CFG_MODE_SPECIAL_TFT_1 (1 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SPECIAL_TFT_2 (2 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SPECIAL_TFT_3 (3 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_NONINTER_CCIR656 (4 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_INTER_CCIR656 (6 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SINGLE_CSTN (8 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SINGLE_MSTN (9 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_DUAL_CSTN (10 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_DUAL_MSTN (11 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SERIAL_TFT (12 << LCD_CFG_MODE_BIT) ++ /* JZ47XX defines */ ++ #define LCD_CFG_MODE_SHARP_HR (1 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_CASIO_TFT (2 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SAMSUNG_ALPHA (3 << LCD_CFG_MODE_BIT) ++ ++ ++ ++/* Vertical Synchronize Register */ ++#define LCD_VSYNC_VPS_BIT 16 /* VSYNC pulse start in line clock, fixed to 0 */ ++#define LCD_VSYNC_VPS_MASK (0xffff << LCD_VSYNC_VPS_BIT) ++#define LCD_VSYNC_VPE_BIT 0 /* VSYNC pulse end in line clock */ ++#define LCD_VSYNC_VPE_MASK (0xffff << LCD_VSYNC_VPS_BIT) ++ ++/* Horizontal Synchronize Register */ ++#define LCD_HSYNC_HPS_BIT 16 /* HSYNC pulse start position in dot clock */ ++#define LCD_HSYNC_HPS_MASK (0xffff << LCD_HSYNC_HPS_BIT) ++#define LCD_HSYNC_HPE_BIT 0 /* HSYNC pulse end position in dot clock */ ++#define LCD_HSYNC_HPE_MASK (0xffff << LCD_HSYNC_HPE_BIT) ++ ++/* Virtual Area Setting Register */ ++#define LCD_VAT_HT_BIT 16 /* Horizontal Total size in dot clock */ ++#define LCD_VAT_HT_MASK (0xffff << LCD_VAT_HT_BIT) ++#define LCD_VAT_VT_BIT 0 /* Vertical Total size in dot clock */ ++#define LCD_VAT_VT_MASK (0xffff << LCD_VAT_VT_BIT) ++ ++/* Display Area Horizontal Start/End Point Register */ ++#define LCD_DAH_HDS_BIT 16 /* Horizontal display area start in dot clock */ ++#define LCD_DAH_HDS_MASK (0xffff << LCD_DAH_HDS_BIT) ++#define LCD_DAH_HDE_BIT 0 /* Horizontal display area end in dot clock */ ++#define LCD_DAH_HDE_MASK (0xffff << LCD_DAH_HDE_BIT) ++ ++/* Display Area Vertical Start/End Point Register */ ++#define LCD_DAV_VDS_BIT 16 /* Vertical display area start in line clock */ ++#define LCD_DAV_VDS_MASK (0xffff << LCD_DAV_VDS_BIT) ++#define LCD_DAV_VDE_BIT 0 /* Vertical display area end in line clock */ ++#define LCD_DAV_VDE_MASK (0xffff << LCD_DAV_VDE_BIT) ++ ++/* PS Signal Setting */ ++#define LCD_PS_PSS_BIT 16 /* PS signal start position in dot clock */ ++#define LCD_PS_PSS_MASK (0xffff << LCD_PS_PSS_BIT) ++#define LCD_PS_PSE_BIT 0 /* PS signal end position in dot clock */ ++#define LCD_PS_PSE_MASK (0xffff << LCD_PS_PSE_BIT) ++ ++/* CLS Signal Setting */ ++#define LCD_CLS_CLSS_BIT 16 /* CLS signal start position in dot clock */ ++#define LCD_CLS_CLSS_MASK (0xffff << LCD_CLS_CLSS_BIT) ++#define LCD_CLS_CLSE_BIT 0 /* CLS signal end position in dot clock */ ++#define LCD_CLS_CLSE_MASK (0xffff << LCD_CLS_CLSE_BIT) ++ ++/* SPL Signal Setting */ ++#define LCD_SPL_SPLS_BIT 16 /* SPL signal start position in dot clock */ ++#define LCD_SPL_SPLS_MASK (0xffff << LCD_SPL_SPLS_BIT) ++#define LCD_SPL_SPLE_BIT 0 /* SPL signal end position in dot clock */ ++#define LCD_SPL_SPLE_MASK (0xffff << LCD_SPL_SPLE_BIT) ++ ++/* REV Signal Setting */ ++#define LCD_REV_REVS_BIT 16 /* REV signal start position in dot clock */ ++#define LCD_REV_REVS_MASK (0xffff << LCD_REV_REVS_BIT) ++ ++/* LCD Control Register */ ++#define LCD_CTRL_BST_BIT 28 /* Burst Length Selection */ ++#define LCD_CTRL_BST_MASK (0x03 << LCD_CTRL_BST_BIT) ++ #define LCD_CTRL_BST_4 (0 << LCD_CTRL_BST_BIT) /* 4-word */ ++ #define LCD_CTRL_BST_8 (1 << LCD_CTRL_BST_BIT) /* 8-word */ ++ #define LCD_CTRL_BST_16 (2 << LCD_CTRL_BST_BIT) /* 16-word */ ++#define LCD_CTRL_RGB565 (0 << 27) /* RGB565 mode */ ++#define LCD_CTRL_RGB555 (1 << 27) /* RGB555 mode */ ++#define LCD_CTRL_OFUP (1 << 26) /* Output FIFO underrun protection enable */ ++#define LCD_CTRL_FRC_BIT 24 /* STN FRC Algorithm Selection */ ++#define LCD_CTRL_FRC_MASK (0x03 << LCD_CTRL_FRC_BIT) ++ #define LCD_CTRL_FRC_16 (0 << LCD_CTRL_FRC_BIT) /* 16 grayscale */ ++ #define LCD_CTRL_FRC_4 (1 << LCD_CTRL_FRC_BIT) /* 4 grayscale */ ++ #define LCD_CTRL_FRC_2 (2 << LCD_CTRL_FRC_BIT) /* 2 grayscale */ ++#define LCD_CTRL_PDD_BIT 16 /* Load Palette Delay Counter */ ++#define LCD_CTRL_PDD_MASK (0xff << LCD_CTRL_PDD_BIT) ++#define LCD_CTRL_EOFM (1 << 13) /* EOF interrupt mask */ ++#define LCD_CTRL_SOFM (1 << 12) /* SOF interrupt mask */ ++#define LCD_CTRL_OFUM (1 << 11) /* Output FIFO underrun interrupt mask */ ++#define LCD_CTRL_IFUM0 (1 << 10) /* Input FIFO 0 underrun interrupt mask */ ++#define LCD_CTRL_IFUM1 (1 << 9) /* Input FIFO 1 underrun interrupt mask */ ++#define LCD_CTRL_LDDM (1 << 8) /* LCD disable done interrupt mask */ ++#define LCD_CTRL_QDM (1 << 7) /* LCD quick disable done interrupt mask */ ++#define LCD_CTRL_BEDN (1 << 6) /* Endian selection */ ++#define LCD_CTRL_PEDN (1 << 5) /* Endian in byte:0-msb first, 1-lsb first */ ++#define LCD_CTRL_DIS (1 << 4) /* Disable indicate bit */ ++#define LCD_CTRL_ENA (1 << 3) /* LCD enable bit */ ++#define LCD_CTRL_BPP_BIT 0 /* Bits Per Pixel */ ++#define LCD_CTRL_BPP_MASK (0x07 << LCD_CTRL_BPP_BIT) ++ #define LCD_CTRL_BPP_1 (0 << LCD_CTRL_BPP_BIT) /* 1 bpp */ ++ #define LCD_CTRL_BPP_2 (1 << LCD_CTRL_BPP_BIT) /* 2 bpp */ ++ #define LCD_CTRL_BPP_4 (2 << LCD_CTRL_BPP_BIT) /* 4 bpp */ ++ #define LCD_CTRL_BPP_8 (3 << LCD_CTRL_BPP_BIT) /* 8 bpp */ ++ #define LCD_CTRL_BPP_16 (4 << LCD_CTRL_BPP_BIT) /* 15/16 bpp */ ++ #define LCD_CTRL_BPP_18_24 (5 << LCD_CTRL_BPP_BIT) /* 18/24/32 bpp */ ++ ++/* LCD Status Register */ ++#define LCD_STATE_QD (1 << 7) /* Quick Disable Done */ ++#define LCD_STATE_EOF (1 << 5) /* EOF Flag */ ++#define LCD_STATE_SOF (1 << 4) /* SOF Flag */ ++#define LCD_STATE_OFU (1 << 3) /* Output FIFO Underrun */ ++#define LCD_STATE_IFU0 (1 << 2) /* Input FIFO 0 Underrun */ ++#define LCD_STATE_IFU1 (1 << 1) /* Input FIFO 1 Underrun */ ++#define LCD_STATE_LDD (1 << 0) /* LCD Disabled */ ++ ++/* DMA Command Register */ ++#define LCD_CMD_SOFINT (1 << 31) ++#define LCD_CMD_EOFINT (1 << 30) ++#define LCD_CMD_PAL (1 << 28) ++#define LCD_CMD_LEN_BIT 0 ++#define LCD_CMD_LEN_MASK (0xffffff << LCD_CMD_LEN_BIT) ++ ++ ++/************************************************************************* ++ * USB Device ++ *************************************************************************/ ++#define USB_BASE UDC_BASE ++ ++#define USB_REG_FADDR (USB_BASE + 0x00) /* Function Address 8-bit */ ++#define USB_REG_POWER (USB_BASE + 0x01) /* Power Managemetn 8-bit */ ++#define USB_REG_INTRIN (USB_BASE + 0x02) /* Interrupt IN 16-bit */ ++#define USB_REG_INTROUT (USB_BASE + 0x04) /* Interrupt OUT 16-bit */ ++#define USB_REG_INTRINE (USB_BASE + 0x06) /* Intr IN enable 16-bit */ ++#define USB_REG_INTROUTE (USB_BASE + 0x08) /* Intr OUT enable 16-bit */ ++#define USB_REG_INTRUSB (USB_BASE + 0x0a) /* Interrupt USB 8-bit */ ++#define USB_REG_INTRUSBE (USB_BASE + 0x0b) /* Interrupt USB Enable 8-bit */ ++#define USB_REG_FRAME (USB_BASE + 0x0c) /* Frame number 16-bit */ ++#define USB_REG_INDEX (USB_BASE + 0x0e) /* Index register 8-bit */ ++#define USB_REG_TESTMODE (USB_BASE + 0x0f) /* USB test mode 8-bit */ ++ ++#define USB_REG_CSR0 (USB_BASE + 0x12) /* EP0 CSR 8-bit */ ++#define USB_REG_INMAXP (USB_BASE + 0x10) /* EP1-2 IN Max Pkt Size 16-bit */ ++#define USB_REG_INCSR (USB_BASE + 0x12) /* EP1-2 IN CSR LSB 8/16bit */ ++#define USB_REG_INCSRH (USB_BASE + 0x13) /* EP1-2 IN CSR MSB 8-bit */ ++#define USB_REG_OUTMAXP (USB_BASE + 0x14) /* EP1 OUT Max Pkt Size 16-bit */ ++#define USB_REG_OUTCSR (USB_BASE + 0x16) /* EP1 OUT CSR LSB 8/16bit */ ++#define USB_REG_OUTCSRH (USB_BASE + 0x17) /* EP1 OUT CSR MSB 8-bit */ ++#define USB_REG_OUTCOUNT (USB_BASE + 0x18) /* bytes in EP0/1 OUT FIFO 16-bit */ ++ ++#define USB_FIFO_EP0 (USB_BASE + 0x20) ++#define USB_FIFO_EP1 (USB_BASE + 0x24) ++#define USB_FIFO_EP2 (USB_BASE + 0x28) ++ ++#define USB_REG_EPINFO (USB_BASE + 0x78) /* Endpoint information */ ++#define USB_REG_RAMINFO (USB_BASE + 0x79) /* RAM information */ ++ ++#define USB_REG_INTR (USB_BASE + 0x200) /* DMA pending interrupts */ ++#define USB_REG_CNTL1 (USB_BASE + 0x204) /* DMA channel 1 control */ ++#define USB_REG_ADDR1 (USB_BASE + 0x208) /* DMA channel 1 AHB memory addr */ ++#define USB_REG_COUNT1 (USB_BASE + 0x20c) /* DMA channel 1 byte count */ ++#define USB_REG_CNTL2 (USB_BASE + 0x214) /* DMA channel 2 control */ ++#define USB_REG_ADDR2 (USB_BASE + 0x218) /* DMA channel 2 AHB memory addr */ ++#define USB_REG_COUNT2 (USB_BASE + 0x21c) /* DMA channel 2 byte count */ ++ ++ ++/* Power register bit masks */ ++#define USB_POWER_SUSPENDM 0x01 ++#define USB_POWER_RESUME 0x04 ++#define USB_POWER_HSMODE 0x10 ++#define USB_POWER_HSENAB 0x20 ++#define USB_POWER_SOFTCONN 0x40 ++ ++/* Interrupt register bit masks */ ++#define USB_INTR_SUSPEND 0x01 ++#define USB_INTR_RESUME 0x02 ++#define USB_INTR_RESET 0x04 ++ ++#define USB_INTR_EP0 0x0001 ++#define USB_INTR_INEP1 0x0002 ++#define USB_INTR_INEP2 0x0004 ++#define USB_INTR_OUTEP1 0x0002 ++ ++/* CSR0 bit masks */ ++#define USB_CSR0_OUTPKTRDY 0x01 ++#define USB_CSR0_INPKTRDY 0x02 ++#define USB_CSR0_SENTSTALL 0x04 ++#define USB_CSR0_DATAEND 0x08 ++#define USB_CSR0_SETUPEND 0x10 ++#define USB_CSR0_SENDSTALL 0x20 ++#define USB_CSR0_SVDOUTPKTRDY 0x40 ++#define USB_CSR0_SVDSETUPEND 0x80 ++ ++/* Endpoint CSR register bits */ ++#define USB_INCSRH_AUTOSET 0x80 ++#define USB_INCSRH_ISO 0x40 ++#define USB_INCSRH_MODE 0x20 ++#define USB_INCSRH_DMAREQENAB 0x10 ++#define USB_INCSRH_DMAREQMODE 0x04 ++#define USB_INCSR_CDT 0x40 ++#define USB_INCSR_SENTSTALL 0x20 ++#define USB_INCSR_SENDSTALL 0x10 ++#define USB_INCSR_FF 0x08 ++#define USB_INCSR_UNDERRUN 0x04 ++#define USB_INCSR_FFNOTEMPT 0x02 ++#define USB_INCSR_INPKTRDY 0x01 ++#define USB_OUTCSRH_AUTOCLR 0x80 ++#define USB_OUTCSRH_ISO 0x40 ++#define USB_OUTCSRH_DMAREQENAB 0x20 ++#define USB_OUTCSRH_DNYT 0x10 ++#define USB_OUTCSRH_DMAREQMODE 0x08 ++#define USB_OUTCSR_CDT 0x80 ++#define USB_OUTCSR_SENTSTALL 0x40 ++#define USB_OUTCSR_SENDSTALL 0x20 ++#define USB_OUTCSR_FF 0x10 ++#define USB_OUTCSR_DATAERR 0x08 ++#define USB_OUTCSR_OVERRUN 0x04 ++#define USB_OUTCSR_FFFULL 0x02 ++#define USB_OUTCSR_OUTPKTRDY 0x01 ++ ++/* Testmode register bits */ ++#define USB_TEST_SE0NAK 0x01 ++#define USB_TEST_J 0x02 ++#define USB_TEST_K 0x04 ++#define USB_TEST_PACKET 0x08 ++ ++/* DMA control bits */ ++#define USB_CNTL_ENA 0x01 ++#define USB_CNTL_DIR_IN 0x02 ++#define USB_CNTL_MODE_1 0x04 ++#define USB_CNTL_INTR_EN 0x08 ++#define USB_CNTL_EP(n) ((n) << 4) ++#define USB_CNTL_BURST_0 (0 << 9) ++#define USB_CNTL_BURST_4 (1 << 9) ++#define USB_CNTL_BURST_8 (2 << 9) ++#define USB_CNTL_BURST_16 (3 << 9) ++ ++#endif /* __JZ4740_REGS_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4740/serial.h linux-2.6.24.7/include/asm-mips/mach-jz4740/serial.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4740/serial.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4740/serial.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,30 @@ ++/* ++ * linux/include/asm-mips/mach-jz4740/serial.h ++ * ++ * Ingenic's JZ4740 common include. ++ * ++ * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_BOARD_SERIAL_H__ ++#define __ASM_BOARD_SERIAL_H__ ++ ++#ifndef CONFIG_SERIAL_MANY_PORTS ++#undef RS_TABLE_SIZE ++#define RS_TABLE_SIZE 1 ++#endif ++ ++#define JZ_BASE_BAUD (12000000/16) ++ ++#define JZ_SERIAL_PORT_DEFNS \ ++ { .baud_base = JZ_BASE_BAUD, .irq = IRQ_UART0, \ ++ .flags = STD_COM_FLAGS, .iomem_base = (u8 *)UART0_BASE, \ ++ .iomem_reg_shift = 2, .io_type = SERIAL_IO_MEM }, ++ ++#endif /* __ASM_BORAD_SERIAL_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4740/war.h linux-2.6.24.7/include/asm-mips/mach-jz4740/war.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4740/war.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4740/war.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,25 @@ ++/* ++ * 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) 2002, 2004, 2007 by Ralf Baechle ++ */ ++#ifndef __ASM_MIPS_MACH_JZ4740_WAR_H ++#define __ASM_MIPS_MACH_JZ4740_WAR_H ++ ++#define R4600_V1_INDEX_ICACHEOP_WAR 0 ++#define R4600_V1_HIT_CACHEOP_WAR 0 ++#define R4600_V2_HIT_CACHEOP_WAR 0 ++#define R5432_CP0_INTERRUPT_WAR 0 ++#define BCM1250_M3_WAR 0 ++#define SIBYTE_1956_WAR 0 ++#define MIPS4K_ICACHE_REFILL_WAR 0 ++#define MIPS_CACHE_SYNC_WAR 0 ++#define TX49XX_ICACHE_INDEX_INV_WAR 0 ++#define RM9000_CDEX_SMP_WAR 0 ++#define ICACHE_REFILLS_WORKAROUND_WAR 0 ++#define R10000_LLSC_WAR 0 ++#define MIPS34K_MISSED_ITLB_WAR 0 ++ ++#endif /* __ASM_MIPS_MACH_JZ4740_WAR_H */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750/board-apus.h linux-2.6.24.7/include/asm-mips/mach-jz4750/board-apus.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750/board-apus.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750/board-apus.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,119 @@ ++/* ++ * linux/include/asm-mips/mach-jz4750/board-apus.h ++ * ++ * JZ4750-based APUS board ver 1.x definition. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4750_APUS_H__ ++#define __ASM_JZ4750_APUS_H__ ++ ++/*====================================================================== ++ * Frequencies of on-board oscillators ++ */ ++#define JZ_EXTAL 24000000 /* Main extal freq: 24 MHz */ ++#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */ ++ ++/*====================================================================== ++ * GPIO ++ */ ++#define GPIO_DISP_OFF_N (32*4+25) /* GPE25 */ ++#define GPIO_SD0_VCC_EN_N (32*2+10) /* GPC10 */ ++#define GPIO_SD0_CD_N (32*2+11) /* GPC11 */ ++#define GPIO_SD0_WP (32*2+12) /* GPC12 */ ++#define GPIO_SD1_VCC_EN_N (32*2+13) /* GPC13 */ ++#define GPIO_SD1_CD_N (32*2+14) /* GPC14 */ ++#define GPIO_USB_DETE (32*2+11) /* GPC15 */ ++#define GPIO_DC_DETE_N (32*2+8) /* GPC8 */ ++#define GPIO_CHARG_STAT_N (32*2+9) /* GPC9 */ ++#define GPIO_LCD_VCC_EN_N (32*3+30) /* GPC10 */ ++#define GPIO_LCD_PWM (32*4+24) /* GPE24 */ ++#define GPIO_UDC_HOTPLUG GPIO_USB_DETE ++ ++ ++/*====================================================================== ++ * LCD backlight ++ */ ++#define LCD_PWM_CHN 4 /* pwm channel */ ++#define LCD_PWM_FULL 101 ++/* 100 level: 0,1,...,100 */ ++#define __lcd_set_backlight_level(n) \ ++do { \ ++ __gpio_as_output(GPIO_LCD_PWM); \ ++ __gpio_set_pin(GPIO_LCD_PWM); \ ++} while (0) ++ ++#define __lcd_close_backlight() \ ++do { \ ++ __gpio_as_output(GPIO_LCD_PWM); \ ++ __gpio_clear_pin(GPIO_LCD_PWM); \ ++} while (0) ++ ++/*====================================================================== ++ * MMC/SD ++ */ ++ ++#define MSC0_WP_PIN GPIO_SD0_WP ++#define MSC0_HOTPLUG_PIN GPIO_SD0_CD_N ++#define MSC0_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD0_CD_N) ++ ++#define MSC1_WP_PIN GPIO_SD1_WP ++#define MSC1_HOTPLUG_PIN GPIO_SD1_CD_N ++#define MSC1_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD1_CD_N) ++ ++#define __msc0_init_io() \ ++do { \ ++ __gpio_as_output(GPIO_SD0_VCC_EN_N); \ ++ __gpio_as_input(GPIO_SD0_CD_N); \ ++} while (0) ++ ++#define __msc0_enable_power() \ ++do { \ ++ __gpio_clear_pin(GPIO_SD0_VCC_EN_N); \ ++} while (0) ++ ++#define __msc0_disable_power() \ ++do { \ ++ __gpio_set_pin(GPIO_SD0_VCC_EN_N); \ ++} while (0) ++ ++#define __msc0_card_detected(s) \ ++({ \ ++ int detected = 1; \ ++ if (__gpio_get_pin(GPIO_SD0_CD_N)) \ ++ detected = 0; \ ++ detected; \ ++}) ++ ++#define __msc1_init_io() \ ++do { \ ++ __gpio_as_output(GPIO_SD1_VCC_EN_N); \ ++ __gpio_as_input(GPIO_SD1_CD_N); \ ++} while (0) ++ ++#define __msc1_enable_power() \ ++do { \ ++ __gpio_clear_pin(GPIO_SD1_VCC_EN_N); \ ++} while (0) ++ ++#define __msc1_disable_power() \ ++do { \ ++ __gpio_set_pin(GPIO_SD1_VCC_EN_N); \ ++} while (0) ++ ++#define __msc1_card_detected(s) \ ++({ \ ++ int detected = 0; \ ++ if (__gpio_get_pin(GPIO_SD1_CD_N)) \ ++ detected = 1; \ ++ detected; \ ++}) ++ ++#endif /* __ASM_JZ4750_APUS_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750/board-fuwa.h linux-2.6.24.7/include/asm-mips/mach-jz4750/board-fuwa.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750/board-fuwa.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750/board-fuwa.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,93 @@ ++/* ++ * linux/include/asm-mips/mach-jz4750/board-fuwa.h ++ * ++ * JZ4750-based FUWA board ver 1.x definition. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4750_FUWA_H__ ++#define __ASM_JZ4750_FUWA_H__ ++ ++#define CONFIG_FPGA /* fuwa is an FPGA board */ ++ ++/*====================================================================== ++ * Frequencies of on-board oscillators ++ */ ++#define JZ_EXTAL 48000000 /* Main extal freq: 12 MHz */ ++#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */ ++ ++ ++/*====================================================================== ++ * GPIO ++ */ ++#define GPIO_SD_VCC_EN_N 113 /* GPD17 */ ++#define GPIO_SD_CD_N 110 /* GPD14 */ ++#define GPIO_SD_WP 112 /* GPD16 */ ++#define GPIO_USB_DETE 102 /* GPD6 */ ++#define GPIO_DC_DETE_N 103 /* GPD7 */ ++#define GPIO_CHARG_STAT_N 111 /* GPD15 */ ++#define GPIO_DISP_OFF_N 121 /* GPD25, LCD_REV */ ++#define GPIO_LED_EN 124 /* GPD28 */ ++ ++#define GPIO_UDC_HOTPLUG GPIO_USB_DETE ++ ++/*====================================================================== ++ * LCD backlight ++ */ ++#define GPIO_LCD_PWM (32*4+20) /* GPE20 */ ++ ++#define LCD_PWM_CHN 0 /* pwm channel */ ++#define LCD_PWM_FULL 101 ++/* 100 level: 0,1,...,100 */ ++#define __lcd_set_backlight_level(n) \ ++do { \ ++ __gpio_as_output(GPIO_LCD_PWM); \ ++ __gpio_set_pin(GPIO_LCD_PWM); \ ++} while (0) ++ ++#define __lcd_close_backlight() \ ++do { \ ++ __gpio_as_output(GPIO_LCD_PWM); \ ++ __gpio_clear_pin(GPIO_LCD_PWM); \ ++} while (0) ++ ++/*====================================================================== ++ * MMC/SD ++ */ ++ ++#define MSC_WP_PIN GPIO_SD_WP ++#define MSC_HOTPLUG_PIN GPIO_SD_CD_N ++#define MSC_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD_CD_N) ++ ++#define __msc_init_io() \ ++do { \ ++ __gpio_as_output(GPIO_SD_VCC_EN_N); \ ++ __gpio_as_input(GPIO_SD_CD_N); \ ++} while (0) ++ ++#define __msc_enable_power() \ ++do { \ ++ __gpio_clear_pin(GPIO_SD_VCC_EN_N); \ ++} while (0) ++ ++#define __msc_disable_power() \ ++do { \ ++ __gpio_set_pin(GPIO_SD_VCC_EN_N); \ ++} while (0) ++ ++#define __msc_card_detected(s) \ ++({ \ ++ int detected = 1; \ ++ if (__gpio_get_pin(GPIO_SD_CD_N)) \ ++ detected = 0; \ ++ detected; \ ++}) ++ ++#endif /* __ASM_JZ4750_FUWA_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750/clock.h linux-2.6.24.7/include/asm-mips/mach-jz4750/clock.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750/clock.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750/clock.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,204 @@ ++/* ++ * linux/include/asm-mips/mach-jz4750/clock.h ++ * ++ * JZ4750 clocks definition. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4750_CLOCK_H__ ++#define __ASM_JZ4750_CLOCK_H__ ++ ++#ifndef JZ_EXTAL ++#define JZ_EXTAL 12000000 /* 3.6864 MHz */ ++#endif ++#ifndef JZ_EXTAL2 ++#define JZ_EXTAL2 32768 /* 32.768 KHz */ ++#endif ++ ++/* ++ * JZ4750 clocks structure ++ */ ++typedef struct { ++ unsigned int cclk; /* CPU clock */ ++ unsigned int hclk; /* System bus clock */ ++ unsigned int pclk; /* Peripheral bus clock */ ++ unsigned int mclk; /* Flash/SRAM/SDRAM clock */ ++ unsigned int lcdclk; /* LCDC module clock */ ++ unsigned int pixclk; /* LCD pixel clock */ ++ unsigned int i2sclk; /* AIC module clock */ ++ unsigned int usbclk; /* USB module clock */ ++ unsigned int mscclk; /* MSC module clock */ ++ unsigned int extalclk; /* EXTAL clock for UART,I2C,SSI,TCU,USB-PHY */ ++ unsigned int rtcclk; /* RTC clock for CPM,INTC,RTC,TCU,WDT */ ++} jz_clocks_t; ++ ++extern jz_clocks_t jz_clocks; ++ ++ ++/* PLL output frequency */ ++static __inline__ unsigned int __cpm_get_pllout(void) ++{ ++ unsigned long m, n, no, pllout; ++ unsigned long cppcr = REG_CPM_CPPCR; ++ unsigned long od[4] = {1, 2, 2, 4}; ++ if ((cppcr & CPM_CPPCR_PLLEN) && !(cppcr & CPM_CPPCR_PLLBP)) { ++ m = __cpm_get_pllm() + 2; ++ n = __cpm_get_plln() + 2; ++ no = od[__cpm_get_pllod()]; ++ pllout = ((JZ_EXTAL) / (n * no)) * m; ++ } else ++ pllout = JZ_EXTAL; ++ return pllout; ++} ++ ++/* PLL output frequency for MSC/I2S/LCD/USB */ ++static __inline__ unsigned int __cpm_get_pllout2(void) ++{ ++ if (REG_CPM_CPCCR & CPM_CPCCR_PCS) ++ return __cpm_get_pllout(); ++ else ++ return __cpm_get_pllout()/2; ++} ++ ++/* CPU core clock */ ++static __inline__ unsigned int __cpm_get_cclk(void) ++{ ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ ++ return __cpm_get_pllout() / div[__cpm_get_cdiv()]; ++} ++ ++/* AHB system bus clock */ ++static __inline__ unsigned int __cpm_get_hclk(void) ++{ ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ ++ return __cpm_get_pllout() / div[__cpm_get_hdiv()]; ++} ++ ++/* Memory bus clock */ ++static __inline__ unsigned int __cpm_get_mclk(void) ++{ ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ ++ return __cpm_get_pllout() / div[__cpm_get_mdiv()]; ++} ++ ++/* APB peripheral bus clock */ ++static __inline__ unsigned int __cpm_get_pclk(void) ++{ ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ ++ return __cpm_get_pllout() / div[__cpm_get_pdiv()]; ++} ++ ++/* LCDC module clock */ ++static __inline__ unsigned int __cpm_get_lcdclk(void) ++{ ++ return __cpm_get_pllout2() / (__cpm_get_ldiv() + 1); ++} ++ ++/* LCD pixel clock */ ++static __inline__ unsigned int __cpm_get_pixclk(void) ++{ ++ return __cpm_get_pllout2() / (__cpm_get_pixdiv() + 1); ++} ++ ++/* I2S clock */ ++static __inline__ unsigned int __cpm_get_i2sclk(void) ++{ ++ if (REG_CPM_CPCCR & CPM_CPCCR_I2CS) { ++ return __cpm_get_pllout2() / (__cpm_get_i2sdiv() + 1); ++ } ++ else { ++ return JZ_EXTAL; ++ } ++} ++ ++/* USB clock */ ++static __inline__ unsigned int __cpm_get_usbclk(void) ++{ ++ if (REG_CPM_CPCCR & CPM_CPCCR_UCS) { ++ return __cpm_get_pllout2() / (__cpm_get_udiv() + 1); ++ } ++ else { ++ return JZ_EXTAL; ++ } ++} ++ ++/* ++ * MSC clock ++ * @n: the index of MMC/SD controller ++ */ ++static __inline__ unsigned int __cpm_get_mscclk(int n) ++{ ++ return __cpm_get_pllout2() / (__cpm_get_mscdiv(n) + 1); ++} ++ ++/* EXTAL clock */ ++static __inline__ unsigned int __cpm_get_extalclk0(void) ++{ ++ return JZ_EXTAL; ++} ++ ++/* EXTAL clock for UART,I2C,SSI,TCU,USB-PHY */ ++static __inline__ unsigned int __cpm_get_extalclk(void) ++{ ++#if defined(CONFIG_FPGA) ++ return JZ_EXTAL; ++#else ++ if (REG_CPM_CPCCR & CPM_CPCCR_ECS) ++ return __cpm_get_extalclk0()/2; ++ else ++ return __cpm_get_extalclk0(); ++#endif ++} ++ ++/* RTC clock for CPM,INTC,RTC,TCU,WDT */ ++static __inline__ unsigned int __cpm_get_rtcclk(void) ++{ ++ return JZ_EXTAL2; ++} ++ ++/* ++ * Output 24MHz for SD and 16MHz for MMC. ++ * @n: the index of MMC/SD controller ++ */ ++static inline void __cpm_select_msc_clk(int n, int sd) ++{ ++ unsigned int pllout2 = __cpm_get_pllout2(); ++ unsigned int div = 0; ++ ++ if (sd) { ++ div = pllout2 / 24000000; ++ } ++ else { ++ div = pllout2 / 16000000; ++ } ++ ++ REG_CPM_MSCCDR(n) = div - 1; ++ REG_CPM_CPCCR |= CPM_CPCCR_CE; ++} ++ ++/* ++ * Output 48MHz for high speed card. ++ */ ++static inline void __cpm_select_msc_clk_high(int n, int sd) ++{ ++ unsigned int pllout2 = __cpm_get_pllout2(); ++ unsigned int div = 0; ++ ++ div = pllout2 / 48000000; ++ ++ REG_CPM_MSCCDR(n) = div - 1; ++ REG_CPM_CPCCR |= CPM_CPCCR_CE; ++} ++ ++#endif /* __ASM_JZ4750_CLOCK_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750/dma.h linux-2.6.24.7/include/asm-mips/mach-jz4750/dma.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750/dma.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750/dma.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,307 @@ ++/* ++ * linux/include/asm-mips/mach-jz4750/dma.h ++ * ++ * JZ4750 DMA definition. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4750_DMA_H__ ++#define __ASM_JZ4750_DMA_H__ ++ ++#include ++#include /* need byte IO */ ++#include /* And spinlocks */ ++#include ++#include ++ ++/* ++ * Descriptor structure for JZ4750 DMA engine ++ * Note: this structure must always be aligned to a 16-bytes boundary. ++ */ ++ ++/* old descriptor 4-word */ ++typedef struct { ++ volatile u32 dcmd; /* DCMD value for the current transfer */ ++ volatile u32 dsadr; /* DSAR value for the current transfer */ ++ volatile u32 dtadr; /* DTAR value for the current transfer */ ++ volatile u32 ddadr; /* Points to the next descriptor + transfer count */ ++} jz_dma_desc; ++ ++/* new descriptor 8-word */ ++typedef struct { ++ volatile u32 dcmd; /* DCMD value for the current transfer */ ++ volatile u32 dsadr; /* DSAR value for the current transfer */ ++ volatile u32 dtadr; /* DTAR value for the current transfer */ ++ volatile u32 ddadr; /* Points to the next descriptor + transfer count */ ++ volatile u32 dstrd; /* DMA source and target stride address */ ++ volatile u32 dreqt; /* DMA request type for current transfer */ ++ volatile u32 reserved0; /* Reserved */ ++ volatile u32 reserved1; /* Reserved */ ++} jz_dma_desc_8word; ++ ++/* DMA Device ID's follow */ ++enum { ++ DMA_ID_EXT = 0, /* External request with DREQn */ ++ DMA_ID_NAND, /* NAND DMA request */ ++ DMA_ID_BCH_ENC, /* BCH Encoding DMA request */ ++ DMA_ID_BCH_DEC, /* BCH Decoding DMA request */ ++ DMA_ID_AUTO, /* Auto-request */ ++// DMA_ID_TSSI_RX, /* TSSI receive fifo full request */ ++ DMA_ID_UART3_TX, /* UART3 transmit-fifo-empty request */ ++ DMA_ID_UART3_RX, /* UART3 receve-fifo-full request */ ++ DMA_ID_UART2_TX, /* UART2 transmit-fifo-empty request */ ++ DMA_ID_UART2_RX, /* UART2 receve-fifo-full request */ ++ DMA_ID_UART1_TX, /* UART1 transmit-fifo-empty request */ ++ DMA_ID_UART1_RX, /* UART1 receve-fifo-full request */ ++ DMA_ID_UART0_TX, /* UART0 transmit-fifo-empty request */ ++ DMA_ID_UART0_RX, /* UART0 receve-fifo-full request */ ++ DMA_ID_SSI0_TX, /* SSI0 transmit-fifo-full request */ ++ DMA_ID_SSI0_RX, /* SSI0 receive-fifo-empty request */ ++ DMA_ID_AIC_TX, /* AIC transmit-fifo-full request */ ++ DMA_ID_AIC_RX, /* AIC receive-fifo-empty request */ ++ DMA_ID_MSC0_TX, /* MSC0 transmit-fifo-full request */ ++ DMA_ID_MSC0_RX, /* MSC0 receive-fifo-empty request */ ++ DMA_ID_TCU_OVERFLOW, /* TCU channel n overflow interrupt */ ++ DMA_ID_SADC, /* SADC transfer request */ ++ DMA_ID_MSC1_TX, /* MSC1 transmit-fifo-full request */ ++ DMA_ID_MSC1_RX, /* MSC1 receive-fifo-empty request */ ++ DMA_ID_SSI1_TX, /* SSI1 transmit-fifo-full request */ ++ DMA_ID_SSI1_RX, /* SSI1 receive-fifo-empty request */ ++ DMA_ID_PCM_TX, /* PM transmit-fifo-full request */ ++ DMA_ID_PCM_RX, /* PM receive-fifo-empty request */ ++ DMA_ID_RAW_SET, ++ DMA_ID_MAX ++}; ++ ++/* DMA modes, simulated by sw */ ++#define DMA_MODE_READ 0x0 /* I/O to memory, no autoinit, increment, single mode */ ++#define DMA_MODE_WRITE 0x1 /* memory to I/O, no autoinit, increment, single mode */ ++#define DMA_AUTOINIT 0x2 ++#define DMA_MODE_MASK 0x3 ++ ++struct jz_dma_chan { ++ int dev_id; /* DMA ID: this channel is allocated if >=0, free otherwise */ ++ unsigned int io; /* DMA channel number */ ++ const char *dev_str; /* string describes the DMA channel */ ++ int irq; /* DMA irq number */ ++ void *irq_dev; /* DMA private device structure */ ++ unsigned int fifo_addr; /* physical fifo address of the requested device */ ++ unsigned int cntl; /* DMA controll */ ++ unsigned int mode; /* DMA configuration */ ++ unsigned int source; /* DMA request source */ ++}; ++ ++extern struct jz_dma_chan jz_dma_table[]; ++ ++ ++#define DMA_8BIT_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_8BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_8BIT_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \ ++ DMAC_DCMD_DS_8BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_16BIT_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_16BIT_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_32BIT_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_32BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_32BIT_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_32BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_16BYTE_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_16BYTE_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_32BYTE_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_32BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_32BYTE_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \ ++ DMAC_DCMD_DS_32BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_32_32BYTE_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_32BYTE | DMAC_DCMD_RDIL_IGN ++#define DMA_AIC_32_16BYTE_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_32_16BYTE_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_16BIT_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_16BIT_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_16BYTE_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_16BYTE_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_16BYTE_TX_CMD_UC \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++extern int jz_request_dma(int dev_id, ++ const char *dev_str, ++ irqreturn_t (*irqhandler)(int, void *), ++ unsigned long irqflags, ++ void *irq_dev_id); ++extern void jz_free_dma(unsigned int dmanr); ++ ++extern int jz_dma_read_proc(char *buf, char **start, off_t fpos, ++ int length, int *eof, void *data); ++extern void dump_jz_dma_channel(unsigned int dmanr); ++ ++extern void enable_dma(unsigned int dmanr); ++extern void disable_dma(unsigned int dmanr); ++extern void set_dma_addr(unsigned int dmanr, unsigned int phyaddr); ++extern void set_dma_count(unsigned int dmanr, unsigned int bytecnt); ++extern void set_dma_mode(unsigned int dmanr, unsigned int mode); ++extern void jz_set_oss_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt); ++extern void jz_set_alsa_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt); ++extern void jz_set_dma_src_width(int dmanr, int nbit); ++extern void jz_set_dma_dest_width(int dmanr, int nbit); ++extern void jz_set_dma_block_size(int dmanr, int nbyte); ++extern unsigned int get_dma_residue(unsigned int dmanr); ++ ++extern spinlock_t dma_spin_lock; ++ ++static __inline__ unsigned long claim_dma_lock(void) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&dma_spin_lock, flags); ++ return flags; ++} ++ ++static __inline__ void release_dma_lock(unsigned long flags) ++{ ++ spin_unlock_irqrestore(&dma_spin_lock, flags); ++} ++ ++/* Clear the 'DMA Pointer Flip Flop'. ++ * Write 0 for LSB/MSB, 1 for MSB/LSB access. ++ */ ++#define clear_dma_ff(channel) ++ ++static __inline__ struct jz_dma_chan *get_dma_chan(unsigned int dmanr) ++{ ++ if (dmanr > MAX_DMA_NUM ++ || jz_dma_table[dmanr].dev_id < 0) ++ return NULL; ++ return &jz_dma_table[dmanr]; ++} ++ ++static __inline__ int dma_halted(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 1; ++ return __dmac_channel_transmit_halt_detected(dmanr) ? 1 : 0; ++} ++ ++static __inline__ unsigned int get_dma_mode(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 0; ++ return chan->mode; ++} ++ ++static __inline__ void clear_dma_done(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); ++} ++ ++static __inline__ void clear_dma_halt(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT); ++ REG_DMAC_DMACR((chan->io)/HALF_DMA_NUM) &= ~(DMAC_DMACR_HLT); ++} ++ ++static __inline__ void clear_dma_flag(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); ++ REG_DMAC_DMACR((chan->io)/HALF_DMA_NUM) &= ~(DMAC_DMACR_HLT | DMAC_DMACR_AR); ++} ++ ++static __inline__ void set_dma_page(unsigned int dmanr, char pagenr) ++{ ++} ++ ++static __inline__ unsigned int get_dma_done_status(unsigned int dmanr) ++{ ++ unsigned long dccsr; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 0; ++ dccsr = REG_DMAC_DCCSR(chan->io); ++ return dccsr & (DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); ++} ++ ++static __inline__ int get_dma_done_irq(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return -1; ++ return chan->irq; ++} ++ ++#endif /* __ASM_JZ4750_DMA_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750/jz4750.h linux-2.6.24.7/include/asm-mips/mach-jz4750/jz4750.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750/jz4750.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750/jz4750.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,44 @@ ++/* ++ * linux/include/asm-mips/mach-jz4750/jz4750.h ++ * ++ * JZ4750 common definition. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4750_H__ ++#define __ASM_JZ4750_H__ ++ ++#include ++#include ++#include ++#include ++ ++/*------------------------------------------------------------------ ++ * Platform definitions ++ */ ++#ifdef CONFIG_JZ4750_FUWA ++#include ++#endif ++ ++#ifdef CONFIG_JZ4750_APUS ++#include ++#endif ++ ++/* Add other platform definition here ... */ ++ ++ ++/*------------------------------------------------------------------ ++ * Follows are related to platform definitions ++ */ ++ ++#include ++#include ++ ++#endif /* __ASM_JZ4750_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750/misc.h linux-2.6.24.7/include/asm-mips/mach-jz4750/misc.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750/misc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750/misc.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,44 @@ ++/* ++ * linux/include/asm-mips/mach-jz4750/misc.h ++ * ++ * Ingenic's JZ4750 common include. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4750_MISC_H__ ++#define __ASM_JZ4750_MISC_H__ ++ ++/*========================================================== ++ * I2C ++ *===========================================================*/ ++ ++#define I2C_EEPROM_DEV 0xA /* b'1010 */ ++#define I2C_RTC_DEV 0xD /* b'1101 */ ++#define DIMM0_SPD_ADDR 0 ++#define DIMM1_SPD_ADDR 1 ++#define DIMM2_SPD_ADDR 2 ++#define DIMM3_SPD_ADDR 3 ++#define JZ_HCI_ADDR 7 ++ ++#define DIMM_SPD_LEN 128 ++#define JZ_HCI_LEN 512 /* 4K bits E2PROM */ ++#define I2C_RTC_LEN 16 ++#define HCI_MAC_OFFSET 64 ++ ++extern void i2c_open(void); ++extern void i2c_close(void); ++extern void i2c_setclk(unsigned int i2cclk); ++ ++extern int i2c_read(unsigned char device, unsigned char *buf, ++ unsigned char address, int count); ++extern int i2c_write(unsigned char device, unsigned char *buf, ++ unsigned char address, int count); ++ ++#endif /* __ASM_JZ4750_MISC_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750/ops.h linux-2.6.24.7/include/asm-mips/mach-jz4750/ops.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750/ops.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750/ops.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,3569 @@ ++/* ++ * linux/include/asm-mips/mach-jz4750/ops.h ++ * ++ * JZ4750 register definition. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * ++ * 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 __JZ4750_OPS_H__ ++#define __JZ4750_OPS_H__ ++ ++/* ++ * Definition of Module Operations ++ */ ++ ++/*************************************************************************** ++ * EMC ++ ***************************************************************************/ ++#define is_share_mode() ((REG_EMC_BCR & EMC_BCR_BSR_MASK) == EMC_BCR_BSR_SHARE) ++#define is_normal_order() (!(REG_EMC_BCR & EMC_BCR_PK_SEL)) ++ ++/*************************************************************************** ++ * GPIO ++ ***************************************************************************/ ++ ++//------------------------------------------------------ ++// GPIO Pins Description ++// ++// PORT 0: ++// ++// PIN/BIT N FUNC0 FUNC1 NOTE ++// 0 D0 - ++// 1 D1 - ++// 2 D2 - ++// 3 D3 - ++// 4 D4 - ++// 5 D5 - ++// 6 D6 - ++// 7 D7 - ++// 8 D8 - ++// 9 D9 - ++// 10 D10 - ++// 11 D11 - ++// 12 D12 - ++// 13 D13 - ++// 14 D14 - ++// 15 D15 - ++// 16 D16 - ++// 17 D17 - ++// 18 D18 - ++// 19 D19 - ++// 20 D20 - ++// 21 D21 - ++// 22 D22 - ++// 23 D23 - ++// 24 D24 - ++// 25 D25 - ++// 26 D26 - ++// 27 D27 - ++// 28 D28 - ++// 29 D29 - ++// 30 D30 - ++// 31 D31 - ++// ++//------------------------------------------------------ ++// PORT 1: ++// ++// PIN/BIT N FUNC0 FUNC1 NOTE ++// 0 A0 - ++// 1 A1 - ++// 2 A2 - ++// 3 A3 - ++// 4 A4 - ++// 5 A5 - ++// 6 A6 - ++// 7 A7 - ++// 8 A8 - ++// 9 A9 - ++// 10 A10 - ++// 11 A11 - ++// 12 A12 - ++// 13 A13 - ++// 14 A14 - ++// 15 A15/CLE SA3 ++// 16 DCS0# - ++// 17 RAS# - ++// 18 CAS# - ++// 19 RDWE#/BUFD# - ++// 20 WE0# - ++// 21 WE1# - ++// 22 WE2# - ++// 23 WE3# - ++// 24 CKO - Note1 ++// 25 CKE - ++// 26 SSI0_CLK - ++// 27 SSI0_DT - ++// 28 SSI0_DR - ++// 29 SSI0_CE0# - ++// 30 SSI0_CE1#_GPC - ++// 31 SSI0_CE2# - ++// ++// Note1: BIT24: it is CKO when chip is reset ++// ++//------------------------------------------------------ ++// PORT 2: ++// ++// PIN/BIT N FUNC0 FUNC1 NOTE ++// 0 SD0 A20 ++// 1 SD1 A21 ++// 2 SD2 A22 ++// 3 SD3 A23 ++// 4 SD4 A24 ++// 5 SD5 A25 ++// 6 SD6 - ++// 7 SD7 - ++// 8 SD8 TSDI0 ++// 9 SD9 TSDI1 ++// 10 SD10 TSDI2 ++// 11 SD11 TSDI3 ++// 12 SD12 TSDI4 ++// 13 SD13 TSDI5 ++// 14 SD14 TSDI6 ++// 15 SD15 TSDI7 ++// 16 A16/ALE SA4 ++// 17 SA0 A17 ++// 18 SA1 A18 ++// 19 SA2 A19 ++// 20 WAIT# - Note2 ++// 21 CS1# - ++// 22 CS2# - ++// 23 CS3# - ++// 24 CS4# - ++// 25 RD# - ++// 26 WR# - ++// 27 FRB# - Note3 ++// 28 FRE# - ++// 29 FWE# - ++// 30 BOOT_SEL0 - Note4 ++// 31 BOOT_SEL1 - Note5 ++// ++// Note2: BIT20: it is WAIT# pin when chip is reset ++// ++// Note3: BIT27: when NAND is used, it should connect to NANF FRB#. ++// ++// Note4: BIT30: it is BOOT_SEL0 when chip is reset, it can used as output GPIO. ++// ++// Note5: BIT31: it is BOOT_SEL1 when chip is reset, it can used as general GPIO. ++// ++//------------------------------------------------------ ++// PORT 3: ++// ++// PIN/BIT N FUNC0 FUNC1 NOTE ++// 0 LCD_D0 - ++// 1 LCD_D1 - ++// 2 LCD_D2 - ++// 3 LCD_D3 - ++// 4 LCD_D4 - ++// 5 LCD_D5 - ++// 6 LCD_D6 - ++// 7 LCD_D7 - ++// 8 LCD_D8 - ++// 9 LCD_D9 - ++// 10 LCD_D10 - ++// 11 LCD_D11 - ++// 12 LCD_D12 - ++// 13 LCD_D13 - ++// 14 LCD_D14 - ++// 15 LCD_D15 - ++// 16 LCD_D16 - ++// 17 LCD_D17 - ++// 18 LCD_PCLK - ++// 19 LCD_HSYNC - ++// 20 LCD_VSYNC - ++// 21 LCD_DE - ++// 22 LCD_CLS - ++// 23 LCD_SPL - ++// 24 LCD_PS - ++// 25 LCD_REV - ++// 26 SSI1_CLK - ++// 27 SSI1_DT - ++// 28 SSI1_DR - ++// 29 SSI1_CE0# - ++// 30 SSI1_CE1# - ++// 31 - - ++// ++//------------------------------------------------------ ++// PORT 4: ++// ++// PIN/BIT N FUNC0 FUNC1 NOTE ++// 0 CIM_D0 - ++// 1 CIM_D1 - ++// 2 CIM_D2 - ++// 3 CIM_D3 - ++// 4 CIM_D4 - ++// 5 CIM_D5 - ++// 6 CIM_D6 - ++// 7 CIM_D7 - ++// 8 CIM_MCLK - ++// 9 CIM_PCLK - ++// 10 CIM_VSYNC - ++// 11 CIM_HSYNC - ++// 12 I2C_SDA - ++// 13 I2C_SCK - ++// 14 - - ++// 15 - - ++// 16 UART1_RxD - ++// 17 UART1_TxD - ++// 18 UART1_CTS PCM_DIN ++// 19 UART1_RTS PCM_DOUT ++// 20 PWM0 PCM_CLK ++// 21 PWM1 PCM_SYN ++// 22 PWM2 SCLK_RSTN ++// 23 PWM3 BCLK ++// 24 PWM4 SYNC ++// 25 PWM5 OWI ++// 26 SDATO UART2_TxD ++// 27 SDATI UART2_RxD ++// 28 DCS1# - ++// 29 - - ++// 30 WKUP - Note6 ++// 31 - - Note7 ++// ++// Note6: BIT30: it is only used as input and interrupt, and with no pull-up and pull-down ++// ++// Note7: BIT31: it is used to select the function of UART or JTAG set by PESEL[31] ++// PESEL[31] = 0, select JTAG function ++// PESEL[31] = 1, select UART function ++// ++//------------------------------------------------------ ++// PORT 5: ++// ++// PIN/BIT N FUNC0 FUNC1 NOTE ++// 0 MSC0_D0 - ++// 1 MSC0_D1 - ++// 2 MSC0_D2 DREQ ++// 3 MSC0_D3 DACK ++// 4 MSC0_D4 UART0_RxD ++// 5 MSC0_D5 UART0_TxD ++// 6 MSC0_D6 UART0_CTS ++// 7 MSC0_D7 UART0_RTS ++// 8 MSC0_CLK - ++// 9 MSC0_CMD - ++// 10 MSC1_D0 - ++// 11 MSC1_D1 - ++// 12 MSC1_D2 - ++// 13 MSC1_D3 - ++// 14 MSC1_CLK - ++// 15 MSC1_CMD - ++// 16 UART3_RxD - ++// 17 UART3_TxD - ++// 18 UART3_CTS - ++// 19 UART3_RTS - ++// 20 TSCLK - ++// 21 TSSTR - ++// 22 TSFRM - ++// 23 TSFAIL - ++// 24 - - ++// 25 - - ++// 26 - - ++// 27 - - ++// 28 - - ++// 29 - - ++// 30 - - ++// 31 - - ++// ++////////////////////////////////////////////////////////// ++ ++/* ++ * p is the port number (0,1,2,3,4,5) ++ * o is the pin offset (0-31) inside the port ++ * n is the absolute number of a pin (0-191), regardless of the port ++ */ ++ ++//------------------------------------------- ++// Function Pins Mode ++ ++#define __gpio_as_func0(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXFUNS(p) = (1 << o); \ ++ REG_GPIO_PXSELC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_as_func1(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXFUNS(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++} while (0) ++ ++/* ++ * D0 ~ D31, A0 ~ A14, DCS0#, RAS#, CAS#, ++ * RDWE#, WE0#, WE1#, WE2#, WE3#, CKO#, CKE# ++ */ ++#define __gpio_as_sdram_32bit() \ ++do { \ ++ REG_GPIO_PXFUNS(0) = 0xffffffff; \ ++ REG_GPIO_PXSELC(0) = 0xffffffff; \ ++ REG_GPIO_PXPES(0) = 0xffffffff; \ ++ REG_GPIO_PXFUNS(1) = 0x03ff7fff; \ ++ REG_GPIO_PXSELC(1) = 0x03ff7fff; \ ++ REG_GPIO_PXPES(1) = 0x03ff7fff; \ ++} while (0) ++ ++/* ++ * D0 ~ D15, A0 ~ A14, DCS0#, RAS#, CAS#, ++ * RDWE#, WE0#, WE1#, WE2#, WE3#, CKO#, CKE# ++ */ ++#define __gpio_as_sdram_16bit() \ ++do { \ ++ if (is_normal_order()) { \ ++ /* 32/16-bit data normal order */ \ ++ REG_GPIO_PXFUNS(0) = 0x0000ffff; \ ++ REG_GPIO_PXSELC(0) = 0x0000ffff; \ ++ REG_GPIO_PXPES(0) = 0x0000ffff; \ ++ } else { \ ++ /* 16-bit data special order */ \ ++ REG_GPIO_PXFUNS(0) = 0x00ffff00; \ ++ REG_GPIO_PXSELC(0) = 0x00ffff00; \ ++ REG_GPIO_PXPES(0) = 0x00ffff00; \ ++ } \ ++ REG_GPIO_PXFUNS(1) = 0x03ff7fff; \ ++ REG_GPIO_PXSELC(1) = 0x03ff7fff; \ ++ REG_GPIO_PXPES(1) = 0x03ff7fff; \ ++} while (0) ++ ++/* ++ * D0 ~ D7, CS1#, CLE, ALE, FRE#, FWE#, FRB#, RDWE#/BUFD# ++ * @n: chip select number(1 ~ 4) ++ */ ++#define __gpio_as_nand_8bit(n) \ ++do { \ ++ if (!is_share_mode()) { \ ++ /* unshare mode */ \ ++ REG_GPIO_PXFUNS(2) = 0x000000ff; /* SD0~SD7 */ \ ++ REG_GPIO_PXSELS(2) = 0x000000ff; \ ++ REG_GPIO_PXPES(2) = 0x000000ff; \ ++ REG_GPIO_PXFUNS(1) = 0x00008000; /* CLE(SA3) */ \ ++ REG_GPIO_PXSELS(1) = 0x00008000; \ ++ REG_GPIO_PXPES(1) = 0x00008000; \ ++ REG_GPIO_PXFUNS(2) = 0x00010000; /* ALE(SA4) */ \ ++ REG_GPIO_PXSELS(2) = 0x00010000; \ ++ REG_GPIO_PXPES(2) = 0x00010000; \ ++ } else { \ ++ /* share mode */ \ ++ if (is_normal_order()) { \ ++ /* 32/16-bit data normal order */ \ ++ REG_GPIO_PXFUNS(0) = 0x000000ff; /* D0~D7 */ \ ++ REG_GPIO_PXSELC(0) = 0x000000ff; \ ++ REG_GPIO_PXPES(0) = 0x000000ff; \ ++ } else { \ ++ /* 16-bit data special order */ \ ++ REG_GPIO_PXFUNS(0) = 0x0000ff00; /* D0~D7 */ \ ++ REG_GPIO_PXSELC(0) = 0x0000ff00; \ ++ REG_GPIO_PXPES(0) = 0x0000ff00; \ ++ } \ ++ REG_GPIO_PXFUNS(1) = 0x00008000; /* CLE(A15) */ \ ++ REG_GPIO_PXSELC(1) = 0x00008000; \ ++ REG_GPIO_PXPES(1) = 0x00008000; \ ++ REG_GPIO_PXFUNS(2) = 0x00010000; /* ALE(A16) */ \ ++ REG_GPIO_PXSELC(2) = 0x00010000; \ ++ REG_GPIO_PXPES(2) = 0x00010000; \ ++ } \ ++ REG_GPIO_PXFUNS(2) = 0x00200000 << ((n)-1); /* CSn */ \ ++ REG_GPIO_PXSELC(2) = 0x00200000 << ((n)-1); \ ++ REG_GPIO_PXPES(2) = 0x00200000 << ((n)-1); \ ++ \ ++ REG_GPIO_PXFUNS(1) = 0x00080000; /* RDWE#/BUFD# */ \ ++ REG_GPIO_PXSELC(1) = 0x00080000; \ ++ REG_GPIO_PXPES(1) = 0x00080000; \ ++ REG_GPIO_PXFUNS(2) = 0x30000000; /* FRE#, FWE# */ \ ++ REG_GPIO_PXSELC(2) = 0x30000000; \ ++ REG_GPIO_PXPES(2) = 0x30000000; \ ++ REG_GPIO_PXFUNC(2) = 0x08000000; /* FRB#(input) */ \ ++ REG_GPIO_PXSELC(2) = 0x08000000; \ ++ REG_GPIO_PXDIRC(2) = 0x08000000; \ ++ REG_GPIO_PXPES(2) = 0x08000000; \ ++} while (0) ++ ++ ++/* ++ * CS4#, RD#, WR#, WAIT#, A0 ~ A22, D0 ~ D7 ++ * @n: chip select number(1 ~ 4) ++ */ ++#define __gpio_as_nor_8bit(n) \ ++do { \ ++ if (is_normal_order()) { \ ++ /* 32/16-bit data normal order */ \ ++ REG_GPIO_PXFUNS(0) = 0x000000ff; \ ++ REG_GPIO_PXSELC(0) = 0x000000ff; \ ++ REG_GPIO_PXPES(0) = 0x000000ff; \ ++ } else { \ ++ /* 16-bit data special order */ \ ++ REG_GPIO_PXFUNS(0) = 0x0000ff00; \ ++ REG_GPIO_PXSELC(0) = 0x0000ff00; \ ++ REG_GPIO_PXPES(0) = 0x0000ff00; \ ++ } \ ++ REG_GPIO_PXFUNS(2) = 0x00200000 << ((n)-1); /* CSn */ \ ++ REG_GPIO_PXSELC(2) = 0x00200000 << ((n)-1); \ ++ REG_GPIO_PXPES(2) = 0x00200000 << ((n)-1); \ ++ \ ++ REG_GPIO_PXFUNS(1) = 0x0000ffff; /* A0~A15 */ \ ++ REG_GPIO_PXSELC(1) = 0x0000ffff; \ ++ REG_GPIO_PXPES(1) = 0x0000ffff; \ ++ REG_GPIO_PXFUNS(2) = 0x06110007; /* RD#, WR#, WAIT#, A20~A22 */ \ ++ REG_GPIO_PXSELC(2) = 0x06110007; \ ++ REG_GPIO_PXPES(2) = 0x06110007; \ ++ REG_GPIO_PXFUNS(2) = 0x000e0000; /* A17~A19 */ \ ++ REG_GPIO_PXSELS(2) = 0x000e0000; \ ++ REG_GPIO_PXPES(2) = 0x000e0000; \ ++} while (0) ++ ++/* ++ * CS4#, RD#, WR#, WAIT#, A0 ~ A22, D0 ~ D15 ++ * @n: chip select number(1 ~ 4) ++ */ ++#define __gpio_as_nor_16bit(n) \ ++do { \ ++ if (is_normal_order()) { \ ++ /* 32/16-bit data normal order */ \ ++ REG_GPIO_PXFUNS(0) = 0x0000ffff; \ ++ REG_GPIO_PXSELC(0) = 0x0000ffff; \ ++ REG_GPIO_PXPES(0) = 0x0000ffff; \ ++ } else { \ ++ /* 16-bit data special order */ \ ++ REG_GPIO_PXFUNS(0) = 0x00ffff00; \ ++ REG_GPIO_PXSELC(0) = 0x00ffff00; \ ++ REG_GPIO_PXPES(0) = 0x00ffff00; \ ++ } \ ++ REG_GPIO_PXFUNS(2) = 0x00200000 << ((n)-1); /* CSn */ \ ++ REG_GPIO_PXSELC(2) = 0x00200000 << ((n)-1); \ ++ REG_GPIO_PXPES(2) = 0x00200000 << ((n)-1); \ ++ \ ++ REG_GPIO_PXFUNS(1) = 0x0000ffff; /* A0~A15 */ \ ++ REG_GPIO_PXSELC(1) = 0x0000ffff; \ ++ REG_GPIO_PXPES(1) = 0x0000ffff; \ ++ REG_GPIO_PXFUNS(2) = 0x06110007; /* RD#, WR#, WAIT#, A20~A22 */ \ ++ REG_GPIO_PXSELC(2) = 0x06110007; \ ++ REG_GPIO_PXPES(2) = 0x06110007; \ ++ REG_GPIO_PXFUNS(2) = 0x000e0000; /* A17~A19 */ \ ++ REG_GPIO_PXSELS(2) = 0x000e0000; \ ++ REG_GPIO_PXPES(2) = 0x000e0000; \ ++} while (0) ++ ++/* ++ * UART0_TxD, UART0_RxD ++ */ ++#define __gpio_as_uart0() \ ++do { \ ++ REG_GPIO_PXFUNS(5) = 0x00000030; \ ++ REG_GPIO_PXSELS(5) = 0x00000030; \ ++ REG_GPIO_PXPES(5) = 0x00000030; \ ++} while (0) ++ ++/* ++ * UART0_TxD, UART0_RxD, UART0_CTS, UART0_RTS ++ */ ++#define __gpio_as_uart0_ctsrts() \ ++do { \ ++ REG_GPIO_PXFUNS(5) = 0x000000f0; \ ++ REG_GPIO_PXSELS(5) = 0x000000f0; \ ++ REG_GPIO_PXPES(5) = 0x000000f0; \ ++} while (0) ++ ++/* ++ * UART1_TxD, UART1_RxD ++ */ ++#define __gpio_as_uart1() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x00030000; \ ++ REG_GPIO_PXSELC(4) = 0x00030000; \ ++ REG_GPIO_PXPES(4) = 0x00030000; \ ++} while (0) ++ ++/* ++ * UART1_TxD, UART1_RxD, UART1_CTS, UART1_RTS ++ */ ++#define __gpio_as_uart1_ctsrts() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x000f0000; \ ++ REG_GPIO_PXSELC(4) = 0x000f0000; \ ++ REG_GPIO_PXPES(4) = 0x000f0000; \ ++} while (0) ++ ++/* ++ * UART2_TxD, UART2_RxD ++ */ ++#define __gpio_as_uart2() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x0c000000; \ ++ REG_GPIO_PXSELS(4) = 0x0c000000; \ ++ REG_GPIO_PXPES(4) = 0x0c000000; \ ++} while (0) ++ ++/* ++ * UART3_TxD, UART3_RxD ++ */ ++#define __gpio_as_uart3() \ ++do { \ ++ REG_GPIO_PXFUNS(5) = 0x00030000; \ ++ REG_GPIO_PXSELC(5) = 0x00030000; \ ++ REG_GPIO_PXPES(5) = 0x00030000; \ ++} while (0) ++ ++/* ++ * UART3_TxD, UART3_RxD, UART3_CTS, UART3_RTS ++ */ ++#define __gpio_as_uart3_ctsrts() \ ++do { \ ++ REG_GPIO_PXFUNS(5) = 0x000f0000; \ ++ REG_GPIO_PXSELC(5) = 0x000f0000; \ ++ REG_GPIO_PXPES(5) = 0x000f0000; \ ++} while (0) ++ ++/* ++ * TSCLK, TSSTR, TSFRM, TSFAIL, TSDI0~7 ++ */ ++#define __gpio_as_tssi() \ ++do { \ ++ REG_GPIO_PXFUNS(2) = 0x0000ff00; \ ++ REG_GPIO_PXSELS(2) = 0x0000ff00; \ ++ REG_GPIO_PXPES(2) = 0x0000ff00; \ ++ REG_GPIO_PXFUNS(5) = 0x00f00000; \ ++ REG_GPIO_PXSELC(5) = 0x00f00000; \ ++ REG_GPIO_PXPES(5) = 0x00f00000; \ ++} while (0) ++ ++/* ++ * LCD_D0~LCD_D7, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE ++ */ ++#define __gpio_as_lcd_8bit() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x003c00ff; \ ++ REG_GPIO_PXSELC(3) = 0x003c00ff; \ ++ REG_GPIO_PXPES(3) = 0x003c00ff; \ ++} while (0) ++ ++/* ++ * LCD_D0~LCD_D15, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE ++ */ ++#define __gpio_as_lcd_16bit() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x003cffff; \ ++ REG_GPIO_PXSELC(3) = 0x003cffff; \ ++ REG_GPIO_PXPES(3) = 0x003cffff; \ ++} while (0) ++ ++/* ++ * LCD_D0~LCD_D17, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE ++ */ ++#define __gpio_as_lcd_18bit() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x003fffff; \ ++ REG_GPIO_PXSELC(3) = 0x003fffff; \ ++ REG_GPIO_PXPES(3) = 0x003fffff; \ ++} while (0) ++ ++/* ++ * LCD_D0~LCD_D17, LCD_D_R1, LCD_D_G0, LCD_D_G1, LCD_D_B1, ++ * LCD_D_R0, LCD_D_B0, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE ++ */ ++#define __gpio_as_lcd_24bit() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x003fffff; \ ++ REG_GPIO_PXSELC(3) = 0x003fffff; \ ++ REG_GPIO_PXPES(3) = 0x003fffff; \ ++ REG_GPIO_PXFUNS(3) = 0x03c00000; \ ++ REG_GPIO_PXSELS(3) = 0x03c00000; \ ++ REG_GPIO_PXPES(3) = 0x03c00000; \ ++ REG_GPIO_PXFUNS(5) = 0x000c0000; \ ++ REG_GPIO_PXSELS(5) = 0x000c0000; \ ++ REG_GPIO_PXPES(5) = 0x000c0000; \ ++} while (0) ++ ++/* ++ * SLCD_DAT0~7, SLCD_CLK, SLCD_RS, SLCD_CS ++ */ ++#define __gpio_as_lcd_smart_pal_8bit() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x001c00ff; \ ++ REG_GPIO_PXSELC(3) = 0x001c00ff; \ ++ REG_GPIO_PXPES(3) = 0x001c00ff; \ ++} while (0) ++ ++/* ++ * SLCD_DAT0~15, SLCD_CLK, SLCD_RS, SLCD_CS ++ */ ++#define __gpio_as_lcd_smart_pal_15bit() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x001cffff; \ ++ REG_GPIO_PXSELC(3) = 0x001cffff; \ ++ REG_GPIO_PXPES(3) = 0x001cffff; \ ++} while (0) ++ ++/* ++ * SLCD_DAT0~17, SLCD_CLK, SLCD_RS, SLCD_CS ++ */ ++#define __gpio_as_lcd_smart_pal_17bit() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x001fffff; \ ++ REG_GPIO_PXSELC(3) = 0x001fffff; \ ++ REG_GPIO_PXPES(3) = 0x001fffff; \ ++} while (0) ++ ++/* ++ * SLCD_DAT15, SLCD_CLK, SLCD_RS, SLCD_CS ++ */ ++#define __gpio_as_lcd_smart_serial() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x001c8000; \ ++ REG_GPIO_PXSELC(3) = 0x001c8000; \ ++ REG_GPIO_PXPES(3) = 0x001c8000; \ ++} while (0) ++ ++/* ++ * LCD_CLS, LCD_SPL, LCD_PS, LCD_REV ++ */ ++#define __gpio_as_lcd_special() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x03C00000; \ ++ REG_GPIO_PXSELC(3) = 0x03C00000; \ ++ REG_GPIO_PXPES(3) = 0x03C00000; \ ++} while (0) ++ ++/* ++ * CIM_D0~CIM_D7, CIM_MCLK, CIM_PCLK, CIM_VSYNC, CIM_HSYNC ++ */ ++#define __gpio_as_cim() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x00000fff; \ ++ REG_GPIO_PXSELC(4) = 0x00000fff; \ ++ REG_GPIO_PXPES(4) = 0x00000fff; \ ++} while (0) ++ ++/* ++ * SDATO, SDATI, BCLK, SYNC, SCLK_RSTN(gpio sepc) or ++ * SDATA_OUT, SDATA_IN, BIT_CLK, SYNC, SCLK_RESET(aic spec) ++ */ ++#define __gpio_as_aic() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x0c000000; \ ++ REG_GPIO_PXSELS(4) = 0x0c000000; \ ++ REG_GPIO_PXPES(4) = 0x0c000000; \ ++ REG_GPIO_PXFUNS(4) = 0x00e00000; \ ++ REG_GPIO_PXSELC(4) = 0x00e00000; \ ++ REG_GPIO_PXPES(4) = 0x00e00000; \ ++} while (0) ++ ++/* ++ * PCM_DIN, PCM_DOUT, PCM_CLK, PCM_SYN ++*/ ++#define __gpio_as_pcm() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x003c0000; \ ++ REG_GPIO_PXSELS(4) = 0x003c0000; \ ++ REG_GPIO_PXPES(4) = 0x003c0000; \ ++} while (0) ++ ++/* ++ * OWI ++*/ ++#define __gpio_as_owi() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x02000000; \ ++ REG_GPIO_PXSELS(4) = 0x02000000; \ ++ REG_GPIO_PXPES(4) = 0x02000000; \ ++} while (0) ++ ++/* ++ * MSC0_CMD, MSC0_CLK, MSC0_D0 ~ MSC0_D3 ++ */ ++#define __gpio_as_msc0_4bit() \ ++do { \ ++ REG_GPIO_PXFUNS(5) = 0x0000030f; \ ++ REG_GPIO_PXSELC(5) = 0x0000030f; \ ++ REG_GPIO_PXPES(5) = 0x0000030f; \ ++} while (0) ++ ++/* ++ * MSC0_CMD, MSC0_CLK, MSC0_D0 ~ MSC0_D7 ++ */ ++#define __gpio_as_msc0_8bit() \ ++do { \ ++ REG_GPIO_PXFUNS(5) = 0x000003ff; \ ++ REG_GPIO_PXSELC(5) = 0x000003ff; \ ++ REG_GPIO_PXPES(5) = 0x000003ff; \ ++} while (0) ++ ++/* ++ * MSC1_CMD, MSC1_CLK, MSC1_D0 ~ MSC1_D3 ++ */ ++#define __gpio_as_msc1_4bit() \ ++do { \ ++ REG_GPIO_PXFUNS(5) = 0x0000fc00; \ ++ REG_GPIO_PXSELC(5) = 0x0000fc00; \ ++ REG_GPIO_PXPES(5) = 0x0000fc00; \ ++} while (0) ++ ++#define __gpio_as_msc __gpio_as_msc0_8bit /* default as msc0 8bit */ ++#define __gpio_as_msc0 __gpio_as_msc0_8bit /* msc0 default as 8bit */ ++#define __gpio_as_msc1 __gpio_as_msc1_4bit /* msc1 only support 4bit */ ++ ++/* ++ * SSI0_CE0, SSI0_CE1#_GPC, SSI0_CE2, SSI0_CLK, SSI0_DT, SSI0_DR ++ */ ++#define __gpio_as_ssi0() \ ++do { \ ++ REG_GPIO_PXFUNS(1) = 0xfc000000; \ ++ REG_GPIO_PXSELC(1) = 0xfc000000; \ ++ REG_GPIO_PXPES(1) = 0xfc000000; \ ++} while (0) ++ ++/* ++ * SSI1_CE0, SSI1_CE1, SSI1_CLK, SSI1_DT, SSI1_DR ++ */ ++#define __gpio_as_ssi1() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x7c000000; \ ++ REG_GPIO_PXSELC(3) = 0x7c000000; \ ++ REG_GPIO_PXPES(3) = 0x7c000000; \ ++} while (0) ++ ++/* n = 0(SSI0), 1(SSI1) */ ++#define __gpio_as_ssi(n) __gpio_as_ssi##n() ++ ++/* ++ * I2C_SCK, I2C_SDA ++ */ ++#define __gpio_as_i2c() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x00003000; \ ++ REG_GPIO_PXSELC(4) = 0x00003000; \ ++ REG_GPIO_PXPES(4) = 0x00003000; \ ++} while (0) ++ ++/* ++ * PWM0 ++ */ ++#define __gpio_as_pwm0() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x00100000; \ ++ REG_GPIO_PXSELC(4) = 0x00100000; \ ++ REG_GPIO_PXPES(4) = 0x00100000; \ ++} while (0) ++ ++/* ++ * PWM1 ++ */ ++#define __gpio_as_pwm1() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x00200000; \ ++ REG_GPIO_PXSELC(4) = 0x00200000; \ ++ REG_GPIO_PXPES(4) = 0x00200000; \ ++} while (0) ++ ++/* ++ * PWM2 ++ */ ++#define __gpio_as_pwm2() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x00400000; \ ++ REG_GPIO_PXSELC(4) = 0x00400000; \ ++ REG_GPIO_PXPES(4) = 0x00400000; \ ++} while (0) ++ ++/* ++ * PWM3 ++ */ ++#define __gpio_as_pwm3() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x00800000; \ ++ REG_GPIO_PXSELC(4) = 0x00800000; \ ++ REG_GPIO_PXPES(4) = 0x00800000; \ ++} while (0) ++ ++/* ++ * PWM4 ++ */ ++#define __gpio_as_pwm4() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x01000000; \ ++ REG_GPIO_PXSELC(4) = 0x01000000; \ ++ REG_GPIO_PXPES(4) = 0x01000000; \ ++} while (0) ++ ++/* ++ * PWM5 ++ */ ++#define __gpio_as_pwm5() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x02000000; \ ++ REG_GPIO_PXSELC(4) = 0x02000000; \ ++ REG_GPIO_PXPES(4) = 0x02000000; \ ++} while (0) ++ ++/* ++ * n = 0 ~ 5 ++ */ ++#define __gpio_as_pwm(n) __gpio_as_pwm##n() ++ ++/* ++ * DREQ ++ */ ++#define __gpio_as_dreq() \ ++do { \ ++ REG_GPIO_PXFUNS(5) = 0x00000004; \ ++ REG_GPIO_PXSELS(5) = 0x00000004; \ ++ REG_GPIO_PXPES(5) = 0x00000004; \ ++} while (0) ++ ++/* ++ * DACK ++ */ ++#define __gpio_as_dack() \ ++do { \ ++ REG_GPIO_PXFUNS(5) = 0x00000008; \ ++ REG_GPIO_PXSELS(5) = 0x00000008; \ ++ REG_GPIO_PXPES(5) = 0x00000008; \ ++} while (0) ++ ++/* ++ * GPIO or Interrupt Mode ++ */ ++#define __gpio_get_port(p) (REG_GPIO_PXPIN(p)) ++ ++#define __gpio_port_as_output(p, o) \ ++do { \ ++ REG_GPIO_PXFUNC(p) = (1 << (o)); \ ++ REG_GPIO_PXSELC(p) = (1 << (o)); \ ++ REG_GPIO_PXDIRS(p) = (1 << (o)); \ ++} while (0) ++ ++#define __gpio_port_as_input(p, o) \ ++do { \ ++ REG_GPIO_PXFUNC(p) = (1 << (o)); \ ++ REG_GPIO_PXSELC(p) = (1 << (o)); \ ++ REG_GPIO_PXDIRC(p) = (1 << (o)); \ ++} while (0) ++ ++#define __gpio_as_output(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ __gpio_port_as_output(p, o); \ ++} while (0) ++ ++#define __gpio_as_input(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ __gpio_port_as_input(p, o); \ ++} while (0) ++ ++#define __gpio_set_pin(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXDATS(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_clear_pin(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXDATC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_get_pin(n) \ ++({ \ ++ unsigned int p, o, v; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ if (__gpio_get_port(p) & (1 << o)) \ ++ v = 1; \ ++ else \ ++ v = 0; \ ++ v; \ ++}) ++ ++#define __gpio_as_irq_high_level(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++ REG_GPIO_PXTRGC(p) = (1 << o); \ ++ REG_GPIO_PXFUNC(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++ REG_GPIO_PXDIRS(p) = (1 << o); \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_as_irq_low_level(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++ REG_GPIO_PXTRGC(p) = (1 << o); \ ++ REG_GPIO_PXFUNC(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++ REG_GPIO_PXDIRC(p) = (1 << o); \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_as_irq_rise_edge(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++ REG_GPIO_PXTRGS(p) = (1 << o); \ ++ REG_GPIO_PXFUNC(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++ REG_GPIO_PXDIRS(p) = (1 << o); \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_as_irq_fall_edge(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++ REG_GPIO_PXTRGS(p) = (1 << o); \ ++ REG_GPIO_PXFUNC(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++ REG_GPIO_PXDIRC(p) = (1 << o); \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_mask_irq(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_unmask_irq(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_ack_irq(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_get_irq() \ ++({ \ ++ unsigned int p, i, tmp, v = 0; \ ++ for (p = 3; p >= 0; p--) { \ ++ tmp = REG_GPIO_PXFLG(p); \ ++ for (i = 0; i < 32; i++) \ ++ if (tmp & (1 << i)) \ ++ v = (32*p + i); \ ++ } \ ++ v; \ ++}) ++ ++#define __gpio_group_irq(n) \ ++({ \ ++ register int tmp, i; \ ++ tmp = REG_GPIO_PXFLG((n)); \ ++ for (i=31;i>=0;i--) \ ++ if (tmp & (1 << i)) \ ++ break; \ ++ i; \ ++}) ++ ++#define __gpio_enable_pull(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXPEC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_disable_pull(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXPES(p) = (1 << o); \ ++} while (0) ++ ++ ++/*************************************************************************** ++ * CPM ++ ***************************************************************************/ ++#define __cpm_get_pllm() \ ++ ((REG_CPM_CPPCR & CPM_CPPCR_PLLM_MASK) >> CPM_CPPCR_PLLM_BIT) ++#define __cpm_get_plln() \ ++ ((REG_CPM_CPPCR & CPM_CPPCR_PLLN_MASK) >> CPM_CPPCR_PLLN_BIT) ++#define __cpm_get_pllod() \ ++ ((REG_CPM_CPPCR & CPM_CPPCR_PLLOD_MASK) >> CPM_CPPCR_PLLOD_BIT) ++ ++#define __cpm_get_cdiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_CDIV_MASK) >> CPM_CPCCR_CDIV_BIT) ++#define __cpm_get_hdiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_HDIV_MASK) >> CPM_CPCCR_HDIV_BIT) ++#define __cpm_get_pdiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_PDIV_MASK) >> CPM_CPCCR_PDIV_BIT) ++#define __cpm_get_mdiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_MDIV_MASK) >> CPM_CPCCR_MDIV_BIT) ++#define __cpm_get_ldiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_LDIV_MASK) >> CPM_CPCCR_LDIV_BIT) ++#define __cpm_get_udiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_UDIV_MASK) >> CPM_CPCCR_UDIV_BIT) ++#define __cpm_get_i2sdiv() \ ++ ((REG_CPM_I2SCDR & CPM_I2SCDR_I2SDIV_MASK) >> CPM_I2SCDR_I2SDIV_BIT) ++#define __cpm_get_pixdiv() \ ++ ((REG_CPM_LPCDR & CPM_LPCDR_PIXDIV_MASK) >> CPM_LPCDR_PIXDIV_BIT) ++#define __cpm_get_mscdiv(n) \ ++ ((REG_CPM_MSCCDR(n) & CPM_MSCCDR_MSCDIV_MASK) >> CPM_MSCCDR_MSCDIV_BIT) ++#define __cpm_get_uhcdiv() \ ++ ((REG_CPM_UHCCDR & CPM_UHCCDR_UHCDIV_MASK) >> CPM_UHCCDR_UHCDIV_BIT) ++#define __cpm_get_ssidiv() \ ++ ((REG_CPM_SSICCDR & CPM_SSICDR_SSICDIV_MASK) >> CPM_SSICDR_SSIDIV_BIT) ++#define __cpm_get_pcmdiv(v) \ ++ ((REG_CPM_PCMCDR & CPM_PCMCDR_PCMCD_MASK) >> CPM_PCMCDR_PCMCD_BIT) ++ ++#define __cpm_set_cdiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_CDIV_MASK) | ((v) << (CPM_CPCCR_CDIV_BIT))) ++#define __cpm_set_hdiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_HDIV_MASK) | ((v) << (CPM_CPCCR_HDIV_BIT))) ++#define __cpm_set_pdiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_PDIV_MASK) | ((v) << (CPM_CPCCR_PDIV_BIT))) ++#define __cpm_set_mdiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_MDIV_MASK) | ((v) << (CPM_CPCCR_MDIV_BIT))) ++#define __cpm_set_ldiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_LDIV_MASK) | ((v) << (CPM_CPCCR_LDIV_BIT))) ++#define __cpm_set_udiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_UDIV_MASK) | ((v) << (CPM_CPCCR_UDIV_BIT))) ++#define __cpm_set_i2sdiv(v) \ ++ (REG_CPM_I2SCDR = (REG_CPM_I2SCDR & ~CPM_I2SCDR_I2SDIV_MASK) | ((v) << (CPM_I2SCDR_I2SDIV_BIT))) ++#define __cpm_set_pixdiv(v) \ ++ (REG_CPM_LPCDR = (REG_CPM_LPCDR & ~CPM_LPCDR_PIXDIV_MASK) | ((v) << (CPM_LPCDR_PIXDIV_BIT))) ++#define __cpm_set_mscdiv(n, v) \ ++ (REG_CPM_MSCCDR(n) = (REG_CPM_MSCCDR(n) & ~CPM_MSCCDR_MSCDIV_MASK) | ((v) << (CPM_MSCCDR_MSCDIV_BIT))) ++#define __cpm_set_uhcdiv(v) \ ++ (REG_CPM_UHCCDR = (REG_CPM_UHCCDR & ~CPM_UHCCDR_UHCDIV_MASK) | ((v) << (CPM_UHCCDR_UHCDIV_BIT))) ++#define __cpm_set_ssidiv(v) \ ++ (REG_CPM_SSICDR = (REG_CPM_SSICDR & ~CPM_SSICDR_SSIDIV_MASK) | ((v) << (CPM_SSICDR_SSIDIV_BIT))) ++#define __cpm_set_pcmdiv(v) \ ++ (REG_CPM_PCMCDR = (REG_CPM_PCMCDR & ~CPM_PCMCDR_PCMCD_MASK) | ((v) << (CPM_PCMCDR_PCMCD_BIT))) ++ ++#define __cpm_select_pcmclk_pll() (REG_CPM_PCMCDR |= CPM_PCMCDR_PCMS) ++#define __cpm_select_pcmclk_exclk() (REG_CPM_PCMCDR &= ~CPM_PCMCDR_PCMS) ++#define __cpm_select_pixclk_ext() (REG_CPM_LPCDR |= CPM_LPCDR_LPCS) ++#define __cpm_select_pixclk_pll() (REG_CPM_LPCDR &= ~CPM_LPCDR_LPCS) ++#define __cpm_select_tveclk_exclk() (REG_CPM_LPCDR |= CPM_CPCCR_LSCS) ++#define __cpm_select_tveclk_pll() (REG_CPM_LPCDR &= ~CPM_LPCDR_LSCS) ++#define __cpm_select_pixclk_lcd() (REG_CPM_LPCDR &= ~CPM_LPCDR_LTCS) ++#define __cpm_select_pixclk_tve() (REG_CPM_LPCDR |= CPM_LPCDR_LTCS) ++#define __cpm_select_i2sclk_exclk() (REG_CPM_CPCCR &= ~CPM_CPCCR_I2CS) ++#define __cpm_select_i2sclk_pll() (REG_CPM_CPCCR |= CPM_CPCCR_I2CS) ++#define __cpm_select_usbclk_exclk() (REG_CPM_CPCCR &= ~CPM_CPCCR_UCS) ++#define __cpm_select_usbclk_pll() (REG_CPM_CPCCR |= CPM_CPCCR_UCS) ++ ++#define __cpm_enable_cko() ++#define __cpm_exclk_direct() (REG_CPM_CPCCR &= ~CPM_CPCCR_ECS) ++#define __cpm_exclk_div2() (REG_CPM_CPCCR |= CPM_CPCCR_ECS) ++#define __cpm_enable_pll_change() (REG_CPM_CPCCR |= CPM_CPCCR_CE) ++#define __cpm_pllout_direct() (REG_CPM_CPCCR |= CPM_CPCCR_PCS) ++#define __cpm_pllout_div2() (REG_CPM_CPCCR &= ~CPM_CPCCR_PCS) ++#define __cpm_pll_enable() (REG_CPM_CPPCR |= CPM_CPPCR_PLLEN) ++ ++#define __cpm_pll_is_off() (REG_CPM_CPPSR & CPM_CPPSR_PLLOFF) ++#define __cpm_pll_is_on() (REG_CPM_CPPSR & CPM_CPPSR_PLLON) ++#define __cpm_pll_bypass() (REG_CPM_CPPSR |= CPM_CPPSR_PLLBP) ++ ++#define __cpm_get_cclk_doze_duty() \ ++ ((REG_CPM_LCR & CPM_LCR_DOZE_DUTY_MASK) >> CPM_LCR_DOZE_DUTY_BIT) ++#define __cpm_set_cclk_doze_duty(v) \ ++ (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_DOZE_DUTY_MASK) | ((v) << (CPM_LCR_DOZE_DUTY_BIT))) ++ ++#define __cpm_doze_mode() (REG_CPM_LCR |= CPM_LCR_DOZE_ON) ++#define __cpm_idle_mode() \ ++ (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_LPM_MASK) | CPM_LCR_LPM_IDLE) ++#define __cpm_sleep_mode() \ ++ (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_LPM_MASK) | CPM_LCR_LPM_SLEEP) ++ ++#define __cpm_stop_all() (REG_CPM_CLKGR = 0x1fffffff) ++#define __cpm_stop_cimram() (REG_CPM_CLKGR |= CPM_CLKGR_CIMRAM) ++#define __cpm_stop_idct() (REG_CPM_CLKGR |= CPM_CLKGR_IDCT) ++#define __cpm_stop_db() (REG_CPM_CLKGR |= CPM_CLKGR_DB) ++#define __cpm_stop_me() (REG_CPM_CLKGR |= CPM_CLKGR_ME) ++#define __cpm_stop_mc() (REG_CPM_CLKGR |= CPM_CLKGR_MC) ++#define __cpm_stop_tve() (REG_CPM_CLKGR |= CPM_CLKGR_TVE) ++#define __cpm_stop_tssi() (REG_CPM_CLKGR |= CPM_CLKGR_TSSI) ++#define __cpm_stop_owi() (REG_CPM_CLKGR |= CPM_CLKGR_OWI) ++#define __cpm_stop_pcm() (REG_CPM_CLKGR |= CPM_CLKGR_PCM) ++#define __cpm_stop_uart3() (REG_CPM_CLKGR |= CPM_CLKGR_UART3) ++#define __cpm_stop_uart2() (REG_CPM_CLKGR |= CPM_CLKGR_UART2) ++#define __cpm_stop_uart1() (REG_CPM_CLKGR |= CPM_CLKGR_UART1) ++#define __cpm_stop_uhc() (REG_CPM_CLKGR |= CPM_CLKGR_UHC) ++#define __cpm_stop_ipu() (REG_CPM_CLKGR |= CPM_CLKGR_IPU) ++#define __cpm_stop_dmac() (REG_CPM_CLKGR |= CPM_CLKGR_DMAC) ++#define __cpm_stop_udc() (REG_CPM_CLKGR |= CPM_CLKGR_UDC) ++#define __cpm_stop_lcd() (REG_CPM_CLKGR |= CPM_CLKGR_LCD) ++#define __cpm_stop_cim() (REG_CPM_CLKGR |= CPM_CLKGR_CIM) ++#define __cpm_stop_sadc() (REG_CPM_CLKGR |= CPM_CLKGR_SADC) ++#define __cpm_stop_msc(n) (REG_CPM_CLKGR |= CPM_CLKGR_MSC##n) ++#define __cpm_stop_aic1() (REG_CPM_CLKGR |= CPM_CLKGR_AIC1) ++#define __cpm_stop_aic2() (REG_CPM_CLKGR |= CPM_CLKGR_AIC2) ++#define __cpm_stop_ssi(n) (REG_CPM_CLKGR |= CPM_CLKGR_SSI##n) ++#define __cpm_stop_i2c() (REG_CPM_CLKGR |= CPM_CLKGR_I2C) ++#define __cpm_stop_rtc() (REG_CPM_CLKGR |= CPM_CLKGR_RTC) ++#define __cpm_stop_tcu() (REG_CPM_CLKGR |= CPM_CLKGR_TCU) ++#define __cpm_stop_uart0() (REG_CPM_CLKGR |= CPM_CLKGR_UART0) ++ ++#define __cpm_start_all() (REG_CPM_CLKGR = 0x0) ++#define __cpm_start_cimram() (REG_CPM_CLKGR &= ~CPM_CLKGR_CIMRAM) ++#define __cpm_start_idct() (REG_CPM_CLKGR &= ~CPM_CLKGR_IDCT) ++#define __cpm_start_db() (REG_CPM_CLKGR &= ~CPM_CLKGR_DB) ++#define __cpm_start_me() (REG_CPM_CLKGR &= ~CPM_CLKGR_ME) ++#define __cpm_start_mc() (REG_CPM_CLKGR &= ~CPM_CLKGR_MC) ++#define __cpm_start_tve() (REG_CPM_CLKGR &= ~CPM_CLKGR_TVE) ++#define __cpm_start_tssi() (REG_CPM_CLKGR &= ~CPM_CLKGR_TSSI) ++#define __cpm_start_owi() (REG_CPM_CLKGR &= ~CPM_CLKGR_OWI) ++#define __cpm_start_pcm() (REG_CPM_CLKGR &= ~CPM_CLKGR_PCM) ++#define __cpm_start_uart3() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART3) ++#define __cpm_start_uart2() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART2) ++#define __cpm_start_uart1() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART1) ++#define __cpm_start_uhc() (REG_CPM_CLKGR &= ~CPM_CLKGR_UHC) ++#define __cpm_start_ipu() (REG_CPM_CLKGR &= ~CPM_CLKGR_IPU) ++#define __cpm_start_dmac() (REG_CPM_CLKGR &= ~CPM_CLKGR_DMAC) ++#define __cpm_start_udc() (REG_CPM_CLKGR &= ~CPM_CLKGR_UDC) ++#define __cpm_start_lcd() (REG_CPM_CLKGR &= ~CPM_CLKGR_LCD) ++#define __cpm_start_cim() (REG_CPM_CLKGR &= ~CPM_CLKGR_CIM) ++#define __cpm_start_sadc() (REG_CPM_CLKGR &= ~CPM_CLKGR_SADC) ++#define __cpm_start_msc(n) (REG_CPM_CLKGR &= ~CPM_CLKGR_MSC##n) ++#define __cpm_start_aic1() (REG_CPM_CLKGR &= ~CPM_CLKGR_AIC1) ++#define __cpm_start_aic2() (REG_CPM_CLKGR &= ~CPM_CLKGR_AIC2) ++#define __cpm_start_ssi(n) (REG_CPM_CLKGR &= ~CPM_CLKGR_SSI##n) ++#define __cpm_start_i2c() (REG_CPM_CLKGR &= ~CPM_CLKGR_I2C) ++#define __cpm_start_rtc() (REG_CPM_CLKGR &= ~CPM_CLKGR_RTC) ++#define __cpm_start_tcu() (REG_CPM_CLKGR &= ~CPM_CLKGR_TCU) ++#define __cpm_start_uart0() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART0) ++ ++#define __cpm_get_o1st() \ ++ ((REG_CPM_OPCR & CPM_OPCR_O1ST_MASK) >> CPM_OPCR_O1ST_BIT) ++#define __cpm_set_o1st(v) \ ++ (REG_CPM_OPCR = (REG_CPM_OPCR & ~CPM_OPCR_O1ST_MASK) | ((v) << (CPM_OPCR_O1ST_BIT))) ++#define __cpm_enable_uhcphy() (REG_CPM_OPCR &= ~CPM_OPCR_UHCPHY_DISABLE) ++#define __cpm_suspend_uhcphy() (REG_CPM_OPCR |= CPM_OPCR_UHCPHY_DISABLE) ++#define __cpm_enable_udcphy() (REG_CPM_OPCR |= CPM_OPCR_UDCPHY_ENABLE) ++#define __cpm_suspend_udcphy() (REG_CPM_OPCR &= ~CPM_OPCR_UDCPHY_ENABLE) ++#define __cpm_enable_osc_in_sleep() (REG_CPM_OPCR |= CPM_OPCR_OSC_ENABLE) ++#define __cpm_disable_osc_in_sleep() (REG_CPM_OPCR &= ~CPM_OPCR_OSC_ENABLE) ++#define __cpm_select_rtcclk_rtc() (REG_CPM_OPCR |= CPM_OPCR_ERCS) ++#define __cpm_select_rtcclk_exclk() (REG_CPM_OPCR &= ~CPM_OPCR_ERCS) ++ ++ ++/*************************************************************************** ++ * TCU ++ ***************************************************************************/ ++// where 'n' is the TCU channel ++#define __tcu_select_extalclk(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_EXT_EN) ++#define __tcu_select_rtcclk(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_RTC_EN) ++#define __tcu_select_pclk(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_PCK_EN) ++#define __tcu_disable_pclk(n) \ ++ REG_TCU_TCSR(n) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PCK_EN); ++#define __tcu_select_clk_div1(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE1) ++#define __tcu_select_clk_div4(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE4) ++#define __tcu_select_clk_div16(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE16) ++#define __tcu_select_clk_div64(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE64) ++#define __tcu_select_clk_div256(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE256) ++#define __tcu_select_clk_div1024(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE1024) ++ ++#define __tcu_enable_pwm_output(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_EN) ++#define __tcu_disable_pwm_output(n) (REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_EN) ++ ++#define __tcu_init_pwm_output_high(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_INITL_HIGH) ++#define __tcu_init_pwm_output_low(n) (REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_INITL_HIGH) ++ ++#define __tcu_set_pwm_output_shutdown_graceful(n) (REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_SD) ++#define __tcu_set_pwm_output_shutdown_abrupt(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_SD) ++ ++#define __tcu_clear_counter_to_zero(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_CNT_CLRZ) ++ ++#define __tcu_ost_enabled() (REG_TCU_TER & TCU_TER_OSTEN) ++#define __tcu_enable_ost() (REG_TCU_TESR = TCU_TESR_OSTST) ++#define __tcu_disable_ost() (REG_TCU_TECR = TCU_TECR_OSTCL) ++ ++#define __tcu_counter_enabled(n) (REG_TCU_TER & (1 << (n))) ++#define __tcu_start_counter(n) (REG_TCU_TESR |= (1 << (n))) ++#define __tcu_stop_counter(n) (REG_TCU_TECR |= (1 << (n))) ++ ++#define __tcu_half_match_flag(n) (REG_TCU_TFR & (1 << ((n) + 16))) ++#define __tcu_full_match_flag(n) (REG_TCU_TFR & (1 << (n))) ++#define __tcu_set_half_match_flag(n) (REG_TCU_TFSR = (1 << ((n) + 16))) ++#define __tcu_set_full_match_flag(n) (REG_TCU_TFSR = (1 << (n))) ++#define __tcu_clear_half_match_flag(n) (REG_TCU_TFCR = (1 << ((n) + 16))) ++#define __tcu_clear_full_match_flag(n) (REG_TCU_TFCR = (1 << (n))) ++#define __tcu_mask_half_match_irq(n) (REG_TCU_TMSR = (1 << ((n) + 16))) ++#define __tcu_mask_full_match_irq(n) (REG_TCU_TMSR = (1 << (n))) ++#define __tcu_unmask_half_match_irq(n) (REG_TCU_TMCR = (1 << ((n) + 16))) ++#define __tcu_unmask_full_match_irq(n) (REG_TCU_TMCR = (1 << (n))) ++ ++#define __tcu_ost_match_flag() (REG_TCU_TFR & TCU_TFR_OSTFLAG) ++#define __tcu_set_ost_match_flag() (REG_TCU_TFSR = TCU_TFSR_OSTFST) ++#define __tcu_clear_ost_match_flag() (REG_TCU_TFCR = TCU_TFCR_OSTFCL) ++#define __tcu_ost_match_irq_masked() (REG_TCU_TMR & TCU_TMR_OSTMASK) ++#define __tcu_mask_ost_match_irq() (REG_TCU_TMSR = TCU_TMSR_OSTMST) ++#define __tcu_unmask_ost_match_irq() (REG_TCU_TMCR = TCU_TMCR_OSTMCL) ++ ++#define __tcu_wdt_clock_stopped() (REG_TCU_TSR & TCU_TSSR_WDTSC) ++#define __tcu_ost_clock_stopped() (REG_TCU_TSR & TCU_TSR_OST) ++#define __tcu_timer_clock_stopped(n) (REG_TCU_TSR & (1 << (n))) ++ ++#define __tcu_start_wdt_clock() (REG_TCU_TSCR = TCU_TSSR_WDTSC) ++#define __tcu_start_ost_clock() (REG_TCU_TSCR = TCU_TSCR_OSTSC) ++#define __tcu_start_timer_clock(n) (REG_TCU_TSCR = (1 << (n))) ++ ++#define __tcu_stop_wdt_clock() (REG_TCU_TSSR = TCU_TSSR_WDTSC) ++#define __tcu_stop_ost_clock() (REG_TCU_TSSR = TCU_TSSR_OSTSS) ++#define __tcu_stop_timer_clock(n) (REG_TCU_TSSR = (1 << (n))) ++ ++#define __tcu_get_count(n) (REG_TCU_TCNT((n))) ++#define __tcu_set_count(n,v) (REG_TCU_TCNT((n)) = (v)) ++#define __tcu_set_full_data(n,v) (REG_TCU_TDFR((n)) = (v)) ++#define __tcu_set_half_data(n,v) (REG_TCU_TDHR((n)) = (v)) ++ ++/* TCU2, counter 1, 2*/ ++#define __tcu_read_real_value(n) (REG_TCU_TSTR & (1 << ((n) + 16))) ++#define __tcu_read_false_value(n) (REG_TCU_TSTR & (1 << ((n) + 16))) ++#define __tcu_counter_busy(n) (REG_TCU_TSTR & (1 << (n))) ++#define __tcu_counter_ready(n) (REG_TCU_TSTR & (1 << (n))) ++ ++#define __tcu_set_read_real_value(n) (REG_TCU_TSTSR = (1 << ((n) + 16))) ++#define __tcu_set_read_false_value(n) (REG_TCU_TSTCR = (1 << ((n) + 16))) ++#define __tcu_set_counter_busy(n) (REG_TCU_TSTSR = (1 << (n))) ++#define __tcu_set_counter_ready(n) (REG_TCU_TSTCR = (1 << (n))) ++ ++/* ost counter */ ++#define __ostcu_set_pwm_output_shutdown_graceful() (REG_TCU_OSTCSR &= ~TCU_TCSR_PWM_SD) ++#define __ostcu_set_ost_output_shutdown_abrupt() (REG_TCU_OSTCSR |= TCU_TCSR_PWM_SD) ++#define __ostcu_select_clk_div1() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE1) ++#define __ostcu_select_clk_div4() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE4) ++#define __ostcu_select_clk_div16() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE16) ++#define __ostcu_select_clk_div64() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE64) ++#define __ostcu_select_clk_div256() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE256) ++#define __ostcu_select_clk_div1024() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE1024) ++#define __ostcu_select_rtcclk() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~(TCU_OSTCSR_EXT_EN | TCU_OSTCSR_RTC_EN | TCU_OSTCSR_PCK_EN)) | TCU_OSTCSR_RTC_EN) ++#define __ostcu_select_extalclk() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~(TCU_OSTCSR_EXT_EN | TCU_OSTCSR_RTC_EN | TCU_OSTCSR_PCK_EN)) | TCU_OSTCSR_EXT_EN) ++#define __ostcu_select_pclk() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~(TCU_OSTCSR_EXT_EN | TCU_OSTCSR_RTC_EN | TCU_OSTCSR_PCK_EN)) | TCU_OSTCSR_PCK_EN) ++ ++ ++/*************************************************************************** ++ * WDT ++ ***************************************************************************/ ++#define __wdt_start() ( REG_WDT_TCER |= WDT_TCER_TCEN ) ++#define __wdt_stop() ( REG_WDT_TCER &= ~WDT_TCER_TCEN ) ++#define __wdt_set_count(v) ( REG_WDT_TCNT = (v) ) ++#define __wdt_set_data(v) ( REG_WDT_TDR = (v) ) ++ ++#define __wdt_select_extalclk() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_EXT_EN) ++#define __wdt_select_rtcclk() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_RTC_EN) ++#define __wdt_select_pclk() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_PCK_EN) ++ ++#define __wdt_select_clk_div1() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE1) ++#define __wdt_select_clk_div4() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE4) ++#define __wdt_select_clk_div16() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE16) ++#define __wdt_select_clk_div64() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE64) ++#define __wdt_select_clk_div256() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE256) ++#define __wdt_select_clk_div1024() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE1024) ++ ++ ++/*************************************************************************** ++ * UART ++ ***************************************************************************/ ++ ++#define __uart_enable(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) |= UARTFCR_UUE | UARTFCR_FE ) ++#define __uart_disable(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) = ~UARTFCR_UUE ) ++ ++#define __uart_enable_transmit_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_TIE ) ++#define __uart_disable_transmit_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~UARTIER_TIE ) ++ ++#define __uart_enable_receive_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE ) ++#define __uart_disable_receive_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~(UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE) ) ++ ++#define __uart_enable_loopback(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) |= UARTMCR_LOOP ) ++#define __uart_disable_loopback(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) &= ~UARTMCR_LOOP ) ++ ++#define __uart_set_8n1(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) = UARTLCR_WLEN_8 ) ++ ++#define __uart_set_baud(n, devclk, baud) \ ++ do { \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) |= UARTLCR_DLAB; \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_DLLR) = (devclk / 16 / baud) & 0xff; \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_DLHR) = ((devclk / 16 / baud) >> 8) & 0xff; \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) &= ~UARTLCR_DLAB; \ ++ } while (0) ++ ++#define __uart_parity_error(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_PER) != 0 ) ++ ++#define __uart_clear_errors(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) &= ~(UARTLSR_ORER | UARTLSR_BRK | UARTLSR_FER | UARTLSR_PER | UARTLSR_RFER) ) ++ ++#define __uart_transmit_fifo_empty(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TDRQ) != 0 ) ++ ++#define __uart_transmit_end(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TEMT) != 0 ) ++ ++#define __uart_transmit_char(n, ch) \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_TDR) = (ch) ++ ++#define __uart_receive_fifo_full(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) ++ ++#define __uart_receive_ready(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) ++ ++#define __uart_receive_char(n) \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_RDR) ++ ++#define __uart_disable_irda() \ ++ ( REG8(IRDA_BASE + OFF_SIRCR) &= ~(SIRCR_TSIRE | SIRCR_RSIRE) ) ++#define __uart_enable_irda() \ ++ /* Tx high pulse as 0, Rx low pulse as 0 */ \ ++ ( REG8(IRDA_BASE + OFF_SIRCR) = SIRCR_TSIRE | SIRCR_RSIRE | SIRCR_RXPL | SIRCR_TPWS ) ++ ++ ++/*************************************************************************** ++ * DMAC ++ ***************************************************************************/ ++ ++/* m is the DMA controller index (0, 1), n is the DMA channel index (0 - 11) */ ++ ++#define __dmac_enable_module(m) \ ++ ( REG_DMAC_DMACR(m) |= DMAC_DMACR_DMAE | DMAC_DMACR_PR_012345 ) ++#define __dmac_disable_module(m) \ ++ ( REG_DMAC_DMACR(m) &= ~DMAC_DMACR_DMAE ) ++ ++/* p=0,1,2,3 */ ++#define __dmac_set_priority(m,p) \ ++do { \ ++ REG_DMAC_DMACR(m) &= ~DMAC_DMACR_PR_MASK; \ ++ REG_DMAC_DMACR(m) |= ((p) << DMAC_DMACR_PR_BIT); \ ++} while (0) ++ ++#define __dmac_test_halt_error(m) ( REG_DMAC_DMACR(m) & DMAC_DMACR_HLT ) ++#define __dmac_test_addr_error(m) ( REG_DMAC_DMACR(m) & DMAC_DMACR_AR ) ++ ++#define __dmac_channel_enable_clk(n) \ ++ REG_DMAC_DMACKE((n)/HALF_DMA_NUM) |= 1 << ((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM); ++ ++#define __dmac_enable_descriptor(n) \ ++ ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_NDES ) ++#define __dmac_disable_descriptor(n) \ ++ ( REG_DMAC_DCCSR((n)) |= DMAC_DCCSR_NDES ) ++ ++#define __dmac_enable_channel(n) \ ++do { \ ++ REG_DMAC_DCCSR((n)) |= DMAC_DCCSR_EN; \ ++} while (0) ++#define __dmac_disable_channel(n) \ ++do { \ ++ REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_EN; \ ++} while (0) ++#define __dmac_channel_enabled(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_EN ) ++ ++#define __dmac_channel_enable_irq(n) \ ++ ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_TIE ) ++#define __dmac_channel_disable_irq(n) \ ++ ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_TIE ) ++ ++#define __dmac_channel_transmit_halt_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_HLT ) ++#define __dmac_channel_transmit_end_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_TT ) ++#define __dmac_channel_address_error_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_AR ) ++#define __dmac_channel_count_terminated_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_CT ) ++#define __dmac_channel_descriptor_invalid_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_INV ) ++ ++#define __dmac_channel_clear_transmit_halt(n) \ ++ do { \ ++ /* clear both channel halt error and globle halt error */ \ ++ REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_HLT; \ ++ REG_DMAC_DMACR(n/HALF_DMA_NUM) &= ~DMAC_DMACR_HLT; \ ++ } while (0) ++#define __dmac_channel_clear_transmit_end(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_TT ) ++#define __dmac_channel_clear_address_error(n) \ ++ do { \ ++ REG_DMAC_DDA(n) = 0; /* clear descriptor address register */ \ ++ REG_DMAC_DSAR(n) = 0; /* clear source address register */ \ ++ REG_DMAC_DTAR(n) = 0; /* clear target address register */ \ ++ /* clear both channel addr error and globle address error */ \ ++ REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_AR; \ ++ REG_DMAC_DMACR(n/HALF_DMA_NUM) &= ~DMAC_DMACR_AR; \ ++ } while (0) ++#define __dmac_channel_clear_count_terminated(n) \ ++ ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_CT ) ++#define __dmac_channel_clear_descriptor_invalid(n) \ ++ ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_INV ) ++ ++#define __dmac_channel_set_transfer_unit_32bit(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_32BIT; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_16bit(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_16BIT; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_8bit(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_8BIT; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_16byte(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_16BYTE; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_32byte(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_32BYTE; \ ++} while (0) ++ ++/* w=8,16,32 */ ++#define __dmac_channel_set_dest_port_width(n,w) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DWDH_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DWDH_##w; \ ++} while (0) ++ ++/* w=8,16,32 */ ++#define __dmac_channel_set_src_port_width(n,w) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_SWDH_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_SWDH_##w; \ ++} while (0) ++ ++/* v=0-15 */ ++#define __dmac_channel_set_rdil(n,v) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_RDIL_MASK; \ ++ REG_DMAC_DCMD((n) |= ((v) << DMAC_DCMD_RDIL_BIT); \ ++} while (0) ++ ++#define __dmac_channel_dest_addr_fixed(n) \ ++ ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DAI ) ++#define __dmac_channel_dest_addr_increment(n) \ ++ ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_DAI ) ++ ++#define __dmac_channel_src_addr_fixed(n) \ ++ ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_SAI ) ++#define __dmac_channel_src_addr_increment(n) \ ++ ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_SAI ) ++ ++#define __dmac_channel_set_doorbell(n) \ ++ ( REG_DMAC_DMADBSR((n)/HALF_DMA_NUM) = (1 << ((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM)) ) ++ ++#define __dmac_channel_irq_detected(n) ( REG_DMAC_DMAIPR((n)/HALF_DMA_NUM) & (1 << ((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM)) ) ++#define __dmac_channel_ack_irq(n) ( REG_DMAC_DMAIPR((n)/HALF_DMA_NUM) &= ~(1 <<((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM)) ) ++ ++static __inline__ int __dmac_get_irq(void) ++{ ++ int i; ++ for (i = 0; i < MAX_DMA_NUM; i++) ++ if (__dmac_channel_irq_detected(i)) ++ return i; ++ return -1; ++} ++ ++ ++/*************************************************************************** ++ * AIC (AC'97 & I2S Controller) ++ ***************************************************************************/ ++ ++#define __aic_enable() ( REG_AIC_FR |= AIC_FR_ENB ) ++#define __aic_disable() ( REG_AIC_FR &= ~AIC_FR_ENB ) ++ ++#define __aic_select_ac97() ( REG_AIC_FR &= ~AIC_FR_AUSEL ) ++#define __aic_select_i2s() ( REG_AIC_FR |= AIC_FR_AUSEL ) ++ ++#define __aic_play_zero() ( REG_AIC_FR &= ~AIC_FR_LSMP ) ++#define __aic_play_lastsample() ( REG_AIC_FR |= AIC_FR_LSMP ) ++ ++#define __i2s_as_master() ( REG_AIC_FR |= AIC_FR_BCKD | AIC_FR_SYNCD ) ++#define __i2s_as_slave() ( REG_AIC_FR &= ~(AIC_FR_BCKD | AIC_FR_SYNCD) ) ++#define __aic_reset_status() ( REG_AIC_FR & AIC_FR_RST ) ++ ++#define __aic_reset() \ ++do { \ ++ REG_AIC_FR |= AIC_FR_RST; \ ++} while(0) ++ ++ ++#define __aic_set_transmit_trigger(n) \ ++do { \ ++ REG_AIC_FR &= ~AIC_FR_TFTH_MASK; \ ++ REG_AIC_FR |= ((n) << AIC_FR_TFTH_BIT); \ ++} while(0) ++ ++#define __aic_set_receive_trigger(n) \ ++do { \ ++ REG_AIC_FR &= ~AIC_FR_RFTH_MASK; \ ++ REG_AIC_FR |= ((n) << AIC_FR_RFTH_BIT); \ ++} while(0) ++ ++#define __aic_enable_record() ( REG_AIC_CR |= AIC_CR_EREC ) ++#define __aic_disable_record() ( REG_AIC_CR &= ~AIC_CR_EREC ) ++#define __aic_enable_replay() ( REG_AIC_CR |= AIC_CR_ERPL ) ++#define __aic_disable_replay() ( REG_AIC_CR &= ~AIC_CR_ERPL ) ++#define __aic_enable_loopback() ( REG_AIC_CR |= AIC_CR_ENLBF ) ++#define __aic_disable_loopback() ( REG_AIC_CR &= ~AIC_CR_ENLBF ) ++ ++#define __aic_flush_fifo() ( REG_AIC_CR |= AIC_CR_FLUSH ) ++#define __aic_unflush_fifo() ( REG_AIC_CR &= ~AIC_CR_FLUSH ) ++ ++#define __aic_enable_transmit_intr() \ ++ ( REG_AIC_CR |= (AIC_CR_ETFS | AIC_CR_ETUR) ) ++#define __aic_disable_transmit_intr() \ ++ ( REG_AIC_CR &= ~(AIC_CR_ETFS | AIC_CR_ETUR) ) ++#define __aic_enable_receive_intr() \ ++ ( REG_AIC_CR |= (AIC_CR_ERFS | AIC_CR_EROR) ) ++#define __aic_disable_receive_intr() \ ++ ( REG_AIC_CR &= ~(AIC_CR_ERFS | AIC_CR_EROR) ) ++ ++#define __aic_enable_transmit_dma() ( REG_AIC_CR |= AIC_CR_TDMS ) ++#define __aic_disable_transmit_dma() ( REG_AIC_CR &= ~AIC_CR_TDMS ) ++#define __aic_enable_receive_dma() ( REG_AIC_CR |= AIC_CR_RDMS ) ++#define __aic_disable_receive_dma() ( REG_AIC_CR &= ~AIC_CR_RDMS ) ++ ++#define __aic_enable_mono2stereo() ( REG_AIC_CR |= AIC_CR_M2S ) ++#define __aic_disable_mono2stereo() ( REG_AIC_CR &= ~AIC_CR_M2S ) ++#define __aic_enable_byteswap() ( REG_AIC_CR |= AIC_CR_ENDSW ) ++#define __aic_disable_byteswap() ( REG_AIC_CR &= ~AIC_CR_ENDSW ) ++#define __aic_enable_unsignadj() ( REG_AIC_CR |= AIC_CR_AVSTSU ) ++#define __aic_disable_unsignadj() ( REG_AIC_CR &= ~AIC_CR_AVSTSU ) ++ ++#define AC97_PCM_XS_L_FRONT AIC_ACCR1_XS_SLOT3 ++#define AC97_PCM_XS_R_FRONT AIC_ACCR1_XS_SLOT4 ++#define AC97_PCM_XS_CENTER AIC_ACCR1_XS_SLOT6 ++#define AC97_PCM_XS_L_SURR AIC_ACCR1_XS_SLOT7 ++#define AC97_PCM_XS_R_SURR AIC_ACCR1_XS_SLOT8 ++#define AC97_PCM_XS_LFE AIC_ACCR1_XS_SLOT9 ++ ++#define AC97_PCM_RS_L_FRONT AIC_ACCR1_RS_SLOT3 ++#define AC97_PCM_RS_R_FRONT AIC_ACCR1_RS_SLOT4 ++#define AC97_PCM_RS_CENTER AIC_ACCR1_RS_SLOT6 ++#define AC97_PCM_RS_L_SURR AIC_ACCR1_RS_SLOT7 ++#define AC97_PCM_RS_R_SURR AIC_ACCR1_RS_SLOT8 ++#define AC97_PCM_RS_LFE AIC_ACCR1_RS_SLOT9 ++ ++#define __ac97_set_xs_none() ( REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK ) ++#define __ac97_set_xs_mono() \ ++do { \ ++ REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK; \ ++ REG_AIC_ACCR1 |= AC97_PCM_XS_R_FRONT; \ ++} while(0) ++#define __ac97_set_xs_stereo() \ ++do { \ ++ REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK; \ ++ REG_AIC_ACCR1 |= AC97_PCM_XS_L_FRONT | AC97_PCM_XS_R_FRONT; \ ++} while(0) ++ ++/* In fact, only stereo is support now. */ ++#define __ac97_set_rs_none() ( REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK ) ++#define __ac97_set_rs_mono() \ ++do { \ ++ REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK; \ ++ REG_AIC_ACCR1 |= AC97_PCM_RS_R_FRONT; \ ++} while(0) ++#define __ac97_set_rs_stereo() \ ++do { \ ++ REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK; \ ++ REG_AIC_ACCR1 |= AC97_PCM_RS_L_FRONT | AC97_PCM_RS_R_FRONT; \ ++} while(0) ++ ++#define __ac97_warm_reset_codec() \ ++ do { \ ++ REG_AIC_ACCR2 |= AIC_ACCR2_SA; \ ++ REG_AIC_ACCR2 |= AIC_ACCR2_SS; \ ++ udelay(2); \ ++ REG_AIC_ACCR2 &= ~AIC_ACCR2_SS; \ ++ REG_AIC_ACCR2 &= ~AIC_ACCR2_SA; \ ++ } while (0) ++ ++#define __ac97_cold_reset_codec() \ ++ do { \ ++ REG_AIC_ACCR2 |= AIC_ACCR2_SR; \ ++ udelay(2); \ ++ REG_AIC_ACCR2 &= ~AIC_ACCR2_SR; \ ++ } while (0) ++ ++/* n=8,16,18,20 */ ++#define __ac97_set_iass(n) \ ++ ( REG_AIC_ACCR2 = (REG_AIC_ACCR2 & ~AIC_ACCR2_IASS_MASK) | AIC_ACCR2_IASS_##n##BIT ) ++#define __ac97_set_oass(n) \ ++ ( REG_AIC_ACCR2 = (REG_AIC_ACCR2 & ~AIC_ACCR2_OASS_MASK) | AIC_ACCR2_OASS_##n##BIT ) ++ ++#define __i2s_select_i2s() ( REG_AIC_I2SCR &= ~AIC_I2SCR_AMSL ) ++#define __i2s_select_msbjustified() ( REG_AIC_I2SCR |= AIC_I2SCR_AMSL ) ++ ++/* n=8,16,18,20,24 */ ++/*#define __i2s_set_sample_size(n) \ ++ ( REG_AIC_I2SCR |= (REG_AIC_I2SCR & ~AIC_I2SCR_WL_MASK) | AIC_I2SCR_WL_##n##BIT )*/ ++ ++#define __i2s_set_oss_sample_size(n) \ ++ ( REG_AIC_CR = (REG_AIC_CR & ~AIC_CR_OSS_MASK) | AIC_CR_OSS_##n##BIT ) ++#define __i2s_set_iss_sample_size(n) \ ++ ( REG_AIC_CR = (REG_AIC_CR & ~AIC_CR_ISS_MASK) | AIC_CR_ISS_##n##BIT ) ++ ++#define __i2s_stop_bitclk() ( REG_AIC_I2SCR |= AIC_I2SCR_STPBK ) ++#define __i2s_start_bitclk() ( REG_AIC_I2SCR &= ~AIC_I2SCR_STPBK ) ++ ++#define __aic_transmit_request() ( REG_AIC_SR & AIC_SR_TFS ) ++#define __aic_receive_request() ( REG_AIC_SR & AIC_SR_RFS ) ++#define __aic_transmit_underrun() ( REG_AIC_SR & AIC_SR_TUR ) ++#define __aic_receive_overrun() ( REG_AIC_SR & AIC_SR_ROR ) ++ ++#define __aic_clear_errors() ( REG_AIC_SR &= ~(AIC_SR_TUR | AIC_SR_ROR) ) ++ ++#define __aic_get_transmit_resident() \ ++ ( (REG_AIC_SR & AIC_SR_TFL_MASK) >> AIC_SR_TFL_BIT ) ++#define __aic_get_receive_count() \ ++ ( (REG_AIC_SR & AIC_SR_RFL_MASK) >> AIC_SR_RFL_BIT ) ++ ++#define __ac97_command_transmitted() ( REG_AIC_ACSR & AIC_ACSR_CADT ) ++#define __ac97_status_received() ( REG_AIC_ACSR & AIC_ACSR_SADR ) ++#define __ac97_status_receive_timeout() ( REG_AIC_ACSR & AIC_ACSR_RSTO ) ++#define __ac97_codec_is_low_power_mode() ( REG_AIC_ACSR & AIC_ACSR_CLPM ) ++#define __ac97_codec_is_ready() ( REG_AIC_ACSR & AIC_ACSR_CRDY ) ++#define __ac97_slot_error_detected() ( REG_AIC_ACSR & AIC_ACSR_SLTERR ) ++#define __ac97_clear_slot_error() ( REG_AIC_ACSR &= ~AIC_ACSR_SLTERR ) ++ ++#define __i2s_is_busy() ( REG_AIC_I2SSR & AIC_I2SSR_BSY ) ++ ++#define CODEC_READ_CMD (1 << 19) ++#define CODEC_WRITE_CMD (0 << 19) ++#define CODEC_REG_INDEX_BIT 12 ++#define CODEC_REG_INDEX_MASK (0x7f << CODEC_REG_INDEX_BIT) /* 18:12 */ ++#define CODEC_REG_DATA_BIT 4 ++#define CODEC_REG_DATA_MASK (0x0ffff << 4) /* 19:4 */ ++ ++#define __ac97_out_rcmd_addr(reg) \ ++do { \ ++ REG_AIC_ACCAR = CODEC_READ_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ ++} while (0) ++ ++#define __ac97_out_wcmd_addr(reg) \ ++do { \ ++ REG_AIC_ACCAR = CODEC_WRITE_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ ++} while (0) ++ ++#define __ac97_out_data(value) \ ++do { \ ++ REG_AIC_ACCDR = ((value) << CODEC_REG_DATA_BIT); \ ++} while (0) ++ ++#define __ac97_in_data() \ ++ ( (REG_AIC_ACSDR & CODEC_REG_DATA_MASK) >> CODEC_REG_DATA_BIT ) ++ ++#define __ac97_in_status_addr() \ ++ ( (REG_AIC_ACSAR & CODEC_REG_INDEX_MASK) >> CODEC_REG_INDEX_BIT ) ++ ++#define __i2s_set_sample_rate(i2sclk, sync) \ ++ ( REG_AIC_I2SDIV = ((i2sclk) / (4*64)) / (sync) ) ++ ++#define __aic_write_tfifo(v) ( REG_AIC_DR = (v) ) ++#define __aic_read_rfifo() ( REG_AIC_DR ) ++ ++#define __aic_internal_codec() ( REG_AIC_FR |= AIC_FR_ICDC ) ++#define __aic_external_codec() ( REG_AIC_FR &= ~AIC_FR_ICDC ) ++ ++// ++// Define next ops for AC97 compatible ++// ++ ++#define AC97_ACSR AIC_ACSR ++ ++#define __ac97_enable() __aic_enable(); __aic_select_ac97() ++#define __ac97_disable() __aic_disable() ++#define __ac97_reset() __aic_reset() ++ ++#define __ac97_set_transmit_trigger(n) __aic_set_transmit_trigger(n) ++#define __ac97_set_receive_trigger(n) __aic_set_receive_trigger(n) ++ ++#define __ac97_enable_record() __aic_enable_record() ++#define __ac97_disable_record() __aic_disable_record() ++#define __ac97_enable_replay() __aic_enable_replay() ++#define __ac97_disable_replay() __aic_disable_replay() ++#define __ac97_enable_loopback() __aic_enable_loopback() ++#define __ac97_disable_loopback() __aic_disable_loopback() ++ ++#define __ac97_enable_transmit_dma() __aic_enable_transmit_dma() ++#define __ac97_disable_transmit_dma() __aic_disable_transmit_dma() ++#define __ac97_enable_receive_dma() __aic_enable_receive_dma() ++#define __ac97_disable_receive_dma() __aic_disable_receive_dma() ++ ++#define __ac97_transmit_request() __aic_transmit_request() ++#define __ac97_receive_request() __aic_receive_request() ++#define __ac97_transmit_underrun() __aic_transmit_underrun() ++#define __ac97_receive_overrun() __aic_receive_overrun() ++ ++#define __ac97_clear_errors() __aic_clear_errors() ++ ++#define __ac97_get_transmit_resident() __aic_get_transmit_resident() ++#define __ac97_get_receive_count() __aic_get_receive_count() ++ ++#define __ac97_enable_transmit_intr() __aic_enable_transmit_intr() ++#define __ac97_disable_transmit_intr() __aic_disable_transmit_intr() ++#define __ac97_enable_receive_intr() __aic_enable_receive_intr() ++#define __ac97_disable_receive_intr() __aic_disable_receive_intr() ++ ++#define __ac97_write_tfifo(v) __aic_write_tfifo(v) ++#define __ac97_read_rfifo() __aic_read_rfifo() ++ ++// ++// Define next ops for I2S compatible ++// ++ ++#define I2S_ACSR AIC_I2SSR ++ ++#define __i2s_enable() __aic_enable(); __aic_select_i2s() ++#define __i2s_disable() __aic_disable() ++#define __i2s_reset() __aic_reset() ++ ++#define __i2s_set_transmit_trigger(n) __aic_set_transmit_trigger(n) ++#define __i2s_set_receive_trigger(n) __aic_set_receive_trigger(n) ++ ++#define __i2s_enable_record() __aic_enable_record() ++#define __i2s_disable_record() __aic_disable_record() ++#define __i2s_enable_replay() __aic_enable_replay() ++#define __i2s_disable_replay() __aic_disable_replay() ++#define __i2s_enable_loopback() __aic_enable_loopback() ++#define __i2s_disable_loopback() __aic_disable_loopback() ++ ++#define __i2s_enable_transmit_dma() __aic_enable_transmit_dma() ++#define __i2s_disable_transmit_dma() __aic_disable_transmit_dma() ++#define __i2s_enable_receive_dma() __aic_enable_receive_dma() ++#define __i2s_disable_receive_dma() __aic_disable_receive_dma() ++ ++#define __i2s_transmit_request() __aic_transmit_request() ++#define __i2s_receive_request() __aic_receive_request() ++#define __i2s_transmit_underrun() __aic_transmit_underrun() ++#define __i2s_receive_overrun() __aic_receive_overrun() ++ ++#define __i2s_clear_errors() __aic_clear_errors() ++ ++#define __i2s_get_transmit_resident() __aic_get_transmit_resident() ++#define __i2s_get_receive_count() __aic_get_receive_count() ++ ++#define __i2s_enable_transmit_intr() __aic_enable_transmit_intr() ++#define __i2s_disable_transmit_intr() __aic_disable_transmit_intr() ++#define __i2s_enable_receive_intr() __aic_enable_receive_intr() ++#define __i2s_disable_receive_intr() __aic_disable_receive_intr() ++ ++#define __i2s_write_tfifo(v) __aic_write_tfifo(v) ++#define __i2s_read_rfifo() __aic_read_rfifo() ++ ++#define __i2s_reset_codec() \ ++ do { \ ++ } while (0) ++ ++/************************************************************************* ++ * PCM Controller operation ++ *************************************************************************/ ++ ++#define __pcm_enable() ( REG_PCM_CTL |= PCM_CTL_PCMEN ) ++#define __pcm_disable() ( REG_PCM_CTL &= ~PCM_CTL_PCMEN ) ++ ++#define __pcm_clk_enable() ( REG_PCM_CTL |= PCM_CTL_CLKEN ) ++#define __pcm_clk_disable() ( REG_PCM_CTL &= ~PCM_CTL_CLKEN ) ++ ++#define __pcm_reset() ( REG_PCM_CTL |= PCM_CTL_RST ) ++#define __pcm_flush_fifo() ( REG_PCM_CTL |= PCM_CTL_FLUSH ) ++ ++#define __pcm_enable_record() ( REG_PCM_CTL |= PCM_CTL_EREC ) ++#define __pcm_disable_record() ( REG_PCM_CTL &= ~PCM_CTL_EREC ) ++#define __pcm_enable_playback() ( REG_PCM_CTL |= PCM_CTL_ERPL ) ++#define __pcm_disable_playback() ( REG_PCM_CTL &= ~PCM_CTL_ERPL ) ++ ++#define __pcm_enable_rxfifo() __pcm_enable_record() ++#define __pcm_disable_rxfifo() __pcm_disable_record() ++#define __pcm_enable_txfifo() __pcm_enable_playback() ++#define __pcm_disable_txfifo() __pcm_disable_playback() ++ ++#define __pcm_last_sample() ( REG_PCM_CTL |= PCM_CTL_LSMP ) ++#define __pcm_zero_sample() ( REG_PCM_CTL &= ~PCM_CTL_LSMP ) ++ ++#define __pcm_enable_transmit_dma() ( REG_PCM_CTL |= PCM_CTL_ETDMA ) ++#define __pcm_disable_transmit_dma() ( REG_PCM_CTL &= ~PCM_CTL_ETDMA ) ++#define __pcm_enable_receive_dma() ( REG_PCM_CTL |= PCM_CTL_ERDMA ) ++#define __pcm_disable_receive_dma() ( REG_PCM_CTL &= ~PCM_CTL_ERDMA ) ++ ++#define __pcm_as_master() ( REG_PCM_CFG &= PCM_CFG_MODE ) ++#define __pcm_as_slave() ( REG_PCM_CFG |= ~PCM_CFG_MODE ) ++ ++#define __pcm_set_transmit_trigger(n) \ ++do { \ ++ REG_PCM_CFG &= ~PCM_CFG_TFTH_MASK; \ ++ REG_PCM_CFG |= ((n) << PCM_CFG_TFTH_BIT); \ ++} while(0) ++ ++#define __pcm_set_receive_trigger(n) \ ++do { \ ++ REG_PCM_CFG &= ~PCM_CFG_RFTH_MASK; \ ++ REG_PCM_CFG |= ((n) << PCM_CFG_RFTH_BIT); \ ++} while(0) ++ ++#define __pcm_omsb_same_sync() ( REG_PCM_CFG &= ~PCM_CFG_OMSBPOS ) ++#define __pcm_omsb_next_sync() ( REG_PCM_CFG |= PCM_CFG_OMSBPOS ) ++ ++#define __pcm_imsb_same_sync() ( REG_PCM_CFG &= ~PCM_CFG_IMSBPOS ) ++#define __pcm_imsb_next_sync() ( REG_PCM_CFG |= PCM_CFG_IMSBPOS ) ++ ++/* set input sample size 8 or 16*/ ++#define __pcm_set_iss(n) \ ++( REG_PCM_CFG = (REG_PCM_CFG & ~PCM_CFG_ISS_MASK) | PCM_CFG_ISS_##n ) ++/* set output sample size 8 or 16*/ ++#define __pcm_set_oss(n) \ ++( REG_PCM_CFG = (REG_PCM_CFG & ~PCM_CFG_OSS_MASK) | PCM_CFG_OSS_##n ) ++ ++#define __pcm_set_valid_slot(n) \ ++( REG_PCM_CFG = (REG_PCM_CFG & ~PCM_CFG_SLOT_MASK) | PCM_CFG_SLOT_##n ) ++ ++#define __pcm_write_data(v) ( REG_PCM_DP = (v) ) ++#define __pcm_read_data() ( REG_PCM_DP ) ++ ++#define __pcm_enable_tfs_intr() ( REG_PCM_INTC |= PCM_INTC_ETFS ) ++#define __pcm_disable_tfs_intr() ( REG_PCM_INTC &= ~PCM_INTC_ETFS ) ++ ++#define __pcm_enable_tur_intr() ( REG_PCM_INTC |= PCM_INTC_ETUR ) ++#define __pcm_disable_tur_intr() ( REG_PCM_INTC &= ~PCM_INTC_ETUR ) ++ ++#define __pcm_enable_rfs_intr() ( REG_PCM_INTC |= PCM_INTC_ERFS ) ++#define __pcm_disable_rfs_intr() ( REG_PCM_INTC &= ~PCM_INTC_ERFS ) ++ ++#define __pcm_enable_ror_intr() ( REG_PCM_INTC |= PCM_INTC_EROR ) ++#define __pcm_disable_ror_intr() ( REG_PCM_INTC &= ~PCM_INTC_EROR ) ++ ++#define __pcm_ints_valid_tx() \ ++( ((REG_PCM_INTS & PCM_INTS_TFL_MASK) >> PCM_INTS_TFL_BIT) ) ++#define __pcm_ints_valid_rx() \ ++( ((REG_PCM_INTS & PCM_INTS_RFL_MASK) >> PCM_INTS_RFL_BIT) ) ++ ++#define __pcm_set_clk_div(n) \ ++( REG_PCM_DIV = (REG_PCM_DIV & ~PCM_DIV_CLKDIV_MASK) | ((n) << PCM_DIV_CLKDIV_BIT) ) ++ ++/* sysclk(cpm_pcm_sysclk) Hz is created by cpm logic, and pcmclk Hz is the pcm in/out clock wanted */ ++#define __pcm_set_clk_rate(sysclk, pcmclk) \ ++__pcm_set_clk_div(((sysclk) / (pcmclk) - 1)) ++ ++#define __pcm_set_sync_div(n) \ ++( REG_PCM_DIV = (REG_PCM_DIV & ~PCM_DIV_SYNDIV_MASK) | ((n) << PCM_DIV_SYNDIV_BIT) ) ++ ++/* pcmclk is source clock Hz, and sync is the frame sync clock Hz wanted */ ++#define __pcm_set_sync_rate(pcmclk, sync) \ ++__pcm_set_sync_div(((pcmclk) / (8 * (sync)) - 1)) ++ ++ /* set sync length in pcmclk n = 0 ... 63 */ ++#define __pcm_set_sync_len(n) \ ++( REG_PCM_DIV = (REG_PCM_DIV & ~PCM_DIV_SYNL_MASK) | (n << PCM_DIV_SYNL_BIT) ) ++ ++ ++/*************************************************************************** ++ * ICDC ++ ***************************************************************************/ ++#define __i2s_internal_codec() __aic_internal_codec() ++#define __i2s_external_codec() __aic_external_codec() ++ ++#define __icdc_clk_ready() ( REG_ICDC_CKCFG & ICDC_CKCFG_CKRDY ) ++#define __icdc_sel_adc() ( REG_ICDC_CKCFG |= ICDC_CKCFG_SELAD ) ++#define __icdc_sel_dac() ( REG_ICDC_CKCFG &= ~ICDC_CKCFG_SELAD ) ++ ++#define __icdc_set_rgwr() ( REG_ICDC_RGADW |= ICDC_RGADW_RGWR ) ++#define __icdc_clear_rgwr() ( REG_ICDC_RGADW &= ~ICDC_RGADW_RGWR ) ++#define __icdc_rgwr_ready() ( REG_ICDC_RGADW & ICDC_RGADW_RGWR ) ++ ++#define __icdc_set_addr(n) \ ++do { \ ++ REG_ICDC_RGADW &= ~ICDC_RGADW_RGADDR_MASK; \ ++ REG_ICDC_RGADW |= (n) << ICDC_RGADW_RGADDR_BIT; \ ++} while(0) ++ ++#define __icdc_set_cmd(n) \ ++do { \ ++ REG_ICDC_RGADW &= ~ICDC_RGADW_RGDIN_MASK; \ ++ REG_ICDC_RGADW |= (n) << ICDC_RGADW_RGDIN_BIT; \ ++} while(0) ++ ++#define __icdc_irq_pending() ( REG_ICDC_RGDATA & ICDC_RGDATA_IRQ ) ++#define __icdc_get_value() ( REG_ICDC_RGDATA & ICDC_RGDATA_RGDOUT_MASK ) ++ ++/*************************************************************************** ++ * INTC ++ ***************************************************************************/ ++#define __intc_unmask_irq(n) ( REG_INTC_IMCR = (1 << (n)) ) ++#define __intc_mask_irq(n) ( REG_INTC_IMSR = (1 << (n)) ) ++#define __intc_ack_irq(n) ( REG_INTC_IPR = (1 << (n)) ) /* A dummy ack, as the Pending Register is Read Only. Should we remove __intc_ack_irq() */ ++ ++ ++/*************************************************************************** ++ * I2C ++ ***************************************************************************/ ++ ++#define __i2c_enable() ( REG_I2C_CR |= I2C_CR_I2CE ) ++#define __i2c_disable() ( REG_I2C_CR &= ~I2C_CR_I2CE ) ++ ++#define __i2c_send_start() ( REG_I2C_CR |= I2C_CR_STA ) ++#define __i2c_send_stop() ( REG_I2C_CR |= I2C_CR_STO ) ++#define __i2c_send_ack() ( REG_I2C_CR &= ~I2C_CR_AC ) ++#define __i2c_send_nack() ( REG_I2C_CR |= I2C_CR_AC ) ++ ++#define __i2c_set_drf() ( REG_I2C_SR |= I2C_SR_DRF ) ++#define __i2c_clear_drf() ( REG_I2C_SR &= ~I2C_SR_DRF ) ++#define __i2c_check_drf() ( REG_I2C_SR & I2C_SR_DRF ) ++ ++#define __i2c_received_ack() ( !(REG_I2C_SR & I2C_SR_ACKF) ) ++#define __i2c_is_busy() ( REG_I2C_SR & I2C_SR_BUSY ) ++#define __i2c_transmit_ended() ( REG_I2C_SR & I2C_SR_TEND ) ++ ++#define __i2c_set_clk(dev_clk, i2c_clk) \ ++ ( REG_I2C_GR = (dev_clk) / (16*(i2c_clk)) - 1 ) ++ ++#define __i2c_read() ( REG_I2C_DR ) ++#define __i2c_write(val) ( REG_I2C_DR = (val) ) ++ ++ ++/*************************************************************************** ++ * MSC ++ ***************************************************************************/ ++/* n = 0, 1 (MSC0, MSC1) */ ++ ++#define __msc_start_op(n) \ ++ ( REG_MSC_STRPCL(n) = MSC_STRPCL_START_OP | MSC_STRPCL_CLOCK_CONTROL_START ) ++ ++#define __msc_set_resto(n, to) ( REG_MSC_RESTO(n) = to ) ++#define __msc_set_rdto(n, to) ( REG_MSC_RDTO(n) = to ) ++#define __msc_set_cmd(n, cmd) ( REG_MSC_CMD(n) = cmd ) ++#define __msc_set_arg(n, arg) ( REG_MSC_ARG(n) = arg ) ++#define __msc_set_nob(n, nob) ( REG_MSC_NOB(n) = nob ) ++#define __msc_get_nob(n) ( REG_MSC_NOB(n) ) ++#define __msc_set_blklen(n, len) ( REG_MSC_BLKLEN(n) = len ) ++#define __msc_set_cmdat(n, cmdat) ( REG_MSC_CMDAT(n) = cmdat ) ++#define __msc_set_cmdat_ioabort(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_IO_ABORT ) ++#define __msc_clear_cmdat_ioabort(n) ( REG_MSC_CMDAT(n) &= ~MSC_CMDAT_IO_ABORT ) ++ ++#define __msc_set_cmdat_bus_width1(n) \ ++do { \ ++ REG_MSC_CMDAT(n) &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ ++ REG_MSC_CMDAT(n) |= MSC_CMDAT_BUS_WIDTH_1BIT; \ ++} while(0) ++ ++#define __msc_set_cmdat_bus_width4(n) \ ++do { \ ++ REG_MSC_CMDAT(n) &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ ++ REG_MSC_CMDAT(n) |= MSC_CMDAT_BUS_WIDTH_4BIT; \ ++} while(0) ++ ++#define __msc_set_cmdat_dma_en(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_DMA_EN ) ++#define __msc_set_cmdat_init(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_INIT ) ++#define __msc_set_cmdat_busy(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_BUSY ) ++#define __msc_set_cmdat_stream(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_STREAM_BLOCK ) ++#define __msc_set_cmdat_block(n) ( REG_MSC_CMDAT(n) &= ~MSC_CMDAT_STREAM_BLOCK ) ++#define __msc_set_cmdat_read(n) ( REG_MSC_CMDAT(n) &= ~MSC_CMDAT_WRITE_READ ) ++#define __msc_set_cmdat_write(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_WRITE_READ ) ++#define __msc_set_cmdat_data_en(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_DATA_EN ) ++ ++/* r is MSC_CMDAT_RESPONSE_FORMAT_Rx or MSC_CMDAT_RESPONSE_FORMAT_NONE */ ++#define __msc_set_cmdat_res_format(n, r) \ ++do { \ ++ REG_MSC_CMDAT(n) &= ~MSC_CMDAT_RESPONSE_FORMAT_MASK; \ ++ REG_MSC_CMDAT(n) |= (r); \ ++} while(0) ++ ++#define __msc_clear_cmdat(n) \ ++ REG_MSC_CMDAT(n) &= ~( MSC_CMDAT_IO_ABORT | MSC_CMDAT_DMA_EN | MSC_CMDAT_INIT| \ ++ MSC_CMDAT_BUSY | MSC_CMDAT_STREAM_BLOCK | MSC_CMDAT_WRITE_READ | \ ++ MSC_CMDAT_DATA_EN | MSC_CMDAT_RESPONSE_FORMAT_MASK ) ++ ++#define __msc_get_imask(n) ( REG_MSC_IMASK(n) ) ++#define __msc_mask_all_intrs(n) ( REG_MSC_IMASK(n) = 0xff ) ++#define __msc_unmask_all_intrs(n) ( REG_MSC_IMASK(n) = 0x00 ) ++#define __msc_mask_rd(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_RXFIFO_RD_REQ ) ++#define __msc_unmask_rd(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_RXFIFO_RD_REQ ) ++#define __msc_mask_wr(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_TXFIFO_WR_REQ ) ++#define __msc_unmask_wr(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_TXFIFO_WR_REQ ) ++#define __msc_mask_endcmdres(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_END_CMD_RES ) ++#define __msc_unmask_endcmdres(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_END_CMD_RES ) ++#define __msc_mask_datatrandone(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_DATA_TRAN_DONE ) ++#define __msc_unmask_datatrandone(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_DATA_TRAN_DONE ) ++#define __msc_mask_prgdone(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_PRG_DONE ) ++#define __msc_unmask_prgdone(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_PRG_DONE ) ++ ++/* m=0,1,2,3,4,5,6,7 */ ++#define __msc_set_clkrt(n, m) \ ++do { \ ++ REG_MSC_CLKRT(n) = m; \ ++} while(0) ++ ++#define __msc_get_ireg(n) ( REG_MSC_IREG(n) ) ++#define __msc_ireg_rd(n) ( REG_MSC_IREG(n) & MSC_IREG_RXFIFO_RD_REQ ) ++#define __msc_ireg_wr(n) ( REG_MSC_IREG(n) & MSC_IREG_TXFIFO_WR_REQ ) ++#define __msc_ireg_end_cmd_res(n) ( REG_MSC_IREG(n) & MSC_IREG_END_CMD_RES ) ++#define __msc_ireg_data_tran_done(n) ( REG_MSC_IREG(n) & MSC_IREG_DATA_TRAN_DONE ) ++#define __msc_ireg_prg_done(n) ( REG_MSC_IREG(n) & MSC_IREG_PRG_DONE ) ++#define __msc_ireg_clear_end_cmd_res(n) ( REG_MSC_IREG(n) = MSC_IREG_END_CMD_RES ) ++#define __msc_ireg_clear_data_tran_done(n) ( REG_MSC_IREG(n) = MSC_IREG_DATA_TRAN_DONE ) ++#define __msc_ireg_clear_prg_done(n) ( REG_MSC_IREG(n) = MSC_IREG_PRG_DONE ) ++ ++#define __msc_get_stat(n) ( REG_MSC_STAT(n) ) ++#define __msc_stat_not_end_cmd_res(n) ( (REG_MSC_STAT(n) & MSC_STAT_END_CMD_RES) == 0) ++#define __msc_stat_crc_err(n) \ ++ ( REG_MSC_STAT(n) & (MSC_STAT_CRC_RES_ERR | MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR_YES) ) ++#define __msc_stat_res_crc_err(n) ( REG_MSC_STAT(n) & MSC_STAT_CRC_RES_ERR ) ++#define __msc_stat_rd_crc_err(n) ( REG_MSC_STAT(n) & MSC_STAT_CRC_READ_ERROR ) ++#define __msc_stat_wr_crc_err(n) ( REG_MSC_STAT(n) & MSC_STAT_CRC_WRITE_ERROR_YES ) ++#define __msc_stat_resto_err(n) ( REG_MSC_STAT(n) & MSC_STAT_TIME_OUT_RES ) ++#define __msc_stat_rdto_err(n) ( REG_MSC_STAT(n) & MSC_STAT_TIME_OUT_READ ) ++ ++#define __msc_rd_resfifo(n) ( REG_MSC_RES(n) ) ++#define __msc_rd_rxfifo(n) ( REG_MSC_RXFIFO(n) ) ++#define __msc_wr_txfifo(n, v) ( REG_MSC_TXFIFO(n) = v ) ++ ++#define __msc_reset(n) \ ++do { \ ++ REG_MSC_STRPCL(n) = MSC_STRPCL_RESET; \ ++ while (REG_MSC_STAT(n) & MSC_STAT_IS_RESETTING); \ ++} while (0) ++ ++#define __msc_start_clk(n) \ ++do { \ ++ REG_MSC_STRPCL(n) = MSC_STRPCL_CLOCK_CONTROL_START; \ ++} while (0) ++ ++#define __msc_stop_clk(n) \ ++do { \ ++ REG_MSC_STRPCL(n) = MSC_STRPCL_CLOCK_CONTROL_STOP; \ ++} while (0) ++ ++#define MMC_CLK 19169200 ++#define SD_CLK 24576000 ++ ++/* msc_clk should little than pclk and little than clk retrieve from card */ ++#define __msc_calc_clk_divisor(type,dev_clk,msc_clk,lv) \ ++do { \ ++ unsigned int rate, pclk, i; \ ++ pclk = dev_clk; \ ++ rate = type?SD_CLK:MMC_CLK; \ ++ if (msc_clk && msc_clk < pclk) \ ++ pclk = msc_clk; \ ++ i = 0; \ ++ while (pclk < rate) \ ++ { \ ++ i ++; \ ++ rate >>= 1; \ ++ } \ ++ lv = i; \ ++} while(0) ++ ++/* divide rate to little than or equal to 400kHz */ ++#define __msc_calc_slow_clk_divisor(type, lv) \ ++do { \ ++ unsigned int rate, i; \ ++ rate = (type?SD_CLK:MMC_CLK)/1000/400; \ ++ i = 0; \ ++ while (rate > 0) \ ++ { \ ++ rate >>= 1; \ ++ i ++; \ ++ } \ ++ lv = i; \ ++} while(0) ++ ++ ++/*************************************************************************** ++ * SSI (Synchronous Serial Interface) ++ ***************************************************************************/ ++/* n = 0, 1 (SSI0, SSI1) */ ++#define __ssi_enable(n) ( REG_SSI_CR0(n) |= SSI_CR0_SSIE ) ++#define __ssi_disable(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_SSIE ) ++#define __ssi_select_ce(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_FSEL ) ++ ++#define __ssi_normal_mode(n) ( REG_SSI_ITR(n) &= ~SSI_ITR_IVLTM_MASK ) ++ ++#define __ssi_select_ce2(n) \ ++do { \ ++ REG_SSI_CR0(n) |= SSI_CR0_FSEL; \ ++ REG_SSI_CR1(n) &= ~SSI_CR1_MULTS; \ ++} while (0) ++ ++#define __ssi_select_gpc(n) \ ++do { \ ++ REG_SSI_CR0(n) &= ~SSI_CR0_FSEL; \ ++ REG_SSI_CR1(n) |= SSI_CR1_MULTS; \ ++} while (0) ++ ++#define __ssi_underrun_auto_clear(n) \ ++do { \ ++ REG_SSI_CR0(n) |= SSI_CR0_EACLRUN; \ ++} while (0) ++ ++#define __ssi_underrun_clear_manually(n) \ ++do { \ ++ REG_SSI_CR0(n) &= ~SSI_CR0_EACLRUN; \ ++} while (0) ++ ++#define __ssi_enable_tx_intr(n) \ ++ ( REG_SSI_CR0(n) |= SSI_CR0_TIE | SSI_CR0_TEIE ) ++ ++#define __ssi_disable_tx_intr(n) \ ++ ( REG_SSI_CR0(n) &= ~(SSI_CR0_TIE | SSI_CR0_TEIE) ) ++ ++#define __ssi_enable_rx_intr(n) \ ++ ( REG_SSI_CR0(n) |= SSI_CR0_RIE | SSI_CR0_REIE ) ++ ++#define __ssi_disable_rx_intr(n) \ ++ ( REG_SSI_CR0(n) &= ~(SSI_CR0_RIE | SSI_CR0_REIE) ) ++ ++#define __ssi_enable_txfifo_half_empty_intr(n) \ ++ ( REG_SSI_CR0(n) |= SSI_CR0_TIE ) ++#define __ssi_disable_txfifo_half_empty_intr(n) \ ++ ( REG_SSI_CR0(n) &= ~SSI_CR0_TIE ) ++#define __ssi_enable_tx_error_intr(n) \ ++ ( REG_SSI_CR0(n) |= SSI_CR0_TEIE ) ++#define __ssi_disable_tx_error_intr(n) \ ++ ( REG_SSI_CR0(n) &= ~SSI_CR0_TEIE ) ++#define __ssi_enable_rxfifo_half_full_intr(n) \ ++ ( REG_SSI_CR0(n) |= SSI_CR0_RIE ) ++#define __ssi_disable_rxfifo_half_full_intr(n) \ ++ ( REG_SSI_CR0(n) &= ~SSI_CR0_RIE ) ++#define __ssi_enable_rx_error_intr(n) \ ++ ( REG_SSI_CR0(n) |= SSI_CR0_REIE ) ++#define __ssi_disable_rx_error_intr(n) \ ++ ( REG_SSI_CR0(n) &= ~SSI_CR0_REIE ) ++ ++#define __ssi_enable_loopback(n) ( REG_SSI_CR0(n) |= SSI_CR0_LOOP ) ++#define __ssi_disable_loopback(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_LOOP ) ++ ++#define __ssi_enable_receive(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_DISREV ) ++#define __ssi_disable_receive(n) ( REG_SSI_CR0(n) |= SSI_CR0_DISREV ) ++ ++#define __ssi_finish_receive(n) \ ++ ( REG_SSI_CR0(n) |= (SSI_CR0_RFINE | SSI_CR0_RFINC) ) ++ ++#define __ssi_disable_recvfinish(n) \ ++ ( REG_SSI_CR0(n) &= ~(SSI_CR0_RFINE | SSI_CR0_RFINC) ) ++ ++#define __ssi_flush_txfifo(n) ( REG_SSI_CR0(n) |= SSI_CR0_TFLUSH ) ++#define __ssi_flush_rxfifo(n) ( REG_SSI_CR0(n) |= SSI_CR0_RFLUSH ) ++ ++#define __ssi_flush_fifo(n) \ ++ ( REG_SSI_CR0(n) |= SSI_CR0_TFLUSH | SSI_CR0_RFLUSH ) ++ ++#define __ssi_finish_transmit(n) ( REG_SSI_CR1(n) &= ~SSI_CR1_UNFIN ) ++#define __ssi_wait_transmit(n) ( REG_SSI_CR1(n) |= SSI_CR1_UNFIN ) ++#define __ssi_use_busy_wait_mode(n) __ssi_wait_transmit(n) ++#define __ssi_unset_busy_wait_mode(n) __ssi_finish_transmit(n) ++ ++#define __ssi_spi_format(n) \ ++ do { \ ++ REG_SSI_CR1(n) &= ~SSI_CR1_FMAT_MASK; \ ++ REG_SSI_CR1(n) |= SSI_CR1_FMAT_SPI; \ ++ REG_SSI_CR1(n) &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK); \ ++ REG_SSI_CR1(n) |= (SSI_CR1_TFVCK_1 | SSI_CR1_TCKFI_1); \ ++ } while (0) ++ ++/* TI's SSP format, must clear SSI_CR1.UNFIN */ ++#define __ssi_ssp_format(n) \ ++ do { \ ++ REG_SSI_CR1(n) &= ~(SSI_CR1_FMAT_MASK | SSI_CR1_UNFIN); \ ++ REG_SSI_CR1(n) |= SSI_CR1_FMAT_SSP; \ ++ } while (0) ++ ++/* National's Microwire format, must clear SSI_CR0.RFINE, and set max delay */ ++#define __ssi_microwire_format(n) \ ++ do { \ ++ REG_SSI_CR1(n) &= ~SSI_CR1_FMAT_MASK; \ ++ REG_SSI_CR1(n) |= SSI_CR1_FMAT_MW1; \ ++ REG_SSI_CR1(n) &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK); \ ++ REG_SSI_CR1(n) |= (SSI_CR1_TFVCK_3 | SSI_CR1_TCKFI_3); \ ++ REG_SSI_CR0(n) &= ~SSI_CR0_RFINE; \ ++ } while (0) ++ ++/* CE# level (FRMHL), CE# in interval time (ITFRM), ++ clock phase and polarity (PHA POL), ++ interval time (SSIITR), interval characters/frame (SSIICR) */ ++ ++/* frmhl,endian,mcom,flen,pha,pol MASK */ ++#define SSICR1_MISC_MASK \ ++ ( SSI_CR1_FRMHL_MASK | SSI_CR1_LFST | SSI_CR1_MCOM_MASK \ ++ | SSI_CR1_FLEN_MASK | SSI_CR1_PHA | SSI_CR1_POL ) ++ ++#define __ssi_spi_set_misc(n,frmhl,endian,flen,mcom,pha,pol) \ ++ do { \ ++ REG_SSI_CR1(n) &= ~SSICR1_MISC_MASK; \ ++ REG_SSI_CR1(n) |= ((frmhl) << 30) | ((endian) << 25) | \ ++ (((mcom) - 1) << 12) | (((flen) - 2) << 4) | \ ++ ((pha) << 1) | (pol); \ ++ } while(0) ++ ++/* Transfer with MSB or LSB first */ ++#define __ssi_set_msb(n) ( REG_SSI_CR1(n) &= ~SSI_CR1_LFST ) ++#define __ssi_set_lsb(n) ( REG_SSI_CR1(n) |= SSI_CR1_LFST ) ++ ++#define __ssi_set_frame_length(n, m) \ ++ REG_SSI_CR1(n) = (REG_SSI_CR1(n) & ~SSI_CR1_FLEN_MASK) | (((m) - 2) << 4) ++ ++/* m = 1 - 16 */ ++#define __ssi_set_microwire_command_length(n,m) \ ++ ( REG_SSI_CR1(n) = ((REG_SSI_CR1(n) & ~SSI_CR1_MCOM_MASK) | SSI_CR1_MCOM_##m##BIT) ) ++ ++/* Set the clock phase for SPI */ ++#define __ssi_set_spi_clock_phase(n, m) \ ++ ( REG_SSI_CR1(n) = ((REG_SSI_CR1(n) & ~SSI_CR1_PHA) | (((m)&0x1)<< 1))) ++ ++/* Set the clock polarity for SPI */ ++#define __ssi_set_spi_clock_polarity(n, p) \ ++ ( REG_SSI_CR1(n) = ((REG_SSI_CR1(n) & ~SSI_CR1_POL) | ((p)&0x1)) ) ++ ++/* SSI tx trigger, m = i x 8 */ ++#define __ssi_set_tx_trigger(n, m) \ ++ do { \ ++ REG_SSI_CR1(n) &= ~SSI_CR1_TTRG_MASK; \ ++ REG_SSI_CR1(n) |= ((m)/8)<> SSI_SR_TFIFONUM_BIT ) ++ ++#define __ssi_get_rxfifo_count(n) \ ++ ( (REG_SSI_SR(n) & SSI_SR_RFIFONUM_MASK) >> SSI_SR_RFIFONUM_BIT ) ++ ++#define __ssi_transfer_end(n) ( REG_SSI_SR(n) & SSI_SR_END ) ++#define __ssi_is_busy(n) ( REG_SSI_SR(n) & SSI_SR_BUSY ) ++ ++#define __ssi_txfifo_full(n) ( REG_SSI_SR(n) & SSI_SR_TFF ) ++#define __ssi_rxfifo_empty(n) ( REG_SSI_SR(n) & SSI_SR_RFE ) ++#define __ssi_rxfifo_half_full(n) ( REG_SSI_SR(n) & SSI_SR_RFHF ) ++#define __ssi_txfifo_half_empty(n) ( REG_SSI_SR(n) & SSI_SR_TFHE ) ++#define __ssi_underrun(n) ( REG_SSI_SR(n) & SSI_SR_UNDR ) ++#define __ssi_overrun(n) ( REG_SSI_SR(n) & SSI_SR_OVER ) ++#define __ssi_clear_underrun(n) ( REG_SSI_SR(n) = ~SSI_SR_UNDR ) ++#define __ssi_clear_overrun(n) ( REG_SSI_SR(n) = ~SSI_SR_OVER ) ++#define __ssi_clear_errors(n) ( REG_SSI_SR(n) &= ~(SSI_SR_UNDR | SSI_SR_OVER) ) ++ ++#define __ssi_set_clk(n, dev_clk, ssi_clk) \ ++ ( REG_SSI_GR(n) = (dev_clk) / (2*(ssi_clk)) - 1 ) ++ ++#define __ssi_receive_data(n) REG_SSI_DR(n) ++#define __ssi_transmit_data(n, v) (REG_SSI_DR(n) = (v)) ++ ++ ++/*************************************************************************** ++ * CIM ++ ***************************************************************************/ ++ ++#define __cim_enable() ( REG_CIM_CTRL |= CIM_CTRL_ENA ) ++#define __cim_disable() ( REG_CIM_CTRL &= ~CIM_CTRL_ENA ) ++ ++/* n = 0, 1, 2, 3 */ ++#define __cim_set_input_data_stream_order(n) \ ++ do { \ ++ REG_CIM_CFG &= CIM_CFG_ORDER_MASK; \ ++ REG_CIM_CFG |= ((n)<>CIM_SIZE_LPF_BIT) ++#define __cim_get_pixel() ((REG_CIM_SIZE&CIM_SIZE_PPL_MASK)>>CIM_SIZE_PPL_BIT) ++ ++#define __cim_set_v_offset(a) ( REG_CIM_OFFSET = (REG_CIM_OFFSET&(~CIM_OFFSET_V_MASK)) | ((a)<>CIM_OFFSET_V_BIT) ++#define __cim_get_h_offset() ((REG_CIM_OFFSET&CIM_OFFSET_H_MASK)>>CIM_OFFSET_H_BIT) ++ ++/************************************************************************* ++ * SLCD (Smart LCD Controller) ++ *************************************************************************/ ++#define __slcd_set_data_18bit() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_18BIT ) ++#define __slcd_set_data_16bit() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_16BIT ) ++#define __slcd_set_data_8bit_x3() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_8BIT_x3 ) ++#define __slcd_set_data_8bit_x2() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_8BIT_x2 ) ++#define __slcd_set_data_8bit_x1() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_8BIT_x1 ) ++#define __slcd_set_data_24bit() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_24BIT ) ++#define __slcd_set_data_9bit_x2() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_9BIT_x2 ) ++ ++#define __slcd_set_cmd_16bit() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_16BIT ) ++#define __slcd_set_cmd_8bit() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_8BIT ) ++#define __slcd_set_cmd_18bit() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_18BIT ) ++#define __slcd_set_cmd_24bit() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_24BIT ) ++ ++#define __slcd_set_cs_high() ( REG_SLCD_CFG |= SLCD_CFG_CS_ACTIVE_HIGH ) ++#define __slcd_set_cs_low() ( REG_SLCD_CFG &= ~SLCD_CFG_CS_ACTIVE_HIGH ) ++ ++#define __slcd_set_rs_high() ( REG_SLCD_CFG |= SLCD_CFG_RS_CMD_HIGH ) ++#define __slcd_set_rs_low() ( REG_SLCD_CFG &= ~SLCD_CFG_RS_CMD_HIGH ) ++ ++#define __slcd_set_clk_falling() ( REG_SLCD_CFG &= ~SLCD_CFG_CLK_ACTIVE_RISING ) ++#define __slcd_set_clk_rising() ( REG_SLCD_CFG |= SLCD_CFG_CLK_ACTIVE_RISING ) ++ ++#define __slcd_set_parallel_type() ( REG_SLCD_CFG &= ~SLCD_CFG_TYPE_SERIAL ) ++#define __slcd_set_serial_type() ( REG_SLCD_CFG |= SLCD_CFG_TYPE_SERIAL ) ++ ++/* SLCD Control Register */ ++#define __slcd_enable_dma() ( REG_SLCD_CTRL |= SLCD_CTRL_DMA_EN ) ++#define __slcd_disable_dma() ( REG_SLCD_CTRL &= ~SLCD_CTRL_DMA_EN ) ++ ++/* SLCD Status Register */ ++#define __slcd_is_busy() ( REG_SLCD_STATE & SLCD_STATE_BUSY ) ++ ++/* SLCD Data Register */ ++#define __slcd_set_cmd_rs() ( REG_SLCD_DATA |= SLCD_DATA_RS_COMMAND) ++#define __slcd_set_data_rs() ( REG_SLCD_DATA &= ~SLCD_DATA_RS_COMMAND) ++ ++ ++/*************************************************************************** ++ * LCD ++ ***************************************************************************/ ++ ++/*************************************************************************** ++ * LCD ++ ***************************************************************************/ ++#define __lcd_as_smart_lcd() ( REG_LCD_CFG |= ( LCD_CFG_LCDPIN_SLCD | LCD_CFG_MODE_SLCD)) ++#define __lcd_as_general_lcd() ( REG_LCD_CFG &= ~( LCD_CFG_LCDPIN_SLCD | LCD_CFG_MODE_SLCD)) ++ ++#define __lcd_enable_tvepeh() ( REG_LCD_CFG |= LCD_CFG_TVEPEH ) ++#define __lcd_disable_tvepeh() ( REG_LCD_CFG &= ~LCD_CFG_TVEPEH ) ++ ++#define __lcd_enable_fuhold() ( REG_LCD_CFG |= LCD_CFG_FUHOLD ) ++#define __lcd_disable_fuhold() ( REG_LCD_CFG &= ~LCD_CFG_FUHOLD ) ++ ++#define __lcd_des_8word() ( REG_LCD_CFG |= LCD_CFG_NEWDES ) ++#define __lcd_des_4word() ( REG_LCD_CFG &= ~LCD_CFG_NEWDES ) ++ ++#define __lcd_enable_bypass_pal() ( REG_LCD_CFG |= LCD_CFG_PALBP ) ++#define __lcd_disable_bypass_pal() ( REG_LCD_CFG &= ~LCD_CFG_PALBP ) ++ ++#define __lcd_set_lcdpnl_term() ( REG_LCD_CTRL |= LCD_CFG_TVEN ) ++#define __lcd_set_tv_term() ( REG_LCD_CTRL &= ~LCD_CFG_TVEN ) ++ ++#define __lcd_enable_auto_recover() ( REG_LCD_CFG |= LCD_CFG_RECOVER ) ++#define __lcd_disable_auto_recover() ( REG_LCD_CFG &= ~LCD_CFG_RECOVER ) ++ ++#define __lcd_enable_dither() ( REG_LCD_CFG |= LCD_CFG_DITHER ) ++#define __lcd_disable_dither() ( REG_LCD_CFG &= ~LCD_CFG_DITHER ) ++ ++#define __lcd_disable_ps_mode() ( REG_LCD_CFG |= LCD_CFG_PSM ) ++#define __lcd_enable_ps_mode() ( REG_LCD_CFG &= ~LCD_CFG_PSM ) ++ ++#define __lcd_disable_cls_mode() ( REG_LCD_CFG |= LCD_CFG_CLSM ) ++#define __lcd_enable_cls_mode() ( REG_LCD_CFG &= ~LCD_CFG_CLSM ) ++ ++#define __lcd_disable_spl_mode() ( REG_LCD_CFG |= LCD_CFG_SPLM ) ++#define __lcd_enable_spl_mode() ( REG_LCD_CFG &= ~LCD_CFG_SPLM ) ++ ++#define __lcd_disable_rev_mode() ( REG_LCD_CFG |= LCD_CFG_REVM ) ++#define __lcd_enable_rev_mode() ( REG_LCD_CFG &= ~LCD_CFG_REVM ) ++ ++#define __lcd_disable_hsync_mode() ( REG_LCD_CFG |= LCD_CFG_HSYNM ) ++#define __lcd_enable_hsync_mode() ( REG_LCD_CFG &= ~LCD_CFG_HSYNM ) ++ ++#define __lcd_disable_pclk_mode() ( REG_LCD_CFG |= LCD_CFG_PCLKM ) ++#define __lcd_enable_pclk_mode() ( REG_LCD_CFG &= ~LCD_CFG_PCLKM ) ++ ++#define __lcd_normal_outdata() ( REG_LCD_CFG &= ~LCD_CFG_INVDAT ) ++#define __lcd_inverse_outdata() ( REG_LCD_CFG |= LCD_CFG_INVDAT ) ++ ++#define __lcd_sync_input() ( REG_LCD_CFG |= LCD_CFG_SYNDIR_IN ) ++#define __lcd_sync_output() ( REG_LCD_CFG &= ~LCD_CFG_SYNDIR_IN ) ++ ++#define __lcd_hsync_active_high() ( REG_LCD_CFG &= ~LCD_CFG_HSP ) ++#define __lcd_hsync_active_low() ( REG_LCD_CFG |= LCD_CFG_HSP ) ++ ++#define __lcd_pclk_rising() ( REG_LCD_CFG &= ~LCD_CFG_PCP ) ++#define __lcd_pclk_falling() ( REG_LCD_CFG |= LCD_CFG_PCP ) ++ ++#define __lcd_de_active_high() ( REG_LCD_CFG &= ~LCD_CFG_DEP ) ++#define __lcd_de_active_low() ( REG_LCD_CFG |= LCD_CFG_DEP ) ++ ++#define __lcd_vsync_rising() ( REG_LCD_CFG &= ~LCD_CFG_VSP ) ++#define __lcd_vsync_falling() ( REG_LCD_CFG |= LCD_CFG_VSP ) ++ ++#define __lcd_set_16_tftpnl() \ ++ ( REG_LCD_CFG = (REG_LCD_CFG & ~LCD_CFG_MODE_TFT_MASK) | LCD_CFG_MODE_TFT_16BIT ) ++ ++#define __lcd_set_18_tftpnl() \ ++ ( REG_LCD_CFG = (REG_LCD_CFG & ~LCD_CFG_MODE_TFT_MASK) | LCD_CFG_MODE_TFT_18BIT ) ++ ++#define __lcd_set_24_tftpnl() ( REG_LCD_CFG |= LCD_CFG_MODE_TFT_24BIT ) ++ ++/* ++ * n=1,2,4,8 for single mono-STN ++ * n=4,8 for dual mono-STN ++ */ ++#define __lcd_set_panel_datawidth(n) \ ++do { \ ++ REG_LCD_CFG &= ~LCD_CFG_PDW_MASK; \ ++ REG_LCD_CFG |= LCD_CFG_PDW_n##; \ ++} while (0) ++ ++/* m = LCD_CFG_MODE_GENERUIC_TFT_xxx */ ++#define __lcd_set_panel_mode(m) \ ++do { \ ++ REG_LCD_CFG &= ~LCD_CFG_MODE_MASK; \ ++ REG_LCD_CFG |= (m); \ ++} while(0) ++ ++/* n=4,8,16 */ ++#define __lcd_set_burst_length(n) \ ++do { \ ++ REG_LCD_CTRL &= ~LCD_CTRL_BST_MASK; \ ++ REG_LCD_CTRL |= LCD_CTRL_BST_n##; \ ++} while (0) ++ ++#define __lcd_select_rgb565() ( REG_LCD_CTRL &= ~LCD_CTRL_RGB555 ) ++#define __lcd_select_rgb555() ( REG_LCD_CTRL |= LCD_CTRL_RGB555 ) ++ ++#define __lcd_set_ofup() ( REG_LCD_CTRL |= LCD_CTRL_OFUP ) ++#define __lcd_clr_ofup() ( REG_LCD_CTRL &= ~LCD_CTRL_OFUP ) ++ ++/* n=2,4,16 */ ++#define __lcd_set_stn_frc(n) \ ++do { \ ++ REG_LCD_CTRL &= ~LCD_CTRL_FRC_MASK; \ ++ REG_LCD_CTRL |= LCD_CTRL_FRC_n##; \ ++} while (0) ++ ++#define __lcd_enable_eof_intr() ( REG_LCD_CTRL |= LCD_CTRL_EOFM ) ++#define __lcd_disable_eof_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_EOFM ) ++ ++#define __lcd_enable_sof_intr() ( REG_LCD_CTRL |= LCD_CTRL_SOFM ) ++#define __lcd_disable_sof_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_SOFM ) ++ ++#define __lcd_enable_ofu_intr() ( REG_LCD_CTRL |= LCD_CTRL_OFUM ) ++#define __lcd_disable_ofu_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_OFUM ) ++ ++#define __lcd_enable_ifu0_intr() ( REG_LCD_CTRL |= LCD_CTRL_IFUM0 ) ++#define __lcd_disable_ifu0_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_IFUM0 ) ++ ++#define __lcd_enable_ifu1_intr() ( REG_LCD_CTRL |= LCD_CTRL_IFUM1 ) ++#define __lcd_disable_ifu1_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_IFUM1 ) ++ ++#define __lcd_enable_ldd_intr() ( REG_LCD_CTRL |= LCD_CTRL_LDDM ) ++#define __lcd_disable_ldd_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_LDDM ) ++ ++#define __lcd_enable_qd_intr() ( REG_LCD_CTRL |= LCD_CTRL_QDM ) ++#define __lcd_disable_qd_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_QDM ) ++ ++#define __lcd_reverse_byte_endian() ( REG_LCD_CTRL |= LCD_CTRL_BEDN ) ++#define __lcd_normal_byte_endian() ( REG_LCD_CTRL &= ~LCD_CTRL_BEDN ) ++ ++#define __lcd_pixel_endian_little() ( REG_LCD_CTRL |= LCD_CTRL_PEDN ) ++#define __lcd_pixel_endian_big() ( REG_LCD_CTRL &= ~LCD_CTRL_PEDN ) ++ ++#define __lcd_set_dis() ( REG_LCD_CTRL |= LCD_CTRL_DIS ) ++#define __lcd_clr_dis() ( REG_LCD_CTRL &= ~LCD_CTRL_DIS ) ++ ++#define __lcd_set_ena() ( REG_LCD_CTRL |= LCD_CTRL_ENA ) ++#define __lcd_clr_ena() ( REG_LCD_CTRL &= ~LCD_CTRL_ENA ) ++ ++/* n=1,2,4,8,16 */ ++#define __lcd_set_bpp(n) \ ++ ( REG_LCD_CTRL = (REG_LCD_CTRL & ~LCD_CTRL_BPP_MASK) | LCD_CTRL_BPP_##n ) ++ ++/* LCD status register indication */ ++ ++#define __lcd_quick_disable_done() ( REG_LCD_STATE & LCD_STATE_QD ) ++#define __lcd_disable_done() ( REG_LCD_STATE & LCD_STATE_LDD ) ++#define __lcd_infifo0_underrun() ( REG_LCD_STATE & LCD_STATE_IFU0 ) ++#define __lcd_infifo1_underrun() ( REG_LCD_STATE & LCD_STATE_IFU1 ) ++#define __lcd_outfifo_underrun() ( REG_LCD_STATE & LCD_STATE_OFU ) ++#define __lcd_start_of_frame() ( REG_LCD_STATE & LCD_STATE_SOF ) ++#define __lcd_end_of_frame() ( REG_LCD_STATE & LCD_STATE_EOF ) ++ ++#define __lcd_clr_outfifounderrun() ( REG_LCD_STATE &= ~LCD_STATE_OFU ) ++#define __lcd_clr_sof() ( REG_LCD_STATE &= ~LCD_STATE_SOF ) ++#define __lcd_clr_eof() ( REG_LCD_STATE &= ~LCD_STATE_EOF ) ++ ++/* OSD functions */ ++#define __lcd_enable_osd() (REG_LCD_OSDC |= LCD_OSDC_OSDEN) ++#define __lcd_enable_f0() (REG_LCD_OSDC |= LCD_OSDC_F0EN) ++#define __lcd_enable_f1() (REG_LCD_OSDC |= LCD_OSDC_F1EN) ++#define __lcd_enable_alpha() (REG_LCD_OSDC |= LCD_OSDC_ALPHAEN) ++#define __lcd_enable_alphamd() (REG_LCD_OSDC |= LCD_OSDC_ALPHAMD) ++ ++#define __lcd_disable_osd() (REG_LCD_OSDC &= ~LCD_OSDC_OSDEN) ++#define __lcd_disable_f0() (REG_LCD_OSDC &= ~LCD_OSDC_F0EN) ++#define __lcd_disable_f1() (REG_LCD_OSDC &= ~LCD_OSDC_F1EN) ++#define __lcd_disable_alpha() (REG_LCD_OSDC &= ~LCD_OSDC_ALPHAEN) ++#define __lcd_disable_alphamd() (REG_LCD_OSDC &= ~LCD_OSDC_ALPHAMD) ++ ++/* OSD Controll Register */ ++#define __lcd_fg1_use_ipu() (REG_LCD_OSDCTRL |= LCD_OSDCTRL_IPU) ++#define __lcd_fg1_use_dma_chan1() (REG_LCD_OSDCTRL &= ~LCD_OSDCTRL_IPU) ++#define __lcd_fg1_unuse_ipu() __lcd_fg1_use_dma_chan1() ++#define __lcd_osd_rgb555_mode() ( REG_LCD_OSDCTRL |= LCD_OSDCTRL_RGB555 ) ++#define __lcd_osd_rgb565_mode() ( REG_LCD_OSDCTRL &= ~LCD_OSDCTRL_RGB555 ) ++#define __lcd_osd_change_size() ( REG_LCD_OSDCTRL |= LCD_OSDCTRL_CHANGES ) ++#define __lcd_osd_bpp_15_16() \ ++ ( REG_LCD_OSDCTRL = (REG_LCD_OSDCTRL & ~LCD_OSDCTRL_OSDBPP_MASK) | LCD_OSDCTRL_OSDBPP_15_16 ) ++#define __lcd_osd_bpp_18_24() \ ++ ( REG_LCD_OSDCTRL = (REG_LCD_OSDCTRL & ~LCD_OSDCTRL_OSDBPP_MASK) | LCD_OSDCTRL_OSDBPP_18_24 ) ++ ++/* OSD State Register */ ++#define __lcd_start_of_fg1() ( REG_LCD_STATE & LCD_OSDS_SOF1 ) ++#define __lcd_end_of_fg1() ( REG_LCD_STATE & LCD_OSDS_EOF1 ) ++#define __lcd_start_of_fg0() ( REG_LCD_STATE & LCD_OSDS_SOF0 ) ++#define __lcd_end_of_fg0() ( REG_LCD_STATE & LCD_OSDS_EOF0 ) ++#define __lcd_change_is_rdy() ( REG_LCD_STATE & LCD_OSDS_READY ) ++ ++/* Foreground Color Key Register 0,1(foreground 0, foreground 1) */ ++#define __lcd_enable_colorkey0() (REG_LCD_KEY0 |= LCD_KEY_KEYEN) ++#define __lcd_enable_colorkey1() (REG_LCD_KEY1 |= LCD_KEY_KEYEN) ++#define __lcd_enable_colorkey0_md() (REG_LCD_KEY0 |= LCD_KEY_KEYMD) ++#define __lcd_enable_colorkey1_md() (REG_LCD_KEY1 |= LCD_KEY_KEYMD) ++#define __lcd_set_colorkey0(key) (REG_LCD_KEY0 = (REG_LCD_KEY0&~0xFFFFFF)|(key)) ++#define __lcd_set_colorkey1(key) (REG_LCD_KEY1 = (REG_LCD_KEY1&~0xFFFFFF)|(key)) ++ ++#define __lcd_disable_colorkey0() (REG_LCD_KEY0 &= ~LCD_KEY_KEYEN) ++#define __lcd_disable_colorkey1() (REG_LCD_KEY1 &= ~LCD_KEY_KEYEN) ++#define __lcd_disable_colorkey0_md() (REG_LCD_KEY0 &= ~LCD_KEY_KEYMD) ++#define __lcd_disable_colorkey1_md() (REG_LCD_KEY1 &= ~LCD_KEY_KEYMD) ++ ++/* IPU Restart Register */ ++#define __lcd_enable_ipu_restart() (REG_LCD_IPUR |= LCD_IPUR_IPUREN) ++#define __lcd_disable_ipu_restart() (REG_LCD_IPUR &= ~LCD_IPUR_IPUREN) ++#define __lcd_set_ipu_restart_triger(n) (REG_LCD_IPUR = (REG_LCD_IPUR&(~0xFFFFFF))|(n)) ++ ++/* RGB Control Register */ ++#define __lcd_enable_rgb_dummy() (REG_LCD_RGBC |= LCD_RGBC_RGBDM) ++#define __lcd_disable_rgb_dummy() (REG_LCD_RGBC &= ~LCD_RGBC_RGBDM) ++ ++#define __lcd_dummy_rgb() (REG_LCD_RGBC |= LCD_RGBC_DMM) ++#define __lcd_rgb_dummy() (REG_LCD_RGBC &= ~LCD_RGBC_DMM) ++ ++#define __lcd_rgb2ycc() (REG_LCD_RGBC |= LCD_RGBC_YCC) ++#define __lcd_notrgb2ycc() (REG_LCD_RGBC &= ~LCD_RGBC_YCC) ++ ++#define __lcd_odd_mode_rgb() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_RGB ) ++#define __lcd_odd_mode_rbg() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_RBG ) ++#define __lcd_odd_mode_grb() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_GRB) ++ ++#define __lcd_odd_mode_gbr() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_GBR) ++#define __lcd_odd_mode_brg() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_BRG) ++#define __lcd_odd_mode_bgr() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_BGR) ++ ++#define __lcd_even_mode_rgb() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_RGB ) ++#define __lcd_even_mode_rbg() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_RBG ) ++#define __lcd_even_mode_grb() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_GRB) ++ ++#define __lcd_even_mode_gbr() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_GBR) ++#define __lcd_even_mode_brg() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_BRG) ++#define __lcd_even_mode_bgr() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_BGR) ++ ++/* Vertical Synchronize Register */ ++#define __lcd_vsync_get_vps() \ ++ ( (REG_LCD_VSYNC & LCD_VSYNC_VPS_MASK) >> LCD_VSYNC_VPS_BIT ) ++ ++#define __lcd_vsync_get_vpe() \ ++ ( (REG_LCD_VSYNC & LCD_VSYNC_VPE_MASK) >> LCD_VSYNC_VPE_BIT ) ++#define __lcd_vsync_set_vpe(n) \ ++do { \ ++ REG_LCD_VSYNC &= ~LCD_VSYNC_VPE_MASK; \ ++ REG_LCD_VSYNC |= (n) << LCD_VSYNC_VPE_BIT; \ ++} while (0) ++ ++#define __lcd_hsync_get_hps() \ ++ ( (REG_LCD_HSYNC & LCD_HSYNC_HPS_MASK) >> LCD_HSYNC_HPS_BIT ) ++#define __lcd_hsync_set_hps(n) \ ++do { \ ++ REG_LCD_HSYNC &= ~LCD_HSYNC_HPS_MASK; \ ++ REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPS_BIT; \ ++} while (0) ++ ++#define __lcd_hsync_get_hpe() \ ++ ( (REG_LCD_HSYNC & LCD_HSYNC_HPE_MASK) >> LCD_VSYNC_HPE_BIT ) ++#define __lcd_hsync_set_hpe(n) \ ++do { \ ++ REG_LCD_HSYNC &= ~LCD_HSYNC_HPE_MASK; \ ++ REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPE_BIT; \ ++} while (0) ++ ++#define __lcd_vat_get_ht() \ ++ ( (REG_LCD_VAT & LCD_VAT_HT_MASK) >> LCD_VAT_HT_BIT ) ++#define __lcd_vat_set_ht(n) \ ++do { \ ++ REG_LCD_VAT &= ~LCD_VAT_HT_MASK; \ ++ REG_LCD_VAT |= (n) << LCD_VAT_HT_BIT; \ ++} while (0) ++ ++#define __lcd_vat_get_vt() \ ++ ( (REG_LCD_VAT & LCD_VAT_VT_MASK) >> LCD_VAT_VT_BIT ) ++#define __lcd_vat_set_vt(n) \ ++do { \ ++ REG_LCD_VAT &= ~LCD_VAT_VT_MASK; \ ++ REG_LCD_VAT |= (n) << LCD_VAT_VT_BIT; \ ++} while (0) ++ ++#define __lcd_dah_get_hds() \ ++ ( (REG_LCD_DAH & LCD_DAH_HDS_MASK) >> LCD_DAH_HDS_BIT ) ++#define __lcd_dah_set_hds(n) \ ++do { \ ++ REG_LCD_DAH &= ~LCD_DAH_HDS_MASK; \ ++ REG_LCD_DAH |= (n) << LCD_DAH_HDS_BIT; \ ++} while (0) ++ ++#define __lcd_dah_get_hde() \ ++ ( (REG_LCD_DAH & LCD_DAH_HDE_MASK) >> LCD_DAH_HDE_BIT ) ++#define __lcd_dah_set_hde(n) \ ++do { \ ++ REG_LCD_DAH &= ~LCD_DAH_HDE_MASK; \ ++ REG_LCD_DAH |= (n) << LCD_DAH_HDE_BIT; \ ++} while (0) ++ ++#define __lcd_dav_get_vds() \ ++ ( (REG_LCD_DAV & LCD_DAV_VDS_MASK) >> LCD_DAV_VDS_BIT ) ++#define __lcd_dav_set_vds(n) \ ++do { \ ++ REG_LCD_DAV &= ~LCD_DAV_VDS_MASK; \ ++ REG_LCD_DAV |= (n) << LCD_DAV_VDS_BIT; \ ++} while (0) ++ ++#define __lcd_dav_get_vde() \ ++ ( (REG_LCD_DAV & LCD_DAV_VDE_MASK) >> LCD_DAV_VDE_BIT ) ++#define __lcd_dav_set_vde(n) \ ++do { \ ++ REG_LCD_DAV &= ~LCD_DAV_VDE_MASK; \ ++ REG_LCD_DAV |= (n) << LCD_DAV_VDE_BIT; \ ++} while (0) ++ ++/* DMA Command Register */ ++#define __lcd_cmd0_set_sofint() ( REG_LCD_CMD0 |= LCD_CMD_SOFINT ) ++#define __lcd_cmd0_clr_sofint() ( REG_LCD_CMD0 &= ~LCD_CMD_SOFINT ) ++#define __lcd_cmd1_set_sofint() ( REG_LCD_CMD1 |= LCD_CMD_SOFINT ) ++#define __lcd_cmd1_clr_sofint() ( REG_LCD_CMD1 &= ~LCD_CMD_SOFINT ) ++ ++#define __lcd_cmd0_set_eofint() ( REG_LCD_CMD0 |= LCD_CMD_EOFINT ) ++#define __lcd_cmd0_clr_eofint() ( REG_LCD_CMD0 &= ~LCD_CMD_EOFINT ) ++#define __lcd_cmd1_set_eofint() ( REG_LCD_CMD1 |= LCD_CMD_EOFINT ) ++#define __lcd_cmd1_clr_eofint() ( REG_LCD_CMD1 &= ~LCD_CMD_EOFINT ) ++ ++#define __lcd_cmd0_set_pal() ( REG_LCD_CMD0 |= LCD_CMD_PAL ) ++#define __lcd_cmd0_clr_pal() ( REG_LCD_CMD0 &= ~LCD_CMD_PAL ) ++ ++#define __lcd_cmd0_get_len() \ ++ ( (REG_LCD_CMD0 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) ++#define __lcd_cmd1_get_len() \ ++ ( (REG_LCD_CMD1 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) ++ ++/************************************************************************* ++ * TVE (TV Encoder Controller) ops ++ *************************************************************************/ ++/* TV Encoder Control register ops */ ++#define __tve_soft_reset() (REG_TVE_CTRL |= TVE_CTRL_SWRST) ++ ++#define __tve_output_colorbar() (REG_TVE_CTRL |= TVE_CTRL_CLBAR) ++#define __tve_output_video() (REG_TVE_CTRL &= ~TVE_CTRL_CLBAR) ++ ++#define __tve_input_cr_first() (REG_TVE_CTRL |= TVE_CTRL_CR1ST) ++#define __tve_input_cb_first() (REG_TVE_CTRL &= ~TVE_CTRL_CR1ST) ++ ++#define __tve_set_0_as_black() (REG_TVE_CTRL |= TVE_CTRL_ZBLACK) ++#define __tve_set_16_as_black() (REG_TVE_CTRL &= ~TVE_CTRL_ZBLACK) ++ ++#define __tve_ena_invert_top_bottom() (REG_TVE_CTRL |= TVE_CTRL_FINV) ++#define __tve_dis_invert_top_bottom() (REG_TVE_CTRL &= ~TVE_CTRL_FINV) ++ ++#define __tve_set_pal_mode() (REG_TVE_CTRL |= TVE_CTRL_PAL) ++#define __tve_set_ntsc_mode() (REG_TVE_CTRL &= ~TVE_CTRL_PAL) ++ ++#define __tve_set_pal_dura() (REG_TVE_CTRL |= TVE_CTRL_SYNCT) ++#define __tve_set_ntsc_dura() (REG_TVE_CTRL &= ~TVE_CTRL_SYNCT) ++ ++/* n = 0 ~ 3 */ ++#define __tve_set_c_bandwidth(n) \ ++do {\ ++ REG_TVE_CTRL &= ~TVE_CTRL_CBW_MASK;\ ++ REG_TVE_CTRL |= (n) << TVE_CTRL_CBW_BIT; \ ++}while(0) ++ ++/* n = 0 ~ 3 */ ++#define __tve_set_c_gain(n) \ ++do {\ ++ REG_TVE_CTRL &= ~TVE_CTRL_CGAIN_MASK;\ ++ (REG_TVE_CTRL |= (n) << TVE_CTRL_CGAIN_BIT; \ ++}while(0) ++ ++/* n = 0 ~ 7 */ ++#define __tve_set_yc_delay(n) \ ++do { \ ++ REG_TVE_CTRL &= ~TVE_CTRL_YCDLY_MASK \ ++ REG_TVE_CTRL |= ((n) << TVE_CTRL_YCDLY_BIT); \ ++} while(0) ++ ++#define __tve_disable_all_dacs() (REG_TVE_CTRL |= TVE_CTRL_DAPD) ++#define __tve_disable_dac1() (REG_TVE_CTRL |= TVE_CTRL_DAPD1) ++#define __tve_enable_dac1() (REG_TVE_CTRL &= ~TVE_CTRL_DAPD1) ++#define __tve_disable_dac2() (REG_TVE_CTRL |= TVE_CTRL_DAPD2) ++#define __tve_enable_dac2() (REG_TVE_CTRL &= ~TVE_CTRL_DAPD2) ++#define __tve_disable_dac3() (REG_TVE_CTRL |= TVE_CTRL_DAPD3) ++#define __tve_enable_dac3() (REG_TVE_CTRL &= ~TVE_CTRL_DAPD3) ++ ++#define __tve_enable_svideo_fmt() (REG_TVE_CTRL |= TVE_CTRL_ECVBS) ++#define __tve_enable_cvbs_fmt() (REG_TVE_CTRL &= ~TVE_CTRL_ECVBS) ++ ++/* TV Encoder Frame Configure register ops */ ++/* n = 0 ~ 255 */ ++#define __tve_set_first_video_line(n) \ ++do {\ ++ REG_TVE_FRCFG &= ~TVE_FRCFG_L1ST_MASK;\ ++ REG_TVE_FRCFG |= (n) << TVE_FRCFG_L1ST_BIT;\ ++} while(0) ++/* n = 0 ~ 1023 */ ++#define __tve_set_line_num_per_frm(n) \ ++do {\ ++ REG_TVE_FRCFG &= ~TVE_FRCFG_NLINE_MASK;\ ++ REG_TVE_CFG |= (n) << TVE_FRCFG_NLINE_BIT;\ ++} while(0) ++#define __tve_get_video_line_num()\ ++ (((REG_TVE_FRCFG & TVE_FRCFG_NLINE_MASK) >> TVE_FRCFG_NLINE_BIT) - 1 - 2 * ((REG_TVE_FRCFG & TVE_FRCFG_L1ST_MASK) >> TVE_FRCFG_L1ST_BIT)) ++ ++/* TV Encoder Signal Level Configure register ops */ ++/* n = 0 ~ 1023 */ ++#define __tve_set_white_level(n) \ ++do {\ ++ REG_TVE_SLCFG1 &= ~TVE_SLCFG1_WHITEL_MASK;\ ++ REG_TVE_SLCFG1 |= (n) << TVE_SLCFG1_WHITEL_BIT;\ ++} while(0) ++/* n = 0 ~ 1023 */ ++#define __tve_set_black_level(n) \ ++do {\ ++ REG_TVE_SLCFG1 &= ~TVE_SLCFG1_BLACKL_MASK;\ ++ REG_TVE_SLCFG1 |= (n) << TVE_SLCFG1_BLACKL_BIT;\ ++} while(0) ++/* n = 0 ~ 1023 */ ++#define __tve_set_blank_level(n) \ ++do {\ ++ REG_TVE_SLCFG2 &= ~TVE_SLCFG2_BLANKL_MASK;\ ++ REG_TVE_SLCFG2 |= (n) << TVE_SLCFG2_BLANKL_BIT;\ ++} while(0) ++/* n = 0 ~ 1023 */ ++#define __tve_set_vbi_blank_level(n) \ ++do {\ ++ REG_TVE_SLCFG2 &= ~TVE_SLCFG2_VBLANKL_MASK;\ ++ REG_TVE_SLCFG2 |= (n) << TVE_SLCFG2_VBLANKL_BIT;\ ++} while(0) ++/* n = 0 ~ 1023 */ ++#define __tve_set_sync_level(n) \ ++do {\ ++ REG_TVE_SLCFG3 &= ~TVE_SLCFG3_SYNCL_MASK;\ ++ REG_TVE_SLCFG3 |= (n) << TVE_SLCFG3_SYNCL_BIT;\ ++} while(0) ++ ++/* TV Encoder Signal Level Configure register ops */ ++/* n = 0 ~ 31 */ ++#define __tve_set_front_porch(n) \ ++do {\ ++ REG_TVE_LTCFG1 &= ~TVE_LTCFG1_FRONTP_MASK;\ ++ REG_TVE_LTCFG1 |= (n) << TVE_LTCFG1_FRONTP_BIT; \ ++} while(0) ++/* n = 0 ~ 127 */ ++#define __tve_set_hsync_width(n) \ ++do {\ ++ REG_TVE_LTCFG1 &= ~TVE_LTCFG1_HSYNCW_MASK;\ ++ REG_TVE_LTCFG1 |= (n) << TVE_LTCFG1_HSYNCW_BIT; \ ++} while(0) ++/* n = 0 ~ 127 */ ++#define __tve_set_back_porch(n) \ ++do {\ ++ REG_TVE_LTCFG1 &= ~TVE_LTCFG1_BACKP_MASK;\ ++ REG_TVE_LTCFG1 |= (n) << TVE_LTCFG1_BACKP_BIT; \ ++} while(0) ++/* n = 0 ~ 2047 */ ++#define __tve_set_active_linec(n) \ ++do {\ ++ REG_TVE_LTCFG2 &= ~TVE_LTCFG2_ACTLIN_MASK;\ ++ REG_TVE_LTCFG2 |= (n) << TVE_LTCFG2_ACTLIN_BIT; \ ++} while(0) ++/* n = 0 ~ 31 */ ++#define __tve_set_breezy_way(n) \ ++do {\ ++ REG_TVE_LTCFG2 &= ~TVE_LTCFG2_PREBW_MASK;\ ++ REG_TVE_LTCFG2 |= (n) << TVE_LTCFG2_PREBW_BIT; \ ++} while(0) ++ ++/* n = 0 ~ 127 */ ++#define __tve_set_burst_width(n) \ ++do {\ ++ REG_TVE_LTCFG2 &= ~TVE_LTCFG2_BURSTW_MASK;\ ++ REG_TVE_LTCFG2 |= (n) << TVE_LTCFG2_BURSTW_BIT; \ ++} while(0) ++ ++/* TV Encoder Chrominance filter and Modulation register ops */ ++/* n = 0 ~ (2^32-1) */ ++#define __tve_set_c_sub_carrier_freq(n) REG_TVE_CFREQ = (n) ++/* n = 0 ~ 255 */ ++#define __tve_set_c_sub_carrier_init_phase(n) \ ++do { \ ++ REG_TVE_CPHASE &= ~TVE_CPHASE_INITPH_MASK; \ ++ REG_TVE_CPHASE |= (n) << TVE_CPHASE_INITPH_BIT; \ ++} while(0) ++/* n = 0 ~ 255 */ ++#define __tve_set_c_sub_carrier_act_phase(n) \ ++do { \ ++ REG_TVE_CPHASE &= ~TVE_CPHASE_ACTPH_MASK; \ ++ REG_TVE_CPHASE |= (n) << TVE_CPHASE_ACTPH_BIT; \ ++} while(0) ++/* n = 0 ~ 255 */ ++#define __tve_set_c_phase_rst_period(n) \ ++do { \ ++ REG_TVE_CPHASE &= ~TVE_CPHASE_CCRSTP_MASK; \ ++ REG_TVE_CPHASE |= (n) << TVE_CPHASE_CCRSTP_BIT; \ ++} while(0) ++/* n = 0 ~ 255 */ ++#define __tve_set_cb_burst_amp(n) \ ++do { \ ++ REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CBBA_MASK; \ ++ REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CBBA_BIT; \ ++} while(0) ++/* n = 0 ~ 255 */ ++#define __tve_set_cr_burst_amp(n) \ ++do { \ ++ REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CRBA_MASK; \ ++ REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CRBA_BIT; \ ++} while(0) ++/* n = 0 ~ 255 */ ++#define __tve_set_cb_gain_amp(n) \ ++do { \ ++ REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CBGAIN_MASK; \ ++ REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CBGAIN_BIT; \ ++} while(0) ++/* n = 0 ~ 255 */ ++#define __tve_set_cr_gain_amp(n) \ ++do { \ ++ REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CRGAIN_MASK; \ ++ REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CRGAIN_BIT; \ ++} while(0) ++ ++/* TV Encoder Wide Screen Signal Control register ops */ ++/* n = 0 ~ 7 */ ++#define __tve_set_notch_freq(n) \ ++do { \ ++ REG_TVE_WSSCR &= ~TVE_WSSCR_NCHFREQ_MASK; \ ++ REG_TVE_WSSCR |= (n) << TVE_WSSCR_NCHFREQ_BIT; \ ++} while(0) ++/* n = 0 ~ 7 */ ++#define __tve_set_notch_width() (REG_TVE_WSSCR |= TVE_WSSCR_NCHW_BIT) ++#define __tve_clear_notch_width() (REG_TVE_WSSCR &= ~TVE_WSSCR_NCHW_BIT) ++#define __tve_enable_notch() (REG_TVE_WSSCR |= TVE_WSSCR_ENCH_BIT) ++#define __tve_disable_notch() (REG_TVE_WSSCR &= ~TVE_WSSCR_ENCH_BIT) ++/* n = 0 ~ 7 */ ++#define __tve_set_wss_edge(n) \ ++do { \ ++ REG_TVE_WSSCR &= ~TVE_WSSCR_WSSEDGE_MASK; \ ++ REG_TVE_WSSCR |= (n) << TVE_WSSCR_WSSEDGE_BIT; \ ++} while(0) ++#define __tve_set_wss_clkbyp() (REG_TVE_WSSCR |= TVE_WSSCR_WSSCKBP_BIT) ++#define __tve_set_wss_type() (REG_TVE_WSSCR |= TVE_WSSCR_WSSTP_BIT) ++#define __tve_enable_wssf1() (REG_TVE_WSSCR |= TVE_WSSCR_EWSS1_BIT) ++#define __tve_enable_wssf0() (REG_TVE_WSSCR |= TVE_WSSCR_EWSS0_BIT) ++ ++/* TV Encoder Wide Screen Signal Configure register 1, 2 and 3 ops */ ++/* n = 0 ~ 1023 */ ++#define __tve_set_wss_level(n) \ ++do { \ ++ REG_TVE_WSSCFG1 &= ~TVE_WSSCFG1_WSSL_MASK; \ ++ REG_TVE_WSSCFG1 |= (n) << TVE_WSSCFG1_WSSL_BIT; \ ++} while(0) ++/* n = 0 ~ 4095 */ ++#define __tve_set_wss_freq(n) \ ++do { \ ++ REG_TVE_WSSCFG1 &= ~TVE_WSSCFG1_WSSFREQ_MASK; \ ++ REG_TVE_WSSCFG1 |= (n) << TVE_WSSCFG1_WSSFREQ_BIT; \ ++} while(0) ++/* n = 0, 1; l = 0 ~ 255 */ ++#define __tve_set_wss_line(n,v) \ ++do { \ ++ REG_TVE_WSSCFG##n &= ~TVE_WSSCFG_WSSLINE_MASK; \ ++ REG_TVE_WSSCFG##n |= (v) << TVE_WSSCFG_WSSLINE_BIT; \ ++} while(0) ++/* n = 0, 1; d = 0 ~ (2^20-1) */ ++#define __tve_set_wss_data(n, v) \ ++do { \ ++ REG_TVE_WSSCFG##n &= ~TVE_WSSCFG_WSSLINE_MASK; \ ++ REG_TVE_WSSCFG##n |= (v) << TVE_WSSCFG_WSSLINE_BIT; \ ++} while(0) ++ ++/*************************************************************************** ++ * RTC ops ++ ***************************************************************************/ ++ ++#define __rtc_write_ready() ( (REG_RTC_RCR & RTC_RCR_WRDY) >> RTC_RCR_WRDY_BIT ) ++#define __rtc_enabled() ( REG_RTC_RCR |= RTC_RCR_RTCE ) ++#define __rtc_disabled() ( REG_RTC_RCR &= ~RTC_RCR_RTCE ) ++#define __rtc_enable_alarm() ( REG_RTC_RCR |= RTC_RCR_AE ) ++#define __rtc_disable_alarm() ( REG_RTC_RCR &= ~RTC_RCR_AE ) ++#define __rtc_enable_alarm_irq() ( REG_RTC_RCR |= RTC_RCR_AIE ) ++#define __rtc_disable_alarm_irq() ( REG_RTC_RCR &= ~RTC_RCR_AIE ) ++#define __rtc_enable_1Hz_irq() ( REG_RTC_RCR |= RTC_RCR_1HZIE ) ++#define __rtc_disable_1Hz_irq() ( REG_RTC_RCR &= ~RTC_RCR_1HZIE ) ++ ++#define __rtc_get_1Hz_flag() ( (REG_RTC_RCR >> RTC_RCR_1HZ_BIT) & 0x1 ) ++#define __rtc_clear_1Hz_flag() ( REG_RTC_RCR &= ~RTC_RCR_1HZ ) ++#define __rtc_get_alarm_flag() ( (REG_RTC_RCR >> RTC_RCR_AF_BIT) & 0x1 ) ++#define __rtc_clear_alarm_flag() ( REG_RTC_RCR &= ~RTC_RCR_AF ) ++ ++#define __rtc_get_second() ( REG_RTC_RSR ) ++#define __rtc_set_second(v) ( REG_RTC_RSR = v ) ++ ++#define __rtc_get_alarm_second() ( REG_RTC_RSAR ) ++#define __rtc_set_alarm_second(v) ( REG_RTC_RSAR = v ) ++ ++#define __rtc_RGR_is_locked() ( (REG_RTC_RGR >> RTC_RGR_LOCK) ) ++#define __rtc_lock_RGR() ( REG_RTC_RGR |= RTC_RGR_LOCK ) ++#define __rtc_unlock_RGR() ( REG_RTC_RGR &= ~RTC_RGR_LOCK ) ++#define __rtc_get_adjc_val() ( (REG_RTC_RGR & RTC_RGR_ADJC_MASK) >> RTC_RGR_ADJC_BIT ) ++#define __rtc_set_adjc_val(v) \ ++ ( REG_RTC_RGR = ( (REG_RTC_RGR & ~RTC_RGR_ADJC_MASK) | (v << RTC_RGR_ADJC_BIT) )) ++#define __rtc_get_nc1Hz_val() ( (REG_RTC_RGR & RTC_RGR_NC1HZ_MASK) >> RTC_RGR_NC1HZ_BIT ) ++#define __rtc_set_nc1Hz_val(v) \ ++ ( REG_RTC_RGR = ( (REG_RTC_RGR & ~RTC_RGR_NC1HZ_MASK) | (v << RTC_RGR_NC1HZ_BIT) )) ++ ++#define __rtc_power_down() ( REG_RTC_HCR |= RTC_HCR_PD ) ++ ++#define __rtc_get_hwfcr_val() ( REG_RTC_HWFCR & RTC_HWFCR_MASK ) ++#define __rtc_set_hwfcr_val(v) ( REG_RTC_HWFCR = (v) & RTC_HWFCR_MASK ) ++#define __rtc_get_hrcr_val() ( REG_RTC_HRCR & RTC_HRCR_MASK ) ++#define __rtc_set_hrcr_val(v) ( REG_RTC_HRCR = (v) & RTC_HRCR_MASK ) ++ ++#define __rtc_enable_alarm_wakeup() ( REG_RTC_HWCR |= RTC_HWCR_EALM ) ++#define __rtc_disable_alarm_wakeup() ( REG_RTC_HWCR &= ~RTC_HWCR_EALM ) ++ ++#define __rtc_status_hib_reset_occur() ( (REG_RTC_HWRSR >> RTC_HWRSR_HR) & 0x1 ) ++#define __rtc_status_ppr_reset_occur() ( (REG_RTC_HWRSR >> RTC_HWRSR_PPR) & 0x1 ) ++#define __rtc_status_wakeup_pin_waken_up() ( (REG_RTC_HWRSR >> RTC_HWRSR_PIN) & 0x1 ) ++#define __rtc_status_alarm_waken_up() ( (REG_RTC_HWRSR >> RTC_HWRSR_ALM) & 0x1 ) ++#define __rtc_clear_hib_stat_all() ( REG_RTC_HWRSR = 0 ) ++ ++#define __rtc_get_scratch_pattern() (REG_RTC_HSPR) ++#define __rtc_set_scratch_pattern(n) (REG_RTC_HSPR = n ) ++ ++/************************************************************************* ++ * BCH ++ *************************************************************************/ ++#define __ecc_encoding_4bit() \ ++do { \ ++ REG_BCH_CRS = BCH_CR_ENCE | BCH_CR_BRST | BCH_CR_BCHE; \ ++ REG_BCH_CRC = BCH_CR_BSEL8; \ ++} while(0) ++#define __ecc_decoding_4bit() \ ++do { \ ++ REG_BCH_CRS = BCH_CR_BRST | BCH_CR_BCHE; \ ++ REG_BCH_CRC = BCH_CR_ENCE | BCH_CR_BSEL8; \ ++} while(0) ++#define __ecc_encoding_8bit() \ ++do { \ ++ REG_BCH_CRS = BCH_CR_ENCE | BCH_CR_BRST | BCH_CR_BSEL8 | BCH_CR_BCHE; \ ++} while(0) ++#define __ecc_decoding_8bit() \ ++do { \ ++ REG_BCH_CRS = BCH_CR_BRST | BCH_CR_BSEL8 | BCH_CR_BCHE; \ ++ REG_BCH_CRC = BCH_CR_ENCE; \ ++} while(0) ++#define __ecc_dma_enable() ( REG_BCH_CRS = BCH_CR_DMAE ) ++#define __ecc_disable() ( REG_BCH_CRC = BCH_CR_BCHE ) ++#define __ecc_encode_sync() while (!(REG_BCH_INTS & BCH_INTS_ENCF)) ++#define __ecc_decode_sync() while (!(REG_BCH_INTS & BCH_INTS_DECF)) ++#define __ecc_cnt_dec(n) \ ++do { \ ++ REG_BCH_CNT &= ~(BCH_CNT_DEC_MASK << BCH_CNT_DEC_BIT); \ ++ REG_BCH_CNT = (n) << BCH_CNT_DEC_BIT; \ ++} while(0) ++#define __ecc_cnt_enc(n) \ ++do { \ ++ REG_BCH_CNT &= ~(BCH_CNT_ENC_MASK << BCH_CNT_ENC_BIT); \ ++ REG_BCH_CNT = (n) << BCH_CNT_ENC_BIT; \ ++} while(0) ++ ++/*************************************************************************** ++ * OWI (one-wire bus) ops ++ ***************************************************************************/ ++ ++/* OW control register ops */ ++#define __owi_enable_all_interrupts() ( REG_OWI_CTL = (OWI_CTL_EBYTE | OWI_CTL_EBIT | OWI_CTL_ERST) ) ++#define __owi_disable_all_interrupts() ( REG_OWI_CTL = 0 ) ++ ++#define __owi_enable_byte_interrupt() ( REG_OWI_CTL |= OWI_CTL_EBYTE ) ++#define __owi_disable_byte_interrupt() ( REG_OWI_CTL &= ~OWI_CTL_EBYTE ) ++#define __owi_enable_bit_interrupt() ( REG_OWI_CTL |= OWI_CTL_EBIT ) ++#define __owi_disable_bit_interrupt() ( REG_OWI_CTL &= ~OWI_CTL_EBIT ) ++#define __owi_enable_rst_interrupt() ( REG_OWI_CTL |= OWI_CTL_ERST ) ++#define __owi_disable_rst_interrupt() ( REG_OWI_CTL &=~OWI_CTL_ERST ) ++ ++/* OW configure register ops */ ++#define __owi_select_regular_mode() ( REG_OWI_CFG &= ~OWI_CFG_MODE ) ++#define __owi_select_overdrive_mode() ( REG_OWI_CFG |= OWI_CFG_MODE ) ++ ++#define __owi_set_rddata() ( REG_OWI_CFG |= OWI_CFG_RDDATA ) ++#define __owi_clr_rddata() ( REG_OWI_CFG &= ~OWI_CFG_RDDATA ) ++#define __owi_get_rddata() ( REG_OWI_CFG & OWI_CFG_RDDATA ) ++ ++#define __owi_set_wrdata() ( REG_OWI_CFG |= OWI_CFG_WRDATA ) ++#define __owi_clr_wrdata() ( REG_OWI_CFG &= ~OWI_CFG_WRDATA ) ++#define __owi_get_wrdata() ( REG_OWI_CFG & OWI_CFG_WRDATA ) ++ ++#define __owi_get_rdst() ( REG_OWI_CFG & OWI_CFG_RDST ) ++ ++#define __owi_set_wr1rd() ( REG_OWI_CFG |= OWI_CFG_WR1RD ) ++#define __owi_clr_wr1rd() ( REG_OWI_CFG &= ~OWI_CFG_WR1RD ) ++#define __owi_get_wr1rd() ( REG_OWI_CFG & OWI_CFG_WR1RD ) ++ ++#define __owi_set_wr0() ( REG_OWI_CFG |= OWI_CFG_WR0 ) ++#define __owi_clr_wr0() ( REG_OWI_CFG &= ~OWI_CFG_WR0 ) ++#define __owi_get_wr0() ( REG_OWI_CFG & OWI_CFG_WR0 ) ++ ++#define __owi_set_rst() ( REG_OWI_CFG |= OWI_CFG_RST ) ++#define __owi_clr_rst() ( REG_OWI_CFG &= ~OWI_CFG_RST ) ++#define __owi_get_rst() ( REG_OWI_CFG & OWI_CFG_RST ) ++ ++#define __owi_enable_ow_ops() ( REG_OWI_CFG |= OWI_CFG_ENA ) ++#define __owi_disable_ow_ops() ( REG_OWI_CFG &= ~OWI_CFG_ENA ) ++#define __owi_get_enable() ( REG_OWI_CFG & OWI_CFG_ENA ) ++ ++#define __owi_wait_ops_rdy() \ ++ do { \ ++ while(__owi_get_enable()); \ ++ udelay(1); \ ++ } while(0); ++ ++/* OW status register ops */ ++#define __owi_clr_sts() ( REG_OWI_STS = 0 ) ++#define __owi_get_sts_pst() ( REG_OWI_STS & OWI_STS_PST ) ++#define __owi_get_sts_byte_rdy() ( REG_OWI_STS & OWI_STS_BYTE_RDY ) ++#define __owi_get_sts_bit_rdy() ( REG_OWI_STS & OWI_STS_BIT_RDY ) ++#define __owi_get_sts_pst_rdy() ( REG_OWI_STS & OWI_STS_PST_RDY ) ++ ++/************************************************************************* ++ * TSSI MPEG 2-TS slave interface operation ++ *************************************************************************/ ++#define __tssi_enable() ( REG_TSSI_ENA |= TSSI_ENA_ENA ) ++#define __tssi_disable() ( REG_TSSI_ENA &= ~TSSI_ENA_ENA ) ++#define __tssi_soft_reset() ( REG_TSSI_ENA |= TSSI_ENA_SFT_RST ) ++#define __tssi_dma_enable() ( REG_TSSI_ENA |= TSSI_ENA_DMA_EN ) ++#define __tssi_dma_disable() ( REG_TSSI_ENA &= ~TSSI_ENA_DMA_EN ) ++#define __tssi_filter_enable() ( REG_TSSI_ENA |= TSSI_ENA_PID_EN ) ++#define __tssi_filter_disable() ( REG_TSSI_ENA &= ~TSSI_ENA_PID_EN ) ++ ++/* n = 4, 8, 16 */ ++#define __tssi_set_tigger_num(n) \ ++ do { \ ++ REG_TSSI_CFG &= ~TSSI_CFG_TRIG_MASK; \ ++ REG_TSSI_CFG |= TSSI_CFG_TRIG_##n; \ ++ } while (0) ++ ++#define __tssi_set_wd_1() ( REG_TSSI_CFG |= TSSI_CFG_END_WD ) ++#define __tssi_set_wd_0() ( REG_TSSI_CFG &= ~TSSI_CFG_END_WD ) ++ ++#define __tssi_set_bt_1() ( REG_TSSI_CFG |= TSSI_CFG_END_BD ) ++#define __tssi_set_bt_0() ( REG_TSSI_CFG &= ~TSSI_CFG_END_BD ) ++ ++#define __tssi_set_data_pola_high() ( REG_TSSI_CFG |= TSSI_CFG_TSDI_H ) ++#define __tssi_set_data_pola_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSDI_H ) ++ ++#define __tssi_set_data_use_data0() ( REG_TSSI_CFG |= TSSI_CFG_USE_0 ) ++#define __tssi_set_data_use_data7() ( REG_TSSI_CFG &= ~TSSI_CFG_USE_0 ) ++ ++#define __tssi_select_clk_fast() ( REG_TSSI_CFG &= ~TSSI_CFG_TSCLK_CH ) ++#define __tssi_select_clk_slow() ( REG_TSSI_CFG |= TSSI_CFG_TSCLK_CH ) ++ ++#define __tssi_select_serail_mode() ( REG_TSSI_CFG &= ~TSSI_CFG_PARAL ) ++#define __tssi_select_paral_mode() ( REG_TSSI_CFG |= TSSI_CFG_PARAL ) ++ ++#define __tssi_select_clk_nega_edge() ( REG_TSSI_CFG &= ~TSSI_CFG_TSCLK_P ) ++#define __tssi_select_clk_posi_edge() ( REG_TSSI_CFG |= TSSI_CFG_TSCLK_P ) ++ ++#define __tssi_select_frm_act_high() ( REG_TSSI_CFG |= TSSI_CFG_TSFRM_H ) ++#define __tssi_select_frm_act_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSFRM_H ) ++ ++#define __tssi_select_str_act_high() ( REG_TSSI_CFG |= TSSI_CFG_TSSTR_H ) ++#define __tssi_select_str_act_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSSTR_H ) ++ ++#define __tssi_select_fail_act_high() ( REG_TSSI_CFG |= TSSI_CFG_TSFAIL_H ) ++#define __tssi_select_fail_act_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSFAIL_H ) ++ ++#define __tssi_enable_ovrn_irq() ( REG_TSSI_CTRL &= ~TSSI_CTRL_OVRNM ) ++#define __tssi_disable_ovrn_irq() ( REG_TSSI_CTRL |= TSSI_CTRL_OVRNM ) ++ ++#define __tssi_enable_trig_irq() ( REG_TSSI_CTRL &= ~TSSI_CTRL_TRIGM ) ++#define __tssi_disable_trig_irq() ( REG_TSSI_CTRL |= TSSI_CTRL_TRIGM ) ++ ++#define __tssi_state_is_overrun() ( REG_TSSI_STAT & TSSI_STAT_OVRN ) ++#define __tssi_state_trigger_meet() ( REG_TSSI_STAT & TSSI_STAT_TRIG ) ++#define __tssi_clear_state() ( REG_TSSI_STAT = 0 ) /* write 0??? */ ++#define __tssi_state_clear_overrun() ( REG_TSSI_STAT = TSSI_STAT_OVRN ) ++ ++#define __tssi_enable_filte_pid0() ( REG_TSSI_PEN |= TSSI_PEN_PID0 ) ++#define __tssi_disable_filte_pid0() ( REG_TSSI_PEN &= ~TSSI_PEN_PID0 ) ++ ++/* m = 0, ..., 15 */ ++#define __tssi_enable_pid_filter(m) \ ++ do { \ ++ int n = (m); \ ++ if ( n>=0 && n <(TSSI_PID_MAX*2) ) { \ ++ if ( n >= TSSI_PID_MAX ) n += 8; \ ++ REG_TSSI_PEN |= ( 1 << n ); \ ++ } \ ++ } while (0) ++ ++/* m = 0, ..., 15 */ ++#define __tssi_disable_pid_filter(m) \ ++ do { \ ++ int n = (m); \ ++ if ( n>=0 && n <(TSSI_PID_MAX*2) ) { \ ++ if ( n >= TSSI_PID_MAX ) n += 8; \ ++ REG_TSSI_PEN &= ~( 1 << n ); \ ++ } \ ++ } while (0) ++ ++/* n = 0, ..., 7 */ ++#define __tssi_set_pid0(n, pid0) \ ++ do { \ ++ REG_TSSI_PID(n) &= ~TSSI_PID_PID0_MASK; \ ++ REG_TSSI_PID(n) |= ((pid0)<=0 && n < TSSI_PID_MAX*2) { \ ++ if ( n < TSSI_PID_MAX ) \ ++ __tssi_set_pid0(n, pid); \ ++ else \ ++ __tssi_set_pid1(n-TSSI_PID_MAX, pid); \ ++ } \ ++ }while (0) ++ ++ ++#if 0 ++/************************************************************************* ++ * IPU (Image Processing Unit) ++ *************************************************************************/ ++#define u32 volatile unsigned long ++ ++#define write_reg(reg, val) \ ++do { \ ++ *(u32 *)(reg) = (val); \ ++} while(0) ++ ++#define read_reg(reg, off) (*(u32 *)((reg)+(off))) ++ ++ ++#define set_ipu_fmt(rgb_888_out_fmt, rgb_out_oft, out_fmt, yuv_pkg_out, in_oft, in_fmt ) \ ++({ write_reg( (IPU_V_BASE + REG_D_FMT), ((in_fmt) & IN_FMT_MSK)< Unsigned toggle enable */ ++#define AIC_CR_FLUSH (1 << 8) /* Flush FIFO */ ++#define AIC_CR_EROR (1 << 6) /* Enable ROR interrupt */ ++#define AIC_CR_ETUR (1 << 5) /* Enable TUR interrupt */ ++#define AIC_CR_ERFS (1 << 4) /* Enable RFS interrupt */ ++#define AIC_CR_ETFS (1 << 3) /* Enable TFS interrupt */ ++#define AIC_CR_ENLBF (1 << 2) /* Enable Loopback Function */ ++#define AIC_CR_ERPL (1 << 1) /* Enable Playback Function */ ++#define AIC_CR_EREC (1 << 0) /* Enable Record Function */ ++ ++/* AIC Controller AC-link Control Register 1 (AIC_ACCR1) */ ++ ++#define AIC_ACCR1_RS_BIT 16 /* Receive Valid Slots */ ++#define AIC_ACCR1_RS_MASK (0x3ff << AIC_ACCR1_RS_BIT) ++ #define AIC_ACCR1_RS_SLOT12 (1 << 25) /* Slot 12 valid bit */ ++ #define AIC_ACCR1_RS_SLOT11 (1 << 24) /* Slot 11 valid bit */ ++ #define AIC_ACCR1_RS_SLOT10 (1 << 23) /* Slot 10 valid bit */ ++ #define AIC_ACCR1_RS_SLOT9 (1 << 22) /* Slot 9 valid bit, LFE */ ++ #define AIC_ACCR1_RS_SLOT8 (1 << 21) /* Slot 8 valid bit, Surround Right */ ++ #define AIC_ACCR1_RS_SLOT7 (1 << 20) /* Slot 7 valid bit, Surround Left */ ++ #define AIC_ACCR1_RS_SLOT6 (1 << 19) /* Slot 6 valid bit, PCM Center */ ++ #define AIC_ACCR1_RS_SLOT5 (1 << 18) /* Slot 5 valid bit */ ++ #define AIC_ACCR1_RS_SLOT4 (1 << 17) /* Slot 4 valid bit, PCM Right */ ++ #define AIC_ACCR1_RS_SLOT3 (1 << 16) /* Slot 3 valid bit, PCM Left */ ++#define AIC_ACCR1_XS_BIT 0 /* Transmit Valid Slots */ ++#define AIC_ACCR1_XS_MASK (0x3ff << AIC_ACCR1_XS_BIT) ++ #define AIC_ACCR1_XS_SLOT12 (1 << 9) /* Slot 12 valid bit */ ++ #define AIC_ACCR1_XS_SLOT11 (1 << 8) /* Slot 11 valid bit */ ++ #define AIC_ACCR1_XS_SLOT10 (1 << 7) /* Slot 10 valid bit */ ++ #define AIC_ACCR1_XS_SLOT9 (1 << 6) /* Slot 9 valid bit, LFE */ ++ #define AIC_ACCR1_XS_SLOT8 (1 << 5) /* Slot 8 valid bit, Surround Right */ ++ #define AIC_ACCR1_XS_SLOT7 (1 << 4) /* Slot 7 valid bit, Surround Left */ ++ #define AIC_ACCR1_XS_SLOT6 (1 << 3) /* Slot 6 valid bit, PCM Center */ ++ #define AIC_ACCR1_XS_SLOT5 (1 << 2) /* Slot 5 valid bit */ ++ #define AIC_ACCR1_XS_SLOT4 (1 << 1) /* Slot 4 valid bit, PCM Right */ ++ #define AIC_ACCR1_XS_SLOT3 (1 << 0) /* Slot 3 valid bit, PCM Left */ ++ ++/* AIC Controller AC-link Control Register 2 (AIC_ACCR2) */ ++ ++#define AIC_ACCR2_ERSTO (1 << 18) /* Enable RSTO interrupt */ ++#define AIC_ACCR2_ESADR (1 << 17) /* Enable SADR interrupt */ ++#define AIC_ACCR2_ECADT (1 << 16) /* Enable CADT interrupt */ ++#define AIC_ACCR2_OASS_BIT 8 /* Output Sample Size for AC-link */ ++#define AIC_ACCR2_OASS_MASK (0x3 << AIC_ACCR2_OASS_BIT) ++ #define AIC_ACCR2_OASS_20BIT (0 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 20-bit */ ++ #define AIC_ACCR2_OASS_18BIT (1 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 18-bit */ ++ #define AIC_ACCR2_OASS_16BIT (2 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 16-bit */ ++ #define AIC_ACCR2_OASS_8BIT (3 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 8-bit */ ++#define AIC_ACCR2_IASS_BIT 6 /* Output Sample Size for AC-link */ ++#define AIC_ACCR2_IASS_MASK (0x3 << AIC_ACCR2_IASS_BIT) ++ #define AIC_ACCR2_IASS_20BIT (0 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 20-bit */ ++ #define AIC_ACCR2_IASS_18BIT (1 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 18-bit */ ++ #define AIC_ACCR2_IASS_16BIT (2 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 16-bit */ ++ #define AIC_ACCR2_IASS_8BIT (3 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 8-bit */ ++#define AIC_ACCR2_SO (1 << 3) /* SDATA_OUT output value */ ++#define AIC_ACCR2_SR (1 << 2) /* RESET# pin level */ ++#define AIC_ACCR2_SS (1 << 1) /* SYNC pin level */ ++#define AIC_ACCR2_SA (1 << 0) /* SYNC and SDATA_OUT alternation */ ++ ++/* AIC Controller I2S/MSB-justified Control Register (AIC_I2SCR) */ ++ ++#define AIC_I2SCR_STPBK (1 << 12) /* Stop BIT_CLK for I2S/MSB-justified */ ++#define AIC_I2SCR_WL_BIT 1 /* Input/Output Sample Size for I2S/MSB-justified */ ++#define AIC_I2SCR_WL_MASK (0x7 << AIC_I2SCR_WL_BIT) ++ #define AIC_I2SCR_WL_24BIT (0 << AIC_I2SCR_WL_BIT) /* Word Length is 24 bit */ ++ #define AIC_I2SCR_WL_20BIT (1 << AIC_I2SCR_WL_BIT) /* Word Length is 20 bit */ ++ #define AIC_I2SCR_WL_18BIT (2 << AIC_I2SCR_WL_BIT) /* Word Length is 18 bit */ ++ #define AIC_I2SCR_WL_16BIT (3 << AIC_I2SCR_WL_BIT) /* Word Length is 16 bit */ ++ #define AIC_I2SCR_WL_8BIT (4 << AIC_I2SCR_WL_BIT) /* Word Length is 8 bit */ ++#define AIC_I2SCR_AMSL (1 << 0) /* 0:I2S, 1:MSB-justified */ ++ ++/* AIC Controller FIFO Status Register (AIC_SR) */ ++ ++#define AIC_SR_RFL_BIT 24 /* Receive FIFO Level */ ++#define AIC_SR_RFL_MASK (0x3f << AIC_SR_RFL_BIT) ++#define AIC_SR_TFL_BIT 8 /* Transmit FIFO level */ ++#define AIC_SR_TFL_MASK (0x3f << AIC_SR_TFL_BIT) ++#define AIC_SR_ROR (1 << 6) /* Receive FIFO Overrun */ ++#define AIC_SR_TUR (1 << 5) /* Transmit FIFO Underrun */ ++#define AIC_SR_RFS (1 << 4) /* Receive FIFO Service Request */ ++#define AIC_SR_TFS (1 << 3) /* Transmit FIFO Service Request */ ++ ++/* AIC Controller AC-link Status Register (AIC_ACSR) */ ++ ++#define AIC_ACSR_SLTERR (1 << 21) /* Slot Error Flag */ ++#define AIC_ACSR_CRDY (1 << 20) /* External CODEC Ready Flag */ ++#define AIC_ACSR_CLPM (1 << 19) /* External CODEC low power mode flag */ ++#define AIC_ACSR_RSTO (1 << 18) /* External CODEC regs read status timeout */ ++#define AIC_ACSR_SADR (1 << 17) /* External CODEC regs status addr and data received */ ++#define AIC_ACSR_CADT (1 << 16) /* Command Address and Data Transmitted */ ++ ++/* AIC Controller I2S/MSB-justified Status Register (AIC_I2SSR) */ ++ ++#define AIC_I2SSR_BSY (1 << 2) /* AIC Busy in I2S/MSB-justified format */ ++ ++/* AIC Controller AC97 codec Command Address Register (AIC_ACCAR) */ ++ ++#define AIC_ACCAR_CAR_BIT 0 ++#define AIC_ACCAR_CAR_MASK (0xfffff << AIC_ACCAR_CAR_BIT) ++ ++/* AIC Controller AC97 codec Command Data Register (AIC_ACCDR) */ ++ ++#define AIC_ACCDR_CDR_BIT 0 ++#define AIC_ACCDR_CDR_MASK (0xfffff << AIC_ACCDR_CDR_BIT) ++ ++/* AIC Controller AC97 codec Status Address Register (AIC_ACSAR) */ ++ ++#define AIC_ACSAR_SAR_BIT 0 ++#define AIC_ACSAR_SAR_MASK (0xfffff << AIC_ACSAR_SAR_BIT) ++ ++/* AIC Controller AC97 codec Status Data Register (AIC_ACSDR) */ ++ ++#define AIC_ACSDR_SDR_BIT 0 ++#define AIC_ACSDR_SDR_MASK (0xfffff << AIC_ACSDR_SDR_BIT) ++ ++/* AIC Controller I2S/MSB-justified Clock Divider Register (AIC_I2SDIV) */ ++ ++#define AIC_I2SDIV_DIV_BIT 0 ++#define AIC_I2SDIV_DIV_MASK (0x7f << AIC_I2SDIV_DIV_BIT) ++ #define AIC_I2SDIV_BITCLK_3072KHZ (0x0C << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 3.072MHz */ ++ #define AIC_I2SDIV_BITCLK_2836KHZ (0x0D << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 2.836MHz */ ++ #define AIC_I2SDIV_BITCLK_1418KHZ (0x1A << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.418MHz */ ++ #define AIC_I2SDIV_BITCLK_1024KHZ (0x24 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.024MHz */ ++ #define AIC_I2SDIV_BITCLK_7089KHZ (0x34 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 708.92KHz */ ++ #define AIC_I2SDIV_BITCLK_512KHZ (0x48 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 512.00KHz */ ++ ++ ++/************************************************************************* ++ * ICDC (Internal CODEC) ++ *************************************************************************/ ++ ++#define ICDC_CKCFG (ICDC_BASE + 0x00a0) /* Clock Configure Register */ ++#define ICDC_RGADW (ICDC_BASE + 0x00a4) /* internal register access control */ ++#define ICDC_RGDATA (ICDC_BASE + 0x00a8) /* internal register data output */ ++ ++#define REG_ICDC_CKCFG REG32(ICDC_CKCFG) ++#define REG_ICDC_RGADW REG32(ICDC_RGADW) ++#define REG_ICDC_RGDATA REG32(ICDC_RGDATA) ++ ++/* ICDC Clock Configure Register */ ++#define ICDC_CKCFG_CKRDY (1 << 1) ++#define ICDC_CKCFG_SELAD (1 << 0) ++ ++/* ICDC internal register access control Register */ ++#define ICDC_RGADW_RGWR (1 << 16) ++#define ICDC_RGADW_RGADDR_BIT 8 ++#define ICDC_RGADW_RGADDR_MASK (0x7f << ICDC_RGADW_RGADDR_BIT) ++#define ICDC_RGADW_RGDIN_BIT 0 ++#define ICDC_RGADW_RGDIN_MASK (0xff << ICDC_RGADW_RGDIN_BIT) ++ ++/* ICDC internal register data output Register */ ++#define ICDC_RGDATA_IRQ (1 << 8) ++#define ICDC_RGDATA_RGDOUT_BIT 0 ++#define ICDC_RGDATA_RGDOUT_MASK (0xff << ICDC_RGDATA_RGDOUT_BIT) ++ ++/************************************************************************* ++ * PCM Controller ++ *************************************************************************/ ++ ++#define PCM_CTL (PCM_BASE + 0x000) ++#define PCM_CFG (PCM_BASE + 0x004) ++#define PCM_DP (PCM_BASE + 0x008) ++#define PCM_INTC (PCM_BASE + 0x00c) ++#define PCM_INTS (PCM_BASE + 0x010) ++#define PCM_DIV (PCM_BASE + 0x014) ++ ++#define REG_PCM_CTL REG32(PCM_CTL) ++#define REG_PCM_CFG REG32(PCM_CFG) ++#define REG_PCM_DP REG32(PCM_DP) ++#define REG_PCM_INTC REG32(PCM_INTC) ++#define REG_PCM_INTS REG32(PCM_INTS) ++#define REG_PCM_DIV REG32(PCM_DIV) ++ ++/* PCM Controller control Register (PCM_CTL) */ ++ ++#define PCM_CTL_ERDMA (1 << 9) /* Enable Receive DMA */ ++#define PCM_CTL_ETDMA (1 << 8) /* Enable Transmit DMA */ ++#define PCM_CTL_LSMP (1 << 7) /* Play Zero sample or last sample */ ++#define PCM_CTL_ERPL (1 << 6) /* Enable Playing Back Function */ ++#define PCM_CTL_EREC (1 << 5) /* Enable Recording Function */ ++#define PCM_CTL_FLUSH (1 << 4) /* FIFO flush */ ++#define PCM_CTL_RST (1 << 3) /* Reset PCM */ ++#define PCM_CTL_CLKEN (1 << 1) /* Enable the clock division logic */ ++#define PCM_CTL_PCMEN (1 << 0) /* Enable PCM module */ ++ ++/* PCM Controller configure Register (PCM_CFG) */ ++ ++#define PCM_CFG_SLOT_BIT 13 ++#define PCM_CFG_SLOT_MASK (0x3 << PCM_CFG_SLOT_BIT) ++ #define PCM_CFG_SLOT_0 (0 << PCM_CFG_SLOT_BIT) /* Slot is 0 */ ++ #define PCM_CFG_SLOT_1 (1 << PCM_CFG_SLOT_BIT) /* Slot is 1 */ ++ #define PCM_CFG_SLOT_2 (2 << PCM_CFG_SLOT_BIT) /* Slot is 2 */ ++ #define PCM_CFG_SLOT_3 (3 << PCM_CFG_SLOT_BIT) /* Slot is 3 */ ++#define PCM_CFG_ISS_BIT 12 ++#define PCM_CFG_ISS_MASK (0x1 << PCM_CFG_ISS_BIT) ++ #define PCM_CFG_ISS_8 (0 << PCM_CFG_ISS_BIT) ++ #define PCM_CFG_ISS_16 (1 << PCM_CFG_ISS_BIT) ++#define PCM_CFG_OSS_BIT 11 ++#define PCM_CFG_OSS_MASK (0x1 << PCM_CFG_OSS_BIT) ++ #define PCM_CFG_OSS_8 (0 << PCM_CFG_OSS_BIT) ++ #define PCM_CFG_OSS_16 (1 << PCM_CFG_OSS_BIT) ++#define PCM_CFG_IMSBPOS (1 << 10) ++#define PCM_CFG_OMSBPOS (1 << 9) ++#define PCM_CFG_RFTH_BIT 5 /* Receive FIFO Threshold */ ++#define PCM_CFG_RFTH_MASK (0xf << PCM_CFG_RFTH_BIT) ++#define PCM_CFG_TFTH_BIT 1 /* Transmit FIFO Threshold */ ++#define PCM_CFG_TFTH_MASK (0xf << PCM_CFG_TFTH_BIT) ++#define PCM_CFG_MODE (0x0 << 0) ++ ++/* PCM Controller interrupt control Register (PCM_INTC) */ ++ ++#define PCM_INTC_ETFS (1 << 3) ++#define PCM_INTC_ETUR (1 << 2) ++#define PCM_INTC_ERFS (1 << 1) ++#define PCM_INTC_EROR (1 << 0) ++ ++/* PCM Controller interrupt status Register (PCM_INTS) */ ++ ++#define PCM_INTS_RSTS (1 << 14) /* Reset or flush has not complete */ ++#define PCM_INTS_TFL_BIT 9 ++#define PCM_INTS_TFL_MASK (0x1f << PCM_INTS_TFL_BIT) ++#define PCM_INTS_TFS (1 << 8) /* Tranmit FIFO Service Request */ ++#define PCM_INTS_TUR (1 << 7) /* Transmit FIFO Under Run */ ++#define PCM_INTS_RFL_BIT 2 ++#define PCM_INTS_RFL_MASK (0x1f << PCM_INTS_RFL_BIT) ++#define PCM_INTS_RFS (1 << 1) /* Receive FIFO Service Request */ ++#define PCM_INTS_ROR (1 << 0) /* Receive FIFO Over Run */ ++ ++/* PCM Controller clock division Register (PCM_DIV) */ ++#define PCM_DIV_SYNL_BIT 11 ++#define PCM_DIV_SYNL_MASK (0x3f << PCM_DIV_SYNL_BIT) ++#define PCM_DIV_SYNDIV_BIT 6 ++#define PCM_DIV_SYNDIV_MASK (0x1f << PCM_DIV_SYNDIV_BIT) ++#define PCM_DIV_CLKDIV_BIT 0 ++#define PCM_DIV_CLKDIV_MASK (0x3f << PCM_DIV_CLKDIV_BIT) ++ ++ ++/************************************************************************* ++ * I2C ++ *************************************************************************/ ++#define I2C_DR (I2C_BASE + 0x000) ++#define I2C_CR (I2C_BASE + 0x004) ++#define I2C_SR (I2C_BASE + 0x008) ++#define I2C_GR (I2C_BASE + 0x00C) ++ ++#define REG_I2C_DR REG8(I2C_DR) ++#define REG_I2C_CR REG8(I2C_CR) ++#define REG_I2C_SR REG8(I2C_SR) ++#define REG_I2C_GR REG16(I2C_GR) ++ ++/* I2C Control Register (I2C_CR) */ ++ ++#define I2C_CR_IEN (1 << 4) ++#define I2C_CR_STA (1 << 3) ++#define I2C_CR_STO (1 << 2) ++#define I2C_CR_AC (1 << 1) ++#define I2C_CR_I2CE (1 << 0) ++ ++/* I2C Status Register (I2C_SR) */ ++ ++#define I2C_SR_STX (1 << 4) ++#define I2C_SR_BUSY (1 << 3) ++#define I2C_SR_TEND (1 << 2) ++#define I2C_SR_DRF (1 << 1) ++#define I2C_SR_ACKF (1 << 0) ++ ++ ++/************************************************************************* ++ * SSI (Synchronous Serial Interface) ++ *************************************************************************/ ++/* n = 0, 1 (SSI0, SSI1) */ ++#define SSI_DR(n) (SSI_BASE + 0x000 + (n)*0x2000) ++#define SSI_CR0(n) (SSI_BASE + 0x004 + (n)*0x2000) ++#define SSI_CR1(n) (SSI_BASE + 0x008 + (n)*0x2000) ++#define SSI_SR(n) (SSI_BASE + 0x00C + (n)*0x2000) ++#define SSI_ITR(n) (SSI_BASE + 0x010 + (n)*0x2000) ++#define SSI_ICR(n) (SSI_BASE + 0x014 + (n)*0x2000) ++#define SSI_GR(n) (SSI_BASE + 0x018 + (n)*0x2000) ++ ++#define REG_SSI_DR(n) REG32(SSI_DR(n)) ++#define REG_SSI_CR0(n) REG16(SSI_CR0(n)) ++#define REG_SSI_CR1(n) REG32(SSI_CR1(n)) ++#define REG_SSI_SR(n) REG32(SSI_SR(n)) ++#define REG_SSI_ITR(n) REG16(SSI_ITR(n)) ++#define REG_SSI_ICR(n) REG8(SSI_ICR(n)) ++#define REG_SSI_GR(n) REG16(SSI_GR(n)) ++ ++/* SSI Data Register (SSI_DR) */ ++ ++#define SSI_DR_GPC_BIT 0 ++#define SSI_DR_GPC_MASK (0x1ff << SSI_DR_GPC_BIT) ++ ++#define SSI_MAX_FIFO_ENTRIES 128 /* 128 txfifo and 128 rxfifo */ ++ ++/* SSI Control Register 0 (SSI_CR0) */ ++ ++#define SSI_CR0_SSIE (1 << 15) ++#define SSI_CR0_TIE (1 << 14) ++#define SSI_CR0_RIE (1 << 13) ++#define SSI_CR0_TEIE (1 << 12) ++#define SSI_CR0_REIE (1 << 11) ++#define SSI_CR0_LOOP (1 << 10) ++#define SSI_CR0_RFINE (1 << 9) ++#define SSI_CR0_RFINC (1 << 8) ++#define SSI_CR0_EACLRUN (1 << 7) /* hardware auto clear underrun when TxFifo no empty */ ++#define SSI_CR0_FSEL (1 << 6) ++#define SSI_CR0_TFLUSH (1 << 2) ++#define SSI_CR0_RFLUSH (1 << 1) ++#define SSI_CR0_DISREV (1 << 0) ++ ++/* SSI Control Register 1 (SSI_CR1) */ ++ ++#define SSI_CR1_FRMHL_BIT 30 ++#define SSI_CR1_FRMHL_MASK (0x3 << SSI_CR1_FRMHL_BIT) ++ #define SSI_CR1_FRMHL_CELOW_CE2LOW (0 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is low valid */ ++ #define SSI_CR1_FRMHL_CEHIGH_CE2LOW (1 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is low valid */ ++ #define SSI_CR1_FRMHL_CELOW_CE2HIGH (2 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is high valid */ ++ #define SSI_CR1_FRMHL_CEHIGH_CE2HIGH (3 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is high valid */ ++#define SSI_CR1_TFVCK_BIT 28 ++#define SSI_CR1_TFVCK_MASK (0x3 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_0 (0 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_1 (1 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_2 (2 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_3 (3 << SSI_CR1_TFVCK_BIT) ++#define SSI_CR1_TCKFI_BIT 26 ++#define SSI_CR1_TCKFI_MASK (0x3 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_0 (0 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_1 (1 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_2 (2 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_3 (3 << SSI_CR1_TCKFI_BIT) ++#define SSI_CR1_LFST (1 << 25) ++#define SSI_CR1_ITFRM (1 << 24) ++#define SSI_CR1_UNFIN (1 << 23) ++#define SSI_CR1_MULTS (1 << 22) ++#define SSI_CR1_FMAT_BIT 20 ++#define SSI_CR1_FMAT_MASK (0x3 << SSI_CR1_FMAT_BIT) ++ #define SSI_CR1_FMAT_SPI (0 << SSI_CR1_FMAT_BIT) /* Motorola¡¯s SPI format */ ++ #define SSI_CR1_FMAT_SSP (1 << SSI_CR1_FMAT_BIT) /* TI's SSP format */ ++ #define SSI_CR1_FMAT_MW1 (2 << SSI_CR1_FMAT_BIT) /* National Microwire 1 format */ ++ #define SSI_CR1_FMAT_MW2 (3 << SSI_CR1_FMAT_BIT) /* National Microwire 2 format */ ++#define SSI_CR1_TTRG_BIT 16 /* SSI1 TX trigger */ ++#define SSI_CR1_TTRG_MASK (0xf << SSI_CR1_TTRG_BIT) ++#define SSI_CR1_MCOM_BIT 12 ++#define SSI_CR1_MCOM_MASK (0xf << SSI_CR1_MCOM_BIT) ++ #define SSI_CR1_MCOM_1BIT (0x0 << SSI_CR1_MCOM_BIT) /* 1-bit command selected */ ++ #define SSI_CR1_MCOM_2BIT (0x1 << SSI_CR1_MCOM_BIT) /* 2-bit command selected */ ++ #define SSI_CR1_MCOM_3BIT (0x2 << SSI_CR1_MCOM_BIT) /* 3-bit command selected */ ++ #define SSI_CR1_MCOM_4BIT (0x3 << SSI_CR1_MCOM_BIT) /* 4-bit command selected */ ++ #define SSI_CR1_MCOM_5BIT (0x4 << SSI_CR1_MCOM_BIT) /* 5-bit command selected */ ++ #define SSI_CR1_MCOM_6BIT (0x5 << SSI_CR1_MCOM_BIT) /* 6-bit command selected */ ++ #define SSI_CR1_MCOM_7BIT (0x6 << SSI_CR1_MCOM_BIT) /* 7-bit command selected */ ++ #define SSI_CR1_MCOM_8BIT (0x7 << SSI_CR1_MCOM_BIT) /* 8-bit command selected */ ++ #define SSI_CR1_MCOM_9BIT (0x8 << SSI_CR1_MCOM_BIT) /* 9-bit command selected */ ++ #define SSI_CR1_MCOM_10BIT (0x9 << SSI_CR1_MCOM_BIT) /* 10-bit command selected */ ++ #define SSI_CR1_MCOM_11BIT (0xA << SSI_CR1_MCOM_BIT) /* 11-bit command selected */ ++ #define SSI_CR1_MCOM_12BIT (0xB << SSI_CR1_MCOM_BIT) /* 12-bit command selected */ ++ #define SSI_CR1_MCOM_13BIT (0xC << SSI_CR1_MCOM_BIT) /* 13-bit command selected */ ++ #define SSI_CR1_MCOM_14BIT (0xD << SSI_CR1_MCOM_BIT) /* 14-bit command selected */ ++ #define SSI_CR1_MCOM_15BIT (0xE << SSI_CR1_MCOM_BIT) /* 15-bit command selected */ ++ #define SSI_CR1_MCOM_16BIT (0xF << SSI_CR1_MCOM_BIT) /* 16-bit command selected */ ++#define SSI_CR1_RTRG_BIT 8 /* SSI RX trigger */ ++#define SSI_CR1_RTRG_MASK (0xf << SSI_CR1_RTRG_BIT) ++#define SSI_CR1_FLEN_BIT 4 ++#define SSI_CR1_FLEN_MASK (0xf << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_2BIT (0x0 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_3BIT (0x1 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_4BIT (0x2 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_5BIT (0x3 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_6BIT (0x4 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_7BIT (0x5 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_8BIT (0x6 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_9BIT (0x7 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_10BIT (0x8 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_11BIT (0x9 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_12BIT (0xA << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_13BIT (0xB << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_14BIT (0xC << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_15BIT (0xD << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_16BIT (0xE << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_17BIT (0xF << SSI_CR1_FLEN_BIT) ++#define SSI_CR1_PHA (1 << 1) ++#define SSI_CR1_POL (1 << 0) ++ ++/* SSI Status Register (SSI_SR) */ ++ ++#define SSI_SR_TFIFONUM_BIT 16 ++#define SSI_SR_TFIFONUM_MASK (0xff << SSI_SR_TFIFONUM_BIT) ++#define SSI_SR_RFIFONUM_BIT 8 ++#define SSI_SR_RFIFONUM_MASK (0xff << SSI_SR_RFIFONUM_BIT) ++#define SSI_SR_END (1 << 7) ++#define SSI_SR_BUSY (1 << 6) ++#define SSI_SR_TFF (1 << 5) ++#define SSI_SR_RFE (1 << 4) ++#define SSI_SR_TFHE (1 << 3) ++#define SSI_SR_RFHF (1 << 2) ++#define SSI_SR_UNDR (1 << 1) ++#define SSI_SR_OVER (1 << 0) ++ ++/* SSI Interval Time Control Register (SSI_ITR) */ ++ ++#define SSI_ITR_CNTCLK (1 << 15) ++#define SSI_ITR_IVLTM_BIT 0 ++#define SSI_ITR_IVLTM_MASK (0x7fff << SSI_ITR_IVLTM_BIT) ++ ++ ++/************************************************************************* ++ * MSC ++ ************************************************************************/ ++/* n = 0, 1 (MSC0, MSC1) */ ++#define MSC_STRPCL(n) (MSC_BASE + (n)*0x1000 + 0x000) ++#define MSC_STAT(n) (MSC_BASE + (n)*0x1000 + 0x004) ++#define MSC_CLKRT(n) (MSC_BASE + (n)*0x1000 + 0x008) ++#define MSC_CMDAT(n) (MSC_BASE + (n)*0x1000 + 0x00C) ++#define MSC_RESTO(n) (MSC_BASE + (n)*0x1000 + 0x010) ++#define MSC_RDTO(n) (MSC_BASE + (n)*0x1000 + 0x014) ++#define MSC_BLKLEN(n) (MSC_BASE + (n)*0x1000 + 0x018) ++#define MSC_NOB(n) (MSC_BASE + (n)*0x1000 + 0x01C) ++#define MSC_SNOB(n) (MSC_BASE + (n)*0x1000 + 0x020) ++#define MSC_IMASK(n) (MSC_BASE + (n)*0x1000 + 0x024) ++#define MSC_IREG(n) (MSC_BASE + (n)*0x1000 + 0x028) ++#define MSC_CMD(n) (MSC_BASE + (n)*0x1000 + 0x02C) ++#define MSC_ARG(n) (MSC_BASE + (n)*0x1000 + 0x030) ++#define MSC_RES(n) (MSC_BASE + (n)*0x1000 + 0x034) ++#define MSC_RXFIFO(n) (MSC_BASE + (n)*0x1000 + 0x038) ++#define MSC_TXFIFO(n) (MSC_BASE + (n)*0x1000 + 0x03C) ++#define MSC_LPM(n) (MSC_BASE + (n)*0x1000 + 0x040) ++ ++#define REG_MSC_STRPCL(n) REG16(MSC_STRPCL(n)) ++#define REG_MSC_STAT(n) REG32(MSC_STAT(n)) ++#define REG_MSC_CLKRT(n) REG16(MSC_CLKRT(n)) ++#define REG_MSC_CMDAT(n) REG32(MSC_CMDAT(n)) ++#define REG_MSC_RESTO(n) REG16(MSC_RESTO(n)) ++#define REG_MSC_RDTO(n) REG16(MSC_RDTO(n)) ++#define REG_MSC_BLKLEN(n) REG16(MSC_BLKLEN(n)) ++#define REG_MSC_NOB(n) REG16(MSC_NOB(n)) ++#define REG_MSC_SNOB(n) REG16(MSC_SNOB(n)) ++#define REG_MSC_IMASK(n) REG32(MSC_IMASK(n)) ++#define REG_MSC_IREG(n) REG16(MSC_IREG(n)) ++#define REG_MSC_CMD(n) REG8(MSC_CMD(n)) ++#define REG_MSC_ARG(n) REG32(MSC_ARG(n)) ++#define REG_MSC_RES(n) REG16(MSC_RES(n)) ++#define REG_MSC_RXFIFO(n) REG32(MSC_RXFIFO(n)) ++#define REG_MSC_TXFIFO(n) REG32(MSC_TXFIFO(n)) ++#define REG_MSC_LPM(n) REG32(MSC_LPM(n)) ++ ++/* MSC Clock and Control Register (MSC_STRPCL) */ ++#define MSC_STRPCL_SEND_CCSD (1 << 15) /*send command completion signal disable to ceata */ ++#define MSC_STRPCL_SEND_AS_CCSD (1 << 14) /*send internally generated stop after sending ccsd */ ++#define MSC_STRPCL_EXIT_MULTIPLE (1 << 7) ++#define MSC_STRPCL_EXIT_TRANSFER (1 << 6) ++#define MSC_STRPCL_START_READWAIT (1 << 5) ++#define MSC_STRPCL_STOP_READWAIT (1 << 4) ++#define MSC_STRPCL_RESET (1 << 3) ++#define MSC_STRPCL_START_OP (1 << 2) ++#define MSC_STRPCL_CLOCK_CONTROL_BIT 0 ++#define MSC_STRPCL_CLOCK_CONTROL_MASK (0x3 << MSC_STRPCL_CLOCK_CONTROL_BIT) ++ #define MSC_STRPCL_CLOCK_CONTROL_STOP (0x1 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Stop MMC/SD clock */ ++ #define MSC_STRPCL_CLOCK_CONTROL_START (0x2 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Start MMC/SD clock */ ++ ++/* MSC Status Register (MSC_STAT) */ ++#define MSC_STAT_AUTO_CMD_DONE (1 << 31) /*12 is internally generated by controller has finished */ ++#define MSC_STAT_IS_RESETTING (1 << 15) ++#define MSC_STAT_SDIO_INT_ACTIVE (1 << 14) ++#define MSC_STAT_PRG_DONE (1 << 13) ++#define MSC_STAT_DATA_TRAN_DONE (1 << 12) ++#define MSC_STAT_END_CMD_RES (1 << 11) ++#define MSC_STAT_DATA_FIFO_AFULL (1 << 10) ++#define MSC_STAT_IS_READWAIT (1 << 9) ++#define MSC_STAT_CLK_EN (1 << 8) ++#define MSC_STAT_DATA_FIFO_FULL (1 << 7) ++#define MSC_STAT_DATA_FIFO_EMPTY (1 << 6) ++#define MSC_STAT_CRC_RES_ERR (1 << 5) ++#define MSC_STAT_CRC_READ_ERROR (1 << 4) ++#define MSC_STAT_CRC_WRITE_ERROR_BIT 2 ++#define MSC_STAT_CRC_WRITE_ERROR_MASK (0x3 << MSC_STAT_CRC_WRITE_ERROR_BIT) ++ #define MSC_STAT_CRC_WRITE_ERROR_NO (0 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No error on transmission of data */ ++ #define MSC_STAT_CRC_WRITE_ERROR (1 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* Card observed erroneous transmission of data */ ++ #define MSC_STAT_CRC_WRITE_ERROR_NOSTS (2 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No CRC status is sent back */ ++#define MSC_STAT_TIME_OUT_RES (1 << 1) ++#define MSC_STAT_TIME_OUT_READ (1 << 0) ++ ++/* MSC Bus Clock Control Register (MSC_CLKRT) */ ++#define MSC_CLKRT_CLK_RATE_BIT 0 ++#define MSC_CLKRT_CLK_RATE_MASK (0x7 << MSC_CLKRT_CLK_RATE_BIT) ++ #define MSC_CLKRT_CLK_RATE_DIV_1 (0x0 << MSC_CLKRT_CLK_RATE_BIT) /* CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_2 (0x1 << MSC_CLKRT_CLK_RATE_BIT) /* 1/2 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_4 (0x2 << MSC_CLKRT_CLK_RATE_BIT) /* 1/4 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_8 (0x3 << MSC_CLKRT_CLK_RATE_BIT) /* 1/8 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_16 (0x4 << MSC_CLKRT_CLK_RATE_BIT) /* 1/16 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_32 (0x5 << MSC_CLKRT_CLK_RATE_BIT) /* 1/32 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_64 (0x6 << MSC_CLKRT_CLK_RATE_BIT) /* 1/64 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_128 (0x7 << MSC_CLKRT_CLK_RATE_BIT) /* 1/128 of CLK_SRC */ ++ ++/* MSC Command Sequence Control Register (MSC_CMDAT) */ ++#define MSC_CMDAT_CCS_EXPECTED (1 << 31) /* interrupts are enabled in ce-ata */ ++#define MSC_CMDAT_READ_CEATA (1 << 30) ++#define MSC_CMDAT_SDIO_PRDT (1 << 17) /* exact 2 cycle */ ++#define MSC_CMDAT_SEND_AS_STOP (1 << 16) ++#define MSC_CMDAT_RTRG_BIT 14 ++ #define MSC_CMDAT_RTRG_EQUALT_8 (0x0 << MSC_CMDAT_RTRG_BIT) ++ #define MSC_CMDAT_RTRG_EQUALT_16 (0x1 << MSC_CMDAT_RTRG_BIT) /* reset value */ ++ #define MSC_CMDAT_RTRG_EQUALT_24 (0x2 << MSC_CMDAT_RTRG_BIT) ++ ++#define MSC_CMDAT_TTRG_BIT 12 ++ #define MSC_CMDAT_TTRG_LESS_8 (0x0 << MSC_CMDAT_TTRG_BIT) ++ #define MSC_CMDAT_TTRG_LESS_16 (0x1 << MSC_CMDAT_TTRG_BIT) /*reset value */ ++ #define MSC_CMDAT_TTRG_LESS_24 (0x2 << MSC_CMDAT_TTRG_BIT) ++#define MSC_CMDAT_STOP_ABORT (1 << 11) ++#define MSC_CMDAT_BUS_WIDTH_BIT 9 ++#define MSC_CMDAT_BUS_WIDTH_MASK (0x3 << MSC_CMDAT_BUS_WIDTH_BIT) ++ #define MSC_CMDAT_BUS_WIDTH_1BIT (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) /* 1-bit data bus */ ++ #define MSC_CMDAT_BUS_WIDTH_4BIT (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) /* 4-bit data bus */ ++ #define MSC_CMDAT_BUS_WIDTH_8BIT (0x3 << MSC_CMDAT_BUS_WIDTH_BIT) /* 8-bit data bus */ ++#define MSC_CMDAT_DMA_EN (1 << 8) ++#define MSC_CMDAT_INIT (1 << 7) ++#define MSC_CMDAT_BUSY (1 << 6) ++#define MSC_CMDAT_STREAM_BLOCK (1 << 5) ++#define MSC_CMDAT_WRITE (1 << 4) ++#define MSC_CMDAT_READ (0 << 4) ++#define MSC_CMDAT_DATA_EN (1 << 3) ++#define MSC_CMDAT_RESPONSE_BIT 0 ++#define MSC_CMDAT_RESPONSE_MASK (0x7 << MSC_CMDAT_RESPONSE_BIT) ++ #define MSC_CMDAT_RESPONSE_NONE (0x0 << MSC_CMDAT_RESPONSE_BIT) /* No response */ ++ #define MSC_CMDAT_RESPONSE_R1 (0x1 << MSC_CMDAT_RESPONSE_BIT) /* Format R1 and R1b */ ++ #define MSC_CMDAT_RESPONSE_R2 (0x2 << MSC_CMDAT_RESPONSE_BIT) /* Format R2 */ ++ #define MSC_CMDAT_RESPONSE_R3 (0x3 << MSC_CMDAT_RESPONSE_BIT) /* Format R3 */ ++ #define MSC_CMDAT_RESPONSE_R4 (0x4 << MSC_CMDAT_RESPONSE_BIT) /* Format R4 */ ++ #define MSC_CMDAT_RESPONSE_R5 (0x5 << MSC_CMDAT_RESPONSE_BIT) /* Format R5 */ ++ #define MSC_CMDAT_RESPONSE_R6 (0x6 << MSC_CMDAT_RESPONSE_BIT) /* Format R6 */ ++ ++#define CMDAT_DMA_EN (1 << 8) ++#define CMDAT_INIT (1 << 7) ++#define CMDAT_BUSY (1 << 6) ++#define CMDAT_STREAM (1 << 5) ++#define CMDAT_WRITE (1 << 4) ++#define CMDAT_DATA_EN (1 << 3) ++ ++/* MSC Interrupts Mask Register (MSC_IMASK) */ ++#define MSC_IMASK_AUTO_CMD_DONE (1 << 8) ++#define MSC_IMASK_SDIO (1 << 7) ++#define MSC_IMASK_TXFIFO_WR_REQ (1 << 6) ++#define MSC_IMASK_RXFIFO_RD_REQ (1 << 5) ++#define MSC_IMASK_END_CMD_RES (1 << 2) ++#define MSC_IMASK_PRG_DONE (1 << 1) ++#define MSC_IMASK_DATA_TRAN_DONE (1 << 0) ++ ++/* MSC Interrupts Status Register (MSC_IREG) */ ++#define MSC_IREG_AUTO_CMD_DONE (1 << 8) ++#define MSC_IREG_SDIO (1 << 7) ++#define MSC_IREG_TXFIFO_WR_REQ (1 << 6) ++#define MSC_IREG_RXFIFO_RD_REQ (1 << 5) ++#define MSC_IREG_END_CMD_RES (1 << 2) ++#define MSC_IREG_PRG_DONE (1 << 1) ++#define MSC_IREG_DATA_TRAN_DONE (1 << 0) ++ ++/* MSC Low Power Mode Register (MSC_LPM) */ ++#define MSC_SET_LPM (1 << 0) ++ ++/************************************************************************* ++ * EMC (External Memory Controller) ++ *************************************************************************/ ++#define EMC_BCR (EMC_BASE + 0x00) /* Bus Control Register */ ++#define EMC_SMCR0 (EMC_BASE + 0x10) /* Static Memory Control Register 0 */ ++#define EMC_SMCR1 (EMC_BASE + 0x14) /* Static Memory Control Register 1 */ ++#define EMC_SMCR2 (EMC_BASE + 0x18) /* Static Memory Control Register 2 */ ++#define EMC_SMCR3 (EMC_BASE + 0x1c) /* Static Memory Control Register 3 */ ++#define EMC_SMCR4 (EMC_BASE + 0x20) /* Static Memory Control Register 4 */ ++#define EMC_SACR0 (EMC_BASE + 0x30) /* Static Memory Bank 0 Addr Config Reg */ ++#define EMC_SACR1 (EMC_BASE + 0x34) /* Static Memory Bank 1 Addr Config Reg */ ++#define EMC_SACR2 (EMC_BASE + 0x38) /* Static Memory Bank 2 Addr Config Reg */ ++#define EMC_SACR3 (EMC_BASE + 0x3c) /* Static Memory Bank 3 Addr Config Reg */ ++#define EMC_SACR4 (EMC_BASE + 0x40) /* Static Memory Bank 4 Addr Config Reg */ ++ ++#define EMC_NFCSR (EMC_BASE + 0x050) /* NAND Flash Control/Status Register */ ++ ++#define EMC_DMCR (EMC_BASE + 0x80) /* DRAM Control Register */ ++#define EMC_RTCSR (EMC_BASE + 0x84) /* Refresh Time Control/Status Register */ ++#define EMC_RTCNT (EMC_BASE + 0x88) /* Refresh Timer Counter */ ++#define EMC_RTCOR (EMC_BASE + 0x8c) /* Refresh Time Constant Register */ ++#define EMC_DMAR0 (EMC_BASE + 0x90) /* SDRAM Bank 0 Addr Config Register */ ++#define EMC_DMAR1 (EMC_BASE + 0x94) /* SDRAM Bank 1 Addr Config Register */ ++#define EMC_SDMR0 (EMC_BASE + 0xa000) /* Mode Register of SDRAM bank 0 */ ++ ++#define REG_EMC_BCR REG32(EMC_BCR) ++#define REG_EMC_SMCR0 REG32(EMC_SMCR0) ++#define REG_EMC_SMCR1 REG32(EMC_SMCR1) ++#define REG_EMC_SMCR2 REG32(EMC_SMCR2) ++#define REG_EMC_SMCR3 REG32(EMC_SMCR3) ++#define REG_EMC_SMCR4 REG32(EMC_SMCR4) ++#define REG_EMC_SACR0 REG32(EMC_SACR0) ++#define REG_EMC_SACR1 REG32(EMC_SACR1) ++#define REG_EMC_SACR2 REG32(EMC_SACR2) ++#define REG_EMC_SACR3 REG32(EMC_SACR3) ++#define REG_EMC_SACR4 REG32(EMC_SACR4) ++ ++#define REG_EMC_NFCSR REG32(EMC_NFCSR) ++ ++#define REG_EMC_DMCR REG32(EMC_DMCR) ++#define REG_EMC_RTCSR REG16(EMC_RTCSR) ++#define REG_EMC_RTCNT REG16(EMC_RTCNT) ++#define REG_EMC_RTCOR REG16(EMC_RTCOR) ++#define REG_EMC_DMAR0 REG32(EMC_DMAR0) ++#define REG_EMC_DMAR1 REG32(EMC_DMAR1) ++ ++/* Bus Control Register */ ++#define EMC_BCR_BT_SEL_BIT 30 ++#define EMC_BCR_BT_SEL_MASK (0x3 << EMC_BCR_BT_SEL_BIT) ++#define EMC_BCR_PK_SEL (1 << 24) ++#define EMC_BCR_BSR_MASK (1 << 2) /* Nand and SDRAM Bus Share Select: 0, share; 1, unshare */ ++ #define EMC_BCR_BSR_SHARE (0 << 2) ++ #define EMC_BCR_BSR_UNSHARE (1 << 2) ++#define EMC_BCR_BRE (1 << 1) ++#define EMC_BCR_ENDIAN (1 << 0) ++ ++/* Static Memory Control Register */ ++#define EMC_SMCR_STRV_BIT 24 ++#define EMC_SMCR_STRV_MASK (0x0f << EMC_SMCR_STRV_BIT) ++#define EMC_SMCR_TAW_BIT 20 ++#define EMC_SMCR_TAW_MASK (0x0f << EMC_SMCR_TAW_BIT) ++#define EMC_SMCR_TBP_BIT 16 ++#define EMC_SMCR_TBP_MASK (0x0f << EMC_SMCR_TBP_BIT) ++#define EMC_SMCR_TAH_BIT 12 ++#define EMC_SMCR_TAH_MASK (0x07 << EMC_SMCR_TAH_BIT) ++#define EMC_SMCR_TAS_BIT 8 ++#define EMC_SMCR_TAS_MASK (0x07 << EMC_SMCR_TAS_BIT) ++#define EMC_SMCR_BW_BIT 6 ++#define EMC_SMCR_BW_MASK (0x03 << EMC_SMCR_BW_BIT) ++ #define EMC_SMCR_BW_8BIT (0 << EMC_SMCR_BW_BIT) ++ #define EMC_SMCR_BW_16BIT (1 << EMC_SMCR_BW_BIT) ++ #define EMC_SMCR_BW_32BIT (2 << EMC_SMCR_BW_BIT) ++#define EMC_SMCR_BCM (1 << 3) ++#define EMC_SMCR_BL_BIT 1 ++#define EMC_SMCR_BL_MASK (0x03 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_4 (0 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_8 (1 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_16 (2 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_32 (3 << EMC_SMCR_BL_BIT) ++#define EMC_SMCR_SMT (1 << 0) ++ ++/* Static Memory Bank Addr Config Reg */ ++#define EMC_SACR_BASE_BIT 8 ++#define EMC_SACR_BASE_MASK (0xff << EMC_SACR_BASE_BIT) ++#define EMC_SACR_MASK_BIT 0 ++#define EMC_SACR_MASK_MASK (0xff << EMC_SACR_MASK_BIT) ++ ++/* NAND Flash Control/Status Register */ ++#define EMC_NFCSR_NFCE4 (1 << 7) /* NAND Flash Enable */ ++#define EMC_NFCSR_NFE4 (1 << 6) /* NAND Flash FCE# Assertion Enable */ ++#define EMC_NFCSR_NFCE3 (1 << 5) ++#define EMC_NFCSR_NFE3 (1 << 4) ++#define EMC_NFCSR_NFCE2 (1 << 3) ++#define EMC_NFCSR_NFE2 (1 << 2) ++#define EMC_NFCSR_NFCE1 (1 << 1) ++#define EMC_NFCSR_NFE1 (1 << 0) ++ ++/* DRAM Control Register */ ++#define EMC_DMCR_BW_BIT 31 ++#define EMC_DMCR_BW (1 << EMC_DMCR_BW_BIT) ++#define EMC_DMCR_CA_BIT 26 ++#define EMC_DMCR_CA_MASK (0x07 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_8 (0 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_9 (1 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_10 (2 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_11 (3 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_12 (4 << EMC_DMCR_CA_BIT) ++#define EMC_DMCR_RMODE (1 << 25) ++#define EMC_DMCR_RFSH (1 << 24) ++#define EMC_DMCR_MRSET (1 << 23) ++#define EMC_DMCR_RA_BIT 20 ++#define EMC_DMCR_RA_MASK (0x03 << EMC_DMCR_RA_BIT) ++ #define EMC_DMCR_RA_11 (0 << EMC_DMCR_RA_BIT) ++ #define EMC_DMCR_RA_12 (1 << EMC_DMCR_RA_BIT) ++ #define EMC_DMCR_RA_13 (2 << EMC_DMCR_RA_BIT) ++#define EMC_DMCR_BA_BIT 19 ++#define EMC_DMCR_BA (1 << EMC_DMCR_BA_BIT) ++#define EMC_DMCR_PDM (1 << 18) ++#define EMC_DMCR_EPIN (1 << 17) ++#define EMC_DMCR_MBSEL (1 << 16) ++#define EMC_DMCR_TRAS_BIT 13 ++#define EMC_DMCR_TRAS_MASK (0x07 << EMC_DMCR_TRAS_BIT) ++#define EMC_DMCR_RCD_BIT 11 ++#define EMC_DMCR_RCD_MASK (0x03 << EMC_DMCR_RCD_BIT) ++#define EMC_DMCR_TPC_BIT 8 ++#define EMC_DMCR_TPC_MASK (0x07 << EMC_DMCR_TPC_BIT) ++#define EMC_DMCR_TRWL_BIT 5 ++#define EMC_DMCR_TRWL_MASK (0x03 << EMC_DMCR_TRWL_BIT) ++#define EMC_DMCR_TRC_BIT 2 ++#define EMC_DMCR_TRC_MASK (0x07 << EMC_DMCR_TRC_BIT) ++#define EMC_DMCR_TCL_BIT 0 ++#define EMC_DMCR_TCL_MASK (0x03 << EMC_DMCR_TCL_BIT) ++ ++/* Refresh Time Control/Status Register */ ++#define EMC_RTCSR_SFR (1 << 8) /* self refresh flag */ ++#define EMC_RTCSR_CMF (1 << 7) ++#define EMC_RTCSR_CKS_BIT 0 ++#define EMC_RTCSR_CKS_MASK (0x07 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_DISABLE (0 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_4 (1 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_16 (2 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_64 (3 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_256 (4 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_1024 (5 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_2048 (6 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_4096 (7 << EMC_RTCSR_CKS_BIT) ++ ++/* SDRAM Bank Address Configuration Register */ ++#define EMC_DMAR_BASE_BIT 8 ++#define EMC_DMAR_BASE_MASK (0xff << EMC_DMAR_BASE_BIT) ++#define EMC_DMAR_MASK_BIT 0 ++#define EMC_DMAR_MASK_MASK (0xff << EMC_DMAR_MASK_BIT) ++ ++/* Mode Register of SDRAM bank 0 */ ++#define EMC_SDMR_BM (1 << 9) /* Write Burst Mode */ ++#define EMC_SDMR_OM_BIT 7 /* Operating Mode */ ++#define EMC_SDMR_OM_MASK (3 << EMC_SDMR_OM_BIT) ++ #define EMC_SDMR_OM_NORMAL (0 << EMC_SDMR_OM_BIT) ++#define EMC_SDMR_CAS_BIT 4 /* CAS Latency */ ++#define EMC_SDMR_CAS_MASK (7 << EMC_SDMR_CAS_BIT) ++ #define EMC_SDMR_CAS_1 (1 << EMC_SDMR_CAS_BIT) ++ #define EMC_SDMR_CAS_2 (2 << EMC_SDMR_CAS_BIT) ++ #define EMC_SDMR_CAS_3 (3 << EMC_SDMR_CAS_BIT) ++#define EMC_SDMR_BT_BIT 3 /* Burst Type */ ++#define EMC_SDMR_BT_MASK (1 << EMC_SDMR_BT_BIT) ++ #define EMC_SDMR_BT_SEQ (0 << EMC_SDMR_BT_BIT) /* Sequential */ ++ #define EMC_SDMR_BT_INT (1 << EMC_SDMR_BT_BIT) /* Interleave */ ++#define EMC_SDMR_BL_BIT 0 /* Burst Length */ ++#define EMC_SDMR_BL_MASK (7 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_1 (0 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_2 (1 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_4 (2 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_8 (3 << EMC_SDMR_BL_BIT) ++ ++#define EMC_SDMR_CAS2_16BIT \ ++ (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) ++#define EMC_SDMR_CAS2_32BIT \ ++ (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) ++#define EMC_SDMR_CAS3_16BIT \ ++ (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) ++#define EMC_SDMR_CAS3_32BIT \ ++ (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) ++ ++ ++/************************************************************************* ++ * CIM ++ *************************************************************************/ ++#define CIM_CFG (CIM_BASE + 0x0000) ++#define CIM_CTRL (CIM_BASE + 0x0004) ++#define CIM_STATE (CIM_BASE + 0x0008) ++#define CIM_IID (CIM_BASE + 0x000C) ++#define CIM_RXFIFO (CIM_BASE + 0x0010) ++#define CIM_DA (CIM_BASE + 0x0020) ++#define CIM_FA (CIM_BASE + 0x0024) ++#define CIM_FID (CIM_BASE + 0x0028) ++#define CIM_CMD (CIM_BASE + 0x002C) ++#define CIM_SIZE (CIM_BASE + 0x0030) ++#define CIM_OFFSET (CIM_BASE + 0x0034) ++#define CIM_RAM_ADDR (CIM_BASE + 0x1000) ++ ++#define REG_CIM_CFG REG32(CIM_CFG) ++#define REG_CIM_CTRL REG32(CIM_CTRL) ++#define REG_CIM_STATE REG32(CIM_STATE) ++#define REG_CIM_IID REG32(CIM_IID) ++#define REG_CIM_RXFIFO REG32(CIM_RXFIFO) ++#define REG_CIM_DA REG32(CIM_DA) ++#define REG_CIM_FA REG32(CIM_FA) ++#define REG_CIM_FID REG32(CIM_FID) ++#define REG_CIM_CMD REG32(CIM_CMD) ++#define REG_CIM_SIZE REG32(CIM_SIZE) ++#define REG_CIM_OFFSET REG32(CIM_OFFSET) ++ ++/* CIM Configuration Register (CIM_CFG) */ ++ ++#define CIM_CFG_ORDER_BIT 18 ++#define CIM_CFG_ORDER_MASK (0x3 << CIM_CFG_ORDER_BIT) ++ #define CIM_CFG_ORDER_0 (0x0 << CIM_CFG_ORDER_BIT) ++ #define CIM_CFG_ORDER_1 (0x1 << CIM_CFG_ORDER_BIT) ++ #define CIM_CFG_ORDER_2 (0x2 << CIM_CFG_ORDER_BIT) ++ #define CIM_CFG_ORDER_3 (0x3 << CIM_CFG_ORDER_BIT) ++#define CIM_CFG_DF_BIT 16 ++#define CIM_CFG_DF_MASK (0x3 << CIM_CFG_DF_BIT) ++ #define CIM_CFG_DF_RGB (0x0 << CIM_CFG_DF_BIT) ++ #define CIM_CFG_DF_RAWRGB CIM_CFG_DF_RGB ++ #define CIM_CFG_DF_BAYERRGB CIM_CFG_DF_RGB ++ #define CIM_CFG_DF_YUV444 (0x1 << CIM_CFG_DF_BIT) ++ #define CIM_CFG_DF_YUV422 (0x2 << CIM_CFG_DF_BIT) ++ #define CIM_CFG_DF_ITU656 (0x3 << CIM_CFG_DF_BIT) ++#define CIM_CFG_INV_DAT (1 << 15) ++#define CIM_CFG_VSP (1 << 14) ++#define CIM_CFG_HSP (1 << 13) ++#define CIM_CFG_PCP (1 << 12) /* PCLK working edge */ ++#define CIM_CFG_DMA_BUSRT_TYPE_BIT 10 ++#define CIM_CFG_DMA_BUSRT_TYPE_MASK (0x3 << CIM_CFG_DMA_BUSRT_TYPE_BIT) ++#define CIM_CFG_DMA_BUSRT_INCR (0 << CIM_CFG_DMA_BUSRT_TYPE_BIT) ++#define CIM_CFG_DMA_BUSRT_INCR4 (1 << CIM_CFG_DMA_BUSRT_TYPE_BIT) ++#define CIM_CFG_DMA_BUSRT_INCR8 (2 << CIM_CFG_DMA_BUSRT_TYPE_BIT) ++#define CIM_CFG_DUMMY_ZERO (1 << 9) ++#define CIM_CFG_EXT_VSYNC (1 << 8) ++#define CIM_CFG_PACK_BIT 4 ++#define CIM_CFG_PACK_MASK (0x7 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_0 (0 << CIM_CFG_PACK_BIT) /* 11 22 33 44 */ ++ #define CIM_CFG_PACK_1 (1 << CIM_CFG_PACK_BIT) /* 22 33 44 11 */ ++ #define CIM_CFG_PACK_2 (2 << CIM_CFG_PACK_BIT) /* 33 44 11 22 */ ++ #define CIM_CFG_PACK_3 (3 << CIM_CFG_PACK_BIT) /* 44 11 22 33 */ ++ #define CIM_CFG_PACK_4 (4 << CIM_CFG_PACK_BIT) /* 44 33 22 11 */ ++ #define CIM_CFG_PACK_5 (5 << CIM_CFG_PACK_BIT) /* 33 22 11 44 */ ++ #define CIM_CFG_PACK_6 (6 << CIM_CFG_PACK_BIT) /* 22 11 44 33 */ ++ #define CIM_CFG_PACK_7 (7 << CIM_CFG_PACK_BIT) /* 11 44 33 22 */ ++#define CIM_CFG_BYPASS_BIT 2 ++#define CIM_CFG_BYPASS_MASK (1 << CIM_CFG_BYPASS_BIT) ++ #define CIM_CFG_BYPASS (1 << CIM_CFG_BYPASS_BIT) ++#define CIM_CFG_DSM_BIT 0 ++#define CIM_CFG_DSM_MASK (0x3 << CIM_CFG_DSM_BIT) ++ #define CIM_CFG_DSM_CPM (0 << CIM_CFG_DSM_BIT) /* CCIR656 Progressive Mode */ ++ #define CIM_CFG_DSM_CIM (1 << CIM_CFG_DSM_BIT) /* CCIR656 Interlace Mode */ ++ #define CIM_CFG_DSM_GCM (2 << CIM_CFG_DSM_BIT) /* Gated Clock Mode */ ++ #define CIM_CFG_DSM_NGCM (3 << CIM_CFG_DSM_BIT) /* Non-Gated Clock Mode */ ++ ++/* CIM Control Register (CIM_CTRL) */ ++ ++#define CIM_CTRL_MCLKDIV_BIT 24 ++#define CIM_CTRL_MCLKDIV_MASK (0xff << CIM_CTRL_MCLKDIV_BIT) ++#define CIM_CTRL_FRC_BIT 16 ++#define CIM_CTRL_FRC_MASK (0xf << CIM_CTRL_FRC_BIT) ++ #define CIM_CTRL_FRC_1 (0x0 << CIM_CTRL_FRC_BIT) /* Sample every frame */ ++ #define CIM_CTRL_FRC_2 (0x1 << CIM_CTRL_FRC_BIT) /* Sample 1/2 frame */ ++ #define CIM_CTRL_FRC_3 (0x2 << CIM_CTRL_FRC_BIT) /* Sample 1/3 frame */ ++ #define CIM_CTRL_FRC_4 (0x3 << CIM_CTRL_FRC_BIT) /* Sample 1/4 frame */ ++ #define CIM_CTRL_FRC_5 (0x4 << CIM_CTRL_FRC_BIT) /* Sample 1/5 frame */ ++ #define CIM_CTRL_FRC_6 (0x5 << CIM_CTRL_FRC_BIT) /* Sample 1/6 frame */ ++ #define CIM_CTRL_FRC_7 (0x6 << CIM_CTRL_FRC_BIT) /* Sample 1/7 frame */ ++ #define CIM_CTRL_FRC_8 (0x7 << CIM_CTRL_FRC_BIT) /* Sample 1/8 frame */ ++ #define CIM_CTRL_FRC_9 (0x8 << CIM_CTRL_FRC_BIT) /* Sample 1/9 frame */ ++ #define CIM_CTRL_FRC_10 (0x9 << CIM_CTRL_FRC_BIT) /* Sample 1/10 frame */ ++ #define CIM_CTRL_FRC_11 (0xA << CIM_CTRL_FRC_BIT) /* Sample 1/11 frame */ ++ #define CIM_CTRL_FRC_12 (0xB << CIM_CTRL_FRC_BIT) /* Sample 1/12 frame */ ++ #define CIM_CTRL_FRC_13 (0xC << CIM_CTRL_FRC_BIT) /* Sample 1/13 frame */ ++ #define CIM_CTRL_FRC_14 (0xD << CIM_CTRL_FRC_BIT) /* Sample 1/14 frame */ ++ #define CIM_CTRL_FRC_15 (0xE << CIM_CTRL_FRC_BIT) /* Sample 1/15 frame */ ++ #define CIM_CTRL_FRC_16 (0xF << CIM_CTRL_FRC_BIT) /* Sample 1/16 frame */ ++#define CIM_CTRL_SIZEEN_BIT 14 ++#define CIM_CTRL_SIZEEN_MASK (0x1 << CIM_CTRL_SIZEEN_BIT) ++#define CIM_CTRL_SIZEEN (0x1 << CIM_CTRL_SIZEEN_BIT) ++#define CIM_CTRL_VDDM (1 << 13) /* VDD interrupt enable */ ++#define CIM_CTRL_DMA_SOFM (1 << 12) ++#define CIM_CTRL_DMA_EOFM (1 << 11) ++#define CIM_CTRL_DMA_STOPM (1 << 10) ++#define CIM_CTRL_RXF_TRIGM (1 << 9) ++#define CIM_CTRL_RXF_OFM (1 << 8) ++#define CIM_CTRL_RXF_TRIG_BIT 4 ++#define CIM_CTRL_RXF_TRIG_MASK (0x7 << CIM_CTRL_RXF_TRIG_BIT) ++ #define CIM_CTRL_RXF_TRIG_4 (0 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 4 */ ++ #define CIM_CTRL_RXF_TRIG_8 (1 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 8 */ ++ #define CIM_CTRL_RXF_TRIG_12 (2 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 12 */ ++ #define CIM_CTRL_RXF_TRIG_16 (3 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 16 */ ++ #define CIM_CTRL_RXF_TRIG_20 (4 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 20 */ ++ #define CIM_CTRL_RXF_TRIG_24 (5 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 24 */ ++ #define CIM_CTRL_RXF_TRIG_28 (6 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 28 */ ++ #define CIM_CTRL_RXF_TRIG_32 (7 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 32 */ ++#define CIM_CTRL_FAST_MODE_MASK (1 << 3) /* CIM fast mode mask */ ++#define CIM_CTRL_FAST_MODE (1 << 3) /* CIM works in fast mode */ ++#define CIM_CTRL_NORMAL_MODE (0 << 3) /* CIM works in normal mode */ ++#define CIM_CTRL_DMA_EN (1 << 2) /* Enable DMA */ ++#define CIM_CTRL_RXF_RST (1 << 1) /* RxFIFO reset */ ++#define CIM_CTRL_ENA (1 << 0) /* Enable CIM */ ++ ++/* CIM State Register (CIM_STATE) */ ++ ++#define CIM_STATE_DMA_SOF (1 << 6) /* DMA start irq */ ++#define CIM_STATE_DMA_EOF (1 << 5) /* DMA end irq */ ++#define CIM_STATE_DMA_STOP (1 << 4) /* DMA stop irq */ ++#define CIM_STATE_RXF_OF (1 << 3) /* RXFIFO over flow irq */ ++#define CIM_STATE_RXF_TRIG (1 << 2) /* RXFIFO triger meet irq */ ++#define CIM_STATE_RXF_EMPTY (1 << 1) /* RXFIFO empty irq */ ++#define CIM_STATE_VDD (1 << 0) /* CIM disabled irq */ ++ ++/* CIM DMA Command Register (CIM_CMD) */ ++ ++#define CIM_CMD_SOFINT (1 << 31) /* enable DMA start irq */ ++#define CIM_CMD_EOFINT (1 << 30) /* enable DMA end irq */ ++#define CIM_CMD_STOP (1 << 28) /* enable DMA stop irq */ ++#define CIM_CMD_LEN_BIT 0 ++#define CIM_CMD_LEN_MASK (0xffffff << CIM_CMD_LEN_BIT) ++ ++/* CIM Image Size Register (CIM_SIZE) */ ++#define CIM_SIZE_LPF_BIT 16 /* Lines per freame for csc output image */ ++#define CIM_SIZE_LPF_MASK (0x1fff << CIM_SIZE_LPF_BIT) ++#define CIM_SIZE_PPL_BIT 0 /* Pixels per line for csc output image, should be an even number */ ++#define CIM_SIZE_PPL_MASK (0x1fff << CIM_SIZE_PPL_BIT) ++ ++/* CIM Image Offset Register (CIM_OFFSET) */ ++#define CIM_OFFSET_V_BIT 16 /* Vertical offset */ ++#define CIM_OFFSET_V_MASK (0xfff << CIM_OFFSET_V_BIT) ++#define CIM_OFFSET_H_BIT 0 /* Horizontal offset, should be an enen number */ ++#define CIM_OFFSET_H_MASK (0xfff << CIM_OFFSET_H_BIT) /*OFFSET_H should be even number*/ ++ ++/************************************************************************* ++ * SADC (Smart A/D Controller) ++ *************************************************************************/ ++ ++#define SADC_ENA (SADC_BASE + 0x00) /* ADC Enable Register */ ++#define SADC_CFG (SADC_BASE + 0x04) /* ADC Configure Register */ ++#define SADC_CTRL (SADC_BASE + 0x08) /* ADC Control Register */ ++#define SADC_STATE (SADC_BASE + 0x0C) /* ADC Status Register*/ ++#define SADC_SAMETIME (SADC_BASE + 0x10) /* ADC Same Point Time Register */ ++#define SADC_WAITTIME (SADC_BASE + 0x14) /* ADC Wait Time Register */ ++#define SADC_TSDAT (SADC_BASE + 0x18) /* ADC Touch Screen Data Register */ ++#define SADC_BATDAT (SADC_BASE + 0x1C) /* ADC PBAT Data Register */ ++#define SADC_SADDAT (SADC_BASE + 0x20) /* ADC SADCIN Data Register */ ++#define SADC_ADCLK (SADC_BASE + 0x28) /* ADC Clock Divide Register */ ++ ++#define REG_SADC_ENA REG8(SADC_ENA) ++#define REG_SADC_CFG REG32(SADC_CFG) ++#define REG_SADC_CTRL REG8(SADC_CTRL) ++#define REG_SADC_STATE REG8(SADC_STATE) ++#define REG_SADC_SAMETIME REG16(SADC_SAMETIME) ++#define REG_SADC_WAITTIME REG16(SADC_WAITTIME) ++#define REG_SADC_TSDAT REG32(SADC_TSDAT) ++#define REG_SADC_BATDAT REG16(SADC_BATDAT) ++#define REG_SADC_SADDAT REG16(SADC_SADDAT) ++#define REG_SADC_ADCLK REG32(SADC_ADCLK) ++ ++/* ADC Enable Register */ ++#define SADC_ENA_ADEN (1 << 7) /* Touch Screen Enable */ ++#define SADC_ENA_ENTR_SLP (1 << 6) /* Touch Screen Enable */ ++#define SADC_ENA_EXIT_SLP (1 << 5) /* Touch Screen Enable */ ++#define SADC_ENA_TSEN (1 << 2) /* Touch Screen Enable */ ++#define SADC_ENA_PBATEN (1 << 1) /* PBAT Enable */ ++#define SADC_ENA_SADCINEN (1 << 0) /* SADCIN Enable */ ++ ++/* ADC Configure Register */ ++#define SADC_CFG_EXIN (1 << 30) ++#define SADC_CFG_CLKOUT_NUM_BIT 16 ++#define SADC_CFG_CLKOUT_NUM_MASK (0x7 << SADC_CFG_CLKOUT_NUM_BIT) ++#define SADC_CFG_TS_DMA (1 << 15) /* Touch Screen DMA Enable */ ++#define SADC_CFG_XYZ_BIT 13 /* XYZ selection */ ++#define SADC_CFG_XYZ_MASK (0x3 << SADC_CFG_XYZ_BIT) ++ #define SADC_CFG_XY (0 << SADC_CFG_XYZ_BIT) ++ #define SADC_CFG_XYZ (1 << SADC_CFG_XYZ_BIT) ++ #define SADC_CFG_XYZ1Z2 (2 << SADC_CFG_XYZ_BIT) ++#define SADC_CFG_SNUM_BIT 10 /* Sample Number */ ++#define SADC_CFG_SNUM_MASK (0x7 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_1 (0x0 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_2 (0x1 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_3 (0x2 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_4 (0x3 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_5 (0x4 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_6 (0x5 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_8 (0x6 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_9 (0x7 << SADC_CFG_SNUM_BIT) ++#define SADC_CFG_CLKDIV_BIT 5 /* AD Converter frequency clock divider */ ++#define SADC_CFG_CLKDIV_MASK (0x1f << SADC_CFG_CLKDIV_BIT) ++#define SADC_CFG_PBAT_HIGH (0 << 4) /* PBAT >= 2.5V */ ++#define SADC_CFG_PBAT_LOW (1 << 4) /* PBAT < 2.5V */ ++#define SADC_CFG_CMD_BIT 0 /* ADC Command */ ++#define SADC_CFG_CMD_MASK (0xf << SADC_CFG_CMD_BIT) ++ #define SADC_CFG_CMD_X_SE (0x0 << SADC_CFG_CMD_BIT) /* X Single-End */ ++ #define SADC_CFG_CMD_Y_SE (0x1 << SADC_CFG_CMD_BIT) /* Y Single-End */ ++ #define SADC_CFG_CMD_X_DIFF (0x2 << SADC_CFG_CMD_BIT) /* X Differential */ ++ #define SADC_CFG_CMD_Y_DIFF (0x3 << SADC_CFG_CMD_BIT) /* Y Differential */ ++ #define SADC_CFG_CMD_Z1_DIFF (0x4 << SADC_CFG_CMD_BIT) /* Z1 Differential */ ++ #define SADC_CFG_CMD_Z2_DIFF (0x5 << SADC_CFG_CMD_BIT) /* Z2 Differential */ ++ #define SADC_CFG_CMD_Z3_DIFF (0x6 << SADC_CFG_CMD_BIT) /* Z3 Differential */ ++ #define SADC_CFG_CMD_Z4_DIFF (0x7 << SADC_CFG_CMD_BIT) /* Z4 Differential */ ++ #define SADC_CFG_CMD_TP_SE (0x8 << SADC_CFG_CMD_BIT) /* Touch Pressure */ ++ #define SADC_CFG_CMD_PBATH_SE (0x9 << SADC_CFG_CMD_BIT) /* PBAT >= 2.5V */ ++ #define SADC_CFG_CMD_PBATL_SE (0xa << SADC_CFG_CMD_BIT) /* PBAT < 2.5V */ ++ #define SADC_CFG_CMD_SADCIN_SE (0xb << SADC_CFG_CMD_BIT) /* Measure SADCIN */ ++ #define SADC_CFG_CMD_INT_PEN (0xc << SADC_CFG_CMD_BIT) /* INT_PEN Enable */ ++ ++/* ADC Control Register */ ++#define SADC_CTRL_SLPENDM (1 << 5) /* sleep Interrupt Mask */ ++#define SADC_CTRL_PENDM (1 << 4) /* Pen Down Interrupt Mask */ ++#define SADC_CTRL_PENUM (1 << 3) /* Pen Up Interrupt Mask */ ++#define SADC_CTRL_TSRDYM (1 << 2) /* Touch Screen Data Ready Interrupt Mask */ ++#define SADC_CTRL_PBATRDYM (1 << 1) /* PBAT Data Ready Interrupt Mask */ ++#define SADC_CTRL_SRDYM (1 << 0) /* SADCIN Data Ready Interrupt Mask */ ++ ++/* ADC Status Register */ ++#define SADC_STATE_SLEEPND (1 << 5) /* Pen Down Interrupt Flag */ ++#define SADC_STATE_PEND (1 << 4) /* Pen Down Interrupt Flag */ ++#define SADC_STATE_PENU (1 << 3) /* Pen Up Interrupt Flag */ ++#define SADC_STATE_TSRDY (1 << 2) /* Touch Screen Data Ready Interrupt Flag */ ++#define SADC_STATE_PBATRDY (1 << 1) /* PBAT Data Ready Interrupt Flag */ ++#define SADC_STATE_SRDY (1 << 0) /* SADCIN Data Ready Interrupt Flag */ ++ ++/* ADC Touch Screen Data Register */ ++#define SADC_TSDAT_DATA0_BIT 0 ++#define SADC_TSDAT_DATA0_MASK (0xfff << SADC_TSDAT_DATA0_BIT) ++#define SADC_TSDAT_TYPE0 (1 << 15) ++#define SADC_TSDAT_DATA1_BIT 16 ++#define SADC_TSDAT_DATA1_MASK (0xfff << SADC_TSDAT_DATA1_BIT) ++#define SADC_TSDAT_TYPE1 (1 << 31) ++ ++/* ADC Clock Divide Register */ ++#define SADC_ADCLK_CLKDIV_10_BIT 16 ++#define SADC_ADCLK_CLKDIV_10_MASK (0x7f << SADC_ADCLK_CLKDIV_10_BIT) ++#define SADC_ADCLK_CLKDIV_BIT 0 ++#define SADC_ADCLK_CLKDIV_MASK (0x3f << SADC_ADCLK_CLKDIV_BIT) ++ ++/************************************************************************* ++ * SLCD (Smart LCD Controller) ++ *************************************************************************/ ++ ++#define SLCD_CFG (SLCD_BASE + 0xA0) /* SLCD Configure Register */ ++#define SLCD_CTRL (SLCD_BASE + 0xA4) /* SLCD Control Register */ ++#define SLCD_STATE (SLCD_BASE + 0xA8) /* SLCD Status Register */ ++#define SLCD_DATA (SLCD_BASE + 0xAC) /* SLCD Data Register */ ++ ++#define REG_SLCD_CFG REG32(SLCD_CFG) ++#define REG_SLCD_CTRL REG8(SLCD_CTRL) ++#define REG_SLCD_STATE REG8(SLCD_STATE) ++#define REG_SLCD_DATA REG32(SLCD_DATA) ++ ++/* SLCD Configure Register */ ++#define SLCD_CFG_DWIDTH_BIT 10 ++#define SLCD_CFG_DWIDTH_MASK (0x7 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_18BIT (0 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_16BIT (1 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_8BIT_x3 (2 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_8BIT_x2 (3 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_8BIT_x1 (4 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_24BIT (5 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_9BIT_x2 (7 << SLCD_CFG_DWIDTH_BIT) ++#define SLCD_CFG_CWIDTH_BIT (8) ++#define SLCD_CFG_CWIDTH_MASK (0x7 << SLCD_CFG_CWIDTH_BIT) ++#define SLCD_CFG_CWIDTH_16BIT (0 << SLCD_CFG_CWIDTH_BIT) ++#define SLCD_CFG_CWIDTH_8BIT (1 << SLCD_CFG_CWIDTH_BIT) ++#define SLCD_CFG_CWIDTH_18BIT (2 << SLCD_CFG_CWIDTH_BIT) ++#define SLCD_CFG_CWIDTH_24BIT (3 << SLCD_CFG_CWIDTH_BIT) ++#define SLCD_CFG_CS_ACTIVE_LOW (0 << 4) ++#define SLCD_CFG_CS_ACTIVE_HIGH (1 << 4) ++#define SLCD_CFG_RS_CMD_LOW (0 << 3) ++#define SLCD_CFG_RS_CMD_HIGH (1 << 3) ++#define SLCD_CFG_CLK_ACTIVE_FALLING (0 << 1) ++#define SLCD_CFG_CLK_ACTIVE_RISING (1 << 1) ++#define SLCD_CFG_TYPE_PARALLEL (0 << 0) ++#define SLCD_CFG_TYPE_SERIAL (1 << 0) ++ ++/* SLCD Control Register */ ++#define SLCD_CTRL_DMA_EN (1 << 0) ++ ++/* SLCD Status Register */ ++#define SLCD_STATE_BUSY (1 << 0) ++ ++/* SLCD Data Register */ ++#define SLCD_DATA_RS_DATA (0 << 31) ++#define SLCD_DATA_RS_COMMAND (1 << 31) ++ ++/************************************************************************* ++ * LCD (LCD Controller) ++ *************************************************************************/ ++#define LCD_CFG (LCD_BASE + 0x00) /* LCD Configure Register */ ++#define LCD_CTRL (LCD_BASE + 0x30) /* LCD Control Register */ ++#define LCD_STATE (LCD_BASE + 0x34) /* LCD Status Register */ ++ ++#define LCD_OSDC (LCD_BASE + 0x100) /* LCD OSD Configure Register */ ++#define LCD_OSDCTRL (LCD_BASE + 0x104) /* LCD OSD Control Register */ ++#define LCD_OSDS (LCD_BASE + 0x108) /* LCD OSD Status Register */ ++#define LCD_BGC (LCD_BASE + 0x10C) /* LCD Background Color Register */ ++#define LCD_KEY0 (LCD_BASE + 0x110) /* LCD Foreground Color Key Register 0 */ ++#define LCD_KEY1 (LCD_BASE + 0x114) /* LCD Foreground Color Key Register 1 */ ++#define LCD_ALPHA (LCD_BASE + 0x118) /* LCD ALPHA Register */ ++#define LCD_IPUR (LCD_BASE + 0x11C) /* LCD IPU Restart Register */ ++ ++#define LCD_VAT (LCD_BASE + 0x0c) /* Virtual Area Setting Register */ ++#define LCD_DAH (LCD_BASE + 0x10) /* Display Area Horizontal Start/End Point */ ++#define LCD_DAV (LCD_BASE + 0x14) /* Display Area Vertical Start/End Point */ ++ ++#define LCD_XYP0 (LCD_BASE + 0x120) /* Foreground 0 XY Position Register */ ++#define LCD_XYP1 (LCD_BASE + 0x124) /* Foreground 1 XY Position Register */ ++#define LCD_SIZE0 (LCD_BASE + 0x128) /* Foreground 0 Size Register */ ++#define LCD_SIZE1 (LCD_BASE + 0x12C) /* Foreground 1 Size Register */ ++#define LCD_RGBC (LCD_BASE + 0x90) /* RGB Controll Register */ ++ ++#define LCD_VSYNC (LCD_BASE + 0x04) /* Vertical Synchronize Register */ ++#define LCD_HSYNC (LCD_BASE + 0x08) /* Horizontal Synchronize Register */ ++#define LCD_PS (LCD_BASE + 0x18) /* PS Signal Setting */ ++#define LCD_CLS (LCD_BASE + 0x1c) /* CLS Signal Setting */ ++#define LCD_SPL (LCD_BASE + 0x20) /* SPL Signal Setting */ ++#define LCD_REV (LCD_BASE + 0x24) /* REV Signal Setting */ ++#define LCD_IID (LCD_BASE + 0x38) /* Interrupt ID Register */ ++#define LCD_DA0 (LCD_BASE + 0x40) /* Descriptor Address Register 0 */ ++#define LCD_SA0 (LCD_BASE + 0x44) /* Source Address Register 0 */ ++#define LCD_FID0 (LCD_BASE + 0x48) /* Frame ID Register 0 */ ++#define LCD_CMD0 (LCD_BASE + 0x4c) /* DMA Command Register 0 */ ++#define LCD_DA1 (LCD_BASE + 0x50) /* Descriptor Address Register 1 */ ++#define LCD_SA1 (LCD_BASE + 0x54) /* Source Address Register 1 */ ++#define LCD_FID1 (LCD_BASE + 0x58) /* Frame ID Register 1 */ ++#define LCD_CMD1 (LCD_BASE + 0x5c) /* DMA Command Register 1 */ ++ ++#define LCD_OFFS0 (LCD_BASE + 0x60) /* DMA Offsize Register 0 */ ++#define LCD_PW0 (LCD_BASE + 0x64) /* DMA Page Width Register 0 */ ++#define LCD_CNUM0 (LCD_BASE + 0x68) /* DMA Command Counter Register 0 */ ++#define LCD_DESSIZE0 (LCD_BASE + 0x6C) /* Foreground Size in Descriptor 0 Register*/ ++#define LCD_OFFS1 (LCD_BASE + 0x70) /* DMA Offsize Register 1 */ ++#define LCD_PW1 (LCD_BASE + 0x74) /* DMA Page Width Register 1 */ ++#define LCD_CNUM1 (LCD_BASE + 0x78) /* DMA Command Counter Register 1 */ ++#define LCD_DESSIZE1 (LCD_BASE + 0x7C) /* Foreground Size in Descriptor 1 Register*/ ++ ++#define REG_LCD_CFG REG32(LCD_CFG) ++#define REG_LCD_CTRL REG32(LCD_CTRL) ++#define REG_LCD_STATE REG32(LCD_STATE) ++ ++#define REG_LCD_OSDC REG16(LCD_OSDC) ++#define REG_LCD_OSDCTRL REG16(LCD_OSDCTRL) ++#define REG_LCD_OSDS REG16(LCD_OSDS) ++#define REG_LCD_BGC REG32(LCD_BGC) ++#define REG_LCD_KEY0 REG32(LCD_KEY0) ++#define REG_LCD_KEY1 REG32(LCD_KEY1) ++#define REG_LCD_ALPHA REG8(LCD_ALPHA) ++#define REG_LCD_IPUR REG32(LCD_IPUR) ++ ++#define REG_LCD_VAT REG32(LCD_VAT) ++#define REG_LCD_DAH REG32(LCD_DAH) ++#define REG_LCD_DAV REG32(LCD_DAV) ++ ++#define REG_LCD_XYP0 REG32(LCD_XYP0) ++#define REG_LCD_XYP1 REG32(LCD_XYP1) ++#define REG_LCD_SIZE0 REG32(LCD_SIZE0) ++#define REG_LCD_SIZE1 REG32(LCD_SIZE1) ++#define REG_LCD_RGBC REG16(LCD_RGBC) ++ ++#define REG_LCD_VSYNC REG32(LCD_VSYNC) ++#define REG_LCD_HSYNC REG32(LCD_HSYNC) ++#define REG_LCD_PS REG32(LCD_PS) ++#define REG_LCD_CLS REG32(LCD_CLS) ++#define REG_LCD_SPL REG32(LCD_SPL) ++#define REG_LCD_REV REG32(LCD_REV) ++#define REG_LCD_IID REG32(LCD_IID) ++#define REG_LCD_DA0 REG32(LCD_DA0) ++#define REG_LCD_SA0 REG32(LCD_SA0) ++#define REG_LCD_FID0 REG32(LCD_FID0) ++#define REG_LCD_CMD0 REG32(LCD_CMD0) ++#define REG_LCD_DA1 REG32(LCD_DA1) ++#define REG_LCD_SA1 REG32(LCD_SA1) ++#define REG_LCD_FID1 REG32(LCD_FID1) ++#define REG_LCD_CMD1 REG32(LCD_CMD1) ++ ++#define REG_LCD_OFFS0 REG32(LCD_OFFS0) ++#define REG_LCD_PW0 REG32(LCD_PW0) ++#define REG_LCD_CNUM0 REG32(LCD_CNUM0) ++#define REG_LCD_DESSIZE0 REG32(LCD_DESSIZE0) ++#define REG_LCD_OFFS1 REG32(LCD_OFFS1) ++#define REG_LCD_PW1 REG32(LCD_PW1) ++#define REG_LCD_CNUM1 REG32(LCD_CNUM1) ++#define REG_LCD_DESSIZE1 REG32(LCD_DESSIZE1) ++ ++/* LCD Configure Register */ ++#define LCD_CFG_LCDPIN_BIT 31 /* LCD pins selection */ ++#define LCD_CFG_LCDPIN_MASK (0x1 << LCD_CFG_LCDPIN_BIT) ++ #define LCD_CFG_LCDPIN_LCD (0x0 << LCD_CFG_LCDPIN_BIT) ++ #define LCD_CFG_LCDPIN_SLCD (0x1 << LCD_CFG_LCDPIN_BIT) ++#define LCD_CFG_TVEPEH (1 << 30) /* TVE PAL enable extra halfline signal */ ++#define LCD_CFG_FUHOLD (1 << 29) /* hold pixel clock when outFIFO underrun */ ++#define LCD_CFG_NEWDES (1 << 28) /* use new descripter. old: 4words, new:8words */ ++#define LCD_CFG_PALBP (1 << 27) /* bypass data format and alpha blending */ ++#define LCD_CFG_TVEN (1 << 26) /* indicate the terminal is lcd or tv */ ++#define LCD_CFG_RECOVER (1 << 25) /* Auto recover when output fifo underrun */ ++#define LCD_CFG_DITHER (1 << 24) /* Dither function */ ++#define LCD_CFG_PSM (1 << 23) /* PS signal mode */ ++#define LCD_CFG_CLSM (1 << 22) /* CLS signal mode */ ++#define LCD_CFG_SPLM (1 << 21) /* SPL signal mode */ ++#define LCD_CFG_REVM (1 << 20) /* REV signal mode */ ++#define LCD_CFG_HSYNM (1 << 19) /* HSYNC signal mode */ ++#define LCD_CFG_PCLKM (1 << 18) /* PCLK signal mode */ ++#define LCD_CFG_INVDAT (1 << 17) /* Inverse output data */ ++#define LCD_CFG_SYNDIR_IN (1 << 16) /* VSYNC&HSYNC direction */ ++#define LCD_CFG_PSP (1 << 15) /* PS pin reset state */ ++#define LCD_CFG_CLSP (1 << 14) /* CLS pin reset state */ ++#define LCD_CFG_SPLP (1 << 13) /* SPL pin reset state */ ++#define LCD_CFG_REVP (1 << 12) /* REV pin reset state */ ++#define LCD_CFG_HSP (1 << 11) /* HSYNC polarity:0-active high,1-active low */ ++#define LCD_CFG_PCP (1 << 10) /* PCLK polarity:0-rising,1-falling */ ++#define LCD_CFG_DEP (1 << 9) /* DE polarity:0-active high,1-active low */ ++#define LCD_CFG_VSP (1 << 8) /* VSYNC polarity:0-rising,1-falling */ ++#define LCD_CFG_MODE_TFT_18BIT (1 << 7) /* 18bit TFT */ ++#define LCD_CFG_MODE_TFT_16BIT (0 << 7) /* 16bit TFT */ ++#define LCD_CFG_MODE_TFT_24BIT (1 << 6) /* 24bit TFT */ ++#define LCD_CFG_PDW_BIT 4 /* STN pins utilization */ ++#define LCD_CFG_PDW_MASK (0x3 << LCD_DEV_PDW_BIT) ++#define LCD_CFG_PDW_1 (0 << LCD_CFG_PDW_BIT) /* LCD_D[0] */ ++ #define LCD_CFG_PDW_2 (1 << LCD_CFG_PDW_BIT) /* LCD_D[0:1] */ ++ #define LCD_CFG_PDW_4 (2 << LCD_CFG_PDW_BIT) /* LCD_D[0:3]/LCD_D[8:11] */ ++ #define LCD_CFG_PDW_8 (3 << LCD_CFG_PDW_BIT) /* LCD_D[0:7]/LCD_D[8:15] */ ++#define LCD_CFG_MODE_BIT 0 /* Display Device Mode Select */ ++#define LCD_CFG_MODE_MASK (0x0f << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_GENERIC_TFT (0 << LCD_CFG_MODE_BIT) /* 16,18 bit TFT */ ++ #define LCD_CFG_MODE_SPECIAL_TFT_1 (1 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SPECIAL_TFT_2 (2 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SPECIAL_TFT_3 (3 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_NONINTER_CCIR656 (4 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_INTER_CCIR656 (6 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SINGLE_CSTN (8 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SINGLE_MSTN (9 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_DUAL_CSTN (10 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_DUAL_MSTN (11 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SERIAL_TFT (12 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_LCM (13 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SLCD LCD_CFG_MODE_LCM ++ ++/* LCD Control Register */ ++#define LCD_CTRL_BST_BIT 28 /* Burst Length Selection */ ++#define LCD_CTRL_BST_MASK (0x03 << LCD_CTRL_BST_BIT) ++ #define LCD_CTRL_BST_4 (0 << LCD_CTRL_BST_BIT) /* 4-word */ ++ #define LCD_CTRL_BST_8 (1 << LCD_CTRL_BST_BIT) /* 8-word */ ++ #define LCD_CTRL_BST_16 (2 << LCD_CTRL_BST_BIT) /* 16-word */ ++ #define LCD_CTRL_BST_32 (3 << LCD_CTRL_BST_BIT) /* 32-word */ ++#define LCD_CTRL_RGB565 (0 << 27) /* RGB565 mode(foreground 0 in OSD mode) */ ++#define LCD_CTRL_RGB555 (1 << 27) /* RGB555 mode(foreground 0 in OSD mode) */ ++#define LCD_CTRL_OFUP (1 << 26) /* Output FIFO underrun protection enable */ ++#define LCD_CTRL_FRC_BIT 24 /* STN FRC Algorithm Selection */ ++#define LCD_CTRL_FRC_MASK (0x03 << LCD_CTRL_FRC_BIT) ++ #define LCD_CTRL_FRC_16 (0 << LCD_CTRL_FRC_BIT) /* 16 grayscale */ ++ #define LCD_CTRL_FRC_4 (1 << LCD_CTRL_FRC_BIT) /* 4 grayscale */ ++ #define LCD_CTRL_FRC_2 (2 << LCD_CTRL_FRC_BIT) /* 2 grayscale */ ++#define LCD_CTRL_PDD_BIT 16 /* Load Palette Delay Counter */ ++#define LCD_CTRL_PDD_MASK (0xff << LCD_CTRL_PDD_BIT) ++#define LCD_CTRL_EOFM (1 << 13) /* EOF interrupt mask */ ++#define LCD_CTRL_SOFM (1 << 12) /* SOF interrupt mask */ ++#define LCD_CTRL_OFUM (1 << 11) /* Output FIFO underrun interrupt mask */ ++#define LCD_CTRL_IFUM0 (1 << 10) /* Input FIFO 0 underrun interrupt mask */ ++#define LCD_CTRL_IFUM1 (1 << 9) /* Input FIFO 1 underrun interrupt mask */ ++#define LCD_CTRL_LDDM (1 << 8) /* LCD disable done interrupt mask */ ++#define LCD_CTRL_QDM (1 << 7) /* LCD quick disable done interrupt mask */ ++#define LCD_CTRL_BEDN (1 << 6) /* Endian selection */ ++#define LCD_CTRL_PEDN (1 << 5) /* Endian in byte:0-msb first, 1-lsb first */ ++#define LCD_CTRL_DIS (1 << 4) /* Disable indicate bit */ ++#define LCD_CTRL_ENA (1 << 3) /* LCD enable bit */ ++#define LCD_CTRL_BPP_BIT 0 /* Bits Per Pixel */ ++#define LCD_CTRL_BPP_MASK (0x07 << LCD_CTRL_BPP_BIT) ++ #define LCD_CTRL_BPP_1 (0 << LCD_CTRL_BPP_BIT) /* 1 bpp */ ++ #define LCD_CTRL_BPP_2 (1 << LCD_CTRL_BPP_BIT) /* 2 bpp */ ++ #define LCD_CTRL_BPP_4 (2 << LCD_CTRL_BPP_BIT) /* 4 bpp */ ++ #define LCD_CTRL_BPP_8 (3 << LCD_CTRL_BPP_BIT) /* 8 bpp */ ++ #define LCD_CTRL_BPP_16 (4 << LCD_CTRL_BPP_BIT) /* 15/16 bpp */ ++ #define LCD_CTRL_BPP_18_24 (5 << LCD_CTRL_BPP_BIT) /* 18/24/32 bpp */ ++ ++/* LCD Status Register */ ++#define LCD_STATE_QD (1 << 7) /* Quick Disable Done */ ++#define LCD_STATE_EOF (1 << 5) /* EOF Flag */ ++#define LCD_STATE_SOF (1 << 4) /* SOF Flag */ ++#define LCD_STATE_OFU (1 << 3) /* Output FIFO Underrun */ ++#define LCD_STATE_IFU0 (1 << 2) /* Input FIFO 0 Underrun */ ++#define LCD_STATE_IFU1 (1 << 1) /* Input FIFO 1 Underrun */ ++#define LCD_STATE_LDD (1 << 0) /* LCD Disabled */ ++ ++/* OSD Configure Register */ ++#define LCD_OSDC_SOFM1 (1 << 15) /* Start of frame interrupt mask for foreground 1 */ ++#define LCD_OSDC_EOFM1 (1 << 14) /* End of frame interrupt mask for foreground 1 */ ++#define LCD_OSDC_REM1 (1 << 13) /* Real end of frame mask for foreground 1 */ ++#define LCD_OSDC_SOFM0 (1 << 11) /* Start of frame interrupt mask for foreground 0 */ ++#define LCD_OSDC_EOFM0 (1 << 10) /* End of frame interrupt mask for foreground 0 */ ++#define LCD_OSDC_REM0 (1 << 9) /* Real end of frame mask for foreground 0 */ ++#define LCD_OSDC_REMB (1 << 7) /* Real end of frame mask for background */ ++#define LCD_OSDC_F1EN (1 << 4) /* enable foreground 1 */ ++#define LCD_OSDC_F0EN (1 << 3) /* enable foreground 0 */ ++#define LCD_OSDC_ALPHAEN (1 << 2) /* enable alpha blending */ ++#define LCD_OSDC_ALPHAMD (1 << 1) /* alpha blending mode */ ++#define LCD_OSDC_OSDEN (1 << 0) /* OSD mode enable */ ++ ++/* OSD Controll Register */ ++#define LCD_OSDCTRL_IPU (1 << 15) /* input data from IPU */ ++#define LCD_OSDCTRL_RGB565 (0 << 4) /* foreground 1, 16bpp, 0-RGB565, 1-RGB555 */ ++#define LCD_OSDCTRL_RGB555 (1 << 4) /* foreground 1, 16bpp, 0-RGB565, 1-RGB555 */ ++#define LCD_OSDCTRL_CHANGES (1 << 3) /* Change size flag */ ++#define LCD_OSDCTRL_OSDBPP_BIT 0 /* Bits Per Pixel of OSD Channel 1 */ ++#define LCD_OSDCTRL_OSDBPP_MASK (0x7< ++ * ++ * 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 __ASM_BOARD_SERIAL_H__ ++#define __ASM_BOARD_SERIAL_H__ ++ ++#ifndef CONFIG_SERIAL_MANY_PORTS ++#undef RS_TABLE_SIZE ++#define RS_TABLE_SIZE 1 ++#endif ++ ++#define JZ_BASE_BAUD (12000000/16) ++ ++#define JZ_SERIAL_PORT_DEFNS \ ++ { .baud_base = JZ_BASE_BAUD, .irq = IRQ_UART0, \ ++ .flags = STD_COM_FLAGS, .iomem_base = (u8 *)UART0_BASE, \ ++ .iomem_reg_shift = 2, .io_type = SERIAL_IO_MEM }, ++ ++#endif /* __ASM_BORAD_SERIAL_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750/war.h linux-2.6.24.7/include/asm-mips/mach-jz4750/war.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750/war.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750/war.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,25 @@ ++/* ++ * 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) 2002, 2004, 2007 by Ralf Baechle ++ */ ++#ifndef __ASM_MIPS_MACH_JZ4740_WAR_H ++#define __ASM_MIPS_MACH_JZ4740_WAR_H ++ ++#define R4600_V1_INDEX_ICACHEOP_WAR 0 ++#define R4600_V1_HIT_CACHEOP_WAR 0 ++#define R4600_V2_HIT_CACHEOP_WAR 0 ++#define R5432_CP0_INTERRUPT_WAR 0 ++#define BCM1250_M3_WAR 0 ++#define SIBYTE_1956_WAR 0 ++#define MIPS4K_ICACHE_REFILL_WAR 0 ++#define MIPS_CACHE_SYNC_WAR 0 ++#define TX49XX_ICACHE_INDEX_INV_WAR 0 ++#define RM9000_CDEX_SMP_WAR 0 ++#define ICACHE_REFILLS_WORKAROUND_WAR 0 ++#define R10000_LLSC_WAR 0 ++#define MIPS34K_MISSED_ITLB_WAR 0 ++ ++#endif /* __ASM_MIPS_MACH_JZ4740_WAR_H */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750d/board-fuwa1.h linux-2.6.24.7/include/asm-mips/mach-jz4750d/board-fuwa1.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750d/board-fuwa1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750d/board-fuwa1.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,124 @@ ++/* ++ * linux/include/asm-mips/mach-jz4750d/board-fuwa1.h ++ * ++ * JZ4750D-based FUWA1 board ver 1.x definition. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4750D_FUWA1_H__ ++#define __ASM_JZ4750D_FUWA1_H__ ++ ++#define CONFIG_FPGA /* fuwa is an FPGA board */ ++ ++/*====================================================================== ++ * Frequencies of on-board oscillators ++ */ ++#define JZ_EXTAL 48000000 /* Main extal freq: 12 MHz */ ++#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */ ++#define CFG_DIV 1 /* hclk=pclk=mclk=CFG_EXTAL/CFG_DIV, just for FPGA board */ ++ ++ ++/*====================================================================== ++ * GPIO ++ */ ++#define GPIO_SD0_VCC_EN_N (32*2+10) /* GPC10 */ ++#define GPIO_SD0_CD_N (32*2+11) /* GPC11 */ ++#define GPIO_SD0_WP (32*2+12) /* GPC12 */ ++#define GPIO_SD1_VCC_EN_N (32*2+13) /* GPC13 */ ++#define GPIO_SD1_CD_N (32*2+14) /* GPC14 */ ++#define GPIO_USB_DETE 102 /* GPD6 */ ++#define GPIO_DC_DETE_N 103 /* GPD7 */ ++#define GPIO_CHARG_STAT_N 111 /* GPD15 */ ++#define GPIO_DISP_OFF_N 121 /* GPD25, LCD_REV */ ++//#define GPIO_LED_EN 124 /* GPD28 */ ++ ++#define GPIO_UDC_HOTPLUG GPIO_USB_DETE ++ ++/*====================================================================== ++ * LCD backlight ++ */ ++#define GPIO_LCD_PWM (32*4+20) /* GPE20 */ ++ ++#define LCD_PWM_CHN 0 /* pwm channel */ ++#define LCD_PWM_FULL 101 ++/* 100 level: 0,1,...,100 */ ++#define __lcd_set_backlight_level(n) \ ++do { \ ++ __gpio_as_output(GPIO_LCD_PWM); \ ++ __gpio_set_pin(GPIO_LCD_PWM); \ ++} while (0) ++ ++#define __lcd_close_backlight() \ ++do { \ ++ __gpio_as_output(GPIO_LCD_PWM); \ ++ __gpio_clear_pin(GPIO_LCD_PWM); \ ++} while (0) ++ ++/*====================================================================== ++ * MMC/SD ++ */ ++ ++#define MSC0_WP_PIN GPIO_SD0_WP ++#define MSC0_HOTPLUG_PIN GPIO_SD0_CD_N ++#define MSC0_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD0_CD_N) ++ ++#define MSC1_WP_PIN GPIO_SD1_WP ++#define MSC1_HOTPLUG_PIN GPIO_SD1_CD_N ++#define MSC1_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD1_CD_N) ++ ++#define __msc0_init_io() \ ++do { \ ++ __gpio_as_output(GPIO_SD0_VCC_EN_N); \ ++ __gpio_as_input(GPIO_SD0_CD_N); \ ++} while (0) ++ ++#define __msc0_enable_power() \ ++do { \ ++ __gpio_clear_pin(GPIO_SD0_VCC_EN_N); \ ++} while (0) ++ ++#define __msc0_disable_power() \ ++do { \ ++ __gpio_set_pin(GPIO_SD0_VCC_EN_N); \ ++} while (0) ++ ++#define __msc0_card_detected(s) \ ++({ \ ++ int detected = 1; \ ++ if (__gpio_get_pin(GPIO_SD0_CD_N)) \ ++ detected = 0; \ ++ detected; \ ++}) ++ ++#define __msc1_init_io() \ ++do { \ ++ /* __gpio_as_output(GPIO_SD1_VCC_EN_N);*/ \ ++ /* __gpio_as_input(GPIO_SD1_CD_N);*/ \ ++} while (0) ++ ++#define __msc1_enable_power() \ ++do { \ ++ /* __gpio_clear_pin(GPIO_SD1_VCC_EN_N);*/ \ ++} while (0) ++ ++#define __msc1_disable_power() \ ++do { \ ++ /* __gpio_set_pin(GPIO_SD1_VCC_EN_N);*/ \ ++} while (0) ++ ++#define __msc1_card_detected(s) \ ++({ \ ++ int detected = 0; \ ++ if (__gpio_get_pin(GPIO_SD1_CD_N)) \ ++ detected = 1; \ ++ detected; \ ++}) ++ ++#endif /* __ASM_JZ4750d_FUWA1_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750d/clock.h linux-2.6.24.7/include/asm-mips/mach-jz4750d/clock.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750d/clock.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750d/clock.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,230 @@ ++/* ++ * linux/include/asm-mips/mach-jz4750d/clock.h ++ * ++ * JZ4750D clocks definition. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4750D_CLOCK_H__ ++#define __ASM_JZ4750D_CLOCK_H__ ++ ++#ifndef JZ_EXTAL ++#define JZ_EXTAL 12000000 /* 3.6864 MHz */ ++#endif ++#ifndef JZ_EXTAL2 ++#define JZ_EXTAL2 32768 /* 32.768 KHz */ ++#endif ++ ++/* ++ * JZ4750D clocks structure ++ */ ++typedef struct { ++ unsigned int cclk; /* CPU clock */ ++ unsigned int hclk; /* System bus clock */ ++ unsigned int pclk; /* Peripheral bus clock */ ++ unsigned int mclk; /* Flash/SRAM/SDRAM clock */ ++ unsigned int lcdclk; /* LCDC module clock */ ++ unsigned int pixclk; /* LCD pixel clock */ ++ unsigned int i2sclk; /* AIC module clock */ ++ unsigned int usbclk; /* USB module clock */ ++ unsigned int mscclk; /* MSC module clock */ ++ unsigned int extalclk; /* EXTAL clock for UART,I2C,SSI,TCU,USB-PHY */ ++ unsigned int rtcclk; /* RTC clock for CPM,INTC,RTC,TCU,WDT */ ++} jz_clocks_t; ++ ++extern jz_clocks_t jz_clocks; ++ ++ ++/* PLL output frequency */ ++static __inline__ unsigned int __cpm_get_pllout(void) ++{ ++#if defined(CONFIG_FPGA) ++ return JZ_EXTAL/CFG_DIV; ++#else ++ unsigned long m, n, no, pllout; ++ unsigned long cppcr = REG_CPM_CPPCR; ++ unsigned long od[4] = {1, 2, 2, 4}; ++ if ((cppcr & CPM_CPPCR_PLLEN) && !(cppcr & CPM_CPPCR_PLLBP)) { ++ m = __cpm_get_pllm() + 2; ++ n = __cpm_get_plln() + 2; ++ no = od[__cpm_get_pllod()]; ++ pllout = ((JZ_EXTAL) / (n * no)) * m; ++ } else ++ pllout = JZ_EXTAL; ++ return pllout; ++#endif ++} ++ ++/* PLL output frequency for MSC/I2S/LCD/USB */ ++static __inline__ unsigned int __cpm_get_pllout2(void) ++{ ++#if defined(CONFIG_FPGA) ++ return JZ_EXTAL/CFG_DIV; ++#else ++ if (REG_CPM_CPCCR & CPM_CPCCR_PCS) ++ return __cpm_get_pllout(); ++ else ++ return __cpm_get_pllout()/2; ++#endif ++} ++ ++/* CPU core clock */ ++static __inline__ unsigned int __cpm_get_cclk(void) ++{ ++ ++#if defined(CONFIG_FGPA) ++ return JZ_EXTAL; ++#else ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ return __cpm_get_pllout() / div[__cpm_get_cdiv()]; ++#endif ++} ++ ++/* AHB system bus clock */ ++static __inline__ unsigned int __cpm_get_hclk(void) ++{ ++#if defined(CONFIG_FPGA) ++ return JZ_EXTAL/CFG_DIV; ++#else ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ ++ return __cpm_get_pllout() / div[__cpm_get_hdiv()]; ++#endif ++ ++} ++ ++/* Memory bus clock */ ++static __inline__ unsigned int __cpm_get_mclk(void) ++{ ++#if defined(CONFIG_FPGA) ++ return JZ_EXTAL/CFG_DIV; ++#else ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ ++ return __cpm_get_pllout() / div[__cpm_get_mdiv()]; ++#endif ++} ++ ++/* APB peripheral bus clock */ ++static __inline__ unsigned int __cpm_get_pclk(void) ++{ ++#if defined(CONFIG_FPGA) ++ return JZ_EXTAL/CFG_DIV; ++#else ++ int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; ++ ++ return __cpm_get_pllout() / div[__cpm_get_pdiv()]; ++#endif ++} ++ ++/* LCDC module clock */ ++static __inline__ unsigned int __cpm_get_lcdclk(void) ++{ ++ return __cpm_get_pllout2() / (__cpm_get_ldiv() + 1); ++} ++ ++/* LCD pixel clock */ ++static __inline__ unsigned int __cpm_get_pixclk(void) ++{ ++ return __cpm_get_pllout2() / (__cpm_get_pixdiv() + 1); ++} ++ ++/* I2S clock */ ++static __inline__ unsigned int __cpm_get_i2sclk(void) ++{ ++ if (REG_CPM_CPCCR & CPM_CPCCR_I2CS) { ++ return __cpm_get_pllout2() / (__cpm_get_i2sdiv() + 1); ++ } ++ else { ++ return JZ_EXTAL; ++ } ++} ++ ++/* USB clock */ ++static __inline__ unsigned int __cpm_get_usbclk(void) ++{ ++ if (REG_CPM_CPCCR & CPM_CPCCR_UCS) { ++ return __cpm_get_pllout2() / (__cpm_get_udiv() + 1); ++ } ++ else { ++ return JZ_EXTAL; ++ } ++} ++ ++/* ++ * MSC clock ++ * @n: the index of MMC/SD controller ++ */ ++static __inline__ unsigned int __cpm_get_mscclk(int n) ++{ ++ return __cpm_get_pllout2() / (__cpm_get_mscdiv(n) + 1); ++} ++ ++/* EXTAL clock */ ++static __inline__ unsigned int __cpm_get_extalclk0(void) ++{ ++ return JZ_EXTAL; ++} ++ ++/* EXTAL clock for UART,I2C,SSI,TCU,USB-PHY */ ++static __inline__ unsigned int __cpm_get_extalclk(void) ++{ ++#if defined(CONFIG_FPGA) ++ return __cpm_get_pllout(); ++#else ++ if (REG_CPM_CPCCR & CPM_CPCCR_ECS) ++ return __cpm_get_extalclk0()/2; ++ else ++ return __cpm_get_extalclk0(); ++#endif ++ ++} ++ ++/* RTC clock for CPM,INTC,RTC,TCU,WDT */ ++static __inline__ unsigned int __cpm_get_rtcclk(void) ++{ ++ return JZ_EXTAL2; ++} ++ ++/* ++ * Output 24MHz for SD and 16MHz for MMC. ++ * @n: the index of MMC/SD controller ++ */ ++static inline void __cpm_select_msc_clk(int n, int sd) ++{ ++ unsigned int pllout2 = __cpm_get_pllout2(); ++ unsigned int div = 0; ++ ++ if (sd) { ++ div = pllout2 / 24000000; ++ } ++ else { ++ div = pllout2 / 16000000; ++ } ++ ++ REG_CPM_MSCCDR(n) = div - 1; ++ REG_CPM_CPCCR |= CPM_CPCCR_CE; ++} ++ ++/* ++ * Output 48MHz for high speed card. ++ */ ++static inline void __cpm_select_msc_clk_high(int n, int sd) ++{ ++ unsigned int pllout2 = __cpm_get_pllout2(); ++ unsigned int div = 0; ++ ++ div = pllout2 / 48000000; ++ ++ REG_CPM_MSCCDR(n) = div - 1; ++ REG_CPM_CPCCR |= CPM_CPCCR_CE; ++} ++ ++#endif /* __ASM_JZ4750D_CLOCK_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750d/dma.h linux-2.6.24.7/include/asm-mips/mach-jz4750d/dma.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750d/dma.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750d/dma.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,307 @@ ++/* ++ * linux/include/asm-mips/mach-jz4750d/dma.h ++ * ++ * JZ4750D DMA definition. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4750D_DMA_H__ ++#define __ASM_JZ4750D_DMA_H__ ++ ++#include ++#include /* need byte IO */ ++#include /* And spinlocks */ ++#include ++#include ++ ++/* ++ * Descriptor structure for JZ4750D DMA engine ++ * Note: this structure must always be aligned to a 16-bytes boundary. ++ */ ++ ++/* old descriptor 4-word */ ++typedef struct { ++ volatile u32 dcmd; /* DCMD value for the current transfer */ ++ volatile u32 dsadr; /* DSAR value for the current transfer */ ++ volatile u32 dtadr; /* DTAR value for the current transfer */ ++ volatile u32 ddadr; /* Points to the next descriptor + transfer count */ ++} jz_dma_desc; ++ ++/* new descriptor 8-word */ ++typedef struct { ++ volatile u32 dcmd; /* DCMD value for the current transfer */ ++ volatile u32 dsadr; /* DSAR value for the current transfer */ ++ volatile u32 dtadr; /* DTAR value for the current transfer */ ++ volatile u32 ddadr; /* Points to the next descriptor + transfer count */ ++ volatile u32 dstrd; /* DMA source and target stride address */ ++ volatile u32 dreqt; /* DMA request type for current transfer */ ++ volatile u32 reserved0; /* Reserved */ ++ volatile u32 reserved1; /* Reserved */ ++} jz_dma_desc_8word; ++ ++/* DMA Device ID's follow */ ++enum { ++ DMA_ID_EXT = 0, /* External request with DREQn */ ++ DMA_ID_NAND, /* NAND DMA request */ ++ DMA_ID_BCH_ENC, /* BCH Encoding DMA request */ ++ DMA_ID_BCH_DEC, /* BCH Decoding DMA request */ ++ DMA_ID_AUTO, /* Auto-request */ ++// DMA_ID_TSSI_RX, /* TSSI receive fifo full request */ ++ DMA_ID_UART3_TX, /* UART3 transmit-fifo-empty request */ ++ DMA_ID_UART3_RX, /* UART3 receve-fifo-full request */ ++ DMA_ID_UART2_TX, /* UART2 transmit-fifo-empty request */ ++ DMA_ID_UART2_RX, /* UART2 receve-fifo-full request */ ++ DMA_ID_UART1_TX, /* UART1 transmit-fifo-empty request */ ++ DMA_ID_UART1_RX, /* UART1 receve-fifo-full request */ ++ DMA_ID_UART0_TX, /* UART0 transmit-fifo-empty request */ ++ DMA_ID_UART0_RX, /* UART0 receve-fifo-full request */ ++ DMA_ID_SSI0_TX, /* SSI0 transmit-fifo-full request */ ++ DMA_ID_SSI0_RX, /* SSI0 receive-fifo-empty request */ ++ DMA_ID_AIC_TX, /* AIC transmit-fifo-full request */ ++ DMA_ID_AIC_RX, /* AIC receive-fifo-empty request */ ++ DMA_ID_MSC0_TX, /* MSC0 transmit-fifo-full request */ ++ DMA_ID_MSC0_RX, /* MSC0 receive-fifo-empty request */ ++ DMA_ID_TCU_OVERFLOW, /* TCU channel n overflow interrupt */ ++ DMA_ID_SADC, /* SADC transfer request */ ++ DMA_ID_MSC1_TX, /* MSC1 transmit-fifo-full request */ ++ DMA_ID_MSC1_RX, /* MSC1 receive-fifo-empty request */ ++ DMA_ID_SSI1_TX, /* SSI1 transmit-fifo-full request */ ++ DMA_ID_SSI1_RX, /* SSI1 receive-fifo-empty request */ ++ DMA_ID_PCM_TX, /* PM transmit-fifo-full request */ ++ DMA_ID_PCM_RX, /* PM receive-fifo-empty request */ ++ DMA_ID_RAW_SET, ++ DMA_ID_MAX ++}; ++ ++/* DMA modes, simulated by sw */ ++#define DMA_MODE_READ 0x0 /* I/O to memory, no autoinit, increment, single mode */ ++#define DMA_MODE_WRITE 0x1 /* memory to I/O, no autoinit, increment, single mode */ ++#define DMA_AUTOINIT 0x2 ++#define DMA_MODE_MASK 0x3 ++ ++struct jz_dma_chan { ++ int dev_id; /* DMA ID: this channel is allocated if >=0, free otherwise */ ++ unsigned int io; /* DMA channel number */ ++ const char *dev_str; /* string describes the DMA channel */ ++ int irq; /* DMA irq number */ ++ void *irq_dev; /* DMA private device structure */ ++ unsigned int fifo_addr; /* physical fifo address of the requested device */ ++ unsigned int cntl; /* DMA controll */ ++ unsigned int mode; /* DMA configuration */ ++ unsigned int source; /* DMA request source */ ++}; ++ ++extern struct jz_dma_chan jz_dma_table[]; ++ ++ ++#define DMA_8BIT_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_8BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_8BIT_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \ ++ DMAC_DCMD_DS_8BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_16BIT_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_16BIT_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_32BIT_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_32BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_32BIT_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_32BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_16BYTE_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_16BYTE_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_32BYTE_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_32BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_32BYTE_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \ ++ DMAC_DCMD_DS_32BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_32_32BYTE_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_32BYTE | DMAC_DCMD_RDIL_IGN ++#define DMA_AIC_32_16BYTE_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_32_16BYTE_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_16BIT_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_16BIT_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_16BYTE_RX_CMD \ ++ DMAC_DCMD_DAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_16BYTE_TX_CMD \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++#define DMA_AIC_16BYTE_TX_CMD_UC \ ++ DMAC_DCMD_SAI | \ ++ DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_16 | \ ++ DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN ++ ++extern int jz_request_dma(int dev_id, ++ const char *dev_str, ++ irqreturn_t (*irqhandler)(int, void *), ++ unsigned long irqflags, ++ void *irq_dev_id); ++extern void jz_free_dma(unsigned int dmanr); ++ ++extern int jz_dma_read_proc(char *buf, char **start, off_t fpos, ++ int length, int *eof, void *data); ++extern void dump_jz_dma_channel(unsigned int dmanr); ++ ++extern void enable_dma(unsigned int dmanr); ++extern void disable_dma(unsigned int dmanr); ++extern void set_dma_addr(unsigned int dmanr, unsigned int phyaddr); ++extern void set_dma_count(unsigned int dmanr, unsigned int bytecnt); ++extern void set_dma_mode(unsigned int dmanr, unsigned int mode); ++extern void jz_set_oss_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt); ++extern void jz_set_alsa_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt); ++extern void jz_set_dma_src_width(int dmanr, int nbit); ++extern void jz_set_dma_dest_width(int dmanr, int nbit); ++extern void jz_set_dma_block_size(int dmanr, int nbyte); ++extern unsigned int get_dma_residue(unsigned int dmanr); ++ ++extern spinlock_t dma_spin_lock; ++ ++static __inline__ unsigned long claim_dma_lock(void) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&dma_spin_lock, flags); ++ return flags; ++} ++ ++static __inline__ void release_dma_lock(unsigned long flags) ++{ ++ spin_unlock_irqrestore(&dma_spin_lock, flags); ++} ++ ++/* Clear the 'DMA Pointer Flip Flop'. ++ * Write 0 for LSB/MSB, 1 for MSB/LSB access. ++ */ ++#define clear_dma_ff(channel) ++ ++static __inline__ struct jz_dma_chan *get_dma_chan(unsigned int dmanr) ++{ ++ if (dmanr > MAX_DMA_NUM ++ || jz_dma_table[dmanr].dev_id < 0) ++ return NULL; ++ return &jz_dma_table[dmanr]; ++} ++ ++static __inline__ int dma_halted(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 1; ++ return __dmac_channel_transmit_halt_detected(dmanr) ? 1 : 0; ++} ++ ++static __inline__ unsigned int get_dma_mode(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 0; ++ return chan->mode; ++} ++ ++static __inline__ void clear_dma_done(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); ++} ++ ++static __inline__ void clear_dma_halt(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT); ++ REG_DMAC_DMACR((chan->io)/HALF_DMA_NUM) &= ~(DMAC_DMACR_HLT); ++} ++ ++static __inline__ void clear_dma_flag(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return; ++ REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); ++ REG_DMAC_DMACR((chan->io)/HALF_DMA_NUM) &= ~(DMAC_DMACR_HLT | DMAC_DMACR_AR); ++} ++ ++static __inline__ void set_dma_page(unsigned int dmanr, char pagenr) ++{ ++} ++ ++static __inline__ unsigned int get_dma_done_status(unsigned int dmanr) ++{ ++ unsigned long dccsr; ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return 0; ++ dccsr = REG_DMAC_DCCSR(chan->io); ++ return dccsr & (DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR); ++} ++ ++static __inline__ int get_dma_done_irq(unsigned int dmanr) ++{ ++ struct jz_dma_chan *chan = get_dma_chan(dmanr); ++ if (!chan) ++ return -1; ++ return chan->irq; ++} ++ ++#endif /* __ASM_JZ4750D_DMA_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750d/jz4750d.h linux-2.6.24.7/include/asm-mips/mach-jz4750d/jz4750d.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750d/jz4750d.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750d/jz4750d.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,40 @@ ++/* ++ * linux/include/asm-mips/mach-jz4750d/jz4750d.h ++ * ++ * JZ4750 common definition. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4750_H__ ++#define __ASM_JZ4750_H__ ++ ++#include ++#include ++#include ++#include ++ ++/*------------------------------------------------------------------ ++ * Platform definitions ++ */ ++#ifdef CONFIG_JZ4750D_FUWA1 ++#include ++#endif ++ ++/* Add other platform definition here ... */ ++ ++ ++/*------------------------------------------------------------------ ++ * Follows are related to platform definitions ++ */ ++ ++#include ++#include ++ ++#endif /* __ASM_JZ4750_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750d/misc.h linux-2.6.24.7/include/asm-mips/mach-jz4750d/misc.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750d/misc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750d/misc.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,44 @@ ++/* ++ * linux/include/asm-mips/mach-jz4750d/misc.h ++ * ++ * Ingenic's JZ4750D common include. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * ++ * Author: ++ * ++ * 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 __ASM_JZ4750D_MISC_H__ ++#define __ASM_JZ4750D_MISC_H__ ++ ++/*========================================================== ++ * I2C ++ *===========================================================*/ ++ ++#define I2C_EEPROM_DEV 0xA /* b'1010 */ ++#define I2C_RTC_DEV 0xD /* b'1101 */ ++#define DIMM0_SPD_ADDR 0 ++#define DIMM1_SPD_ADDR 1 ++#define DIMM2_SPD_ADDR 2 ++#define DIMM3_SPD_ADDR 3 ++#define JZ_HCI_ADDR 7 ++ ++#define DIMM_SPD_LEN 128 ++#define JZ_HCI_LEN 512 /* 4K bits E2PROM */ ++#define I2C_RTC_LEN 16 ++#define HCI_MAC_OFFSET 64 ++ ++extern void i2c_open(void); ++extern void i2c_close(void); ++extern void i2c_setclk(unsigned int i2cclk); ++ ++extern int i2c_read(unsigned char device, unsigned char *buf, ++ unsigned char address, int count); ++extern int i2c_write(unsigned char device, unsigned char *buf, ++ unsigned char address, int count); ++ ++#endif /* __ASM_JZ4750D_MISC_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750d/ops.h linux-2.6.24.7/include/asm-mips/mach-jz4750d/ops.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750d/ops.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750d/ops.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,3450 @@ ++/* ++ * linux/include/asm-mips/mach-jz4750d/ops.h ++ * ++ * JZ4750D register definition. ++ * ++ * Copyright (C) 2008 Ingenic Semiconductor Inc. ++ * ++ * 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 __JZ4750D_OPS_H__ ++#define __JZ4750D_OPS_H__ ++ ++/* ++ * Definition of Module Operations ++ */ ++ ++/*************************************************************************** ++ * EMC ++ ***************************************************************************/ ++#define is_share_mode() ((REG_EMC_BCR & EMC_BCR_BSR_MASK) == EMC_BCR_BSR_SHARE) ++ ++/*************************************************************************** ++ * GPIO ++ ***************************************************************************/ ++ ++//------------------------------------------------------ ++// GPIO Pins Description ++// ++// PORT 0: ++// ++// PIN/BIT N FUNC0 FUNC1 NOTE ++// 0 D0 - ++// 1 D1 - ++// 2 D2 - ++// 3 D3 - ++// 4 D4 - ++// 5 D5 - ++// 6 D6 - ++// 7 D7 - ++// 8 D8 - ++// 9 D9 - ++// 10 D10 - ++// 11 D11 - ++// 12 D12 - ++// 13 D13 - ++// 14 D14 - ++// 15 D15 - ++// 16 D16 - ++// 17 D17 - ++// 18 D18 - ++// 19 D19 - ++// 20 D20 - ++// 21 D21 - ++// 22 D22 - ++// 23 D23 - ++// 24 D24 - ++// 25 D25 - ++// 26 D26 - ++// 27 D27 - ++// 28 D28 - ++// 29 D29 - ++// 30 D30 - ++// 31 D31 - ++// ++//------------------------------------------------------ ++// PORT 1: ++// ++// PIN/BIT N FUNC0 FUNC1 FUNC2 NOTE ++// 0 A0 - - ++// 1 A1 - - ++// 2 A2 - - ++// 3 A3 - - ++// 4 A4 - - ++// 5 A5 - - ++// 6 A6 - - ++// 7 A7 - - ++// 8 A8 - - ++// 9 A9 - - ++// 10 A10 - - ++// 11 A11 - - ++// 12 A12 - - ++// 13 A13 - - ++// 14 A14 - - ++// 15 A15/CLE CL(unshare) MSC0_CLK ++// 16 DCS0# - - ++// 17 RAS# - - ++// 18 CAS# - - ++// 19 SDWE#/BUFD# - - ++// 20 WE0# - - ++// 21 WE1# - - ++// 22 WE2# - - ++// 23 WE3# - - ++// 24 CKO - - Note1 ++// 25 CKE - - ++// 26 SSI_CLK MSC1_CLK - ++// 27 SSI_DT MSC1_D1 - ++// 28 SSI_DR MSC1_D0 - ++// 29 SSI_CE0# MSC1_CMD - ++// 30 SSI_GPC MSC1_D2 - ++// 31 SSI_CE1# MSC1_D3 - ++// ++// Note1: BIT24: it is CKO when chip is reset ++// ++//------------------------------------------------------ ++// PORT 2: ++// ++// PIN/BIT N FUNC0 FUNC1 FUNC2 NOTE ++// 0 SD0 A20 - ++// 1 SD1 A21 - ++// 2 SD2 A22 - ++// 3 SD3 A23 - ++// 4 SD4 A24 - ++// 5 SD5 A25 - ++// 6 SD6 - - ++// 7 SD7 - - ++// 8 SD8 TSDI0 - ++// 9 SD9 TSDI1 - ++// 10 SD10 TSDI2 - ++// 11 SD11 TSDI3 - ++// 12 SD12 TSDI4 - ++// 13 SD13 TSDI5 - ++// 14 SD14 TSDI6 - ++// 15 SD15 TSDI7 - ++// 16 A16/ALE AL(unshare) MSC0_CMD ++// 17 A17 MSC0_D3 - ++// 18 A18 DREQ - ++// 19 A19 DACK - ++// 20 WAIT# - - Note2 ++// 21 CS1# - - ++// 22 CS2# - - ++// 23 CS3# - - ++// 24 CS4# - - ++// 25 RD# - - ++// 26 WR# - - ++// 27 FRB# - - Note3 ++// 28 FRE# MSC0_D0 - ++// 29 FWE# MSC0_D1 - ++// 30 - - - Note4 ++// 31 - - - Note5 ++// ++// Note2: BIT20: it is WIAT# pin when chip is reset ++// ++// Note3: BIT27: when NAND is used, it should connect to NANF FRB#. ++// ++// Note4: BIT30: it is BOOT_SEL0 which would be set as input without pulling when chip is reset. ++// ++// Note5: BIT31: it is BOOT_SEL1 which would be set as input without pulling when chip is reset. ++// ++//------------------------------------------------------ ++// PORT 3: ++// ++// PIN/BIT N FUNC0 FUNC1 NOTE ++// 0 LCD_B2 - ++// 1 LCD_B3 - ++// 2 LCD_B4 - ++// 3 LCD_B5 - ++// 4 LCD_B6 - ++// 5 LCD_B7 - ++// 6 LCD_G2 - ++// 7 LCD_G3 - ++// 8 LCD_G4 - ++// 9 LCD_G5 - ++// 10 LCD_G6 - ++// 11 LCD_G7 - ++// 12 LCD_R2 - ++// 13 LCD_R3 - ++// 14 LCD_R4 - ++// 15 LCD_R5 - ++// 16 LCD_R6 - ++// 17 LCD_R7 - ++// 18 LCD_PCLK - ++// 19 LCD_HSYNC - ++// 20 LCD_VSYNC - ++// 21 LCD_DE - ++// 22 LCD_CLS LCD_R1 ++// 23 LCD_SPL LCD_G0 ++// 24 LCD_PS LCD_G1 ++// 25 LCD_REV LCD_B1 ++// 26 LCD_B0 - ++// 27 LCD_R0 - ++// 28 UART0_RXD TSCLK ++// 29 UART0_TXD TSSTR ++// 30 UART0_CTS TSFRM ++// 31 UART0_RTS TSFAIL ++// ++//------------------------------------------------------ ++// PORT 4: ++// ++// PIN/BIT N FUNC0 FUNC1 FUNC2 NOTE ++// 0 CIM_D0 TSDI0 - ++// 1 CIM_D1 TSDI1 - ++// 2 CIM_D2 TSDI2 - ++// 3 CIM_D3 TSDI3 - ++// 4 CIM_D4 TSDI4 - ++// 5 CIM_D5 TSDI5 - ++// 6 CIM_D6 TSDI6 - ++// 7 CIM_D7 TSDI7 - ++// 8 CIM_MCLK TSFAIL - ++// 9 CIM_PCLK TSCLK - ++// 10 CIM_VSYNC TSSTR - ++// 11 CIM_HSYNC TSFRM - ++// 12 I2C_SDA - - ++// 13 I2C_SCK - - ++// 18 SDATO - - ++// 19 SDATI - - ++// 20 PWM0 - - ++// 22 PWM2 SYNC - ++// 23 PWM3 UART1_RxD BCLK ++// 24 PWM4 - - ++// 25 PWM5 UART1_TxD SCLK_RSTN ++// 28 DCS1# - - ++// 29 - - - Note6 ++// 30 WKUP - - Note7 ++// 31 - - - Note8 ++// ++// Note6: BIT29: it is BOOT_SEL2 which would be set as input without pulling when chip is reset. ++// Note7: BIT30: it is only used as input and interrupt, and with no pull-up and pull-down ++// Note8: BIT31: it is used to select the function of UART or JTAG set by PESEL[31] ++// PESEL[31] = 0, select JTAG function ++// PESEL[31] = 1, select UART function ++// ++//------------------------------------------------------ ++// PORT 5: ++// ++// PIN/BIT N FUNC0 FUNC1 NOTE ++// 10 SSI_CLK - ++// 11 SSI_DT PWM1 ++// 12 SSI_DR - ++// 13 SSI_CE0# - ++// 14 SSI_GPC - ++// 15 SSI_CE2# - ++// ++////////////////////////////////////////////////////////// ++ ++/*---------------------------------------------------------------- ++ * p is the port number (0,1,2,3,4,5) ++ * o is the pin offset (0-31) inside the port ++ * n is the absolute number of a pin (0-127), regardless of the port ++ */ ++ ++//------------------------------------------- ++// Function Pins Mode ++ ++#define __gpio_as_func0(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXFUNS(p) = (1 << o); \ ++ REG_GPIO_PXTRGC(p) = (1 << o); \ ++ REG_GPIO_PXSELC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_as_func1(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXFUNS(p) = (1 << o); \ ++ REG_GPIO_PXTRGC(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_as_func2(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXFUNS(p) = (1 << o); \ ++ REG_GPIO_PXTRGS(p) = (1 << o); \ ++ REG_GPIO_PXSELC(p) = (1 << o); \ ++} while (0) ++ ++/* ++ * D0 ~ D31, A0 ~ A14, DCS0#, RAS#, CAS#, ++ * RDWE#, WE0#, WE1#, WE2#, WE3#, CKO#, CKE# ++ */ ++#define __gpio_as_sdram_32bit() \ ++do { \ ++ REG_GPIO_PXFUNS(0) = 0xffffffff; \ ++ REG_GPIO_PXSELC(0) = 0xffffffff; \ ++ REG_GPIO_PXPES(0) = 0xffffffff; \ ++ REG_GPIO_PXFUNS(1) = 0x03ff7fff; \ ++ REG_GPIO_PXSELC(1) = 0x03ff7fff; \ ++ REG_GPIO_PXPES(1) = 0x03ff7fff; \ ++} while (0) ++ ++ ++/* ++ * D0 ~ D31, A0 ~ A14, DCS0#, RAS#, CAS#, ++ * RDWE#, WE0#, WE1#, WE2#, WE3#, CKO#, CKE# ++ * !!!!DCS1# ++ */ ++#define __gpio_as_sdram_x2_32bit() \ ++do { \ ++ REG_GPIO_PXFUNS(0) = 0xffffffff; \ ++ REG_GPIO_PXSELC(0) = 0xffffffff; \ ++ REG_GPIO_PXPES(0) = 0xffffffff; \ ++ REG_GPIO_PXFUNS(1) = 0x03ff7fff; \ ++ REG_GPIO_PXSELC(1) = 0x03ff7fff; \ ++ REG_GPIO_PXPES(1) = 0x03ff7fff; \ ++ REG_GPIO_PXFUNS(4) = 0x10000000; \ ++ REG_GPIO_PXSELC(4) = 0x10000000; \ ++ REG_GPIO_PXPES(4) = 0x10000000; \ ++} while (0) ++ ++/* ++ * D0 ~ D15, A0 ~ A14, DCS0#, RAS#, CAS#, ++ * RDWE#, WE0#, WE1#, WE2#, WE3#, CKO#, CKE# ++ */ ++#define __gpio_as_sdram_16bit() \ ++do { \ ++ /* 32/16-bit data normal order */ \ ++ REG_GPIO_PXFUNS(0) = 0x0000ffff; \ ++ REG_GPIO_PXSELC(0) = 0x0000ffff; \ ++ REG_GPIO_PXPES(0) = 0x0000ffff; \ ++ REG_GPIO_PXFUNS(1) = 0x03ff7fff; \ ++ REG_GPIO_PXSELC(1) = 0x03ff7fff; \ ++ REG_GPIO_PXPES(1) = 0x03ff7fff; \ ++} while (0) ++ ++/* ++ * D0 ~ D7, CS1#, CLE, ALE, FRE#, FWE#, FRB#, RDWE#/BUFD# ++ * @n: chip select number(1 ~ 4) ++ */ ++#define __gpio_as_nand_8bit(n) \ ++do { \ ++ if (!is_share_mode()) { \ ++ /* unshare mode */ \ ++ REG_GPIO_PXFUNS(2) = 0x000000ff; /* SD0~SD7 */ \ ++ REG_GPIO_PXSELS(2) = 0x000000ff; \ ++ REG_GPIO_PXPES(2) = 0x000000ff; \ ++ REG_GPIO_PXFUNS(1) = 0x00008000; /* CLE(SA3) */ \ ++ REG_GPIO_PXSELS(1) = 0x00008000; \ ++ REG_GPIO_PXPES(1) = 0x00008000; \ ++ REG_GPIO_PXFUNS(2) = 0x00010000; /* ALE(SA4) */ \ ++ REG_GPIO_PXSELS(2) = 0x00010000; \ ++ REG_GPIO_PXPES(2) = 0x00010000; \ ++ } else { \ ++ /* 32/16-bit data bus */ \ ++ REG_GPIO_PXFUNS(0) = 0x000000ff; /* D0~D7 */ \ ++ REG_GPIO_PXSELC(0) = 0x000000ff; \ ++ REG_GPIO_PXPES(0) = 0x000000ff; \ ++ REG_GPIO_PXFUNS(1) = 0x00008000; /* CLE(A15) */ \ ++ REG_GPIO_PXSELC(1) = 0x00008000; \ ++ REG_GPIO_PXPES(1) = 0x00008000; \ ++ REG_GPIO_PXFUNS(2) = 0x00010000; /* ALE(A16) */ \ ++ REG_GPIO_PXSELC(2) = 0x00010000; \ ++ REG_GPIO_PXPES(2) = 0x00010000; \ ++ } \ ++ REG_GPIO_PXFUNS(2) = 0x00200000 << ((n)-1); /* CSn */ \ ++ REG_GPIO_PXSELC(2) = 0x00200000 << ((n)-1); \ ++ REG_GPIO_PXPES(2) = 0x00200000 << ((n)-1); \ ++ \ ++ REG_GPIO_PXFUNS(1) = 0x00080000; /* RDWE#/BUFD# */ \ ++ REG_GPIO_PXSELC(1) = 0x00080000; \ ++ REG_GPIO_PXPES(1) = 0x00080000; \ ++ REG_GPIO_PXFUNS(2) = 0x30000000; /* FRE#, FWE# */ \ ++ REG_GPIO_PXSELC(2) = 0x30000000; \ ++ REG_GPIO_PXPES(2) = 0x30000000; \ ++ REG_GPIO_PXFUNC(2) = 0x08000000; /* FRB#(input) */ \ ++ REG_GPIO_PXSELC(2) = 0x08000000; \ ++ REG_GPIO_PXDIRC(2) = 0x08000000; \ ++ REG_GPIO_PXPES(2) = 0x08000000; \ ++} while (0) ++ ++ ++/* ++ * CS4#, RD#, WR#, WAIT#, A0 ~ A22, D0 ~ D7 ++ * @n: chip select number(1 ~ 4) ++ */ ++#define __gpio_as_nor_8bit(n) \ ++do { \ ++ /* 32/16-bit data bus */ \ ++ REG_GPIO_PXFUNS(0) = 0x000000ff; \ ++ REG_GPIO_PXSELC(0) = 0x000000ff; \ ++ REG_GPIO_PXPES(0) = 0x000000ff; \ ++ \ ++ REG_GPIO_PXFUNS(2) = 0x00200000 << ((n)-1); /* CSn */ \ ++ REG_GPIO_PXSELC(2) = 0x00200000 << ((n)-1); \ ++ REG_GPIO_PXPES(2) = 0x00200000 << ((n)-1); \ ++ \ ++ REG_GPIO_PXFUNS(1) = 0x0000ffff; /* A0~A15 */ \ ++ REG_GPIO_PXSELC(1) = 0x0000ffff; \ ++ REG_GPIO_PXPES(1) = 0x0000ffff; \ ++ REG_GPIO_PXFUNS(2) = 0x06110007; /* RD#, WR#, WAIT#, A20~A22 */ \ ++ REG_GPIO_PXSELC(2) = 0x06110007; \ ++ REG_GPIO_PXPES(2) = 0x06110007; \ ++ REG_GPIO_PXFUNS(2) = 0x000e0000; /* A17~A19 */ \ ++ REG_GPIO_PXSELS(2) = 0x000e0000; \ ++ REG_GPIO_PXPES(2) = 0x000e0000; \ ++} while (0) ++ ++/* ++ * CS4#, RD#, WR#, WAIT#, A0 ~ A22, D0 ~ D15 ++ * @n: chip select number(1 ~ 4) ++ */ ++#define __gpio_as_nor_16bit(n) \ ++do { \ ++ /* 32/16-bit data normal order */ \ ++ REG_GPIO_PXFUNS(0) = 0x0000ffff; \ ++ REG_GPIO_PXSELC(0) = 0x0000ffff; \ ++ REG_GPIO_PXPES(0) = 0x0000ffff; \ ++ \ ++ REG_GPIO_PXFUNS(2) = 0x00200000 << ((n)-1); /* CSn */ \ ++ REG_GPIO_PXSELC(2) = 0x00200000 << ((n)-1); \ ++ REG_GPIO_PXPES(2) = 0x00200000 << ((n)-1); \ ++ \ ++ REG_GPIO_PXFUNS(1) = 0x0000ffff; /* A0~A15 */ \ ++ REG_GPIO_PXSELC(1) = 0x0000ffff; \ ++ REG_GPIO_PXPES(1) = 0x0000ffff; \ ++ REG_GPIO_PXFUNS(2) = 0x06110007; /* RD#, WR#, WAIT#, A20~A22 */ \ ++ REG_GPIO_PXSELC(2) = 0x06110007; \ ++ REG_GPIO_PXPES(2) = 0x06110007; \ ++ REG_GPIO_PXFUNS(2) = 0x000e0000; /* A17~A19 */ \ ++ REG_GPIO_PXSELS(2) = 0x000e0000; \ ++ REG_GPIO_PXPES(2) = 0x000e0000; \ ++} while (0) ++ ++/* ++ * UART0_TxD, UART0_RxD ++ */ ++#define __gpio_as_uart0() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x30000000; \ ++ REG_GPIO_PXSELC(3) = 0x30000000; \ ++ REG_GPIO_PXPES(3) = 0x30000000; \ ++} while (0) ++ ++/* ++ * UART0_TxD, UART0_RxD, UART0_CTS, UART0_RTS ++ */ ++#define __gpio_as_uart0_ctsrts() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0xf0000000; \ ++ REG_GPIO_PXSELC(3) = 0xf0000000; \ ++ REG_GPIO_PXPES(3) = 0xf0000000; \ ++} while (0) ++ ++/* ++ * UART1_TxD, UART1_RxD ++ */ ++#define __gpio_as_uart1() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x02800000; \ ++ REG_GPIO_PXSELC(4) = 0x02800000; \ ++ REG_GPIO_PXPES(4) = 0x02800000; \ ++} while (0) ++ ++/* ++ * UART2_TxD, UART2_RxD, UART2_CTS, UART2_RTS using JTAG pins ++ */ ++#define __gpio_as_uart2() \ ++do { \ ++ REG_GPIO_PXSELS(4) = 0x80000000; \ ++} while (0) ++ ++/* ++ * TSCLK, TSSTR, TSFRM, TSFAIL, TSDI0~7 ++ */ ++#define __gpio_as_tssi() \ ++do { \ ++ REG_GPIO_PXFUNS(2) = 0x0000ff00; \ ++ REG_GPIO_PXSELS(2) = 0x0000ff00; \ ++ REG_GPIO_PXPES(2) = 0x0000ff00; \ ++ REG_GPIO_PXFUNS(5) = 0x00f00000; \ ++ REG_GPIO_PXSELC(5) = 0x00f00000; \ ++ REG_GPIO_PXPES(5) = 0x00f00000; \ ++} while (0) ++ ++/* ++ * LCD_D0~LCD_D7, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE ++ */ ++#define __gpio_as_lcd_8bit() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x003c00ff; \ ++ REG_GPIO_PXTRGC(3) = 0x003c00ff; \ ++ REG_GPIO_PXSELC(3) = 0x003c00ff; \ ++ REG_GPIO_PXPES(3) = 0x003c00ff; \ ++} while (0) ++ ++/* ++ * LCD_D0~LCD_D15, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE ++ */ ++#define __gpio_as_lcd_16bit() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x003cffff; \ ++ REG_GPIO_PXTRGC(3) = 0x003cffff; \ ++ REG_GPIO_PXSELC(3) = 0x003cffff; \ ++ REG_GPIO_PXPES(3) = 0x003cffff; \ ++} while (0) ++ ++/* ++ * LCD_R2~LCD_R7, LCD_G2~LCD_G7, LCD_B2~LCD_B7, ++ * LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE ++ */ ++#define __gpio_as_lcd_18bit() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x003fffff; \ ++ REG_GPIO_PXTRGC(3) = 0x003fffff; \ ++ REG_GPIO_PXSELC(3) = 0x003fffff; \ ++ REG_GPIO_PXPES(3) = 0x003fffff; \ ++} while (0) ++ ++/* ++ * LCD_R0~LCD_R7, LCD_G0~LCD_G7, LCD_B0~LCD_B7, ++ * LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE ++ */ ++#define __gpio_as_lcd_24bit() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x0fffffff; \ ++ REG_GPIO_PXTRGC(3) = 0x0fffffff; \ ++ REG_GPIO_PXSELC(3) = 0x0c3fffff; \ ++ REG_GPIO_PXSELS(3) = 0x03c00000; \ ++ REG_GPIO_PXPES(3) = 0x0fffffff; \ ++} while (0) ++ ++/* ++ * LCD_CLS, LCD_SPL, LCD_PS, LCD_REV ++ */ ++#define __gpio_as_lcd_special() \ ++do { \ ++ REG_GPIO_PXFUNS(3) = 0x03C00000; \ ++ REG_GPIO_PXSELC(3) = 0x03C00000; \ ++ REG_GPIO_PXPES(3) = 0x03C00000; \ ++} while (0) ++ ++/* ++ * CIM_D0~CIM_D7, CIM_MCLK, CIM_PCLK, CIM_VSYNC, CIM_HSYNC ++ */ ++#define __gpio_as_cim() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x00000fff; \ ++ REG_GPIO_PXSELC(4) = 0x00000fff; \ ++ REG_GPIO_PXPES(4) = 0x00000fff; \ ++} while (0) ++ ++/* ++ * SDATO, SDATI, BCLK, SYNC, SCLK_RSTN(gpio sepc) or ++ * SDATA_OUT, SDATA_IN, BIT_CLK, SYNC, SCLK_RESET(aic spec) ++ */ ++#define __gpio_as_aic() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x16c00000; \ ++ REG_GPIO_PXTRGC(4) = 0x02c00000; \ ++ REG_GPIO_PXTRGS(4) = 0x14000000; \ ++ REG_GPIO_PXSELC(4) = 0x14c00000; \ ++ REG_GPIO_PXSELS(4) = 0x02000000; \ ++ REG_GPIO_PXPES(4) = 0x16c00000; \ ++} while (0) ++ ++/* ++ * MSC0_CMD, MSC0_CLK, MSC0_D0 ~ MSC0_D3 ++ */ ++#define __gpio_as_msc0_4bit() \ ++do { \ ++ REG_GPIO_PXFUNS(1) = 0x00008000; \ ++ REG_GPIO_PXTRGS(1) = 0x00008000; \ ++ REG_GPIO_PXSELC(1) = 0x00008000; \ ++ REG_GPIO_PXPES(1) = 0x00008000; \ ++ REG_GPIO_PXFUNS(2) = 0x38030000; \ ++ REG_GPIO_PXTRGS(2) = 0x00010000; \ ++ REG_GPIO_PXTRGC(2) = 0x38020000; \ ++ REG_GPIO_PXSELC(2) = 0x08010000; \ ++ REG_GPIO_PXSELS(2) = 0x30020000; \ ++ REG_GPIO_PXPES(2) = 0x38030000; \ ++} while (0) ++ ++ ++/* ++ * MSC1_CMD, MSC1_CLK, MSC1_D0 ~ MSC1_D3 ++ */ ++#define __gpio_as_msc1_4bit() \ ++do { \ ++ REG_GPIO_PXFUNS(1) = 0xfc000000; \ ++ REG_GPIO_PXTRGC(1) = 0xfc000000; \ ++ REG_GPIO_PXSELS(1) = 0xfc000000; \ ++ REG_GPIO_PXPES(1) = 0xfc000000; \ ++} while (0) ++ ++#define __gpio_as_msc __gpio_as_msc0_4bit /* default as msc0 4bit */ ++#define __gpio_as_msc0 __gpio_as_msc0_4bit /* msc0 default as 4bit */ ++#define __gpio_as_msc1 __gpio_as_msc1_4bit /* msc1 only support 4bit */ ++ ++/* ++ * SSI_CE0, SSI_CE1, SSI_GPC, SSI_CLK, SSI_DT, SSI_DR ++ */ ++#define __gpio_as_ssi() \ ++do { \ ++ REG_GPIO_PXFUNS(1) = 0xfc000000; \ ++ REG_GPIO_PXTRGC(1) = 0xfc000000; \ ++ REG_GPIO_PXSELC(1) = 0xfc000000; \ ++ REG_GPIO_PXPES(1) = 0xfc000000; \ ++} while (0) ++ ++/* ++ * SSI_CE0, SSI_CE2, SSI_GPC, SSI_CLK, SSI_DT, SSI1_DR ++ */ ++#define __gpio_as_ssi_1() \ ++do { \ ++ REG_GPIO_PXFUNS(5) = 0x0000fc00; \ ++ REG_GPIO_PXTRGC(5) = 0x0000fc00; \ ++ REG_GPIO_PXSELC(5) = 0x0000fc00; \ ++ REG_GPIO_PXPES(5) = 0x0000fc00; \ ++} while (0) ++ ++/* ++ * I2C_SCK, I2C_SDA ++ */ ++#define __gpio_as_i2c() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x00003000; \ ++ REG_GPIO_PXSELC(4) = 0x00003000; \ ++ REG_GPIO_PXPES(4) = 0x00003000; \ ++} while (0) ++ ++/* ++ * PWM0 ++ */ ++#define __gpio_as_pwm0() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x00100000; \ ++ REG_GPIO_PXSELC(4) = 0x00100000; \ ++ REG_GPIO_PXPES(4) = 0x00100000; \ ++} while (0) ++ ++/* ++ * PWM1 ++ */ ++#define __gpio_as_pwm1() \ ++do { \ ++ REG_GPIO_PXFUNS(5) = 0x00000800; \ ++ REG_GPIO_PXSELC(5) = 0x00000800; \ ++ REG_GPIO_PXPES(5) = 0x00000800; \ ++} while (0) ++ ++/* ++ * PWM2 ++ */ ++#define __gpio_as_pwm2() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x00400000; \ ++ REG_GPIO_PXSELC(4) = 0x00400000; \ ++ REG_GPIO_PXPES(4) = 0x00400000; \ ++} while (0) ++ ++/* ++ * PWM3 ++ */ ++#define __gpio_as_pwm3() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x00800000; \ ++ REG_GPIO_PXSELC(4) = 0x00800000; \ ++ REG_GPIO_PXPES(4) = 0x00800000; \ ++} while (0) ++ ++/* ++ * PWM4 ++ */ ++#define __gpio_as_pwm4() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x01000000; \ ++ REG_GPIO_PXSELC(4) = 0x01000000; \ ++ REG_GPIO_PXPES(4) = 0x01000000; \ ++} while (0) ++ ++/* ++ * PWM5 ++ */ ++#define __gpio_as_pwm5() \ ++do { \ ++ REG_GPIO_PXFUNS(4) = 0x02000000; \ ++ REG_GPIO_PXSELC(4) = 0x02000000; \ ++ REG_GPIO_PXPES(4) = 0x02000000; \ ++} while (0) ++ ++/* ++ * n = 0 ~ 5 ++ */ ++#define __gpio_as_pwm(n) __gpio_as_pwm##n() ++ ++/* ++ * DREQ ++ */ ++#define __gpio_as_dreq() \ ++do { \ ++ REG_GPIO_PXFUNS(5) = 0x00000004; \ ++ REG_GPIO_PXSELS(5) = 0x00000004; \ ++ REG_GPIO_PXPES(5) = 0x00000004; \ ++} while (0) ++ ++/* ++ * DACK ++ */ ++#define __gpio_as_dack() \ ++do { \ ++ REG_GPIO_PXFUNS(5) = 0x00000008; \ ++ REG_GPIO_PXSELS(5) = 0x00000008; \ ++ REG_GPIO_PXPES(5) = 0x00000008; \ ++} while (0) ++ ++/* ++ * GPIO or Interrupt Mode ++ */ ++#define __gpio_get_port(p) (REG_GPIO_PXPIN(p)) ++ ++#define __gpio_port_as_output(p, o) \ ++do { \ ++ REG_GPIO_PXFUNC(p) = (1 << (o)); \ ++ REG_GPIO_PXSELC(p) = (1 << (o)); \ ++ REG_GPIO_PXDIRS(p) = (1 << (o)); \ ++} while (0) ++ ++#define __gpio_port_as_input(p, o) \ ++do { \ ++ REG_GPIO_PXFUNC(p) = (1 << (o)); \ ++ REG_GPIO_PXSELC(p) = (1 << (o)); \ ++ REG_GPIO_PXDIRC(p) = (1 << (o)); \ ++} while (0) ++ ++#define __gpio_as_output(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ __gpio_port_as_output(p, o); \ ++} while (0) ++ ++#define __gpio_as_input(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ __gpio_port_as_input(p, o); \ ++} while (0) ++ ++#define __gpio_set_pin(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXDATS(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_clear_pin(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXDATC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_get_pin(n) \ ++({ \ ++ unsigned int p, o, v; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ if (__gpio_get_port(p) & (1 << o)) \ ++ v = 1; \ ++ else \ ++ v = 0; \ ++ v; \ ++}) ++ ++#define __gpio_as_irq_high_level(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++ REG_GPIO_PXTRGC(p) = (1 << o); \ ++ REG_GPIO_PXFUNC(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++ REG_GPIO_PXDIRS(p) = (1 << o); \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_as_irq_low_level(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++ REG_GPIO_PXTRGC(p) = (1 << o); \ ++ REG_GPIO_PXFUNC(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++ REG_GPIO_PXDIRC(p) = (1 << o); \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_as_irq_rise_edge(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++ REG_GPIO_PXTRGS(p) = (1 << o); \ ++ REG_GPIO_PXFUNC(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++ REG_GPIO_PXDIRS(p) = (1 << o); \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_as_irq_fall_edge(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++ REG_GPIO_PXTRGS(p) = (1 << o); \ ++ REG_GPIO_PXFUNC(p) = (1 << o); \ ++ REG_GPIO_PXSELS(p) = (1 << o); \ ++ REG_GPIO_PXDIRC(p) = (1 << o); \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_mask_irq(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMS(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_unmask_irq(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXIMC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_ack_irq(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXFLGC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_get_irq() \ ++({ \ ++ unsigned int p, i, tmp, v = 0; \ ++ for (p = 3; p >= 0; p--) { \ ++ tmp = REG_GPIO_PXFLG(p); \ ++ for (i = 0; i < 32; i++) \ ++ if (tmp & (1 << i)) \ ++ v = (32*p + i); \ ++ } \ ++ v; \ ++}) ++ ++#define __gpio_group_irq(n) \ ++({ \ ++ register int tmp, i; \ ++ tmp = REG_GPIO_PXFLG((n)); \ ++ for (i=31;i>=0;i--) \ ++ if (tmp & (1 << i)) \ ++ break; \ ++ i; \ ++}) ++ ++#define __gpio_enable_pull(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXPEC(p) = (1 << o); \ ++} while (0) ++ ++#define __gpio_disable_pull(n) \ ++do { \ ++ unsigned int p, o; \ ++ p = (n) / 32; \ ++ o = (n) % 32; \ ++ REG_GPIO_PXPES(p) = (1 << o); \ ++} while (0) ++ ++ ++/*************************************************************************** ++ * CPM ++ ***************************************************************************/ ++#define __cpm_get_pllm() \ ++ ((REG_CPM_CPPCR & CPM_CPPCR_PLLM_MASK) >> CPM_CPPCR_PLLM_BIT) ++#define __cpm_get_plln() \ ++ ((REG_CPM_CPPCR & CPM_CPPCR_PLLN_MASK) >> CPM_CPPCR_PLLN_BIT) ++#define __cpm_get_pllod() \ ++ ((REG_CPM_CPPCR & CPM_CPPCR_PLLOD_MASK) >> CPM_CPPCR_PLLOD_BIT) ++ ++#define __cpm_get_cdiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_CDIV_MASK) >> CPM_CPCCR_CDIV_BIT) ++#define __cpm_get_hdiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_HDIV_MASK) >> CPM_CPCCR_HDIV_BIT) ++#define __cpm_get_pdiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_PDIV_MASK) >> CPM_CPCCR_PDIV_BIT) ++#define __cpm_get_mdiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_MDIV_MASK) >> CPM_CPCCR_MDIV_BIT) ++#define __cpm_get_ldiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_LDIV_MASK) >> CPM_CPCCR_LDIV_BIT) ++#define __cpm_get_udiv() \ ++ ((REG_CPM_CPCCR & CPM_CPCCR_UDIV_MASK) >> CPM_CPCCR_UDIV_BIT) ++#define __cpm_get_i2sdiv() \ ++ ((REG_CPM_I2SCDR & CPM_I2SCDR_I2SDIV_MASK) >> CPM_I2SCDR_I2SDIV_BIT) ++#define __cpm_get_pixdiv() \ ++ ((REG_CPM_LPCDR & CPM_LPCDR_PIXDIV_MASK) >> CPM_LPCDR_PIXDIV_BIT) ++#define __cpm_get_mscdiv(n) \ ++ ((REG_CPM_MSCCDR(n) & CPM_MSCCDR_MSCDIV_MASK) >> CPM_MSCCDR_MSCDIV_BIT) ++#define __cpm_get_uhcdiv() \ ++ ((REG_CPM_UHCCDR & CPM_UHCCDR_UHCDIV_MASK) >> CPM_UHCCDR_UHCDIV_BIT) ++#define __cpm_get_ssidiv() \ ++ ((REG_CPM_SSICCDR & CPM_SSICDR_SSICDIV_MASK) >> CPM_SSICDR_SSIDIV_BIT) ++#define __cpm_get_pcmdiv(v) \ ++ ((REG_CPM_PCMCDR & CPM_PCMCDR_PCMCD_MASK) >> CPM_PCMCDR_PCMCD_BIT) ++ ++#define __cpm_set_cdiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_CDIV_MASK) | ((v) << (CPM_CPCCR_CDIV_BIT))) ++#define __cpm_set_hdiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_HDIV_MASK) | ((v) << (CPM_CPCCR_HDIV_BIT))) ++#define __cpm_set_pdiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_PDIV_MASK) | ((v) << (CPM_CPCCR_PDIV_BIT))) ++#define __cpm_set_mdiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_MDIV_MASK) | ((v) << (CPM_CPCCR_MDIV_BIT))) ++#define __cpm_set_ldiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_LDIV_MASK) | ((v) << (CPM_CPCCR_LDIV_BIT))) ++#define __cpm_set_udiv(v) \ ++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_UDIV_MASK) | ((v) << (CPM_CPCCR_UDIV_BIT))) ++#define __cpm_set_i2sdiv(v) \ ++ (REG_CPM_I2SCDR = (REG_CPM_I2SCDR & ~CPM_I2SCDR_I2SDIV_MASK) | ((v) << (CPM_I2SCDR_I2SDIV_BIT))) ++#define __cpm_set_pixdiv(v) \ ++ (REG_CPM_LPCDR = (REG_CPM_LPCDR & ~CPM_LPCDR_PIXDIV_MASK) | ((v) << (CPM_LPCDR_PIXDIV_BIT))) ++#define __cpm_set_mscdiv(n, v) \ ++ (REG_CPM_MSCCDR(n) = (REG_CPM_MSCCDR(n) & ~CPM_MSCCDR_MSCDIV_MASK) | ((v) << (CPM_MSCCDR_MSCDIV_BIT))) ++#define __cpm_set_uhcdiv(v) \ ++ (REG_CPM_UHCCDR = (REG_CPM_UHCCDR & ~CPM_UHCCDR_UHCDIV_MASK) | ((v) << (CPM_UHCCDR_UHCDIV_BIT))) ++#define __cpm_set_ssidiv(v) \ ++ (REG_CPM_SSICDR = (REG_CPM_SSICDR & ~CPM_SSICDR_SSIDIV_MASK) | ((v) << (CPM_SSICDR_SSIDIV_BIT))) ++#define __cpm_set_pcmdiv(v) \ ++ (REG_CPM_PCMCDR = (REG_CPM_PCMCDR & ~CPM_PCMCDR_PCMCD_MASK) | ((v) << (CPM_PCMCDR_PCMCD_BIT))) ++ ++#define __cpm_select_pcmclk_pll() (REG_CPM_PCMCDR |= CPM_PCMCDR_PCMS) ++#define __cpm_select_pcmclk_exclk() (REG_CPM_PCMCDR &= ~CPM_PCMCDR_PCMS) ++#define __cpm_select_pixclk_ext() (REG_CPM_LPCDR |= CPM_LPCDR_LPCS) ++#define __cpm_select_pixclk_pll() (REG_CPM_LPCDR &= ~CPM_LPCDR_LPCS) ++#define __cpm_select_tveclk_exclk() (REG_CPM_LPCDR |= CPM_CPCCR_LSCS) ++#define __cpm_select_tveclk_pll() (REG_CPM_LPCDR &= ~CPM_LPCDR_LSCS) ++#define __cpm_select_pixclk_lcd() (REG_CPM_LPCDR &= ~CPM_LPCDR_LTCS) ++#define __cpm_select_pixclk_tve() (REG_CPM_LPCDR |= CPM_LPCDR_LTCS) ++#define __cpm_select_i2sclk_exclk() (REG_CPM_CPCCR &= ~CPM_CPCCR_I2CS) ++#define __cpm_select_i2sclk_pll() (REG_CPM_CPCCR |= CPM_CPCCR_I2CS) ++#define __cpm_select_usbclk_exclk() (REG_CPM_CPCCR &= ~CPM_CPCCR_UCS) ++#define __cpm_select_usbclk_pll() (REG_CPM_CPCCR |= CPM_CPCCR_UCS) ++ ++#define __cpm_enable_cko() ++#define __cpm_exclk_direct() (REG_CPM_CPCCR &= ~CPM_CPCCR_ECS) ++#define __cpm_exclk_div2() (REG_CPM_CPCCR |= CPM_CPCCR_ECS) ++#define __cpm_enable_pll_change() (REG_CPM_CPCCR |= CPM_CPCCR_CE) ++#define __cpm_pllout_direct() (REG_CPM_CPCCR |= CPM_CPCCR_PCS) ++#define __cpm_pllout_div2() (REG_CPM_CPCCR &= ~CPM_CPCCR_PCS) ++#define __cpm_pll_enable() (REG_CPM_CPPCR |= CPM_CPPCR_PLLEN) ++ ++#define __cpm_pll_is_off() (REG_CPM_CPPSR & CPM_CPPSR_PLLOFF) ++#define __cpm_pll_is_on() (REG_CPM_CPPSR & CPM_CPPSR_PLLON) ++#define __cpm_pll_bypass() (REG_CPM_CPPSR |= CPM_CPPSR_PLLBP) ++ ++#define __cpm_get_cclk_doze_duty() \ ++ ((REG_CPM_LCR & CPM_LCR_DOZE_DUTY_MASK) >> CPM_LCR_DOZE_DUTY_BIT) ++#define __cpm_set_cclk_doze_duty(v) \ ++ (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_DOZE_DUTY_MASK) | ((v) << (CPM_LCR_DOZE_DUTY_BIT))) ++ ++#define __cpm_doze_mode() (REG_CPM_LCR |= CPM_LCR_DOZE_ON) ++#define __cpm_idle_mode() \ ++ (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_LPM_MASK) | CPM_LCR_LPM_IDLE) ++#define __cpm_sleep_mode() \ ++ (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_LPM_MASK) | CPM_LCR_LPM_SLEEP) ++ ++#define __cpm_stop_all() (REG_CPM_CLKGR = 0x1fffffff) ++#define __cpm_stop_cimram() (REG_CPM_CLKGR |= CPM_CLKGR_CIMRAM) ++#define __cpm_stop_idct() (REG_CPM_CLKGR |= CPM_CLKGR_IDCT) ++#define __cpm_stop_db() (REG_CPM_CLKGR |= CPM_CLKGR_DB) ++#define __cpm_stop_me() (REG_CPM_CLKGR |= CPM_CLKGR_ME) ++#define __cpm_stop_mc() (REG_CPM_CLKGR |= CPM_CLKGR_MC) ++#define __cpm_stop_tve() (REG_CPM_CLKGR |= CPM_CLKGR_TVE) ++#define __cpm_stop_tssi() (REG_CPM_CLKGR |= CPM_CLKGR_TSSI) ++#define __cpm_stop_owi() (REG_CPM_CLKGR |= CPM_CLKGR_OWI) ++#define __cpm_stop_pcm() (REG_CPM_CLKGR |= CPM_CLKGR_PCM) ++#define __cpm_stop_uart3() (REG_CPM_CLKGR |= CPM_CLKGR_UART3) ++#define __cpm_stop_uart2() (REG_CPM_CLKGR |= CPM_CLKGR_UART2) ++#define __cpm_stop_uart1() (REG_CPM_CLKGR |= CPM_CLKGR_UART1) ++#define __cpm_stop_uhc() (REG_CPM_CLKGR |= CPM_CLKGR_UHC) ++#define __cpm_stop_ipu() (REG_CPM_CLKGR |= CPM_CLKGR_IPU) ++#define __cpm_stop_dmac() (REG_CPM_CLKGR |= CPM_CLKGR_DMAC) ++#define __cpm_stop_udc() (REG_CPM_CLKGR |= CPM_CLKGR_UDC) ++#define __cpm_stop_lcd() (REG_CPM_CLKGR |= CPM_CLKGR_LCD) ++#define __cpm_stop_cim() (REG_CPM_CLKGR |= CPM_CLKGR_CIM) ++#define __cpm_stop_sadc() (REG_CPM_CLKGR |= CPM_CLKGR_SADC) ++#define __cpm_stop_msc(n) (REG_CPM_CLKGR |= CPM_CLKGR_MSC##n) ++#define __cpm_stop_aic1() (REG_CPM_CLKGR |= CPM_CLKGR_AIC1) ++#define __cpm_stop_aic2() (REG_CPM_CLKGR |= CPM_CLKGR_AIC2) ++#define __cpm_stop_ssi(n) (REG_CPM_CLKGR |= CPM_CLKGR_SSI##n) ++#define __cpm_stop_i2c() (REG_CPM_CLKGR |= CPM_CLKGR_I2C) ++#define __cpm_stop_rtc() (REG_CPM_CLKGR |= CPM_CLKGR_RTC) ++#define __cpm_stop_tcu() (REG_CPM_CLKGR |= CPM_CLKGR_TCU) ++#define __cpm_stop_uart0() (REG_CPM_CLKGR |= CPM_CLKGR_UART0) ++ ++#define __cpm_start_all() (REG_CPM_CLKGR = 0x0) ++#define __cpm_start_cimram() (REG_CPM_CLKGR &= ~CPM_CLKGR_CIMRAM) ++#define __cpm_start_idct() (REG_CPM_CLKGR &= ~CPM_CLKGR_IDCT) ++#define __cpm_start_db() (REG_CPM_CLKGR &= ~CPM_CLKGR_DB) ++#define __cpm_start_me() (REG_CPM_CLKGR &= ~CPM_CLKGR_ME) ++#define __cpm_start_mc() (REG_CPM_CLKGR &= ~CPM_CLKGR_MC) ++#define __cpm_start_tve() (REG_CPM_CLKGR &= ~CPM_CLKGR_TVE) ++#define __cpm_start_tssi() (REG_CPM_CLKGR &= ~CPM_CLKGR_TSSI) ++#define __cpm_start_owi() (REG_CPM_CLKGR &= ~CPM_CLKGR_OWI) ++#define __cpm_start_pcm() (REG_CPM_CLKGR &= ~CPM_CLKGR_PCM) ++#define __cpm_start_uart3() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART3) ++#define __cpm_start_uart2() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART2) ++#define __cpm_start_uart1() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART1) ++#define __cpm_start_uhc() (REG_CPM_CLKGR &= ~CPM_CLKGR_UHC) ++#define __cpm_start_ipu() (REG_CPM_CLKGR &= ~CPM_CLKGR_IPU) ++#define __cpm_start_dmac() (REG_CPM_CLKGR &= ~CPM_CLKGR_DMAC) ++#define __cpm_start_udc() (REG_CPM_CLKGR &= ~CPM_CLKGR_UDC) ++#define __cpm_start_lcd() (REG_CPM_CLKGR &= ~CPM_CLKGR_LCD) ++#define __cpm_start_cim() (REG_CPM_CLKGR &= ~CPM_CLKGR_CIM) ++#define __cpm_start_sadc() (REG_CPM_CLKGR &= ~CPM_CLKGR_SADC) ++#define __cpm_start_msc(n) (REG_CPM_CLKGR &= ~CPM_CLKGR_MSC##n) ++#define __cpm_start_aic1() (REG_CPM_CLKGR &= ~CPM_CLKGR_AIC1) ++#define __cpm_start_aic2() (REG_CPM_CLKGR &= ~CPM_CLKGR_AIC2) ++#define __cpm_start_ssi(n) (REG_CPM_CLKGR &= ~CPM_CLKGR_SSI##n) ++#define __cpm_start_i2c() (REG_CPM_CLKGR &= ~CPM_CLKGR_I2C) ++#define __cpm_start_rtc() (REG_CPM_CLKGR &= ~CPM_CLKGR_RTC) ++#define __cpm_start_tcu() (REG_CPM_CLKGR &= ~CPM_CLKGR_TCU) ++#define __cpm_start_uart0() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART0) ++ ++#define __cpm_get_o1st() \ ++ ((REG_CPM_OPCR & CPM_OPCR_O1ST_MASK) >> CPM_OPCR_O1ST_BIT) ++#define __cpm_set_o1st(v) \ ++ (REG_CPM_OPCR = (REG_CPM_OPCR & ~CPM_OPCR_O1ST_MASK) | ((v) << (CPM_OPCR_O1ST_BIT))) ++#define __cpm_enable_uhcphy() (REG_CPM_OPCR &= ~CPM_OPCR_UHCPHY_DISABLE) ++#define __cpm_suspend_uhcphy() (REG_CPM_OPCR |= CPM_OPCR_UHCPHY_DISABLE) ++#define __cpm_enable_udcphy() (REG_CPM_OPCR |= CPM_OPCR_UDCPHY_ENABLE) ++#define __cpm_suspend_udcphy() (REG_CPM_OPCR &= ~CPM_OPCR_UDCPHY_ENABLE) ++#define __cpm_enable_osc_in_sleep() (REG_CPM_OPCR |= CPM_OPCR_OSC_ENABLE) ++#define __cpm_disable_osc_in_sleep() (REG_CPM_OPCR &= ~CPM_OPCR_OSC_ENABLE) ++#define __cpm_select_rtcclk_rtc() (REG_CPM_OPCR |= CPM_OPCR_ERCS) ++#define __cpm_select_rtcclk_exclk() (REG_CPM_OPCR &= ~CPM_OPCR_ERCS) ++ ++ ++/*************************************************************************** ++ * TCU ++ ***************************************************************************/ ++// where 'n' is the TCU channel ++#define __tcu_select_extalclk(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_EXT_EN) ++#define __tcu_select_rtcclk(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_RTC_EN) ++#define __tcu_select_pclk(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_PCK_EN) ++#define __tcu_disable_pclk(n) \ ++ REG_TCU_TCSR(n) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PCK_EN); ++#define __tcu_select_clk_div1(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE1) ++#define __tcu_select_clk_div4(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE4) ++#define __tcu_select_clk_div16(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE16) ++#define __tcu_select_clk_div64(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE64) ++#define __tcu_select_clk_div256(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE256) ++#define __tcu_select_clk_div1024(n) \ ++ (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE1024) ++ ++#define __tcu_enable_pwm_output(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_EN) ++#define __tcu_disable_pwm_output(n) (REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_EN) ++ ++#define __tcu_init_pwm_output_high(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_INITL_HIGH) ++#define __tcu_init_pwm_output_low(n) (REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_INITL_HIGH) ++ ++#define __tcu_set_pwm_output_shutdown_graceful(n) (REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_SD) ++#define __tcu_set_pwm_output_shutdown_abrupt(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_SD) ++ ++#define __tcu_clear_counter_to_zero(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_CNT_CLRZ) ++ ++#define __tcu_ost_enabled() (REG_TCU_TER & TCU_TER_OSTEN) ++#define __tcu_enable_ost() (REG_TCU_TESR = TCU_TESR_OSTST) ++#define __tcu_disable_ost() (REG_TCU_TECR = TCU_TECR_OSTCL) ++ ++#define __tcu_counter_enabled(n) (REG_TCU_TER & (1 << (n))) ++#define __tcu_start_counter(n) (REG_TCU_TESR |= (1 << (n))) ++#define __tcu_stop_counter(n) (REG_TCU_TECR |= (1 << (n))) ++ ++#define __tcu_half_match_flag(n) (REG_TCU_TFR & (1 << ((n) + 16))) ++#define __tcu_full_match_flag(n) (REG_TCU_TFR & (1 << (n))) ++#define __tcu_set_half_match_flag(n) (REG_TCU_TFSR = (1 << ((n) + 16))) ++#define __tcu_set_full_match_flag(n) (REG_TCU_TFSR = (1 << (n))) ++#define __tcu_clear_half_match_flag(n) (REG_TCU_TFCR = (1 << ((n) + 16))) ++#define __tcu_clear_full_match_flag(n) (REG_TCU_TFCR = (1 << (n))) ++#define __tcu_mask_half_match_irq(n) (REG_TCU_TMSR = (1 << ((n) + 16))) ++#define __tcu_mask_full_match_irq(n) (REG_TCU_TMSR = (1 << (n))) ++#define __tcu_unmask_half_match_irq(n) (REG_TCU_TMCR = (1 << ((n) + 16))) ++#define __tcu_unmask_full_match_irq(n) (REG_TCU_TMCR = (1 << (n))) ++ ++#define __tcu_ost_match_flag() (REG_TCU_TFR & TCU_TFR_OSTFLAG) ++#define __tcu_set_ost_match_flag() (REG_TCU_TFSR = TCU_TFSR_OSTFST) ++#define __tcu_clear_ost_match_flag() (REG_TCU_TFCR = TCU_TFCR_OSTFCL) ++#define __tcu_ost_match_irq_masked() (REG_TCU_TMR & TCU_TMR_OSTMASK) ++#define __tcu_mask_ost_match_irq() (REG_TCU_TMSR = TCU_TMSR_OSTMST) ++#define __tcu_unmask_ost_match_irq() (REG_TCU_TMCR = TCU_TMCR_OSTMCL) ++ ++#define __tcu_wdt_clock_stopped() (REG_TCU_TSR & TCU_TSSR_WDTSC) ++#define __tcu_ost_clock_stopped() (REG_TCU_TSR & TCU_TSR_OST) ++#define __tcu_timer_clock_stopped(n) (REG_TCU_TSR & (1 << (n))) ++ ++#define __tcu_start_wdt_clock() (REG_TCU_TSCR = TCU_TSSR_WDTSC) ++#define __tcu_start_ost_clock() (REG_TCU_TSCR = TCU_TSCR_OSTSC) ++#define __tcu_start_timer_clock(n) (REG_TCU_TSCR = (1 << (n))) ++ ++#define __tcu_stop_wdt_clock() (REG_TCU_TSSR = TCU_TSSR_WDTSC) ++#define __tcu_stop_ost_clock() (REG_TCU_TSSR = TCU_TSSR_OSTSS) ++#define __tcu_stop_timer_clock(n) (REG_TCU_TSSR = (1 << (n))) ++ ++#define __tcu_get_count(n) (REG_TCU_TCNT((n))) ++#define __tcu_set_count(n,v) (REG_TCU_TCNT((n)) = (v)) ++#define __tcu_set_full_data(n,v) (REG_TCU_TDFR((n)) = (v)) ++#define __tcu_set_half_data(n,v) (REG_TCU_TDHR((n)) = (v)) ++ ++/* TCU2, counter 1, 2*/ ++#define __tcu_read_real_value(n) (REG_TCU_TSTR & (1 << ((n) + 16))) ++#define __tcu_read_false_value(n) (REG_TCU_TSTR & (1 << ((n) + 16))) ++#define __tcu_counter_busy(n) (REG_TCU_TSTR & (1 << (n))) ++#define __tcu_counter_ready(n) (REG_TCU_TSTR & (1 << (n))) ++ ++#define __tcu_set_read_real_value(n) (REG_TCU_TSTSR = (1 << ((n) + 16))) ++#define __tcu_set_read_false_value(n) (REG_TCU_TSTCR = (1 << ((n) + 16))) ++#define __tcu_set_counter_busy(n) (REG_TCU_TSTSR = (1 << (n))) ++#define __tcu_set_counter_ready(n) (REG_TCU_TSTCR = (1 << (n))) ++ ++/* ost counter */ ++#define __ostcu_set_pwm_output_shutdown_graceful() (REG_TCU_OSTCSR &= ~TCU_TCSR_PWM_SD) ++#define __ostcu_set_ost_output_shutdown_abrupt() (REG_TCU_OSTCSR |= TCU_TCSR_PWM_SD) ++#define __ostcu_select_clk_div1() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE1) ++#define __ostcu_select_clk_div4() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE4) ++#define __ostcu_select_clk_div16() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE16) ++#define __ostcu_select_clk_div64() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE64) ++#define __ostcu_select_clk_div256() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE256) ++#define __ostcu_select_clk_div1024() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE1024) ++#define __ostcu_select_rtcclk() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~(TCU_OSTCSR_EXT_EN | TCU_OSTCSR_RTC_EN | TCU_OSTCSR_PCK_EN)) | TCU_OSTCSR_RTC_EN) ++#define __ostcu_select_extalclk() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~(TCU_OSTCSR_EXT_EN | TCU_OSTCSR_RTC_EN | TCU_OSTCSR_PCK_EN)) | TCU_OSTCSR_EXT_EN) ++#define __ostcu_select_pclk() \ ++ (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~(TCU_OSTCSR_EXT_EN | TCU_OSTCSR_RTC_EN | TCU_OSTCSR_PCK_EN)) | TCU_OSTCSR_PCK_EN) ++ ++ ++/*************************************************************************** ++ * WDT ++ ***************************************************************************/ ++#define __wdt_start() ( REG_WDT_TCER |= WDT_TCER_TCEN ) ++#define __wdt_stop() ( REG_WDT_TCER &= ~WDT_TCER_TCEN ) ++#define __wdt_set_count(v) ( REG_WDT_TCNT = (v) ) ++#define __wdt_set_data(v) ( REG_WDT_TDR = (v) ) ++ ++#define __wdt_select_extalclk() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_EXT_EN) ++#define __wdt_select_rtcclk() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_RTC_EN) ++#define __wdt_select_pclk() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_PCK_EN) ++ ++#define __wdt_select_clk_div1() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE1) ++#define __wdt_select_clk_div4() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE4) ++#define __wdt_select_clk_div16() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE16) ++#define __wdt_select_clk_div64() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE64) ++#define __wdt_select_clk_div256() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE256) ++#define __wdt_select_clk_div1024() \ ++ (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE1024) ++ ++ ++/*************************************************************************** ++ * UART ++ ***************************************************************************/ ++ ++#define __uart_enable(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) |= UARTFCR_UUE | UARTFCR_FE ) ++#define __uart_disable(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) = ~UARTFCR_UUE ) ++ ++#define __uart_enable_transmit_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_TIE ) ++#define __uart_disable_transmit_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~UARTIER_TIE ) ++ ++#define __uart_enable_receive_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE ) ++#define __uart_disable_receive_irq(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~(UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE) ) ++ ++#define __uart_enable_loopback(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) |= UARTMCR_LOOP ) ++#define __uart_disable_loopback(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) &= ~UARTMCR_LOOP ) ++ ++#define __uart_set_8n1(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) = UARTLCR_WLEN_8 ) ++ ++#define __uart_set_baud(n, devclk, baud) \ ++ do { \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) |= UARTLCR_DLAB; \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_DLLR) = (devclk / 16 / baud) & 0xff; \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_DLHR) = ((devclk / 16 / baud) >> 8) & 0xff; \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) &= ~UARTLCR_DLAB; \ ++ } while (0) ++ ++#define __uart_parity_error(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_PER) != 0 ) ++ ++#define __uart_clear_errors(n) \ ++ ( REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) &= ~(UARTLSR_ORER | UARTLSR_BRK | UARTLSR_FER | UARTLSR_PER | UARTLSR_RFER) ) ++ ++#define __uart_transmit_fifo_empty(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TDRQ) != 0 ) ++ ++#define __uart_transmit_end(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TEMT) != 0 ) ++ ++#define __uart_transmit_char(n, ch) \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_TDR) = (ch) ++ ++#define __uart_receive_fifo_full(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) ++ ++#define __uart_receive_ready(n) \ ++ ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) ++ ++#define __uart_receive_char(n) \ ++ REG8(UART_BASE + UART_OFF*(n) + OFF_RDR) ++ ++#define __uart_disable_irda() \ ++ ( REG8(IRDA_BASE + OFF_SIRCR) &= ~(SIRCR_TSIRE | SIRCR_RSIRE) ) ++#define __uart_enable_irda() \ ++ /* Tx high pulse as 0, Rx low pulse as 0 */ \ ++ ( REG8(IRDA_BASE + OFF_SIRCR) = SIRCR_TSIRE | SIRCR_RSIRE | SIRCR_RXPL | SIRCR_TPWS ) ++ ++ ++/*************************************************************************** ++ * DMAC ++ ***************************************************************************/ ++ ++/* m is the DMA controller index (0, 1), n is the DMA channel index (0 - 11) */ ++ ++#define __dmac_enable_module(m) \ ++ ( REG_DMAC_DMACR(m) |= DMAC_DMACR_DMAE | DMAC_DMACR_PR_012345 ) ++#define __dmac_disable_module(m) \ ++ ( REG_DMAC_DMACR(m) &= ~DMAC_DMACR_DMAE ) ++ ++/* p=0,1,2,3 */ ++#define __dmac_set_priority(m,p) \ ++do { \ ++ REG_DMAC_DMACR(m) &= ~DMAC_DMACR_PR_MASK; \ ++ REG_DMAC_DMACR(m) |= ((p) << DMAC_DMACR_PR_BIT); \ ++} while (0) ++ ++#define __dmac_test_halt_error(m) ( REG_DMAC_DMACR(m) & DMAC_DMACR_HLT ) ++#define __dmac_test_addr_error(m) ( REG_DMAC_DMACR(m) & DMAC_DMACR_AR ) ++ ++#define __dmac_channel_enable_clk(n) \ ++ REG_DMAC_DMACKE((n)/HALF_DMA_NUM) |= 1 << ((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM); ++ ++#define __dmac_enable_descriptor(n) \ ++ ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_NDES ) ++#define __dmac_disable_descriptor(n) \ ++ ( REG_DMAC_DCCSR((n)) |= DMAC_DCCSR_NDES ) ++ ++#define __dmac_enable_channel(n) \ ++do { \ ++ REG_DMAC_DCCSR((n)) |= DMAC_DCCSR_EN; \ ++} while (0) ++#define __dmac_disable_channel(n) \ ++do { \ ++ REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_EN; \ ++} while (0) ++#define __dmac_channel_enabled(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_EN ) ++ ++#define __dmac_channel_enable_irq(n) \ ++ ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_TIE ) ++#define __dmac_channel_disable_irq(n) \ ++ ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_TIE ) ++ ++#define __dmac_channel_transmit_halt_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_HLT ) ++#define __dmac_channel_transmit_end_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_TT ) ++#define __dmac_channel_address_error_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_AR ) ++#define __dmac_channel_count_terminated_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_CT ) ++#define __dmac_channel_descriptor_invalid_detected(n) \ ++ ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_INV ) ++ ++#define __dmac_channel_clear_transmit_halt(n) \ ++ do { \ ++ /* clear both channel halt error and globle halt error */ \ ++ REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_HLT; \ ++ REG_DMAC_DMACR(n/HALF_DMA_NUM) &= ~DMAC_DMACR_HLT; \ ++ } while (0) ++#define __dmac_channel_clear_transmit_end(n) \ ++ ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_TT ) ++#define __dmac_channel_clear_address_error(n) \ ++ do { \ ++ REG_DMAC_DDA(n) = 0; /* clear descriptor address register */ \ ++ REG_DMAC_DSAR(n) = 0; /* clear source address register */ \ ++ REG_DMAC_DTAR(n) = 0; /* clear target address register */ \ ++ /* clear both channel addr error and globle address error */ \ ++ REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_AR; \ ++ REG_DMAC_DMACR(n/HALF_DMA_NUM) &= ~DMAC_DMACR_AR; \ ++ } while (0) ++#define __dmac_channel_clear_count_terminated(n) \ ++ ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_CT ) ++#define __dmac_channel_clear_descriptor_invalid(n) \ ++ ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_INV ) ++ ++#define __dmac_channel_set_transfer_unit_32bit(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_32BIT; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_16bit(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_16BIT; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_8bit(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_8BIT; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_16byte(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_16BYTE; \ ++} while (0) ++ ++#define __dmac_channel_set_transfer_unit_32byte(n) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_32BYTE; \ ++} while (0) ++ ++/* w=8,16,32 */ ++#define __dmac_channel_set_dest_port_width(n,w) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DWDH_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_DWDH_##w; \ ++} while (0) ++ ++/* w=8,16,32 */ ++#define __dmac_channel_set_src_port_width(n,w) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_SWDH_MASK; \ ++ REG_DMAC_DCMD((n)) |= DMAC_DCMD_SWDH_##w; \ ++} while (0) ++ ++/* v=0-15 */ ++#define __dmac_channel_set_rdil(n,v) \ ++do { \ ++ REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_RDIL_MASK; \ ++ REG_DMAC_DCMD((n) |= ((v) << DMAC_DCMD_RDIL_BIT); \ ++} while (0) ++ ++#define __dmac_channel_dest_addr_fixed(n) \ ++ ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DAI ) ++#define __dmac_channel_dest_addr_increment(n) \ ++ ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_DAI ) ++ ++#define __dmac_channel_src_addr_fixed(n) \ ++ ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_SAI ) ++#define __dmac_channel_src_addr_increment(n) \ ++ ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_SAI ) ++ ++#define __dmac_channel_set_doorbell(n) \ ++ ( REG_DMAC_DMADBSR((n)/HALF_DMA_NUM) = (1 << ((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM)) ) ++ ++#define __dmac_channel_irq_detected(n) ( REG_DMAC_DMAIPR((n)/HALF_DMA_NUM) & (1 << ((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM)) ) ++#define __dmac_channel_ack_irq(n) ( REG_DMAC_DMAIPR((n)/HALF_DMA_NUM) &= ~(1 <<((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM)) ) ++ ++static __inline__ int __dmac_get_irq(void) ++{ ++ int i; ++ for (i = 0; i < MAX_DMA_NUM; i++) ++ if (__dmac_channel_irq_detected(i)) ++ return i; ++ return -1; ++} ++ ++ ++/*************************************************************************** ++ * AIC (AC'97 & I2S Controller) ++ ***************************************************************************/ ++ ++#define __aic_enable() ( REG_AIC_FR |= AIC_FR_ENB ) ++#define __aic_disable() ( REG_AIC_FR &= ~AIC_FR_ENB ) ++ ++#define __aic_select_ac97() ( REG_AIC_FR &= ~AIC_FR_AUSEL ) ++#define __aic_select_i2s() ( REG_AIC_FR |= AIC_FR_AUSEL ) ++ ++#define __aic_play_zero() ( REG_AIC_FR &= ~AIC_FR_LSMP ) ++#define __aic_play_lastsample() ( REG_AIC_FR |= AIC_FR_LSMP ) ++ ++#define __i2s_as_master() ( REG_AIC_FR |= AIC_FR_BCKD | AIC_FR_SYNCD ) ++#define __i2s_as_slave() ( REG_AIC_FR &= ~(AIC_FR_BCKD | AIC_FR_SYNCD) ) ++#define __aic_reset_status() ( REG_AIC_FR & AIC_FR_RST ) ++ ++#define __aic_reset() \ ++do { \ ++ REG_AIC_FR |= AIC_FR_RST; \ ++} while(0) ++ ++ ++#define __aic_set_transmit_trigger(n) \ ++do { \ ++ REG_AIC_FR &= ~AIC_FR_TFTH_MASK; \ ++ REG_AIC_FR |= ((n) << AIC_FR_TFTH_BIT); \ ++} while(0) ++ ++#define __aic_set_receive_trigger(n) \ ++do { \ ++ REG_AIC_FR &= ~AIC_FR_RFTH_MASK; \ ++ REG_AIC_FR |= ((n) << AIC_FR_RFTH_BIT); \ ++} while(0) ++ ++#define __aic_enable_record() ( REG_AIC_CR |= AIC_CR_EREC ) ++#define __aic_disable_record() ( REG_AIC_CR &= ~AIC_CR_EREC ) ++#define __aic_enable_replay() ( REG_AIC_CR |= AIC_CR_ERPL ) ++#define __aic_disable_replay() ( REG_AIC_CR &= ~AIC_CR_ERPL ) ++#define __aic_enable_loopback() ( REG_AIC_CR |= AIC_CR_ENLBF ) ++#define __aic_disable_loopback() ( REG_AIC_CR &= ~AIC_CR_ENLBF ) ++ ++#define __aic_flush_fifo() ( REG_AIC_CR |= AIC_CR_FLUSH ) ++#define __aic_unflush_fifo() ( REG_AIC_CR &= ~AIC_CR_FLUSH ) ++ ++#define __aic_enable_transmit_intr() \ ++ ( REG_AIC_CR |= (AIC_CR_ETFS | AIC_CR_ETUR) ) ++#define __aic_disable_transmit_intr() \ ++ ( REG_AIC_CR &= ~(AIC_CR_ETFS | AIC_CR_ETUR) ) ++#define __aic_enable_receive_intr() \ ++ ( REG_AIC_CR |= (AIC_CR_ERFS | AIC_CR_EROR) ) ++#define __aic_disable_receive_intr() \ ++ ( REG_AIC_CR &= ~(AIC_CR_ERFS | AIC_CR_EROR) ) ++ ++#define __aic_enable_transmit_dma() ( REG_AIC_CR |= AIC_CR_TDMS ) ++#define __aic_disable_transmit_dma() ( REG_AIC_CR &= ~AIC_CR_TDMS ) ++#define __aic_enable_receive_dma() ( REG_AIC_CR |= AIC_CR_RDMS ) ++#define __aic_disable_receive_dma() ( REG_AIC_CR &= ~AIC_CR_RDMS ) ++ ++#define __aic_enable_mono2stereo() ( REG_AIC_CR |= AIC_CR_M2S ) ++#define __aic_disable_mono2stereo() ( REG_AIC_CR &= ~AIC_CR_M2S ) ++#define __aic_enable_byteswap() ( REG_AIC_CR |= AIC_CR_ENDSW ) ++#define __aic_disable_byteswap() ( REG_AIC_CR &= ~AIC_CR_ENDSW ) ++#define __aic_enable_unsignadj() ( REG_AIC_CR |= AIC_CR_AVSTSU ) ++#define __aic_disable_unsignadj() ( REG_AIC_CR &= ~AIC_CR_AVSTSU ) ++ ++#define AC97_PCM_XS_L_FRONT AIC_ACCR1_XS_SLOT3 ++#define AC97_PCM_XS_R_FRONT AIC_ACCR1_XS_SLOT4 ++#define AC97_PCM_XS_CENTER AIC_ACCR1_XS_SLOT6 ++#define AC97_PCM_XS_L_SURR AIC_ACCR1_XS_SLOT7 ++#define AC97_PCM_XS_R_SURR AIC_ACCR1_XS_SLOT8 ++#define AC97_PCM_XS_LFE AIC_ACCR1_XS_SLOT9 ++ ++#define AC97_PCM_RS_L_FRONT AIC_ACCR1_RS_SLOT3 ++#define AC97_PCM_RS_R_FRONT AIC_ACCR1_RS_SLOT4 ++#define AC97_PCM_RS_CENTER AIC_ACCR1_RS_SLOT6 ++#define AC97_PCM_RS_L_SURR AIC_ACCR1_RS_SLOT7 ++#define AC97_PCM_RS_R_SURR AIC_ACCR1_RS_SLOT8 ++#define AC97_PCM_RS_LFE AIC_ACCR1_RS_SLOT9 ++ ++#define __ac97_set_xs_none() ( REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK ) ++#define __ac97_set_xs_mono() \ ++do { \ ++ REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK; \ ++ REG_AIC_ACCR1 |= AC97_PCM_XS_R_FRONT; \ ++} while(0) ++#define __ac97_set_xs_stereo() \ ++do { \ ++ REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK; \ ++ REG_AIC_ACCR1 |= AC97_PCM_XS_L_FRONT | AC97_PCM_XS_R_FRONT; \ ++} while(0) ++ ++/* In fact, only stereo is support now. */ ++#define __ac97_set_rs_none() ( REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK ) ++#define __ac97_set_rs_mono() \ ++do { \ ++ REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK; \ ++ REG_AIC_ACCR1 |= AC97_PCM_RS_R_FRONT; \ ++} while(0) ++#define __ac97_set_rs_stereo() \ ++do { \ ++ REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK; \ ++ REG_AIC_ACCR1 |= AC97_PCM_RS_L_FRONT | AC97_PCM_RS_R_FRONT; \ ++} while(0) ++ ++#define __ac97_warm_reset_codec() \ ++ do { \ ++ REG_AIC_ACCR2 |= AIC_ACCR2_SA; \ ++ REG_AIC_ACCR2 |= AIC_ACCR2_SS; \ ++ udelay(2); \ ++ REG_AIC_ACCR2 &= ~AIC_ACCR2_SS; \ ++ REG_AIC_ACCR2 &= ~AIC_ACCR2_SA; \ ++ } while (0) ++ ++#define __ac97_cold_reset_codec() \ ++ do { \ ++ REG_AIC_ACCR2 |= AIC_ACCR2_SR; \ ++ udelay(2); \ ++ REG_AIC_ACCR2 &= ~AIC_ACCR2_SR; \ ++ } while (0) ++ ++/* n=8,16,18,20 */ ++#define __ac97_set_iass(n) \ ++ ( REG_AIC_ACCR2 = (REG_AIC_ACCR2 & ~AIC_ACCR2_IASS_MASK) | AIC_ACCR2_IASS_##n##BIT ) ++#define __ac97_set_oass(n) \ ++ ( REG_AIC_ACCR2 = (REG_AIC_ACCR2 & ~AIC_ACCR2_OASS_MASK) | AIC_ACCR2_OASS_##n##BIT ) ++ ++#define __i2s_select_i2s() ( REG_AIC_I2SCR &= ~AIC_I2SCR_AMSL ) ++#define __i2s_select_msbjustified() ( REG_AIC_I2SCR |= AIC_I2SCR_AMSL ) ++ ++/* n=8,16,18,20,24 */ ++/*#define __i2s_set_sample_size(n) \ ++ ( REG_AIC_I2SCR |= (REG_AIC_I2SCR & ~AIC_I2SCR_WL_MASK) | AIC_I2SCR_WL_##n##BIT )*/ ++ ++#define __i2s_set_oss_sample_size(n) \ ++ ( REG_AIC_CR = (REG_AIC_CR & ~AIC_CR_OSS_MASK) | AIC_CR_OSS_##n##BIT ) ++#define __i2s_set_iss_sample_size(n) \ ++ ( REG_AIC_CR = (REG_AIC_CR & ~AIC_CR_ISS_MASK) | AIC_CR_ISS_##n##BIT ) ++ ++#define __i2s_stop_bitclk() ( REG_AIC_I2SCR |= AIC_I2SCR_STPBK ) ++#define __i2s_start_bitclk() ( REG_AIC_I2SCR &= ~AIC_I2SCR_STPBK ) ++ ++#define __aic_transmit_request() ( REG_AIC_SR & AIC_SR_TFS ) ++#define __aic_receive_request() ( REG_AIC_SR & AIC_SR_RFS ) ++#define __aic_transmit_underrun() ( REG_AIC_SR & AIC_SR_TUR ) ++#define __aic_receive_overrun() ( REG_AIC_SR & AIC_SR_ROR ) ++ ++#define __aic_clear_errors() ( REG_AIC_SR &= ~(AIC_SR_TUR | AIC_SR_ROR) ) ++ ++#define __aic_get_transmit_resident() \ ++ ( (REG_AIC_SR & AIC_SR_TFL_MASK) >> AIC_SR_TFL_BIT ) ++#define __aic_get_receive_count() \ ++ ( (REG_AIC_SR & AIC_SR_RFL_MASK) >> AIC_SR_RFL_BIT ) ++ ++#define __ac97_command_transmitted() ( REG_AIC_ACSR & AIC_ACSR_CADT ) ++#define __ac97_status_received() ( REG_AIC_ACSR & AIC_ACSR_SADR ) ++#define __ac97_status_receive_timeout() ( REG_AIC_ACSR & AIC_ACSR_RSTO ) ++#define __ac97_codec_is_low_power_mode() ( REG_AIC_ACSR & AIC_ACSR_CLPM ) ++#define __ac97_codec_is_ready() ( REG_AIC_ACSR & AIC_ACSR_CRDY ) ++#define __ac97_slot_error_detected() ( REG_AIC_ACSR & AIC_ACSR_SLTERR ) ++#define __ac97_clear_slot_error() ( REG_AIC_ACSR &= ~AIC_ACSR_SLTERR ) ++ ++#define __i2s_is_busy() ( REG_AIC_I2SSR & AIC_I2SSR_BSY ) ++ ++#define CODEC_READ_CMD (1 << 19) ++#define CODEC_WRITE_CMD (0 << 19) ++#define CODEC_REG_INDEX_BIT 12 ++#define CODEC_REG_INDEX_MASK (0x7f << CODEC_REG_INDEX_BIT) /* 18:12 */ ++#define CODEC_REG_DATA_BIT 4 ++#define CODEC_REG_DATA_MASK (0x0ffff << 4) /* 19:4 */ ++ ++#define __ac97_out_rcmd_addr(reg) \ ++do { \ ++ REG_AIC_ACCAR = CODEC_READ_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ ++} while (0) ++ ++#define __ac97_out_wcmd_addr(reg) \ ++do { \ ++ REG_AIC_ACCAR = CODEC_WRITE_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ ++} while (0) ++ ++#define __ac97_out_data(value) \ ++do { \ ++ REG_AIC_ACCDR = ((value) << CODEC_REG_DATA_BIT); \ ++} while (0) ++ ++#define __ac97_in_data() \ ++ ( (REG_AIC_ACSDR & CODEC_REG_DATA_MASK) >> CODEC_REG_DATA_BIT ) ++ ++#define __ac97_in_status_addr() \ ++ ( (REG_AIC_ACSAR & CODEC_REG_INDEX_MASK) >> CODEC_REG_INDEX_BIT ) ++ ++#define __i2s_set_sample_rate(i2sclk, sync) \ ++ ( REG_AIC_I2SDIV = ((i2sclk) / (4*64)) / (sync) ) ++ ++#define __aic_write_tfifo(v) ( REG_AIC_DR = (v) ) ++#define __aic_read_rfifo() ( REG_AIC_DR ) ++ ++#define __aic_internal_codec() ( REG_AIC_FR |= AIC_FR_ICDC ) ++#define __aic_external_codec() ( REG_AIC_FR &= ~AIC_FR_ICDC ) ++ ++// ++// Define next ops for AC97 compatible ++// ++ ++#define AC97_ACSR AIC_ACSR ++ ++#define __ac97_enable() __aic_enable(); __aic_select_ac97() ++#define __ac97_disable() __aic_disable() ++#define __ac97_reset() __aic_reset() ++ ++#define __ac97_set_transmit_trigger(n) __aic_set_transmit_trigger(n) ++#define __ac97_set_receive_trigger(n) __aic_set_receive_trigger(n) ++ ++#define __ac97_enable_record() __aic_enable_record() ++#define __ac97_disable_record() __aic_disable_record() ++#define __ac97_enable_replay() __aic_enable_replay() ++#define __ac97_disable_replay() __aic_disable_replay() ++#define __ac97_enable_loopback() __aic_enable_loopback() ++#define __ac97_disable_loopback() __aic_disable_loopback() ++ ++#define __ac97_enable_transmit_dma() __aic_enable_transmit_dma() ++#define __ac97_disable_transmit_dma() __aic_disable_transmit_dma() ++#define __ac97_enable_receive_dma() __aic_enable_receive_dma() ++#define __ac97_disable_receive_dma() __aic_disable_receive_dma() ++ ++#define __ac97_transmit_request() __aic_transmit_request() ++#define __ac97_receive_request() __aic_receive_request() ++#define __ac97_transmit_underrun() __aic_transmit_underrun() ++#define __ac97_receive_overrun() __aic_receive_overrun() ++ ++#define __ac97_clear_errors() __aic_clear_errors() ++ ++#define __ac97_get_transmit_resident() __aic_get_transmit_resident() ++#define __ac97_get_receive_count() __aic_get_receive_count() ++ ++#define __ac97_enable_transmit_intr() __aic_enable_transmit_intr() ++#define __ac97_disable_transmit_intr() __aic_disable_transmit_intr() ++#define __ac97_enable_receive_intr() __aic_enable_receive_intr() ++#define __ac97_disable_receive_intr() __aic_disable_receive_intr() ++ ++#define __ac97_write_tfifo(v) __aic_write_tfifo(v) ++#define __ac97_read_rfifo() __aic_read_rfifo() ++ ++// ++// Define next ops for I2S compatible ++// ++ ++#define I2S_ACSR AIC_I2SSR ++ ++#define __i2s_enable() __aic_enable(); __aic_select_i2s() ++#define __i2s_disable() __aic_disable() ++#define __i2s_reset() __aic_reset() ++ ++#define __i2s_set_transmit_trigger(n) __aic_set_transmit_trigger(n) ++#define __i2s_set_receive_trigger(n) __aic_set_receive_trigger(n) ++ ++#define __i2s_enable_record() __aic_enable_record() ++#define __i2s_disable_record() __aic_disable_record() ++#define __i2s_enable_replay() __aic_enable_replay() ++#define __i2s_disable_replay() __aic_disable_replay() ++#define __i2s_enable_loopback() __aic_enable_loopback() ++#define __i2s_disable_loopback() __aic_disable_loopback() ++ ++#define __i2s_enable_transmit_dma() __aic_enable_transmit_dma() ++#define __i2s_disable_transmit_dma() __aic_disable_transmit_dma() ++#define __i2s_enable_receive_dma() __aic_enable_receive_dma() ++#define __i2s_disable_receive_dma() __aic_disable_receive_dma() ++ ++#define __i2s_transmit_request() __aic_transmit_request() ++#define __i2s_receive_request() __aic_receive_request() ++#define __i2s_transmit_underrun() __aic_transmit_underrun() ++#define __i2s_receive_overrun() __aic_receive_overrun() ++ ++#define __i2s_clear_errors() __aic_clear_errors() ++ ++#define __i2s_get_transmit_resident() __aic_get_transmit_resident() ++#define __i2s_get_receive_count() __aic_get_receive_count() ++ ++#define __i2s_enable_transmit_intr() __aic_enable_transmit_intr() ++#define __i2s_disable_transmit_intr() __aic_disable_transmit_intr() ++#define __i2s_enable_receive_intr() __aic_enable_receive_intr() ++#define __i2s_disable_receive_intr() __aic_disable_receive_intr() ++ ++#define __i2s_write_tfifo(v) __aic_write_tfifo(v) ++#define __i2s_read_rfifo() __aic_read_rfifo() ++ ++#define __i2s_reset_codec() \ ++ do { \ ++ } while (0) ++ ++/************************************************************************* ++ * PCM Controller operation ++ *************************************************************************/ ++ ++#define __pcm_enable() ( REG_PCM_CTL |= PCM_CTL_PCMEN ) ++#define __pcm_disable() ( REG_PCM_CTL &= ~PCM_CTL_PCMEN ) ++ ++#define __pcm_clk_enable() ( REG_PCM_CTL |= PCM_CTL_CLKEN ) ++#define __pcm_clk_disable() ( REG_PCM_CTL &= ~PCM_CTL_CLKEN ) ++ ++#define __pcm_reset() ( REG_PCM_CTL |= PCM_CTL_RST ) ++#define __pcm_flush_fifo() ( REG_PCM_CTL |= PCM_CTL_FLUSH ) ++ ++#define __pcm_enable_record() ( REG_PCM_CTL |= PCM_CTL_EREC ) ++#define __pcm_disable_record() ( REG_PCM_CTL &= ~PCM_CTL_EREC ) ++#define __pcm_enable_playback() ( REG_PCM_CTL |= PCM_CTL_ERPL ) ++#define __pcm_disable_playback() ( REG_PCM_CTL &= ~PCM_CTL_ERPL ) ++ ++#define __pcm_enable_rxfifo() __pcm_enable_record() ++#define __pcm_disable_rxfifo() __pcm_disable_record() ++#define __pcm_enable_txfifo() __pcm_enable_playback() ++#define __pcm_disable_txfifo() __pcm_disable_playback() ++ ++#define __pcm_last_sample() ( REG_PCM_CTL |= PCM_CTL_LSMP ) ++#define __pcm_zero_sample() ( REG_PCM_CTL &= ~PCM_CTL_LSMP ) ++ ++#define __pcm_enable_transmit_dma() ( REG_PCM_CTL |= PCM_CTL_ETDMA ) ++#define __pcm_disable_transmit_dma() ( REG_PCM_CTL &= ~PCM_CTL_ETDMA ) ++#define __pcm_enable_receive_dma() ( REG_PCM_CTL |= PCM_CTL_ERDMA ) ++#define __pcm_disable_receive_dma() ( REG_PCM_CTL &= ~PCM_CTL_ERDMA ) ++ ++#define __pcm_as_master() ( REG_PCM_CFG &= PCM_CFG_MODE ) ++#define __pcm_as_slave() ( REG_PCM_CFG |= ~PCM_CFG_MODE ) ++ ++#define __pcm_set_transmit_trigger(n) \ ++do { \ ++ REG_PCM_CFG &= ~PCM_CFG_TFTH_MASK; \ ++ REG_PCM_CFG |= ((n) << PCM_CFG_TFTH_BIT); \ ++} while(0) ++ ++#define __pcm_set_receive_trigger(n) \ ++do { \ ++ REG_PCM_CFG &= ~PCM_CFG_RFTH_MASK; \ ++ REG_PCM_CFG |= ((n) << PCM_CFG_RFTH_BIT); \ ++} while(0) ++ ++#define __pcm_omsb_same_sync() ( REG_PCM_CFG &= ~PCM_CFG_OMSBPOS ) ++#define __pcm_omsb_next_sync() ( REG_PCM_CFG |= PCM_CFG_OMSBPOS ) ++ ++#define __pcm_imsb_same_sync() ( REG_PCM_CFG &= ~PCM_CFG_IMSBPOS ) ++#define __pcm_imsb_next_sync() ( REG_PCM_CFG |= PCM_CFG_IMSBPOS ) ++ ++/* set input sample size 8 or 16*/ ++#define __pcm_set_iss(n) \ ++( REG_PCM_CFG = (REG_PCM_CFG & ~PCM_CFG_ISS_MASK) | PCM_CFG_ISS_##n ) ++/* set output sample size 8 or 16*/ ++#define __pcm_set_oss(n) \ ++( REG_PCM_CFG = (REG_PCM_CFG & ~PCM_CFG_OSS_MASK) | PCM_CFG_OSS_##n ) ++ ++#define __pcm_set_valid_slot(n) \ ++( REG_PCM_CFG = (REG_PCM_CFG & ~PCM_CFG_SLOT_MASK) | PCM_CFG_SLOT_##n ) ++ ++#define __pcm_write_data(v) ( REG_PCM_DP = (v) ) ++#define __pcm_read_data() ( REG_PCM_DP ) ++ ++#define __pcm_enable_tfs_intr() ( REG_PCM_INTC |= PCM_INTC_ETFS ) ++#define __pcm_disable_tfs_intr() ( REG_PCM_INTC &= ~PCM_INTC_ETFS ) ++ ++#define __pcm_enable_tur_intr() ( REG_PCM_INTC |= PCM_INTC_ETUR ) ++#define __pcm_disable_tur_intr() ( REG_PCM_INTC &= ~PCM_INTC_ETUR ) ++ ++#define __pcm_enable_rfs_intr() ( REG_PCM_INTC |= PCM_INTC_ERFS ) ++#define __pcm_disable_rfs_intr() ( REG_PCM_INTC &= ~PCM_INTC_ERFS ) ++ ++#define __pcm_enable_ror_intr() ( REG_PCM_INTC |= PCM_INTC_EROR ) ++#define __pcm_disable_ror_intr() ( REG_PCM_INTC &= ~PCM_INTC_EROR ) ++ ++#define __pcm_ints_valid_tx() \ ++( ((REG_PCM_INTS & PCM_INTS_TFL_MASK) >> PCM_INTS_TFL_BIT) ) ++#define __pcm_ints_valid_rx() \ ++( ((REG_PCM_INTS & PCM_INTS_RFL_MASK) >> PCM_INTS_RFL_BIT) ) ++ ++#define __pcm_set_clk_div(n) \ ++( REG_PCM_DIV = (REG_PCM_DIV & ~PCM_DIV_CLKDIV_MASK) | ((n) << PCM_DIV_CLKDIV_BIT) ) ++ ++/* sysclk(cpm_pcm_sysclk) Hz is created by cpm logic, and pcmclk Hz is the pcm in/out clock wanted */ ++#define __pcm_set_clk_rate(sysclk, pcmclk) \ ++__pcm_set_clk_div(((sysclk) / (pcmclk) - 1)) ++ ++#define __pcm_set_sync_div(n) \ ++( REG_PCM_DIV = (REG_PCM_DIV & ~PCM_DIV_SYNDIV_MASK) | ((n) << PCM_DIV_SYNDIV_BIT) ) ++ ++/* pcmclk is source clock Hz, and sync is the frame sync clock Hz wanted */ ++#define __pcm_set_sync_rate(pcmclk, sync) \ ++__pcm_set_sync_div(((pcmclk) / (8 * (sync)) - 1)) ++ ++ /* set sync length in pcmclk n = 0 ... 63 */ ++#define __pcm_set_sync_len(n) \ ++( REG_PCM_DIV = (REG_PCM_DIV & ~PCM_DIV_SYNL_MASK) | (n << PCM_DIV_SYNL_BIT) ) ++ ++ ++/*************************************************************************** ++ * ICDC ++ ***************************************************************************/ ++#define __i2s_internal_codec() __aic_internal_codec() ++#define __i2s_external_codec() __aic_external_codec() ++ ++#define __icdc_clk_ready() ( REG_ICDC_CKCFG & ICDC_CKCFG_CKRDY ) ++#define __icdc_sel_adc() ( REG_ICDC_CKCFG |= ICDC_CKCFG_SELAD ) ++#define __icdc_sel_dac() ( REG_ICDC_CKCFG &= ~ICDC_CKCFG_SELAD ) ++ ++#define __icdc_set_rgwr() ( REG_ICDC_RGADW |= ICDC_RGADW_RGWR ) ++#define __icdc_clear_rgwr() ( REG_ICDC_RGADW &= ~ICDC_RGADW_RGWR ) ++#define __icdc_rgwr_ready() ( REG_ICDC_RGADW & ICDC_RGADW_RGWR ) ++ ++#define __icdc_set_addr(n) \ ++do { \ ++ REG_ICDC_RGADW &= ~ICDC_RGADW_RGADDR_MASK; \ ++ REG_ICDC_RGADW |= (n) << ICDC_RGADW_RGADDR_BIT; \ ++} while(0) ++ ++#define __icdc_set_cmd(n) \ ++do { \ ++ REG_ICDC_RGADW &= ~ICDC_RGADW_RGDIN_MASK; \ ++ REG_ICDC_RGADW |= (n) << ICDC_RGADW_RGDIN_BIT; \ ++} while(0) ++ ++#define __icdc_irq_pending() ( REG_ICDC_RGDATA & ICDC_RGDATA_IRQ ) ++#define __icdc_get_value() ( REG_ICDC_RGDATA & ICDC_RGDATA_RGDOUT_MASK ) ++ ++/*************************************************************************** ++ * INTC ++ ***************************************************************************/ ++#define __intc_unmask_irq(n) ( REG_INTC_IMCR = (1 << (n)) ) ++#define __intc_mask_irq(n) ( REG_INTC_IMSR = (1 << (n)) ) ++#define __intc_ack_irq(n) ( REG_INTC_IPR = (1 << (n)) ) /* A dummy ack, as the Pending Register is Read Only. Should we remove __intc_ack_irq() */ ++ ++ ++/*************************************************************************** ++ * I2C ++ ***************************************************************************/ ++ ++#define __i2c_enable() ( REG_I2C_CR |= I2C_CR_I2CE ) ++#define __i2c_disable() ( REG_I2C_CR &= ~I2C_CR_I2CE ) ++ ++#define __i2c_send_start() ( REG_I2C_CR |= I2C_CR_STA ) ++#define __i2c_send_stop() ( REG_I2C_CR |= I2C_CR_STO ) ++#define __i2c_send_ack() ( REG_I2C_CR &= ~I2C_CR_AC ) ++#define __i2c_send_nack() ( REG_I2C_CR |= I2C_CR_AC ) ++ ++#define __i2c_set_drf() ( REG_I2C_SR |= I2C_SR_DRF ) ++#define __i2c_clear_drf() ( REG_I2C_SR &= ~I2C_SR_DRF ) ++#define __i2c_check_drf() ( REG_I2C_SR & I2C_SR_DRF ) ++ ++#define __i2c_received_ack() ( !(REG_I2C_SR & I2C_SR_ACKF) ) ++#define __i2c_is_busy() ( REG_I2C_SR & I2C_SR_BUSY ) ++#define __i2c_transmit_ended() ( REG_I2C_SR & I2C_SR_TEND ) ++ ++#define __i2c_set_clk(dev_clk, i2c_clk) \ ++ ( REG_I2C_GR = (dev_clk) / (16*(i2c_clk)) - 1 ) ++ ++#define __i2c_read() ( REG_I2C_DR ) ++#define __i2c_write(val) ( REG_I2C_DR = (val) ) ++ ++ ++/*************************************************************************** ++ * MSC ++ ***************************************************************************/ ++/* n = 0, 1 (MSC0, MSC1) */ ++ ++#define __msc_start_op(n) \ ++ ( REG_MSC_STRPCL(n) = MSC_STRPCL_START_OP | MSC_STRPCL_CLOCK_CONTROL_START ) ++ ++#define __msc_set_resto(n, to) ( REG_MSC_RESTO(n) = to ) ++#define __msc_set_rdto(n, to) ( REG_MSC_RDTO(n) = to ) ++#define __msc_set_cmd(n, cmd) ( REG_MSC_CMD(n) = cmd ) ++#define __msc_set_arg(n, arg) ( REG_MSC_ARG(n) = arg ) ++#define __msc_set_nob(n, nob) ( REG_MSC_NOB(n) = nob ) ++#define __msc_get_nob(n) ( REG_MSC_NOB(n) ) ++#define __msc_set_blklen(n, len) ( REG_MSC_BLKLEN(n) = len ) ++#define __msc_set_cmdat(n, cmdat) ( REG_MSC_CMDAT(n) = cmdat ) ++#define __msc_set_cmdat_ioabort(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_IO_ABORT ) ++#define __msc_clear_cmdat_ioabort(n) ( REG_MSC_CMDAT(n) &= ~MSC_CMDAT_IO_ABORT ) ++ ++#define __msc_set_cmdat_bus_width1(n) \ ++do { \ ++ REG_MSC_CMDAT(n) &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ ++ REG_MSC_CMDAT(n) |= MSC_CMDAT_BUS_WIDTH_1BIT; \ ++} while(0) ++ ++#define __msc_set_cmdat_bus_width4(n) \ ++do { \ ++ REG_MSC_CMDAT(n) &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ ++ REG_MSC_CMDAT(n) |= MSC_CMDAT_BUS_WIDTH_4BIT; \ ++} while(0) ++ ++#define __msc_set_cmdat_dma_en(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_DMA_EN ) ++#define __msc_set_cmdat_init(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_INIT ) ++#define __msc_set_cmdat_busy(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_BUSY ) ++#define __msc_set_cmdat_stream(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_STREAM_BLOCK ) ++#define __msc_set_cmdat_block(n) ( REG_MSC_CMDAT(n) &= ~MSC_CMDAT_STREAM_BLOCK ) ++#define __msc_set_cmdat_read(n) ( REG_MSC_CMDAT(n) &= ~MSC_CMDAT_WRITE_READ ) ++#define __msc_set_cmdat_write(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_WRITE_READ ) ++#define __msc_set_cmdat_data_en(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_DATA_EN ) ++ ++/* r is MSC_CMDAT_RESPONSE_FORMAT_Rx or MSC_CMDAT_RESPONSE_FORMAT_NONE */ ++#define __msc_set_cmdat_res_format(n, r) \ ++do { \ ++ REG_MSC_CMDAT(n) &= ~MSC_CMDAT_RESPONSE_FORMAT_MASK; \ ++ REG_MSC_CMDAT(n) |= (r); \ ++} while(0) ++ ++#define __msc_clear_cmdat(n) \ ++ REG_MSC_CMDAT(n) &= ~( MSC_CMDAT_IO_ABORT | MSC_CMDAT_DMA_EN | MSC_CMDAT_INIT| \ ++ MSC_CMDAT_BUSY | MSC_CMDAT_STREAM_BLOCK | MSC_CMDAT_WRITE_READ | \ ++ MSC_CMDAT_DATA_EN | MSC_CMDAT_RESPONSE_FORMAT_MASK ) ++ ++#define __msc_get_imask(n) ( REG_MSC_IMASK(n) ) ++#define __msc_mask_all_intrs(n) ( REG_MSC_IMASK(n) = 0xff ) ++#define __msc_unmask_all_intrs(n) ( REG_MSC_IMASK(n) = 0x00 ) ++#define __msc_mask_rd(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_RXFIFO_RD_REQ ) ++#define __msc_unmask_rd(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_RXFIFO_RD_REQ ) ++#define __msc_mask_wr(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_TXFIFO_WR_REQ ) ++#define __msc_unmask_wr(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_TXFIFO_WR_REQ ) ++#define __msc_mask_endcmdres(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_END_CMD_RES ) ++#define __msc_unmask_endcmdres(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_END_CMD_RES ) ++#define __msc_mask_datatrandone(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_DATA_TRAN_DONE ) ++#define __msc_unmask_datatrandone(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_DATA_TRAN_DONE ) ++#define __msc_mask_prgdone(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_PRG_DONE ) ++#define __msc_unmask_prgdone(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_PRG_DONE ) ++ ++/* m=0,1,2,3,4,5,6,7 */ ++#define __msc_set_clkrt(n, m) \ ++do { \ ++ REG_MSC_CLKRT(n) = m; \ ++} while(0) ++ ++#define __msc_get_ireg(n) ( REG_MSC_IREG(n) ) ++#define __msc_ireg_rd(n) ( REG_MSC_IREG(n) & MSC_IREG_RXFIFO_RD_REQ ) ++#define __msc_ireg_wr(n) ( REG_MSC_IREG(n) & MSC_IREG_TXFIFO_WR_REQ ) ++#define __msc_ireg_end_cmd_res(n) ( REG_MSC_IREG(n) & MSC_IREG_END_CMD_RES ) ++#define __msc_ireg_data_tran_done(n) ( REG_MSC_IREG(n) & MSC_IREG_DATA_TRAN_DONE ) ++#define __msc_ireg_prg_done(n) ( REG_MSC_IREG(n) & MSC_IREG_PRG_DONE ) ++#define __msc_ireg_clear_end_cmd_res(n) ( REG_MSC_IREG(n) = MSC_IREG_END_CMD_RES ) ++#define __msc_ireg_clear_data_tran_done(n) ( REG_MSC_IREG(n) = MSC_IREG_DATA_TRAN_DONE ) ++#define __msc_ireg_clear_prg_done(n) ( REG_MSC_IREG(n) = MSC_IREG_PRG_DONE ) ++ ++#define __msc_get_stat(n) ( REG_MSC_STAT(n) ) ++#define __msc_stat_not_end_cmd_res(n) ( (REG_MSC_STAT(n) & MSC_STAT_END_CMD_RES) == 0) ++#define __msc_stat_crc_err(n) \ ++ ( REG_MSC_STAT(n) & (MSC_STAT_CRC_RES_ERR | MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR_YES) ) ++#define __msc_stat_res_crc_err(n) ( REG_MSC_STAT(n) & MSC_STAT_CRC_RES_ERR ) ++#define __msc_stat_rd_crc_err(n) ( REG_MSC_STAT(n) & MSC_STAT_CRC_READ_ERROR ) ++#define __msc_stat_wr_crc_err(n) ( REG_MSC_STAT(n) & MSC_STAT_CRC_WRITE_ERROR_YES ) ++#define __msc_stat_resto_err(n) ( REG_MSC_STAT(n) & MSC_STAT_TIME_OUT_RES ) ++#define __msc_stat_rdto_err(n) ( REG_MSC_STAT(n) & MSC_STAT_TIME_OUT_READ ) ++ ++#define __msc_rd_resfifo(n) ( REG_MSC_RES(n) ) ++#define __msc_rd_rxfifo(n) ( REG_MSC_RXFIFO(n) ) ++#define __msc_wr_txfifo(n, v) ( REG_MSC_TXFIFO(n) = v ) ++ ++#define __msc_reset(n) \ ++do { \ ++ REG_MSC_STRPCL(n) = MSC_STRPCL_RESET; \ ++ while (REG_MSC_STAT(n) & MSC_STAT_IS_RESETTING); \ ++} while (0) ++ ++#define __msc_start_clk(n) \ ++do { \ ++ REG_MSC_STRPCL(n) = MSC_STRPCL_CLOCK_CONTROL_START; \ ++} while (0) ++ ++#define __msc_stop_clk(n) \ ++do { \ ++ REG_MSC_STRPCL(n) = MSC_STRPCL_CLOCK_CONTROL_STOP; \ ++} while (0) ++ ++#define MMC_CLK 19169200 ++#define SD_CLK 24576000 ++ ++/* msc_clk should little than pclk and little than clk retrieve from card */ ++#define __msc_calc_clk_divisor(type,dev_clk,msc_clk,lv) \ ++do { \ ++ unsigned int rate, pclk, i; \ ++ pclk = dev_clk; \ ++ rate = type?SD_CLK:MMC_CLK; \ ++ if (msc_clk && msc_clk < pclk) \ ++ pclk = msc_clk; \ ++ i = 0; \ ++ while (pclk < rate) \ ++ { \ ++ i ++; \ ++ rate >>= 1; \ ++ } \ ++ lv = i; \ ++} while(0) ++ ++/* divide rate to little than or equal to 400kHz */ ++#define __msc_calc_slow_clk_divisor(type, lv) \ ++do { \ ++ unsigned int rate, i; \ ++ rate = (type?SD_CLK:MMC_CLK)/1000/400; \ ++ i = 0; \ ++ while (rate > 0) \ ++ { \ ++ rate >>= 1; \ ++ i ++; \ ++ } \ ++ lv = i; \ ++} while(0) ++ ++ ++/*************************************************************************** ++ * SSI (Synchronous Serial Interface) ++ ***************************************************************************/ ++/* n = 0, 1 (SSI0, SSI1) */ ++#define __ssi_enable(n) ( REG_SSI_CR0(n) |= SSI_CR0_SSIE ) ++#define __ssi_disable(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_SSIE ) ++#define __ssi_select_ce(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_FSEL ) ++ ++#define __ssi_normal_mode(n) ( REG_SSI_ITR(n) &= ~SSI_ITR_IVLTM_MASK ) ++ ++#define __ssi_select_ce2(n) \ ++do { \ ++ REG_SSI_CR0(n) |= SSI_CR0_FSEL; \ ++ REG_SSI_CR1(n) &= ~SSI_CR1_MULTS; \ ++} while (0) ++ ++#define __ssi_select_gpc(n) \ ++do { \ ++ REG_SSI_CR0(n) &= ~SSI_CR0_FSEL; \ ++ REG_SSI_CR1(n) |= SSI_CR1_MULTS; \ ++} while (0) ++ ++#define __ssi_underrun_auto_clear(n) \ ++do { \ ++ REG_SSI_CR0(n) |= SSI_CR0_EACLRUN; \ ++} while (0) ++ ++#define __ssi_underrun_clear_manually(n) \ ++do { \ ++ REG_SSI_CR0(n) &= ~SSI_CR0_EACLRUN; \ ++} while (0) ++ ++#define __ssi_enable_tx_intr(n) \ ++ ( REG_SSI_CR0(n) |= SSI_CR0_TIE | SSI_CR0_TEIE ) ++ ++#define __ssi_disable_tx_intr(n) \ ++ ( REG_SSI_CR0(n) &= ~(SSI_CR0_TIE | SSI_CR0_TEIE) ) ++ ++#define __ssi_enable_rx_intr(n) \ ++ ( REG_SSI_CR0(n) |= SSI_CR0_RIE | SSI_CR0_REIE ) ++ ++#define __ssi_disable_rx_intr(n) \ ++ ( REG_SSI_CR0(n) &= ~(SSI_CR0_RIE | SSI_CR0_REIE) ) ++ ++#define __ssi_enable_txfifo_half_empty_intr(n) \ ++ ( REG_SSI_CR0(n) |= SSI_CR0_TIE ) ++#define __ssi_disable_txfifo_half_empty_intr(n) \ ++ ( REG_SSI_CR0(n) &= ~SSI_CR0_TIE ) ++#define __ssi_enable_tx_error_intr(n) \ ++ ( REG_SSI_CR0(n) |= SSI_CR0_TEIE ) ++#define __ssi_disable_tx_error_intr(n) \ ++ ( REG_SSI_CR0(n) &= ~SSI_CR0_TEIE ) ++#define __ssi_enable_rxfifo_half_full_intr(n) \ ++ ( REG_SSI_CR0(n) |= SSI_CR0_RIE ) ++#define __ssi_disable_rxfifo_half_full_intr(n) \ ++ ( REG_SSI_CR0(n) &= ~SSI_CR0_RIE ) ++#define __ssi_enable_rx_error_intr(n) \ ++ ( REG_SSI_CR0(n) |= SSI_CR0_REIE ) ++#define __ssi_disable_rx_error_intr(n) \ ++ ( REG_SSI_CR0(n) &= ~SSI_CR0_REIE ) ++ ++#define __ssi_enable_loopback(n) ( REG_SSI_CR0(n) |= SSI_CR0_LOOP ) ++#define __ssi_disable_loopback(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_LOOP ) ++ ++#define __ssi_enable_receive(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_DISREV ) ++#define __ssi_disable_receive(n) ( REG_SSI_CR0(n) |= SSI_CR0_DISREV ) ++ ++#define __ssi_finish_receive(n) \ ++ ( REG_SSI_CR0(n) |= (SSI_CR0_RFINE | SSI_CR0_RFINC) ) ++ ++#define __ssi_disable_recvfinish(n) \ ++ ( REG_SSI_CR0(n) &= ~(SSI_CR0_RFINE | SSI_CR0_RFINC) ) ++ ++#define __ssi_flush_txfifo(n) ( REG_SSI_CR0(n) |= SSI_CR0_TFLUSH ) ++#define __ssi_flush_rxfifo(n) ( REG_SSI_CR0(n) |= SSI_CR0_RFLUSH ) ++ ++#define __ssi_flush_fifo(n) \ ++ ( REG_SSI_CR0(n) |= SSI_CR0_TFLUSH | SSI_CR0_RFLUSH ) ++ ++#define __ssi_finish_transmit(n) ( REG_SSI_CR1(n) &= ~SSI_CR1_UNFIN ) ++#define __ssi_wait_transmit(n) ( REG_SSI_CR1(n) |= SSI_CR1_UNFIN ) ++#define __ssi_use_busy_wait_mode(n) __ssi_wait_transmit(n) ++#define __ssi_unset_busy_wait_mode(n) __ssi_finish_transmit(n) ++ ++#define __ssi_spi_format(n) \ ++ do { \ ++ REG_SSI_CR1(n) &= ~SSI_CR1_FMAT_MASK; \ ++ REG_SSI_CR1(n) |= SSI_CR1_FMAT_SPI; \ ++ REG_SSI_CR1(n) &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK); \ ++ REG_SSI_CR1(n) |= (SSI_CR1_TFVCK_1 | SSI_CR1_TCKFI_1); \ ++ } while (0) ++ ++/* TI's SSP format, must clear SSI_CR1.UNFIN */ ++#define __ssi_ssp_format(n) \ ++ do { \ ++ REG_SSI_CR1(n) &= ~(SSI_CR1_FMAT_MASK | SSI_CR1_UNFIN); \ ++ REG_SSI_CR1(n) |= SSI_CR1_FMAT_SSP; \ ++ } while (0) ++ ++/* National's Microwire format, must clear SSI_CR0.RFINE, and set max delay */ ++#define __ssi_microwire_format(n) \ ++ do { \ ++ REG_SSI_CR1(n) &= ~SSI_CR1_FMAT_MASK; \ ++ REG_SSI_CR1(n) |= SSI_CR1_FMAT_MW1; \ ++ REG_SSI_CR1(n) &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK); \ ++ REG_SSI_CR1(n) |= (SSI_CR1_TFVCK_3 | SSI_CR1_TCKFI_3); \ ++ REG_SSI_CR0(n) &= ~SSI_CR0_RFINE; \ ++ } while (0) ++ ++/* CE# level (FRMHL), CE# in interval time (ITFRM), ++ clock phase and polarity (PHA POL), ++ interval time (SSIITR), interval characters/frame (SSIICR) */ ++ ++/* frmhl,endian,mcom,flen,pha,pol MASK */ ++#define SSICR1_MISC_MASK \ ++ ( SSI_CR1_FRMHL_MASK | SSI_CR1_LFST | SSI_CR1_MCOM_MASK \ ++ | SSI_CR1_FLEN_MASK | SSI_CR1_PHA | SSI_CR1_POL ) ++ ++#define __ssi_spi_set_misc(n,frmhl,endian,flen,mcom,pha,pol) \ ++ do { \ ++ REG_SSI_CR1(n) &= ~SSICR1_MISC_MASK; \ ++ REG_SSI_CR1(n) |= ((frmhl) << 30) | ((endian) << 25) | \ ++ (((mcom) - 1) << 12) | (((flen) - 2) << 4) | \ ++ ((pha) << 1) | (pol); \ ++ } while(0) ++ ++/* Transfer with MSB or LSB first */ ++#define __ssi_set_msb(n) ( REG_SSI_CR1(n) &= ~SSI_CR1_LFST ) ++#define __ssi_set_lsb(n) ( REG_SSI_CR1(n) |= SSI_CR1_LFST ) ++ ++#define __ssi_set_frame_length(n, m) \ ++ REG_SSI_CR1(n) = (REG_SSI_CR1(n) & ~SSI_CR1_FLEN_MASK) | (((m) - 2) << 4) ++ ++/* m = 1 - 16 */ ++#define __ssi_set_microwire_command_length(n,m) \ ++ ( REG_SSI_CR1(n) = ((REG_SSI_CR1(n) & ~SSI_CR1_MCOM_MASK) | SSI_CR1_MCOM_##m##BIT) ) ++ ++/* Set the clock phase for SPI */ ++#define __ssi_set_spi_clock_phase(n, m) \ ++ ( REG_SSI_CR1(n) = ((REG_SSI_CR1(n) & ~SSI_CR1_PHA) | (((m)&0x1)<< 1))) ++ ++/* Set the clock polarity for SPI */ ++#define __ssi_set_spi_clock_polarity(n, p) \ ++ ( REG_SSI_CR1(n) = ((REG_SSI_CR1(n) & ~SSI_CR1_POL) | ((p)&0x1)) ) ++ ++/* SSI tx trigger, m = i x 8 */ ++#define __ssi_set_tx_trigger(n, m) \ ++ do { \ ++ REG_SSI_CR1(n) &= ~SSI_CR1_TTRG_MASK; \ ++ REG_SSI_CR1(n) |= ((m)/8)<> SSI_SR_TFIFONUM_BIT ) ++ ++#define __ssi_get_rxfifo_count(n) \ ++ ( (REG_SSI_SR(n) & SSI_SR_RFIFONUM_MASK) >> SSI_SR_RFIFONUM_BIT ) ++ ++#define __ssi_transfer_end(n) ( REG_SSI_SR(n) & SSI_SR_END ) ++#define __ssi_is_busy(n) ( REG_SSI_SR(n) & SSI_SR_BUSY ) ++ ++#define __ssi_txfifo_full(n) ( REG_SSI_SR(n) & SSI_SR_TFF ) ++#define __ssi_rxfifo_empty(n) ( REG_SSI_SR(n) & SSI_SR_RFE ) ++#define __ssi_rxfifo_half_full(n) ( REG_SSI_SR(n) & SSI_SR_RFHF ) ++#define __ssi_txfifo_half_empty(n) ( REG_SSI_SR(n) & SSI_SR_TFHE ) ++#define __ssi_underrun(n) ( REG_SSI_SR(n) & SSI_SR_UNDR ) ++#define __ssi_overrun(n) ( REG_SSI_SR(n) & SSI_SR_OVER ) ++#define __ssi_clear_underrun(n) ( REG_SSI_SR(n) = ~SSI_SR_UNDR ) ++#define __ssi_clear_overrun(n) ( REG_SSI_SR(n) = ~SSI_SR_OVER ) ++#define __ssi_clear_errors(n) ( REG_SSI_SR(n) &= ~(SSI_SR_UNDR | SSI_SR_OVER) ) ++ ++#define __ssi_set_clk(n, dev_clk, ssi_clk) \ ++ ( REG_SSI_GR(n) = (dev_clk) / (2*(ssi_clk)) - 1 ) ++ ++#define __ssi_receive_data(n) REG_SSI_DR(n) ++#define __ssi_transmit_data(n, v) (REG_SSI_DR(n) = (v)) ++ ++ ++/*************************************************************************** ++ * CIM ++ ***************************************************************************/ ++ ++#define __cim_enable() ( REG_CIM_CTRL |= CIM_CTRL_ENA ) ++#define __cim_disable() ( REG_CIM_CTRL &= ~CIM_CTRL_ENA ) ++ ++/* n = 0, 1, 2, 3 */ ++#define __cim_set_input_data_stream_order(n) \ ++ do { \ ++ REG_CIM_CFG &= CIM_CFG_ORDER_MASK; \ ++ REG_CIM_CFG |= ((n)<>CIM_SIZE_LPF_BIT) ++#define __cim_get_pixel() ((REG_CIM_SIZE&CIM_SIZE_PPL_MASK)>>CIM_SIZE_PPL_BIT) ++ ++#define __cim_set_v_offset(a) ( REG_CIM_OFFSET = (REG_CIM_OFFSET&(~CIM_OFFSET_V_MASK)) | ((a)<>CIM_OFFSET_V_BIT) ++#define __cim_get_h_offset() ((REG_CIM_OFFSET&CIM_OFFSET_H_MASK)>>CIM_OFFSET_H_BIT) ++ ++/************************************************************************* ++ * SLCD (Smart LCD Controller) ++ *************************************************************************/ ++#define __slcd_set_data_18bit() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_18BIT ) ++#define __slcd_set_data_16bit() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_16BIT ) ++#define __slcd_set_data_8bit_x3() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_8BIT_x3 ) ++#define __slcd_set_data_8bit_x2() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_8BIT_x2 ) ++#define __slcd_set_data_8bit_x1() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_8BIT_x1 ) ++#define __slcd_set_data_24bit() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_24BIT ) ++#define __slcd_set_data_9bit_x2() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_9BIT_x2 ) ++ ++#define __slcd_set_cmd_16bit() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_16BIT ) ++#define __slcd_set_cmd_8bit() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_8BIT ) ++#define __slcd_set_cmd_18bit() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_18BIT ) ++#define __slcd_set_cmd_24bit() \ ++ ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_24BIT ) ++ ++#define __slcd_set_cs_high() ( REG_SLCD_CFG |= SLCD_CFG_CS_ACTIVE_HIGH ) ++#define __slcd_set_cs_low() ( REG_SLCD_CFG &= ~SLCD_CFG_CS_ACTIVE_HIGH ) ++ ++#define __slcd_set_rs_high() ( REG_SLCD_CFG |= SLCD_CFG_RS_CMD_HIGH ) ++#define __slcd_set_rs_low() ( REG_SLCD_CFG &= ~SLCD_CFG_RS_CMD_HIGH ) ++ ++#define __slcd_set_clk_falling() ( REG_SLCD_CFG &= ~SLCD_CFG_CLK_ACTIVE_RISING ) ++#define __slcd_set_clk_rising() ( REG_SLCD_CFG |= SLCD_CFG_CLK_ACTIVE_RISING ) ++ ++#define __slcd_set_parallel_type() ( REG_SLCD_CFG &= ~SLCD_CFG_TYPE_SERIAL ) ++#define __slcd_set_serial_type() ( REG_SLCD_CFG |= SLCD_CFG_TYPE_SERIAL ) ++ ++/* SLCD Control Register */ ++#define __slcd_enable_dma() ( REG_SLCD_CTRL |= SLCD_CTRL_DMA_EN ) ++#define __slcd_disable_dma() ( REG_SLCD_CTRL &= ~SLCD_CTRL_DMA_EN ) ++ ++/* SLCD Status Register */ ++#define __slcd_is_busy() ( REG_SLCD_STATE & SLCD_STATE_BUSY ) ++ ++/* SLCD Data Register */ ++#define __slcd_set_cmd_rs() ( REG_SLCD_DATA |= SLCD_DATA_RS_COMMAND) ++#define __slcd_set_data_rs() ( REG_SLCD_DATA &= ~SLCD_DATA_RS_COMMAND) ++ ++ ++/*************************************************************************** ++ * LCD ++ ***************************************************************************/ ++ ++/*************************************************************************** ++ * LCD ++ ***************************************************************************/ ++#define __lcd_as_smart_lcd() ( REG_LCD_CFG |= ( LCD_CFG_LCDPIN_SLCD | LCD_CFG_MODE_SLCD)) ++#define __lcd_as_general_lcd() ( REG_LCD_CFG &= ~( LCD_CFG_LCDPIN_SLCD | LCD_CFG_MODE_SLCD)) ++ ++#define __lcd_enable_tvepeh() ( REG_LCD_CFG |= LCD_CFG_TVEPEH ) ++#define __lcd_disable_tvepeh() ( REG_LCD_CFG &= ~LCD_CFG_TVEPEH ) ++ ++#define __lcd_enable_fuhold() ( REG_LCD_CFG |= LCD_CFG_FUHOLD ) ++#define __lcd_disable_fuhold() ( REG_LCD_CFG &= ~LCD_CFG_FUHOLD ) ++ ++#define __lcd_des_8word() ( REG_LCD_CFG |= LCD_CFG_NEWDES ) ++#define __lcd_des_4word() ( REG_LCD_CFG &= ~LCD_CFG_NEWDES ) ++ ++#define __lcd_enable_bypass_pal() ( REG_LCD_CFG |= LCD_CFG_PALBP ) ++#define __lcd_disable_bypass_pal() ( REG_LCD_CFG &= ~LCD_CFG_PALBP ) ++ ++#define __lcd_set_lcdpnl_term() ( REG_LCD_CTRL |= LCD_CFG_TVEN ) ++#define __lcd_set_tv_term() ( REG_LCD_CTRL &= ~LCD_CFG_TVEN ) ++ ++#define __lcd_enable_auto_recover() ( REG_LCD_CFG |= LCD_CFG_RECOVER ) ++#define __lcd_disable_auto_recover() ( REG_LCD_CFG &= ~LCD_CFG_RECOVER ) ++ ++#define __lcd_enable_dither() ( REG_LCD_CFG |= LCD_CFG_DITHER ) ++#define __lcd_disable_dither() ( REG_LCD_CFG &= ~LCD_CFG_DITHER ) ++ ++#define __lcd_disable_ps_mode() ( REG_LCD_CFG |= LCD_CFG_PSM ) ++#define __lcd_enable_ps_mode() ( REG_LCD_CFG &= ~LCD_CFG_PSM ) ++ ++#define __lcd_disable_cls_mode() ( REG_LCD_CFG |= LCD_CFG_CLSM ) ++#define __lcd_enable_cls_mode() ( REG_LCD_CFG &= ~LCD_CFG_CLSM ) ++ ++#define __lcd_disable_spl_mode() ( REG_LCD_CFG |= LCD_CFG_SPLM ) ++#define __lcd_enable_spl_mode() ( REG_LCD_CFG &= ~LCD_CFG_SPLM ) ++ ++#define __lcd_disable_rev_mode() ( REG_LCD_CFG |= LCD_CFG_REVM ) ++#define __lcd_enable_rev_mode() ( REG_LCD_CFG &= ~LCD_CFG_REVM ) ++ ++#define __lcd_disable_hsync_mode() ( REG_LCD_CFG |= LCD_CFG_HSYNM ) ++#define __lcd_enable_hsync_mode() ( REG_LCD_CFG &= ~LCD_CFG_HSYNM ) ++ ++#define __lcd_disable_pclk_mode() ( REG_LCD_CFG |= LCD_CFG_PCLKM ) ++#define __lcd_enable_pclk_mode() ( REG_LCD_CFG &= ~LCD_CFG_PCLKM ) ++ ++#define __lcd_normal_outdata() ( REG_LCD_CFG &= ~LCD_CFG_INVDAT ) ++#define __lcd_inverse_outdata() ( REG_LCD_CFG |= LCD_CFG_INVDAT ) ++ ++#define __lcd_sync_input() ( REG_LCD_CFG |= LCD_CFG_SYNDIR_IN ) ++#define __lcd_sync_output() ( REG_LCD_CFG &= ~LCD_CFG_SYNDIR_IN ) ++ ++#define __lcd_hsync_active_high() ( REG_LCD_CFG &= ~LCD_CFG_HSP ) ++#define __lcd_hsync_active_low() ( REG_LCD_CFG |= LCD_CFG_HSP ) ++ ++#define __lcd_pclk_rising() ( REG_LCD_CFG &= ~LCD_CFG_PCP ) ++#define __lcd_pclk_falling() ( REG_LCD_CFG |= LCD_CFG_PCP ) ++ ++#define __lcd_de_active_high() ( REG_LCD_CFG &= ~LCD_CFG_DEP ) ++#define __lcd_de_active_low() ( REG_LCD_CFG |= LCD_CFG_DEP ) ++ ++#define __lcd_vsync_rising() ( REG_LCD_CFG &= ~LCD_CFG_VSP ) ++#define __lcd_vsync_falling() ( REG_LCD_CFG |= LCD_CFG_VSP ) ++ ++#define __lcd_set_16_tftpnl() \ ++ ( REG_LCD_CFG = (REG_LCD_CFG & ~LCD_CFG_MODE_TFT_MASK) | LCD_CFG_MODE_TFT_16BIT ) ++ ++#define __lcd_set_18_tftpnl() \ ++ ( REG_LCD_CFG = (REG_LCD_CFG & ~LCD_CFG_MODE_TFT_MASK) | LCD_CFG_MODE_TFT_18BIT ) ++ ++#define __lcd_set_24_tftpnl() ( REG_LCD_CFG |= LCD_CFG_MODE_TFT_24BIT ) ++ ++/* ++ * n=1,2,4,8 for single mono-STN ++ * n=4,8 for dual mono-STN ++ */ ++#define __lcd_set_panel_datawidth(n) \ ++do { \ ++ REG_LCD_CFG &= ~LCD_CFG_PDW_MASK; \ ++ REG_LCD_CFG |= LCD_CFG_PDW_n##; \ ++} while (0) ++ ++/* m = LCD_CFG_MODE_GENERUIC_TFT_xxx */ ++#define __lcd_set_panel_mode(m) \ ++do { \ ++ REG_LCD_CFG &= ~LCD_CFG_MODE_MASK; \ ++ REG_LCD_CFG |= (m); \ ++} while(0) ++ ++/* n=4,8,16 */ ++#define __lcd_set_burst_length(n) \ ++do { \ ++ REG_LCD_CTRL &= ~LCD_CTRL_BST_MASK; \ ++ REG_LCD_CTRL |= LCD_CTRL_BST_n##; \ ++} while (0) ++ ++#define __lcd_select_rgb565() ( REG_LCD_CTRL &= ~LCD_CTRL_RGB555 ) ++#define __lcd_select_rgb555() ( REG_LCD_CTRL |= LCD_CTRL_RGB555 ) ++ ++#define __lcd_set_ofup() ( REG_LCD_CTRL |= LCD_CTRL_OFUP ) ++#define __lcd_clr_ofup() ( REG_LCD_CTRL &= ~LCD_CTRL_OFUP ) ++ ++/* n=2,4,16 */ ++#define __lcd_set_stn_frc(n) \ ++do { \ ++ REG_LCD_CTRL &= ~LCD_CTRL_FRC_MASK; \ ++ REG_LCD_CTRL |= LCD_CTRL_FRC_n##; \ ++} while (0) ++ ++#define __lcd_enable_eof_intr() ( REG_LCD_CTRL |= LCD_CTRL_EOFM ) ++#define __lcd_disable_eof_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_EOFM ) ++ ++#define __lcd_enable_sof_intr() ( REG_LCD_CTRL |= LCD_CTRL_SOFM ) ++#define __lcd_disable_sof_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_SOFM ) ++ ++#define __lcd_enable_ofu_intr() ( REG_LCD_CTRL |= LCD_CTRL_OFUM ) ++#define __lcd_disable_ofu_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_OFUM ) ++ ++#define __lcd_enable_ifu0_intr() ( REG_LCD_CTRL |= LCD_CTRL_IFUM0 ) ++#define __lcd_disable_ifu0_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_IFUM0 ) ++ ++#define __lcd_enable_ifu1_intr() ( REG_LCD_CTRL |= LCD_CTRL_IFUM1 ) ++#define __lcd_disable_ifu1_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_IFUM1 ) ++ ++#define __lcd_enable_ldd_intr() ( REG_LCD_CTRL |= LCD_CTRL_LDDM ) ++#define __lcd_disable_ldd_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_LDDM ) ++ ++#define __lcd_enable_qd_intr() ( REG_LCD_CTRL |= LCD_CTRL_QDM ) ++#define __lcd_disable_qd_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_QDM ) ++ ++#define __lcd_reverse_byte_endian() ( REG_LCD_CTRL |= LCD_CTRL_BEDN ) ++#define __lcd_normal_byte_endian() ( REG_LCD_CTRL &= ~LCD_CTRL_BEDN ) ++ ++#define __lcd_pixel_endian_little() ( REG_LCD_CTRL |= LCD_CTRL_PEDN ) ++#define __lcd_pixel_endian_big() ( REG_LCD_CTRL &= ~LCD_CTRL_PEDN ) ++ ++#define __lcd_set_dis() ( REG_LCD_CTRL |= LCD_CTRL_DIS ) ++#define __lcd_clr_dis() ( REG_LCD_CTRL &= ~LCD_CTRL_DIS ) ++ ++#define __lcd_set_ena() ( REG_LCD_CTRL |= LCD_CTRL_ENA ) ++#define __lcd_clr_ena() ( REG_LCD_CTRL &= ~LCD_CTRL_ENA ) ++ ++/* n=1,2,4,8,16 */ ++#define __lcd_set_bpp(n) \ ++ ( REG_LCD_CTRL = (REG_LCD_CTRL & ~LCD_CTRL_BPP_MASK) | LCD_CTRL_BPP_##n ) ++ ++/* LCD status register indication */ ++ ++#define __lcd_quick_disable_done() ( REG_LCD_STATE & LCD_STATE_QD ) ++#define __lcd_disable_done() ( REG_LCD_STATE & LCD_STATE_LDD ) ++#define __lcd_infifo0_underrun() ( REG_LCD_STATE & LCD_STATE_IFU0 ) ++#define __lcd_infifo1_underrun() ( REG_LCD_STATE & LCD_STATE_IFU1 ) ++#define __lcd_outfifo_underrun() ( REG_LCD_STATE & LCD_STATE_OFU ) ++#define __lcd_start_of_frame() ( REG_LCD_STATE & LCD_STATE_SOF ) ++#define __lcd_end_of_frame() ( REG_LCD_STATE & LCD_STATE_EOF ) ++ ++#define __lcd_clr_outfifounderrun() ( REG_LCD_STATE &= ~LCD_STATE_OFU ) ++#define __lcd_clr_sof() ( REG_LCD_STATE &= ~LCD_STATE_SOF ) ++#define __lcd_clr_eof() ( REG_LCD_STATE &= ~LCD_STATE_EOF ) ++ ++/* OSD functions */ ++#define __lcd_enable_osd() (REG_LCD_OSDC |= LCD_OSDC_OSDEN) ++#define __lcd_enable_f0() (REG_LCD_OSDC |= LCD_OSDC_F0EN) ++#define __lcd_enable_f1() (REG_LCD_OSDC |= LCD_OSDC_F1EN) ++#define __lcd_enable_alpha() (REG_LCD_OSDC |= LCD_OSDC_ALPHAEN) ++#define __lcd_enable_alphamd() (REG_LCD_OSDC |= LCD_OSDC_ALPHAMD) ++ ++#define __lcd_disable_osd() (REG_LCD_OSDC &= ~LCD_OSDC_OSDEN) ++#define __lcd_disable_f0() (REG_LCD_OSDC &= ~LCD_OSDC_F0EN) ++#define __lcd_disable_f1() (REG_LCD_OSDC &= ~LCD_OSDC_F1EN) ++#define __lcd_disable_alpha() (REG_LCD_OSDC &= ~LCD_OSDC_ALPHAEN) ++#define __lcd_disable_alphamd() (REG_LCD_OSDC &= ~LCD_OSDC_ALPHAMD) ++ ++/* OSD Controll Register */ ++#define __lcd_fg1_use_ipu() (REG_LCD_OSDCTRL |= LCD_OSDCTRL_IPU) ++#define __lcd_fg1_use_dma_chan1() (REG_LCD_OSDCTRL &= ~LCD_OSDCTRL_IPU) ++#define __lcd_fg1_unuse_ipu() __lcd_fg1_use_dma_chan1() ++#define __lcd_osd_rgb555_mode() ( REG_LCD_OSDCTRL |= LCD_OSDCTRL_RGB555 ) ++#define __lcd_osd_rgb565_mode() ( REG_LCD_OSDCTRL &= ~LCD_OSDCTRL_RGB555 ) ++#define __lcd_osd_change_size() ( REG_LCD_OSDCTRL |= LCD_OSDCTRL_CHANGES ) ++#define __lcd_osd_bpp_15_16() \ ++ ( REG_LCD_OSDCTRL = (REG_LCD_OSDCTRL & ~LCD_OSDCTRL_OSDBPP_MASK) | LCD_OSDCTRL_OSDBPP_15_16 ) ++#define __lcd_osd_bpp_18_24() \ ++ ( REG_LCD_OSDCTRL = (REG_LCD_OSDCTRL & ~LCD_OSDCTRL_OSDBPP_MASK) | LCD_OSDCTRL_OSDBPP_18_24 ) ++ ++/* OSD State Register */ ++#define __lcd_start_of_fg1() ( REG_LCD_STATE & LCD_OSDS_SOF1 ) ++#define __lcd_end_of_fg1() ( REG_LCD_STATE & LCD_OSDS_EOF1 ) ++#define __lcd_start_of_fg0() ( REG_LCD_STATE & LCD_OSDS_SOF0 ) ++#define __lcd_end_of_fg0() ( REG_LCD_STATE & LCD_OSDS_EOF0 ) ++#define __lcd_change_is_rdy() ( REG_LCD_STATE & LCD_OSDS_READY ) ++ ++/* Foreground Color Key Register 0,1(foreground 0, foreground 1) */ ++#define __lcd_enable_colorkey0() (REG_LCD_KEY0 |= LCD_KEY_KEYEN) ++#define __lcd_enable_colorkey1() (REG_LCD_KEY1 |= LCD_KEY_KEYEN) ++#define __lcd_enable_colorkey0_md() (REG_LCD_KEY0 |= LCD_KEY_KEYMD) ++#define __lcd_enable_colorkey1_md() (REG_LCD_KEY1 |= LCD_KEY_KEYMD) ++#define __lcd_set_colorkey0(key) (REG_LCD_KEY0 = (REG_LCD_KEY0&~0xFFFFFF)|(key)) ++#define __lcd_set_colorkey1(key) (REG_LCD_KEY1 = (REG_LCD_KEY1&~0xFFFFFF)|(key)) ++ ++#define __lcd_disable_colorkey0() (REG_LCD_KEY0 &= ~LCD_KEY_KEYEN) ++#define __lcd_disable_colorkey1() (REG_LCD_KEY1 &= ~LCD_KEY_KEYEN) ++#define __lcd_disable_colorkey0_md() (REG_LCD_KEY0 &= ~LCD_KEY_KEYMD) ++#define __lcd_disable_colorkey1_md() (REG_LCD_KEY1 &= ~LCD_KEY_KEYMD) ++ ++/* IPU Restart Register */ ++#define __lcd_enable_ipu_restart() (REG_LCD_IPUR |= LCD_IPUR_IPUREN) ++#define __lcd_disable_ipu_restart() (REG_LCD_IPUR &= ~LCD_IPUR_IPUREN) ++#define __lcd_set_ipu_restart_triger(n) (REG_LCD_IPUR = (REG_LCD_IPUR&(~0xFFFFFF))|(n)) ++ ++/* RGB Control Register */ ++#define __lcd_enable_rgb_dummy() (REG_LCD_RGBC |= LCD_RGBC_RGBDM) ++#define __lcd_disable_rgb_dummy() (REG_LCD_RGBC &= ~LCD_RGBC_RGBDM) ++ ++#define __lcd_dummy_rgb() (REG_LCD_RGBC |= LCD_RGBC_DMM) ++#define __lcd_rgb_dummy() (REG_LCD_RGBC &= ~LCD_RGBC_DMM) ++ ++#define __lcd_rgb2ycc() (REG_LCD_RGBC |= LCD_RGBC_YCC) ++#define __lcd_notrgb2ycc() (REG_LCD_RGBC &= ~LCD_RGBC_YCC) ++ ++#define __lcd_odd_mode_rgb() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_RGB ) ++#define __lcd_odd_mode_rbg() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_RBG ) ++#define __lcd_odd_mode_grb() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_GRB) ++ ++#define __lcd_odd_mode_gbr() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_GBR) ++#define __lcd_odd_mode_brg() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_BRG) ++#define __lcd_odd_mode_bgr() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_BGR) ++ ++#define __lcd_even_mode_rgb() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_RGB ) ++#define __lcd_even_mode_rbg() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_RBG ) ++#define __lcd_even_mode_grb() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_GRB) ++ ++#define __lcd_even_mode_gbr() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_GBR) ++#define __lcd_even_mode_brg() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_BRG) ++#define __lcd_even_mode_bgr() \ ++ ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_BGR) ++ ++/* Vertical Synchronize Register */ ++#define __lcd_vsync_get_vps() \ ++ ( (REG_LCD_VSYNC & LCD_VSYNC_VPS_MASK) >> LCD_VSYNC_VPS_BIT ) ++ ++#define __lcd_vsync_get_vpe() \ ++ ( (REG_LCD_VSYNC & LCD_VSYNC_VPE_MASK) >> LCD_VSYNC_VPE_BIT ) ++#define __lcd_vsync_set_vpe(n) \ ++do { \ ++ REG_LCD_VSYNC &= ~LCD_VSYNC_VPE_MASK; \ ++ REG_LCD_VSYNC |= (n) << LCD_VSYNC_VPE_BIT; \ ++} while (0) ++ ++#define __lcd_hsync_get_hps() \ ++ ( (REG_LCD_HSYNC & LCD_HSYNC_HPS_MASK) >> LCD_HSYNC_HPS_BIT ) ++#define __lcd_hsync_set_hps(n) \ ++do { \ ++ REG_LCD_HSYNC &= ~LCD_HSYNC_HPS_MASK; \ ++ REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPS_BIT; \ ++} while (0) ++ ++#define __lcd_hsync_get_hpe() \ ++ ( (REG_LCD_HSYNC & LCD_HSYNC_HPE_MASK) >> LCD_VSYNC_HPE_BIT ) ++#define __lcd_hsync_set_hpe(n) \ ++do { \ ++ REG_LCD_HSYNC &= ~LCD_HSYNC_HPE_MASK; \ ++ REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPE_BIT; \ ++} while (0) ++ ++#define __lcd_vat_get_ht() \ ++ ( (REG_LCD_VAT & LCD_VAT_HT_MASK) >> LCD_VAT_HT_BIT ) ++#define __lcd_vat_set_ht(n) \ ++do { \ ++ REG_LCD_VAT &= ~LCD_VAT_HT_MASK; \ ++ REG_LCD_VAT |= (n) << LCD_VAT_HT_BIT; \ ++} while (0) ++ ++#define __lcd_vat_get_vt() \ ++ ( (REG_LCD_VAT & LCD_VAT_VT_MASK) >> LCD_VAT_VT_BIT ) ++#define __lcd_vat_set_vt(n) \ ++do { \ ++ REG_LCD_VAT &= ~LCD_VAT_VT_MASK; \ ++ REG_LCD_VAT |= (n) << LCD_VAT_VT_BIT; \ ++} while (0) ++ ++#define __lcd_dah_get_hds() \ ++ ( (REG_LCD_DAH & LCD_DAH_HDS_MASK) >> LCD_DAH_HDS_BIT ) ++#define __lcd_dah_set_hds(n) \ ++do { \ ++ REG_LCD_DAH &= ~LCD_DAH_HDS_MASK; \ ++ REG_LCD_DAH |= (n) << LCD_DAH_HDS_BIT; \ ++} while (0) ++ ++#define __lcd_dah_get_hde() \ ++ ( (REG_LCD_DAH & LCD_DAH_HDE_MASK) >> LCD_DAH_HDE_BIT ) ++#define __lcd_dah_set_hde(n) \ ++do { \ ++ REG_LCD_DAH &= ~LCD_DAH_HDE_MASK; \ ++ REG_LCD_DAH |= (n) << LCD_DAH_HDE_BIT; \ ++} while (0) ++ ++#define __lcd_dav_get_vds() \ ++ ( (REG_LCD_DAV & LCD_DAV_VDS_MASK) >> LCD_DAV_VDS_BIT ) ++#define __lcd_dav_set_vds(n) \ ++do { \ ++ REG_LCD_DAV &= ~LCD_DAV_VDS_MASK; \ ++ REG_LCD_DAV |= (n) << LCD_DAV_VDS_BIT; \ ++} while (0) ++ ++#define __lcd_dav_get_vde() \ ++ ( (REG_LCD_DAV & LCD_DAV_VDE_MASK) >> LCD_DAV_VDE_BIT ) ++#define __lcd_dav_set_vde(n) \ ++do { \ ++ REG_LCD_DAV &= ~LCD_DAV_VDE_MASK; \ ++ REG_LCD_DAV |= (n) << LCD_DAV_VDE_BIT; \ ++} while (0) ++ ++/* DMA Command Register */ ++#define __lcd_cmd0_set_sofint() ( REG_LCD_CMD0 |= LCD_CMD_SOFINT ) ++#define __lcd_cmd0_clr_sofint() ( REG_LCD_CMD0 &= ~LCD_CMD_SOFINT ) ++#define __lcd_cmd1_set_sofint() ( REG_LCD_CMD1 |= LCD_CMD_SOFINT ) ++#define __lcd_cmd1_clr_sofint() ( REG_LCD_CMD1 &= ~LCD_CMD_SOFINT ) ++ ++#define __lcd_cmd0_set_eofint() ( REG_LCD_CMD0 |= LCD_CMD_EOFINT ) ++#define __lcd_cmd0_clr_eofint() ( REG_LCD_CMD0 &= ~LCD_CMD_EOFINT ) ++#define __lcd_cmd1_set_eofint() ( REG_LCD_CMD1 |= LCD_CMD_EOFINT ) ++#define __lcd_cmd1_clr_eofint() ( REG_LCD_CMD1 &= ~LCD_CMD_EOFINT ) ++ ++#define __lcd_cmd0_set_pal() ( REG_LCD_CMD0 |= LCD_CMD_PAL ) ++#define __lcd_cmd0_clr_pal() ( REG_LCD_CMD0 &= ~LCD_CMD_PAL ) ++ ++#define __lcd_cmd0_get_len() \ ++ ( (REG_LCD_CMD0 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) ++#define __lcd_cmd1_get_len() \ ++ ( (REG_LCD_CMD1 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) ++ ++/************************************************************************* ++ * TVE (TV Encoder Controller) ops ++ *************************************************************************/ ++/* TV Encoder Control register ops */ ++#define __tve_soft_reset() (REG_TVE_CTRL |= TVE_CTRL_SWRST) ++ ++#define __tve_output_colorbar() (REG_TVE_CTRL |= TVE_CTRL_CLBAR) ++#define __tve_output_video() (REG_TVE_CTRL &= ~TVE_CTRL_CLBAR) ++ ++#define __tve_input_cr_first() (REG_TVE_CTRL |= TVE_CTRL_CR1ST) ++#define __tve_input_cb_first() (REG_TVE_CTRL &= ~TVE_CTRL_CR1ST) ++ ++#define __tve_set_0_as_black() (REG_TVE_CTRL |= TVE_CTRL_ZBLACK) ++#define __tve_set_16_as_black() (REG_TVE_CTRL &= ~TVE_CTRL_ZBLACK) ++ ++#define __tve_ena_invert_top_bottom() (REG_TVE_CTRL |= TVE_CTRL_FINV) ++#define __tve_dis_invert_top_bottom() (REG_TVE_CTRL &= ~TVE_CTRL_FINV) ++ ++#define __tve_set_pal_mode() (REG_TVE_CTRL |= TVE_CTRL_PAL) ++#define __tve_set_ntsc_mode() (REG_TVE_CTRL &= ~TVE_CTRL_PAL) ++ ++#define __tve_set_pal_dura() (REG_TVE_CTRL |= TVE_CTRL_SYNCT) ++#define __tve_set_ntsc_dura() (REG_TVE_CTRL &= ~TVE_CTRL_SYNCT) ++ ++/* n = 0 ~ 3 */ ++#define __tve_set_c_bandwidth(n) \ ++do {\ ++ REG_TVE_CTRL &= ~TVE_CTRL_CBW_MASK;\ ++ REG_TVE_CTRL |= (n) << TVE_CTRL_CBW_BIT; \ ++}while(0) ++ ++/* n = 0 ~ 3 */ ++#define __tve_set_c_gain(n) \ ++do {\ ++ REG_TVE_CTRL &= ~TVE_CTRL_CGAIN_MASK;\ ++ (REG_TVE_CTRL |= (n) << TVE_CTRL_CGAIN_BIT; \ ++}while(0) ++ ++/* n = 0 ~ 7 */ ++#define __tve_set_yc_delay(n) \ ++do { \ ++ REG_TVE_CTRL &= ~TVE_CTRL_YCDLY_MASK \ ++ REG_TVE_CTRL |= ((n) << TVE_CTRL_YCDLY_BIT); \ ++} while(0) ++ ++#define __tve_disable_all_dacs() (REG_TVE_CTRL |= TVE_CTRL_DAPD) ++#define __tve_disable_dac1() (REG_TVE_CTRL |= TVE_CTRL_DAPD1) ++#define __tve_enable_dac1() (REG_TVE_CTRL &= ~TVE_CTRL_DAPD1) ++#define __tve_disable_dac2() (REG_TVE_CTRL |= TVE_CTRL_DAPD2) ++#define __tve_enable_dac2() (REG_TVE_CTRL &= ~TVE_CTRL_DAPD2) ++#define __tve_disable_dac3() (REG_TVE_CTRL |= TVE_CTRL_DAPD3) ++#define __tve_enable_dac3() (REG_TVE_CTRL &= ~TVE_CTRL_DAPD3) ++ ++#define __tve_enable_svideo_fmt() (REG_TVE_CTRL |= TVE_CTRL_ECVBS) ++#define __tve_enable_cvbs_fmt() (REG_TVE_CTRL &= ~TVE_CTRL_ECVBS) ++ ++/* TV Encoder Frame Configure register ops */ ++/* n = 0 ~ 255 */ ++#define __tve_set_first_video_line(n) \ ++do {\ ++ REG_TVE_FRCFG &= ~TVE_FRCFG_L1ST_MASK;\ ++ REG_TVE_FRCFG |= (n) << TVE_FRCFG_L1ST_BIT;\ ++} while(0) ++/* n = 0 ~ 1023 */ ++#define __tve_set_line_num_per_frm(n) \ ++do {\ ++ REG_TVE_FRCFG &= ~TVE_FRCFG_NLINE_MASK;\ ++ REG_TVE_CFG |= (n) << TVE_FRCFG_NLINE_BIT;\ ++} while(0) ++#define __tve_get_video_line_num()\ ++ (((REG_TVE_FRCFG & TVE_FRCFG_NLINE_MASK) >> TVE_FRCFG_NLINE_BIT) - 1 - 2 * ((REG_TVE_FRCFG & TVE_FRCFG_L1ST_MASK) >> TVE_FRCFG_L1ST_BIT)) ++ ++/* TV Encoder Signal Level Configure register ops */ ++/* n = 0 ~ 1023 */ ++#define __tve_set_white_level(n) \ ++do {\ ++ REG_TVE_SLCFG1 &= ~TVE_SLCFG1_WHITEL_MASK;\ ++ REG_TVE_SLCFG1 |= (n) << TVE_SLCFG1_WHITEL_BIT;\ ++} while(0) ++/* n = 0 ~ 1023 */ ++#define __tve_set_black_level(n) \ ++do {\ ++ REG_TVE_SLCFG1 &= ~TVE_SLCFG1_BLACKL_MASK;\ ++ REG_TVE_SLCFG1 |= (n) << TVE_SLCFG1_BLACKL_BIT;\ ++} while(0) ++/* n = 0 ~ 1023 */ ++#define __tve_set_blank_level(n) \ ++do {\ ++ REG_TVE_SLCFG2 &= ~TVE_SLCFG2_BLANKL_MASK;\ ++ REG_TVE_SLCFG2 |= (n) << TVE_SLCFG2_BLANKL_BIT;\ ++} while(0) ++/* n = 0 ~ 1023 */ ++#define __tve_set_vbi_blank_level(n) \ ++do {\ ++ REG_TVE_SLCFG2 &= ~TVE_SLCFG2_VBLANKL_MASK;\ ++ REG_TVE_SLCFG2 |= (n) << TVE_SLCFG2_VBLANKL_BIT;\ ++} while(0) ++/* n = 0 ~ 1023 */ ++#define __tve_set_sync_level(n) \ ++do {\ ++ REG_TVE_SLCFG3 &= ~TVE_SLCFG3_SYNCL_MASK;\ ++ REG_TVE_SLCFG3 |= (n) << TVE_SLCFG3_SYNCL_BIT;\ ++} while(0) ++ ++/* TV Encoder Signal Level Configure register ops */ ++/* n = 0 ~ 31 */ ++#define __tve_set_front_porch(n) \ ++do {\ ++ REG_TVE_LTCFG1 &= ~TVE_LTCFG1_FRONTP_MASK;\ ++ REG_TVE_LTCFG1 |= (n) << TVE_LTCFG1_FRONTP_BIT; \ ++} while(0) ++/* n = 0 ~ 127 */ ++#define __tve_set_hsync_width(n) \ ++do {\ ++ REG_TVE_LTCFG1 &= ~TVE_LTCFG1_HSYNCW_MASK;\ ++ REG_TVE_LTCFG1 |= (n) << TVE_LTCFG1_HSYNCW_BIT; \ ++} while(0) ++/* n = 0 ~ 127 */ ++#define __tve_set_back_porch(n) \ ++do {\ ++ REG_TVE_LTCFG1 &= ~TVE_LTCFG1_BACKP_MASK;\ ++ REG_TVE_LTCFG1 |= (n) << TVE_LTCFG1_BACKP_BIT; \ ++} while(0) ++/* n = 0 ~ 2047 */ ++#define __tve_set_active_linec(n) \ ++do {\ ++ REG_TVE_LTCFG2 &= ~TVE_LTCFG2_ACTLIN_MASK;\ ++ REG_TVE_LTCFG2 |= (n) << TVE_LTCFG2_ACTLIN_BIT; \ ++} while(0) ++/* n = 0 ~ 31 */ ++#define __tve_set_breezy_way(n) \ ++do {\ ++ REG_TVE_LTCFG2 &= ~TVE_LTCFG2_PREBW_MASK;\ ++ REG_TVE_LTCFG2 |= (n) << TVE_LTCFG2_PREBW_BIT; \ ++} while(0) ++ ++/* n = 0 ~ 127 */ ++#define __tve_set_burst_width(n) \ ++do {\ ++ REG_TVE_LTCFG2 &= ~TVE_LTCFG2_BURSTW_MASK;\ ++ REG_TVE_LTCFG2 |= (n) << TVE_LTCFG2_BURSTW_BIT; \ ++} while(0) ++ ++/* TV Encoder Chrominance filter and Modulation register ops */ ++/* n = 0 ~ (2^32-1) */ ++#define __tve_set_c_sub_carrier_freq(n) REG_TVE_CFREQ = (n) ++/* n = 0 ~ 255 */ ++#define __tve_set_c_sub_carrier_init_phase(n) \ ++do { \ ++ REG_TVE_CPHASE &= ~TVE_CPHASE_INITPH_MASK; \ ++ REG_TVE_CPHASE |= (n) << TVE_CPHASE_INITPH_BIT; \ ++} while(0) ++/* n = 0 ~ 255 */ ++#define __tve_set_c_sub_carrier_act_phase(n) \ ++do { \ ++ REG_TVE_CPHASE &= ~TVE_CPHASE_ACTPH_MASK; \ ++ REG_TVE_CPHASE |= (n) << TVE_CPHASE_ACTPH_BIT; \ ++} while(0) ++/* n = 0 ~ 255 */ ++#define __tve_set_c_phase_rst_period(n) \ ++do { \ ++ REG_TVE_CPHASE &= ~TVE_CPHASE_CCRSTP_MASK; \ ++ REG_TVE_CPHASE |= (n) << TVE_CPHASE_CCRSTP_BIT; \ ++} while(0) ++/* n = 0 ~ 255 */ ++#define __tve_set_cb_burst_amp(n) \ ++do { \ ++ REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CBBA_MASK; \ ++ REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CBBA_BIT; \ ++} while(0) ++/* n = 0 ~ 255 */ ++#define __tve_set_cr_burst_amp(n) \ ++do { \ ++ REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CRBA_MASK; \ ++ REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CRBA_BIT; \ ++} while(0) ++/* n = 0 ~ 255 */ ++#define __tve_set_cb_gain_amp(n) \ ++do { \ ++ REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CBGAIN_MASK; \ ++ REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CBGAIN_BIT; \ ++} while(0) ++/* n = 0 ~ 255 */ ++#define __tve_set_cr_gain_amp(n) \ ++do { \ ++ REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CRGAIN_MASK; \ ++ REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CRGAIN_BIT; \ ++} while(0) ++ ++/* TV Encoder Wide Screen Signal Control register ops */ ++/* n = 0 ~ 7 */ ++#define __tve_set_notch_freq(n) \ ++do { \ ++ REG_TVE_WSSCR &= ~TVE_WSSCR_NCHFREQ_MASK; \ ++ REG_TVE_WSSCR |= (n) << TVE_WSSCR_NCHFREQ_BIT; \ ++} while(0) ++/* n = 0 ~ 7 */ ++#define __tve_set_notch_width() (REG_TVE_WSSCR |= TVE_WSSCR_NCHW_BIT) ++#define __tve_clear_notch_width() (REG_TVE_WSSCR &= ~TVE_WSSCR_NCHW_BIT) ++#define __tve_enable_notch() (REG_TVE_WSSCR |= TVE_WSSCR_ENCH_BIT) ++#define __tve_disable_notch() (REG_TVE_WSSCR &= ~TVE_WSSCR_ENCH_BIT) ++/* n = 0 ~ 7 */ ++#define __tve_set_wss_edge(n) \ ++do { \ ++ REG_TVE_WSSCR &= ~TVE_WSSCR_WSSEDGE_MASK; \ ++ REG_TVE_WSSCR |= (n) << TVE_WSSCR_WSSEDGE_BIT; \ ++} while(0) ++#define __tve_set_wss_clkbyp() (REG_TVE_WSSCR |= TVE_WSSCR_WSSCKBP_BIT) ++#define __tve_set_wss_type() (REG_TVE_WSSCR |= TVE_WSSCR_WSSTP_BIT) ++#define __tve_enable_wssf1() (REG_TVE_WSSCR |= TVE_WSSCR_EWSS1_BIT) ++#define __tve_enable_wssf0() (REG_TVE_WSSCR |= TVE_WSSCR_EWSS0_BIT) ++ ++/* TV Encoder Wide Screen Signal Configure register 1, 2 and 3 ops */ ++/* n = 0 ~ 1023 */ ++#define __tve_set_wss_level(n) \ ++do { \ ++ REG_TVE_WSSCFG1 &= ~TVE_WSSCFG1_WSSL_MASK; \ ++ REG_TVE_WSSCFG1 |= (n) << TVE_WSSCFG1_WSSL_BIT; \ ++} while(0) ++/* n = 0 ~ 4095 */ ++#define __tve_set_wss_freq(n) \ ++do { \ ++ REG_TVE_WSSCFG1 &= ~TVE_WSSCFG1_WSSFREQ_MASK; \ ++ REG_TVE_WSSCFG1 |= (n) << TVE_WSSCFG1_WSSFREQ_BIT; \ ++} while(0) ++/* n = 0, 1; l = 0 ~ 255 */ ++#define __tve_set_wss_line(n,v) \ ++do { \ ++ REG_TVE_WSSCFG##n &= ~TVE_WSSCFG_WSSLINE_MASK; \ ++ REG_TVE_WSSCFG##n |= (v) << TVE_WSSCFG_WSSLINE_BIT; \ ++} while(0) ++/* n = 0, 1; d = 0 ~ (2^20-1) */ ++#define __tve_set_wss_data(n, v) \ ++do { \ ++ REG_TVE_WSSCFG##n &= ~TVE_WSSCFG_WSSLINE_MASK; \ ++ REG_TVE_WSSCFG##n |= (v) << TVE_WSSCFG_WSSLINE_BIT; \ ++} while(0) ++ ++/*************************************************************************** ++ * RTC ops ++ ***************************************************************************/ ++ ++#define __rtc_write_ready() ( (REG_RTC_RCR & RTC_RCR_WRDY) >> RTC_RCR_WRDY_BIT ) ++#define __rtc_enabled() ( REG_RTC_RCR |= RTC_RCR_RTCE ) ++#define __rtc_disabled() ( REG_RTC_RCR &= ~RTC_RCR_RTCE ) ++#define __rtc_enable_alarm() ( REG_RTC_RCR |= RTC_RCR_AE ) ++#define __rtc_disable_alarm() ( REG_RTC_RCR &= ~RTC_RCR_AE ) ++#define __rtc_enable_alarm_irq() ( REG_RTC_RCR |= RTC_RCR_AIE ) ++#define __rtc_disable_alarm_irq() ( REG_RTC_RCR &= ~RTC_RCR_AIE ) ++#define __rtc_enable_1Hz_irq() ( REG_RTC_RCR |= RTC_RCR_1HZIE ) ++#define __rtc_disable_1Hz_irq() ( REG_RTC_RCR &= ~RTC_RCR_1HZIE ) ++ ++#define __rtc_get_1Hz_flag() ( (REG_RTC_RCR >> RTC_RCR_1HZ_BIT) & 0x1 ) ++#define __rtc_clear_1Hz_flag() ( REG_RTC_RCR &= ~RTC_RCR_1HZ ) ++#define __rtc_get_alarm_flag() ( (REG_RTC_RCR >> RTC_RCR_AF_BIT) & 0x1 ) ++#define __rtc_clear_alarm_flag() ( REG_RTC_RCR &= ~RTC_RCR_AF ) ++ ++#define __rtc_get_second() ( REG_RTC_RSR ) ++#define __rtc_set_second(v) ( REG_RTC_RSR = v ) ++ ++#define __rtc_get_alarm_second() ( REG_RTC_RSAR ) ++#define __rtc_set_alarm_second(v) ( REG_RTC_RSAR = v ) ++ ++#define __rtc_RGR_is_locked() ( (REG_RTC_RGR >> RTC_RGR_LOCK) ) ++#define __rtc_lock_RGR() ( REG_RTC_RGR |= RTC_RGR_LOCK ) ++#define __rtc_unlock_RGR() ( REG_RTC_RGR &= ~RTC_RGR_LOCK ) ++#define __rtc_get_adjc_val() ( (REG_RTC_RGR & RTC_RGR_ADJC_MASK) >> RTC_RGR_ADJC_BIT ) ++#define __rtc_set_adjc_val(v) \ ++ ( REG_RTC_RGR = ( (REG_RTC_RGR & ~RTC_RGR_ADJC_MASK) | (v << RTC_RGR_ADJC_BIT) )) ++#define __rtc_get_nc1Hz_val() ( (REG_RTC_RGR & RTC_RGR_NC1HZ_MASK) >> RTC_RGR_NC1HZ_BIT ) ++#define __rtc_set_nc1Hz_val(v) \ ++ ( REG_RTC_RGR = ( (REG_RTC_RGR & ~RTC_RGR_NC1HZ_MASK) | (v << RTC_RGR_NC1HZ_BIT) )) ++ ++#define __rtc_power_down() ( REG_RTC_HCR |= RTC_HCR_PD ) ++ ++#define __rtc_get_hwfcr_val() ( REG_RTC_HWFCR & RTC_HWFCR_MASK ) ++#define __rtc_set_hwfcr_val(v) ( REG_RTC_HWFCR = (v) & RTC_HWFCR_MASK ) ++#define __rtc_get_hrcr_val() ( REG_RTC_HRCR & RTC_HRCR_MASK ) ++#define __rtc_set_hrcr_val(v) ( REG_RTC_HRCR = (v) & RTC_HRCR_MASK ) ++ ++#define __rtc_enable_alarm_wakeup() ( REG_RTC_HWCR |= RTC_HWCR_EALM ) ++#define __rtc_disable_alarm_wakeup() ( REG_RTC_HWCR &= ~RTC_HWCR_EALM ) ++ ++#define __rtc_status_hib_reset_occur() ( (REG_RTC_HWRSR >> RTC_HWRSR_HR) & 0x1 ) ++#define __rtc_status_ppr_reset_occur() ( (REG_RTC_HWRSR >> RTC_HWRSR_PPR) & 0x1 ) ++#define __rtc_status_wakeup_pin_waken_up() ( (REG_RTC_HWRSR >> RTC_HWRSR_PIN) & 0x1 ) ++#define __rtc_status_alarm_waken_up() ( (REG_RTC_HWRSR >> RTC_HWRSR_ALM) & 0x1 ) ++#define __rtc_clear_hib_stat_all() ( REG_RTC_HWRSR = 0 ) ++ ++#define __rtc_get_scratch_pattern() (REG_RTC_HSPR) ++#define __rtc_set_scratch_pattern(n) (REG_RTC_HSPR = n ) ++ ++/************************************************************************* ++ * BCH ++ *************************************************************************/ ++#define __ecc_encoding_4bit() \ ++do { \ ++ REG_BCH_CRS = BCH_CR_ENCE | BCH_CR_BRST | BCH_CR_BCHE; \ ++ REG_BCH_CRC = BCH_CR_BSEL8; \ ++} while(0) ++#define __ecc_decoding_4bit() \ ++do { \ ++ REG_BCH_CRS = BCH_CR_BRST | BCH_CR_BCHE; \ ++ REG_BCH_CRC = BCH_CR_ENCE | BCH_CR_BSEL8; \ ++} while(0) ++#define __ecc_encoding_8bit() \ ++do { \ ++ REG_BCH_CRS = BCH_CR_ENCE | BCH_CR_BRST | BCH_CR_BSEL8 | BCH_CR_BCHE; \ ++} while(0) ++#define __ecc_decoding_8bit() \ ++do { \ ++ REG_BCH_CRS = BCH_CR_BRST | BCH_CR_BSEL8 | BCH_CR_BCHE; \ ++ REG_BCH_CRC = BCH_CR_ENCE; \ ++} while(0) ++#define __ecc_dma_enable() ( REG_BCH_CRS = BCH_CR_DMAE ) ++#define __ecc_disable() ( REG_BCH_CRC = BCH_CR_BCHE ) ++#define __ecc_encode_sync() while (!(REG_BCH_INTS & BCH_INTS_ENCF)) ++#define __ecc_decode_sync() while (!(REG_BCH_INTS & BCH_INTS_DECF)) ++#define __ecc_cnt_dec(n) \ ++do { \ ++ REG_BCH_CNT &= ~(BCH_CNT_DEC_MASK << BCH_CNT_DEC_BIT); \ ++ REG_BCH_CNT = (n) << BCH_CNT_DEC_BIT; \ ++} while(0) ++#define __ecc_cnt_enc(n) \ ++do { \ ++ REG_BCH_CNT &= ~(BCH_CNT_ENC_MASK << BCH_CNT_ENC_BIT); \ ++ REG_BCH_CNT = (n) << BCH_CNT_ENC_BIT; \ ++} while(0) ++ ++/*************************************************************************** ++ * OWI (one-wire bus) ops ++ ***************************************************************************/ ++ ++/* OW control register ops */ ++#define __owi_enable_all_interrupts() ( REG_OWI_CTL = (OWI_CTL_EBYTE | OWI_CTL_EBIT | OWI_CTL_ERST) ) ++#define __owi_disable_all_interrupts() ( REG_OWI_CTL = 0 ) ++ ++#define __owi_enable_byte_interrupt() ( REG_OWI_CTL |= OWI_CTL_EBYTE ) ++#define __owi_disable_byte_interrupt() ( REG_OWI_CTL &= ~OWI_CTL_EBYTE ) ++#define __owi_enable_bit_interrupt() ( REG_OWI_CTL |= OWI_CTL_EBIT ) ++#define __owi_disable_bit_interrupt() ( REG_OWI_CTL &= ~OWI_CTL_EBIT ) ++#define __owi_enable_rst_interrupt() ( REG_OWI_CTL |= OWI_CTL_ERST ) ++#define __owi_disable_rst_interrupt() ( REG_OWI_CTL &=~OWI_CTL_ERST ) ++ ++/* OW configure register ops */ ++#define __owi_select_regular_mode() ( REG_OWI_CFG &= ~OWI_CFG_MODE ) ++#define __owi_select_overdrive_mode() ( REG_OWI_CFG |= OWI_CFG_MODE ) ++ ++#define __owi_set_rddata() ( REG_OWI_CFG |= OWI_CFG_RDDATA ) ++#define __owi_clr_rddata() ( REG_OWI_CFG &= ~OWI_CFG_RDDATA ) ++#define __owi_get_rddata() ( REG_OWI_CFG & OWI_CFG_RDDATA ) ++ ++#define __owi_set_wrdata() ( REG_OWI_CFG |= OWI_CFG_WRDATA ) ++#define __owi_clr_wrdata() ( REG_OWI_CFG &= ~OWI_CFG_WRDATA ) ++#define __owi_get_wrdata() ( REG_OWI_CFG & OWI_CFG_WRDATA ) ++ ++#define __owi_get_rdst() ( REG_OWI_CFG & OWI_CFG_RDST ) ++ ++#define __owi_set_wr1rd() ( REG_OWI_CFG |= OWI_CFG_WR1RD ) ++#define __owi_clr_wr1rd() ( REG_OWI_CFG &= ~OWI_CFG_WR1RD ) ++#define __owi_get_wr1rd() ( REG_OWI_CFG & OWI_CFG_WR1RD ) ++ ++#define __owi_set_wr0() ( REG_OWI_CFG |= OWI_CFG_WR0 ) ++#define __owi_clr_wr0() ( REG_OWI_CFG &= ~OWI_CFG_WR0 ) ++#define __owi_get_wr0() ( REG_OWI_CFG & OWI_CFG_WR0 ) ++ ++#define __owi_set_rst() ( REG_OWI_CFG |= OWI_CFG_RST ) ++#define __owi_clr_rst() ( REG_OWI_CFG &= ~OWI_CFG_RST ) ++#define __owi_get_rst() ( REG_OWI_CFG & OWI_CFG_RST ) ++ ++#define __owi_enable_ow_ops() ( REG_OWI_CFG |= OWI_CFG_ENA ) ++#define __owi_disable_ow_ops() ( REG_OWI_CFG &= ~OWI_CFG_ENA ) ++#define __owi_get_enable() ( REG_OWI_CFG & OWI_CFG_ENA ) ++ ++#define __owi_wait_ops_rdy() \ ++ do { \ ++ while(__owi_get_enable()); \ ++ udelay(1); \ ++ } while(0); ++ ++/* OW status register ops */ ++#define __owi_clr_sts() ( REG_OWI_STS = 0 ) ++#define __owi_get_sts_pst() ( REG_OWI_STS & OWI_STS_PST ) ++#define __owi_get_sts_byte_rdy() ( REG_OWI_STS & OWI_STS_BYTE_RDY ) ++#define __owi_get_sts_bit_rdy() ( REG_OWI_STS & OWI_STS_BIT_RDY ) ++#define __owi_get_sts_pst_rdy() ( REG_OWI_STS & OWI_STS_PST_RDY ) ++ ++/************************************************************************* ++ * TSSI MPEG 2-TS slave interface operation ++ *************************************************************************/ ++#define __tssi_enable() ( REG_TSSI_ENA |= TSSI_ENA_ENA ) ++#define __tssi_disable() ( REG_TSSI_ENA &= ~TSSI_ENA_ENA ) ++#define __tssi_soft_reset() ( REG_TSSI_ENA |= TSSI_ENA_SFT_RST ) ++#define __tssi_dma_enable() ( REG_TSSI_ENA |= TSSI_ENA_DMA_EN ) ++#define __tssi_dma_disable() ( REG_TSSI_ENA &= ~TSSI_ENA_DMA_EN ) ++#define __tssi_filter_enable() ( REG_TSSI_ENA |= TSSI_ENA_PID_EN ) ++#define __tssi_filter_disable() ( REG_TSSI_ENA &= ~TSSI_ENA_PID_EN ) ++ ++/* n = 4, 8, 16 */ ++#define __tssi_set_tigger_num(n) \ ++ do { \ ++ REG_TSSI_CFG &= ~TSSI_CFG_TRIG_MASK; \ ++ REG_TSSI_CFG |= TSSI_CFG_TRIG_##n; \ ++ } while (0) ++ ++#define __tssi_set_wd_1() ( REG_TSSI_CFG |= TSSI_CFG_END_WD ) ++#define __tssi_set_wd_0() ( REG_TSSI_CFG &= ~TSSI_CFG_END_WD ) ++ ++#define __tssi_set_bt_1() ( REG_TSSI_CFG |= TSSI_CFG_END_BD ) ++#define __tssi_set_bt_0() ( REG_TSSI_CFG &= ~TSSI_CFG_END_BD ) ++ ++#define __tssi_set_data_pola_high() ( REG_TSSI_CFG |= TSSI_CFG_TSDI_H ) ++#define __tssi_set_data_pola_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSDI_H ) ++ ++#define __tssi_set_data_use_data0() ( REG_TSSI_CFG |= TSSI_CFG_USE_0 ) ++#define __tssi_set_data_use_data7() ( REG_TSSI_CFG &= ~TSSI_CFG_USE_0 ) ++ ++#define __tssi_select_clk_fast() ( REG_TSSI_CFG &= ~TSSI_CFG_TSCLK_CH ) ++#define __tssi_select_clk_slow() ( REG_TSSI_CFG |= TSSI_CFG_TSCLK_CH ) ++ ++#define __tssi_select_serail_mode() ( REG_TSSI_CFG &= ~TSSI_CFG_PARAL ) ++#define __tssi_select_paral_mode() ( REG_TSSI_CFG |= TSSI_CFG_PARAL ) ++ ++#define __tssi_select_clk_nega_edge() ( REG_TSSI_CFG &= ~TSSI_CFG_TSCLK_P ) ++#define __tssi_select_clk_posi_edge() ( REG_TSSI_CFG |= TSSI_CFG_TSCLK_P ) ++ ++#define __tssi_select_frm_act_high() ( REG_TSSI_CFG |= TSSI_CFG_TSFRM_H ) ++#define __tssi_select_frm_act_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSFRM_H ) ++ ++#define __tssi_select_str_act_high() ( REG_TSSI_CFG |= TSSI_CFG_TSSTR_H ) ++#define __tssi_select_str_act_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSSTR_H ) ++ ++#define __tssi_select_fail_act_high() ( REG_TSSI_CFG |= TSSI_CFG_TSFAIL_H ) ++#define __tssi_select_fail_act_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSFAIL_H ) ++ ++#define __tssi_enable_ovrn_irq() ( REG_TSSI_CTRL &= ~TSSI_CTRL_OVRNM ) ++#define __tssi_disable_ovrn_irq() ( REG_TSSI_CTRL |= TSSI_CTRL_OVRNM ) ++ ++#define __tssi_enable_trig_irq() ( REG_TSSI_CTRL &= ~TSSI_CTRL_TRIGM ) ++#define __tssi_disable_trig_irq() ( REG_TSSI_CTRL |= TSSI_CTRL_TRIGM ) ++ ++#define __tssi_state_is_overrun() ( REG_TSSI_STAT & TSSI_STAT_OVRN ) ++#define __tssi_state_trigger_meet() ( REG_TSSI_STAT & TSSI_STAT_TRIG ) ++#define __tssi_clear_state() ( REG_TSSI_STAT = 0 ) /* write 0??? */ ++#define __tssi_state_clear_overrun() ( REG_TSSI_STAT = TSSI_STAT_OVRN ) ++ ++#define __tssi_enable_filte_pid0() ( REG_TSSI_PEN |= TSSI_PEN_PID0 ) ++#define __tssi_disable_filte_pid0() ( REG_TSSI_PEN &= ~TSSI_PEN_PID0 ) ++ ++/* m = 0, ..., 15 */ ++#define __tssi_enable_pid_filter(m) \ ++ do { \ ++ int n = (m); \ ++ if ( n>=0 && n <(TSSI_PID_MAX*2) ) { \ ++ if ( n >= TSSI_PID_MAX ) n += 8; \ ++ REG_TSSI_PEN |= ( 1 << n ); \ ++ } \ ++ } while (0) ++ ++/* m = 0, ..., 15 */ ++#define __tssi_disable_pid_filter(m) \ ++ do { \ ++ int n = (m); \ ++ if ( n>=0 && n <(TSSI_PID_MAX*2) ) { \ ++ if ( n >= TSSI_PID_MAX ) n += 8; \ ++ REG_TSSI_PEN &= ~( 1 << n ); \ ++ } \ ++ } while (0) ++ ++/* n = 0, ..., 7 */ ++#define __tssi_set_pid0(n, pid0) \ ++ do { \ ++ REG_TSSI_PID(n) &= ~TSSI_PID_PID0_MASK; \ ++ REG_TSSI_PID(n) |= ((pid0)<=0 && n < TSSI_PID_MAX*2) { \ ++ if ( n < TSSI_PID_MAX ) \ ++ __tssi_set_pid0(n, pid); \ ++ else \ ++ __tssi_set_pid1(n-TSSI_PID_MAX, pid); \ ++ } \ ++ }while (0) ++ ++ ++#if 0 ++/************************************************************************* ++ * IPU (Image Processing Unit) ++ *************************************************************************/ ++#define u32 volatile unsigned long ++ ++#define write_reg(reg, val) \ ++do { \ ++ *(u32 *)(reg) = (val); \ ++} while(0) ++ ++#define read_reg(reg, off) (*(u32 *)((reg)+(off))) ++ ++ ++#define set_ipu_fmt(rgb_888_out_fmt, rgb_out_oft, out_fmt, yuv_pkg_out, in_oft, in_fmt ) \ ++({ write_reg( (IPU_V_BASE + REG_D_FMT), ((in_fmt) & IN_FMT_MSK)< Unsigned toggle enable */ ++#define AIC_CR_FLUSH (1 << 8) /* Flush FIFO */ ++#define AIC_CR_EROR (1 << 6) /* Enable ROR interrupt */ ++#define AIC_CR_ETUR (1 << 5) /* Enable TUR interrupt */ ++#define AIC_CR_ERFS (1 << 4) /* Enable RFS interrupt */ ++#define AIC_CR_ETFS (1 << 3) /* Enable TFS interrupt */ ++#define AIC_CR_ENLBF (1 << 2) /* Enable Loopback Function */ ++#define AIC_CR_ERPL (1 << 1) /* Enable Playback Function */ ++#define AIC_CR_EREC (1 << 0) /* Enable Record Function */ ++ ++/* AIC Controller AC-link Control Register 1 (AIC_ACCR1) */ ++ ++#define AIC_ACCR1_RS_BIT 16 /* Receive Valid Slots */ ++#define AIC_ACCR1_RS_MASK (0x3ff << AIC_ACCR1_RS_BIT) ++ #define AIC_ACCR1_RS_SLOT12 (1 << 25) /* Slot 12 valid bit */ ++ #define AIC_ACCR1_RS_SLOT11 (1 << 24) /* Slot 11 valid bit */ ++ #define AIC_ACCR1_RS_SLOT10 (1 << 23) /* Slot 10 valid bit */ ++ #define AIC_ACCR1_RS_SLOT9 (1 << 22) /* Slot 9 valid bit, LFE */ ++ #define AIC_ACCR1_RS_SLOT8 (1 << 21) /* Slot 8 valid bit, Surround Right */ ++ #define AIC_ACCR1_RS_SLOT7 (1 << 20) /* Slot 7 valid bit, Surround Left */ ++ #define AIC_ACCR1_RS_SLOT6 (1 << 19) /* Slot 6 valid bit, PCM Center */ ++ #define AIC_ACCR1_RS_SLOT5 (1 << 18) /* Slot 5 valid bit */ ++ #define AIC_ACCR1_RS_SLOT4 (1 << 17) /* Slot 4 valid bit, PCM Right */ ++ #define AIC_ACCR1_RS_SLOT3 (1 << 16) /* Slot 3 valid bit, PCM Left */ ++#define AIC_ACCR1_XS_BIT 0 /* Transmit Valid Slots */ ++#define AIC_ACCR1_XS_MASK (0x3ff << AIC_ACCR1_XS_BIT) ++ #define AIC_ACCR1_XS_SLOT12 (1 << 9) /* Slot 12 valid bit */ ++ #define AIC_ACCR1_XS_SLOT11 (1 << 8) /* Slot 11 valid bit */ ++ #define AIC_ACCR1_XS_SLOT10 (1 << 7) /* Slot 10 valid bit */ ++ #define AIC_ACCR1_XS_SLOT9 (1 << 6) /* Slot 9 valid bit, LFE */ ++ #define AIC_ACCR1_XS_SLOT8 (1 << 5) /* Slot 8 valid bit, Surround Right */ ++ #define AIC_ACCR1_XS_SLOT7 (1 << 4) /* Slot 7 valid bit, Surround Left */ ++ #define AIC_ACCR1_XS_SLOT6 (1 << 3) /* Slot 6 valid bit, PCM Center */ ++ #define AIC_ACCR1_XS_SLOT5 (1 << 2) /* Slot 5 valid bit */ ++ #define AIC_ACCR1_XS_SLOT4 (1 << 1) /* Slot 4 valid bit, PCM Right */ ++ #define AIC_ACCR1_XS_SLOT3 (1 << 0) /* Slot 3 valid bit, PCM Left */ ++ ++/* AIC Controller AC-link Control Register 2 (AIC_ACCR2) */ ++ ++#define AIC_ACCR2_ERSTO (1 << 18) /* Enable RSTO interrupt */ ++#define AIC_ACCR2_ESADR (1 << 17) /* Enable SADR interrupt */ ++#define AIC_ACCR2_ECADT (1 << 16) /* Enable CADT interrupt */ ++#define AIC_ACCR2_OASS_BIT 8 /* Output Sample Size for AC-link */ ++#define AIC_ACCR2_OASS_MASK (0x3 << AIC_ACCR2_OASS_BIT) ++ #define AIC_ACCR2_OASS_20BIT (0 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 20-bit */ ++ #define AIC_ACCR2_OASS_18BIT (1 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 18-bit */ ++ #define AIC_ACCR2_OASS_16BIT (2 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 16-bit */ ++ #define AIC_ACCR2_OASS_8BIT (3 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 8-bit */ ++#define AIC_ACCR2_IASS_BIT 6 /* Output Sample Size for AC-link */ ++#define AIC_ACCR2_IASS_MASK (0x3 << AIC_ACCR2_IASS_BIT) ++ #define AIC_ACCR2_IASS_20BIT (0 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 20-bit */ ++ #define AIC_ACCR2_IASS_18BIT (1 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 18-bit */ ++ #define AIC_ACCR2_IASS_16BIT (2 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 16-bit */ ++ #define AIC_ACCR2_IASS_8BIT (3 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 8-bit */ ++#define AIC_ACCR2_SO (1 << 3) /* SDATA_OUT output value */ ++#define AIC_ACCR2_SR (1 << 2) /* RESET# pin level */ ++#define AIC_ACCR2_SS (1 << 1) /* SYNC pin level */ ++#define AIC_ACCR2_SA (1 << 0) /* SYNC and SDATA_OUT alternation */ ++ ++/* AIC Controller I2S/MSB-justified Control Register (AIC_I2SCR) */ ++ ++#define AIC_I2SCR_STPBK (1 << 12) /* Stop BIT_CLK for I2S/MSB-justified */ ++#define AIC_I2SCR_WL_BIT 1 /* Input/Output Sample Size for I2S/MSB-justified */ ++#define AIC_I2SCR_WL_MASK (0x7 << AIC_I2SCR_WL_BIT) ++ #define AIC_I2SCR_WL_24BIT (0 << AIC_I2SCR_WL_BIT) /* Word Length is 24 bit */ ++ #define AIC_I2SCR_WL_20BIT (1 << AIC_I2SCR_WL_BIT) /* Word Length is 20 bit */ ++ #define AIC_I2SCR_WL_18BIT (2 << AIC_I2SCR_WL_BIT) /* Word Length is 18 bit */ ++ #define AIC_I2SCR_WL_16BIT (3 << AIC_I2SCR_WL_BIT) /* Word Length is 16 bit */ ++ #define AIC_I2SCR_WL_8BIT (4 << AIC_I2SCR_WL_BIT) /* Word Length is 8 bit */ ++#define AIC_I2SCR_AMSL (1 << 0) /* 0:I2S, 1:MSB-justified */ ++ ++/* AIC Controller FIFO Status Register (AIC_SR) */ ++ ++#define AIC_SR_RFL_BIT 24 /* Receive FIFO Level */ ++#define AIC_SR_RFL_MASK (0x3f << AIC_SR_RFL_BIT) ++#define AIC_SR_TFL_BIT 8 /* Transmit FIFO level */ ++#define AIC_SR_TFL_MASK (0x3f << AIC_SR_TFL_BIT) ++#define AIC_SR_ROR (1 << 6) /* Receive FIFO Overrun */ ++#define AIC_SR_TUR (1 << 5) /* Transmit FIFO Underrun */ ++#define AIC_SR_RFS (1 << 4) /* Receive FIFO Service Request */ ++#define AIC_SR_TFS (1 << 3) /* Transmit FIFO Service Request */ ++ ++/* AIC Controller AC-link Status Register (AIC_ACSR) */ ++ ++#define AIC_ACSR_SLTERR (1 << 21) /* Slot Error Flag */ ++#define AIC_ACSR_CRDY (1 << 20) /* External CODEC Ready Flag */ ++#define AIC_ACSR_CLPM (1 << 19) /* External CODEC low power mode flag */ ++#define AIC_ACSR_RSTO (1 << 18) /* External CODEC regs read status timeout */ ++#define AIC_ACSR_SADR (1 << 17) /* External CODEC regs status addr and data received */ ++#define AIC_ACSR_CADT (1 << 16) /* Command Address and Data Transmitted */ ++ ++/* AIC Controller I2S/MSB-justified Status Register (AIC_I2SSR) */ ++ ++#define AIC_I2SSR_BSY (1 << 2) /* AIC Busy in I2S/MSB-justified format */ ++ ++/* AIC Controller AC97 codec Command Address Register (AIC_ACCAR) */ ++ ++#define AIC_ACCAR_CAR_BIT 0 ++#define AIC_ACCAR_CAR_MASK (0xfffff << AIC_ACCAR_CAR_BIT) ++ ++/* AIC Controller AC97 codec Command Data Register (AIC_ACCDR) */ ++ ++#define AIC_ACCDR_CDR_BIT 0 ++#define AIC_ACCDR_CDR_MASK (0xfffff << AIC_ACCDR_CDR_BIT) ++ ++/* AIC Controller AC97 codec Status Address Register (AIC_ACSAR) */ ++ ++#define AIC_ACSAR_SAR_BIT 0 ++#define AIC_ACSAR_SAR_MASK (0xfffff << AIC_ACSAR_SAR_BIT) ++ ++/* AIC Controller AC97 codec Status Data Register (AIC_ACSDR) */ ++ ++#define AIC_ACSDR_SDR_BIT 0 ++#define AIC_ACSDR_SDR_MASK (0xfffff << AIC_ACSDR_SDR_BIT) ++ ++/* AIC Controller I2S/MSB-justified Clock Divider Register (AIC_I2SDIV) */ ++ ++#define AIC_I2SDIV_DIV_BIT 0 ++#define AIC_I2SDIV_DIV_MASK (0x7f << AIC_I2SDIV_DIV_BIT) ++ #define AIC_I2SDIV_BITCLK_3072KHZ (0x0C << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 3.072MHz */ ++ #define AIC_I2SDIV_BITCLK_2836KHZ (0x0D << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 2.836MHz */ ++ #define AIC_I2SDIV_BITCLK_1418KHZ (0x1A << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.418MHz */ ++ #define AIC_I2SDIV_BITCLK_1024KHZ (0x24 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.024MHz */ ++ #define AIC_I2SDIV_BITCLK_7089KHZ (0x34 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 708.92KHz */ ++ #define AIC_I2SDIV_BITCLK_512KHZ (0x48 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 512.00KHz */ ++ ++ ++/************************************************************************* ++ * ICDC (Internal CODEC) ++ *************************************************************************/ ++ ++#define ICDC_CKCFG (ICDC_BASE + 0x00a0) /* Clock Configure Register */ ++#define ICDC_RGADW (ICDC_BASE + 0x00a4) /* internal register access control */ ++#define ICDC_RGDATA (ICDC_BASE + 0x00a8) /* internal register data output */ ++ ++#define REG_ICDC_CKCFG REG32(ICDC_CKCFG) ++#define REG_ICDC_RGADW REG32(ICDC_RGADW) ++#define REG_ICDC_RGDATA REG32(ICDC_RGDATA) ++ ++/* ICDC Clock Configure Register */ ++#define ICDC_CKCFG_CKRDY (1 << 1) ++#define ICDC_CKCFG_SELAD (1 << 0) ++ ++/* ICDC internal register access control Register */ ++#define ICDC_RGADW_RGWR (1 << 16) ++#define ICDC_RGADW_RGADDR_BIT 8 ++#define ICDC_RGADW_RGADDR_MASK (0x7f << ICDC_RGADW_RGADDR_BIT) ++#define ICDC_RGADW_RGDIN_BIT 0 ++#define ICDC_RGADW_RGDIN_MASK (0xff << ICDC_RGADW_RGDIN_BIT) ++ ++/* ICDC internal register data output Register */ ++#define ICDC_RGDATA_IRQ (1 << 8) ++#define ICDC_RGDATA_RGDOUT_BIT 0 ++#define ICDC_RGDATA_RGDOUT_MASK (0xff << ICDC_RGDATA_RGDOUT_BIT) ++ ++/************************************************************************* ++ * PCM Controller ++ *************************************************************************/ ++ ++#define PCM_CTL (PCM_BASE + 0x000) ++#define PCM_CFG (PCM_BASE + 0x004) ++#define PCM_DP (PCM_BASE + 0x008) ++#define PCM_INTC (PCM_BASE + 0x00c) ++#define PCM_INTS (PCM_BASE + 0x010) ++#define PCM_DIV (PCM_BASE + 0x014) ++ ++#define REG_PCM_CTL REG32(PCM_CTL) ++#define REG_PCM_CFG REG32(PCM_CFG) ++#define REG_PCM_DP REG32(PCM_DP) ++#define REG_PCM_INTC REG32(PCM_INTC) ++#define REG_PCM_INTS REG32(PCM_INTS) ++#define REG_PCM_DIV REG32(PCM_DIV) ++ ++/* PCM Controller control Register (PCM_CTL) */ ++ ++#define PCM_CTL_ERDMA (1 << 9) /* Enable Receive DMA */ ++#define PCM_CTL_ETDMA (1 << 8) /* Enable Transmit DMA */ ++#define PCM_CTL_LSMP (1 << 7) /* Play Zero sample or last sample */ ++#define PCM_CTL_ERPL (1 << 6) /* Enable Playing Back Function */ ++#define PCM_CTL_EREC (1 << 5) /* Enable Recording Function */ ++#define PCM_CTL_FLUSH (1 << 4) /* FIFO flush */ ++#define PCM_CTL_RST (1 << 3) /* Reset PCM */ ++#define PCM_CTL_CLKEN (1 << 1) /* Enable the clock division logic */ ++#define PCM_CTL_PCMEN (1 << 0) /* Enable PCM module */ ++ ++/* PCM Controller configure Register (PCM_CFG) */ ++ ++#define PCM_CFG_SLOT_BIT 13 ++#define PCM_CFG_SLOT_MASK (0x3 << PCM_CFG_SLOT_BIT) ++ #define PCM_CFG_SLOT_0 (0 << PCM_CFG_SLOT_BIT) /* Slot is 0 */ ++ #define PCM_CFG_SLOT_1 (1 << PCM_CFG_SLOT_BIT) /* Slot is 1 */ ++ #define PCM_CFG_SLOT_2 (2 << PCM_CFG_SLOT_BIT) /* Slot is 2 */ ++ #define PCM_CFG_SLOT_3 (3 << PCM_CFG_SLOT_BIT) /* Slot is 3 */ ++#define PCM_CFG_ISS_BIT 12 ++#define PCM_CFG_ISS_MASK (0x1 << PCM_CFG_ISS_BIT) ++ #define PCM_CFG_ISS_8 (0 << PCM_CFG_ISS_BIT) ++ #define PCM_CFG_ISS_16 (1 << PCM_CFG_ISS_BIT) ++#define PCM_CFG_OSS_BIT 11 ++#define PCM_CFG_OSS_MASK (0x1 << PCM_CFG_OSS_BIT) ++ #define PCM_CFG_OSS_8 (0 << PCM_CFG_OSS_BIT) ++ #define PCM_CFG_OSS_16 (1 << PCM_CFG_OSS_BIT) ++#define PCM_CFG_IMSBPOS (1 << 10) ++#define PCM_CFG_OMSBPOS (1 << 9) ++#define PCM_CFG_RFTH_BIT 5 /* Receive FIFO Threshold */ ++#define PCM_CFG_RFTH_MASK (0xf << PCM_CFG_RFTH_BIT) ++#define PCM_CFG_TFTH_BIT 1 /* Transmit FIFO Threshold */ ++#define PCM_CFG_TFTH_MASK (0xf << PCM_CFG_TFTH_BIT) ++#define PCM_CFG_MODE (0x0 << 0) ++ ++/* PCM Controller interrupt control Register (PCM_INTC) */ ++ ++#define PCM_INTC_ETFS (1 << 3) ++#define PCM_INTC_ETUR (1 << 2) ++#define PCM_INTC_ERFS (1 << 1) ++#define PCM_INTC_EROR (1 << 0) ++ ++/* PCM Controller interrupt status Register (PCM_INTS) */ ++ ++#define PCM_INTS_RSTS (1 << 14) /* Reset or flush has not complete */ ++#define PCM_INTS_TFL_BIT 9 ++#define PCM_INTS_TFL_MASK (0x1f << PCM_INTS_TFL_BIT) ++#define PCM_INTS_TFS (1 << 8) /* Tranmit FIFO Service Request */ ++#define PCM_INTS_TUR (1 << 7) /* Transmit FIFO Under Run */ ++#define PCM_INTS_RFL_BIT 2 ++#define PCM_INTS_RFL_MASK (0x1f << PCM_INTS_RFL_BIT) ++#define PCM_INTS_RFS (1 << 1) /* Receive FIFO Service Request */ ++#define PCM_INTS_ROR (1 << 0) /* Receive FIFO Over Run */ ++ ++/* PCM Controller clock division Register (PCM_DIV) */ ++#define PCM_DIV_SYNL_BIT 11 ++#define PCM_DIV_SYNL_MASK (0x3f << PCM_DIV_SYNL_BIT) ++#define PCM_DIV_SYNDIV_BIT 6 ++#define PCM_DIV_SYNDIV_MASK (0x1f << PCM_DIV_SYNDIV_BIT) ++#define PCM_DIV_CLKDIV_BIT 0 ++#define PCM_DIV_CLKDIV_MASK (0x3f << PCM_DIV_CLKDIV_BIT) ++ ++ ++/************************************************************************* ++ * I2C ++ *************************************************************************/ ++#define I2C_DR (I2C_BASE + 0x000) ++#define I2C_CR (I2C_BASE + 0x004) ++#define I2C_SR (I2C_BASE + 0x008) ++#define I2C_GR (I2C_BASE + 0x00C) ++ ++#define REG_I2C_DR REG8(I2C_DR) ++#define REG_I2C_CR REG8(I2C_CR) ++#define REG_I2C_SR REG8(I2C_SR) ++#define REG_I2C_GR REG16(I2C_GR) ++ ++/* I2C Control Register (I2C_CR) */ ++ ++#define I2C_CR_IEN (1 << 4) ++#define I2C_CR_STA (1 << 3) ++#define I2C_CR_STO (1 << 2) ++#define I2C_CR_AC (1 << 1) ++#define I2C_CR_I2CE (1 << 0) ++ ++/* I2C Status Register (I2C_SR) */ ++ ++#define I2C_SR_STX (1 << 4) ++#define I2C_SR_BUSY (1 << 3) ++#define I2C_SR_TEND (1 << 2) ++#define I2C_SR_DRF (1 << 1) ++#define I2C_SR_ACKF (1 << 0) ++ ++ ++/************************************************************************* ++ * SSI (Synchronous Serial Interface) ++ *************************************************************************/ ++/* n = 0, 1 (SSI0, SSI1) */ ++#define SSI_DR(n) (SSI_BASE + 0x000 + (n)*0x2000) ++#define SSI_CR0(n) (SSI_BASE + 0x004 + (n)*0x2000) ++#define SSI_CR1(n) (SSI_BASE + 0x008 + (n)*0x2000) ++#define SSI_SR(n) (SSI_BASE + 0x00C + (n)*0x2000) ++#define SSI_ITR(n) (SSI_BASE + 0x010 + (n)*0x2000) ++#define SSI_ICR(n) (SSI_BASE + 0x014 + (n)*0x2000) ++#define SSI_GR(n) (SSI_BASE + 0x018 + (n)*0x2000) ++ ++#define REG_SSI_DR(n) REG32(SSI_DR(n)) ++#define REG_SSI_CR0(n) REG16(SSI_CR0(n)) ++#define REG_SSI_CR1(n) REG32(SSI_CR1(n)) ++#define REG_SSI_SR(n) REG32(SSI_SR(n)) ++#define REG_SSI_ITR(n) REG16(SSI_ITR(n)) ++#define REG_SSI_ICR(n) REG8(SSI_ICR(n)) ++#define REG_SSI_GR(n) REG16(SSI_GR(n)) ++ ++/* SSI Data Register (SSI_DR) */ ++ ++#define SSI_DR_GPC_BIT 0 ++#define SSI_DR_GPC_MASK (0x1ff << SSI_DR_GPC_BIT) ++ ++#define SSI_MAX_FIFO_ENTRIES 128 /* 128 txfifo and 128 rxfifo */ ++ ++/* SSI Control Register 0 (SSI_CR0) */ ++ ++#define SSI_CR0_SSIE (1 << 15) ++#define SSI_CR0_TIE (1 << 14) ++#define SSI_CR0_RIE (1 << 13) ++#define SSI_CR0_TEIE (1 << 12) ++#define SSI_CR0_REIE (1 << 11) ++#define SSI_CR0_LOOP (1 << 10) ++#define SSI_CR0_RFINE (1 << 9) ++#define SSI_CR0_RFINC (1 << 8) ++#define SSI_CR0_EACLRUN (1 << 7) /* hardware auto clear underrun when TxFifo no empty */ ++#define SSI_CR0_FSEL (1 << 6) ++#define SSI_CR0_TFLUSH (1 << 2) ++#define SSI_CR0_RFLUSH (1 << 1) ++#define SSI_CR0_DISREV (1 << 0) ++ ++/* SSI Control Register 1 (SSI_CR1) */ ++ ++#define SSI_CR1_FRMHL_BIT 30 ++#define SSI_CR1_FRMHL_MASK (0x3 << SSI_CR1_FRMHL_BIT) ++ #define SSI_CR1_FRMHL_CELOW_CE2LOW (0 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is low valid */ ++ #define SSI_CR1_FRMHL_CEHIGH_CE2LOW (1 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is low valid */ ++ #define SSI_CR1_FRMHL_CELOW_CE2HIGH (2 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is high valid */ ++ #define SSI_CR1_FRMHL_CEHIGH_CE2HIGH (3 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is high valid */ ++#define SSI_CR1_TFVCK_BIT 28 ++#define SSI_CR1_TFVCK_MASK (0x3 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_0 (0 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_1 (1 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_2 (2 << SSI_CR1_TFVCK_BIT) ++ #define SSI_CR1_TFVCK_3 (3 << SSI_CR1_TFVCK_BIT) ++#define SSI_CR1_TCKFI_BIT 26 ++#define SSI_CR1_TCKFI_MASK (0x3 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_0 (0 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_1 (1 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_2 (2 << SSI_CR1_TCKFI_BIT) ++ #define SSI_CR1_TCKFI_3 (3 << SSI_CR1_TCKFI_BIT) ++#define SSI_CR1_LFST (1 << 25) ++#define SSI_CR1_ITFRM (1 << 24) ++#define SSI_CR1_UNFIN (1 << 23) ++#define SSI_CR1_MULTS (1 << 22) ++#define SSI_CR1_FMAT_BIT 20 ++#define SSI_CR1_FMAT_MASK (0x3 << SSI_CR1_FMAT_BIT) ++ #define SSI_CR1_FMAT_SPI (0 << SSI_CR1_FMAT_BIT) /* Motorola¡¯s SPI format */ ++ #define SSI_CR1_FMAT_SSP (1 << SSI_CR1_FMAT_BIT) /* TI's SSP format */ ++ #define SSI_CR1_FMAT_MW1 (2 << SSI_CR1_FMAT_BIT) /* National Microwire 1 format */ ++ #define SSI_CR1_FMAT_MW2 (3 << SSI_CR1_FMAT_BIT) /* National Microwire 2 format */ ++#define SSI_CR1_TTRG_BIT 16 /* SSI1 TX trigger */ ++#define SSI_CR1_TTRG_MASK (0xf << SSI_CR1_TTRG_BIT) ++#define SSI_CR1_MCOM_BIT 12 ++#define SSI_CR1_MCOM_MASK (0xf << SSI_CR1_MCOM_BIT) ++ #define SSI_CR1_MCOM_1BIT (0x0 << SSI_CR1_MCOM_BIT) /* 1-bit command selected */ ++ #define SSI_CR1_MCOM_2BIT (0x1 << SSI_CR1_MCOM_BIT) /* 2-bit command selected */ ++ #define SSI_CR1_MCOM_3BIT (0x2 << SSI_CR1_MCOM_BIT) /* 3-bit command selected */ ++ #define SSI_CR1_MCOM_4BIT (0x3 << SSI_CR1_MCOM_BIT) /* 4-bit command selected */ ++ #define SSI_CR1_MCOM_5BIT (0x4 << SSI_CR1_MCOM_BIT) /* 5-bit command selected */ ++ #define SSI_CR1_MCOM_6BIT (0x5 << SSI_CR1_MCOM_BIT) /* 6-bit command selected */ ++ #define SSI_CR1_MCOM_7BIT (0x6 << SSI_CR1_MCOM_BIT) /* 7-bit command selected */ ++ #define SSI_CR1_MCOM_8BIT (0x7 << SSI_CR1_MCOM_BIT) /* 8-bit command selected */ ++ #define SSI_CR1_MCOM_9BIT (0x8 << SSI_CR1_MCOM_BIT) /* 9-bit command selected */ ++ #define SSI_CR1_MCOM_10BIT (0x9 << SSI_CR1_MCOM_BIT) /* 10-bit command selected */ ++ #define SSI_CR1_MCOM_11BIT (0xA << SSI_CR1_MCOM_BIT) /* 11-bit command selected */ ++ #define SSI_CR1_MCOM_12BIT (0xB << SSI_CR1_MCOM_BIT) /* 12-bit command selected */ ++ #define SSI_CR1_MCOM_13BIT (0xC << SSI_CR1_MCOM_BIT) /* 13-bit command selected */ ++ #define SSI_CR1_MCOM_14BIT (0xD << SSI_CR1_MCOM_BIT) /* 14-bit command selected */ ++ #define SSI_CR1_MCOM_15BIT (0xE << SSI_CR1_MCOM_BIT) /* 15-bit command selected */ ++ #define SSI_CR1_MCOM_16BIT (0xF << SSI_CR1_MCOM_BIT) /* 16-bit command selected */ ++#define SSI_CR1_RTRG_BIT 8 /* SSI RX trigger */ ++#define SSI_CR1_RTRG_MASK (0xf << SSI_CR1_RTRG_BIT) ++#define SSI_CR1_FLEN_BIT 4 ++#define SSI_CR1_FLEN_MASK (0xf << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_2BIT (0x0 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_3BIT (0x1 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_4BIT (0x2 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_5BIT (0x3 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_6BIT (0x4 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_7BIT (0x5 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_8BIT (0x6 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_9BIT (0x7 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_10BIT (0x8 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_11BIT (0x9 << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_12BIT (0xA << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_13BIT (0xB << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_14BIT (0xC << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_15BIT (0xD << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_16BIT (0xE << SSI_CR1_FLEN_BIT) ++ #define SSI_CR1_FLEN_17BIT (0xF << SSI_CR1_FLEN_BIT) ++#define SSI_CR1_PHA (1 << 1) ++#define SSI_CR1_POL (1 << 0) ++ ++/* SSI Status Register (SSI_SR) */ ++ ++#define SSI_SR_TFIFONUM_BIT 16 ++#define SSI_SR_TFIFONUM_MASK (0xff << SSI_SR_TFIFONUM_BIT) ++#define SSI_SR_RFIFONUM_BIT 8 ++#define SSI_SR_RFIFONUM_MASK (0xff << SSI_SR_RFIFONUM_BIT) ++#define SSI_SR_END (1 << 7) ++#define SSI_SR_BUSY (1 << 6) ++#define SSI_SR_TFF (1 << 5) ++#define SSI_SR_RFE (1 << 4) ++#define SSI_SR_TFHE (1 << 3) ++#define SSI_SR_RFHF (1 << 2) ++#define SSI_SR_UNDR (1 << 1) ++#define SSI_SR_OVER (1 << 0) ++ ++/* SSI Interval Time Control Register (SSI_ITR) */ ++ ++#define SSI_ITR_CNTCLK (1 << 15) ++#define SSI_ITR_IVLTM_BIT 0 ++#define SSI_ITR_IVLTM_MASK (0x7fff << SSI_ITR_IVLTM_BIT) ++ ++ ++/************************************************************************* ++ * MSC ++ ************************************************************************/ ++/* n = 0, 1 (MSC0, MSC1) */ ++#define MSC_STRPCL(n) (MSC_BASE + (n)*0x1000 + 0x000) ++#define MSC_STAT(n) (MSC_BASE + (n)*0x1000 + 0x004) ++#define MSC_CLKRT(n) (MSC_BASE + (n)*0x1000 + 0x008) ++#define MSC_CMDAT(n) (MSC_BASE + (n)*0x1000 + 0x00C) ++#define MSC_RESTO(n) (MSC_BASE + (n)*0x1000 + 0x010) ++#define MSC_RDTO(n) (MSC_BASE + (n)*0x1000 + 0x014) ++#define MSC_BLKLEN(n) (MSC_BASE + (n)*0x1000 + 0x018) ++#define MSC_NOB(n) (MSC_BASE + (n)*0x1000 + 0x01C) ++#define MSC_SNOB(n) (MSC_BASE + (n)*0x1000 + 0x020) ++#define MSC_IMASK(n) (MSC_BASE + (n)*0x1000 + 0x024) ++#define MSC_IREG(n) (MSC_BASE + (n)*0x1000 + 0x028) ++#define MSC_CMD(n) (MSC_BASE + (n)*0x1000 + 0x02C) ++#define MSC_ARG(n) (MSC_BASE + (n)*0x1000 + 0x030) ++#define MSC_RES(n) (MSC_BASE + (n)*0x1000 + 0x034) ++#define MSC_RXFIFO(n) (MSC_BASE + (n)*0x1000 + 0x038) ++#define MSC_TXFIFO(n) (MSC_BASE + (n)*0x1000 + 0x03C) ++#define MSC_LPM(n) (MSC_BASE + (n)*0x1000 + 0x040) ++ ++#define REG_MSC_STRPCL(n) REG16(MSC_STRPCL(n)) ++#define REG_MSC_STAT(n) REG32(MSC_STAT(n)) ++#define REG_MSC_CLKRT(n) REG16(MSC_CLKRT(n)) ++#define REG_MSC_CMDAT(n) REG32(MSC_CMDAT(n)) ++#define REG_MSC_RESTO(n) REG16(MSC_RESTO(n)) ++#define REG_MSC_RDTO(n) REG16(MSC_RDTO(n)) ++#define REG_MSC_BLKLEN(n) REG16(MSC_BLKLEN(n)) ++#define REG_MSC_NOB(n) REG16(MSC_NOB(n)) ++#define REG_MSC_SNOB(n) REG16(MSC_SNOB(n)) ++#define REG_MSC_IMASK(n) REG32(MSC_IMASK(n)) ++#define REG_MSC_IREG(n) REG16(MSC_IREG(n)) ++#define REG_MSC_CMD(n) REG8(MSC_CMD(n)) ++#define REG_MSC_ARG(n) REG32(MSC_ARG(n)) ++#define REG_MSC_RES(n) REG16(MSC_RES(n)) ++#define REG_MSC_RXFIFO(n) REG32(MSC_RXFIFO(n)) ++#define REG_MSC_TXFIFO(n) REG32(MSC_TXFIFO(n)) ++#define REG_MSC_LPM(n) REG32(MSC_LPM(n)) ++ ++/* MSC Clock and Control Register (MSC_STRPCL) */ ++#define MSC_STRPCL_SEND_CCSD (1 << 15) /*send command completion signal disable to ceata */ ++#define MSC_STRPCL_SEND_AS_CCSD (1 << 14) /*send internally generated stop after sending ccsd */ ++#define MSC_STRPCL_EXIT_MULTIPLE (1 << 7) ++#define MSC_STRPCL_EXIT_TRANSFER (1 << 6) ++#define MSC_STRPCL_START_READWAIT (1 << 5) ++#define MSC_STRPCL_STOP_READWAIT (1 << 4) ++#define MSC_STRPCL_RESET (1 << 3) ++#define MSC_STRPCL_START_OP (1 << 2) ++#define MSC_STRPCL_CLOCK_CONTROL_BIT 0 ++#define MSC_STRPCL_CLOCK_CONTROL_MASK (0x3 << MSC_STRPCL_CLOCK_CONTROL_BIT) ++ #define MSC_STRPCL_CLOCK_CONTROL_STOP (0x1 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Stop MMC/SD clock */ ++ #define MSC_STRPCL_CLOCK_CONTROL_START (0x2 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Start MMC/SD clock */ ++ ++/* MSC Status Register (MSC_STAT) */ ++#define MSC_STAT_AUTO_CMD_DONE (1 << 31) /*12 is internally generated by controller has finished */ ++#define MSC_STAT_IS_RESETTING (1 << 15) ++#define MSC_STAT_SDIO_INT_ACTIVE (1 << 14) ++#define MSC_STAT_PRG_DONE (1 << 13) ++#define MSC_STAT_DATA_TRAN_DONE (1 << 12) ++#define MSC_STAT_END_CMD_RES (1 << 11) ++#define MSC_STAT_DATA_FIFO_AFULL (1 << 10) ++#define MSC_STAT_IS_READWAIT (1 << 9) ++#define MSC_STAT_CLK_EN (1 << 8) ++#define MSC_STAT_DATA_FIFO_FULL (1 << 7) ++#define MSC_STAT_DATA_FIFO_EMPTY (1 << 6) ++#define MSC_STAT_CRC_RES_ERR (1 << 5) ++#define MSC_STAT_CRC_READ_ERROR (1 << 4) ++#define MSC_STAT_CRC_WRITE_ERROR_BIT 2 ++#define MSC_STAT_CRC_WRITE_ERROR_MASK (0x3 << MSC_STAT_CRC_WRITE_ERROR_BIT) ++ #define MSC_STAT_CRC_WRITE_ERROR_NO (0 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No error on transmission of data */ ++ #define MSC_STAT_CRC_WRITE_ERROR (1 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* Card observed erroneous transmission of data */ ++ #define MSC_STAT_CRC_WRITE_ERROR_NOSTS (2 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No CRC status is sent back */ ++#define MSC_STAT_TIME_OUT_RES (1 << 1) ++#define MSC_STAT_TIME_OUT_READ (1 << 0) ++ ++/* MSC Bus Clock Control Register (MSC_CLKRT) */ ++#define MSC_CLKRT_CLK_RATE_BIT 0 ++#define MSC_CLKRT_CLK_RATE_MASK (0x7 << MSC_CLKRT_CLK_RATE_BIT) ++ #define MSC_CLKRT_CLK_RATE_DIV_1 (0x0 << MSC_CLKRT_CLK_RATE_BIT) /* CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_2 (0x1 << MSC_CLKRT_CLK_RATE_BIT) /* 1/2 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_4 (0x2 << MSC_CLKRT_CLK_RATE_BIT) /* 1/4 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_8 (0x3 << MSC_CLKRT_CLK_RATE_BIT) /* 1/8 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_16 (0x4 << MSC_CLKRT_CLK_RATE_BIT) /* 1/16 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_32 (0x5 << MSC_CLKRT_CLK_RATE_BIT) /* 1/32 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_64 (0x6 << MSC_CLKRT_CLK_RATE_BIT) /* 1/64 of CLK_SRC */ ++ #define MSC_CLKRT_CLK_RATE_DIV_128 (0x7 << MSC_CLKRT_CLK_RATE_BIT) /* 1/128 of CLK_SRC */ ++ ++/* MSC Command Sequence Control Register (MSC_CMDAT) */ ++#define MSC_CMDAT_CCS_EXPECTED (1 << 31) /* interrupts are enabled in ce-ata */ ++#define MSC_CMDAT_READ_CEATA (1 << 30) ++#define MSC_CMDAT_SDIO_PRDT (1 << 17) /* exact 2 cycle */ ++#define MSC_CMDAT_SEND_AS_STOP (1 << 16) ++#define MSC_CMDAT_RTRG_BIT 14 ++ #define MSC_CMDAT_RTRG_EQUALT_8 (0x0 << MSC_CMDAT_RTRG_BIT) ++ #define MSC_CMDAT_RTRG_EQUALT_16 (0x1 << MSC_CMDAT_RTRG_BIT) /* reset value */ ++ #define MSC_CMDAT_RTRG_EQUALT_24 (0x2 << MSC_CMDAT_RTRG_BIT) ++ ++#define MSC_CMDAT_TTRG_BIT 12 ++ #define MSC_CMDAT_TTRG_LESS_8 (0x0 << MSC_CMDAT_TTRG_BIT) ++ #define MSC_CMDAT_TTRG_LESS_16 (0x1 << MSC_CMDAT_TTRG_BIT) /*reset value */ ++ #define MSC_CMDAT_TTRG_LESS_24 (0x2 << MSC_CMDAT_TTRG_BIT) ++#define MSC_CMDAT_STOP_ABORT (1 << 11) ++#define MSC_CMDAT_BUS_WIDTH_BIT 9 ++#define MSC_CMDAT_BUS_WIDTH_MASK (0x3 << MSC_CMDAT_BUS_WIDTH_BIT) ++ #define MSC_CMDAT_BUS_WIDTH_1BIT (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) /* 1-bit data bus */ ++ #define MSC_CMDAT_BUS_WIDTH_4BIT (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) /* 4-bit data bus */ ++ #define MSC_CMDAT_BUS_WIDTH_8BIT (0x3 << MSC_CMDAT_BUS_WIDTH_BIT) /* 8-bit data bus */ ++#define MSC_CMDAT_DMA_EN (1 << 8) ++#define MSC_CMDAT_INIT (1 << 7) ++#define MSC_CMDAT_BUSY (1 << 6) ++#define MSC_CMDAT_STREAM_BLOCK (1 << 5) ++#define MSC_CMDAT_WRITE (1 << 4) ++#define MSC_CMDAT_READ (0 << 4) ++#define MSC_CMDAT_DATA_EN (1 << 3) ++#define MSC_CMDAT_RESPONSE_BIT 0 ++#define MSC_CMDAT_RESPONSE_MASK (0x7 << MSC_CMDAT_RESPONSE_BIT) ++ #define MSC_CMDAT_RESPONSE_NONE (0x0 << MSC_CMDAT_RESPONSE_BIT) /* No response */ ++ #define MSC_CMDAT_RESPONSE_R1 (0x1 << MSC_CMDAT_RESPONSE_BIT) /* Format R1 and R1b */ ++ #define MSC_CMDAT_RESPONSE_R2 (0x2 << MSC_CMDAT_RESPONSE_BIT) /* Format R2 */ ++ #define MSC_CMDAT_RESPONSE_R3 (0x3 << MSC_CMDAT_RESPONSE_BIT) /* Format R3 */ ++ #define MSC_CMDAT_RESPONSE_R4 (0x4 << MSC_CMDAT_RESPONSE_BIT) /* Format R4 */ ++ #define MSC_CMDAT_RESPONSE_R5 (0x5 << MSC_CMDAT_RESPONSE_BIT) /* Format R5 */ ++ #define MSC_CMDAT_RESPONSE_R6 (0x6 << MSC_CMDAT_RESPONSE_BIT) /* Format R6 */ ++ ++#define CMDAT_DMA_EN (1 << 8) ++#define CMDAT_INIT (1 << 7) ++#define CMDAT_BUSY (1 << 6) ++#define CMDAT_STREAM (1 << 5) ++#define CMDAT_WRITE (1 << 4) ++#define CMDAT_DATA_EN (1 << 3) ++ ++/* MSC Interrupts Mask Register (MSC_IMASK) */ ++#define MSC_IMASK_AUTO_CMD_DONE (1 << 8) ++#define MSC_IMASK_SDIO (1 << 7) ++#define MSC_IMASK_TXFIFO_WR_REQ (1 << 6) ++#define MSC_IMASK_RXFIFO_RD_REQ (1 << 5) ++#define MSC_IMASK_END_CMD_RES (1 << 2) ++#define MSC_IMASK_PRG_DONE (1 << 1) ++#define MSC_IMASK_DATA_TRAN_DONE (1 << 0) ++ ++/* MSC Interrupts Status Register (MSC_IREG) */ ++#define MSC_IREG_AUTO_CMD_DONE (1 << 8) ++#define MSC_IREG_SDIO (1 << 7) ++#define MSC_IREG_TXFIFO_WR_REQ (1 << 6) ++#define MSC_IREG_RXFIFO_RD_REQ (1 << 5) ++#define MSC_IREG_END_CMD_RES (1 << 2) ++#define MSC_IREG_PRG_DONE (1 << 1) ++#define MSC_IREG_DATA_TRAN_DONE (1 << 0) ++ ++/* MSC Low Power Mode Register (MSC_LPM) */ ++#define MSC_SET_LPM (1 << 0) ++ ++/************************************************************************* ++ * EMC (External Memory Controller) ++ *************************************************************************/ ++#define EMC_BCR (EMC_BASE + 0x00) /* Bus Control Register */ ++#define EMC_SMCR0 (EMC_BASE + 0x10) /* Static Memory Control Register 0 */ ++#define EMC_SMCR1 (EMC_BASE + 0x14) /* Static Memory Control Register 1 */ ++#define EMC_SMCR2 (EMC_BASE + 0x18) /* Static Memory Control Register 2 */ ++#define EMC_SMCR3 (EMC_BASE + 0x1c) /* Static Memory Control Register 3 */ ++#define EMC_SMCR4 (EMC_BASE + 0x20) /* Static Memory Control Register 4 */ ++#define EMC_SACR0 (EMC_BASE + 0x30) /* Static Memory Bank 0 Addr Config Reg */ ++#define EMC_SACR1 (EMC_BASE + 0x34) /* Static Memory Bank 1 Addr Config Reg */ ++#define EMC_SACR2 (EMC_BASE + 0x38) /* Static Memory Bank 2 Addr Config Reg */ ++#define EMC_SACR3 (EMC_BASE + 0x3c) /* Static Memory Bank 3 Addr Config Reg */ ++#define EMC_SACR4 (EMC_BASE + 0x40) /* Static Memory Bank 4 Addr Config Reg */ ++ ++#define EMC_NFCSR (EMC_BASE + 0x050) /* NAND Flash Control/Status Register */ ++ ++#define EMC_DMCR (EMC_BASE + 0x80) /* DRAM Control Register */ ++#define EMC_RTCSR (EMC_BASE + 0x84) /* Refresh Time Control/Status Register */ ++#define EMC_RTCNT (EMC_BASE + 0x88) /* Refresh Timer Counter */ ++#define EMC_RTCOR (EMC_BASE + 0x8c) /* Refresh Time Constant Register */ ++#define EMC_DMAR0 (EMC_BASE + 0x90) /* SDRAM Bank 0 Addr Config Register */ ++#define EMC_DMAR1 (EMC_BASE + 0x94) /* SDRAM Bank 1 Addr Config Register */ ++#define EMC_SDMR0 (EMC_BASE + 0xa000) /* Mode Register of SDRAM bank 0 */ ++ ++#define REG_EMC_BCR REG32(EMC_BCR) ++#define REG_EMC_SMCR0 REG32(EMC_SMCR0) ++#define REG_EMC_SMCR1 REG32(EMC_SMCR1) ++#define REG_EMC_SMCR2 REG32(EMC_SMCR2) ++#define REG_EMC_SMCR3 REG32(EMC_SMCR3) ++#define REG_EMC_SMCR4 REG32(EMC_SMCR4) ++#define REG_EMC_SACR0 REG32(EMC_SACR0) ++#define REG_EMC_SACR1 REG32(EMC_SACR1) ++#define REG_EMC_SACR2 REG32(EMC_SACR2) ++#define REG_EMC_SACR3 REG32(EMC_SACR3) ++#define REG_EMC_SACR4 REG32(EMC_SACR4) ++ ++#define REG_EMC_NFCSR REG32(EMC_NFCSR) ++ ++#define REG_EMC_DMCR REG32(EMC_DMCR) ++#define REG_EMC_RTCSR REG16(EMC_RTCSR) ++#define REG_EMC_RTCNT REG16(EMC_RTCNT) ++#define REG_EMC_RTCOR REG16(EMC_RTCOR) ++#define REG_EMC_DMAR0 REG32(EMC_DMAR0) ++#define REG_EMC_DMAR1 REG32(EMC_DMAR1) ++ ++/* Bus Control Register */ ++#define EMC_BCR_BT_SEL_BIT 30 ++#define EMC_BCR_BT_SEL_MASK (0x3 << EMC_BCR_BT_SEL_BIT) ++#define EMC_BCR_PK_SEL (1 << 24) ++#define EMC_BCR_BSR_MASK (1 << 2) /* Nand and SDRAM Bus Share Select: 0, share; 1, unshare */ ++ #define EMC_BCR_BSR_SHARE (0 << 2) ++ #define EMC_BCR_BSR_UNSHARE (1 << 2) ++#define EMC_BCR_BRE (1 << 1) ++#define EMC_BCR_ENDIAN (1 << 0) ++ ++/* Static Memory Control Register */ ++#define EMC_SMCR_STRV_BIT 24 ++#define EMC_SMCR_STRV_MASK (0x0f << EMC_SMCR_STRV_BIT) ++#define EMC_SMCR_TAW_BIT 20 ++#define EMC_SMCR_TAW_MASK (0x0f << EMC_SMCR_TAW_BIT) ++#define EMC_SMCR_TBP_BIT 16 ++#define EMC_SMCR_TBP_MASK (0x0f << EMC_SMCR_TBP_BIT) ++#define EMC_SMCR_TAH_BIT 12 ++#define EMC_SMCR_TAH_MASK (0x07 << EMC_SMCR_TAH_BIT) ++#define EMC_SMCR_TAS_BIT 8 ++#define EMC_SMCR_TAS_MASK (0x07 << EMC_SMCR_TAS_BIT) ++#define EMC_SMCR_BW_BIT 6 ++#define EMC_SMCR_BW_MASK (0x03 << EMC_SMCR_BW_BIT) ++ #define EMC_SMCR_BW_8BIT (0 << EMC_SMCR_BW_BIT) ++ #define EMC_SMCR_BW_16BIT (1 << EMC_SMCR_BW_BIT) ++ #define EMC_SMCR_BW_32BIT (2 << EMC_SMCR_BW_BIT) ++#define EMC_SMCR_BCM (1 << 3) ++#define EMC_SMCR_BL_BIT 1 ++#define EMC_SMCR_BL_MASK (0x03 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_4 (0 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_8 (1 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_16 (2 << EMC_SMCR_BL_BIT) ++ #define EMC_SMCR_BL_32 (3 << EMC_SMCR_BL_BIT) ++#define EMC_SMCR_SMT (1 << 0) ++ ++/* Static Memory Bank Addr Config Reg */ ++#define EMC_SACR_BASE_BIT 8 ++#define EMC_SACR_BASE_MASK (0xff << EMC_SACR_BASE_BIT) ++#define EMC_SACR_MASK_BIT 0 ++#define EMC_SACR_MASK_MASK (0xff << EMC_SACR_MASK_BIT) ++ ++/* NAND Flash Control/Status Register */ ++#define EMC_NFCSR_NFCE4 (1 << 7) /* NAND Flash Enable */ ++#define EMC_NFCSR_NFE4 (1 << 6) /* NAND Flash FCE# Assertion Enable */ ++#define EMC_NFCSR_NFCE3 (1 << 5) ++#define EMC_NFCSR_NFE3 (1 << 4) ++#define EMC_NFCSR_NFCE2 (1 << 3) ++#define EMC_NFCSR_NFE2 (1 << 2) ++#define EMC_NFCSR_NFCE1 (1 << 1) ++#define EMC_NFCSR_NFE1 (1 << 0) ++ ++/* DRAM Control Register */ ++#define EMC_DMCR_BW_BIT 31 ++#define EMC_DMCR_BW (1 << EMC_DMCR_BW_BIT) ++#define EMC_DMCR_CA_BIT 26 ++#define EMC_DMCR_CA_MASK (0x07 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_8 (0 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_9 (1 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_10 (2 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_11 (3 << EMC_DMCR_CA_BIT) ++ #define EMC_DMCR_CA_12 (4 << EMC_DMCR_CA_BIT) ++#define EMC_DMCR_RMODE (1 << 25) ++#define EMC_DMCR_RFSH (1 << 24) ++#define EMC_DMCR_MRSET (1 << 23) ++#define EMC_DMCR_RA_BIT 20 ++#define EMC_DMCR_RA_MASK (0x03 << EMC_DMCR_RA_BIT) ++ #define EMC_DMCR_RA_11 (0 << EMC_DMCR_RA_BIT) ++ #define EMC_DMCR_RA_12 (1 << EMC_DMCR_RA_BIT) ++ #define EMC_DMCR_RA_13 (2 << EMC_DMCR_RA_BIT) ++#define EMC_DMCR_BA_BIT 19 ++#define EMC_DMCR_BA (1 << EMC_DMCR_BA_BIT) ++#define EMC_DMCR_PDM (1 << 18) ++#define EMC_DMCR_EPIN (1 << 17) ++#define EMC_DMCR_MBSEL (1 << 16) ++#define EMC_DMCR_TRAS_BIT 13 ++#define EMC_DMCR_TRAS_MASK (0x07 << EMC_DMCR_TRAS_BIT) ++#define EMC_DMCR_RCD_BIT 11 ++#define EMC_DMCR_RCD_MASK (0x03 << EMC_DMCR_RCD_BIT) ++#define EMC_DMCR_TPC_BIT 8 ++#define EMC_DMCR_TPC_MASK (0x07 << EMC_DMCR_TPC_BIT) ++#define EMC_DMCR_TRWL_BIT 5 ++#define EMC_DMCR_TRWL_MASK (0x03 << EMC_DMCR_TRWL_BIT) ++#define EMC_DMCR_TRC_BIT 2 ++#define EMC_DMCR_TRC_MASK (0x07 << EMC_DMCR_TRC_BIT) ++#define EMC_DMCR_TCL_BIT 0 ++#define EMC_DMCR_TCL_MASK (0x03 << EMC_DMCR_TCL_BIT) ++ ++/* Refresh Time Control/Status Register */ ++#define EMC_RTCSR_SFR (1 << 8) /* self refresh flag */ ++#define EMC_RTCSR_CMF (1 << 7) ++#define EMC_RTCSR_CKS_BIT 0 ++#define EMC_RTCSR_CKS_MASK (0x07 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_DISABLE (0 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_4 (1 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_16 (2 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_64 (3 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_256 (4 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_1024 (5 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_2048 (6 << EMC_RTCSR_CKS_BIT) ++ #define EMC_RTCSR_CKS_4096 (7 << EMC_RTCSR_CKS_BIT) ++ ++/* SDRAM Bank Address Configuration Register */ ++#define EMC_DMAR_BASE_BIT 8 ++#define EMC_DMAR_BASE_MASK (0xff << EMC_DMAR_BASE_BIT) ++#define EMC_DMAR_MASK_BIT 0 ++#define EMC_DMAR_MASK_MASK (0xff << EMC_DMAR_MASK_BIT) ++ ++/* Mode Register of SDRAM bank 0 */ ++#define EMC_SDMR_BM (1 << 9) /* Write Burst Mode */ ++#define EMC_SDMR_OM_BIT 7 /* Operating Mode */ ++#define EMC_SDMR_OM_MASK (3 << EMC_SDMR_OM_BIT) ++ #define EMC_SDMR_OM_NORMAL (0 << EMC_SDMR_OM_BIT) ++#define EMC_SDMR_CAS_BIT 4 /* CAS Latency */ ++#define EMC_SDMR_CAS_MASK (7 << EMC_SDMR_CAS_BIT) ++ #define EMC_SDMR_CAS_1 (1 << EMC_SDMR_CAS_BIT) ++ #define EMC_SDMR_CAS_2 (2 << EMC_SDMR_CAS_BIT) ++ #define EMC_SDMR_CAS_3 (3 << EMC_SDMR_CAS_BIT) ++#define EMC_SDMR_BT_BIT 3 /* Burst Type */ ++#define EMC_SDMR_BT_MASK (1 << EMC_SDMR_BT_BIT) ++ #define EMC_SDMR_BT_SEQ (0 << EMC_SDMR_BT_BIT) /* Sequential */ ++ #define EMC_SDMR_BT_INT (1 << EMC_SDMR_BT_BIT) /* Interleave */ ++#define EMC_SDMR_BL_BIT 0 /* Burst Length */ ++#define EMC_SDMR_BL_MASK (7 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_1 (0 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_2 (1 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_4 (2 << EMC_SDMR_BL_BIT) ++ #define EMC_SDMR_BL_8 (3 << EMC_SDMR_BL_BIT) ++ ++#define EMC_SDMR_CAS2_16BIT \ ++ (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) ++#define EMC_SDMR_CAS2_32BIT \ ++ (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) ++#define EMC_SDMR_CAS3_16BIT \ ++ (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) ++#define EMC_SDMR_CAS3_32BIT \ ++ (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) ++ ++ ++/************************************************************************* ++ * CIM ++ *************************************************************************/ ++#define CIM_CFG (CIM_BASE + 0x0000) ++#define CIM_CTRL (CIM_BASE + 0x0004) ++#define CIM_STATE (CIM_BASE + 0x0008) ++#define CIM_IID (CIM_BASE + 0x000C) ++#define CIM_RXFIFO (CIM_BASE + 0x0010) ++#define CIM_DA (CIM_BASE + 0x0020) ++#define CIM_FA (CIM_BASE + 0x0024) ++#define CIM_FID (CIM_BASE + 0x0028) ++#define CIM_CMD (CIM_BASE + 0x002C) ++#define CIM_SIZE (CIM_BASE + 0x0030) ++#define CIM_OFFSET (CIM_BASE + 0x0034) ++#define CIM_RAM_ADDR (CIM_BASE + 0x1000) ++ ++#define REG_CIM_CFG REG32(CIM_CFG) ++#define REG_CIM_CTRL REG32(CIM_CTRL) ++#define REG_CIM_STATE REG32(CIM_STATE) ++#define REG_CIM_IID REG32(CIM_IID) ++#define REG_CIM_RXFIFO REG32(CIM_RXFIFO) ++#define REG_CIM_DA REG32(CIM_DA) ++#define REG_CIM_FA REG32(CIM_FA) ++#define REG_CIM_FID REG32(CIM_FID) ++#define REG_CIM_CMD REG32(CIM_CMD) ++#define REG_CIM_SIZE REG32(CIM_SIZE) ++#define REG_CIM_OFFSET REG32(CIM_OFFSET) ++ ++#define CIM_CFG_ORDER_BIT 18 ++#define CIM_CFG_ORDER_MASK (0x3 << CIM_CFG_ORDER_BIT) ++ #define CIM_CFG_ORDER_0 (0x0 << CIM_CFG_ORDER_BIT) /* Y0CbY1Cr; YCbCr */ ++ #define CIM_CFG_ORDER_1 (0x1 << CIM_CFG_ORDER_BIT) /* Y0CrY1Cb; YCrCb */ ++ #define CIM_CFG_ORDER_2 (0x2 << CIM_CFG_ORDER_BIT) /* CbY0CrY1; CbCrY */ ++ #define CIM_CFG_ORDER_3 (0x3 << CIM_CFG_ORDER_BIT) /* CrY0CbY1; CrCbY */ ++#define CIM_CFG_DF_BIT 16 ++#define CIM_CFG_DF_MASK (0x3 << CIM_CFG_DF_BIT) ++ #define CIM_CFG_DF_YUV444 (0x1 << CIM_CFG_DF_BIT) /* YCbCr444 */ ++ #define CIM_CFG_DF_YUV422 (0x2 << CIM_CFG_DF_BIT) /* YCbCr422 */ ++ #define CIM_CFG_DF_ITU656 (0x3 << CIM_CFG_DF_BIT) /* ITU656 YCbCr422 */ ++#define CIM_CFG_INV_DAT (1 << 15) ++#define CIM_CFG_VSP (1 << 14) /* VSYNC Polarity:0-rising edge active,1-falling edge active */ ++#define CIM_CFG_HSP (1 << 13) /* HSYNC Polarity:0-rising edge active,1-falling edge active */ ++#define CIM_CFG_PCP (1 << 12) /* PCLK working edge: 0-rising, 1-falling */ ++#define CIM_CFG_DMA_BURST_TYPE_BIT 10 ++#define CIM_CFG_DMA_BURST_TYPE_MASK (0x3 << CIM_CFG_DMA_BURST_TYPE_BIT) ++ #define CIM_CFG_DMA_BURST_INCR4 (0 << CIM_CFG_DMA_BURST_TYPE_BIT) ++ #define CIM_CFG_DMA_BURT_INCR8 (1 << CIM_CFG_DMA_BURST_TYPE_BIT) /* Suggested */ ++ #define CIM_CFG_DMA_BURT_INCR16 (2 << CIM_CFG_DMA_BURST_TYPE_BIT) /* Suggested High speed AHB*/ ++#define CIM_CFG_DUMMY_ZERO (1 << 9) ++#define CIM_CFG_EXT_VSYNC (1 << 8) /* Only for ITU656 Progressive mode */ ++#define CIM_CFG_PACK_BIT 4 ++#define CIM_CFG_PACK_MASK (0x7 << CIM_CFG_PACK_BIT) ++ #define CIM_CFG_PACK_0 (0 << CIM_CFG_PACK_BIT) /* 11 22 33 44 0xY0CbY1Cr */ ++ #define CIM_CFG_PACK_1 (1 << CIM_CFG_PACK_BIT) /* 22 33 44 11 0xCbY1CrY0 */ ++ #define CIM_CFG_PACK_2 (2 << CIM_CFG_PACK_BIT) /* 33 44 11 22 0xY1CrY0Cb */ ++ #define CIM_CFG_PACK_3 (3 << CIM_CFG_PACK_BIT) /* 44 11 22 33 0xCrY0CbY1 */ ++ #define CIM_CFG_PACK_4 (4 << CIM_CFG_PACK_BIT) /* 44 33 22 11 0xCrY1CbY0 */ ++ #define CIM_CFG_PACK_5 (5 << CIM_CFG_PACK_BIT) /* 33 22 11 44 0xY1CbY0Cr */ ++ #define CIM_CFG_PACK_6 (6 << CIM_CFG_PACK_BIT) /* 22 11 44 33 0xCbY0CrY1 */ ++ #define CIM_CFG_PACK_7 (7 << CIM_CFG_PACK_BIT) /* 11 44 33 22 0xY0CrY1Cb */ ++#define CIM_CFG_BYPASS_BIT 2 ++#define CIM_CFG_BYPASS_MASK (1 << CIM_CFG_BYPASS_BIT) ++ #define CIM_CFG_BYPASS (1 << CIM_CFG_BYPASS_BIT) ++#define CIM_CFG_DSM_BIT 0 ++#define CIM_CFG_DSM_MASK (0x3 << CIM_CFG_DSM_BIT) ++ #define CIM_CFG_DSM_CPM (0 << CIM_CFG_DSM_BIT) /* CCIR656 Progressive Mode */ ++ #define CIM_CFG_DSM_CIM (1 << CIM_CFG_DSM_BIT) /* CCIR656 Interlace Mode */ ++ #define CIM_CFG_DSM_GCM (2 << CIM_CFG_DSM_BIT) /* Gated Clock Mode */ ++ ++/* CIM Control Register (CIM_CTRL) */ ++#define CIM_CTRL_EEOF_LINE_BIT 20 ++#define CIM_CTRL_EEOF_LINE_MASK (0xfff << CIM_CTRL_MCLKDIV_BIT) ++#define CIM_CTRL_FRC_BIT 16 ++#define CIM_CTRL_FRC_MASK (0xf << CIM_CTRL_FRC_BIT) ++ #define CIM_CTRL_FRC_1 (0x0 << CIM_CTRL_FRC_BIT) /* Sample every frame */ ++ #define CIM_CTRL_FRC_2 (0x1 << CIM_CTRL_FRC_BIT) /* Sample 1/2 frame */ ++ #define CIM_CTRL_FRC_3 (0x2 << CIM_CTRL_FRC_BIT) /* Sample 1/3 frame */ ++ #define CIM_CTRL_FRC_4 (0x3 << CIM_CTRL_FRC_BIT) /* Sample 1/4 frame */ ++ #define CIM_CTRL_FRC_5 (0x4 << CIM_CTRL_FRC_BIT) /* Sample 1/5 frame */ ++ #define CIM_CTRL_FRC_6 (0x5 << CIM_CTRL_FRC_BIT) /* Sample 1/6 frame */ ++ #define CIM_CTRL_FRC_7 (0x6 << CIM_CTRL_FRC_BIT) /* Sample 1/7 frame */ ++ #define CIM_CTRL_FRC_8 (0x7 << CIM_CTRL_FRC_BIT) /* Sample 1/8 frame */ ++ #define CIM_CTRL_FRC_9 (0x8 << CIM_CTRL_FRC_BIT) /* Sample 1/9 frame */ ++ #define CIM_CTRL_FRC_10 (0x9 << CIM_CTRL_FRC_BIT) /* Sample 1/10 frame */ ++ #define CIM_CTRL_FRC_11 (0xA << CIM_CTRL_FRC_BIT) /* Sample 1/11 frame */ ++ #define CIM_CTRL_FRC_12 (0xB << CIM_CTRL_FRC_BIT) /* Sample 1/12 frame */ ++ #define CIM_CTRL_FRC_13 (0xC << CIM_CTRL_FRC_BIT) /* Sample 1/13 frame */ ++ #define CIM_CTRL_FRC_14 (0xD << CIM_CTRL_FRC_BIT) /* Sample 1/14 frame */ ++ #define CIM_CTRL_FRC_15 (0xE << CIM_CTRL_FRC_BIT) /* Sample 1/15 frame */ ++ #define CIM_CTRL_FRC_16 (0xF << CIM_CTRL_FRC_BIT) /* Sample 1/16 frame */ ++ ++#define CIM_CTRL_DMA_EEOFM (1 << 15) /* Enable EEOF interrupt */ ++#define CIM_CTRL_WIN_EN (1 << 14) ++#define CIM_CTRL_VDDM (1 << 13) /* VDD interrupt enable */ ++#define CIM_CTRL_DMA_SOFM (1 << 12) ++#define CIM_CTRL_DMA_EOFM (1 << 11) ++#define CIM_CTRL_DMA_STOPM (1 << 10) ++#define CIM_CTRL_RXF_TRIGM (1 << 9) ++#define CIM_CTRL_RXF_OFM (1 << 8) ++#define CIM_CTRL_DMA_SYNC (1 << 7) /*when change DA, do frame sync */ ++#define CIM_CTRL_RXF_TRIG_BIT 3 ++#define CIM_CTRL_RXF_TRIG_MASK (0xf << CIM_CTRL_RXF_TRIG_BIT) /* trigger value = (n+1)*burst_type */ ++#define CIM_CTRL_DMA_EN (1 << 2) /* Enable DMA */ ++#define CIM_CTRL_RXF_RST (1 << 1) /* RxFIFO reset */ ++#define CIM_CTRL_ENA (1 << 0) /* Enable CIM */ ++ ++/* CIM State Register (CIM_STATE) */ ++#define CIM_STATE_DMA_EEOF (1 << 7) /* DMA Line EEOf irq */ ++#define CIM_STATE_DMA_SOF (1 << 6) /* DMA start irq */ ++#define CIM_STATE_DMA_EOF (1 << 5) /* DMA end irq */ ++#define CIM_STATE_DMA_STOP (1 << 4) /* DMA stop irq */ ++#define CIM_STATE_RXF_OF (1 << 3) /* RXFIFO over flow irq */ ++#define CIM_STATE_RXF_TRIG (1 << 2) /* RXFIFO triger meet irq */ ++#define CIM_STATE_RXF_EMPTY (1 << 1) /* RXFIFO empty irq */ ++#define CIM_STATE_VDD (1 << 0) /* CIM disabled irq */ ++ ++/* CIM DMA Command Register (CIM_CMD) */ ++ ++#define CIM_CMD_SOFINT (1 << 31) /* enable DMA start irq */ ++#define CIM_CMD_EOFINT (1 << 30) /* enable DMA end irq */ ++#define CIM_CMD_EEOFINT (1 << 29) /* enable DMA EEOF irq */ ++#define CIM_CMD_STOP (1 << 28) /* enable DMA stop irq */ ++#define CIM_CMD_OFRCV (1 << 27) /* enable recovery when TXFiFo overflow */ ++#define CIM_CMD_LEN_BIT 0 ++#define CIM_CMD_LEN_MASK (0xffffff << CIM_CMD_LEN_BIT) ++ ++/* CIM Window-Image Size Register (CIM_SIZE) */ ++#define CIM_SIZE_LPF_BIT 16 /* Lines per freame for csc output image */ ++#define CIM_SIZE_LPF_MASK (0x1fff << CIM_SIZE_LPF_BIT) ++#define CIM_SIZE_PPL_BIT 0 /* Pixels per line for csc output image, should be an even number */ ++#define CIM_SIZE_PPL_MASK (0x1fff << CIM_SIZE_PPL_BIT) ++ ++/* CIM Image Offset Register (CIM_OFFSET) */ ++#define CIM_OFFSET_V_BIT 16 /* Vertical offset */ ++#define CIM_OFFSET_V_MASK (0xfff << CIM_OFFSET_V_BIT) ++#define CIM_OFFSET_H_BIT 0 /* Horizontal offset, should be an enen number */ ++#define CIM_OFFSET_H_MASK (0xfff << CIM_OFFSET_H_BIT) /*OFFSET_H should be even number*/ ++ ++/************************************************************************* ++ * SADC (Smart A/D Controller) ++ *************************************************************************/ ++ ++#define SADC_ENA (SADC_BASE + 0x00) /* ADC Enable Register */ ++#define SADC_CFG (SADC_BASE + 0x04) /* ADC Configure Register */ ++#define SADC_CTRL (SADC_BASE + 0x08) /* ADC Control Register */ ++#define SADC_STATE (SADC_BASE + 0x0C) /* ADC Status Register*/ ++#define SADC_SAMETIME (SADC_BASE + 0x10) /* ADC Same Point Time Register */ ++#define SADC_WAITTIME (SADC_BASE + 0x14) /* ADC Wait Time Register */ ++#define SADC_TSDAT (SADC_BASE + 0x18) /* ADC Touch Screen Data Register */ ++#define SADC_BATDAT (SADC_BASE + 0x1C) /* ADC PBAT Data Register */ ++#define SADC_SADDAT (SADC_BASE + 0x20) /* ADC SADCIN Data Register */ ++#define SADC_ADCLK (SADC_BASE + 0x28) /* ADC Clock Divide Register */ ++ ++#define REG_SADC_ENA REG8(SADC_ENA) ++#define REG_SADC_CFG REG32(SADC_CFG) ++#define REG_SADC_CTRL REG8(SADC_CTRL) ++#define REG_SADC_STATE REG8(SADC_STATE) ++#define REG_SADC_SAMETIME REG16(SADC_SAMETIME) ++#define REG_SADC_WAITTIME REG16(SADC_WAITTIME) ++#define REG_SADC_TSDAT REG32(SADC_TSDAT) ++#define REG_SADC_BATDAT REG16(SADC_BATDAT) ++#define REG_SADC_SADDAT REG16(SADC_SADDAT) ++#define REG_SADC_ADCLK REG32(SADC_ADCLK) ++ ++/* ADC Enable Register */ ++#define SADC_ENA_ADEN (1 << 7) /* Touch Screen Enable */ ++#define SADC_ENA_ENTR_SLP (1 << 6) /* Touch Screen Enable */ ++#define SADC_ENA_EXIT_SLP (1 << 5) /* Touch Screen Enable */ ++#define SADC_ENA_TSEN (1 << 2) /* Touch Screen Enable */ ++#define SADC_ENA_PBATEN (1 << 1) /* PBAT Enable */ ++#define SADC_ENA_SADCINEN (1 << 0) /* SADCIN Enable */ ++ ++/* ADC Configure Register */ ++#define SADC_CFG_EXIN (1 << 30) ++#define SADC_CFG_CLKOUT_NUM_BIT 16 ++#define SADC_CFG_CLKOUT_NUM_MASK (0x7 << SADC_CFG_CLKOUT_NUM_BIT) ++#define SADC_CFG_TS_DMA (1 << 15) /* Touch Screen DMA Enable */ ++#define SADC_CFG_XYZ_BIT 13 /* XYZ selection */ ++#define SADC_CFG_XYZ_MASK (0x3 << SADC_CFG_XYZ_BIT) ++ #define SADC_CFG_XY (0 << SADC_CFG_XYZ_BIT) ++ #define SADC_CFG_XYZ (1 << SADC_CFG_XYZ_BIT) ++ #define SADC_CFG_XYZ1Z2 (2 << SADC_CFG_XYZ_BIT) ++#define SADC_CFG_SNUM_BIT 10 /* Sample Number */ ++#define SADC_CFG_SNUM_MASK (0x7 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_1 (0x0 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_2 (0x1 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_3 (0x2 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_4 (0x3 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_5 (0x4 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_6 (0x5 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_8 (0x6 << SADC_CFG_SNUM_BIT) ++ #define SADC_CFG_SNUM_9 (0x7 << SADC_CFG_SNUM_BIT) ++#define SADC_CFG_CLKDIV_BIT 5 /* AD Converter frequency clock divider */ ++#define SADC_CFG_CLKDIV_MASK (0x1f << SADC_CFG_CLKDIV_BIT) ++#define SADC_CFG_PBAT_HIGH (0 << 4) /* PBAT >= 2.5V */ ++#define SADC_CFG_PBAT_LOW (1 << 4) /* PBAT < 2.5V */ ++#define SADC_CFG_CMD_BIT 0 /* ADC Command */ ++#define SADC_CFG_CMD_MASK (0xf << SADC_CFG_CMD_BIT) ++ #define SADC_CFG_CMD_X_SE (0x0 << SADC_CFG_CMD_BIT) /* X Single-End */ ++ #define SADC_CFG_CMD_Y_SE (0x1 << SADC_CFG_CMD_BIT) /* Y Single-End */ ++ #define SADC_CFG_CMD_X_DIFF (0x2 << SADC_CFG_CMD_BIT) /* X Differential */ ++ #define SADC_CFG_CMD_Y_DIFF (0x3 << SADC_CFG_CMD_BIT) /* Y Differential */ ++ #define SADC_CFG_CMD_Z1_DIFF (0x4 << SADC_CFG_CMD_BIT) /* Z1 Differential */ ++ #define SADC_CFG_CMD_Z2_DIFF (0x5 << SADC_CFG_CMD_BIT) /* Z2 Differential */ ++ #define SADC_CFG_CMD_Z3_DIFF (0x6 << SADC_CFG_CMD_BIT) /* Z3 Differential */ ++ #define SADC_CFG_CMD_Z4_DIFF (0x7 << SADC_CFG_CMD_BIT) /* Z4 Differential */ ++ #define SADC_CFG_CMD_TP_SE (0x8 << SADC_CFG_CMD_BIT) /* Touch Pressure */ ++ #define SADC_CFG_CMD_PBATH_SE (0x9 << SADC_CFG_CMD_BIT) /* PBAT >= 2.5V */ ++ #define SADC_CFG_CMD_PBATL_SE (0xa << SADC_CFG_CMD_BIT) /* PBAT < 2.5V */ ++ #define SADC_CFG_CMD_SADCIN_SE (0xb << SADC_CFG_CMD_BIT) /* Measure SADCIN */ ++ #define SADC_CFG_CMD_INT_PEN (0xc << SADC_CFG_CMD_BIT) /* INT_PEN Enable */ ++ ++/* ADC Control Register */ ++#define SADC_CTRL_SLPENDM (1 << 5) /* sleep Interrupt Mask */ ++#define SADC_CTRL_PENDM (1 << 4) /* Pen Down Interrupt Mask */ ++#define SADC_CTRL_PENUM (1 << 3) /* Pen Up Interrupt Mask */ ++#define SADC_CTRL_TSRDYM (1 << 2) /* Touch Screen Data Ready Interrupt Mask */ ++#define SADC_CTRL_PBATRDYM (1 << 1) /* PBAT Data Ready Interrupt Mask */ ++#define SADC_CTRL_SRDYM (1 << 0) /* SADCIN Data Ready Interrupt Mask */ ++ ++/* ADC Status Register */ ++#define SADC_STATE_SLEEPND (1 << 5) /* Pen Down Interrupt Flag */ ++#define SADC_STATE_PEND (1 << 4) /* Pen Down Interrupt Flag */ ++#define SADC_STATE_PENU (1 << 3) /* Pen Up Interrupt Flag */ ++#define SADC_STATE_TSRDY (1 << 2) /* Touch Screen Data Ready Interrupt Flag */ ++#define SADC_STATE_PBATRDY (1 << 1) /* PBAT Data Ready Interrupt Flag */ ++#define SADC_STATE_SRDY (1 << 0) /* SADCIN Data Ready Interrupt Flag */ ++ ++/* ADC Touch Screen Data Register */ ++#define SADC_TSDAT_DATA0_BIT 0 ++#define SADC_TSDAT_DATA0_MASK (0xfff << SADC_TSDAT_DATA0_BIT) ++#define SADC_TSDAT_TYPE0 (1 << 15) ++#define SADC_TSDAT_DATA1_BIT 16 ++#define SADC_TSDAT_DATA1_MASK (0xfff << SADC_TSDAT_DATA1_BIT) ++#define SADC_TSDAT_TYPE1 (1 << 31) ++ ++/* ADC Clock Divide Register */ ++#define SADC_ADCLK_CLKDIV_10_BIT 16 ++#define SADC_ADCLK_CLKDIV_10_MASK (0x7f << SADC_ADCLK_CLKDIV_10_BIT) ++#define SADC_ADCLK_CLKDIV_BIT 0 ++#define SADC_ADCLK_CLKDIV_MASK (0x3f << SADC_ADCLK_CLKDIV_BIT) ++ ++/************************************************************************* ++ * SLCD (Smart LCD Controller) ++ *************************************************************************/ ++ ++#define SLCD_CFG (SLCD_BASE + 0xA0) /* SLCD Configure Register */ ++#define SLCD_CTRL (SLCD_BASE + 0xA4) /* SLCD Control Register */ ++#define SLCD_STATE (SLCD_BASE + 0xA8) /* SLCD Status Register */ ++#define SLCD_DATA (SLCD_BASE + 0xAC) /* SLCD Data Register */ ++ ++#define REG_SLCD_CFG REG32(SLCD_CFG) ++#define REG_SLCD_CTRL REG8(SLCD_CTRL) ++#define REG_SLCD_STATE REG8(SLCD_STATE) ++#define REG_SLCD_DATA REG32(SLCD_DATA) ++ ++/* SLCD Configure Register */ ++#define SLCD_CFG_DWIDTH_BIT 10 ++#define SLCD_CFG_DWIDTH_MASK (0x7 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_18BIT (0 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_16BIT (1 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_8BIT_x3 (2 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_8BIT_x2 (3 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_8BIT_x1 (4 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_24BIT (5 << SLCD_CFG_DWIDTH_BIT) ++ #define SLCD_CFG_DWIDTH_9BIT_x2 (7 << SLCD_CFG_DWIDTH_BIT) ++#define SLCD_CFG_CWIDTH_BIT (8) ++#define SLCD_CFG_CWIDTH_MASK (0x7 << SLCD_CFG_CWIDTH_BIT) ++#define SLCD_CFG_CWIDTH_16BIT (0 << SLCD_CFG_CWIDTH_BIT) ++#define SLCD_CFG_CWIDTH_8BIT (1 << SLCD_CFG_CWIDTH_BIT) ++#define SLCD_CFG_CWIDTH_18BIT (2 << SLCD_CFG_CWIDTH_BIT) ++#define SLCD_CFG_CWIDTH_24BIT (3 << SLCD_CFG_CWIDTH_BIT) ++#define SLCD_CFG_CS_ACTIVE_LOW (0 << 4) ++#define SLCD_CFG_CS_ACTIVE_HIGH (1 << 4) ++#define SLCD_CFG_RS_CMD_LOW (0 << 3) ++#define SLCD_CFG_RS_CMD_HIGH (1 << 3) ++#define SLCD_CFG_CLK_ACTIVE_FALLING (0 << 1) ++#define SLCD_CFG_CLK_ACTIVE_RISING (1 << 1) ++#define SLCD_CFG_TYPE_PARALLEL (0 << 0) ++#define SLCD_CFG_TYPE_SERIAL (1 << 0) ++ ++/* SLCD Control Register */ ++#define SLCD_CTRL_DMA_EN (1 << 0) ++ ++/* SLCD Status Register */ ++#define SLCD_STATE_BUSY (1 << 0) ++ ++/* SLCD Data Register */ ++#define SLCD_DATA_RS_DATA (0 << 31) ++#define SLCD_DATA_RS_COMMAND (1 << 31) ++ ++/************************************************************************* ++ * LCD (LCD Controller) ++ *************************************************************************/ ++#define LCD_CFG (LCD_BASE + 0x00) /* LCD Configure Register */ ++#define LCD_CTRL (LCD_BASE + 0x30) /* LCD Control Register */ ++#define LCD_STATE (LCD_BASE + 0x34) /* LCD Status Register */ ++ ++#define LCD_OSDC (LCD_BASE + 0x100) /* LCD OSD Configure Register */ ++#define LCD_OSDCTRL (LCD_BASE + 0x104) /* LCD OSD Control Register */ ++#define LCD_OSDS (LCD_BASE + 0x108) /* LCD OSD Status Register */ ++#define LCD_BGC (LCD_BASE + 0x10C) /* LCD Background Color Register */ ++#define LCD_KEY0 (LCD_BASE + 0x110) /* LCD Foreground Color Key Register 0 */ ++#define LCD_KEY1 (LCD_BASE + 0x114) /* LCD Foreground Color Key Register 1 */ ++#define LCD_ALPHA (LCD_BASE + 0x118) /* LCD ALPHA Register */ ++#define LCD_IPUR (LCD_BASE + 0x11C) /* LCD IPU Restart Register */ ++ ++#define LCD_VAT (LCD_BASE + 0x0c) /* Virtual Area Setting Register */ ++#define LCD_DAH (LCD_BASE + 0x10) /* Display Area Horizontal Start/End Point */ ++#define LCD_DAV (LCD_BASE + 0x14) /* Display Area Vertical Start/End Point */ ++ ++#define LCD_XYP0 (LCD_BASE + 0x120) /* Foreground 0 XY Position Register */ ++#define LCD_XYP1 (LCD_BASE + 0x124) /* Foreground 1 XY Position Register */ ++#define LCD_SIZE0 (LCD_BASE + 0x128) /* Foreground 0 Size Register */ ++#define LCD_SIZE1 (LCD_BASE + 0x12C) /* Foreground 1 Size Register */ ++#define LCD_RGBC (LCD_BASE + 0x90) /* RGB Controll Register */ ++ ++#define LCD_VSYNC (LCD_BASE + 0x04) /* Vertical Synchronize Register */ ++#define LCD_HSYNC (LCD_BASE + 0x08) /* Horizontal Synchronize Register */ ++#define LCD_PS (LCD_BASE + 0x18) /* PS Signal Setting */ ++#define LCD_CLS (LCD_BASE + 0x1c) /* CLS Signal Setting */ ++#define LCD_SPL (LCD_BASE + 0x20) /* SPL Signal Setting */ ++#define LCD_REV (LCD_BASE + 0x24) /* REV Signal Setting */ ++#define LCD_IID (LCD_BASE + 0x38) /* Interrupt ID Register */ ++#define LCD_DA0 (LCD_BASE + 0x40) /* Descriptor Address Register 0 */ ++#define LCD_SA0 (LCD_BASE + 0x44) /* Source Address Register 0 */ ++#define LCD_FID0 (LCD_BASE + 0x48) /* Frame ID Register 0 */ ++#define LCD_CMD0 (LCD_BASE + 0x4c) /* DMA Command Register 0 */ ++#define LCD_DA1 (LCD_BASE + 0x50) /* Descriptor Address Register 1 */ ++#define LCD_SA1 (LCD_BASE + 0x54) /* Source Address Register 1 */ ++#define LCD_FID1 (LCD_BASE + 0x58) /* Frame ID Register 1 */ ++#define LCD_CMD1 (LCD_BASE + 0x5c) /* DMA Command Register 1 */ ++ ++#define LCD_OFFS0 (LCD_BASE + 0x60) /* DMA Offsize Register 0 */ ++#define LCD_PW0 (LCD_BASE + 0x64) /* DMA Page Width Register 0 */ ++#define LCD_CNUM0 (LCD_BASE + 0x68) /* DMA Command Counter Register 0 */ ++#define LCD_DESSIZE0 (LCD_BASE + 0x6C) /* Foreground Size in Descriptor 0 Register*/ ++#define LCD_OFFS1 (LCD_BASE + 0x70) /* DMA Offsize Register 1 */ ++#define LCD_PW1 (LCD_BASE + 0x74) /* DMA Page Width Register 1 */ ++#define LCD_CNUM1 (LCD_BASE + 0x78) /* DMA Command Counter Register 1 */ ++#define LCD_DESSIZE1 (LCD_BASE + 0x7C) /* Foreground Size in Descriptor 1 Register*/ ++ ++#define REG_LCD_CFG REG32(LCD_CFG) ++#define REG_LCD_CTRL REG32(LCD_CTRL) ++#define REG_LCD_STATE REG32(LCD_STATE) ++ ++#define REG_LCD_OSDC REG16(LCD_OSDC) ++#define REG_LCD_OSDCTRL REG16(LCD_OSDCTRL) ++#define REG_LCD_OSDS REG16(LCD_OSDS) ++#define REG_LCD_BGC REG32(LCD_BGC) ++#define REG_LCD_KEY0 REG32(LCD_KEY0) ++#define REG_LCD_KEY1 REG32(LCD_KEY1) ++#define REG_LCD_ALPHA REG8(LCD_ALPHA) ++#define REG_LCD_IPUR REG32(LCD_IPUR) ++ ++#define REG_LCD_VAT REG32(LCD_VAT) ++#define REG_LCD_DAH REG32(LCD_DAH) ++#define REG_LCD_DAV REG32(LCD_DAV) ++ ++#define REG_LCD_XYP0 REG32(LCD_XYP0) ++#define REG_LCD_XYP1 REG32(LCD_XYP1) ++#define REG_LCD_SIZE0 REG32(LCD_SIZE0) ++#define REG_LCD_SIZE1 REG32(LCD_SIZE1) ++#define REG_LCD_RGBC REG16(LCD_RGBC) ++ ++#define REG_LCD_VSYNC REG32(LCD_VSYNC) ++#define REG_LCD_HSYNC REG32(LCD_HSYNC) ++#define REG_LCD_PS REG32(LCD_PS) ++#define REG_LCD_CLS REG32(LCD_CLS) ++#define REG_LCD_SPL REG32(LCD_SPL) ++#define REG_LCD_REV REG32(LCD_REV) ++#define REG_LCD_IID REG32(LCD_IID) ++#define REG_LCD_DA0 REG32(LCD_DA0) ++#define REG_LCD_SA0 REG32(LCD_SA0) ++#define REG_LCD_FID0 REG32(LCD_FID0) ++#define REG_LCD_CMD0 REG32(LCD_CMD0) ++#define REG_LCD_DA1 REG32(LCD_DA1) ++#define REG_LCD_SA1 REG32(LCD_SA1) ++#define REG_LCD_FID1 REG32(LCD_FID1) ++#define REG_LCD_CMD1 REG32(LCD_CMD1) ++ ++#define REG_LCD_OFFS0 REG32(LCD_OFFS0) ++#define REG_LCD_PW0 REG32(LCD_PW0) ++#define REG_LCD_CNUM0 REG32(LCD_CNUM0) ++#define REG_LCD_DESSIZE0 REG32(LCD_DESSIZE0) ++#define REG_LCD_OFFS1 REG32(LCD_OFFS1) ++#define REG_LCD_PW1 REG32(LCD_PW1) ++#define REG_LCD_CNUM1 REG32(LCD_CNUM1) ++#define REG_LCD_DESSIZE1 REG32(LCD_DESSIZE1) ++ ++/* LCD Configure Register */ ++#define LCD_CFG_LCDPIN_BIT 31 /* LCD pins selection */ ++#define LCD_CFG_LCDPIN_MASK (0x1 << LCD_CFG_LCDPIN_BIT) ++ #define LCD_CFG_LCDPIN_LCD (0x0 << LCD_CFG_LCDPIN_BIT) ++ #define LCD_CFG_LCDPIN_SLCD (0x1 << LCD_CFG_LCDPIN_BIT) ++#define LCD_CFG_TVEPEH (1 << 30) /* TVE PAL enable extra halfline signal */ ++#define LCD_CFG_FUHOLD (1 << 29) /* hold pixel clock when outFIFO underrun */ ++#define LCD_CFG_NEWDES (1 << 28) /* use new descripter. old: 4words, new:8words */ ++#define LCD_CFG_PALBP (1 << 27) /* bypass data format and alpha blending */ ++#define LCD_CFG_TVEN (1 << 26) /* indicate the terminal is lcd or tv */ ++#define LCD_CFG_RECOVER (1 << 25) /* Auto recover when output fifo underrun */ ++#define LCD_CFG_DITHER (1 << 24) /* Dither function */ ++#define LCD_CFG_PSM (1 << 23) /* PS signal mode */ ++#define LCD_CFG_CLSM (1 << 22) /* CLS signal mode */ ++#define LCD_CFG_SPLM (1 << 21) /* SPL signal mode */ ++#define LCD_CFG_REVM (1 << 20) /* REV signal mode */ ++#define LCD_CFG_HSYNM (1 << 19) /* HSYNC signal mode */ ++#define LCD_CFG_PCLKM (1 << 18) /* PCLK signal mode */ ++#define LCD_CFG_INVDAT (1 << 17) /* Inverse output data */ ++#define LCD_CFG_SYNDIR_IN (1 << 16) /* VSYNC&HSYNC direction */ ++#define LCD_CFG_PSP (1 << 15) /* PS pin reset state */ ++#define LCD_CFG_CLSP (1 << 14) /* CLS pin reset state */ ++#define LCD_CFG_SPLP (1 << 13) /* SPL pin reset state */ ++#define LCD_CFG_REVP (1 << 12) /* REV pin reset state */ ++#define LCD_CFG_HSP (1 << 11) /* HSYNC polarity:0-active high,1-active low */ ++#define LCD_CFG_PCP (1 << 10) /* PCLK polarity:0-rising,1-falling */ ++#define LCD_CFG_DEP (1 << 9) /* DE polarity:0-active high,1-active low */ ++#define LCD_CFG_VSP (1 << 8) /* VSYNC polarity:0-rising,1-falling */ ++#define LCD_CFG_MODE_TFT_18BIT (1 << 7) /* 18bit TFT */ ++#define LCD_CFG_MODE_TFT_16BIT (0 << 7) /* 16bit TFT */ ++#define LCD_CFG_MODE_TFT_24BIT (1 << 6) /* 24bit TFT */ ++#define LCD_CFG_PDW_BIT 4 /* STN pins utilization */ ++#define LCD_CFG_PDW_MASK (0x3 << LCD_DEV_PDW_BIT) ++#define LCD_CFG_PDW_1 (0 << LCD_CFG_PDW_BIT) /* LCD_D[0] */ ++ #define LCD_CFG_PDW_2 (1 << LCD_CFG_PDW_BIT) /* LCD_D[0:1] */ ++ #define LCD_CFG_PDW_4 (2 << LCD_CFG_PDW_BIT) /* LCD_D[0:3]/LCD_D[8:11] */ ++ #define LCD_CFG_PDW_8 (3 << LCD_CFG_PDW_BIT) /* LCD_D[0:7]/LCD_D[8:15] */ ++#define LCD_CFG_MODE_BIT 0 /* Display Device Mode Select */ ++#define LCD_CFG_MODE_MASK (0x0f << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_GENERIC_TFT (0 << LCD_CFG_MODE_BIT) /* 16,18 bit TFT */ ++ #define LCD_CFG_MODE_SPECIAL_TFT_1 (1 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SPECIAL_TFT_2 (2 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SPECIAL_TFT_3 (3 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_NONINTER_CCIR656 (4 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_INTER_CCIR656 (6 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SINGLE_CSTN (8 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SINGLE_MSTN (9 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_DUAL_CSTN (10 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_DUAL_MSTN (11 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SERIAL_TFT (12 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_LCM (13 << LCD_CFG_MODE_BIT) ++ #define LCD_CFG_MODE_SLCD LCD_CFG_MODE_LCM ++ ++/* LCD Control Register */ ++#define LCD_CTRL_BST_BIT 28 /* Burst Length Selection */ ++#define LCD_CTRL_BST_MASK (0x03 << LCD_CTRL_BST_BIT) ++ #define LCD_CTRL_BST_4 (0 << LCD_CTRL_BST_BIT) /* 4-word */ ++ #define LCD_CTRL_BST_8 (1 << LCD_CTRL_BST_BIT) /* 8-word */ ++ #define LCD_CTRL_BST_16 (2 << LCD_CTRL_BST_BIT) /* 16-word */ ++ #define LCD_CTRL_BST_32 (3 << LCD_CTRL_BST_BIT) /* 32-word */ ++#define LCD_CTRL_RGB565 (0 << 27) /* RGB565 mode(foreground 0 in OSD mode) */ ++#define LCD_CTRL_RGB555 (1 << 27) /* RGB555 mode(foreground 0 in OSD mode) */ ++#define LCD_CTRL_OFUP (1 << 26) /* Output FIFO underrun protection enable */ ++#define LCD_CTRL_FRC_BIT 24 /* STN FRC Algorithm Selection */ ++#define LCD_CTRL_FRC_MASK (0x03 << LCD_CTRL_FRC_BIT) ++ #define LCD_CTRL_FRC_16 (0 << LCD_CTRL_FRC_BIT) /* 16 grayscale */ ++ #define LCD_CTRL_FRC_4 (1 << LCD_CTRL_FRC_BIT) /* 4 grayscale */ ++ #define LCD_CTRL_FRC_2 (2 << LCD_CTRL_FRC_BIT) /* 2 grayscale */ ++#define LCD_CTRL_PDD_BIT 16 /* Load Palette Delay Counter */ ++#define LCD_CTRL_PDD_MASK (0xff << LCD_CTRL_PDD_BIT) ++#define LCD_CTRL_EOFM (1 << 13) /* EOF interrupt mask */ ++#define LCD_CTRL_SOFM (1 << 12) /* SOF interrupt mask */ ++#define LCD_CTRL_OFUM (1 << 11) /* Output FIFO underrun interrupt mask */ ++#define LCD_CTRL_IFUM0 (1 << 10) /* Input FIFO 0 underrun interrupt mask */ ++#define LCD_CTRL_IFUM1 (1 << 9) /* Input FIFO 1 underrun interrupt mask */ ++#define LCD_CTRL_LDDM (1 << 8) /* LCD disable done interrupt mask */ ++#define LCD_CTRL_QDM (1 << 7) /* LCD quick disable done interrupt mask */ ++#define LCD_CTRL_BEDN (1 << 6) /* Endian selection */ ++#define LCD_CTRL_PEDN (1 << 5) /* Endian in byte:0-msb first, 1-lsb first */ ++#define LCD_CTRL_DIS (1 << 4) /* Disable indicate bit */ ++#define LCD_CTRL_ENA (1 << 3) /* LCD enable bit */ ++#define LCD_CTRL_BPP_BIT 0 /* Bits Per Pixel */ ++#define LCD_CTRL_BPP_MASK (0x07 << LCD_CTRL_BPP_BIT) ++ #define LCD_CTRL_BPP_1 (0 << LCD_CTRL_BPP_BIT) /* 1 bpp */ ++ #define LCD_CTRL_BPP_2 (1 << LCD_CTRL_BPP_BIT) /* 2 bpp */ ++ #define LCD_CTRL_BPP_4 (2 << LCD_CTRL_BPP_BIT) /* 4 bpp */ ++ #define LCD_CTRL_BPP_8 (3 << LCD_CTRL_BPP_BIT) /* 8 bpp */ ++ #define LCD_CTRL_BPP_16 (4 << LCD_CTRL_BPP_BIT) /* 15/16 bpp */ ++ #define LCD_CTRL_BPP_18_24 (5 << LCD_CTRL_BPP_BIT) /* 18/24/32 bpp */ ++ #define LCD_CTRL_BPP_CMPS_24 (6 << LCD_CTRL_BPP_BIT) /* 24 compress bpp */ ++ ++/* LCD Status Register */ ++#define LCD_STATE_QD (1 << 7) /* Quick Disable Done */ ++#define LCD_STATE_EOF (1 << 5) /* EOF Flag */ ++#define LCD_STATE_SOF (1 << 4) /* SOF Flag */ ++#define LCD_STATE_OFU (1 << 3) /* Output FIFO Underrun */ ++#define LCD_STATE_IFU0 (1 << 2) /* Input FIFO 0 Underrun */ ++#define LCD_STATE_IFU1 (1 << 1) /* Input FIFO 1 Underrun */ ++#define LCD_STATE_LDD (1 << 0) /* LCD Disabled */ ++ ++/* OSD Configure Register */ ++#define LCD_OSDC_SOFM1 (1 << 15) /* Start of frame interrupt mask for foreground 1 */ ++#define LCD_OSDC_EOFM1 (1 << 14) /* End of frame interrupt mask for foreground 1 */ ++#define LCD_OSDC_REM1 (1 << 13) /* Real end of frame mask for foreground 1 */ ++#define LCD_OSDC_SOFM0 (1 << 11) /* Start of frame interrupt mask for foreground 0 */ ++#define LCD_OSDC_EOFM0 (1 << 10) /* End of frame interrupt mask for foreground 0 */ ++#define LCD_OSDC_REM0 (1 << 9) /* Real end of frame mask for foreground 0 */ ++#define LCD_OSDC_REMB (1 << 7) /* Real end of frame mask for background */ ++#define LCD_OSDC_F1EN (1 << 4) /* enable foreground 1 */ ++#define LCD_OSDC_F0EN (1 << 3) /* enable foreground 0 */ ++#define LCD_OSDC_ALPHAEN (1 << 2) /* enable alpha blending */ ++#define LCD_OSDC_ALPHAMD (1 << 1) /* alpha blending mode */ ++#define LCD_OSDC_OSDEN (1 << 0) /* OSD mode enable */ ++ ++/* OSD Controll Register */ ++#define LCD_OSDCTRL_IPU (1 << 15) /* input data from IPU */ ++#define LCD_OSDCTRL_RGB565 (0 << 4) /* foreground 1, 16bpp, 0-RGB565, 1-RGB555 */ ++#define LCD_OSDCTRL_RGB555 (1 << 4) /* foreground 1, 16bpp, 0-RGB565, 1-RGB555 */ ++#define LCD_OSDCTRL_CHANGES (1 << 3) /* Change size flag */ ++#define LCD_OSDCTRL_OSDBPP_BIT 0 /* Bits Per Pixel of OSD Channel 1 */ ++#define LCD_OSDCTRL_OSDBPP_MASK (0x7< ++ * ++ * 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 __ASM_BOARD_SERIAL_H__ ++#define __ASM_BOARD_SERIAL_H__ ++ ++#ifndef CONFIG_SERIAL_MANY_PORTS ++#undef RS_TABLE_SIZE ++#define RS_TABLE_SIZE 1 ++#endif ++ ++#define JZ_BASE_BAUD (12000000/16) ++ ++#define JZ_SERIAL_PORT_DEFNS \ ++ { .baud_base = JZ_BASE_BAUD, .irq = IRQ_UART0, \ ++ .flags = STD_COM_FLAGS, .iomem_base = (u8 *)UART0_BASE, \ ++ .iomem_reg_shift = 2, .io_type = SERIAL_IO_MEM }, ++ ++#endif /* __ASM_BORAD_SERIAL_H__ */ +diff -urN linux-2.6.24.7.old/include/asm-mips/mach-jz4750d/war.h linux-2.6.24.7/include/asm-mips/mach-jz4750d/war.h +--- linux-2.6.24.7.old/include/asm-mips/mach-jz4750d/war.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/mach-jz4750d/war.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,25 @@ ++/* ++ * 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) 2002, 2004, 2007 by Ralf Baechle ++ */ ++#ifndef __ASM_MIPS_MACH_JZ4740_WAR_H ++#define __ASM_MIPS_MACH_JZ4740_WAR_H ++ ++#define R4600_V1_INDEX_ICACHEOP_WAR 0 ++#define R4600_V1_HIT_CACHEOP_WAR 0 ++#define R4600_V2_HIT_CACHEOP_WAR 0 ++#define R5432_CP0_INTERRUPT_WAR 0 ++#define BCM1250_M3_WAR 0 ++#define SIBYTE_1956_WAR 0 ++#define MIPS4K_ICACHE_REFILL_WAR 0 ++#define MIPS_CACHE_SYNC_WAR 0 ++#define TX49XX_ICACHE_INDEX_INV_WAR 0 ++#define RM9000_CDEX_SMP_WAR 0 ++#define ICACHE_REFILLS_WORKAROUND_WAR 0 ++#define R10000_LLSC_WAR 0 ++#define MIPS34K_MISSED_ITLB_WAR 0 ++ ++#endif /* __ASM_MIPS_MACH_JZ4740_WAR_H */ +diff -urN linux-2.6.24.7.old/include/asm-mips/ptrace.h linux-2.6.24.7/include/asm-mips/ptrace.h +--- linux-2.6.24.7.old/include/asm-mips/ptrace.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/include/asm-mips/ptrace.h 2009-04-12 18:13:57.000000000 +0200 +@@ -79,7 +79,7 @@ + /* + * Does the process account for user or for system time? + */ +-#define user_mode(regs) (((regs)->cp0_status & KU_MASK) == KU_USER) ++#define user_mode(regs) ((((regs)->cp0_status & KU_MASK) == KU_USER) || (((regs)->cp0_status & 0x08000000) == 0x08000000)) + + #define instruction_pointer(regs) ((regs)->cp0_epc) + #define profile_pc(regs) instruction_pointer(regs) +diff -urN linux-2.6.24.7.old/include/asm-mips/r4kcache.h linux-2.6.24.7/include/asm-mips/r4kcache.h +--- linux-2.6.24.7.old/include/asm-mips/r4kcache.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/include/asm-mips/r4kcache.h 2009-04-12 18:13:57.000000000 +0200 +@@ -17,6 +17,58 @@ + #include + #include + ++#ifdef CONFIG_JZRISC ++ ++#define K0_TO_K1() \ ++do { \ ++ unsigned long __k0_addr; \ ++ \ ++ __asm__ __volatile__( \ ++ "la %0, 1f\n\t" \ ++ "or %0, %0, %1\n\t" \ ++ "jr %0\n\t" \ ++ "nop\n\t" \ ++ "1: nop\n" \ ++ : "=&r"(__k0_addr) \ ++ : "r" (0x20000000) ); \ ++} while(0) ++ ++#define K1_TO_K0() \ ++do { \ ++ unsigned long __k0_addr; \ ++ __asm__ __volatile__( \ ++ "nop;nop;nop;nop;nop;nop;nop\n\t" \ ++ "la %0, 1f\n\t" \ ++ "jr %0\n\t" \ ++ "nop\n\t" \ ++ "1: nop\n" \ ++ : "=&r" (__k0_addr)); \ ++} while (0) ++ ++#define INVALIDATE_BTB() \ ++do { \ ++ unsigned long tmp; \ ++ __asm__ __volatile__( \ ++ ".set mips32\n\t" \ ++ "mfc0 %0, $16, 7\n\t" \ ++ "nop\n\t" \ ++ "ori %0, 2\n\t" \ ++ "mtc0 %0, $16, 7\n\t" \ ++ "nop\n\t" \ ++ : "=&r" (tmp)); \ ++} while (0) ++ ++#define SYNC_WB() __asm__ __volatile__ ("sync") ++ ++#else /* CONFIG_JZRISC */ ++ ++#define K0_TO_K1() do { } while (0) ++#define K1_TO_K0() do { } while (0) ++#define INVALIDATE_BTB() do { } while (0) ++#define SYNC_WB() do { } while (0) ++ ++#endif /* CONFIG_JZRISC */ ++ + /* + * This macro return a properly sign-extended address suitable as base address + * for indexed cache operations. Two issues here: +@@ -144,6 +196,7 @@ + { + __iflush_prologue + cache_op(Index_Invalidate_I, addr); ++ INVALIDATE_BTB(); + __iflush_epilogue + } + +@@ -151,6 +204,7 @@ + { + __dflush_prologue + cache_op(Index_Writeback_Inv_D, addr); ++ SYNC_WB(); + __dflush_epilogue + } + +@@ -163,6 +217,7 @@ + { + __iflush_prologue + cache_op(Hit_Invalidate_I, addr); ++ INVALIDATE_BTB(); + __iflush_epilogue + } + +@@ -170,6 +225,7 @@ + { + __dflush_prologue + cache_op(Hit_Writeback_Inv_D, addr); ++ SYNC_WB(); + __dflush_epilogue + } + +@@ -177,6 +233,7 @@ + { + __dflush_prologue + cache_op(Hit_Invalidate_D, addr); ++ SYNC_WB(); + __dflush_epilogue + } + +@@ -209,6 +266,7 @@ + static inline void protected_flush_icache_line(unsigned long addr) + { + protected_cache_op(Hit_Invalidate_I, addr); ++ INVALIDATE_BTB(); + } + + /* +@@ -220,6 +278,7 @@ + static inline void protected_writeback_dcache_line(unsigned long addr) + { + protected_cache_op(Hit_Writeback_Inv_D, addr); ++ SYNC_WB(); + } + + static inline void protected_writeback_scache_line(unsigned long addr) +@@ -396,13 +455,123 @@ + __BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16) + __BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 16) + __BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 16) ++#ifndef CONFIG_JZRISC + __BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 32) + __BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 32) ++#endif + __BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 32) + __BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64) + __BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 64) + __BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 128) + ++#ifdef CONFIG_JZRISC ++ ++static inline void blast_dcache32(void) ++{ ++ unsigned long start = INDEX_BASE; ++ unsigned long end = start + current_cpu_data.dcache.waysize; ++ unsigned long ws_inc = 1UL << current_cpu_data.dcache.waybit; ++ unsigned long ws_end = current_cpu_data.dcache.ways << ++ current_cpu_data.dcache.waybit; ++ unsigned long ws, addr; ++ ++ for (ws = 0; ws < ws_end; ws += ws_inc) ++ for (addr = start; addr < end; addr += 0x400) ++ cache32_unroll32(addr|ws,Index_Writeback_Inv_D); ++ ++ SYNC_WB(); ++} ++ ++static inline void blast_dcache32_page(unsigned long page) ++{ ++ unsigned long start = page; ++ unsigned long end = page + PAGE_SIZE; ++ ++ do { ++ cache32_unroll32(start,Hit_Writeback_Inv_D); ++ start += 0x400; ++ } while (start < end); ++ ++ SYNC_WB(); ++} ++ ++static inline void blast_dcache32_page_indexed(unsigned long page) ++{ ++ unsigned long indexmask = current_cpu_data.dcache.waysize - 1; ++ unsigned long start = INDEX_BASE + (page & indexmask); ++ unsigned long end = start + PAGE_SIZE; ++ unsigned long ws_inc = 1UL << current_cpu_data.dcache.waybit; ++ unsigned long ws_end = current_cpu_data.dcache.ways << ++ current_cpu_data.dcache.waybit; ++ unsigned long ws, addr; ++ ++ for (ws = 0; ws < ws_end; ws += ws_inc) ++ for (addr = start; addr < end; addr += 0x400) ++ cache32_unroll32(addr|ws,Index_Writeback_Inv_D); ++ ++ SYNC_WB(); ++} ++ ++static inline void blast_icache32(void) ++{ ++ unsigned long start = INDEX_BASE; ++ unsigned long end = start + current_cpu_data.icache.waysize; ++ unsigned long ws_inc = 1UL << current_cpu_data.icache.waybit; ++ unsigned long ws_end = current_cpu_data.icache.ways << ++ current_cpu_data.icache.waybit; ++ unsigned long ws, addr; ++ ++ K0_TO_K1(); ++ ++ for (ws = 0; ws < ws_end; ws += ws_inc) ++ for (addr = start; addr < end; addr += 0x400) ++ cache32_unroll32(addr|ws,Index_Invalidate_I); ++ ++ INVALIDATE_BTB(); ++ ++ K1_TO_K0(); ++} ++ ++static inline void blast_icache32_page(unsigned long page) ++{ ++ unsigned long start = page; ++ unsigned long end = page + PAGE_SIZE; ++ ++ K0_TO_K1(); ++ ++ do { ++ cache32_unroll32(start,Hit_Invalidate_I); ++ start += 0x400; ++ } while (start < end); ++ ++ INVALIDATE_BTB(); ++ ++ K1_TO_K0(); ++} ++ ++static inline void blast_icache32_page_indexed(unsigned long page) ++{ ++ unsigned long indexmask = current_cpu_data.icache.waysize - 1; ++ unsigned long start = INDEX_BASE + (page & indexmask); ++ unsigned long end = start + PAGE_SIZE; ++ unsigned long ws_inc = 1UL << current_cpu_data.icache.waybit; ++ unsigned long ws_end = current_cpu_data.icache.ways << ++ current_cpu_data.icache.waybit; ++ unsigned long ws, addr; ++ ++ K0_TO_K1(); ++ ++ for (ws = 0; ws < ws_end; ws += ws_inc) ++ for (addr = start; addr < end; addr += 0x400) ++ cache32_unroll32(addr|ws,Index_Invalidate_I); ++ ++ INVALIDATE_BTB(); ++ ++ K1_TO_K0(); ++} ++ ++#endif /* CONFIG_JZRISC */ ++ + /* build blast_xxx_range, protected_blast_xxx_range */ + #define __BUILD_BLAST_CACHE_RANGE(pfx, desc, hitop, prot) \ + static inline void prot##blast_##pfx##cache##_range(unsigned long start, \ +@@ -424,13 +593,73 @@ + __##pfx##flush_epilogue \ + } + ++#ifndef CONFIG_JZRISC + __BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, protected_) ++#endif + __BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, protected_) ++#ifndef CONFIG_JZRISC + __BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, protected_) + __BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, ) ++#endif + __BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, ) + /* blast_inv_dcache_range */ + __BUILD_BLAST_CACHE_RANGE(inv_d, dcache, Hit_Invalidate_D, ) + __BUILD_BLAST_CACHE_RANGE(inv_s, scache, Hit_Invalidate_SD, ) + ++#ifdef CONFIG_JZRISC ++ ++static inline void protected_blast_dcache_range(unsigned long start, ++ unsigned long end) ++{ ++ unsigned long lsize = cpu_dcache_line_size(); ++ unsigned long addr = start & ~(lsize - 1); ++ unsigned long aend = (end - 1) & ~(lsize - 1); ++ ++ while (1) { ++ protected_cache_op(Hit_Writeback_Inv_D, addr); ++ if (addr == aend) ++ break; ++ addr += lsize; ++ } ++ SYNC_WB(); ++} ++ ++static inline void protected_blast_icache_range(unsigned long start, ++ unsigned long end) ++{ ++ unsigned long lsize = cpu_icache_line_size(); ++ unsigned long addr = start & ~(lsize - 1); ++ unsigned long aend = (end - 1) & ~(lsize - 1); ++ ++ K0_TO_K1(); ++ ++ while (1) { ++ protected_cache_op(Hit_Invalidate_I, addr); ++ if (addr == aend) ++ break; ++ addr += lsize; ++ } ++ INVALIDATE_BTB(); ++ ++ K1_TO_K0(); ++} ++ ++static inline void blast_dcache_range(unsigned long start, ++ unsigned long end) ++{ ++ unsigned long lsize = cpu_dcache_line_size(); ++ unsigned long addr = start & ~(lsize - 1); ++ unsigned long aend = (end - 1) & ~(lsize - 1); ++ ++ while (1) { ++ cache_op(Hit_Writeback_Inv_D, addr); ++ if (addr == aend) ++ break; ++ addr += lsize; ++ } ++ SYNC_WB(); ++} ++ ++#endif /* CONFIG_JZRISC */ ++ + #endif /* _ASM_R4KCACHE_H */ +diff -urN linux-2.6.24.7.old/include/asm-mips/sizes.h linux-2.6.24.7/include/asm-mips/sizes.h +--- linux-2.6.24.7.old/include/asm-mips/sizes.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/asm-mips/sizes.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,56 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++/* DO NOT EDIT!! - this file automatically generated ++ * from .s file by awk -f s2h.awk ++ */ ++/* Size definitions ++ * Copyright (C) ARM Limited 1998. All rights reserved. ++ */ ++ ++#ifndef __sizes_h ++#define __sizes_h 1 ++ ++/* handy sizes */ ++#define SZ_16 0x00000010 ++#define SZ_256 0x00000100 ++#define SZ_512 0x00000200 ++ ++#define SZ_1K 0x00000400 ++#define SZ_4K 0x00001000 ++#define SZ_8K 0x00002000 ++#define SZ_16K 0x00004000 ++#define SZ_64K 0x00010000 ++#define SZ_128K 0x00020000 ++#define SZ_256K 0x00040000 ++#define SZ_512K 0x00080000 ++ ++#define SZ_1M 0x00100000 ++#define SZ_2M 0x00200000 ++#define SZ_4M 0x00400000 ++#define SZ_8M 0x00800000 ++#define SZ_16M 0x01000000 ++#define SZ_32M 0x02000000 ++#define SZ_64M 0x04000000 ++#define SZ_128M 0x08000000 ++#define SZ_256M 0x10000000 ++#define SZ_512M 0x20000000 ++ ++#define SZ_1G 0x40000000 ++#define SZ_2G 0x80000000 ++ ++#endif ++ ++/* END */ +diff -urN linux-2.6.24.7.old/include/linux/fs.h linux-2.6.24.7/include/linux/fs.h +--- linux-2.6.24.7.old/include/linux/fs.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/include/linux/fs.h 2009-04-12 18:13:57.000000000 +0200 +@@ -1670,6 +1670,8 @@ + extern int invalidate_inode_pages2(struct address_space *mapping); + extern int invalidate_inode_pages2_range(struct address_space *mapping, + pgoff_t start, pgoff_t end); ++extern void generic_sync_sb_inodes(struct super_block *sb, ++ struct writeback_control *wbc); + extern int write_inode_now(struct inode *, int); + extern int filemap_fdatawrite(struct address_space *); + extern int filemap_flush(struct address_space *); +diff -urN linux-2.6.24.7.old/include/linux/i2c-dev.h linux-2.6.24.7/include/linux/i2c-dev.h +--- linux-2.6.24.7.old/include/linux/i2c-dev.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/include/linux/i2c-dev.h 2009-04-12 18:13:57.000000000 +0200 +@@ -49,7 +49,8 @@ + + #define I2C_PEC 0x0708 /* != 0 to use PEC with SMBus */ + #define I2C_SMBUS 0x0720 /* SMBus transfer */ +- ++#define I2C_SET_SUB_ADDRESS 0x0730 /* SMBus transfer */ ++#define I2C_SET_CLOCK 0x0731 /* SMBus transfer */ + + /* This is the structure as used in the I2C_SMBUS ioctl call */ + struct i2c_smbus_ioctl_data { +@@ -71,4 +72,5 @@ + #define I2C_MAJOR 89 /* Device major number */ + #endif + ++extern void i2c_jz_setclk(unsigned int i2cclk); + #endif /* _LINUX_I2C_DEV_H */ +diff -urN linux-2.6.24.7.old/include/linux/mmc/host.h linux-2.6.24.7/include/linux/mmc/host.h +--- linux-2.6.24.7.old/include/linux/mmc/host.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/include/linux/mmc/host.h 2009-04-12 18:13:57.000000000 +0200 +@@ -41,6 +41,7 @@ + + #define MMC_BUS_WIDTH_1 0 + #define MMC_BUS_WIDTH_4 2 ++#define MMC_BUS_WIDTH_8 4 + + unsigned char timing; /* timing specification used */ + +diff -urN linux-2.6.24.7.old/include/linux/mtd/mtd.h linux-2.6.24.7/include/linux/mtd/mtd.h +--- linux-2.6.24.7.old/include/linux/mtd/mtd.h 2009-04-12 18:05:07.000000000 +0200 ++++ linux-2.6.24.7/include/linux/mtd/mtd.h 2009-04-12 18:13:57.000000000 +0200 +@@ -32,9 +32,9 @@ + specific to any particular block. */ + struct erase_info { + struct mtd_info *mtd; +- u_int32_t addr; +- u_int32_t len; +- u_int32_t fail_addr; ++ u_int64_t addr; ++ u_int64_t len; ++ u_int64_t fail_addr; + u_long time; + u_long retries; + u_int dev; +@@ -46,7 +46,7 @@ + }; + + struct mtd_erase_region_info { +- u_int32_t offset; /* At which this region starts, from the beginning of the MTD */ ++ u_int64_t offset; /* At which this region starts, from the beginning of the MTD */ + u_int32_t erasesize; /* For this region */ + u_int32_t numblocks; /* Number of blocks of erasesize in this region */ + unsigned long *lockmap; /* If keeping bitmap of locks */ +@@ -89,10 +89,10 @@ + */ + struct mtd_oob_ops { + mtd_oob_mode_t mode; +- size_t len; +- size_t retlen; +- size_t ooblen; +- size_t oobretlen; ++ size_mtd_t len; ++ size_mtd_t retlen; ++ size_mtd_t ooblen; ++ size_mtd_t oobretlen; + uint32_t ooboffs; + uint8_t *datbuf; + uint8_t *oobbuf; +@@ -102,7 +102,7 @@ + struct mtd_info { + u_char type; + u_int32_t flags; +- u_int32_t size; // Total size of the MTD ++ u_int64_t size; // Total size of the MTD + + /* "Major" erase size for the device. Naïve users may take this + * to be the only erase size available, or may use the more detailed +@@ -144,18 +144,18 @@ + int (*erase) (struct mtd_info *mtd, struct erase_info *instr); + + /* This stuff for eXecute-In-Place */ +- int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf); ++ int (*point) (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, size_mtd_t *retlen, u_char **mtdbuf); + + /* We probably shouldn't allow XIP if the unpoint isn't a NULL */ +- void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len); ++ void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_mtd_t from, size_mtd_t len); + + +- 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 (*read) (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, size_mtd_t *retlen, u_char *buf); ++ int (*write) (struct mtd_info *mtd, loff_mtd_t to, size_mtd_t len, size_mtd_t *retlen, const u_char *buf); + +- int (*read_oob) (struct mtd_info *mtd, loff_t from, ++ int (*read_oob) (struct mtd_info *mtd, loff_mtd_t from, + struct mtd_oob_ops *ops); +- int (*write_oob) (struct mtd_info *mtd, loff_t to, ++ int (*write_oob) (struct mtd_info *mtd, loff_mtd_t to, + struct mtd_oob_ops *ops); + + /* +@@ -163,33 +163,33 @@ + * flash devices. The user data is one time programmable but the + * factory data is read only. + */ +- 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 from, 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 (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_mtd_t len); ++ int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, size_mtd_t *retlen, u_char *buf); ++ int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_mtd_t len); ++ int (*read_user_prot_reg) (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, size_mtd_t *retlen, u_char *buf); ++ int (*write_user_prot_reg) (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, size_mtd_t *retlen, u_char *buf); ++ int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len); + + /* kvec-based read/write methods. + NB: The 'count' parameter is the number of _vectors_, each of + which contains an (ofs, len) tuple. + */ +- int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); ++ int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_mtd_t to, size_mtd_t *retlen); + + /* Sync */ + void (*sync) (struct mtd_info *mtd); + + /* Chip-supported device locking */ +- int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len); +- int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len); ++ int (*lock) (struct mtd_info *mtd, loff_mtd_t ofs, size_mtd_t len); ++ int (*unlock) (struct mtd_info *mtd, loff_mtd_t ofs, size_mtd_t len); + + /* Power Management functions */ + int (*suspend) (struct mtd_info *mtd); + void (*resume) (struct mtd_info *mtd); + + /* Bad block management functions */ +- int (*block_isbad) (struct mtd_info *mtd, loff_t ofs); +- int (*block_markbad) (struct mtd_info *mtd, loff_t ofs); ++ int (*block_isbad) (struct mtd_info *mtd, loff_mtd_t ofs); ++ int (*block_markbad) (struct mtd_info *mtd, loff_mtd_t ofs); + + struct notifier_block reboot_notifier; /* default mode before reboot */ + +@@ -237,10 +237,10 @@ + extern int unregister_mtd_user (struct mtd_notifier *old); + + int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, +- unsigned long count, loff_t to, size_t *retlen); ++ unsigned long count, loff_mtd_t to, size_mtd_t *retlen); + + int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs, +- unsigned long count, loff_t from, size_t *retlen); ++ unsigned long count, loff_mtd_t from, size_mtd_t *retlen); + + #ifdef CONFIG_MTD_PARTITIONS + void mtd_erase_callback(struct erase_info *instr); +diff -urN linux-2.6.24.7.old/include/linux/mtd/mtd.h.orig linux-2.6.24.7/include/linux/mtd/mtd.h.orig +--- linux-2.6.24.7.old/include/linux/mtd/mtd.h.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/linux/mtd/mtd.h.orig 2009-04-12 18:01:55.000000000 +0200 +@@ -0,0 +1,274 @@ ++/* ++ * $Id: mtd.h,v 1.61 2005/11/07 11:14:54 gleixner Exp $ ++ * ++ * Copyright (C) 1999-2003 David Woodhouse et al. ++ * ++ * Released under GPL ++ */ ++ ++#ifndef __MTD_MTD_H__ ++#define __MTD_MTD_H__ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define MTD_CHAR_MAJOR 90 ++#define MTD_BLOCK_MAJOR 31 ++#define MAX_MTD_DEVICES 32 ++ ++#define MTD_ERASE_PENDING 0x01 ++#define MTD_ERASING 0x02 ++#define MTD_ERASE_SUSPEND 0x04 ++#define MTD_ERASE_DONE 0x08 ++#define MTD_ERASE_FAILED 0x10 ++ ++/* If the erase fails, fail_addr might indicate exactly which block failed. If ++ fail_addr = 0xffffffff, the failure was not at the device level or was not ++ specific to any particular block. */ ++struct erase_info { ++ struct mtd_info *mtd; ++ u_int32_t addr; ++ u_int32_t len; ++ u_int32_t fail_addr; ++ u_long time; ++ u_long retries; ++ u_int dev; ++ u_int cell; ++ void (*callback) (struct erase_info *self); ++ u_long priv; ++ u_char state; ++ struct erase_info *next; ++}; ++ ++struct mtd_erase_region_info { ++ u_int32_t offset; /* At which this region starts, from the beginning of the MTD */ ++ u_int32_t erasesize; /* For this region */ ++ u_int32_t numblocks; /* Number of blocks of erasesize in this region */ ++ unsigned long *lockmap; /* If keeping bitmap of locks */ ++}; ++ ++/* ++ * oob operation modes ++ * ++ * MTD_OOB_PLACE: oob data are placed at the given offset ++ * MTD_OOB_AUTO: oob data are automatically placed at the free areas ++ * which are defined by the ecclayout ++ * MTD_OOB_RAW: mode to read raw data+oob in one chunk. The oob data ++ * is inserted into the data. Thats a raw image of the ++ * flash contents. ++ */ ++typedef enum { ++ MTD_OOB_PLACE, ++ MTD_OOB_AUTO, ++ MTD_OOB_RAW, ++} mtd_oob_mode_t; ++ ++/** ++ * struct mtd_oob_ops - oob operation operands ++ * @mode: operation mode ++ * ++ * @len: number of data bytes to write/read ++ * ++ * @retlen: number of data bytes written/read ++ * ++ * @ooblen: number of oob bytes to write/read ++ * @oobretlen: number of oob bytes written/read ++ * @ooboffs: offset of oob data in the oob area (only relevant when ++ * mode = MTD_OOB_PLACE) ++ * @datbuf: data buffer - if NULL only oob data are read/written ++ * @oobbuf: oob data buffer ++ * ++ * Note, it is allowed to read more then one OOB area at one go, but not write. ++ * The interface assumes that the OOB write requests program only one page's ++ * OOB area. ++ */ ++struct mtd_oob_ops { ++ mtd_oob_mode_t mode; ++ size_t len; ++ size_t retlen; ++ size_t ooblen; ++ size_t oobretlen; ++ uint32_t ooboffs; ++ uint8_t *datbuf; ++ uint8_t *oobbuf; ++}; ++ ++struct mtd_info; ++struct mtd_info { ++ u_char type; ++ u_int32_t flags; ++ u_int32_t size; // Total size of the MTD ++ ++ /* "Major" erase size for the device. Naïve users may take this ++ * to be the only erase size available, or may use the more detailed ++ * information below if they desire ++ */ ++ u_int32_t erasesize; ++ /* Minimal writable flash unit size. In case of NOR flash it is 1 (even ++ * though individual bits can be cleared), in case of NAND flash it is ++ * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR ++ * it is of ECC block size, etc. It is illegal to have writesize = 0. ++ * Any driver registering a struct mtd_info must ensure a writesize of ++ * 1 or larger. ++ */ ++ u_int32_t writesize; ++ ++ u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) ++ u_int32_t oobavail; // Available OOB bytes per block ++ ++ // Kernel-only stuff starts here. ++ char *name; ++ int index; ++ ++ /* ecc layout structure pointer - read only ! */ ++ struct nand_ecclayout *ecclayout; ++ ++ /* Data for variable erase regions. If numeraseregions is zero, ++ * it means that the whole device has erasesize as given above. ++ */ ++ int numeraseregions; ++ struct mtd_erase_region_info *eraseregions; ++ ++ /* ++ * 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 (*erase) (struct mtd_info *mtd, struct erase_info *instr); ++ ++ /* This stuff for eXecute-In-Place */ ++ int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf); ++ ++ /* We probably shouldn't allow XIP if the unpoint isn't a NULL */ ++ void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len); ++ ++ ++ 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 (*read_oob) (struct mtd_info *mtd, loff_t from, ++ struct mtd_oob_ops *ops); ++ int (*write_oob) (struct mtd_info *mtd, loff_t to, ++ struct mtd_oob_ops *ops); ++ ++ /* ++ * Methods 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 (*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 from, size_t len, size_t *retlen, u_char *buf); ++ int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len); ++ ++ /* kvec-based read/write methods. ++ NB: The 'count' parameter is the number of _vectors_, each of ++ which contains an (ofs, len) tuple. ++ */ ++ int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); ++ ++ /* Sync */ ++ void (*sync) (struct mtd_info *mtd); ++ ++ /* Chip-supported device locking */ ++ int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len); ++ int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len); ++ ++ /* Power Management functions */ ++ int (*suspend) (struct mtd_info *mtd); ++ void (*resume) (struct mtd_info *mtd); ++ ++ /* Bad block management functions */ ++ int (*block_isbad) (struct mtd_info *mtd, loff_t ofs); ++ int (*block_markbad) (struct mtd_info *mtd, loff_t ofs); ++ ++ struct notifier_block reboot_notifier; /* default mode before reboot */ ++ ++ /* ECC status information */ ++ struct mtd_ecc_stats ecc_stats; ++ /* Subpage shift (NAND) */ ++ int subpage_sft; ++ ++ void *priv; ++ ++ struct module *owner; ++ int usecount; ++ ++ int (*refresh_device)(struct mtd_info *mtd); ++ struct mtd_info *split; ++ ++ /* If the driver is something smart, like UBI, it may need to maintain ++ * its own reference counting. The below functions are only for driver. ++ * The driver may register its callbacks. These callbacks are not ++ * supposed to be called by MTD users */ ++ int (*get_device) (struct mtd_info *mtd); ++ void (*put_device) (struct mtd_info *mtd); ++}; ++ ++ ++ /* Kernel-side ioctl definitions */ ++ ++extern int add_mtd_device(struct mtd_info *mtd); ++extern int del_mtd_device (struct mtd_info *mtd); ++ ++extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); ++extern struct mtd_info *get_mtd_device_nm(const char *name); ++ ++extern void put_mtd_device(struct mtd_info *mtd); ++ ++ ++struct mtd_notifier { ++ void (*add)(struct mtd_info *mtd); ++ void (*remove)(struct mtd_info *mtd); ++ struct list_head list; ++}; ++ ++ ++extern void register_mtd_user (struct mtd_notifier *new); ++extern int unregister_mtd_user (struct mtd_notifier *old); ++ ++int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, ++ unsigned long count, loff_t to, size_t *retlen); ++ ++int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs, ++ unsigned long count, loff_t from, size_t *retlen); ++ ++#ifdef CONFIG_MTD_PARTITIONS ++void mtd_erase_callback(struct erase_info *instr); ++#else ++static inline void mtd_erase_callback(struct erase_info *instr) ++{ ++ if (instr->callback) ++ instr->callback(instr); ++} ++#endif ++ ++/* ++ * Debugging macro and defines ++ */ ++#define MTD_DEBUG_LEVEL0 (0) /* Quiet */ ++#define MTD_DEBUG_LEVEL1 (1) /* Audible */ ++#define MTD_DEBUG_LEVEL2 (2) /* Loud */ ++#define MTD_DEBUG_LEVEL3 (3) /* Noisy */ ++ ++#ifdef CONFIG_MTD_DEBUG ++#define DEBUG(n, args...) \ ++ do { \ ++ if (n <= CONFIG_MTD_DEBUG_VERBOSE) \ ++ printk(KERN_INFO args); \ ++ } while(0) ++#else /* CONFIG_MTD_DEBUG */ ++#define DEBUG(n, args...) do { } while(0) ++ ++#endif /* CONFIG_MTD_DEBUG */ ++ ++#endif /* __MTD_MTD_H__ */ +diff -urN linux-2.6.24.7.old/include/linux/mtd/nand.h linux-2.6.24.7/include/linux/mtd/nand.h +--- linux-2.6.24.7.old/include/linux/mtd/nand.h 2009-04-12 18:05:07.000000000 +0200 ++++ linux-2.6.24.7/include/linux/mtd/nand.h 2009-04-12 18:13:57.000000000 +0200 +@@ -39,14 +39,14 @@ + extern void nand_wait_ready(struct mtd_info *mtd); + + /* The maximum number of NAND chips in an array */ +-#define NAND_MAX_CHIPS 8 ++#define NAND_MAX_CHIPS 4 + + /* This constant declares the max. oobsize / page, which + * is supported now. If you add a chip with bigger oobsize/page + * adjust this accordingly. + */ +-#define NAND_MAX_OOBSIZE 64 +-#define NAND_MAX_PAGESIZE 2048 ++#define NAND_MAX_OOBSIZE 256 ++#define NAND_MAX_PAGESIZE 8192 + + /* + * Constants for hardware specific CLE/ALE/NCE function +@@ -55,6 +55,10 @@ + * bits in one go. + */ + /* Select the chip by setting nCE to low */ ++#define NAND_NCE1 0x08 ++#define NAND_NCE2 0x10 ++#define NAND_NCE3 0x20 ++#define NAND_NCE4 0x40 + #define NAND_NCE 0x01 + /* Select the command latch by setting CLE to high */ + #define NAND_CLE 0x02 +@@ -372,8 +376,8 @@ + void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); + int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); + void (*select_chip)(struct mtd_info *mtd, int chip); +- int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); +- int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); ++ int (*block_bad)(struct mtd_info *mtd, loff_mtd_t ofs, int getchip); ++ int (*block_markbad)(struct mtd_info *mtd, loff_mtd_t ofs); + void (*cmd_ctrl)(struct mtd_info *mtd, int dat, + unsigned int ctrl); + int (*dev_ready)(struct mtd_info *mtd); +@@ -393,12 +397,14 @@ + int bbt_erase_shift; + int chip_shift; + int numchips; +- unsigned long chipsize; ++ u64 chipsize; + int pagemask; + int pagebuf; + int subpagesize; + uint8_t cellinfo; + int badblockpos; ++ int realplanenum; /* number of planes the NAND has */ ++ int planenum; /* number of planes operating synchronously */ + + nand_state_t state; + +@@ -450,7 +456,7 @@ + char *name; + int id; + unsigned long pagesize; +- unsigned long chipsize; ++ u64 chipsize; + unsigned long erasesize; + unsigned long options; + }; +@@ -538,13 +544,13 @@ + #define NAND_BBT_SCAN_MAXBLOCKS 4 + + extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd); +-extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs); ++extern int nand_update_bbt(struct mtd_info *mtd, loff_mtd_t offs); + extern int nand_default_bbt(struct mtd_info *mtd); +-extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt); ++extern int nand_isbad_bbt(struct mtd_info *mtd, loff_mtd_t offs, int allowbbt); + extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, + int allowbbt); +-extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, +- size_t * retlen, uint8_t * buf); ++extern int nand_do_read(struct mtd_info *mtd, loff_mtd_t from, size_mtd_t len, ++ size_mtd_t * retlen, uint8_t * buf); + + /* + * Constants for oob configuration +diff -urN linux-2.6.24.7.old/include/linux/mtd/partitions.h linux-2.6.24.7/include/linux/mtd/partitions.h +--- linux-2.6.24.7.old/include/linux/mtd/partitions.h 2009-04-12 18:05:07.000000000 +0200 ++++ linux-2.6.24.7/include/linux/mtd/partitions.h 2009-04-12 18:13:57.000000000 +0200 +@@ -39,8 +39,9 @@ + struct mtd_partition; + struct mtd_partition { + char *name; /* identifier string */ +- u_int32_t size; /* partition size */ +- u_int32_t offset; /* offset within the master MTD space */ ++ u_int64_t size; /* partition size */ ++ u_int64_t offset; /* offset within the master MTD space */ ++ char use_planes; /* flag to specify whether multiple planes of NAND is used in the partition */ + u_int32_t mask_flags; /* master MTD flags to mask out for this partition */ + struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ + struct mtd_info **mtdp; /* pointer to store the MTD object */ +diff -urN linux-2.6.24.7.old/include/linux/mtd/partitions.h.orig linux-2.6.24.7/include/linux/mtd/partitions.h.orig +--- linux-2.6.24.7.old/include/linux/mtd/partitions.h.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/linux/mtd/partitions.h.orig 2009-04-12 18:01:55.000000000 +0200 +@@ -0,0 +1,78 @@ ++/* ++ * MTD partitioning layer definitions ++ * ++ * (C) 2000 Nicolas Pitre ++ * ++ * This code is GPL ++ * ++ * $Id: partitions.h,v 1.17 2005/11/07 11:14:55 gleixner Exp $ ++ */ ++ ++#ifndef MTD_PARTITIONS_H ++#define MTD_PARTITIONS_H ++ ++#include ++ ++ ++/* ++ * Partition definition structure: ++ * ++ * An array of struct partition is passed along with a MTD object to ++ * add_mtd_partitions() to create them. ++ * ++ * For each partition, these fields are available: ++ * name: string that will be used to label the partition's MTD device. ++ * size: the partition size; if defined as MTDPART_SIZ_FULL, the partition ++ * will extend to the end of the master MTD device. ++ * offset: absolute starting position within the master MTD device; if ++ * defined as MTDPART_OFS_APPEND, the partition will start where the ++ * previous one ended; if MTDPART_OFS_NXTBLK, at the next erase block. ++ * mask_flags: contains flags that have to be masked (removed) from the ++ * master MTD flag set for the corresponding MTD partition. ++ * For example, to force a read-only partition, simply adding ++ * MTD_WRITEABLE to the mask_flags will do the trick. ++ * ++ * Note: writeable partitions require their size and offset be ++ * erasesize aligned (e.g. use MTDPART_OFS_NEXTBLK). ++ */ ++ ++struct mtd_partition; ++struct mtd_partition { ++ char *name; /* identifier string */ ++ u_int32_t size; /* partition size */ ++ u_int32_t offset; /* offset within the master MTD space */ ++ u_int32_t mask_flags; /* master MTD flags to mask out for this partition */ ++ struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ ++ struct mtd_info **mtdp; /* pointer to store the MTD object */ ++ int (*refresh_partition)(struct mtd_info *); ++}; ++ ++#define MTDPART_OFS_NXTBLK (-2) ++#define MTDPART_OFS_APPEND (-1) ++#define MTDPART_SIZ_FULL (0) ++ ++ ++int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); ++int del_mtd_partitions(struct mtd_info *); ++int refresh_mtd_partitions(struct mtd_info *); ++ ++/* ++ * Functions dealing with the various ways of partitioning the space ++ */ ++ ++struct mtd_part_parser { ++ struct list_head list; ++ struct module *owner; ++ const char *name; ++ int (*parse_fn)(struct mtd_info *, struct mtd_partition **, unsigned long); ++}; ++ ++extern int register_mtd_parser(struct mtd_part_parser *parser); ++extern int deregister_mtd_parser(struct mtd_part_parser *parser); ++extern int parse_mtd_partitions(struct mtd_info *master, const char **types, ++ struct mtd_partition **pparts, unsigned long origin); ++ ++#define put_partition_parser(p) do { module_put((p)->owner); } while(0) ++ ++#endif ++ +diff -urN linux-2.6.24.7.old/include/linux/mtd/ubi.h linux-2.6.24.7/include/linux/mtd/ubi.h +--- linux-2.6.24.7.old/include/linux/mtd/ubi.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/include/linux/mtd/ubi.h 2009-04-12 18:13:57.000000000 +0200 +@@ -26,23 +26,6 @@ + #include + + /* +- * UBI data type hint constants. +- * +- * UBI_LONGTERM: long-term data +- * UBI_SHORTTERM: short-term data +- * UBI_UNKNOWN: data persistence is unknown +- * +- * These constants are used when data is written to UBI volumes in order to +- * help the UBI wear-leveling unit to find more appropriate physical +- * eraseblocks. +- */ +-enum { +- UBI_LONGTERM = 1, +- UBI_SHORTTERM, +- UBI_UNKNOWN +-}; +- +-/* + * enum ubi_open_mode - UBI volume open mode constants. + * + * UBI_READONLY: read-only mode +@@ -167,6 +150,7 @@ + int len, int dtype); + int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum); + int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum); ++int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype); + int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum); + + /* +diff -urN linux-2.6.24.7.old/include/linux/vt.h linux-2.6.24.7/include/linux/vt.h +--- linux-2.6.24.7.old/include/linux/vt.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/include/linux/vt.h 2009-04-12 18:13:57.000000000 +0200 +@@ -18,10 +18,16 @@ + * resizing). + */ + #define MIN_NR_CONSOLES 1 /* must be at least 1 */ +-#define MAX_NR_CONSOLES 63 /* serial lines start at 64 */ +-#define MAX_NR_USER_CONSOLES 63 /* must be root to allocate above this */ +- /* Note: the ioctl VT_GETSTATE does not work for +- consoles 16 and higher (since it returns a short) */ ++ ++#if defined(CONFIG_JZSOC) ++#define MAX_NR_CONSOLES 2 ++#define MAX_NR_USER_CONSOLES 2 ++#else ++#define MAX_NR_CONSOLES 63 /* serial lines start at 64 */ ++#define MAX_NR_USER_CONSOLES 63 /* must be root to allocate above this */ ++ /* Note: the ioctl VT_GETSTATE does not work for ++ consoles 16 and higher (since it returns a short) */ ++#endif + + /* 0x56 is 'V', to avoid collision with termios and kd */ + +diff -urN linux-2.6.24.7.old/include/linux/writeback.h linux-2.6.24.7/include/linux/writeback.h +--- linux-2.6.24.7.old/include/linux/writeback.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/include/linux/writeback.h 2009-04-12 18:13:57.000000000 +0200 +@@ -70,6 +70,7 @@ + void writeback_inodes(struct writeback_control *wbc); + int inode_wait(void *); + void sync_inodes_sb(struct super_block *, int wait); ++void writeback_inodes_sb(struct super_block *sb, struct writeback_control *wbc); + void sync_inodes(int wait); + + /* writeback.h requires fs.h; it, too, is not included from here. */ +diff -urN linux-2.6.24.7.old/include/mtd/mtd-abi.h linux-2.6.24.7/include/mtd/mtd-abi.h +--- linux-2.6.24.7.old/include/mtd/mtd-abi.h 2009-04-12 18:05:07.000000000 +0200 ++++ linux-2.6.24.7/include/mtd/mtd-abi.h 2009-04-12 18:23:10.000000000 +0200 +@@ -7,9 +7,18 @@ + #ifndef __MTD_ABI_H__ + #define __MTD_ABI_H__ + ++ ++#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into ++ separate files was to avoid #ifdef __KERNEL__ */ ++#define __user ++#endif ++ ++typedef unsigned long long size_mtd_t; ++typedef unsigned long long loff_mtd_t; ++ + struct erase_info_user { +- uint32_t start; +- uint32_t length; ++ uint64_t start; ++ uint64_t length; + }; + + struct mtd_oob_buf { +@@ -18,6 +27,14 @@ + unsigned char __user *ptr; + }; + ++struct mtd_page_buf { ++ uint32_t start; //page start address ++ uint32_t ooblength; ++ uint32_t datlength; ++ unsigned char __user *oobptr; ++ unsigned char __user *datptr; ++}; ++ + #define MTD_ABSENT 0 + #define MTD_RAM 1 + #define MTD_ROM 2 +@@ -52,7 +69,7 @@ + struct mtd_info_user { + uint8_t type; + uint32_t flags; +- uint32_t size; // Total size of the MTD ++ uint64_t size; // Total size of the MTD + uint32_t erasesize; + uint32_t writesize; + uint32_t oobsize; // Amount of OOB data per block (e.g. 16) +@@ -63,7 +80,7 @@ + }; + + struct region_info_user { +- uint32_t offset; /* At which this region starts, ++ uint64_t offset; /* At which this region starts, + * from the beginning of the MTD */ + uint32_t erasesize; /* For this region */ + uint32_t numblocks; /* Number of blocks in this region */ +@@ -86,8 +103,8 @@ + #define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) + #define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) + #define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo) +-#define MEMGETBADBLOCK _IOW('M', 11, loff_t) +-#define MEMSETBADBLOCK _IOW('M', 12, loff_t) ++#define MEMGETBADBLOCK _IOW('M', 11, loff_mtd_t) ++#define MEMSETBADBLOCK _IOW('M', 12, loff_mtd_t) + #define OTPSELECT _IOR('M', 13, int) + #define OTPGETREGIONCOUNT _IOW('M', 14, int) + #define OTPGETREGIONINFO _IOW('M', 15, struct otp_info) +@@ -95,6 +112,7 @@ + #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) + #define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) + #define MTDFILEMODE _IO('M', 19) ++#define MEMWRITEPAGE _IOWR('M', 20, struct mtd_page_buf) + #define MTDREFRESH _IO('M', 23) + + /* +@@ -105,7 +123,7 @@ + uint32_t useecc; + uint32_t eccbytes; + uint32_t oobfree[8][2]; +- uint32_t eccpos[32]; ++ uint32_t eccpos[104]; + }; + + struct nand_oobfree { +@@ -120,7 +138,7 @@ + */ + struct nand_ecclayout { + uint32_t eccbytes; +- uint32_t eccpos[64]; ++ uint32_t eccpos[128]; + uint32_t oobavail; + struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; + }; +diff -urN linux-2.6.24.7.old/include/mtd/ubi-user.h linux-2.6.24.7/include/mtd/ubi-user.h +--- linux-2.6.24.7.old/include/mtd/ubi-user.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/include/mtd/ubi-user.h 2009-04-12 18:13:57.000000000 +0200 +@@ -21,7 +21,26 @@ + #ifndef __UBI_USER_H__ + #define __UBI_USER_H__ + ++#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into ++ separate files was to avoid #ifdef __KERNEL__ */ ++#define __user ++#endif + /* ++ * UBI device creation (the same as MTD device attachment) ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * MTD devices may be attached using %UBI_IOCATT ioctl command of the UBI ++ * control device. The caller has to properly fill and pass ++ * &struct ubi_attach_req object - UBI will attach the MTD device specified in ++ * the request and return the newly created UBI device number as the ioctl ++ * return value. ++ * ++ * UBI device deletion (the same as MTD device detachment) ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * An UBI device maybe deleted with %UBI_IOCDET ioctl command of the UBI ++ * control device. ++ * + * UBI volume creation + * ~~~~~~~~~~~~~~~~~~~ + * +@@ -48,7 +67,7 @@ + * + * Volume update should be done via the %UBI_IOCVOLUP IOCTL command of the + * corresponding UBI volume character device. A pointer to a 64-bit update +- * size should be passed to the IOCTL. After then, UBI expects user to write ++ * size should be passed to the IOCTL. After this, UBI expects user to write + * this number of bytes to the volume character device. The update is finished + * when the claimed number of bytes is passed. So, the volume update sequence + * is something like: +@@ -57,14 +76,24 @@ + * ioctl(fd, UBI_IOCVOLUP, &image_size); + * write(fd, buf, image_size); + * close(fd); ++ * ++ * Atomic eraseblock change ++ * ~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * Atomic eraseblock change operation is done via the %UBI_IOCEBCH IOCTL ++ * command of the corresponding UBI volume character device. A pointer to ++ * &struct ubi_leb_change_req has to be passed to the IOCTL. Then the user is ++ * expected to write the requested amount of bytes. This is similar to the ++ * "volume update" IOCTL. + */ + + /* +- * When a new volume is created, users may either specify the volume number they +- * want to create or to let UBI automatically assign a volume number using this +- * constant. ++ * When a new UBI volume or UBI device is created, users may either specify the ++ * volume/device number they want to create or to let UBI automatically assign ++ * the number using these constants. + */ + #define UBI_VOL_NUM_AUTO (-1) ++#define UBI_DEV_NUM_AUTO (-1) + + /* Maximum volume name length */ + #define UBI_MAX_VOLUME_NAME 127 +@@ -80,6 +109,15 @@ + /* Re-size an UBI volume */ + #define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req) + ++/* IOCTL commands of the UBI control character device */ ++ ++#define UBI_CTRL_IOC_MAGIC 'o' ++ ++/* Attach an MTD device */ ++#define UBI_IOCATT _IOW(UBI_CTRL_IOC_MAGIC, 64, struct ubi_attach_req) ++/* Detach an MTD device */ ++#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t) ++ + /* IOCTL commands of UBI volume character devices */ + + #define UBI_VOL_IOC_MAGIC 'O' +@@ -88,6 +126,30 @@ + #define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t) + /* An eraseblock erasure command, used for debugging, disabled by default */ + #define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t) ++/* An atomic eraseblock change command */ ++#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t) ++/* Start UBI leb read */ ++#define UBI_IOCLEBREAD _IOWR(UBI_VOL_IOC_MAGIC, 3, struct ubi_leb) ++ ++/* Maximum MTD device name length supported by UBI */ ++#define MAX_UBI_MTD_NAME_LEN 127 ++ ++/* ++ * UBI data type hint constants. ++ * ++ * UBI_LONGTERM: long-term data ++ * UBI_SHORTTERM: short-term data ++ * UBI_UNKNOWN: data persistence is unknown ++ * ++ * These constants are used when data is written to UBI volumes in order to ++ * help the UBI wear-leveling unit to find more appropriate physical ++ * eraseblocks. ++ */ ++enum { ++ UBI_LONGTERM = 1, ++ UBI_SHORTTERM = 2, ++ UBI_UNKNOWN = 3, ++}; + + /* + * UBI volume type constants. +@@ -97,22 +159,58 @@ + */ + enum { + UBI_DYNAMIC_VOLUME = 3, +- UBI_STATIC_VOLUME = 4 ++ UBI_STATIC_VOLUME = 4, ++}; ++ ++/** ++ * struct ubi_attach_req - attach MTD device request. ++ * @ubi_num: UBI device number to create ++ * @mtd_num: MTD device number to attach ++ * @vid_hdr_offset: VID header offset (use defaults if %0) ++ * @padding: reserved for future, not used, has to be zeroed ++ * ++ * This data structure is used to specify MTD device UBI has to attach and the ++ * parameters it has to use. The number which should be assigned to the new UBI ++ * device is passed in @ubi_num. UBI may automatically assign the number if ++ * @UBI_DEV_NUM_AUTO is passed. In this case, the device number is returned in ++ * @ubi_num. ++ * ++ * Most applications should pass %0 in @vid_hdr_offset to make UBI use default ++ * offset of the VID header within physical eraseblocks. The default offset is ++ * the next min. I/O unit after the EC header. For example, it will be offset ++ * 512 in case of a 512 bytes page NAND flash with no sub-page support. Or ++ * it will be 512 in case of a 2KiB page NAND flash with 4 512-byte sub-pages. ++ * ++ * But in rare cases, if this optimizes things, the VID header may be placed to ++ * a different offset. For example, the boot-loader might do things faster if the ++ * VID header sits at the end of the first 2KiB NAND page with 4 sub-pages. As ++ * the boot-loader would not normally need to read EC headers (unless it needs ++ * UBI in RW mode), it might be faster to calculate ECC. This is weird example, ++ * but it real-life example. So, in this example, @vid_hdr_offer would be ++ * 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes ++ * aligned, which is OK, as UBI is clever enough to realize this is 4th sub-page ++ * of the first page and add needed padding. ++ */ ++struct ubi_attach_req { ++ int32_t ubi_num; ++ int32_t mtd_num; ++ int32_t vid_hdr_offset; ++ uint8_t padding[12]; + }; + + /** + * struct ubi_mkvol_req - volume description data structure used in +- * volume creation requests. ++ * volume creation requests. + * @vol_id: volume number + * @alignment: volume alignment + * @bytes: volume size in bytes + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) +- * @padding1: reserved for future, not used ++ * @padding1: reserved for future, not used, has to be zeroed + * @name_len: volume name length +- * @padding2: reserved for future, not used ++ * @padding2: reserved for future, not used, has to be zeroed + * @name: volume name + * +- * This structure is used by userspace programs when creating new volumes. The ++ * This structure is used by user-space programs when creating new volumes. The + * @used_bytes field is only necessary when creating static volumes. + * + * The @alignment field specifies the required alignment of the volume logical +@@ -139,7 +237,7 @@ + int8_t padding1; + int16_t name_len; + int8_t padding2[4]; +- char name[UBI_MAX_VOLUME_NAME+1]; ++ char name[UBI_MAX_VOLUME_NAME + 1]; + } __attribute__ ((packed)); + + /** +@@ -158,4 +256,29 @@ + int32_t vol_id; + } __attribute__ ((packed)); + ++/** ++ * struct ubi_leb_change_req - a data structure used in atomic logical ++ * eraseblock change requests. ++ * @lnum: logical eraseblock number to change ++ * @bytes: how many bytes will be written to the logical eraseblock ++ * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN) ++ * @padding: reserved for future, not used, has to be zeroed ++ */ ++struct ubi_leb_change_req { ++ int32_t lnum; ++ int32_t bytes; ++ uint8_t dtype; ++ uint8_t padding[7]; ++} __attribute__ ((packed)); ++ ++/** ++ * struct ubi_leb - a data structure describe LEB. ++ * @lnum: logical eraseblock number to dump ++ * @lebbuf: LEB data buffer ++ */ ++struct ubi_leb{ ++ unsigned int lnum; ++ char __user *buf; ++}; ++ + #endif /* __UBI_USER_H__ */ +diff -urN linux-2.6.24.7.old/include/sound/pcm.h linux-2.6.24.7/include/sound/pcm.h +--- linux-2.6.24.7.old/include/sound/pcm.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/include/sound/pcm.h 2009-04-12 18:13:57.000000000 +0200 +@@ -107,23 +107,23 @@ + #define SNDRV_PCM_RATE_5512 (1<<0) /* 5512Hz */ + #define SNDRV_PCM_RATE_8000 (1<<1) /* 8000Hz */ + #define SNDRV_PCM_RATE_11025 (1<<2) /* 11025Hz */ +-#define SNDRV_PCM_RATE_16000 (1<<3) /* 16000Hz */ +-#define SNDRV_PCM_RATE_22050 (1<<4) /* 22050Hz */ +-#define SNDRV_PCM_RATE_32000 (1<<5) /* 32000Hz */ +-#define SNDRV_PCM_RATE_44100 (1<<6) /* 44100Hz */ +-#define SNDRV_PCM_RATE_48000 (1<<7) /* 48000Hz */ +-#define SNDRV_PCM_RATE_64000 (1<<8) /* 64000Hz */ +-#define SNDRV_PCM_RATE_88200 (1<<9) /* 88200Hz */ +-#define SNDRV_PCM_RATE_96000 (1<<10) /* 96000Hz */ +-#define SNDRV_PCM_RATE_176400 (1<<11) /* 176400Hz */ +-#define SNDRV_PCM_RATE_192000 (1<<12) /* 192000Hz */ ++#define SNDRV_PCM_RATE_12000 (1<<3) /* 12000Hz */ ++#define SNDRV_PCM_RATE_16000 (1<<4) /* 16000Hz */ ++#define SNDRV_PCM_RATE_22050 (1<<5) /* 22050Hz */ ++#define SNDRV_PCM_RATE_24000 (1<<6) /* 24000Hz */ ++#define SNDRV_PCM_RATE_32000 (1<<7) /* 32000Hz */ ++#define SNDRV_PCM_RATE_44100 (1<<8) /* 44100Hz */ ++#define SNDRV_PCM_RATE_48000 (1<<9) /* 48000Hz */ ++#define SNDRV_PCM_RATE_64000 (1<<10) /* 64000Hz */ ++#define SNDRV_PCM_RATE_88200 (1<<11) /* 88200Hz */ ++#define SNDRV_PCM_RATE_96000 (1<<12) /* 96000Hz */ ++#define SNDRV_PCM_RATE_176400 (1<<13) /* 176400Hz */ ++#define SNDRV_PCM_RATE_192000 (1<<14) /* 192000Hz */ + + #define SNDRV_PCM_RATE_CONTINUOUS (1<<30) /* continuous range */ + #define SNDRV_PCM_RATE_KNOT (1<<31) /* supports more non-continuos rates */ + +-#define SNDRV_PCM_RATE_8000_44100 (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_11025|\ +- SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_22050|\ +- SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100) ++#define SNDRV_PCM_RATE_8000_44100 (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_11025|SNDRV_PCM_RATE_12000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_22050|SNDRV_PCM_RATE_24000|SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100) + #define SNDRV_PCM_RATE_8000_48000 (SNDRV_PCM_RATE_8000_44100|SNDRV_PCM_RATE_48000) + #define SNDRV_PCM_RATE_8000_96000 (SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_64000|\ + SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000) +diff -urN linux-2.6.24.7.old/include/sound/pcm.h.org linux-2.6.24.7/include/sound/pcm.h.org +--- linux-2.6.24.7.old/include/sound/pcm.h.org 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/include/sound/pcm.h.org 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1011 @@ ++#ifndef __SOUND_PCM_H ++#define __SOUND_PCM_H ++ ++/* ++ * Digital Audio (PCM) abstract layer ++ * Copyright (c) by Jaroslav Kysela ++ * Abramo Bagnara ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define snd_pcm_substream_chip(substream) ((substream)->private_data) ++#define snd_pcm_chip(pcm) ((pcm)->private_data) ++ ++#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) ++#include "pcm_oss.h" ++#endif ++ ++/* ++ * Hardware (lowlevel) section ++ */ ++ ++struct snd_pcm_hardware { ++ unsigned int info; /* SNDRV_PCM_INFO_* */ ++ u64 formats; /* SNDRV_PCM_FMTBIT_* */ ++ unsigned int rates; /* SNDRV_PCM_RATE_* */ ++ unsigned int rate_min; /* min rate */ ++ unsigned int rate_max; /* max rate */ ++ unsigned int channels_min; /* min channels */ ++ unsigned int channels_max; /* max channels */ ++ size_t buffer_bytes_max; /* max buffer size */ ++ size_t period_bytes_min; /* min period size */ ++ size_t period_bytes_max; /* max period size */ ++ unsigned int periods_min; /* min # of periods */ ++ unsigned int periods_max; /* max # of periods */ ++ size_t fifo_size; /* fifo size in bytes */ ++}; ++ ++struct snd_pcm_substream; ++ ++struct snd_pcm_ops { ++ int (*open)(struct snd_pcm_substream *substream); ++ int (*close)(struct snd_pcm_substream *substream); ++ int (*ioctl)(struct snd_pcm_substream * substream, ++ unsigned int cmd, void *arg); ++ int (*hw_params)(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params); ++ int (*hw_free)(struct snd_pcm_substream *substream); ++ int (*prepare)(struct snd_pcm_substream *substream); ++ int (*trigger)(struct snd_pcm_substream *substream, int cmd); ++ snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream); ++ int (*copy)(struct snd_pcm_substream *substream, int channel, ++ snd_pcm_uframes_t pos, ++ void __user *buf, snd_pcm_uframes_t count); ++ int (*silence)(struct snd_pcm_substream *substream, int channel, ++ snd_pcm_uframes_t pos, snd_pcm_uframes_t count); ++ struct page *(*page)(struct snd_pcm_substream *substream, ++ unsigned long offset); ++ int (*mmap)(struct snd_pcm_substream *substream, struct vm_area_struct *vma); ++ int (*ack)(struct snd_pcm_substream *substream); ++}; ++ ++/* ++ * ++ */ ++ ++#define SNDRV_PCM_DEVICES 8 ++ ++#define SNDRV_PCM_IOCTL1_FALSE ((void *)0) ++#define SNDRV_PCM_IOCTL1_TRUE ((void *)1) ++ ++#define SNDRV_PCM_IOCTL1_RESET 0 ++#define SNDRV_PCM_IOCTL1_INFO 1 ++#define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2 ++#define SNDRV_PCM_IOCTL1_GSTATE 3 ++ ++#define SNDRV_PCM_TRIGGER_STOP 0 ++#define SNDRV_PCM_TRIGGER_START 1 ++#define SNDRV_PCM_TRIGGER_PAUSE_PUSH 3 ++#define SNDRV_PCM_TRIGGER_PAUSE_RELEASE 4 ++#define SNDRV_PCM_TRIGGER_SUSPEND 5 ++#define SNDRV_PCM_TRIGGER_RESUME 6 ++ ++#define SNDRV_PCM_POS_XRUN ((snd_pcm_uframes_t)-1) ++ ++/* If you change this don't forget to change rates[] table in pcm_native.c */ ++#define SNDRV_PCM_RATE_5512 (1<<0) /* 5512Hz */ ++#define SNDRV_PCM_RATE_8000 (1<<1) /* 8000Hz */ ++#define SNDRV_PCM_RATE_11025 (1<<2) /* 11025Hz */ ++#define SNDRV_PCM_RATE_16000 (1<<3) /* 16000Hz */ ++#define SNDRV_PCM_RATE_22050 (1<<4) /* 22050Hz */ ++#define SNDRV_PCM_RATE_32000 (1<<5) /* 32000Hz */ ++#define SNDRV_PCM_RATE_44100 (1<<6) /* 44100Hz */ ++#define SNDRV_PCM_RATE_48000 (1<<7) /* 48000Hz */ ++#define SNDRV_PCM_RATE_64000 (1<<8) /* 64000Hz */ ++#define SNDRV_PCM_RATE_88200 (1<<9) /* 88200Hz */ ++#define SNDRV_PCM_RATE_96000 (1<<10) /* 96000Hz */ ++#define SNDRV_PCM_RATE_176400 (1<<11) /* 176400Hz */ ++#define SNDRV_PCM_RATE_192000 (1<<12) /* 192000Hz */ ++ ++#define SNDRV_PCM_RATE_CONTINUOUS (1<<30) /* continuous range */ ++#define SNDRV_PCM_RATE_KNOT (1<<31) /* supports more non-continuos rates */ ++ ++#define SNDRV_PCM_RATE_8000_44100 (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_11025|\ ++ SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_22050|\ ++ SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100) ++#define SNDRV_PCM_RATE_8000_48000 (SNDRV_PCM_RATE_8000_44100|SNDRV_PCM_RATE_48000) ++#define SNDRV_PCM_RATE_8000_96000 (SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_64000|\ ++ SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000) ++#define SNDRV_PCM_RATE_8000_192000 (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\ ++ SNDRV_PCM_RATE_192000) ++#define SNDRV_PCM_FMTBIT_S8 (1ULL << SNDRV_PCM_FORMAT_S8) ++#define SNDRV_PCM_FMTBIT_U8 (1ULL << SNDRV_PCM_FORMAT_U8) ++#define SNDRV_PCM_FMTBIT_S16_LE (1ULL << SNDRV_PCM_FORMAT_S16_LE) ++#define SNDRV_PCM_FMTBIT_S16_BE (1ULL << SNDRV_PCM_FORMAT_S16_BE) ++#define SNDRV_PCM_FMTBIT_U16_LE (1ULL << SNDRV_PCM_FORMAT_U16_LE) ++#define SNDRV_PCM_FMTBIT_U16_BE (1ULL << SNDRV_PCM_FORMAT_U16_BE) ++#define SNDRV_PCM_FMTBIT_S24_LE (1ULL << SNDRV_PCM_FORMAT_S24_LE) ++#define SNDRV_PCM_FMTBIT_S24_BE (1ULL << SNDRV_PCM_FORMAT_S24_BE) ++#define SNDRV_PCM_FMTBIT_U24_LE (1ULL << SNDRV_PCM_FORMAT_U24_LE) ++#define SNDRV_PCM_FMTBIT_U24_BE (1ULL << SNDRV_PCM_FORMAT_U24_BE) ++#define SNDRV_PCM_FMTBIT_S32_LE (1ULL << SNDRV_PCM_FORMAT_S32_LE) ++#define SNDRV_PCM_FMTBIT_S32_BE (1ULL << SNDRV_PCM_FORMAT_S32_BE) ++#define SNDRV_PCM_FMTBIT_U32_LE (1ULL << SNDRV_PCM_FORMAT_U32_LE) ++#define SNDRV_PCM_FMTBIT_U32_BE (1ULL << SNDRV_PCM_FORMAT_U32_BE) ++#define SNDRV_PCM_FMTBIT_FLOAT_LE (1ULL << SNDRV_PCM_FORMAT_FLOAT_LE) ++#define SNDRV_PCM_FMTBIT_FLOAT_BE (1ULL << SNDRV_PCM_FORMAT_FLOAT_BE) ++#define SNDRV_PCM_FMTBIT_FLOAT64_LE (1ULL << SNDRV_PCM_FORMAT_FLOAT64_LE) ++#define SNDRV_PCM_FMTBIT_FLOAT64_BE (1ULL << SNDRV_PCM_FORMAT_FLOAT64_BE) ++#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) ++#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE) ++#define SNDRV_PCM_FMTBIT_MU_LAW (1ULL << SNDRV_PCM_FORMAT_MU_LAW) ++#define SNDRV_PCM_FMTBIT_A_LAW (1ULL << SNDRV_PCM_FORMAT_A_LAW) ++#define SNDRV_PCM_FMTBIT_IMA_ADPCM (1ULL << SNDRV_PCM_FORMAT_IMA_ADPCM) ++#define SNDRV_PCM_FMTBIT_MPEG (1ULL << SNDRV_PCM_FORMAT_MPEG) ++#define SNDRV_PCM_FMTBIT_GSM (1ULL << SNDRV_PCM_FORMAT_GSM) ++#define SNDRV_PCM_FMTBIT_SPECIAL (1ULL << SNDRV_PCM_FORMAT_SPECIAL) ++#define SNDRV_PCM_FMTBIT_S24_3LE (1ULL << SNDRV_PCM_FORMAT_S24_3LE) ++#define SNDRV_PCM_FMTBIT_U24_3LE (1ULL << SNDRV_PCM_FORMAT_U24_3LE) ++#define SNDRV_PCM_FMTBIT_S24_3BE (1ULL << SNDRV_PCM_FORMAT_S24_3BE) ++#define SNDRV_PCM_FMTBIT_U24_3BE (1ULL << SNDRV_PCM_FORMAT_U24_3BE) ++#define SNDRV_PCM_FMTBIT_S20_3LE (1ULL << SNDRV_PCM_FORMAT_S20_3LE) ++#define SNDRV_PCM_FMTBIT_U20_3LE (1ULL << SNDRV_PCM_FORMAT_U20_3LE) ++#define SNDRV_PCM_FMTBIT_S20_3BE (1ULL << SNDRV_PCM_FORMAT_S20_3BE) ++#define SNDRV_PCM_FMTBIT_U20_3BE (1ULL << SNDRV_PCM_FORMAT_U20_3BE) ++#define SNDRV_PCM_FMTBIT_S18_3LE (1ULL << SNDRV_PCM_FORMAT_S18_3LE) ++#define SNDRV_PCM_FMTBIT_U18_3LE (1ULL << SNDRV_PCM_FORMAT_U18_3LE) ++#define SNDRV_PCM_FMTBIT_S18_3BE (1ULL << SNDRV_PCM_FORMAT_S18_3BE) ++#define SNDRV_PCM_FMTBIT_U18_3BE (1ULL << SNDRV_PCM_FORMAT_U18_3BE) ++ ++#ifdef SNDRV_LITTLE_ENDIAN ++#define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_LE ++#define SNDRV_PCM_FMTBIT_U16 SNDRV_PCM_FMTBIT_U16_LE ++#define SNDRV_PCM_FMTBIT_S24 SNDRV_PCM_FMTBIT_S24_LE ++#define SNDRV_PCM_FMTBIT_U24 SNDRV_PCM_FMTBIT_U24_LE ++#define SNDRV_PCM_FMTBIT_S32 SNDRV_PCM_FMTBIT_S32_LE ++#define SNDRV_PCM_FMTBIT_U32 SNDRV_PCM_FMTBIT_U32_LE ++#define SNDRV_PCM_FMTBIT_FLOAT SNDRV_PCM_FMTBIT_FLOAT_LE ++#define SNDRV_PCM_FMTBIT_FLOAT64 SNDRV_PCM_FMTBIT_FLOAT64_LE ++#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE ++#endif ++#ifdef SNDRV_BIG_ENDIAN ++#define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_BE ++#define SNDRV_PCM_FMTBIT_U16 SNDRV_PCM_FMTBIT_U16_BE ++#define SNDRV_PCM_FMTBIT_S24 SNDRV_PCM_FMTBIT_S24_BE ++#define SNDRV_PCM_FMTBIT_U24 SNDRV_PCM_FMTBIT_U24_BE ++#define SNDRV_PCM_FMTBIT_S32 SNDRV_PCM_FMTBIT_S32_BE ++#define SNDRV_PCM_FMTBIT_U32 SNDRV_PCM_FMTBIT_U32_BE ++#define SNDRV_PCM_FMTBIT_FLOAT SNDRV_PCM_FMTBIT_FLOAT_BE ++#define SNDRV_PCM_FMTBIT_FLOAT64 SNDRV_PCM_FMTBIT_FLOAT64_BE ++#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE ++#endif ++ ++struct snd_pcm_file { ++ struct snd_pcm_substream *substream; ++ int no_compat_mmap; ++}; ++ ++struct snd_pcm_hw_rule; ++typedef int (*snd_pcm_hw_rule_func_t)(struct snd_pcm_hw_params *params, ++ struct snd_pcm_hw_rule *rule); ++ ++struct snd_pcm_hw_rule { ++ unsigned int cond; ++ snd_pcm_hw_rule_func_t func; ++ int var; ++ int deps[4]; ++ void *private; ++}; ++ ++struct snd_pcm_hw_constraints { ++ struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - ++ SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; ++ struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - ++ SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; ++ unsigned int rules_num; ++ unsigned int rules_all; ++ struct snd_pcm_hw_rule *rules; ++}; ++ ++static inline struct snd_mask *constrs_mask(struct snd_pcm_hw_constraints *constrs, ++ snd_pcm_hw_param_t var) ++{ ++ return &constrs->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; ++} ++ ++static inline struct snd_interval *constrs_interval(struct snd_pcm_hw_constraints *constrs, ++ snd_pcm_hw_param_t var) ++{ ++ return &constrs->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; ++} ++ ++struct snd_ratnum { ++ unsigned int num; ++ unsigned int den_min, den_max, den_step; ++}; ++ ++struct snd_ratden { ++ unsigned int num_min, num_max, num_step; ++ unsigned int den; ++}; ++ ++struct snd_pcm_hw_constraint_ratnums { ++ int nrats; ++ struct snd_ratnum *rats; ++}; ++ ++struct snd_pcm_hw_constraint_ratdens { ++ int nrats; ++ struct snd_ratden *rats; ++}; ++ ++struct snd_pcm_hw_constraint_list { ++ unsigned int count; ++ unsigned int *list; ++ unsigned int mask; ++}; ++ ++struct snd_pcm_runtime { ++ /* -- Status -- */ ++ struct snd_pcm_substream *trigger_master; ++ struct timespec trigger_tstamp; /* trigger timestamp */ ++ int overrange; ++ snd_pcm_uframes_t avail_max; ++ snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ ++ snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*/ ++ ++ /* -- HW params -- */ ++ snd_pcm_access_t access; /* access mode */ ++ snd_pcm_format_t format; /* SNDRV_PCM_FORMAT_* */ ++ snd_pcm_subformat_t subformat; /* subformat */ ++ unsigned int rate; /* rate in Hz */ ++ unsigned int channels; /* channels */ ++ snd_pcm_uframes_t period_size; /* period size */ ++ unsigned int periods; /* periods */ ++ snd_pcm_uframes_t buffer_size; /* buffer size */ ++ unsigned int tick_time; /* tick time */ ++ snd_pcm_uframes_t min_align; /* Min alignment for the format */ ++ size_t byte_align; ++ unsigned int frame_bits; ++ unsigned int sample_bits; ++ unsigned int info; ++ unsigned int rate_num; ++ unsigned int rate_den; ++ ++ /* -- SW params -- */ ++ int tstamp_mode; /* mmap timestamp is updated */ ++ unsigned int period_step; ++ unsigned int sleep_min; /* min ticks to sleep */ ++ snd_pcm_uframes_t xfer_align; /* xfer size need to be a multiple */ ++ snd_pcm_uframes_t start_threshold; ++ snd_pcm_uframes_t stop_threshold; ++ snd_pcm_uframes_t silence_threshold; /* Silence filling happens when ++ noise is nearest than this */ ++ snd_pcm_uframes_t silence_size; /* Silence filling size */ ++ snd_pcm_uframes_t boundary; /* pointers wrap point */ ++ ++ snd_pcm_uframes_t silence_start; /* starting pointer to silence area */ ++ snd_pcm_uframes_t silence_filled; /* size filled with silence */ ++ ++ union snd_pcm_sync_id sync; /* hardware synchronization ID */ ++ ++ /* -- mmap -- */ ++ struct snd_pcm_mmap_status *status; ++ struct snd_pcm_mmap_control *control; ++ ++ /* -- locking / scheduling -- */ ++ wait_queue_head_t sleep; ++ struct timer_list tick_timer; ++ struct fasync_struct *fasync; ++ ++ /* -- private section -- */ ++ void *private_data; ++ void (*private_free)(struct snd_pcm_runtime *runtime); ++ ++ /* -- hardware description -- */ ++ struct snd_pcm_hardware hw; ++ struct snd_pcm_hw_constraints hw_constraints; ++ ++ /* -- interrupt callbacks -- */ ++ void (*transfer_ack_begin)(struct snd_pcm_substream *substream); ++ void (*transfer_ack_end)(struct snd_pcm_substream *substream); ++ ++ /* -- timer -- */ ++ unsigned int timer_resolution; /* timer resolution */ ++ ++ /* -- DMA -- */ ++ unsigned char *dma_area; /* DMA area */ ++ dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */ ++ size_t dma_bytes; /* size of DMA area */ ++ ++ struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */ ++ ++#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) ++ /* -- OSS things -- */ ++ struct snd_pcm_oss_runtime oss; ++#endif ++}; ++ ++struct snd_pcm_group { /* keep linked substreams */ ++ spinlock_t lock; ++ struct list_head substreams; ++ int count; ++}; ++ ++struct snd_pcm_substream { ++ struct snd_pcm *pcm; ++ struct snd_pcm_str *pstr; ++ void *private_data; /* copied from pcm->private_data */ ++ int number; ++ char name[32]; /* substream name */ ++ int stream; /* stream (direction) */ ++ char latency_id[20]; /* latency identifier */ ++ size_t buffer_bytes_max; /* limit ring buffer size */ ++ struct snd_dma_buffer dma_buffer; ++ unsigned int dma_buf_id; ++ size_t dma_max; ++ /* -- hardware operations -- */ ++ struct snd_pcm_ops *ops; ++ /* -- runtime information -- */ ++ struct snd_pcm_runtime *runtime; ++ /* -- timer section -- */ ++ struct snd_timer *timer; /* timer */ ++ unsigned timer_running: 1; /* time is running */ ++ spinlock_t timer_lock; ++ /* -- next substream -- */ ++ struct snd_pcm_substream *next; ++ /* -- linked substreams -- */ ++ struct list_head link_list; /* linked list member */ ++ struct snd_pcm_group self_group; /* fake group for non linked substream (with substream lock inside) */ ++ struct snd_pcm_group *group; /* pointer to current group */ ++ /* -- assigned files -- */ ++ void *file; ++ int ref_count; ++ atomic_t mmap_count; ++ unsigned int f_flags; ++ void (*pcm_release)(struct snd_pcm_substream *); ++#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) ++ /* -- OSS things -- */ ++ struct snd_pcm_oss_substream oss; ++#endif ++#ifdef CONFIG_SND_VERBOSE_PROCFS ++ struct snd_info_entry *proc_root; ++ struct snd_info_entry *proc_info_entry; ++ struct snd_info_entry *proc_hw_params_entry; ++ struct snd_info_entry *proc_sw_params_entry; ++ struct snd_info_entry *proc_status_entry; ++ struct snd_info_entry *proc_prealloc_entry; ++ struct snd_info_entry *proc_prealloc_max_entry; ++#endif ++ /* misc flags */ ++ unsigned int hw_opened: 1; ++}; ++ ++#define SUBSTREAM_BUSY(substream) ((substream)->ref_count > 0) ++ ++ ++struct snd_pcm_str { ++ int stream; /* stream (direction) */ ++ struct snd_pcm *pcm; ++ /* -- substreams -- */ ++ unsigned int substream_count; ++ unsigned int substream_opened; ++ struct snd_pcm_substream *substream; ++#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) ++ /* -- OSS things -- */ ++ struct snd_pcm_oss_stream oss; ++#endif ++#ifdef CONFIG_SND_VERBOSE_PROCFS ++ struct snd_info_entry *proc_root; ++ struct snd_info_entry *proc_info_entry; ++#ifdef CONFIG_SND_PCM_XRUN_DEBUG ++ unsigned int xrun_debug; /* 0 = disabled, 1 = verbose, 2 = stacktrace */ ++ struct snd_info_entry *proc_xrun_debug_entry; ++#endif ++#endif ++}; ++ ++struct snd_pcm { ++ struct snd_card *card; ++ struct list_head list; ++ unsigned int device; /* device number */ ++ unsigned int info_flags; ++ unsigned short dev_class; ++ unsigned short dev_subclass; ++ char id[64]; ++ char name[80]; ++ struct snd_pcm_str streams[2]; ++ struct mutex open_mutex; ++ wait_queue_head_t open_wait; ++ void *private_data; ++ void (*private_free) (struct snd_pcm *pcm); ++ struct device *dev; /* actual hw device this belongs to */ ++#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) ++ struct snd_pcm_oss oss; ++#endif ++}; ++ ++struct snd_pcm_notify { ++ int (*n_register) (struct snd_pcm * pcm); ++ int (*n_disconnect) (struct snd_pcm * pcm); ++ int (*n_unregister) (struct snd_pcm * pcm); ++ struct list_head list; ++}; ++ ++/* ++ * Registering ++ */ ++ ++extern const struct file_operations snd_pcm_f_ops[2]; ++ ++int snd_pcm_new(struct snd_card *card, char *id, int device, ++ int playback_count, int capture_count, ++ struct snd_pcm **rpcm); ++int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count); ++ ++int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree); ++ ++/* ++ * Native I/O ++ */ ++ ++extern rwlock_t snd_pcm_link_rwlock; ++ ++int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info); ++int snd_pcm_info_user(struct snd_pcm_substream *substream, ++ struct snd_pcm_info __user *info); ++int snd_pcm_status(struct snd_pcm_substream *substream, ++ struct snd_pcm_status *status); ++int snd_pcm_start(struct snd_pcm_substream *substream); ++int snd_pcm_stop(struct snd_pcm_substream *substream, int status); ++int snd_pcm_drain_done(struct snd_pcm_substream *substream); ++#ifdef CONFIG_PM ++int snd_pcm_suspend(struct snd_pcm_substream *substream); ++int snd_pcm_suspend_all(struct snd_pcm *pcm); ++#endif ++int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg); ++int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, struct file *file, ++ struct snd_pcm_substream **rsubstream); ++void snd_pcm_release_substream(struct snd_pcm_substream *substream); ++int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, struct file *file, ++ struct snd_pcm_substream **rsubstream); ++void snd_pcm_detach_substream(struct snd_pcm_substream *substream); ++void snd_pcm_vma_notify_data(void *client, void *data); ++int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area); ++ ++#if BITS_PER_LONG >= 64 ++ ++static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) ++{ ++ *rem = *n % div; ++ *n /= div; ++} ++ ++#elif defined(i386) ++ ++static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) ++{ ++ u_int32_t low, high; ++ low = *n & 0xffffffff; ++ high = *n >> 32; ++ if (high) { ++ u_int32_t high1 = high % div; ++ high /= div; ++ asm("divl %2":"=a" (low), "=d" (*rem):"rm" (div), "a" (low), "d" (high1)); ++ *n = (u_int64_t)high << 32 | low; ++ } else { ++ *n = low / div; ++ *rem = low % div; ++ } ++} ++#else ++ ++static inline void divl(u_int32_t high, u_int32_t low, ++ u_int32_t div, ++ u_int32_t *q, u_int32_t *r) ++{ ++ u_int64_t n = (u_int64_t)high << 32 | low; ++ u_int64_t d = (u_int64_t)div << 31; ++ u_int32_t q1 = 0; ++ int c = 32; ++ while (n > 0xffffffffU) { ++ q1 <<= 1; ++ if (n >= d) { ++ n -= d; ++ q1 |= 1; ++ } ++ d >>= 1; ++ c--; ++ } ++ q1 <<= c; ++ if (n) { ++ low = n; ++ *q = q1 | (low / div); ++ *r = low % div; ++ } else { ++ *r = 0; ++ *q = q1; ++ } ++ return; ++} ++ ++static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) ++{ ++ u_int32_t low, high; ++ low = *n & 0xffffffff; ++ high = *n >> 32; ++ if (high) { ++ u_int32_t high1 = high % div; ++ u_int32_t low1 = low; ++ high /= div; ++ divl(high1, low1, div, &low, rem); ++ *n = (u_int64_t)high << 32 | low; ++ } else { ++ *n = low / div; ++ *rem = low % div; ++ } ++} ++#endif ++ ++/* ++ * PCM library ++ */ ++ ++static inline int snd_pcm_stream_linked(struct snd_pcm_substream *substream) ++{ ++ return substream->group != &substream->self_group; ++} ++ ++static inline void snd_pcm_stream_lock(struct snd_pcm_substream *substream) ++{ ++ read_lock(&snd_pcm_link_rwlock); ++ spin_lock(&substream->self_group.lock); ++} ++ ++static inline void snd_pcm_stream_unlock(struct snd_pcm_substream *substream) ++{ ++ spin_unlock(&substream->self_group.lock); ++ read_unlock(&snd_pcm_link_rwlock); ++} ++ ++static inline void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) ++{ ++ read_lock_irq(&snd_pcm_link_rwlock); ++ spin_lock(&substream->self_group.lock); ++} ++ ++static inline void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream) ++{ ++ spin_unlock(&substream->self_group.lock); ++ read_unlock_irq(&snd_pcm_link_rwlock); ++} ++ ++#define snd_pcm_stream_lock_irqsave(substream, flags) \ ++do { \ ++ read_lock_irqsave(&snd_pcm_link_rwlock, (flags)); \ ++ spin_lock(&substream->self_group.lock); \ ++} while (0) ++ ++#define snd_pcm_stream_unlock_irqrestore(substream, flags) \ ++do { \ ++ spin_unlock(&substream->self_group.lock); \ ++ read_unlock_irqrestore(&snd_pcm_link_rwlock, (flags)); \ ++} while (0) ++ ++#define snd_pcm_group_for_each_entry(s, substream) \ ++ list_for_each_entry(s, &substream->group->substreams, link_list) ++ ++static inline int snd_pcm_running(struct snd_pcm_substream *substream) ++{ ++ return (substream->runtime->status->state == SNDRV_PCM_STATE_RUNNING || ++ (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING && ++ substream->stream == SNDRV_PCM_STREAM_PLAYBACK)); ++} ++ ++static inline ssize_t bytes_to_samples(struct snd_pcm_runtime *runtime, ssize_t size) ++{ ++ return size * 8 / runtime->sample_bits; ++} ++ ++static inline snd_pcm_sframes_t bytes_to_frames(struct snd_pcm_runtime *runtime, ssize_t size) ++{ ++ return size * 8 / runtime->frame_bits; ++} ++ ++static inline ssize_t samples_to_bytes(struct snd_pcm_runtime *runtime, ssize_t size) ++{ ++ return size * runtime->sample_bits / 8; ++} ++ ++static inline ssize_t frames_to_bytes(struct snd_pcm_runtime *runtime, snd_pcm_sframes_t size) ++{ ++ return size * runtime->frame_bits / 8; ++} ++ ++static inline int frame_aligned(struct snd_pcm_runtime *runtime, ssize_t bytes) ++{ ++ return bytes % runtime->byte_align == 0; ++} ++ ++static inline size_t snd_pcm_lib_buffer_bytes(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ return frames_to_bytes(runtime, runtime->buffer_size); ++} ++ ++static inline size_t snd_pcm_lib_period_bytes(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ return frames_to_bytes(runtime, runtime->period_size); ++} ++ ++/* ++ * result is: 0 ... (boundary - 1) ++ */ ++static inline snd_pcm_uframes_t snd_pcm_playback_avail(struct snd_pcm_runtime *runtime) ++{ ++ snd_pcm_sframes_t avail = runtime->status->hw_ptr + runtime->buffer_size - runtime->control->appl_ptr; ++ if (avail < 0) ++ avail += runtime->boundary; ++ else if ((snd_pcm_uframes_t) avail >= runtime->boundary) ++ avail -= runtime->boundary; ++ return avail; ++} ++ ++/* ++ * result is: 0 ... (boundary - 1) ++ */ ++static inline snd_pcm_uframes_t snd_pcm_capture_avail(struct snd_pcm_runtime *runtime) ++{ ++ snd_pcm_sframes_t avail = runtime->status->hw_ptr - runtime->control->appl_ptr; ++ if (avail < 0) ++ avail += runtime->boundary; ++ return avail; ++} ++ ++static inline snd_pcm_sframes_t snd_pcm_playback_hw_avail(struct snd_pcm_runtime *runtime) ++{ ++ return runtime->buffer_size - snd_pcm_playback_avail(runtime); ++} ++ ++static inline snd_pcm_sframes_t snd_pcm_capture_hw_avail(struct snd_pcm_runtime *runtime) ++{ ++ return runtime->buffer_size - snd_pcm_capture_avail(runtime); ++} ++ ++/** ++ * snd_pcm_playback_ready - check whether the playback buffer is available ++ * @substream: the pcm substream instance ++ * ++ * Checks whether enough free space is available on the playback buffer. ++ * ++ * Returns non-zero if available, or zero if not. ++ */ ++static inline int snd_pcm_playback_ready(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ return snd_pcm_playback_avail(runtime) >= runtime->control->avail_min; ++} ++ ++/** ++ * snd_pcm_capture_ready - check whether the capture buffer is available ++ * @substream: the pcm substream instance ++ * ++ * Checks whether enough capture data is available on the capture buffer. ++ * ++ * Returns non-zero if available, or zero if not. ++ */ ++static inline int snd_pcm_capture_ready(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ return snd_pcm_capture_avail(runtime) >= runtime->control->avail_min; ++} ++ ++/** ++ * snd_pcm_playback_data - check whether any data exists on the playback buffer ++ * @substream: the pcm substream instance ++ * ++ * Checks whether any data exists on the playback buffer. If stop_threshold ++ * is bigger or equal to boundary, then this function returns always non-zero. ++ * ++ * Returns non-zero if exists, or zero if not. ++ */ ++static inline int snd_pcm_playback_data(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ ++ if (runtime->stop_threshold >= runtime->boundary) ++ return 1; ++ return snd_pcm_playback_avail(runtime) < runtime->buffer_size; ++} ++ ++/** ++ * snd_pcm_playback_empty - check whether the playback buffer is empty ++ * @substream: the pcm substream instance ++ * ++ * Checks whether the playback buffer is empty. ++ * ++ * Returns non-zero if empty, or zero if not. ++ */ ++static inline int snd_pcm_playback_empty(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ return snd_pcm_playback_avail(runtime) >= runtime->buffer_size; ++} ++ ++/** ++ * snd_pcm_capture_empty - check whether the capture buffer is empty ++ * @substream: the pcm substream instance ++ * ++ * Checks whether the capture buffer is empty. ++ * ++ * Returns non-zero if empty, or zero if not. ++ */ ++static inline int snd_pcm_capture_empty(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ return snd_pcm_capture_avail(runtime) == 0; ++} ++ ++static inline void snd_pcm_trigger_done(struct snd_pcm_substream *substream, ++ struct snd_pcm_substream *master) ++{ ++ substream->runtime->trigger_master = master; ++} ++ ++static inline int hw_is_mask(int var) ++{ ++ return var >= SNDRV_PCM_HW_PARAM_FIRST_MASK && ++ var <= SNDRV_PCM_HW_PARAM_LAST_MASK; ++} ++ ++static inline int hw_is_interval(int var) ++{ ++ return var >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL && ++ var <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; ++} ++ ++static inline struct snd_mask *hw_param_mask(struct snd_pcm_hw_params *params, ++ snd_pcm_hw_param_t var) ++{ ++ return ¶ms->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; ++} ++ ++static inline struct snd_interval *hw_param_interval(struct snd_pcm_hw_params *params, ++ snd_pcm_hw_param_t var) ++{ ++ return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; ++} ++ ++static inline const struct snd_mask *hw_param_mask_c(const struct snd_pcm_hw_params *params, ++ snd_pcm_hw_param_t var) ++{ ++ return ¶ms->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; ++} ++ ++static inline const struct snd_interval *hw_param_interval_c(const struct snd_pcm_hw_params *params, ++ snd_pcm_hw_param_t var) ++{ ++ return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; ++} ++ ++#define params_access(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS)) ++#define params_format(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT)) ++#define params_subformat(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_SUBFORMAT)) ++#define params_channels(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min ++#define params_rate(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_RATE)->min ++#define params_period_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIOD_SIZE)->min ++#define params_period_bytes(p) ((params_period_size(p)*snd_pcm_format_physical_width(params_format(p))*params_channels(p))/8) ++#define params_periods(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIODS)->min ++#define params_buffer_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min ++#define params_buffer_bytes(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min ++#define params_tick_time(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_TICK_TIME)->min ++ ++ ++int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v); ++void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c); ++void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c); ++void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, ++ unsigned int k, struct snd_interval *c); ++void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, ++ const struct snd_interval *b, struct snd_interval *c); ++int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask); ++int snd_interval_ratnum(struct snd_interval *i, ++ unsigned int rats_count, struct snd_ratnum *rats, ++ unsigned int *nump, unsigned int *denp); ++ ++void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params); ++void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var); ++int snd_pcm_hw_params_choose(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); ++ ++int snd_pcm_hw_refine(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); ++ ++int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream); ++int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream); ++ ++int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, ++ u_int32_t mask); ++int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, ++ u_int64_t mask); ++int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, ++ unsigned int min, unsigned int max); ++int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var); ++int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, ++ unsigned int cond, ++ snd_pcm_hw_param_t var, ++ struct snd_pcm_hw_constraint_list *l); ++int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, ++ unsigned int cond, ++ snd_pcm_hw_param_t var, ++ struct snd_pcm_hw_constraint_ratnums *r); ++int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, ++ unsigned int cond, ++ snd_pcm_hw_param_t var, ++ struct snd_pcm_hw_constraint_ratdens *r); ++int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, ++ unsigned int cond, ++ unsigned int width, ++ unsigned int msbits); ++int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, ++ unsigned int cond, ++ snd_pcm_hw_param_t var, ++ unsigned long step); ++int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, ++ unsigned int cond, ++ snd_pcm_hw_param_t var); ++int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, ++ unsigned int cond, ++ int var, ++ snd_pcm_hw_rule_func_t func, void *private, ++ int dep, ...); ++ ++int snd_pcm_format_signed(snd_pcm_format_t format); ++int snd_pcm_format_unsigned(snd_pcm_format_t format); ++int snd_pcm_format_linear(snd_pcm_format_t format); ++int snd_pcm_format_little_endian(snd_pcm_format_t format); ++int snd_pcm_format_big_endian(snd_pcm_format_t format); ++#if 0 /* just for DocBook */ ++/** ++ * snd_pcm_format_cpu_endian - Check the PCM format is CPU-endian ++ * @format: the format to check ++ * ++ * Returns 1 if the given PCM format is CPU-endian, 0 if ++ * opposite, or a negative error code if endian not specified. ++ */ ++int snd_pcm_format_cpu_endian(snd_pcm_format_t format); ++#endif /* DocBook */ ++#ifdef SNDRV_LITTLE_ENDIAN ++#define snd_pcm_format_cpu_endian(format) snd_pcm_format_little_endian(format) ++#else ++#define snd_pcm_format_cpu_endian(format) snd_pcm_format_big_endian(format) ++#endif ++int snd_pcm_format_width(snd_pcm_format_t format); /* in bits */ ++int snd_pcm_format_physical_width(snd_pcm_format_t format); /* in bits */ ++ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples); ++const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format); ++int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int frames); ++snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian); ++ ++void snd_pcm_set_ops(struct snd_pcm * pcm, int direction, struct snd_pcm_ops *ops); ++void snd_pcm_set_sync(struct snd_pcm_substream *substream); ++int snd_pcm_lib_interleave_len(struct snd_pcm_substream *substream); ++int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, ++ unsigned int cmd, void *arg); ++int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream); ++int snd_pcm_playback_xrun_check(struct snd_pcm_substream *substream); ++int snd_pcm_capture_xrun_check(struct snd_pcm_substream *substream); ++int snd_pcm_playback_xrun_asap(struct snd_pcm_substream *substream); ++int snd_pcm_capture_xrun_asap(struct snd_pcm_substream *substream); ++void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr); ++void snd_pcm_tick_prepare(struct snd_pcm_substream *substream); ++void snd_pcm_tick_set(struct snd_pcm_substream *substream, unsigned long ticks); ++void snd_pcm_tick_elapsed(struct snd_pcm_substream *substream); ++void snd_pcm_period_elapsed(struct snd_pcm_substream *substream); ++snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, ++ const void __user *buf, ++ snd_pcm_uframes_t frames); ++snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, ++ void __user *buf, snd_pcm_uframes_t frames); ++snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, ++ void __user **bufs, snd_pcm_uframes_t frames); ++snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, ++ void __user **bufs, snd_pcm_uframes_t frames); ++ ++extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates; ++ ++int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime); ++unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate); ++ ++static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substream, ++ struct snd_dma_buffer *bufp) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ if (bufp) { ++ runtime->dma_buffer_p = bufp; ++ runtime->dma_area = bufp->area; ++ runtime->dma_addr = bufp->addr; ++ runtime->dma_bytes = bufp->bytes; ++ } else { ++ runtime->dma_buffer_p = NULL; ++ runtime->dma_area = NULL; ++ runtime->dma_addr = 0; ++ runtime->dma_bytes = 0; ++ } ++} ++ ++/* ++ * Timer interface ++ */ ++ ++void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream); ++void snd_pcm_timer_init(struct snd_pcm_substream *substream); ++void snd_pcm_timer_done(struct snd_pcm_substream *substream); ++ ++/* ++ * Memory ++ */ ++ ++int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream); ++int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm); ++int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream, ++ int type, struct device *data, ++ size_t size, size_t max); ++int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm, ++ int type, void *data, ++ size_t size, size_t max); ++int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size); ++int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream); ++ ++#define snd_pcm_substream_sgbuf(substream) ((substream)->runtime->dma_buffer_p->private_data) ++#define snd_pcm_sgbuf_pages(size) snd_sgbuf_aligned_pages(size) ++#define snd_pcm_sgbuf_get_addr(sgbuf,ofs) snd_sgbuf_get_addr(sgbuf,ofs) ++struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset); ++ ++/* handle mmap counter - PCM mmap callback should handle this counter properly */ ++static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area) ++{ ++ struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data; ++ atomic_inc(&substream->mmap_count); ++} ++ ++static inline void snd_pcm_mmap_data_close(struct vm_area_struct *area) ++{ ++ struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data; ++ atomic_dec(&substream->mmap_count); ++} ++ ++/* mmap for io-memory area */ ++#if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_ALPHA) ++#define SNDRV_PCM_INFO_MMAP_IOMEM SNDRV_PCM_INFO_MMAP ++int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_struct *area); ++#else ++#define SNDRV_PCM_INFO_MMAP_IOMEM 0 ++#define snd_pcm_lib_mmap_iomem NULL ++#endif ++ ++static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) ++{ ++ *max = dma < 4 ? 64 * 1024 : 128 * 1024; ++} ++ ++/* ++ * Misc ++ */ ++ ++#define SNDRV_PCM_DEFAULT_CON_SPDIF (IEC958_AES0_CON_EMPHASIS_NONE|\ ++ (IEC958_AES1_CON_ORIGINAL<<8)|\ ++ (IEC958_AES1_CON_PCM_CODER<<8)|\ ++ (IEC958_AES3_CON_FS_48000<<24)) ++ ++#endif /* __SOUND_PCM_H */ +diff -urN linux-2.6.24.7.old/init/do_mounts.c linux-2.6.24.7/init/do_mounts.c +--- linux-2.6.24.7.old/init/do_mounts.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/init/do_mounts.c 2009-04-12 18:13:57.000000000 +0200 +@@ -440,7 +440,9 @@ + + if (saved_root_name[0]) { + root_device_name = saved_root_name; +- if (!strncmp(root_device_name, "mtd", 3)) { ++ if (!strncmp(root_device_name, "mtd", 3) || ++ !strncmp(root_device_name, "ubi", 3) || ++ !strncmp(root_device_name, "mmc", 3)) { + mount_block_root(root_device_name, root_mountflags); + goto out; + } +diff -urN linux-2.6.24.7.old/sound/core/pcm_native.c linux-2.6.24.7/sound/core/pcm_native.c +--- linux-2.6.24.7.old/sound/core/pcm_native.c 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/sound/core/pcm_native.c 2009-04-12 18:13:57.000000000 +0200 +@@ -1780,12 +1780,13 @@ + return snd_interval_refine(hw_param_interval(params, rule->var), &t); + } + +-#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 ++#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 14 + #error "Change this table" + #endif + +-static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, +- 48000, 64000, 88200, 96000, 176400, 192000 }; ++static unsigned int rates[] = { 5512, 8000, 11025, 12000, 16000, 22050, 24000, ++ 32000, 44100, 48000, 64000, 88200, 96000, ++ 176400, 192000 }; + + const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { + .count = ARRAY_SIZE(rates), +@@ -1796,9 +1797,17 @@ + struct snd_pcm_hw_rule *rule) + { + struct snd_pcm_hardware *hw = rule->private; ++#if 0 + return snd_interval_list(hw_param_interval(params, rule->var), + snd_pcm_known_rates.count, + snd_pcm_known_rates.list, hw->rates); ++#else ++ //printk("hw->rates=0x%08x\n",hw->rates);//0x3b6 ++ hw->rates = 0x3fe;//12KHz and 24KHz bits are all zero,you need set 1 ++ return snd_interval_list(hw_param_interval(params, rule->var), ++ snd_pcm_known_rates.count, ++ snd_pcm_known_rates.list, hw->rates); ++#endif + } + + static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params, +diff -urN linux-2.6.24.7.old/sound/core/pcm_native.c.org linux-2.6.24.7/sound/core/pcm_native.c.org +--- linux-2.6.24.7.old/sound/core/pcm_native.c.org 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/core/pcm_native.c.org 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,3451 @@ ++/* ++ * Digital Audio (PCM) abstract layer ++ * Copyright (c) by Jaroslav Kysela ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Compatibility ++ */ ++ ++struct snd_pcm_hw_params_old { ++ unsigned int flags; ++ unsigned int masks[SNDRV_PCM_HW_PARAM_SUBFORMAT - ++ SNDRV_PCM_HW_PARAM_ACCESS + 1]; ++ struct snd_interval intervals[SNDRV_PCM_HW_PARAM_TICK_TIME - ++ SNDRV_PCM_HW_PARAM_SAMPLE_BITS + 1]; ++ unsigned int rmask; ++ unsigned int cmask; ++ unsigned int info; ++ unsigned int msbits; ++ unsigned int rate_num; ++ unsigned int rate_den; ++ snd_pcm_uframes_t fifo_size; ++ unsigned char reserved[64]; ++}; ++ ++#ifdef CONFIG_SND_SUPPORT_OLD_API ++#define SNDRV_PCM_IOCTL_HW_REFINE_OLD _IOWR('A', 0x10, struct snd_pcm_hw_params_old) ++#define SNDRV_PCM_IOCTL_HW_PARAMS_OLD _IOWR('A', 0x11, struct snd_pcm_hw_params_old) ++ ++static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params_old __user * _oparams); ++static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params_old __user * _oparams); ++#endif ++static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream); ++ ++/* ++ * ++ */ ++ ++DEFINE_RWLOCK(snd_pcm_link_rwlock); ++EXPORT_SYMBOL(snd_pcm_link_rwlock); ++ ++static DECLARE_RWSEM(snd_pcm_link_rwsem); ++ ++static inline mm_segment_t snd_enter_user(void) ++{ ++ mm_segment_t fs = get_fs(); ++ set_fs(get_ds()); ++ return fs; ++} ++ ++static inline void snd_leave_user(mm_segment_t fs) ++{ ++ set_fs(fs); ++} ++ ++ ++ ++int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) ++{ ++ struct snd_pcm_runtime *runtime; ++ struct snd_pcm *pcm = substream->pcm; ++ struct snd_pcm_str *pstr = substream->pstr; ++ ++ snd_assert(substream != NULL, return -ENXIO); ++ memset(info, 0, sizeof(*info)); ++ info->card = pcm->card->number; ++ info->device = pcm->device; ++ info->stream = substream->stream; ++ info->subdevice = substream->number; ++ strlcpy(info->id, pcm->id, sizeof(info->id)); ++ strlcpy(info->name, pcm->name, sizeof(info->name)); ++ info->dev_class = pcm->dev_class; ++ info->dev_subclass = pcm->dev_subclass; ++ info->subdevices_count = pstr->substream_count; ++ info->subdevices_avail = pstr->substream_count - pstr->substream_opened; ++ strlcpy(info->subname, substream->name, sizeof(info->subname)); ++ runtime = substream->runtime; ++ /* AB: FIXME!!! This is definitely nonsense */ ++ if (runtime) { ++ info->sync = runtime->sync; ++ substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info); ++ } ++ return 0; ++} ++ ++int snd_pcm_info_user(struct snd_pcm_substream *substream, ++ struct snd_pcm_info __user * _info) ++{ ++ struct snd_pcm_info *info; ++ int err; ++ ++ info = kmalloc(sizeof(*info), GFP_KERNEL); ++ if (! info) ++ return -ENOMEM; ++ err = snd_pcm_info(substream, info); ++ if (err >= 0) { ++ if (copy_to_user(_info, info, sizeof(*info))) ++ err = -EFAULT; ++ } ++ kfree(info); ++ return err; ++} ++ ++#undef RULES_DEBUG ++ ++#ifdef RULES_DEBUG ++#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v ++char *snd_pcm_hw_param_names[] = { ++ HW_PARAM(ACCESS), ++ HW_PARAM(FORMAT), ++ HW_PARAM(SUBFORMAT), ++ HW_PARAM(SAMPLE_BITS), ++ HW_PARAM(FRAME_BITS), ++ HW_PARAM(CHANNELS), ++ HW_PARAM(RATE), ++ HW_PARAM(PERIOD_TIME), ++ HW_PARAM(PERIOD_SIZE), ++ HW_PARAM(PERIOD_BYTES), ++ HW_PARAM(PERIODS), ++ HW_PARAM(BUFFER_TIME), ++ HW_PARAM(BUFFER_SIZE), ++ HW_PARAM(BUFFER_BYTES), ++ HW_PARAM(TICK_TIME), ++}; ++#endif ++ ++int snd_pcm_hw_refine(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ unsigned int k; ++ struct snd_pcm_hardware *hw; ++ struct snd_interval *i = NULL; ++ struct snd_mask *m = NULL; ++ struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; ++ unsigned int rstamps[constrs->rules_num]; ++ unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; ++ unsigned int stamp = 2; ++ int changed, again; ++ ++ params->info = 0; ++ params->fifo_size = 0; ++ if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) ++ params->msbits = 0; ++ if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { ++ params->rate_num = 0; ++ params->rate_den = 0; ++ } ++ ++ for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { ++ m = hw_param_mask(params, k); ++ if (snd_mask_empty(m)) ++ return -EINVAL; ++ if (!(params->rmask & (1 << k))) ++ continue; ++#ifdef RULES_DEBUG ++ printk("%s = ", snd_pcm_hw_param_names[k]); ++ printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); ++#endif ++ changed = snd_mask_refine(m, constrs_mask(constrs, k)); ++#ifdef RULES_DEBUG ++ printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); ++#endif ++ if (changed) ++ params->cmask |= 1 << k; ++ if (changed < 0) ++ return changed; ++ } ++ ++ for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { ++ i = hw_param_interval(params, k); ++ if (snd_interval_empty(i)) ++ return -EINVAL; ++ if (!(params->rmask & (1 << k))) ++ continue; ++#ifdef RULES_DEBUG ++ printk("%s = ", snd_pcm_hw_param_names[k]); ++ if (i->empty) ++ printk("empty"); ++ else ++ printk("%c%u %u%c", ++ i->openmin ? '(' : '[', i->min, ++ i->max, i->openmax ? ')' : ']'); ++ printk(" -> "); ++#endif ++ changed = snd_interval_refine(i, constrs_interval(constrs, k)); ++#ifdef RULES_DEBUG ++ if (i->empty) ++ printk("empty\n"); ++ else ++ printk("%c%u %u%c\n", ++ i->openmin ? '(' : '[', i->min, ++ i->max, i->openmax ? ')' : ']'); ++#endif ++ if (changed) ++ params->cmask |= 1 << k; ++ if (changed < 0) ++ return changed; ++ } ++ ++ for (k = 0; k < constrs->rules_num; k++) ++ rstamps[k] = 0; ++ for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) ++ vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; ++ do { ++ again = 0; ++ for (k = 0; k < constrs->rules_num; k++) { ++ struct snd_pcm_hw_rule *r = &constrs->rules[k]; ++ unsigned int d; ++ int doit = 0; ++ if (r->cond && !(r->cond & params->flags)) ++ continue; ++ for (d = 0; r->deps[d] >= 0; d++) { ++ if (vstamps[r->deps[d]] > rstamps[k]) { ++ doit = 1; ++ break; ++ } ++ } ++ if (!doit) ++ continue; ++#ifdef RULES_DEBUG ++ printk("Rule %d [%p]: ", k, r->func); ++ if (r->var >= 0) { ++ printk("%s = ", snd_pcm_hw_param_names[r->var]); ++ if (hw_is_mask(r->var)) { ++ m = hw_param_mask(params, r->var); ++ printk("%x", *m->bits); ++ } else { ++ i = hw_param_interval(params, r->var); ++ if (i->empty) ++ printk("empty"); ++ else ++ printk("%c%u %u%c", ++ i->openmin ? '(' : '[', i->min, ++ i->max, i->openmax ? ')' : ']'); ++ } ++ } ++#endif ++ changed = r->func(params, r); ++#ifdef RULES_DEBUG ++ if (r->var >= 0) { ++ printk(" -> "); ++ if (hw_is_mask(r->var)) ++ printk("%x", *m->bits); ++ else { ++ if (i->empty) ++ printk("empty"); ++ else ++ printk("%c%u %u%c", ++ i->openmin ? '(' : '[', i->min, ++ i->max, i->openmax ? ')' : ']'); ++ } ++ } ++ printk("\n"); ++#endif ++ rstamps[k] = stamp; ++ if (changed && r->var >= 0) { ++ params->cmask |= (1 << r->var); ++ vstamps[r->var] = stamp; ++ again = 1; ++ } ++ if (changed < 0) ++ return changed; ++ stamp++; ++ } ++ } while (again); ++ if (!params->msbits) { ++ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); ++ if (snd_interval_single(i)) ++ params->msbits = snd_interval_value(i); ++ } ++ ++ if (!params->rate_den) { ++ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); ++ if (snd_interval_single(i)) { ++ params->rate_num = snd_interval_value(i); ++ params->rate_den = 1; ++ } ++ } ++ ++ hw = &substream->runtime->hw; ++ if (!params->info) ++ params->info = hw->info; ++ if (!params->fifo_size) ++ params->fifo_size = hw->fifo_size; ++ params->rmask = 0; ++ return 0; ++} ++ ++EXPORT_SYMBOL(snd_pcm_hw_refine); ++ ++static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params __user * _params) ++{ ++ struct snd_pcm_hw_params *params; ++ int err; ++ ++ params = kmalloc(sizeof(*params), GFP_KERNEL); ++ if (!params) { ++ err = -ENOMEM; ++ goto out; ++ } ++ if (copy_from_user(params, _params, sizeof(*params))) { ++ err = -EFAULT; ++ goto out; ++ } ++ err = snd_pcm_hw_refine(substream, params); ++ if (copy_to_user(_params, params, sizeof(*params))) { ++ if (!err) ++ err = -EFAULT; ++ } ++out: ++ kfree(params); ++ return err; ++} ++ ++static int period_to_usecs(struct snd_pcm_runtime *runtime) ++{ ++ int usecs; ++ ++ if (! runtime->rate) ++ return -1; /* invalid */ ++ ++ /* take 75% of period time as the deadline */ ++ usecs = (750000 / runtime->rate) * runtime->period_size; ++ usecs += ((750000 % runtime->rate) * runtime->period_size) / ++ runtime->rate; ++ ++ return usecs; ++} ++ ++static int snd_pcm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_pcm_runtime *runtime; ++ int err, usecs; ++ unsigned int bits; ++ snd_pcm_uframes_t frames; ++ ++ snd_assert(substream != NULL, return -ENXIO); ++ runtime = substream->runtime; ++ snd_assert(runtime != NULL, return -ENXIO); ++ snd_pcm_stream_lock_irq(substream); ++ switch (runtime->status->state) { ++ case SNDRV_PCM_STATE_OPEN: ++ case SNDRV_PCM_STATE_SETUP: ++ case SNDRV_PCM_STATE_PREPARED: ++ break; ++ default: ++ snd_pcm_stream_unlock_irq(substream); ++ return -EBADFD; ++ } ++ snd_pcm_stream_unlock_irq(substream); ++#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) ++ if (!substream->oss.oss) ++#endif ++ if (atomic_read(&substream->mmap_count)) ++ return -EBADFD; ++ ++ params->rmask = ~0U; ++ err = snd_pcm_hw_refine(substream, params); ++ if (err < 0) ++ goto _error; ++ ++ err = snd_pcm_hw_params_choose(substream, params); ++ if (err < 0) ++ goto _error; ++ ++ if (substream->ops->hw_params != NULL) { ++ err = substream->ops->hw_params(substream, params); ++ if (err < 0) ++ goto _error; ++ } ++ ++ runtime->access = params_access(params); ++ runtime->format = params_format(params); ++ runtime->subformat = params_subformat(params); ++ runtime->channels = params_channels(params); ++ runtime->rate = params_rate(params); ++ runtime->period_size = params_period_size(params); ++ runtime->periods = params_periods(params); ++ runtime->buffer_size = params_buffer_size(params); ++ runtime->tick_time = params_tick_time(params); ++ runtime->info = params->info; ++ runtime->rate_num = params->rate_num; ++ runtime->rate_den = params->rate_den; ++ ++ bits = snd_pcm_format_physical_width(runtime->format); ++ runtime->sample_bits = bits; ++ bits *= runtime->channels; ++ runtime->frame_bits = bits; ++ frames = 1; ++ while (bits % 8 != 0) { ++ bits *= 2; ++ frames *= 2; ++ } ++ runtime->byte_align = bits / 8; ++ runtime->min_align = frames; ++ ++ /* Default sw params */ ++ runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; ++ runtime->period_step = 1; ++ runtime->sleep_min = 0; ++ runtime->control->avail_min = runtime->period_size; ++ runtime->xfer_align = runtime->period_size; ++ runtime->start_threshold = 1; ++ runtime->stop_threshold = runtime->buffer_size; ++ runtime->silence_threshold = 0; ++ runtime->silence_size = 0; ++ runtime->boundary = runtime->buffer_size; ++ while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size) ++ runtime->boundary *= 2; ++ ++ snd_pcm_timer_resolution_change(substream); ++ runtime->status->state = SNDRV_PCM_STATE_SETUP; ++ ++ remove_acceptable_latency(substream->latency_id); ++ if ((usecs = period_to_usecs(runtime)) >= 0) ++ set_acceptable_latency(substream->latency_id, usecs); ++ return 0; ++ _error: ++ /* hardware might be unuseable from this time, ++ so we force application to retry to set ++ the correct hardware parameter settings */ ++ runtime->status->state = SNDRV_PCM_STATE_OPEN; ++ if (substream->ops->hw_free != NULL) ++ substream->ops->hw_free(substream); ++ return err; ++} ++ ++static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params __user * _params) ++{ ++ struct snd_pcm_hw_params *params; ++ int err; ++ ++ params = kmalloc(sizeof(*params), GFP_KERNEL); ++ if (!params) { ++ err = -ENOMEM; ++ goto out; ++ } ++ if (copy_from_user(params, _params, sizeof(*params))) { ++ err = -EFAULT; ++ goto out; ++ } ++ err = snd_pcm_hw_params(substream, params); ++ if (copy_to_user(_params, params, sizeof(*params))) { ++ if (!err) ++ err = -EFAULT; ++ } ++out: ++ kfree(params); ++ return err; ++} ++ ++static int snd_pcm_hw_free(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime; ++ int result = 0; ++ ++ snd_assert(substream != NULL, return -ENXIO); ++ runtime = substream->runtime; ++ snd_assert(runtime != NULL, return -ENXIO); ++ snd_pcm_stream_lock_irq(substream); ++ switch (runtime->status->state) { ++ case SNDRV_PCM_STATE_SETUP: ++ case SNDRV_PCM_STATE_PREPARED: ++ break; ++ default: ++ snd_pcm_stream_unlock_irq(substream); ++ return -EBADFD; ++ } ++ snd_pcm_stream_unlock_irq(substream); ++ if (atomic_read(&substream->mmap_count)) ++ return -EBADFD; ++ if (substream->ops->hw_free) ++ result = substream->ops->hw_free(substream); ++ runtime->status->state = SNDRV_PCM_STATE_OPEN; ++ remove_acceptable_latency(substream->latency_id); ++ return result; ++} ++ ++static int snd_pcm_sw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_sw_params *params) ++{ ++ struct snd_pcm_runtime *runtime; ++ ++ snd_assert(substream != NULL, return -ENXIO); ++ runtime = substream->runtime; ++ snd_assert(runtime != NULL, return -ENXIO); ++ snd_pcm_stream_lock_irq(substream); ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { ++ snd_pcm_stream_unlock_irq(substream); ++ return -EBADFD; ++ } ++ snd_pcm_stream_unlock_irq(substream); ++ ++ if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST) ++ return -EINVAL; ++ if (params->avail_min == 0) ++ return -EINVAL; ++ if (params->xfer_align == 0 || ++ params->xfer_align % runtime->min_align != 0) ++ return -EINVAL; ++ if (params->silence_size >= runtime->boundary) { ++ if (params->silence_threshold != 0) ++ return -EINVAL; ++ } else { ++ if (params->silence_size > params->silence_threshold) ++ return -EINVAL; ++ if (params->silence_threshold > runtime->buffer_size) ++ return -EINVAL; ++ } ++ snd_pcm_stream_lock_irq(substream); ++ runtime->tstamp_mode = params->tstamp_mode; ++ runtime->sleep_min = params->sleep_min; ++ runtime->period_step = params->period_step; ++ runtime->control->avail_min = params->avail_min; ++ runtime->start_threshold = params->start_threshold; ++ runtime->stop_threshold = params->stop_threshold; ++ runtime->silence_threshold = params->silence_threshold; ++ runtime->silence_size = params->silence_size; ++ runtime->xfer_align = params->xfer_align; ++ params->boundary = runtime->boundary; ++ if (snd_pcm_running(substream)) { ++ if (runtime->sleep_min) ++ snd_pcm_tick_prepare(substream); ++ else ++ snd_pcm_tick_set(substream, 0); ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && ++ runtime->silence_size > 0) ++ snd_pcm_playback_silence(substream, ULONG_MAX); ++ wake_up(&runtime->sleep); ++ } ++ snd_pcm_stream_unlock_irq(substream); ++ return 0; ++} ++ ++static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream, ++ struct snd_pcm_sw_params __user * _params) ++{ ++ struct snd_pcm_sw_params params; ++ int err; ++ if (copy_from_user(¶ms, _params, sizeof(params))) ++ return -EFAULT; ++ err = snd_pcm_sw_params(substream, ¶ms); ++ if (copy_to_user(_params, ¶ms, sizeof(params))) ++ return -EFAULT; ++ return err; ++} ++ ++int snd_pcm_status(struct snd_pcm_substream *substream, ++ struct snd_pcm_status *status) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ ++ snd_pcm_stream_lock_irq(substream); ++ status->state = runtime->status->state; ++ status->suspended_state = runtime->status->suspended_state; ++ if (status->state == SNDRV_PCM_STATE_OPEN) ++ goto _end; ++ status->trigger_tstamp = runtime->trigger_tstamp; ++ if (snd_pcm_running(substream)) { ++ snd_pcm_update_hw_ptr(substream); ++ if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) ++ status->tstamp = runtime->status->tstamp; ++ else ++ getnstimeofday(&status->tstamp); ++ } else ++ getnstimeofday(&status->tstamp); ++ status->appl_ptr = runtime->control->appl_ptr; ++ status->hw_ptr = runtime->status->hw_ptr; ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ status->avail = snd_pcm_playback_avail(runtime); ++ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING || ++ runtime->status->state == SNDRV_PCM_STATE_DRAINING) ++ status->delay = runtime->buffer_size - status->avail; ++ else ++ status->delay = 0; ++ } else { ++ status->avail = snd_pcm_capture_avail(runtime); ++ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) ++ status->delay = status->avail; ++ else ++ status->delay = 0; ++ } ++ status->avail_max = runtime->avail_max; ++ status->overrange = runtime->overrange; ++ runtime->avail_max = 0; ++ runtime->overrange = 0; ++ _end: ++ snd_pcm_stream_unlock_irq(substream); ++ return 0; ++} ++ ++static int snd_pcm_status_user(struct snd_pcm_substream *substream, ++ struct snd_pcm_status __user * _status) ++{ ++ struct snd_pcm_status status; ++ struct snd_pcm_runtime *runtime; ++ int res; ++ ++ snd_assert(substream != NULL, return -ENXIO); ++ runtime = substream->runtime; ++ memset(&status, 0, sizeof(status)); ++ res = snd_pcm_status(substream, &status); ++ if (res < 0) ++ return res; ++ if (copy_to_user(_status, &status, sizeof(status))) ++ return -EFAULT; ++ return 0; ++} ++ ++static int snd_pcm_channel_info(struct snd_pcm_substream *substream, ++ struct snd_pcm_channel_info * info) ++{ ++ struct snd_pcm_runtime *runtime; ++ unsigned int channel; ++ ++ snd_assert(substream != NULL, return -ENXIO); ++ channel = info->channel; ++ runtime = substream->runtime; ++ snd_pcm_stream_lock_irq(substream); ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { ++ snd_pcm_stream_unlock_irq(substream); ++ return -EBADFD; ++ } ++ snd_pcm_stream_unlock_irq(substream); ++ if (channel >= runtime->channels) ++ return -EINVAL; ++ memset(info, 0, sizeof(*info)); ++ info->channel = channel; ++ return substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, info); ++} ++ ++static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream, ++ struct snd_pcm_channel_info __user * _info) ++{ ++ struct snd_pcm_channel_info info; ++ int res; ++ ++ if (copy_from_user(&info, _info, sizeof(info))) ++ return -EFAULT; ++ res = snd_pcm_channel_info(substream, &info); ++ if (res < 0) ++ return res; ++ if (copy_to_user(_info, &info, sizeof(info))) ++ return -EFAULT; ++ return 0; ++} ++ ++static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ if (runtime->trigger_master == NULL) ++ return; ++ if (runtime->trigger_master == substream) { ++ getnstimeofday(&runtime->trigger_tstamp); ++ } else { ++ snd_pcm_trigger_tstamp(runtime->trigger_master); ++ runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp; ++ } ++ runtime->trigger_master = NULL; ++} ++ ++struct action_ops { ++ int (*pre_action)(struct snd_pcm_substream *substream, int state); ++ int (*do_action)(struct snd_pcm_substream *substream, int state); ++ void (*undo_action)(struct snd_pcm_substream *substream, int state); ++ void (*post_action)(struct snd_pcm_substream *substream, int state); ++}; ++ ++/* ++ * this functions is core for handling of linked stream ++ * Note: the stream state might be changed also on failure ++ * Note2: call with calling stream lock + link lock ++ */ ++static int snd_pcm_action_group(struct action_ops *ops, ++ struct snd_pcm_substream *substream, ++ int state, int do_lock) ++{ ++ struct snd_pcm_substream *s = NULL; ++ struct snd_pcm_substream *s1; ++ int res = 0; ++ ++ snd_pcm_group_for_each_entry(s, substream) { ++ if (do_lock && s != substream) ++ spin_lock_nested(&s->self_group.lock, ++ SINGLE_DEPTH_NESTING); ++ res = ops->pre_action(s, state); ++ if (res < 0) ++ goto _unlock; ++ } ++ snd_pcm_group_for_each_entry(s, substream) { ++ res = ops->do_action(s, state); ++ if (res < 0) { ++ if (ops->undo_action) { ++ snd_pcm_group_for_each_entry(s1, substream) { ++ if (s1 == s) /* failed stream */ ++ break; ++ ops->undo_action(s1, state); ++ } ++ } ++ s = NULL; /* unlock all */ ++ goto _unlock; ++ } ++ } ++ snd_pcm_group_for_each_entry(s, substream) { ++ ops->post_action(s, state); ++ } ++ _unlock: ++ if (do_lock) { ++ /* unlock streams */ ++ snd_pcm_group_for_each_entry(s1, substream) { ++ if (s1 != substream) ++ spin_unlock(&s1->self_group.lock); ++ if (s1 == s) /* end */ ++ break; ++ } ++ } ++ return res; ++} ++ ++/* ++ * Note: call with stream lock ++ */ ++static int snd_pcm_action_single(struct action_ops *ops, ++ struct snd_pcm_substream *substream, ++ int state) ++{ ++ int res; ++ ++ res = ops->pre_action(substream, state); ++ if (res < 0) ++ return res; ++ res = ops->do_action(substream, state); ++ if (res == 0) ++ ops->post_action(substream, state); ++ else if (ops->undo_action) ++ ops->undo_action(substream, state); ++ return res; ++} ++ ++/* ++ * Note: call with stream lock ++ */ ++static int snd_pcm_action(struct action_ops *ops, ++ struct snd_pcm_substream *substream, ++ int state) ++{ ++ int res; ++ ++ if (snd_pcm_stream_linked(substream)) { ++ if (!spin_trylock(&substream->group->lock)) { ++ spin_unlock(&substream->self_group.lock); ++ spin_lock(&substream->group->lock); ++ spin_lock(&substream->self_group.lock); ++ } ++ res = snd_pcm_action_group(ops, substream, state, 1); ++ spin_unlock(&substream->group->lock); ++ } else { ++ res = snd_pcm_action_single(ops, substream, state); ++ } ++ return res; ++} ++ ++/* ++ * Note: don't use any locks before ++ */ ++static int snd_pcm_action_lock_irq(struct action_ops *ops, ++ struct snd_pcm_substream *substream, ++ int state) ++{ ++ int res; ++ ++ read_lock_irq(&snd_pcm_link_rwlock); ++ if (snd_pcm_stream_linked(substream)) { ++ spin_lock(&substream->group->lock); ++ spin_lock(&substream->self_group.lock); ++ res = snd_pcm_action_group(ops, substream, state, 1); ++ spin_unlock(&substream->self_group.lock); ++ spin_unlock(&substream->group->lock); ++ } else { ++ spin_lock(&substream->self_group.lock); ++ res = snd_pcm_action_single(ops, substream, state); ++ spin_unlock(&substream->self_group.lock); ++ } ++ read_unlock_irq(&snd_pcm_link_rwlock); ++ return res; ++} ++ ++/* ++ */ ++static int snd_pcm_action_nonatomic(struct action_ops *ops, ++ struct snd_pcm_substream *substream, ++ int state) ++{ ++ int res; ++ ++ down_read(&snd_pcm_link_rwsem); ++ if (snd_pcm_stream_linked(substream)) ++ res = snd_pcm_action_group(ops, substream, state, 0); ++ else ++ res = snd_pcm_action_single(ops, substream, state); ++ up_read(&snd_pcm_link_rwsem); ++ return res; ++} ++ ++/* ++ * start callbacks ++ */ ++static int snd_pcm_pre_start(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ if (runtime->status->state != SNDRV_PCM_STATE_PREPARED) ++ return -EBADFD; ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && ++ !snd_pcm_playback_data(substream)) ++ return -EPIPE; ++ runtime->trigger_master = substream; ++ return 0; ++} ++ ++static int snd_pcm_do_start(struct snd_pcm_substream *substream, int state) ++{ ++ if (substream->runtime->trigger_master != substream) ++ return 0; ++ return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START); ++} ++ ++static void snd_pcm_undo_start(struct snd_pcm_substream *substream, int state) ++{ ++ if (substream->runtime->trigger_master == substream) ++ substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP); ++} ++ ++static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ snd_pcm_trigger_tstamp(substream); ++ runtime->status->state = state; ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && ++ runtime->silence_size > 0) ++ snd_pcm_playback_silence(substream, ULONG_MAX); ++ if (runtime->sleep_min) ++ snd_pcm_tick_prepare(substream); ++ if (substream->timer) ++ snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART, ++ &runtime->trigger_tstamp); ++} ++ ++static struct action_ops snd_pcm_action_start = { ++ .pre_action = snd_pcm_pre_start, ++ .do_action = snd_pcm_do_start, ++ .undo_action = snd_pcm_undo_start, ++ .post_action = snd_pcm_post_start ++}; ++ ++/** ++ * snd_pcm_start ++ * @substream: the PCM substream instance ++ * ++ * Start all linked streams. ++ */ ++int snd_pcm_start(struct snd_pcm_substream *substream) ++{ ++ return snd_pcm_action(&snd_pcm_action_start, substream, ++ SNDRV_PCM_STATE_RUNNING); ++} ++ ++/* ++ * stop callbacks ++ */ ++static int snd_pcm_pre_stop(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) ++ return -EBADFD; ++ runtime->trigger_master = substream; ++ return 0; ++} ++ ++static int snd_pcm_do_stop(struct snd_pcm_substream *substream, int state) ++{ ++ if (substream->runtime->trigger_master == substream && ++ snd_pcm_running(substream)) ++ substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP); ++ return 0; /* unconditonally stop all substreams */ ++} ++ ++static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ if (runtime->status->state != state) { ++ snd_pcm_trigger_tstamp(substream); ++ if (substream->timer) ++ snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP, ++ &runtime->trigger_tstamp); ++ runtime->status->state = state; ++ snd_pcm_tick_set(substream, 0); ++ } ++ wake_up(&runtime->sleep); ++} ++ ++static struct action_ops snd_pcm_action_stop = { ++ .pre_action = snd_pcm_pre_stop, ++ .do_action = snd_pcm_do_stop, ++ .post_action = snd_pcm_post_stop ++}; ++ ++/** ++ * snd_pcm_stop ++ * @substream: the PCM substream instance ++ * @state: PCM state after stopping the stream ++ * ++ * Try to stop all running streams in the substream group. ++ * The state of each stream is changed to the given value after that unconditionally. ++ */ ++int snd_pcm_stop(struct snd_pcm_substream *substream, int state) ++{ ++ return snd_pcm_action(&snd_pcm_action_stop, substream, state); ++} ++ ++EXPORT_SYMBOL(snd_pcm_stop); ++ ++/** ++ * snd_pcm_drain_done ++ * @substream: the PCM substream ++ * ++ * Stop the DMA only when the given stream is playback. ++ * The state is changed to SETUP. ++ * Unlike snd_pcm_stop(), this affects only the given stream. ++ */ ++int snd_pcm_drain_done(struct snd_pcm_substream *substream) ++{ ++ return snd_pcm_action_single(&snd_pcm_action_stop, substream, ++ SNDRV_PCM_STATE_SETUP); ++} ++ ++/* ++ * pause callbacks ++ */ ++static int snd_pcm_pre_pause(struct snd_pcm_substream *substream, int push) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ if (!(runtime->info & SNDRV_PCM_INFO_PAUSE)) ++ return -ENOSYS; ++ if (push) { ++ if (runtime->status->state != SNDRV_PCM_STATE_RUNNING) ++ return -EBADFD; ++ } else if (runtime->status->state != SNDRV_PCM_STATE_PAUSED) ++ return -EBADFD; ++ runtime->trigger_master = substream; ++ return 0; ++} ++ ++static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push) ++{ ++ if (substream->runtime->trigger_master != substream) ++ return 0; ++ return substream->ops->trigger(substream, ++ push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH : ++ SNDRV_PCM_TRIGGER_PAUSE_RELEASE); ++} ++ ++static void snd_pcm_undo_pause(struct snd_pcm_substream *substream, int push) ++{ ++ if (substream->runtime->trigger_master == substream) ++ substream->ops->trigger(substream, ++ push ? SNDRV_PCM_TRIGGER_PAUSE_RELEASE : ++ SNDRV_PCM_TRIGGER_PAUSE_PUSH); ++} ++ ++static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ snd_pcm_trigger_tstamp(substream); ++ if (push) { ++ runtime->status->state = SNDRV_PCM_STATE_PAUSED; ++ if (substream->timer) ++ snd_timer_notify(substream->timer, ++ SNDRV_TIMER_EVENT_MPAUSE, ++ &runtime->trigger_tstamp); ++ snd_pcm_tick_set(substream, 0); ++ wake_up(&runtime->sleep); ++ } else { ++ runtime->status->state = SNDRV_PCM_STATE_RUNNING; ++ if (runtime->sleep_min) ++ snd_pcm_tick_prepare(substream); ++ if (substream->timer) ++ snd_timer_notify(substream->timer, ++ SNDRV_TIMER_EVENT_MCONTINUE, ++ &runtime->trigger_tstamp); ++ } ++} ++ ++static struct action_ops snd_pcm_action_pause = { ++ .pre_action = snd_pcm_pre_pause, ++ .do_action = snd_pcm_do_pause, ++ .undo_action = snd_pcm_undo_pause, ++ .post_action = snd_pcm_post_pause ++}; ++ ++/* ++ * Push/release the pause for all linked streams. ++ */ ++static int snd_pcm_pause(struct snd_pcm_substream *substream, int push) ++{ ++ return snd_pcm_action(&snd_pcm_action_pause, substream, push); ++} ++ ++#ifdef CONFIG_PM ++/* suspend */ ++ ++static int snd_pcm_pre_suspend(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) ++ return -EBUSY; ++ runtime->trigger_master = substream; ++ return 0; ++} ++ ++static int snd_pcm_do_suspend(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ if (runtime->trigger_master != substream) ++ return 0; ++ if (! snd_pcm_running(substream)) ++ return 0; ++ substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND); ++ return 0; /* suspend unconditionally */ ++} ++ ++static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ snd_pcm_trigger_tstamp(substream); ++ if (substream->timer) ++ snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND, ++ &runtime->trigger_tstamp); ++ runtime->status->suspended_state = runtime->status->state; ++ runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; ++ snd_pcm_tick_set(substream, 0); ++ wake_up(&runtime->sleep); ++} ++ ++static struct action_ops snd_pcm_action_suspend = { ++ .pre_action = snd_pcm_pre_suspend, ++ .do_action = snd_pcm_do_suspend, ++ .post_action = snd_pcm_post_suspend ++}; ++ ++/** ++ * snd_pcm_suspend ++ * @substream: the PCM substream ++ * ++ * Trigger SUSPEND to all linked streams. ++ * After this call, all streams are changed to SUSPENDED state. ++ */ ++int snd_pcm_suspend(struct snd_pcm_substream *substream) ++{ ++ int err; ++ unsigned long flags; ++ ++ if (! substream) ++ return 0; ++ ++ snd_pcm_stream_lock_irqsave(substream, flags); ++ err = snd_pcm_action(&snd_pcm_action_suspend, substream, 0); ++ snd_pcm_stream_unlock_irqrestore(substream, flags); ++ return err; ++} ++ ++EXPORT_SYMBOL(snd_pcm_suspend); ++ ++/** ++ * snd_pcm_suspend_all ++ * @pcm: the PCM instance ++ * ++ * Trigger SUSPEND to all substreams in the given pcm. ++ * After this call, all streams are changed to SUSPENDED state. ++ */ ++int snd_pcm_suspend_all(struct snd_pcm *pcm) ++{ ++ struct snd_pcm_substream *substream; ++ int stream, err = 0; ++ ++ if (! pcm) ++ return 0; ++ ++ for (stream = 0; stream < 2; stream++) { ++ for (substream = pcm->streams[stream].substream; ++ substream; substream = substream->next) { ++ /* FIXME: the open/close code should lock this as well */ ++ if (substream->runtime == NULL) ++ continue; ++ err = snd_pcm_suspend(substream); ++ if (err < 0 && err != -EBUSY) ++ return err; ++ } ++ } ++ return 0; ++} ++ ++EXPORT_SYMBOL(snd_pcm_suspend_all); ++ ++/* resume */ ++ ++static int snd_pcm_pre_resume(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ if (!(runtime->info & SNDRV_PCM_INFO_RESUME)) ++ return -ENOSYS; ++ runtime->trigger_master = substream; ++ return 0; ++} ++ ++static int snd_pcm_do_resume(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ if (runtime->trigger_master != substream) ++ return 0; ++ /* DMA not running previously? */ ++ if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING && ++ (runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING || ++ substream->stream != SNDRV_PCM_STREAM_PLAYBACK)) ++ return 0; ++ return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME); ++} ++ ++static void snd_pcm_undo_resume(struct snd_pcm_substream *substream, int state) ++{ ++ if (substream->runtime->trigger_master == substream && ++ snd_pcm_running(substream)) ++ substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND); ++} ++ ++static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ snd_pcm_trigger_tstamp(substream); ++ if (substream->timer) ++ snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME, ++ &runtime->trigger_tstamp); ++ runtime->status->state = runtime->status->suspended_state; ++ if (runtime->sleep_min) ++ snd_pcm_tick_prepare(substream); ++} ++ ++static struct action_ops snd_pcm_action_resume = { ++ .pre_action = snd_pcm_pre_resume, ++ .do_action = snd_pcm_do_resume, ++ .undo_action = snd_pcm_undo_resume, ++ .post_action = snd_pcm_post_resume ++}; ++ ++static int snd_pcm_resume(struct snd_pcm_substream *substream) ++{ ++ struct snd_card *card = substream->pcm->card; ++ int res; ++ ++ snd_power_lock(card); ++ if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0) ++ res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0); ++ snd_power_unlock(card); ++ return res; ++} ++ ++#else ++ ++static int snd_pcm_resume(struct snd_pcm_substream *substream) ++{ ++ return -ENOSYS; ++} ++ ++#endif /* CONFIG_PM */ ++ ++/* ++ * xrun ioctl ++ * ++ * Change the RUNNING stream(s) to XRUN state. ++ */ ++static int snd_pcm_xrun(struct snd_pcm_substream *substream) ++{ ++ struct snd_card *card = substream->pcm->card; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ int result; ++ ++ snd_power_lock(card); ++ if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { ++ result = snd_power_wait(card, SNDRV_CTL_POWER_D0); ++ if (result < 0) ++ goto _unlock; ++ } ++ ++ snd_pcm_stream_lock_irq(substream); ++ switch (runtime->status->state) { ++ case SNDRV_PCM_STATE_XRUN: ++ result = 0; /* already there */ ++ break; ++ case SNDRV_PCM_STATE_RUNNING: ++ result = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); ++ break; ++ default: ++ result = -EBADFD; ++ } ++ snd_pcm_stream_unlock_irq(substream); ++ _unlock: ++ snd_power_unlock(card); ++ return result; ++} ++ ++/* ++ * reset ioctl ++ */ ++static int snd_pcm_pre_reset(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ switch (runtime->status->state) { ++ case SNDRV_PCM_STATE_RUNNING: ++ case SNDRV_PCM_STATE_PREPARED: ++ case SNDRV_PCM_STATE_PAUSED: ++ case SNDRV_PCM_STATE_SUSPENDED: ++ return 0; ++ default: ++ return -EBADFD; ++ } ++} ++ ++static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL); ++ if (err < 0) ++ return err; ++ // snd_assert(runtime->status->hw_ptr < runtime->buffer_size, ); ++ runtime->hw_ptr_base = 0; ++ runtime->hw_ptr_interrupt = runtime->status->hw_ptr - ++ runtime->status->hw_ptr % runtime->period_size; ++ runtime->silence_start = runtime->status->hw_ptr; ++ runtime->silence_filled = 0; ++ return 0; ++} ++ ++static void snd_pcm_post_reset(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ runtime->control->appl_ptr = runtime->status->hw_ptr; ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && ++ runtime->silence_size > 0) ++ snd_pcm_playback_silence(substream, ULONG_MAX); ++} ++ ++static struct action_ops snd_pcm_action_reset = { ++ .pre_action = snd_pcm_pre_reset, ++ .do_action = snd_pcm_do_reset, ++ .post_action = snd_pcm_post_reset ++}; ++ ++static int snd_pcm_reset(struct snd_pcm_substream *substream) ++{ ++ return snd_pcm_action_nonatomic(&snd_pcm_action_reset, substream, 0); ++} ++ ++/* ++ * prepare ioctl ++ */ ++/* we use the second argument for updating f_flags */ ++static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream, ++ int f_flags) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN || ++ runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) ++ return -EBADFD; ++ if (snd_pcm_running(substream)) ++ return -EBUSY; ++ substream->f_flags = f_flags; ++ return 0; ++} ++ ++static int snd_pcm_do_prepare(struct snd_pcm_substream *substream, int state) ++{ ++ int err; ++ err = substream->ops->prepare(substream); ++ if (err < 0) ++ return err; ++ return snd_pcm_do_reset(substream, 0); ++} ++ ++static void snd_pcm_post_prepare(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ runtime->control->appl_ptr = runtime->status->hw_ptr; ++ runtime->status->state = SNDRV_PCM_STATE_PREPARED; ++} ++ ++static struct action_ops snd_pcm_action_prepare = { ++ .pre_action = snd_pcm_pre_prepare, ++ .do_action = snd_pcm_do_prepare, ++ .post_action = snd_pcm_post_prepare ++}; ++ ++/** ++ * snd_pcm_prepare ++ * @substream: the PCM substream instance ++ * @file: file to refer f_flags ++ * ++ * Prepare the PCM substream to be triggerable. ++ */ ++static int snd_pcm_prepare(struct snd_pcm_substream *substream, ++ struct file *file) ++{ ++ int res; ++ struct snd_card *card = substream->pcm->card; ++ int f_flags; ++ ++ if (file) ++ f_flags = file->f_flags; ++ else ++ f_flags = substream->f_flags; ++ ++ snd_power_lock(card); ++ if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0) ++ res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, ++ substream, f_flags); ++ snd_power_unlock(card); ++ return res; ++} ++ ++/* ++ * drain ioctl ++ */ ++ ++static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state) ++{ ++ if (substream->f_flags & O_NONBLOCK) ++ return -EAGAIN; ++ substream->runtime->trigger_master = substream; ++ return 0; ++} ++ ++static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ switch (runtime->status->state) { ++ case SNDRV_PCM_STATE_PREPARED: ++ /* start playback stream if possible */ ++ if (! snd_pcm_playback_empty(substream)) { ++ snd_pcm_do_start(substream, SNDRV_PCM_STATE_DRAINING); ++ snd_pcm_post_start(substream, SNDRV_PCM_STATE_DRAINING); ++ } ++ break; ++ case SNDRV_PCM_STATE_RUNNING: ++ runtime->status->state = SNDRV_PCM_STATE_DRAINING; ++ break; ++ default: ++ break; ++ } ++ } else { ++ /* stop running stream */ ++ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) { ++ int state = snd_pcm_capture_avail(runtime) > 0 ? ++ SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP; ++ snd_pcm_do_stop(substream, state); ++ snd_pcm_post_stop(substream, state); ++ } ++ } ++ return 0; ++} ++ ++static void snd_pcm_post_drain_init(struct snd_pcm_substream *substream, int state) ++{ ++} ++ ++static struct action_ops snd_pcm_action_drain_init = { ++ .pre_action = snd_pcm_pre_drain_init, ++ .do_action = snd_pcm_do_drain_init, ++ .post_action = snd_pcm_post_drain_init ++}; ++ ++struct drain_rec { ++ struct snd_pcm_substream *substream; ++ wait_queue_t wait; ++ snd_pcm_uframes_t stop_threshold; ++}; ++ ++static int snd_pcm_drop(struct snd_pcm_substream *substream); ++ ++/* ++ * Drain the stream(s). ++ * When the substream is linked, sync until the draining of all playback streams ++ * is finished. ++ * After this call, all streams are supposed to be either SETUP or DRAINING ++ * (capture only) state. ++ */ ++static int snd_pcm_drain(struct snd_pcm_substream *substream) ++{ ++ struct snd_card *card; ++ struct snd_pcm_runtime *runtime; ++ struct snd_pcm_substream *s; ++ int result = 0; ++ int i, num_drecs; ++ struct drain_rec *drec, drec_tmp, *d; ++ ++ snd_assert(substream != NULL, return -ENXIO); ++ card = substream->pcm->card; ++ runtime = substream->runtime; ++ ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) ++ return -EBADFD; ++ ++ snd_power_lock(card); ++ if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { ++ result = snd_power_wait(card, SNDRV_CTL_POWER_D0); ++ if (result < 0) { ++ snd_power_unlock(card); ++ return result; ++ } ++ } ++ ++ /* allocate temporary record for drain sync */ ++ down_read(&snd_pcm_link_rwsem); ++ if (snd_pcm_stream_linked(substream)) { ++ drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL); ++ if (! drec) { ++ up_read(&snd_pcm_link_rwsem); ++ snd_power_unlock(card); ++ return -ENOMEM; ++ } ++ } else ++ drec = &drec_tmp; ++ ++ /* count only playback streams */ ++ num_drecs = 0; ++ snd_pcm_group_for_each_entry(s, substream) { ++ runtime = s->runtime; ++ if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ d = &drec[num_drecs++]; ++ d->substream = s; ++ init_waitqueue_entry(&d->wait, current); ++ add_wait_queue(&runtime->sleep, &d->wait); ++ /* stop_threshold fixup to avoid endless loop when ++ * stop_threshold > buffer_size ++ */ ++ d->stop_threshold = runtime->stop_threshold; ++ if (runtime->stop_threshold > runtime->buffer_size) ++ runtime->stop_threshold = runtime->buffer_size; ++ } ++ } ++ up_read(&snd_pcm_link_rwsem); ++ ++ snd_pcm_stream_lock_irq(substream); ++ /* resume pause */ ++ if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED) ++ snd_pcm_pause(substream, 0); ++ ++ /* pre-start/stop - all running streams are changed to DRAINING state */ ++ result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0); ++ if (result < 0) { ++ snd_pcm_stream_unlock_irq(substream); ++ goto _error; ++ } ++ ++ for (;;) { ++ long tout; ++ if (signal_pending(current)) { ++ result = -ERESTARTSYS; ++ break; ++ } ++ /* all finished? */ ++ for (i = 0; i < num_drecs; i++) { ++ runtime = drec[i].substream->runtime; ++ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) ++ break; ++ } ++ if (i == num_drecs) ++ break; /* yes, all drained */ ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ snd_pcm_stream_unlock_irq(substream); ++ snd_power_unlock(card); ++ tout = schedule_timeout(10 * HZ); ++ snd_power_lock(card); ++ snd_pcm_stream_lock_irq(substream); ++ if (tout == 0) { ++ if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) ++ result = -ESTRPIPE; ++ else { ++ snd_printd("playback drain error (DMA or IRQ trouble?)\n"); ++ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); ++ result = -EIO; ++ } ++ break; ++ } ++ } ++ ++ snd_pcm_stream_unlock_irq(substream); ++ ++ _error: ++ for (i = 0; i < num_drecs; i++) { ++ d = &drec[i]; ++ runtime = d->substream->runtime; ++ remove_wait_queue(&runtime->sleep, &d->wait); ++ runtime->stop_threshold = d->stop_threshold; ++ } ++ ++ if (drec != &drec_tmp) ++ kfree(drec); ++ snd_power_unlock(card); ++ ++ return result; ++} ++ ++/* ++ * drop ioctl ++ * ++ * Immediately put all linked substreams into SETUP state. ++ */ ++static int snd_pcm_drop(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime; ++ struct snd_card *card; ++ int result = 0; ++ ++ snd_assert(substream != NULL, return -ENXIO); ++ runtime = substream->runtime; ++ card = substream->pcm->card; ++ ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN || ++ runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) ++ return -EBADFD; ++ ++ snd_power_lock(card); ++ if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { ++ result = snd_power_wait(card, SNDRV_CTL_POWER_D0); ++ if (result < 0) ++ goto _unlock; ++ } ++ ++ snd_pcm_stream_lock_irq(substream); ++ /* resume pause */ ++ if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) ++ snd_pcm_pause(substream, 0); ++ ++ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); ++ /* runtime->control->appl_ptr = runtime->status->hw_ptr; */ ++ snd_pcm_stream_unlock_irq(substream); ++ _unlock: ++ snd_power_unlock(card); ++ return result; ++} ++ ++ ++/* WARNING: Don't forget to fput back the file */ ++static struct file *snd_pcm_file_fd(int fd) ++{ ++ struct file *file; ++ struct inode *inode; ++ unsigned int minor; ++ ++ file = fget(fd); ++ if (!file) ++ return NULL; ++ inode = file->f_path.dentry->d_inode; ++ if (!S_ISCHR(inode->i_mode) || ++ imajor(inode) != snd_major) { ++ fput(file); ++ return NULL; ++ } ++ minor = iminor(inode); ++ if (!snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) && ++ !snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE)) { ++ fput(file); ++ return NULL; ++ } ++ return file; ++} ++ ++/* ++ * PCM link handling ++ */ ++static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) ++{ ++ int res = 0; ++ struct file *file; ++ struct snd_pcm_file *pcm_file; ++ struct snd_pcm_substream *substream1; ++ ++ file = snd_pcm_file_fd(fd); ++ if (!file) ++ return -EBADFD; ++ pcm_file = file->private_data; ++ substream1 = pcm_file->substream; ++ down_write(&snd_pcm_link_rwsem); ++ write_lock_irq(&snd_pcm_link_rwlock); ++ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN || ++ substream->runtime->status->state != substream1->runtime->status->state) { ++ res = -EBADFD; ++ goto _end; ++ } ++ if (snd_pcm_stream_linked(substream1)) { ++ res = -EALREADY; ++ goto _end; ++ } ++ if (!snd_pcm_stream_linked(substream)) { ++ substream->group = kmalloc(sizeof(struct snd_pcm_group), GFP_ATOMIC); ++ if (substream->group == NULL) { ++ res = -ENOMEM; ++ goto _end; ++ } ++ spin_lock_init(&substream->group->lock); ++ INIT_LIST_HEAD(&substream->group->substreams); ++ list_add_tail(&substream->link_list, &substream->group->substreams); ++ substream->group->count = 1; ++ } ++ list_add_tail(&substream1->link_list, &substream->group->substreams); ++ substream->group->count++; ++ substream1->group = substream->group; ++ _end: ++ write_unlock_irq(&snd_pcm_link_rwlock); ++ up_write(&snd_pcm_link_rwsem); ++ fput(file); ++ return res; ++} ++ ++static void relink_to_local(struct snd_pcm_substream *substream) ++{ ++ substream->group = &substream->self_group; ++ INIT_LIST_HEAD(&substream->self_group.substreams); ++ list_add_tail(&substream->link_list, &substream->self_group.substreams); ++} ++ ++static int snd_pcm_unlink(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_substream *s; ++ int res = 0; ++ ++ down_write(&snd_pcm_link_rwsem); ++ write_lock_irq(&snd_pcm_link_rwlock); ++ if (!snd_pcm_stream_linked(substream)) { ++ res = -EALREADY; ++ goto _end; ++ } ++ list_del(&substream->link_list); ++ substream->group->count--; ++ if (substream->group->count == 1) { /* detach the last stream, too */ ++ snd_pcm_group_for_each_entry(s, substream) { ++ relink_to_local(s); ++ break; ++ } ++ kfree(substream->group); ++ } ++ relink_to_local(substream); ++ _end: ++ write_unlock_irq(&snd_pcm_link_rwlock); ++ up_write(&snd_pcm_link_rwsem); ++ return res; ++} ++ ++/* ++ * hw configurator ++ */ ++static int snd_pcm_hw_rule_mul(struct snd_pcm_hw_params *params, ++ struct snd_pcm_hw_rule *rule) ++{ ++ struct snd_interval t; ++ snd_interval_mul(hw_param_interval_c(params, rule->deps[0]), ++ hw_param_interval_c(params, rule->deps[1]), &t); ++ return snd_interval_refine(hw_param_interval(params, rule->var), &t); ++} ++ ++static int snd_pcm_hw_rule_div(struct snd_pcm_hw_params *params, ++ struct snd_pcm_hw_rule *rule) ++{ ++ struct snd_interval t; ++ snd_interval_div(hw_param_interval_c(params, rule->deps[0]), ++ hw_param_interval_c(params, rule->deps[1]), &t); ++ return snd_interval_refine(hw_param_interval(params, rule->var), &t); ++} ++ ++static int snd_pcm_hw_rule_muldivk(struct snd_pcm_hw_params *params, ++ struct snd_pcm_hw_rule *rule) ++{ ++ struct snd_interval t; ++ snd_interval_muldivk(hw_param_interval_c(params, rule->deps[0]), ++ hw_param_interval_c(params, rule->deps[1]), ++ (unsigned long) rule->private, &t); ++ return snd_interval_refine(hw_param_interval(params, rule->var), &t); ++} ++ ++static int snd_pcm_hw_rule_mulkdiv(struct snd_pcm_hw_params *params, ++ struct snd_pcm_hw_rule *rule) ++{ ++ struct snd_interval t; ++ snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]), ++ (unsigned long) rule->private, ++ hw_param_interval_c(params, rule->deps[1]), &t); ++ return snd_interval_refine(hw_param_interval(params, rule->var), &t); ++} ++ ++static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params, ++ struct snd_pcm_hw_rule *rule) ++{ ++ unsigned int k; ++ struct snd_interval *i = hw_param_interval(params, rule->deps[0]); ++ struct snd_mask m; ++ struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); ++ snd_mask_any(&m); ++ for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { ++ int bits; ++ if (! snd_mask_test(mask, k)) ++ continue; ++ bits = snd_pcm_format_physical_width(k); ++ if (bits <= 0) ++ continue; /* ignore invalid formats */ ++ if ((unsigned)bits < i->min || (unsigned)bits > i->max) ++ snd_mask_reset(&m, k); ++ } ++ return snd_mask_refine(mask, &m); ++} ++ ++static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params, ++ struct snd_pcm_hw_rule *rule) ++{ ++ struct snd_interval t; ++ unsigned int k; ++ t.min = UINT_MAX; ++ t.max = 0; ++ t.openmin = 0; ++ t.openmax = 0; ++ for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { ++ int bits; ++ if (! snd_mask_test(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k)) ++ continue; ++ bits = snd_pcm_format_physical_width(k); ++ if (bits <= 0) ++ continue; /* ignore invalid formats */ ++ if (t.min > (unsigned)bits) ++ t.min = bits; ++ if (t.max < (unsigned)bits) ++ t.max = bits; ++ } ++ t.integer = 1; ++ return snd_interval_refine(hw_param_interval(params, rule->var), &t); ++} ++ ++#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 ++#error "Change this table" ++#endif ++ ++static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, ++ 48000, 64000, 88200, 96000, 176400, 192000 }; ++ ++const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { ++ .count = ARRAY_SIZE(rates), ++ .list = rates, ++}; ++ ++static int snd_pcm_hw_rule_rate(struct snd_pcm_hw_params *params, ++ struct snd_pcm_hw_rule *rule) ++{ ++ struct snd_pcm_hardware *hw = rule->private; ++ return snd_interval_list(hw_param_interval(params, rule->var), ++ snd_pcm_known_rates.count, ++ snd_pcm_known_rates.list, hw->rates); ++} ++ ++static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params, ++ struct snd_pcm_hw_rule *rule) ++{ ++ struct snd_interval t; ++ struct snd_pcm_substream *substream = rule->private; ++ t.min = 0; ++ t.max = substream->buffer_bytes_max; ++ t.openmin = 0; ++ t.openmax = 0; ++ t.integer = 1; ++ return snd_interval_refine(hw_param_interval(params, rule->var), &t); ++} ++ ++int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; ++ int k, err; ++ ++ for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { ++ snd_mask_any(constrs_mask(constrs, k)); ++ } ++ ++ for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { ++ snd_interval_any(constrs_interval(constrs, k)); ++ } ++ ++ snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_CHANNELS)); ++ snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_SIZE)); ++ snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)); ++ snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)); ++ snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS)); ++ ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, ++ snd_pcm_hw_rule_format, NULL, ++ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, ++ snd_pcm_hw_rule_sample_bits, NULL, ++ SNDRV_PCM_HW_PARAM_FORMAT, ++ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, ++ snd_pcm_hw_rule_div, NULL, ++ SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, ++ snd_pcm_hw_rule_mul, NULL, ++ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, ++ snd_pcm_hw_rule_mulkdiv, (void*) 8, ++ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, ++ snd_pcm_hw_rule_mulkdiv, (void*) 8, ++ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, ++ snd_pcm_hw_rule_div, NULL, ++ SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, ++ snd_pcm_hw_rule_mulkdiv, (void*) 1000000, ++ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_TIME, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, ++ snd_pcm_hw_rule_mulkdiv, (void*) 1000000, ++ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_BUFFER_TIME, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, ++ snd_pcm_hw_rule_div, NULL, ++ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, ++ snd_pcm_hw_rule_div, NULL, ++ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, ++ snd_pcm_hw_rule_mulkdiv, (void*) 8, ++ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, ++ snd_pcm_hw_rule_muldivk, (void*) 1000000, ++ SNDRV_PCM_HW_PARAM_PERIOD_TIME, SNDRV_PCM_HW_PARAM_RATE, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, ++ snd_pcm_hw_rule_mul, NULL, ++ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, ++ snd_pcm_hw_rule_mulkdiv, (void*) 8, ++ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, ++ snd_pcm_hw_rule_muldivk, (void*) 1000000, ++ SNDRV_PCM_HW_PARAM_BUFFER_TIME, SNDRV_PCM_HW_PARAM_RATE, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, ++ snd_pcm_hw_rule_muldivk, (void*) 8, ++ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, ++ snd_pcm_hw_rule_muldivk, (void*) 8, ++ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME, ++ snd_pcm_hw_rule_mulkdiv, (void*) 1000000, ++ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1); ++ if (err < 0) ++ return err; ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_TIME, ++ snd_pcm_hw_rule_mulkdiv, (void*) 1000000, ++ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1); ++ if (err < 0) ++ return err; ++ return 0; ++} ++ ++int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct snd_pcm_hardware *hw = &runtime->hw; ++ int err; ++ unsigned int mask = 0; ++ ++ if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) ++ mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED; ++ if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) ++ mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; ++ if (hw->info & SNDRV_PCM_INFO_MMAP) { ++ if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) ++ mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; ++ if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) ++ mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED; ++ if (hw->info & SNDRV_PCM_INFO_COMPLEX) ++ mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX; ++ } ++ err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask); ++ snd_assert(err >= 0, return -EINVAL); ++ ++ err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats); ++ snd_assert(err >= 0, return -EINVAL); ++ ++ err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD); ++ snd_assert(err >= 0, return -EINVAL); ++ ++ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, ++ hw->channels_min, hw->channels_max); ++ snd_assert(err >= 0, return -EINVAL); ++ ++ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, ++ hw->rate_min, hw->rate_max); ++ snd_assert(err >= 0, return -EINVAL); ++ ++ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, ++ hw->period_bytes_min, hw->period_bytes_max); ++ snd_assert(err >= 0, return -EINVAL); ++ ++ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS, ++ hw->periods_min, hw->periods_max); ++ snd_assert(err >= 0, return -EINVAL); ++ ++ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, ++ hw->period_bytes_min, hw->buffer_bytes_max); ++ snd_assert(err >= 0, return -EINVAL); ++ ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, ++ snd_pcm_hw_rule_buffer_bytes_max, substream, ++ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1); ++ if (err < 0) ++ return err; ++ ++ /* FIXME: remove */ ++ if (runtime->dma_bytes) { ++ err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes); ++ snd_assert(err >= 0, return -EINVAL); ++ } ++ ++ if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) { ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, ++ snd_pcm_hw_rule_rate, hw, ++ SNDRV_PCM_HW_PARAM_RATE, -1); ++ if (err < 0) ++ return err; ++ } ++ ++ /* FIXME: this belong to lowlevel */ ++ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_TICK_TIME, ++ 1000000 / HZ, 1000000 / HZ); ++ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); ++ ++ return 0; ++} ++ ++static void pcm_release_private(struct snd_pcm_substream *substream) ++{ ++ snd_pcm_unlink(substream); ++} ++ ++void snd_pcm_release_substream(struct snd_pcm_substream *substream) ++{ ++ substream->ref_count--; ++ if (substream->ref_count > 0) ++ return; ++ ++ snd_pcm_drop(substream); ++ if (substream->hw_opened) { ++ if (substream->ops->hw_free != NULL) ++ substream->ops->hw_free(substream); ++ substream->ops->close(substream); ++ substream->hw_opened = 0; ++ } ++ if (substream->pcm_release) { ++ substream->pcm_release(substream); ++ substream->pcm_release = NULL; ++ } ++ snd_pcm_detach_substream(substream); ++} ++ ++EXPORT_SYMBOL(snd_pcm_release_substream); ++ ++int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, ++ struct file *file, ++ struct snd_pcm_substream **rsubstream) ++{ ++ struct snd_pcm_substream *substream; ++ int err; ++ ++ err = snd_pcm_attach_substream(pcm, stream, file, &substream); ++ if (err < 0) ++ return err; ++ if (substream->ref_count > 1) { ++ *rsubstream = substream; ++ return 0; ++ } ++ ++ err = snd_pcm_hw_constraints_init(substream); ++ if (err < 0) { ++ snd_printd("snd_pcm_hw_constraints_init failed\n"); ++ goto error; ++ } ++ ++ if ((err = substream->ops->open(substream)) < 0) ++ goto error; ++ ++ substream->hw_opened = 1; ++ ++ err = snd_pcm_hw_constraints_complete(substream); ++ if (err < 0) { ++ snd_printd("snd_pcm_hw_constraints_complete failed\n"); ++ goto error; ++ } ++ ++ *rsubstream = substream; ++ return 0; ++ ++ error: ++ snd_pcm_release_substream(substream); ++ return err; ++} ++ ++EXPORT_SYMBOL(snd_pcm_open_substream); ++ ++static int snd_pcm_open_file(struct file *file, ++ struct snd_pcm *pcm, ++ int stream, ++ struct snd_pcm_file **rpcm_file) ++{ ++ struct snd_pcm_file *pcm_file; ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_str *str; ++ int err; ++ ++ snd_assert(rpcm_file != NULL, return -EINVAL); ++ *rpcm_file = NULL; ++ ++ err = snd_pcm_open_substream(pcm, stream, file, &substream); ++ if (err < 0) ++ return err; ++ ++ pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL); ++ if (pcm_file == NULL) { ++ snd_pcm_release_substream(substream); ++ return -ENOMEM; ++ } ++ pcm_file->substream = substream; ++ if (substream->ref_count == 1) { ++ str = substream->pstr; ++ substream->file = pcm_file; ++ substream->pcm_release = pcm_release_private; ++ } ++ file->private_data = pcm_file; ++ *rpcm_file = pcm_file; ++ return 0; ++} ++ ++static int snd_pcm_playback_open(struct inode *inode, struct file *file) ++{ ++ struct snd_pcm *pcm; ++ ++ pcm = snd_lookup_minor_data(iminor(inode), ++ SNDRV_DEVICE_TYPE_PCM_PLAYBACK); ++ return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK); ++} ++ ++static int snd_pcm_capture_open(struct inode *inode, struct file *file) ++{ ++ struct snd_pcm *pcm; ++ ++ pcm = snd_lookup_minor_data(iminor(inode), ++ SNDRV_DEVICE_TYPE_PCM_CAPTURE); ++ return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE); ++} ++ ++static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream) ++{ ++ int err; ++ struct snd_pcm_file *pcm_file; ++ wait_queue_t wait; ++ ++ if (pcm == NULL) { ++ err = -ENODEV; ++ goto __error1; ++ } ++ err = snd_card_file_add(pcm->card, file); ++ if (err < 0) ++ goto __error1; ++ if (!try_module_get(pcm->card->module)) { ++ err = -EFAULT; ++ goto __error2; ++ } ++ init_waitqueue_entry(&wait, current); ++ add_wait_queue(&pcm->open_wait, &wait); ++ mutex_lock(&pcm->open_mutex); ++ while (1) { ++ err = snd_pcm_open_file(file, pcm, stream, &pcm_file); ++ if (err >= 0) ++ break; ++ if (err == -EAGAIN) { ++ if (file->f_flags & O_NONBLOCK) { ++ err = -EBUSY; ++ break; ++ } ++ } else ++ break; ++ set_current_state(TASK_INTERRUPTIBLE); ++ mutex_unlock(&pcm->open_mutex); ++ schedule(); ++ mutex_lock(&pcm->open_mutex); ++ if (signal_pending(current)) { ++ err = -ERESTARTSYS; ++ break; ++ } ++ } ++ remove_wait_queue(&pcm->open_wait, &wait); ++ mutex_unlock(&pcm->open_mutex); ++ if (err < 0) ++ goto __error; ++ return err; ++ ++ __error: ++ module_put(pcm->card->module); ++ __error2: ++ snd_card_file_remove(pcm->card, file); ++ __error1: ++ return err; ++} ++ ++static int snd_pcm_release(struct inode *inode, struct file *file) ++{ ++ struct snd_pcm *pcm; ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_file *pcm_file; ++ ++ pcm_file = file->private_data; ++ substream = pcm_file->substream; ++ snd_assert(substream != NULL, return -ENXIO); ++ pcm = substream->pcm; ++ fasync_helper(-1, file, 0, &substream->runtime->fasync); ++ mutex_lock(&pcm->open_mutex); ++ snd_pcm_release_substream(substream); ++ kfree(pcm_file); ++ mutex_unlock(&pcm->open_mutex); ++ wake_up(&pcm->open_wait); ++ module_put(pcm->card->module); ++ snd_card_file_remove(pcm->card, file); ++ return 0; ++} ++ ++static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *substream, ++ snd_pcm_uframes_t frames) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ snd_pcm_sframes_t appl_ptr; ++ snd_pcm_sframes_t ret; ++ snd_pcm_sframes_t hw_avail; ++ ++ if (frames == 0) ++ return 0; ++ ++ snd_pcm_stream_lock_irq(substream); ++ switch (runtime->status->state) { ++ case SNDRV_PCM_STATE_PREPARED: ++ break; ++ case SNDRV_PCM_STATE_DRAINING: ++ case SNDRV_PCM_STATE_RUNNING: ++ if (snd_pcm_update_hw_ptr(substream) >= 0) ++ break; ++ /* Fall through */ ++ case SNDRV_PCM_STATE_XRUN: ++ ret = -EPIPE; ++ goto __end; ++ default: ++ ret = -EBADFD; ++ goto __end; ++ } ++ ++ hw_avail = snd_pcm_playback_hw_avail(runtime); ++ if (hw_avail <= 0) { ++ ret = 0; ++ goto __end; ++ } ++ if (frames > (snd_pcm_uframes_t)hw_avail) ++ frames = hw_avail; ++ else ++ frames -= frames % runtime->xfer_align; ++ appl_ptr = runtime->control->appl_ptr - frames; ++ if (appl_ptr < 0) ++ appl_ptr += runtime->boundary; ++ runtime->control->appl_ptr = appl_ptr; ++ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && ++ runtime->sleep_min) ++ snd_pcm_tick_prepare(substream); ++ ret = frames; ++ __end: ++ snd_pcm_stream_unlock_irq(substream); ++ return ret; ++} ++ ++static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substream, ++ snd_pcm_uframes_t frames) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ snd_pcm_sframes_t appl_ptr; ++ snd_pcm_sframes_t ret; ++ snd_pcm_sframes_t hw_avail; ++ ++ if (frames == 0) ++ return 0; ++ ++ snd_pcm_stream_lock_irq(substream); ++ switch (runtime->status->state) { ++ case SNDRV_PCM_STATE_PREPARED: ++ case SNDRV_PCM_STATE_DRAINING: ++ break; ++ case SNDRV_PCM_STATE_RUNNING: ++ if (snd_pcm_update_hw_ptr(substream) >= 0) ++ break; ++ /* Fall through */ ++ case SNDRV_PCM_STATE_XRUN: ++ ret = -EPIPE; ++ goto __end; ++ default: ++ ret = -EBADFD; ++ goto __end; ++ } ++ ++ hw_avail = snd_pcm_capture_hw_avail(runtime); ++ if (hw_avail <= 0) { ++ ret = 0; ++ goto __end; ++ } ++ if (frames > (snd_pcm_uframes_t)hw_avail) ++ frames = hw_avail; ++ else ++ frames -= frames % runtime->xfer_align; ++ appl_ptr = runtime->control->appl_ptr - frames; ++ if (appl_ptr < 0) ++ appl_ptr += runtime->boundary; ++ runtime->control->appl_ptr = appl_ptr; ++ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && ++ runtime->sleep_min) ++ snd_pcm_tick_prepare(substream); ++ ret = frames; ++ __end: ++ snd_pcm_stream_unlock_irq(substream); ++ return ret; ++} ++ ++static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *substream, ++ snd_pcm_uframes_t frames) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ snd_pcm_sframes_t appl_ptr; ++ snd_pcm_sframes_t ret; ++ snd_pcm_sframes_t avail; ++ ++ if (frames == 0) ++ return 0; ++ ++ snd_pcm_stream_lock_irq(substream); ++ switch (runtime->status->state) { ++ case SNDRV_PCM_STATE_PREPARED: ++ case SNDRV_PCM_STATE_PAUSED: ++ break; ++ case SNDRV_PCM_STATE_DRAINING: ++ case SNDRV_PCM_STATE_RUNNING: ++ if (snd_pcm_update_hw_ptr(substream) >= 0) ++ break; ++ /* Fall through */ ++ case SNDRV_PCM_STATE_XRUN: ++ ret = -EPIPE; ++ goto __end; ++ default: ++ ret = -EBADFD; ++ goto __end; ++ } ++ ++ avail = snd_pcm_playback_avail(runtime); ++ if (avail <= 0) { ++ ret = 0; ++ goto __end; ++ } ++ if (frames > (snd_pcm_uframes_t)avail) ++ frames = avail; ++ else ++ frames -= frames % runtime->xfer_align; ++ appl_ptr = runtime->control->appl_ptr + frames; ++ if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) ++ appl_ptr -= runtime->boundary; ++ runtime->control->appl_ptr = appl_ptr; ++ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && ++ runtime->sleep_min) ++ snd_pcm_tick_prepare(substream); ++ ret = frames; ++ __end: ++ snd_pcm_stream_unlock_irq(substream); ++ return ret; ++} ++ ++static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *substream, ++ snd_pcm_uframes_t frames) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ snd_pcm_sframes_t appl_ptr; ++ snd_pcm_sframes_t ret; ++ snd_pcm_sframes_t avail; ++ ++ if (frames == 0) ++ return 0; ++ ++ snd_pcm_stream_lock_irq(substream); ++ switch (runtime->status->state) { ++ case SNDRV_PCM_STATE_PREPARED: ++ case SNDRV_PCM_STATE_DRAINING: ++ case SNDRV_PCM_STATE_PAUSED: ++ break; ++ case SNDRV_PCM_STATE_RUNNING: ++ if (snd_pcm_update_hw_ptr(substream) >= 0) ++ break; ++ /* Fall through */ ++ case SNDRV_PCM_STATE_XRUN: ++ ret = -EPIPE; ++ goto __end; ++ default: ++ ret = -EBADFD; ++ goto __end; ++ } ++ ++ avail = snd_pcm_capture_avail(runtime); ++ if (avail <= 0) { ++ ret = 0; ++ goto __end; ++ } ++ if (frames > (snd_pcm_uframes_t)avail) ++ frames = avail; ++ else ++ frames -= frames % runtime->xfer_align; ++ appl_ptr = runtime->control->appl_ptr + frames; ++ if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) ++ appl_ptr -= runtime->boundary; ++ runtime->control->appl_ptr = appl_ptr; ++ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && ++ runtime->sleep_min) ++ snd_pcm_tick_prepare(substream); ++ ret = frames; ++ __end: ++ snd_pcm_stream_unlock_irq(substream); ++ return ret; ++} ++ ++static int snd_pcm_hwsync(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ int err; ++ ++ snd_pcm_stream_lock_irq(substream); ++ switch (runtime->status->state) { ++ case SNDRV_PCM_STATE_DRAINING: ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ goto __badfd; ++ case SNDRV_PCM_STATE_RUNNING: ++ if ((err = snd_pcm_update_hw_ptr(substream)) < 0) ++ break; ++ /* Fall through */ ++ case SNDRV_PCM_STATE_PREPARED: ++ case SNDRV_PCM_STATE_SUSPENDED: ++ err = 0; ++ break; ++ case SNDRV_PCM_STATE_XRUN: ++ err = -EPIPE; ++ break; ++ default: ++ __badfd: ++ err = -EBADFD; ++ break; ++ } ++ snd_pcm_stream_unlock_irq(substream); ++ return err; ++} ++ ++static int snd_pcm_delay(struct snd_pcm_substream *substream, ++ snd_pcm_sframes_t __user *res) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ int err; ++ snd_pcm_sframes_t n = 0; ++ ++ snd_pcm_stream_lock_irq(substream); ++ switch (runtime->status->state) { ++ case SNDRV_PCM_STATE_DRAINING: ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ goto __badfd; ++ case SNDRV_PCM_STATE_RUNNING: ++ if ((err = snd_pcm_update_hw_ptr(substream)) < 0) ++ break; ++ /* Fall through */ ++ case SNDRV_PCM_STATE_PREPARED: ++ case SNDRV_PCM_STATE_SUSPENDED: ++ err = 0; ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ n = snd_pcm_playback_hw_avail(runtime); ++ else ++ n = snd_pcm_capture_avail(runtime); ++ break; ++ case SNDRV_PCM_STATE_XRUN: ++ err = -EPIPE; ++ break; ++ default: ++ __badfd: ++ err = -EBADFD; ++ break; ++ } ++ snd_pcm_stream_unlock_irq(substream); ++ if (!err) ++ if (put_user(n, res)) ++ err = -EFAULT; ++ return err; ++} ++ ++static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, ++ struct snd_pcm_sync_ptr __user *_sync_ptr) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct snd_pcm_sync_ptr sync_ptr; ++ volatile struct snd_pcm_mmap_status *status; ++ volatile struct snd_pcm_mmap_control *control; ++ int err; ++ ++ memset(&sync_ptr, 0, sizeof(sync_ptr)); ++ if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags))) ++ return -EFAULT; ++ if (copy_from_user(&sync_ptr.c.control, &(_sync_ptr->c.control), sizeof(struct snd_pcm_mmap_control))) ++ return -EFAULT; ++ status = runtime->status; ++ control = runtime->control; ++ if (sync_ptr.flags & SNDRV_PCM_SYNC_PTR_HWSYNC) { ++ err = snd_pcm_hwsync(substream); ++ if (err < 0) ++ return err; ++ } ++ snd_pcm_stream_lock_irq(substream); ++ if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) ++ control->appl_ptr = sync_ptr.c.control.appl_ptr; ++ else ++ sync_ptr.c.control.appl_ptr = control->appl_ptr; ++ if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) ++ control->avail_min = sync_ptr.c.control.avail_min; ++ else ++ sync_ptr.c.control.avail_min = control->avail_min; ++ sync_ptr.s.status.state = status->state; ++ sync_ptr.s.status.hw_ptr = status->hw_ptr; ++ sync_ptr.s.status.tstamp = status->tstamp; ++ sync_ptr.s.status.suspended_state = status->suspended_state; ++ snd_pcm_stream_unlock_irq(substream); ++ if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr))) ++ return -EFAULT; ++ return 0; ++} ++ ++static int snd_pcm_common_ioctl1(struct file *file, ++ struct snd_pcm_substream *substream, ++ unsigned int cmd, void __user *arg) ++{ ++ snd_assert(substream != NULL, return -ENXIO); ++ ++ switch (cmd) { ++ case SNDRV_PCM_IOCTL_PVERSION: ++ return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0; ++ case SNDRV_PCM_IOCTL_INFO: ++ return snd_pcm_info_user(substream, arg); ++ case SNDRV_PCM_IOCTL_TSTAMP: /* just for compatibility */ ++ return 0; ++ case SNDRV_PCM_IOCTL_HW_REFINE: ++ return snd_pcm_hw_refine_user(substream, arg); ++ case SNDRV_PCM_IOCTL_HW_PARAMS: ++ return snd_pcm_hw_params_user(substream, arg); ++ case SNDRV_PCM_IOCTL_HW_FREE: ++ return snd_pcm_hw_free(substream); ++ case SNDRV_PCM_IOCTL_SW_PARAMS: ++ return snd_pcm_sw_params_user(substream, arg); ++ case SNDRV_PCM_IOCTL_STATUS: ++ return snd_pcm_status_user(substream, arg); ++ case SNDRV_PCM_IOCTL_CHANNEL_INFO: ++ return snd_pcm_channel_info_user(substream, arg); ++ case SNDRV_PCM_IOCTL_PREPARE: ++ return snd_pcm_prepare(substream, file); ++ case SNDRV_PCM_IOCTL_RESET: ++ return snd_pcm_reset(substream); ++ case SNDRV_PCM_IOCTL_START: ++ return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING); ++ case SNDRV_PCM_IOCTL_LINK: ++ return snd_pcm_link(substream, (int)(unsigned long) arg); ++ case SNDRV_PCM_IOCTL_UNLINK: ++ return snd_pcm_unlink(substream); ++ case SNDRV_PCM_IOCTL_RESUME: ++ return snd_pcm_resume(substream); ++ case SNDRV_PCM_IOCTL_XRUN: ++ return snd_pcm_xrun(substream); ++ case SNDRV_PCM_IOCTL_HWSYNC: ++ return snd_pcm_hwsync(substream); ++ case SNDRV_PCM_IOCTL_DELAY: ++ return snd_pcm_delay(substream, arg); ++ case SNDRV_PCM_IOCTL_SYNC_PTR: ++ return snd_pcm_sync_ptr(substream, arg); ++#ifdef CONFIG_SND_SUPPORT_OLD_API ++ case SNDRV_PCM_IOCTL_HW_REFINE_OLD: ++ return snd_pcm_hw_refine_old_user(substream, arg); ++ case SNDRV_PCM_IOCTL_HW_PARAMS_OLD: ++ return snd_pcm_hw_params_old_user(substream, arg); ++#endif ++ case SNDRV_PCM_IOCTL_DRAIN: ++ return snd_pcm_drain(substream); ++ case SNDRV_PCM_IOCTL_DROP: ++ return snd_pcm_drop(substream); ++ case SNDRV_PCM_IOCTL_PAUSE: ++ { ++ int res; ++ snd_pcm_stream_lock_irq(substream); ++ res = snd_pcm_pause(substream, (int)(unsigned long)arg); ++ snd_pcm_stream_unlock_irq(substream); ++ return res; ++ } ++ } ++ snd_printd("unknown ioctl = 0x%x\n", cmd); ++ return -ENOTTY; ++} ++ ++static int snd_pcm_playback_ioctl1(struct file *file, ++ struct snd_pcm_substream *substream, ++ unsigned int cmd, void __user *arg) ++{ ++ snd_assert(substream != NULL, return -ENXIO); ++ snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL); ++ switch (cmd) { ++ case SNDRV_PCM_IOCTL_WRITEI_FRAMES: ++ { ++ struct snd_xferi xferi; ++ struct snd_xferi __user *_xferi = arg; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ snd_pcm_sframes_t result; ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) ++ return -EBADFD; ++ if (put_user(0, &_xferi->result)) ++ return -EFAULT; ++ if (copy_from_user(&xferi, _xferi, sizeof(xferi))) ++ return -EFAULT; ++ result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames); ++ __put_user(result, &_xferi->result); ++ return result < 0 ? result : 0; ++ } ++ case SNDRV_PCM_IOCTL_WRITEN_FRAMES: ++ { ++ struct snd_xfern xfern; ++ struct snd_xfern __user *_xfern = arg; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ void __user **bufs; ++ snd_pcm_sframes_t result; ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) ++ return -EBADFD; ++ if (runtime->channels > 128) ++ return -EINVAL; ++ if (put_user(0, &_xfern->result)) ++ return -EFAULT; ++ if (copy_from_user(&xfern, _xfern, sizeof(xfern))) ++ return -EFAULT; ++ bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL); ++ if (bufs == NULL) ++ return -ENOMEM; ++ if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) { ++ kfree(bufs); ++ return -EFAULT; ++ } ++ result = snd_pcm_lib_writev(substream, bufs, xfern.frames); ++ kfree(bufs); ++ __put_user(result, &_xfern->result); ++ return result < 0 ? result : 0; ++ } ++ case SNDRV_PCM_IOCTL_REWIND: ++ { ++ snd_pcm_uframes_t frames; ++ snd_pcm_uframes_t __user *_frames = arg; ++ snd_pcm_sframes_t result; ++ if (get_user(frames, _frames)) ++ return -EFAULT; ++ if (put_user(0, _frames)) ++ return -EFAULT; ++ result = snd_pcm_playback_rewind(substream, frames); ++ __put_user(result, _frames); ++ return result < 0 ? result : 0; ++ } ++ case SNDRV_PCM_IOCTL_FORWARD: ++ { ++ snd_pcm_uframes_t frames; ++ snd_pcm_uframes_t __user *_frames = arg; ++ snd_pcm_sframes_t result; ++ if (get_user(frames, _frames)) ++ return -EFAULT; ++ if (put_user(0, _frames)) ++ return -EFAULT; ++ result = snd_pcm_playback_forward(substream, frames); ++ __put_user(result, _frames); ++ return result < 0 ? result : 0; ++ } ++ } ++ return snd_pcm_common_ioctl1(file, substream, cmd, arg); ++} ++ ++static int snd_pcm_capture_ioctl1(struct file *file, ++ struct snd_pcm_substream *substream, ++ unsigned int cmd, void __user *arg) ++{ ++ snd_assert(substream != NULL, return -ENXIO); ++ snd_assert(substream->stream == SNDRV_PCM_STREAM_CAPTURE, return -EINVAL); ++ switch (cmd) { ++ case SNDRV_PCM_IOCTL_READI_FRAMES: ++ { ++ struct snd_xferi xferi; ++ struct snd_xferi __user *_xferi = arg; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ snd_pcm_sframes_t result; ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) ++ return -EBADFD; ++ if (put_user(0, &_xferi->result)) ++ return -EFAULT; ++ if (copy_from_user(&xferi, _xferi, sizeof(xferi))) ++ return -EFAULT; ++ result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames); ++ __put_user(result, &_xferi->result); ++ return result < 0 ? result : 0; ++ } ++ case SNDRV_PCM_IOCTL_READN_FRAMES: ++ { ++ struct snd_xfern xfern; ++ struct snd_xfern __user *_xfern = arg; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ void *bufs; ++ snd_pcm_sframes_t result; ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) ++ return -EBADFD; ++ if (runtime->channels > 128) ++ return -EINVAL; ++ if (put_user(0, &_xfern->result)) ++ return -EFAULT; ++ if (copy_from_user(&xfern, _xfern, sizeof(xfern))) ++ return -EFAULT; ++ bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL); ++ if (bufs == NULL) ++ return -ENOMEM; ++ if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) { ++ kfree(bufs); ++ return -EFAULT; ++ } ++ result = snd_pcm_lib_readv(substream, bufs, xfern.frames); ++ kfree(bufs); ++ __put_user(result, &_xfern->result); ++ return result < 0 ? result : 0; ++ } ++ case SNDRV_PCM_IOCTL_REWIND: ++ { ++ snd_pcm_uframes_t frames; ++ snd_pcm_uframes_t __user *_frames = arg; ++ snd_pcm_sframes_t result; ++ if (get_user(frames, _frames)) ++ return -EFAULT; ++ if (put_user(0, _frames)) ++ return -EFAULT; ++ result = snd_pcm_capture_rewind(substream, frames); ++ __put_user(result, _frames); ++ return result < 0 ? result : 0; ++ } ++ case SNDRV_PCM_IOCTL_FORWARD: ++ { ++ snd_pcm_uframes_t frames; ++ snd_pcm_uframes_t __user *_frames = arg; ++ snd_pcm_sframes_t result; ++ if (get_user(frames, _frames)) ++ return -EFAULT; ++ if (put_user(0, _frames)) ++ return -EFAULT; ++ result = snd_pcm_capture_forward(substream, frames); ++ __put_user(result, _frames); ++ return result < 0 ? result : 0; ++ } ++ } ++ return snd_pcm_common_ioctl1(file, substream, cmd, arg); ++} ++ ++static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct snd_pcm_file *pcm_file; ++ ++ pcm_file = file->private_data; ++ ++ if (((cmd >> 8) & 0xff) != 'A') ++ return -ENOTTY; ++ ++ return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd, ++ (void __user *)arg); ++} ++ ++static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct snd_pcm_file *pcm_file; ++ ++ pcm_file = file->private_data; ++ ++ if (((cmd >> 8) & 0xff) != 'A') ++ return -ENOTTY; ++ ++ return snd_pcm_capture_ioctl1(file, pcm_file->substream, cmd, ++ (void __user *)arg); ++} ++ ++int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, ++ unsigned int cmd, void *arg) ++{ ++ mm_segment_t fs; ++ int result; ++ ++ fs = snd_enter_user(); ++ switch (substream->stream) { ++ case SNDRV_PCM_STREAM_PLAYBACK: ++ result = snd_pcm_playback_ioctl1(NULL, substream, cmd, ++ (void __user *)arg); ++ break; ++ case SNDRV_PCM_STREAM_CAPTURE: ++ result = snd_pcm_capture_ioctl1(NULL, substream, cmd, ++ (void __user *)arg); ++ break; ++ default: ++ result = -EINVAL; ++ break; ++ } ++ snd_leave_user(fs); ++ return result; ++} ++ ++EXPORT_SYMBOL(snd_pcm_kernel_ioctl); ++ ++static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count, ++ loff_t * offset) ++{ ++ struct snd_pcm_file *pcm_file; ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_runtime *runtime; ++ snd_pcm_sframes_t result; ++ ++ pcm_file = file->private_data; ++ substream = pcm_file->substream; ++ snd_assert(substream != NULL, return -ENXIO); ++ runtime = substream->runtime; ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) ++ return -EBADFD; ++ if (!frame_aligned(runtime, count)) ++ return -EINVAL; ++ count = bytes_to_frames(runtime, count); ++ result = snd_pcm_lib_read(substream, buf, count); ++ if (result > 0) ++ result = frames_to_bytes(runtime, result); ++ return result; ++} ++ ++static ssize_t snd_pcm_write(struct file *file, const char __user *buf, ++ size_t count, loff_t * offset) ++{ ++ struct snd_pcm_file *pcm_file; ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_runtime *runtime; ++ snd_pcm_sframes_t result; ++ ++ pcm_file = file->private_data; ++ substream = pcm_file->substream; ++ snd_assert(substream != NULL, result = -ENXIO; goto end); ++ runtime = substream->runtime; ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { ++ result = -EBADFD; ++ goto end; ++ } ++ if (!frame_aligned(runtime, count)) { ++ result = -EINVAL; ++ goto end; ++ } ++ count = bytes_to_frames(runtime, count); ++ result = snd_pcm_lib_write(substream, buf, count); ++ if (result > 0) ++ result = frames_to_bytes(runtime, result); ++ end: ++ return result; ++} ++ ++static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov, ++ unsigned long nr_segs, loff_t pos) ++ ++{ ++ struct snd_pcm_file *pcm_file; ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_runtime *runtime; ++ snd_pcm_sframes_t result; ++ unsigned long i; ++ void __user **bufs; ++ snd_pcm_uframes_t frames; ++ ++ pcm_file = iocb->ki_filp->private_data; ++ substream = pcm_file->substream; ++ snd_assert(substream != NULL, return -ENXIO); ++ runtime = substream->runtime; ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) ++ return -EBADFD; ++ if (nr_segs > 1024 || nr_segs != runtime->channels) ++ return -EINVAL; ++ if (!frame_aligned(runtime, iov->iov_len)) ++ return -EINVAL; ++ frames = bytes_to_samples(runtime, iov->iov_len); ++ bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL); ++ if (bufs == NULL) ++ return -ENOMEM; ++ for (i = 0; i < nr_segs; ++i) ++ bufs[i] = iov[i].iov_base; ++ result = snd_pcm_lib_readv(substream, bufs, frames); ++ if (result > 0) ++ result = frames_to_bytes(runtime, result); ++ kfree(bufs); ++ return result; ++} ++ ++static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov, ++ unsigned long nr_segs, loff_t pos) ++{ ++ struct snd_pcm_file *pcm_file; ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_runtime *runtime; ++ snd_pcm_sframes_t result; ++ unsigned long i; ++ void __user **bufs; ++ snd_pcm_uframes_t frames; ++ ++ pcm_file = iocb->ki_filp->private_data; ++ substream = pcm_file->substream; ++ snd_assert(substream != NULL, result = -ENXIO; goto end); ++ runtime = substream->runtime; ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { ++ result = -EBADFD; ++ goto end; ++ } ++ if (nr_segs > 128 || nr_segs != runtime->channels || ++ !frame_aligned(runtime, iov->iov_len)) { ++ result = -EINVAL; ++ goto end; ++ } ++ frames = bytes_to_samples(runtime, iov->iov_len); ++ bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL); ++ if (bufs == NULL) ++ return -ENOMEM; ++ for (i = 0; i < nr_segs; ++i) ++ bufs[i] = iov[i].iov_base; ++ result = snd_pcm_lib_writev(substream, bufs, frames); ++ if (result > 0) ++ result = frames_to_bytes(runtime, result); ++ kfree(bufs); ++ end: ++ return result; ++} ++ ++static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait) ++{ ++ struct snd_pcm_file *pcm_file; ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_runtime *runtime; ++ unsigned int mask; ++ snd_pcm_uframes_t avail; ++ ++ pcm_file = file->private_data; ++ ++ substream = pcm_file->substream; ++ snd_assert(substream != NULL, return -ENXIO); ++ runtime = substream->runtime; ++ ++ poll_wait(file, &runtime->sleep, wait); ++ ++ snd_pcm_stream_lock_irq(substream); ++ avail = snd_pcm_playback_avail(runtime); ++ switch (runtime->status->state) { ++ case SNDRV_PCM_STATE_RUNNING: ++ case SNDRV_PCM_STATE_PREPARED: ++ case SNDRV_PCM_STATE_PAUSED: ++ if (avail >= runtime->control->avail_min) { ++ mask = POLLOUT | POLLWRNORM; ++ break; ++ } ++ /* Fall through */ ++ case SNDRV_PCM_STATE_DRAINING: ++ mask = 0; ++ break; ++ default: ++ mask = POLLOUT | POLLWRNORM | POLLERR; ++ break; ++ } ++ snd_pcm_stream_unlock_irq(substream); ++ return mask; ++} ++ ++static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait) ++{ ++ struct snd_pcm_file *pcm_file; ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_runtime *runtime; ++ unsigned int mask; ++ snd_pcm_uframes_t avail; ++ ++ pcm_file = file->private_data; ++ ++ substream = pcm_file->substream; ++ snd_assert(substream != NULL, return -ENXIO); ++ runtime = substream->runtime; ++ ++ poll_wait(file, &runtime->sleep, wait); ++ ++ snd_pcm_stream_lock_irq(substream); ++ avail = snd_pcm_capture_avail(runtime); ++ switch (runtime->status->state) { ++ case SNDRV_PCM_STATE_RUNNING: ++ case SNDRV_PCM_STATE_PREPARED: ++ case SNDRV_PCM_STATE_PAUSED: ++ if (avail >= runtime->control->avail_min) { ++ mask = POLLIN | POLLRDNORM; ++ break; ++ } ++ mask = 0; ++ break; ++ case SNDRV_PCM_STATE_DRAINING: ++ if (avail > 0) { ++ mask = POLLIN | POLLRDNORM; ++ break; ++ } ++ /* Fall through */ ++ default: ++ mask = POLLIN | POLLRDNORM | POLLERR; ++ break; ++ } ++ snd_pcm_stream_unlock_irq(substream); ++ return mask; ++} ++ ++/* ++ * mmap support ++ */ ++ ++/* ++ * Only on coherent architectures, we can mmap the status and the control records ++ * for effcient data transfer. On others, we have to use HWSYNC ioctl... ++ */ ++#if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_ALPHA) ++/* ++ * mmap status record ++ */ ++static struct page * snd_pcm_mmap_status_nopage(struct vm_area_struct *area, ++ unsigned long address, int *type) ++{ ++ struct snd_pcm_substream *substream = area->vm_private_data; ++ struct snd_pcm_runtime *runtime; ++ struct page * page; ++ ++ if (substream == NULL) ++ return NOPAGE_SIGBUS; ++ runtime = substream->runtime; ++ page = virt_to_page(runtime->status); ++ get_page(page); ++ if (type) ++ *type = VM_FAULT_MINOR; ++ return page; ++} ++ ++static struct vm_operations_struct snd_pcm_vm_ops_status = ++{ ++ .nopage = snd_pcm_mmap_status_nopage, ++}; ++ ++static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file, ++ struct vm_area_struct *area) ++{ ++ struct snd_pcm_runtime *runtime; ++ long size; ++ if (!(area->vm_flags & VM_READ)) ++ return -EINVAL; ++ runtime = substream->runtime; ++ snd_assert(runtime != NULL, return -EAGAIN); ++ size = area->vm_end - area->vm_start; ++ if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))) ++ return -EINVAL; ++ area->vm_ops = &snd_pcm_vm_ops_status; ++ area->vm_private_data = substream; ++ area->vm_flags |= VM_RESERVED; ++ return 0; ++} ++ ++/* ++ * mmap control record ++ */ ++static struct page * snd_pcm_mmap_control_nopage(struct vm_area_struct *area, ++ unsigned long address, int *type) ++{ ++ struct snd_pcm_substream *substream = area->vm_private_data; ++ struct snd_pcm_runtime *runtime; ++ struct page * page; ++ ++ if (substream == NULL) ++ return NOPAGE_SIGBUS; ++ runtime = substream->runtime; ++ page = virt_to_page(runtime->control); ++ get_page(page); ++ if (type) ++ *type = VM_FAULT_MINOR; ++ return page; ++} ++ ++static struct vm_operations_struct snd_pcm_vm_ops_control = ++{ ++ .nopage = snd_pcm_mmap_control_nopage, ++}; ++ ++static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file, ++ struct vm_area_struct *area) ++{ ++ struct snd_pcm_runtime *runtime; ++ long size; ++ if (!(area->vm_flags & VM_READ)) ++ return -EINVAL; ++ runtime = substream->runtime; ++ snd_assert(runtime != NULL, return -EAGAIN); ++ size = area->vm_end - area->vm_start; ++ if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))) ++ return -EINVAL; ++ area->vm_ops = &snd_pcm_vm_ops_control; ++ area->vm_private_data = substream; ++ area->vm_flags |= VM_RESERVED; ++ return 0; ++} ++#else /* ! coherent mmap */ ++/* ++ * don't support mmap for status and control records. ++ */ ++static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file, ++ struct vm_area_struct *area) ++{ ++ return -ENXIO; ++} ++static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file, ++ struct vm_area_struct *area) ++{ ++ return -ENXIO; ++} ++#endif /* coherent mmap */ ++ ++/* ++ * nopage callback for mmapping a RAM page ++ */ ++static struct page *snd_pcm_mmap_data_nopage(struct vm_area_struct *area, ++ unsigned long address, int *type) ++{ ++ struct snd_pcm_substream *substream = area->vm_private_data; ++ struct snd_pcm_runtime *runtime; ++ unsigned long offset; ++ struct page * page; ++ void *vaddr; ++ size_t dma_bytes; ++ ++ if (substream == NULL) ++ return NOPAGE_SIGBUS; ++ runtime = substream->runtime; ++ offset = area->vm_pgoff << PAGE_SHIFT; ++ offset += address - area->vm_start; ++ snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_SIGBUS); ++ dma_bytes = PAGE_ALIGN(runtime->dma_bytes); ++ if (offset > dma_bytes - PAGE_SIZE) ++ return NOPAGE_SIGBUS; ++ if (substream->ops->page) { ++ page = substream->ops->page(substream, offset); ++ if (! page) ++ return NOPAGE_OOM; /* XXX: is this really due to OOM? */ ++ } else { ++ vaddr = runtime->dma_area + offset; ++ page = virt_to_page(vaddr); ++ } ++ get_page(page); ++ if (type) ++ *type = VM_FAULT_MINOR; ++ return page; ++} ++ ++static struct vm_operations_struct snd_pcm_vm_ops_data = ++{ ++ .open = snd_pcm_mmap_data_open, ++ .close = snd_pcm_mmap_data_close, ++ .nopage = snd_pcm_mmap_data_nopage, ++}; ++ ++/* ++ * mmap the DMA buffer on RAM ++ */ ++static int snd_pcm_default_mmap(struct snd_pcm_substream *substream, ++ struct vm_area_struct *area) ++{ ++ area->vm_ops = &snd_pcm_vm_ops_data; ++ area->vm_private_data = substream; ++ area->vm_flags |= VM_RESERVED; ++ atomic_inc(&substream->mmap_count); ++ return 0; ++} ++ ++/* ++ * mmap the DMA buffer on I/O memory area ++ */ ++#if SNDRV_PCM_INFO_MMAP_IOMEM ++static struct vm_operations_struct snd_pcm_vm_ops_data_mmio = ++{ ++ .open = snd_pcm_mmap_data_open, ++ .close = snd_pcm_mmap_data_close, ++}; ++ ++int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, ++ struct vm_area_struct *area) ++{ ++ long size; ++ unsigned long offset; ++ ++#ifdef pgprot_noncached ++ area->vm_page_prot = pgprot_noncached(area->vm_page_prot); ++#endif ++ area->vm_ops = &snd_pcm_vm_ops_data_mmio; ++ area->vm_private_data = substream; ++ area->vm_flags |= VM_IO; ++ size = area->vm_end - area->vm_start; ++ offset = area->vm_pgoff << PAGE_SHIFT; ++ if (io_remap_pfn_range(area, area->vm_start, ++ (substream->runtime->dma_addr + offset) >> PAGE_SHIFT, ++ size, area->vm_page_prot)) ++ return -EAGAIN; ++ atomic_inc(&substream->mmap_count); ++ return 0; ++} ++ ++EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); ++#endif /* SNDRV_PCM_INFO_MMAP */ ++ ++/* ++ * mmap DMA buffer ++ */ ++int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, ++ struct vm_area_struct *area) ++{ ++ struct snd_pcm_runtime *runtime; ++ long size; ++ unsigned long offset; ++ size_t dma_bytes; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ if (!(area->vm_flags & (VM_WRITE|VM_READ))) ++ return -EINVAL; ++ } else { ++ if (!(area->vm_flags & VM_READ)) ++ return -EINVAL; ++ } ++ runtime = substream->runtime; ++ snd_assert(runtime != NULL, return -EAGAIN); ++ if (runtime->status->state == SNDRV_PCM_STATE_OPEN) ++ return -EBADFD; ++ if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) ++ return -ENXIO; ++ if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || ++ runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) ++ return -EINVAL; ++ size = area->vm_end - area->vm_start; ++ offset = area->vm_pgoff << PAGE_SHIFT; ++ dma_bytes = PAGE_ALIGN(runtime->dma_bytes); ++ if ((size_t)size > dma_bytes) ++ return -EINVAL; ++ if (offset > dma_bytes - size) ++ return -EINVAL; ++ ++ if (substream->ops->mmap) ++ return substream->ops->mmap(substream, area); ++ else ++ return snd_pcm_default_mmap(substream, area); ++} ++ ++EXPORT_SYMBOL(snd_pcm_mmap_data); ++ ++static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) ++{ ++ struct snd_pcm_file * pcm_file; ++ struct snd_pcm_substream *substream; ++ unsigned long offset; ++ ++ pcm_file = file->private_data; ++ substream = pcm_file->substream; ++ snd_assert(substream != NULL, return -ENXIO); ++ ++ offset = area->vm_pgoff << PAGE_SHIFT; ++ switch (offset) { ++ case SNDRV_PCM_MMAP_OFFSET_STATUS: ++ if (pcm_file->no_compat_mmap) ++ return -ENXIO; ++ return snd_pcm_mmap_status(substream, file, area); ++ case SNDRV_PCM_MMAP_OFFSET_CONTROL: ++ if (pcm_file->no_compat_mmap) ++ return -ENXIO; ++ return snd_pcm_mmap_control(substream, file, area); ++ default: ++ return snd_pcm_mmap_data(substream, file, area); ++ } ++ return 0; ++} ++ ++static int snd_pcm_fasync(int fd, struct file * file, int on) ++{ ++ struct snd_pcm_file * pcm_file; ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_runtime *runtime; ++ int err; ++ ++ pcm_file = file->private_data; ++ substream = pcm_file->substream; ++ snd_assert(substream != NULL, return -ENXIO); ++ runtime = substream->runtime; ++ ++ err = fasync_helper(fd, file, on, &runtime->fasync); ++ if (err < 0) ++ return err; ++ return 0; ++} ++ ++/* ++ * ioctl32 compat ++ */ ++#ifdef CONFIG_COMPAT ++#include "pcm_compat.c" ++#else ++#define snd_pcm_ioctl_compat NULL ++#endif ++ ++/* ++ * To be removed helpers to keep binary compatibility ++ */ ++ ++#ifdef CONFIG_SND_SUPPORT_OLD_API ++#define __OLD_TO_NEW_MASK(x) ((x&7)|((x&0x07fffff8)<<5)) ++#define __NEW_TO_OLD_MASK(x) ((x&7)|((x&0xffffff00)>>5)) ++ ++static void snd_pcm_hw_convert_from_old_params(struct snd_pcm_hw_params *params, ++ struct snd_pcm_hw_params_old *oparams) ++{ ++ unsigned int i; ++ ++ memset(params, 0, sizeof(*params)); ++ params->flags = oparams->flags; ++ for (i = 0; i < ARRAY_SIZE(oparams->masks); i++) ++ params->masks[i].bits[0] = oparams->masks[i]; ++ memcpy(params->intervals, oparams->intervals, sizeof(oparams->intervals)); ++ params->rmask = __OLD_TO_NEW_MASK(oparams->rmask); ++ params->cmask = __OLD_TO_NEW_MASK(oparams->cmask); ++ params->info = oparams->info; ++ params->msbits = oparams->msbits; ++ params->rate_num = oparams->rate_num; ++ params->rate_den = oparams->rate_den; ++ params->fifo_size = oparams->fifo_size; ++} ++ ++static void snd_pcm_hw_convert_to_old_params(struct snd_pcm_hw_params_old *oparams, ++ struct snd_pcm_hw_params *params) ++{ ++ unsigned int i; ++ ++ memset(oparams, 0, sizeof(*oparams)); ++ oparams->flags = params->flags; ++ for (i = 0; i < ARRAY_SIZE(oparams->masks); i++) ++ oparams->masks[i] = params->masks[i].bits[0]; ++ memcpy(oparams->intervals, params->intervals, sizeof(oparams->intervals)); ++ oparams->rmask = __NEW_TO_OLD_MASK(params->rmask); ++ oparams->cmask = __NEW_TO_OLD_MASK(params->cmask); ++ oparams->info = params->info; ++ oparams->msbits = params->msbits; ++ oparams->rate_num = params->rate_num; ++ oparams->rate_den = params->rate_den; ++ oparams->fifo_size = params->fifo_size; ++} ++ ++static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params_old __user * _oparams) ++{ ++ struct snd_pcm_hw_params *params; ++ struct snd_pcm_hw_params_old *oparams = NULL; ++ int err; ++ ++ params = kmalloc(sizeof(*params), GFP_KERNEL); ++ if (!params) { ++ err = -ENOMEM; ++ goto out; ++ } ++ oparams = kmalloc(sizeof(*oparams), GFP_KERNEL); ++ if (!oparams) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ if (copy_from_user(oparams, _oparams, sizeof(*oparams))) { ++ err = -EFAULT; ++ goto out; ++ } ++ snd_pcm_hw_convert_from_old_params(params, oparams); ++ err = snd_pcm_hw_refine(substream, params); ++ snd_pcm_hw_convert_to_old_params(oparams, params); ++ if (copy_to_user(_oparams, oparams, sizeof(*oparams))) { ++ if (!err) ++ err = -EFAULT; ++ } ++out: ++ kfree(params); ++ kfree(oparams); ++ return err; ++} ++ ++static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params_old __user * _oparams) ++{ ++ struct snd_pcm_hw_params *params; ++ struct snd_pcm_hw_params_old *oparams = NULL; ++ int err; ++ ++ params = kmalloc(sizeof(*params), GFP_KERNEL); ++ if (!params) { ++ err = -ENOMEM; ++ goto out; ++ } ++ oparams = kmalloc(sizeof(*oparams), GFP_KERNEL); ++ if (!oparams) { ++ err = -ENOMEM; ++ goto out; ++ } ++ if (copy_from_user(oparams, _oparams, sizeof(*oparams))) { ++ err = -EFAULT; ++ goto out; ++ } ++ snd_pcm_hw_convert_from_old_params(params, oparams); ++ err = snd_pcm_hw_params(substream, params); ++ snd_pcm_hw_convert_to_old_params(oparams, params); ++ if (copy_to_user(_oparams, oparams, sizeof(*oparams))) { ++ if (!err) ++ err = -EFAULT; ++ } ++out: ++ kfree(params); ++ kfree(oparams); ++ return err; ++} ++#endif /* CONFIG_SND_SUPPORT_OLD_API */ ++ ++/* ++ * Register section ++ */ ++ ++const struct file_operations snd_pcm_f_ops[2] = { ++ { ++ .owner = THIS_MODULE, ++ .write = snd_pcm_write, ++ .aio_write = snd_pcm_aio_write, ++ .open = snd_pcm_playback_open, ++ .release = snd_pcm_release, ++ .poll = snd_pcm_playback_poll, ++ .unlocked_ioctl = snd_pcm_playback_ioctl, ++ .compat_ioctl = snd_pcm_ioctl_compat, ++ .mmap = snd_pcm_mmap, ++ .fasync = snd_pcm_fasync, ++ }, ++ { ++ .owner = THIS_MODULE, ++ .read = snd_pcm_read, ++ .aio_read = snd_pcm_aio_read, ++ .open = snd_pcm_capture_open, ++ .release = snd_pcm_release, ++ .poll = snd_pcm_capture_poll, ++ .unlocked_ioctl = snd_pcm_capture_ioctl, ++ .compat_ioctl = snd_pcm_ioctl_compat, ++ .mmap = snd_pcm_mmap, ++ .fasync = snd_pcm_fasync, ++ } ++}; +diff -urN linux-2.6.24.7.old/sound/oss/Kconfig linux-2.6.24.7/sound/oss/Kconfig +--- linux-2.6.24.7.old/sound/oss/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/sound/oss/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -5,6 +5,91 @@ + # + # Prompt user for primary drivers. + ++config OSS_OBSOLETE ++ bool "Obsolete OSS drivers" ++ depends on SOUND_PRIME ++ help ++ This option enables support for obsolete OSS drivers that ++ are scheduled for removal in the near future. ++ ++ Please contact Adrian Bunk if you had to ++ say Y here because your hardware is not properly supported ++ by ALSA. ++ ++ If unsure, say N. ++ ++config SOUND_JZ_AC97 ++ bool "Jz On-Chip AC97 driver" ++ depends on OSS_OBSOLETE ++ help ++ Say Y here if you have want to select the on-chip AC97 driver ++ on Jz4730/Jz4740/Jz5730. ++ ++config SOUND_JZ_I2S ++ bool "Jz On-Chip I2S driver" ++ depends on OSS_OBSOLETE ++ help ++ Say Y here if you have want to select the on-chip I2S driver ++ on Jz4730/Jz4740/Jz5730. ++ ++config SOUND_JZ_PCM ++ bool "Jz On-Chip PCM driver" ++ depends on SOC_JZ4750 ++ help ++ Say Y here if you have want to select the on-chip PCM driver ++ on Jz4750. ++ ++choice ++ prompt "I2S codec type" ++ depends on SOUND_JZ_I2S ++ ++config I2S_AK4642EN ++ bool "AK4642EN" ++ depends on SOC_JZ4730 ++ help ++ Answer Y if you have an external AK4642EN codec. ++ ++config I2S_ICODEC ++ bool "Internal On-Chip codec" ++ depends on SOC_JZ4740 ++ help ++ Answer Y if you have an internal I2S codec. ++ ++config I2S_DLV ++ bool "Internal On-Chip codec on Jz4750 or Jz4750d" ++ depends on SOC_JZ4750 || SOC_JZ4750D ++ help ++ Answer Y if you have an internal I2S codec on Jz4750 or Jz4750d. ++ ++endchoice ++ ++choice ++ prompt "PCM codec type" ++ depends on SOUND_JZ_PCM ++ ++config PCM_TLV320AIC1106 ++ bool "TLV320AIC1106" ++ help ++ Answer Y if you have an TI tlv320aic 1106 codec. ++ ++endchoice ++ ++config SOUND_BT878 ++ tristate "BT878 audio dma" ++ depends on SOUND_PRIME && PCI && OSS_OBSOLETE ++ ---help--- ++ Audio DMA support for bt878 based grabber boards. As you might have ++ already noticed, bt878 is listed with two functions in /proc/pci. ++ Function 0 does the video stuff (bt848 compatible), function 1 does ++ the same for audio data. This is a driver for the audio part of ++ the chip. If you say 'Y' here you get a oss-compatible dsp device ++ where you can record from. If you want just watch TV you probably ++ don't need this driver as most TV cards handle sound with a short ++ cable from the TV card to your sound card's line-in. ++ ++ To compile this driver as a module, choose M here: the module will ++ be called btaudio. ++ + config SOUND_BCM_CS4297A + tristate "Crystal Sound CS4297a (for Swarm)" + depends on SOUND_PRIME && SIBYTE_SWARM +@@ -15,6 +100,13 @@ + note that CONFIG_KGDB should not be enabled at the same + time, since it also attempts to use this UART port. + ++config SOUND_ICH ++ tristate "Intel ICH (i8xx) audio support" ++ depends on SOUND_PRIME && PCI && OSS_OBSOLETE ++ help ++ Support for integral audio in Intel's I/O Controller Hub (ICH) ++ chipset, as used on the 810/820/840 motherboards. ++ + config SOUND_VWSND + tristate "SGI Visual Workstation Sound" + depends on SOUND_PRIME && X86_VISWS +@@ -31,6 +123,14 @@ + Say Y or M if you have an SGI Indy or Indigo2 system and want to be able to + use its on-board A2 audio system. + ++config SOUND_VRC5477 ++ tristate "NEC Vrc5477 AC97 sound" ++ depends on SOUND_PRIME && DDB5477 ++ help ++ Say Y here to enable sound support for the NEC Vrc5477 chip, an ++ integrated, multi-function controller chip for MIPS CPUs. Works ++ with the AC97 codec. ++ + config SOUND_AU1550_AC97 + tristate "Au1550/Au1200 AC97 Sound" + select SND_AC97_CODEC +@@ -75,7 +175,7 @@ + + + This driver differs slightly from OSS/Free, so PLEASE READ the +- comments at the top of . ++ comments at the top of . + + config SOUND_MSNDCLAS + tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey" +@@ -302,9 +402,29 @@ + and Pinnacle). Larger values reduce the chance of data overruns at + the expense of overall latency. If unsure, use the default. + ++config SOUND_VIA82CXXX ++ tristate "VIA 82C686 Audio Codec" ++ depends on SOUND_PRIME && PCI && OSS_OBSOLETE ++ help ++ Say Y here to include support for the audio codec found on VIA ++ 82Cxxx-based chips. Typically these are built into a motherboard. ++ ++ DO NOT select Sound Blaster or Adlib with this driver, unless ++ you have a Sound Blaster or Adlib card in addition to your VIA ++ audio chip. ++ ++config MIDI_VIA82CXXX ++ bool "VIA 82C686 MIDI" ++ depends on SOUND_VIA82CXXX && ISA_DMA_API ++ help ++ Answer Y to use the MIDI interface of the Via686. You may need to ++ enable this in the BIOS before it will work. This is for connection ++ to external MIDI hardware, and is not required for software playback ++ of MIDI files. ++ + config SOUND_OSS + tristate "OSS sound modules" +- depends on SOUND_PRIME && ISA_DMA_API && VIRT_TO_BUS ++ depends on SOUND_PRIME && ISA_DMA_API + help + OSS is the Open Sound System suite of sound card drivers. They make + sound programming easier since they provide a common API. Say Y or +@@ -336,10 +456,23 @@ + + Say Y unless you have 16MB or more RAM or a PCI sound card. + ++config SOUND_CS4232 ++ tristate "Crystal CS4232 based (PnP) cards" ++ depends on SOUND_OSS && OSS_OBSOLETE ++ help ++ Say Y here if you have a card based on the Crystal CS4232 chip set, ++ which uses its own Plug and Play protocol. ++ ++ If you compile the driver into the kernel, you have to add ++ "cs4232=,,,,," to the kernel ++ command line. ++ ++ See for more information on ++ configuring this card. ++ + config SOUND_SSCAPE + tristate "Ensoniq SoundScape support" + depends on SOUND_OSS +- depends on VIRT_TO_BUS + help + Answer Y if you have a sound card based on the Ensoniq SoundScape + chipset. Such cards are being manufactured at least by Ensoniq, Spea +@@ -564,7 +697,7 @@ + questions. + + Read the file and the head of +- as well as ++ as well as + to get more information + about this driver and its configuration. + +@@ -642,6 +775,13 @@ + Say Y here to include support for the Rockwell WaveArtist sound + system. This driver is mainly for the NetWinder. + ++config SOUND_TVMIXER ++ tristate "TV card (bt848) mixer support" ++ depends on SOUND_PRIME && I2C && VIDEO_V4L1 && OSS_OBSOLETE ++ help ++ Support for audio mixer facilities on the BT848 TV frame-grabber ++ card. ++ + config SOUND_KAHLUA + tristate "XpressAudio Sound Blaster emulation" + depends on SOUND_SB +diff -urN linux-2.6.24.7.old/sound/oss/Makefile linux-2.6.24.7/sound/oss/Makefile +--- linux-2.6.24.7.old/sound/oss/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/sound/oss/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -10,6 +10,12 @@ + + # Please leave it as is, cause the link order is significant ! + ++obj-$(CONFIG_SOUND_JZ_AC97) += jz_ac97.o ac97_codec.o ++obj-$(CONFIG_I2S_AK4642EN) += ak4642en.o ++obj-$(CONFIG_I2S_ICODEC) += jzcodec.o jz_i2s.o ++obj-$(CONFIG_I2S_DLV) += jzdlv.o jz_i2s.o ++obj-$(CONFIG_SOUND_JZ_PCM) += jz_pcm_tlv320aic1106_dma.o ++ + obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o + obj-$(CONFIG_SOUND_HAL2) += hal2.o + obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o +diff -urN linux-2.6.24.7.old/sound/oss/ak4642en.c linux-2.6.24.7/sound/oss/ak4642en.c +--- linux-2.6.24.7.old/sound/oss/ak4642en.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/oss/ak4642en.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,712 @@ ++/* ++ * linux/sound/oss/ak4642en.c ++ * ++ * AKM ak4642en codec chip driver to I2S interface ++ * ++ * Copyright (c) 2005-2007 Ingenic Semiconductor Inc. ++ * Author: ++ * ++ * 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. ++ * ++ * Because the normal application of AUDIO devices are focused on Little_endian, ++ * then we only perform the little endian data format in driver. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sound_config.h" ++ ++extern mixer_info info; ++extern _old_mixer_info old_info; ++extern int abnormal_data_count; ++ ++extern void (*clear_codec_mode)(void); ++extern void (*set_codec_gpio_pin)(void); ++extern void (*each_time_init_codec)(void); ++extern void (*set_codec_record)(void); ++extern void (*set_codec_replay)(void); ++extern void (*clear_codec_record)(void); ++extern void (*clear_codec_replay)(void); ++extern void (*set_codec_speed)(int range); ++extern void (*codec_mixer_old_info_id_name)(void); ++extern void (*codec_mixer_info_id_name)(void); ++extern void (*set_codec_volume)(int val); ++extern void (*set_codec_mic)(int val); ++extern void (*i2s_resume_codec)(void); ++extern void (*i2s_suspend_codec)(int wr,int rd); ++extern void (*set_replay_hp_or_speaker)(void); ++ ++#define I2S_PDN 68 ++#define JACK_PLUG_PIN 83 ++#define JACK_PLUG_IRQ (IRQ_GPIO_0 + JACK_PLUG_PIN) ++ ++static int jack_plug_level, old_level; ++static unsigned int i2c_addr = 0x26; //AK4642EN device address at I2C bus ++static unsigned int i2c_clk = 100000;//AK4642EN 400kHz max,but 100kHz here ++static unsigned int spk_hp = 0; ++static int codec_volume; ++ ++void set_ak4642en_gpio_pin(void); ++void each_time_init_ak4642en(void); ++void set_ak4642en_replay(void); ++void set_ak4642en_record(void); ++void turn_on_ak4642en(void); ++void turn_off_ak4642en(void); ++void set_ak4642en_speed(int rate); ++void reset_ak4642en(void); ++void ak4642en_mixer_old_info_id_name(void); ++void ak4642en_mixer_info_id_name(void); ++void set_ak4642en_bass(int val); ++void set_ak4642en_volume(int val); ++void set_ak4642en_mic(int val); ++void resume_ak4642en(void); ++void suspend_ak4642en(int wr,int rd); ++ ++static void write_reg(u8 reg, u8 val) ++{ ++ i2c_open(); ++ i2c_setclk(i2c_clk); ++ i2c_write((i2c_addr >> 1), &val, reg, 1); ++ i2c_close(); ++} ++ ++#if 0 ++static u8 read_reg(u8 reg) ++{ ++ u8 val; ++ i2c_open(); ++ i2c_setclk(i2c_clk); ++ i2c_read((i2c_addr >> 1), &val, reg, 1); ++ i2c_close(); ++ return val; ++} ++ ++static u16 i2s_codec_read(u8 reg) ++{ ++ u16 value; ++ value = read_reg(reg); ++ return value; ++} ++#endif ++ ++static void i2s_codec_write(u8 reg, u16 data) ++{ ++ u8 val = data & 0xff; ++ write_reg(reg, val); ++} ++ ++void set_ak4642en_gpio_pin(void) ++{ ++ //set AIC pin to I2S slave mode,only GPIO70,71,77,78 ++ __gpio_as_output(68); ++ __gpio_clear_pin(68); ++ __gpio_as_output(69); ++ __gpio_clear_pin(69); ++ __gpio_as_output(70); ++ __gpio_clear_pin(70); ++ __gpio_as_input(71); ++ __gpio_clear_pin(71); ++ __gpio_as_input(77); ++ __gpio_clear_pin(77); ++ __gpio_as_input(78); ++ __gpio_clear_pin(78); ++ REG_GPIO_GPALR(2) &= 0xC3FF0CFF; ++ REG_GPIO_GPALR(2) |= 0x14005000; ++ //set SCC clock initialization ++ REG_SCC1_CR(SCC1_BASE) = 0x00000000; ++ udelay(2); ++ REG_SCC1_CR(SCC1_BASE) |= 1 << 31; ++ udelay(2); ++ ++ __gpio_as_output(I2S_PDN); ++ __gpio_set_pin(I2S_PDN); ++ udelay(5); ++ __gpio_clear_pin(I2S_PDN); ++ ndelay(300);//>150ns ++ __gpio_set_pin(I2S_PDN); ++ mdelay(1); ++ //set PLL Master mode ++ i2s_codec_write(0x01, 0x0008);//master ++ i2s_codec_write(0x04, 0x006b);//ref:12MHz;BITCLK:64fs;I2S compli ++ i2s_codec_write(0x05, 0x000b);//sync:48KHz; ++ i2s_codec_write(0x00, 0x0040);//PMVCM ++ i2s_codec_write(0x01, 0x0009);//master,PLL enable ++ mdelay(40); ++ jack_plug_level = 10; ++ old_level = 100; ++ spk_hp = 0; ++ __gpio_disable_pull(JACK_PLUG_PIN); ++ udelay(10); ++ __gpio_as_input(JACK_PLUG_PIN); ++ jack_plug_level = __gpio_get_pin(JACK_PLUG_PIN); ++ //i suppose jack_plug_lvel is 1 indicate with HPO ++ if (jack_plug_level > 1 || jack_plug_level <0) ++ printk("Audio ak4642en codec Jack plug level is wrong!\n"); ++ if (jack_plug_level) ++ __gpio_as_irq_fall_edge(JACK_PLUG_PIN); ++ else ++ __gpio_as_irq_rise_edge(JACK_PLUG_PIN); ++} ++ ++void clear_ak4642en_mode(void) ++{ ++ spk_hp = 0; ++ i2s_codec_write(0x01, 0x0008);//master,PLL disable ++ //free_irq(JACK_PLUG_IRQ, i2s_controller); ++ __gpio_clear_pin(I2S_PDN); ++ udelay(2); ++ REG_SCC1_CR(SCC1_BASE) &= 0 << 31; ++ udelay(2); ++} ++ ++void set_ak4642en_replay(void) ++{ ++ //for poll ++ /*jack_plug_level is H for SPK,is L for HP*/ ++ jack_plug_level = __gpio_get_pin(JACK_PLUG_PIN); ++ if(old_level == jack_plug_level) ++ return; ++ old_level = jack_plug_level; ++ if(spk_hp == 1) ++ { ++ if(jack_plug_level == 1) ++ { ++ //now HeadPhone output,so clear SPK ++ i2s_codec_write(0x02, 0x0020); ++ i2s_codec_write(0x02, 0x0000); ++ i2s_codec_write(0x00, 0x0040); ++ } ++ else ++ { ++ //now Speaker output,so clear HP ++ i2s_codec_write(0x01, 0x0039); ++ i2s_codec_write(0x01, 0x0009); ++ i2s_codec_write(0x00, 0x0040); ++ i2s_codec_write(0x0e, 0x0000); ++ i2s_codec_write(0x0f, 0x0008); ++ } ++ } ++ spk_hp = 1; ++ if(jack_plug_level == 1) ++ { ++ //for HeadPhone output ++ i2s_codec_write(0x00, 0x0060); // ++ i2s_codec_write(0x0f, 0x0009); //5-10 ++ ++ i2s_codec_write(0x00, 0x0064); // ++ i2s_codec_write(0x09, 0x0091);// volume control 0dB ++ i2s_codec_write(0x0c, 0x0091);// 0dB(right) ++ //eq off ++ i2s_codec_write(0x11, 0x0000);//5-10 ++ i2s_codec_write(0x01, 0x0039); // ++ ++ i2s_codec_write(0x01, 0x0079); // ++ } ++ else ++ { ++ //for Speaker output ++ i2s_codec_write(0x00, 0x0040); ++ i2s_codec_write(0x02, 0x0020); ++ ++ i2s_codec_write(0x03, 0x0018);//5-10 ++ i2s_codec_write(0x06, 0x003c); ++ ++ i2s_codec_write(0x08, 0x00A1);//5-10 ++ ++ i2s_codec_write(0x0b, 0x0040); //5-10 ++ ++ i2s_codec_write(0x07, 0x002d); //5-10 ++ i2s_codec_write(0x09, 0x0091); ++ i2s_codec_write(0x0c, 0x0091); ++ //HP volume output value ++ ++ i2s_codec_write(0x0a, codec_volume);//5-10 ++ i2s_codec_write(0x0d, codec_volume);//5-10 ++ ++ i2s_codec_write(0x00, 0x0074); ++ i2s_codec_write(0x02, 0x00a0); ++ } ++} ++ ++void set_ak4642en_record(void) ++{ ++ abnormal_data_count = 0; ++ i2s_codec_write(0x02, 0x0004); ++ i2s_codec_write(0x03, 0x0038);// recording volume add ++ i2s_codec_write(0x06, 0x0000);//for ALC short waiting time ++ i2s_codec_write(0x08, 0x00e1); ++ i2s_codec_write(0x0b, 0x0000); ++ i2s_codec_write(0x07, 0x0021); // ALC on ++ ++ i2s_codec_write(0x10, 0x0000);//0x0001 ++ //i2s_codec_write(0x10, 0x0001);//0x0001 ++ i2s_codec_write(0x01, 0x0039); //for open pop noise ++ i2s_codec_write(0x01, 0x0079); ++ i2s_codec_write(0x00, 0x0065); ++ mdelay(300); ++} ++ ++void clear_ak4642en_replay(void) ++{ ++ //for poll ++ old_level = 100; ++ spk_hp = 0; ++ if(jack_plug_level == 1) ++ { ++ //for HeadPhone output ++ i2s_codec_write(0x01, 0x0039); // for close pop noise ++ mdelay(300); ++ i2s_codec_write(0x01, 0x0009); //PLL on I2S ++ i2s_codec_write(0x07, 0x0001); ++ i2s_codec_write(0x11, 0x0000); ++ i2s_codec_write(0x00, 0x0040); ++ i2s_codec_write(0x0f, 0x0008); // for open pop noise ++ } ++ else ++ { ++ //for Speaker output ++ i2s_codec_write(0x02, 0x0020); ++ i2s_codec_write(0x07, 0x0001); ++ i2s_codec_write(0x11, 0x0000); ++ i2s_codec_write(0x02, 0x0000); ++ i2s_codec_write(0x00, 0x0040); // for close pop noise ++ } ++} ++ ++void clear_ak4642en_record(void) ++{ ++ //for Mic input(Stereo) ++ i2s_codec_write(0x02, 0x0001); ++ i2s_codec_write(0x07, 0x0001); ++ i2s_codec_write(0x11, 0x0000); ++} ++ ++void each_time_init_ak4642en(void) ++{ ++ __i2s_disable(); ++ __i2s_as_slave(); ++ __i2s_set_sample_size(16); ++} ++ ++void set_ak4642en_speed(int rate) ++{ ++ //codec work at frequency ++ unsigned short speed = 0; ++ unsigned short val = 0; ++ switch (rate) ++ { ++ case 8000: ++ speed = 0x00; ++ if(jack_plug_level == 1) //speaker ++ { ++ i2s_codec_write(0x16, 0x0000); ++ i2s_codec_write(0x17, 0x0000); ++ i2s_codec_write(0x18, 0x0000); ++ i2s_codec_write(0x19, 0x0000); ++ i2s_codec_write(0x1A, 0x0000); ++ i2s_codec_write(0x1B, 0x0000); ++ i2s_codec_write(0x1C, 0x0027);//800hz ++ i2s_codec_write(0x1D, 0x0018); ++ i2s_codec_write(0x1E, 0x00b2); ++ i2s_codec_write(0x1F, 0x002f); ++ i2s_codec_write(0x11, 0x0010); //eq on ++ } ++ break; ++ case 12000: ++ speed = 0x01; ++ if(jack_plug_level == 1) ++ { ++ i2s_codec_write(0x16, 0x0000); ++ i2s_codec_write(0x17, 0x0000); ++ i2s_codec_write(0x18, 0x0000); ++ i2s_codec_write(0x19, 0x0000); ++ i2s_codec_write(0x1A, 0x0000); ++ i2s_codec_write(0x1B, 0x0000); ++ i2s_codec_write(0x1C, 0x0064); ++ i2s_codec_write(0x1D, 0x001a); ++ i2s_codec_write(0x1E, 0x0038); ++ i2s_codec_write(0x1F, 0x002b); ++ i2s_codec_write(0x11, 0x0010); //eq on ++ } ++ break; ++ case 16000: ++ speed = 0x02; ++ if(jack_plug_level == 1) ++ { ++ i2s_codec_write(0x16, 0x00af); ++ i2s_codec_write(0x17, 0x0020); ++ i2s_codec_write(0x18, 0x0043); ++ i2s_codec_write(0x19, 0x001a); ++ i2s_codec_write(0x1A, 0x00af); ++ i2s_codec_write(0x1B, 0x0020); ++ i2s_codec_write(0x1C, 0x00a0); ++ i2s_codec_write(0x1D, 0x001b); ++ i2s_codec_write(0x1E, 0x00c0); ++ i2s_codec_write(0x1F, 0x0028); ++ i2s_codec_write(0x11, 0x0018); //eq on ++ } ++ break; ++ case 24000: ++ speed = 0x03; ++ if(jack_plug_level == 1) ++ { ++ i2s_codec_write(0x16, 0x0086); ++ i2s_codec_write(0x17, 0x0015); ++ i2s_codec_write(0x18, 0x005d); ++ i2s_codec_write(0x19, 0x0006); ++ i2s_codec_write(0x1A, 0x0086); ++ i2s_codec_write(0x1B, 0x0015); ++ i2s_codec_write(0x1C, 0x00f5); ++ i2s_codec_write(0x1D, 0x001c); ++ i2s_codec_write(0x1E, 0x0016); ++ i2s_codec_write(0x1F, 0x0026); ++ i2s_codec_write(0x11, 0x0018); //eq on ++ } ++ break; ++ case 7350: ++ speed = 0x04; ++ if(jack_plug_level == 1) ++ { ++ i2s_codec_write(0x16, 0x0000); ++ i2s_codec_write(0x17, 0x0000); ++ i2s_codec_write(0x18, 0x0000); ++ i2s_codec_write(0x19, 0x0000); ++ i2s_codec_write(0x1A, 0x0000); ++ i2s_codec_write(0x1B, 0x0000); ++ i2s_codec_write(0x1C, 0x0027); ++ i2s_codec_write(0x1D, 0x0018); ++ i2s_codec_write(0x1E, 0x00b2); ++ i2s_codec_write(0x1F, 0x002f); ++ i2s_codec_write(0x11, 0x0010); //eq on ++ } ++ break; ++ case 11025: ++ speed = 0x05; ++ if(jack_plug_level == 1) ++ { ++ i2s_codec_write(0x16, 0x0059); ++ i2s_codec_write(0x17, 0x000d); ++ i2s_codec_write(0x18, 0x00cb); ++ i2s_codec_write(0x19, 0x0037); ++ i2s_codec_write(0x1A, 0x0059); ++ i2s_codec_write(0x1B, 0x000d); ++ i2s_codec_write(0x1C, 0x0046); ++ i2s_codec_write(0x1D, 0x001e); ++ i2s_codec_write(0x1E, 0x0074); ++ i2s_codec_write(0x1F, 0x0023); ++ i2s_codec_write(0x11, 0x0018); //eq on ++ } ++ break; ++ case 14700: ++ speed = 0x06; ++ if(jack_plug_level == 1) ++ { ++ i2s_codec_write(0x16, 0x0000); ++ i2s_codec_write(0x17, 0x0000); ++ i2s_codec_write(0x18, 0x0000); ++ i2s_codec_write(0x19, 0x0000); ++ i2s_codec_write(0x1A, 0x0000); ++ i2s_codec_write(0x1B, 0x0000); ++ i2s_codec_write(0x1C, 0x004a); ++ i2s_codec_write(0x1D, 0x001b); ++ i2s_codec_write(0x1E, 0x006c); ++ i2s_codec_write(0x1F, 0x0029); ++ i2s_codec_write(0x11, 0x0010); //eq on ++ } ++ break; ++ case 22050: ++ speed = 0x07; ++ if(jack_plug_level == 1) ++ { ++ i2s_codec_write(0x16, 0x002d); ++ i2s_codec_write(0x17, 0x0017); ++ i2s_codec_write(0x18, 0x0050); ++ i2s_codec_write(0x19, 0x0009); ++ i2s_codec_write(0x1A, 0x002d); ++ i2s_codec_write(0x1B, 0x0017); ++ i2s_codec_write(0x1C, 0x00d7); ++ i2s_codec_write(0x1D, 0x001c); ++ i2s_codec_write(0x1E, 0x0093); ++ i2s_codec_write(0x1F, 0x0026); ++ i2s_codec_write(0x11, 0x0018); //eq on ++ } ++ break; ++ case 32000: ++ speed = 0x0a; ++ if(jack_plug_level == 1) ++ { ++ i2s_codec_write(0x16, 0x0012); ++ i2s_codec_write(0x17, 0x0011); ++ i2s_codec_write(0x18, 0x006e); ++ i2s_codec_write(0x19, 0x003e); ++ i2s_codec_write(0x1A, 0x0012); ++ i2s_codec_write(0x1B, 0x0011); ++ i2s_codec_write(0x1C, 0x00aa); ++ i2s_codec_write(0x1D, 0x001d); ++ i2s_codec_write(0x1E, 0x00ab); ++ i2s_codec_write(0x1F, 0x0024); ++ i2s_codec_write(0x11, 0x0018); //eq on ++ } ++ break; ++ case 48000: ++ speed = 0x0b; ++ if(jack_plug_level == 1) ++ { ++ i2s_codec_write(0x16, 0x0082); ++ i2s_codec_write(0x17, 0x000c); ++ i2s_codec_write(0x18, 0x004b); ++ i2s_codec_write(0x19, 0x0036); ++ i2s_codec_write(0x1A, 0x0082); ++ i2s_codec_write(0x1B, 0x000c); ++ i2s_codec_write(0x1C, 0x0068); ++ i2s_codec_write(0x1D, 0x001e); ++ i2s_codec_write(0x1E, 0x0030); ++ i2s_codec_write(0x1F, 0x0023); ++ i2s_codec_write(0x11, 0x0018); //eq on ++ } ++ break; ++ case 29400: ++ speed = 0x0e; ++ if(jack_plug_level == 1) ++ { ++ i2s_codec_write(0x16, 0x003d); ++ i2s_codec_write(0x17, 0x0012); ++ i2s_codec_write(0x18, 0x0083); ++ i2s_codec_write(0x19, 0x0000); ++ i2s_codec_write(0x1A, 0x003d); ++ i2s_codec_write(0x1B, 0x0012); ++ i2s_codec_write(0x1C, 0x0079); ++ i2s_codec_write(0x1D, 0x001d); ++ i2s_codec_write(0x1E, 0x000d); ++ i2s_codec_write(0x1F, 0x0025); ++ i2s_codec_write(0x11, 0x0018); //eq on ++ } ++ break; ++ case 44100: ++ speed = 0x0f; ++ if(jack_plug_level == 1) ++ { ++ i2s_codec_write(0x16, 0x0059); ++ i2s_codec_write(0x17, 0x000d); ++ i2s_codec_write(0x18, 0x00cb); ++ i2s_codec_write(0x19, 0x0037); ++ i2s_codec_write(0x1A, 0x0059); ++ i2s_codec_write(0x1B, 0x000d); ++ i2s_codec_write(0x1C, 0x0046); ++ i2s_codec_write(0x1D, 0x001e); ++ i2s_codec_write(0x1E, 0x0074); ++ i2s_codec_write(0x1F, 0x0023); ++ i2s_codec_write(0x11, 0x0018); //eq on ++ } ++ break; ++ default: ++ break; ++ } ++ val = speed & 0x08; ++ val = val << 2; ++ speed = speed & 0x07; ++ val = val | speed; ++ i2s_codec_write(0x05, val); ++} ++ ++void ak4642en_mixer_old_info_id_name(void) ++{ ++ strncpy(info.id, "AK4642EN", sizeof(info.id)); ++ strncpy(info.name,"AKM AK4642en codec", sizeof(info.name)); ++} ++ ++void ak4642en_mixer_info_id_name(void) ++{ ++ strncpy(old_info.id, "AK4642EN", sizeof(old_info.id)); ++ strncpy(old_info.name,"AKM AK4642en codec", sizeof(old_info.name)); ++} ++ ++void set_ak4642en_volume(int val) ++{ ++ if ( val == 0 ) ++ codec_volume = 255; ++ else if ( val > 1 && val <= 10) ++ codec_volume = 92; ++ else if ( val > 10 && val <= 20 ) ++ codec_volume = 67; ++ else if ( val > 20 && val <= 30 ) ++ codec_volume = 50; ++ else if ( val > 30 && val <= 40 ) ++ codec_volume = 40; ++ else if ( val > 40 && val <= 50 ) ++ codec_volume = 30; ++ else if ( val > 50 && val <= 60 ) ++ codec_volume = 22; ++ else if ( val > 60&& val <= 70 ) ++ codec_volume = 15; ++ else if ( val > 70 && val <= 80 ) ++ codec_volume = 8; ++ else if ( val > 80 && val <= 90 ) ++ codec_volume = 4; ++ else if ( val > 90 && val <= 100 ) ++ codec_volume = 2; ++ ++ i2s_codec_write(0x0a, codec_volume); ++ i2s_codec_write(0x0d, codec_volume); ++} ++ ++void set_ak4642en_mic(int val) ++{ ++ int mic_gain; ++ mic_gain = 241 * val /100; ++ i2s_codec_write(0x09, mic_gain); ++ i2s_codec_write(0x0c, mic_gain); ++} ++ ++void resume_ak4642en(void) ++{ ++ __gpio_as_output(17); ++ __gpio_set_pin(17); //enable ak4642 ++ __gpio_as_output(68); ++ __gpio_clear_pin(68); ++ __gpio_as_output(69); ++ __gpio_clear_pin(69); ++ __gpio_as_output(70); ++ __gpio_clear_pin(70); ++ __gpio_as_input(71); ++ __gpio_clear_pin(71); ++ __gpio_as_input(77); ++ __gpio_clear_pin(77); ++ __gpio_as_input(78); ++ __gpio_clear_pin(78); ++ REG_GPIO_GPALR(2) &= 0xC3FF0CFF; ++ REG_GPIO_GPALR(2) |= 0x14005000; ++ //set SCC clock initialization ++ REG_SCC1_CR(SCC1_BASE) = 0x00000000; ++ udelay(2); ++ REG_SCC1_CR(SCC1_BASE) |= 1 << 31; ++ udelay(2); ++ __gpio_as_output(I2S_PDN); ++ __gpio_set_pin(I2S_PDN); ++ udelay(5); ++ __gpio_clear_pin(I2S_PDN); ++ ndelay(300);//>150ns ++ __gpio_set_pin(I2S_PDN); ++ mdelay(1); ++ //set PLL Master mode ++ i2s_codec_write(0x01, 0x0008);//master ++ i2s_codec_write(0x04, 0x006b);//ref:12MHz;BITCLK:64fs;I2S compli ++ i2s_codec_write(0x05, 0x000b);//sync:48KHz; ++ i2s_codec_write(0x00, 0x0040);//PMVCM ++ i2s_codec_write(0x01, 0x0009);//master,PLL enable ++ jack_plug_level = 10; ++ old_level = 100; ++ spk_hp = 0; ++ __gpio_as_input(JACK_PLUG_PIN); ++ jack_plug_level = __gpio_get_pin(JACK_PLUG_PIN); ++ //i suppose jack_plug_lvel is 1 indicate with HPO ++ if(jack_plug_level > 1 || jack_plug_level <0) ++ printk("Audio ak4642en codec Jack plug level is wrong!\n"); ++ if(jack_plug_level) ++ __gpio_as_irq_fall_edge(JACK_PLUG_PIN); ++ else ++ __gpio_as_irq_rise_edge(JACK_PLUG_PIN); ++ ++ i2s_codec_write(0x00, 0x0065); //for resume power ++ i2s_codec_write(0x01, 0x0039); //for open pop noise ++ i2s_codec_write(0x01, 0x0079); ++ i2s_codec_write(0x0a, codec_volume); ++ i2s_codec_write(0x0d, codec_volume); ++} ++ ++void suspend_ak4642en(int wr,int rd) ++{ ++ if(wr) //playing ++ { ++ if(jack_plug_level == 0) ++ { ++ i2s_codec_write(0x01, 0x0039); // for close pop noise ++ mdelay(500); ++ i2s_codec_write(0x01, 0x0009); //PLL on I2S ++ i2s_codec_write(0x07, 0x0001); ++ i2s_codec_write(0x11, 0x0000); ++ i2s_codec_write(0x00, 0x0040); ++ i2s_codec_write(0x0f, 0x0008); // for open pop noise ++ ++ } ++ else ++ { ++ //for Speaker output ++ i2s_codec_write(0x02, 0x0020); ++ i2s_codec_write(0x07, 0x0001); ++ i2s_codec_write(0x11, 0x0000); ++ i2s_codec_write(0x02, 0x0000); ++ i2s_codec_write(0x00, 0x0040); // for close pop noise ++ } ++ } ++ ++ if(rd) // recording ++ { ++ i2s_codec_write(0x02, 0x0001); // 5-11 a1 ++ i2s_codec_write(0x07, 0x0001); ++ i2s_codec_write(0x11, 0x0000); ++ mdelay(300); ++ } ++ __gpio_as_output(17); ++ __gpio_clear_pin(17);//disable ak4642 ++ __i2s_disable(); ++} ++ ++static int __init init_ak4642en(void) ++{ ++ set_codec_gpio_pin = set_ak4642en_gpio_pin; ++ each_time_init_codec = each_time_init_ak4642en; ++ clear_codec_mode = clear_ak4642en_mode; ++ ++ set_codec_record = set_ak4642en_record; ++ set_codec_replay = set_ak4642en_replay; ++ set_replay_hp_or_speaker = set_ak4642en_replay; ++ ++ set_codec_speed = set_ak4642en_speed; ++ clear_codec_record = clear_ak4642en_record; ++ clear_codec_replay = clear_ak4642en_replay; ++ ++ codec_mixer_old_info_id_name = ak4642en_mixer_old_info_id_name; ++ codec_mixer_info_id_name = ak4642en_mixer_info_id_name; ++ ++ set_codec_volume = set_ak4642en_volume; ++ ++ set_codec_mic = set_ak4642en_mic; ++ ++ i2s_resume_codec = resume_ak4642en; ++ i2s_suspend_codec = suspend_ak4642en; ++ printk("---> ak4642en initialization!\n"); ++ return 0; ++} ++ ++static void __exit cleanup_ak4642en(void) ++{ ++ spk_hp = 0; ++ i2s_codec_write(0x01, 0x0008);//master,PLL disable ++ //free_irq(JACK_PLUG_IRQ, i2s_controller); ++ __gpio_clear_pin(I2S_PDN); ++ udelay(2); ++ REG_SCC1_CR(SCC1_BASE) &= 0 << 31; ++ udelay(2); ++} ++ ++module_init(init_ak4642en); ++module_exit(cleanup_ak4642en); +diff -urN linux-2.6.24.7.old/sound/oss/jz_ac97.c linux-2.6.24.7/sound/oss/jz_ac97.c +--- linux-2.6.24.7.old/sound/oss/jz_ac97.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/oss/jz_ac97.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,2252 @@ ++/* ++ * linux/drivers/sound/jz_ac97.c ++ * ++ * Jz On-Chip AC97 audio driver. ++ * ++ * Copyright (C) 2005 - 2007, Ingenic Semiconductor Inc. ++ * ++ * 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. ++ * ++ * Because the normal application of AUDIO devices are focused on Little_endian, ++ * then we only perform the little endian data format in driver. ++ * ++ */ ++ ++#define __NO_VERSION__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++//#include ++#include "sound_config.h" ++ ++#define DMA_ID_AC97_TX DMA_ID_AIC_TX ++#define DMA_ID_AC97_RX DMA_ID_AIC_RX ++ ++/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ ++#define NR_AC97 2 ++ ++#define STANDARD_SPEED 48000 ++#define MAX_RETRY 100 ++ ++static unsigned int k_8000[] = { ++ 0, 42, 85, 128, 170, 213, ++}; ++ ++static unsigned int reload_8000[] = { ++ 1, 0, 0, 0, 0, 0, ++}; ++ ++static unsigned int k_11025[] = { ++ 0, 58, 117, 176, 234, 37, 96, 154, ++ 213, 16, 74, 133, 192, 250, 53, 112, ++ 170, 229, 32, 90, 149, 208, 10, 69, ++ 128, 186, 245, 48, 106, 165, 224, 26, ++ 85, 144, 202, 5, 64, 122, 181, 240, ++ 42, 101, 160, 218, 21, 80, 138, 197, ++}; ++ ++static unsigned int reload_11025[] = { ++ 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, ++ 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, ++ 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, ++}; ++ ++static unsigned int k_16000[] = { ++ 0, 85, 170, ++}; ++ ++static unsigned int reload_16000[] = { ++ 1, 0, 0, ++}; ++ ++static unsigned int k_22050[] = { ++ 0, 117, 234, 96, 213, 74, 192, 53, ++ 170, 32, 149, 10, 128, 245, 106, 224, ++ 85, 202, 64, 181, 42, 160, 21, 138, ++}; ++ ++static unsigned int reload_22050[] = { ++ 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, ++ 1, 0, 1, 0, 1, 0, 1, 0, ++}; ++ ++static unsigned int k_24000[] = { ++ 0, 128, ++}; ++ ++static unsigned int reload_24000[] = { ++ 1, 0, ++}; ++ ++static unsigned int k_32000[] = { ++ 0, 170, 85, ++}; ++ ++static unsigned int reload_32000[] = { ++ 1, 0, 1, ++}; ++ ++static unsigned int k_44100[] = { ++ 0, 234, 213, 192, 170, 149, 128, 106, ++ 85, 64, 42, 21, ++}; ++ ++static unsigned int reload_44100[] = { ++ 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++}; ++ ++static unsigned int k_48000[] = { ++ 0, ++}; ++ ++static unsigned int reload_48000[] = { ++ 1, ++}; ++ ++ ++static unsigned int f_scale_counts[8] = { ++ 6, 48, 3, 24, 2, 3, 12, 1, ++}; ++ ++static int jz_audio_rate; ++static char jz_audio_format; ++static char jz_audio_channels; ++static int jz_audio_k; /* rate expand multiple */ ++static int jz_audio_q; /* rate expand compensate */ ++static int jz_audio_count; /* total count of voice data */ ++static int last_jz_audio_count; ++ ++static int jz_audio_fragments;//unused fragment amount ++static int jz_audio_fragstotal; ++static int jz_audio_fragsize; ++static int jz_audio_dma_tran_count;//bytes count of one DMA transfer ++ ++static unsigned int f_scale_count; ++static unsigned int *f_scale_array; ++static unsigned int *f_scale_reload; ++static unsigned int f_scale_idx; ++ ++static void (*old_mksound)(unsigned int hz, unsigned int ticks); ++extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); ++extern void jz_set_dma_block_size(int dmanr, int nbyte); ++extern void jz_set_dma_dest_width(int dmanr, int nbit); ++extern void jz_set_dma_src_width(int dmanr, int nbit); ++ ++static void jz_update_filler(int bits, int channels); ++ ++static void Init_In_Out_queue(int fragstotal,int fragsize); ++static void Free_In_Out_queue(int fragstotal,int fragsize); ++ ++static irqreturn_t ++jz_ac97_replay_dma_irq(int irqnr, void *dev_id); ++static irqreturn_t ++jz_ac97_record_dma_irq(int irqnr, void *dev_id); ++ ++static void ++(*replay_filler)(unsigned long src_start, int count, int id); ++static int ++(*record_filler)(unsigned long dst_start, int count, int id); ++ ++static struct file_operations jz_ac97_audio_fops; ++ ++DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue); ++DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue); ++ ++struct jz_ac97_controller_info ++{ ++ int io_base; ++ int dma1; /* play */ ++ int dma2; /* record */ ++ ++ char *name; ++ ++ int dev_audio; ++ struct ac97_codec *ac97_codec[NR_AC97]; ++ ++ unsigned short ac97_features; ++ ++ int opened1; ++ int opened2; ++ ++ unsigned char *tmp1; /* tmp buffer for sample conversions */ ++ unsigned char *tmp2; ++ ++ spinlock_t lock; ++ spinlock_t ioctllock; ++ wait_queue_head_t dac_wait; ++ wait_queue_head_t adc_wait; ++ ++ int nextIn; // byte index to next-in to DMA buffer ++ int nextOut; // byte index to next-out from DMA buffer ++ int count; // current byte count in DMA buffer ++ int finish; // current transfered byte count in DMA buffer ++ unsigned total_bytes; // total bytes written or read ++ unsigned blocks; ++ unsigned error; // over/underrun ++ ++ /* We use two devices, because we can do simultaneous play and record. ++ This keeps track of which device is being used for what purpose; ++ these are the actual device numbers. */ ++ int dev_for_play; ++ int dev_for_record; ++ ++ int playing; ++ int recording; ++ int patched; ++ unsigned long rec_buf_size; ++ unsigned long playback_buf_size; ++ ++#ifdef CONFIG_PM ++ struct pm_dev *pm; ++#endif ++}; ++ ++static struct jz_ac97_controller_info *ac97_controller = NULL; ++ ++static int jz_readAC97Reg(struct ac97_codec *dev, u8 reg); ++static int jz_writeAC97Reg(struct ac97_codec *dev, u8 reg, u16 data); ++static u16 ac97_codec_read(struct ac97_codec *codec, u8 reg); ++static void ac97_codec_write(struct ac97_codec *codec, u8 reg, u16 data); ++ ++#define QUEUE_MAX 2 ++ ++typedef struct buffer_queue_s { ++ int count; ++ int *id; ++ spinlock_t lock; ++} buffer_queue_t; ++ ++static unsigned long *out_dma_buf = NULL; ++static unsigned long *out_dma_pbuf = NULL; ++static unsigned long *out_dma_buf_data_count = NULL; ++static unsigned long *in_dma_buf = NULL; ++static unsigned long *in_dma_pbuf = NULL; ++static unsigned long *in_dma_buf_data_count = NULL; ++ ++static buffer_queue_t out_empty_queue; ++static buffer_queue_t out_full_queue; ++static buffer_queue_t out_busy_queue; ++ ++static buffer_queue_t in_empty_queue; ++static buffer_queue_t in_full_queue; ++static buffer_queue_t in_busy_queue; ++ ++static int first_record_call = 0; ++ ++static inline int get_buffer_id(struct buffer_queue_s *q) ++{ ++ int r, i; ++ unsigned long flags; ++ spin_lock_irqsave(&q->lock, flags); ++ if (q->count == 0) { ++ spin_unlock_irqrestore(&q->lock, flags); ++ return -1; ++ } ++ r = *(q->id + 0); ++ for (i=0;i < q->count-1;i++) ++ *(q->id + i) = *(q->id + (i+1)); ++ q->count --; ++ spin_unlock_irqrestore(&q->lock, flags); ++ return r; ++} ++ ++static inline void put_buffer_id(struct buffer_queue_s *q, int id) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&q->lock, flags); ++ *(q->id + q->count) = id; ++ q->count ++; ++ spin_unlock_irqrestore(&q->lock, flags); ++} ++ ++static inline int elements_in_queue(struct buffer_queue_s *q) ++{ ++ int r; ++ unsigned long flags; ++ spin_lock_irqsave(&q->lock, flags); ++ r = q->count; ++ spin_unlock_irqrestore(&q->lock, flags); ++ return r; ++} ++ ++/**************************************************************************** ++ * Architecture related routines ++ ****************************************************************************/ ++static inline ++void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode) ++{ ++ unsigned long flags; ++ struct jz_ac97_controller_info * controller = ++ (struct jz_ac97_controller_info *) dev_id; ++ //for DSP_GETOPTR ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ jz_audio_dma_tran_count = count; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ flags = claim_dma_lock(); ++ disable_dma(chan); ++ clear_dma_ff(chan); ++ set_dma_mode(chan, mode); ++ set_dma_addr(chan, phyaddr); ++ if (count == 0) { ++ count++; ++ printk(KERN_DEBUG "%s: JzSOC DMA controller can't set dma count zero!\n", ++ __FUNCTION__); ++ } ++ set_dma_count(chan, count); ++ enable_dma(chan); ++ release_dma_lock(flags); ++} ++ ++static irqreturn_t ++jz_ac97_record_dma_irq (int irq, void *dev_id) ++{ ++ struct jz_ac97_controller_info * controller = ++ (struct jz_ac97_controller_info *) dev_id; ++ int dma = controller->dma2; ++ int id1, id2; ++ unsigned long flags; ++ ++ disable_dma(dma); ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ ++ //for DSP_GETIPTR ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ id1 = get_buffer_id(&in_busy_queue); ++ put_buffer_id(&in_full_queue, id1); ++ wake_up(&rx_wait_queue); ++ wake_up(&controller->adc_wait); ++ if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id2); ++ ++ *(in_dma_buf_data_count + id2) = *(in_dma_buf_data_count + id1); ++ audio_start_dma(dma,dev_id, ++ *(in_dma_pbuf + id2), ++ *(in_dma_buf_data_count + id2), ++ DMA_MODE_READ); ++ } else { ++ in_busy_queue.count = 0; ++ } ++ } ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t ++jz_ac97_replay_dma_irq (int irq, void *dev_id) ++{ ++ struct jz_ac97_controller_info * controller = ++ (struct jz_ac97_controller_info *) dev_id; ++ int dma = controller->dma1, id; ++ unsigned long flags; ++ ++ disable_dma(dma); ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ //for DSP_GETOPTR ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ if ((id = get_buffer_id(&out_busy_queue)) < 0) { ++ printk(KERN_DEBUG "Strange DMA finish interrupt for AC97 module\n"); ++ } ++ ++ put_buffer_id(&out_empty_queue, id); ++ if ((id = get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_busy_queue, id); ++ if(*(out_dma_buf_data_count + id) > 0) { ++ audio_start_dma(dma, dev_id, *(out_dma_pbuf + id), ++ *(out_dma_buf_data_count + id), ++ DMA_MODE_WRITE); ++ } ++ } else { ++ out_busy_queue.count = 0; ++ } ++ if (elements_in_queue(&out_empty_queue) > 0) { ++ wake_up(&tx_wait_queue); ++ wake_up(&controller->dac_wait); ++ } ++ } ++ return IRQ_HANDLED; ++} ++ ++/* ++ * Initialize the onchip AC97 controller ++ */ ++static void jz_ac97_initHw(struct jz_ac97_controller_info *controller) ++{ ++ __ac97_disable(); ++ __ac97_reset(); ++ __ac97_enable(); ++ ++ __ac97_cold_reset_codec(); ++ /* wait for a long time to let ac97 controller reset completely, ++ * otherwise, registers except ACFR will be clear by reset, can't be ++ * set correctly. ++ */ ++ udelay(160); ++ ++ __ac97_disable_record(); ++ __ac97_disable_replay(); ++ __ac97_disable_loopback(); ++ ++ /* Check the trigger threshold reset value to detect version */ ++ if (((REG_AIC_FR & AIC_FR_TFTH_MASK) >> AIC_FR_TFTH_BIT) == 8) { ++ printk("JzAC97: patched controller detected.\n"); ++ controller->patched = 1; ++ } else { ++ printk("JzAC97: standard controller detected.\n"); ++ controller->patched = 0; ++ } ++ ++ /* Set FIFO data size. Which shows valid data bits. ++ * ++ */ ++ __ac97_set_oass(8); ++ __ac97_set_iass(8); ++ ++ __ac97_set_xs_stereo(); ++ __ac97_set_rs_stereo(); ++} ++ ++/* ++ * Initialize all of in(out)_empty_queue value ++ */ ++static void Init_In_Out_queue(int fragstotal,int fragsize) ++{ ++ int i; ++ if(out_dma_buf || in_dma_buf) ++ return; ++ in_empty_queue.count = fragstotal; ++ out_empty_queue.count = fragstotal; ++ ++ out_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!out_dma_buf) ++ goto all_mem_err; ++ out_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!out_dma_pbuf) ++ goto all_mem_err; ++ out_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!out_dma_buf_data_count) ++ goto all_mem_err; ++ in_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_buf) ++ goto all_mem_err; ++ in_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_pbuf) ++ goto all_mem_err; ++ in_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_buf_data_count) ++ goto all_mem_err; ++ in_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_empty_queue.id) ++ goto all_mem_err; ++ in_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_full_queue.id) ++ goto all_mem_err; ++ in_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_busy_queue.id) ++ goto all_mem_err; ++ out_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_empty_queue.id) ++ goto all_mem_err; ++ out_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_full_queue.id) ++ goto all_mem_err; ++ out_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_busy_queue.id) ++ goto all_mem_err; ++ ++ for (i=0;i < fragstotal;i++) { ++ *(in_empty_queue.id + i) = i; ++ *(out_empty_queue.id + i) = i; ++ } ++ ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ /*alloc DMA buffer*/ ++ for (i = 0; i < jz_audio_fragstotal; i++) { ++ *(out_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); ++ if (*(out_dma_buf + i) == 0) { ++ printk(" can't allocate required DMA(OUT) buffers.\n"); ++ goto mem_failed_out; ++ } ++ *(out_dma_pbuf + i) = virt_to_phys((void *)(*(out_dma_buf + i))); ++ } ++ ++ for (i = 0; i < jz_audio_fragstotal; i++) { ++ *(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); ++ if (*(in_dma_buf + i) == 0) { ++ printk(" can't allocate required DMA(IN) buffers.\n"); ++ goto mem_failed_in; ++ } ++ *(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i))); ++ dma_cache_wback_inv(*(in_dma_buf + i), 4096*8);//fragsize ++ *(in_dma_buf + i) = KSEG1ADDR(*(in_dma_buf + i)); ++ } ++ return ; ++ ++ all_mem_err: ++ printk("error:allocate memory occur error!\n"); ++ return ; ++ ++mem_failed_out: ++ ++ for (i = 0; i < jz_audio_fragstotal; i++) { ++ if(*(out_dma_buf + i)) ++ free_pages(*(out_dma_buf + i), get_order(fragsize)); ++ } ++ return ; ++ ++mem_failed_in: ++ ++ for (i = 0; i < jz_audio_fragstotal; i++) { ++ if(*(in_dma_buf + i)) ++ free_pages(*(in_dma_buf + i), get_order(fragsize)); ++ } ++ return ; ++ ++} ++static void Free_In_Out_queue(int fragstotal,int fragsize) ++{ ++ int i; ++ if(out_dma_buf != NULL) ++ { ++ for (i = 0; i < jz_audio_fragstotal; i++) ++ { ++ if(*(out_dma_buf + i)) ++ free_pages(*(out_dma_buf + i), get_order(fragsize)); ++ *(out_dma_buf + i) = 0; ++ } ++ kfree(out_dma_buf); ++ out_dma_buf = NULL; ++ } ++ if(out_dma_pbuf) ++ { ++ kfree(out_dma_pbuf); ++ out_dma_pbuf = NULL; ++ } ++ if(out_dma_buf_data_count) ++ { ++ kfree(out_dma_buf_data_count); ++ out_dma_buf_data_count = NULL; ++ } ++ if(in_dma_buf) ++ { ++ for (i = 0; i < jz_audio_fragstotal; i++) ++ { ++ if(*(in_dma_buf + i)) ++ free_pages(*(in_dma_buf + i), get_order(fragsize)); ++ *(in_dma_buf + i) = 0; ++ } ++ kfree(in_dma_buf); ++ in_dma_buf = NULL; ++ } ++ if(in_dma_pbuf) ++ { ++ kfree(in_dma_pbuf); ++ in_dma_pbuf = NULL; ++ } ++ if(in_dma_buf_data_count) ++ { ++ kfree(in_dma_buf_data_count); ++ in_dma_buf_data_count = NULL; ++ } ++ if(in_empty_queue.id) ++ { ++ kfree(in_empty_queue.id); ++ in_empty_queue.id = NULL; ++ } ++ if(in_full_queue.id) ++ { ++ kfree(in_full_queue.id); ++ in_full_queue.id = NULL; ++ } ++ if(in_busy_queue.id) ++ { ++ kfree(in_busy_queue.id); ++ in_busy_queue.id = NULL; ++ } ++ if(out_empty_queue.id) ++ { ++ kfree(out_empty_queue.id); ++ out_empty_queue.id = NULL; ++ } ++ if(out_full_queue.id) ++ { ++ kfree(out_full_queue.id); ++ out_full_queue.id = NULL; ++ } ++ if(out_busy_queue.id) ++ { ++ kfree(out_busy_queue.id); ++ out_busy_queue.id = NULL; ++ } ++ ++ in_empty_queue.count = fragstotal; ++ out_empty_queue.count = fragstotal; ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ return ; ++} ++ ++/* ++ * Reset everything ++ */ ++static void ++jz_ac97_full_reset(struct jz_ac97_controller_info *controller) ++{ ++ jz_ac97_initHw(controller); ++} ++ ++ ++static void ++jz_ac97_mksound(unsigned int hz, unsigned int ticks) ++{ ++// printk("BEEP - %d %d!\n", hz, ticks); ++} ++ ++static int jz_audio_set_speed(int dev, int rate) ++{ ++ /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 ? */ ++ u32 dacp; ++ struct ac97_codec *codec=ac97_controller->ac97_codec[0]; ++ ++ if (rate > 48000) ++ rate = 48000; ++ if (rate < 8000) ++ rate = 8000; ++ ++ ++ /* Power down the DAC */ ++ dacp=ac97_codec_read(codec, AC97_POWER_CONTROL); ++ ac97_codec_write(codec, AC97_POWER_CONTROL, dacp|0x0200); ++ /* Load the rate; only 48Khz playback available, read always zero */ ++ if ((ac97_controller->patched) && (ac97_controller->ac97_features & 1)) ++ ac97_codec_write(codec, AC97_PCM_FRONT_DAC_RATE, rate); ++ else ++ ac97_codec_write(codec, AC97_PCM_FRONT_DAC_RATE, 48000); ++ ++ /* Power it back up */ ++ ac97_codec_write(codec, AC97_POWER_CONTROL, dacp); ++ ++ jz_audio_rate = rate; ++ jz_audio_k = STANDARD_SPEED / rate; ++ if (rate * jz_audio_k != STANDARD_SPEED) ++ jz_audio_q = rate / ((STANDARD_SPEED / jz_audio_k) - rate ); ++ else ++ jz_audio_q = 0x1fffffff; /* a very big value, don't compensate */ ++ ++ switch (rate) { ++ case 8000: ++ f_scale_count = f_scale_counts[0]; ++ f_scale_array = k_8000; ++ f_scale_reload = reload_8000; ++ break; ++ case 11025: ++ f_scale_count = f_scale_counts[1]; ++ f_scale_array = k_11025; ++ f_scale_reload = reload_11025; ++ break; ++ case 16000: ++ f_scale_count = f_scale_counts[2]; ++ f_scale_array = k_16000; ++ f_scale_reload = reload_16000; ++ break; ++ case 22050: ++ f_scale_count = f_scale_counts[3]; ++ f_scale_array = k_22050; ++ f_scale_reload = reload_22050; ++ break; ++ case 24000: ++ f_scale_count = f_scale_counts[4]; ++ f_scale_array = k_24000; ++ f_scale_reload = reload_24000; ++ break; ++ case 32000: ++ f_scale_count = f_scale_counts[5]; ++ f_scale_array = k_32000; ++ f_scale_reload = reload_32000; ++ break; ++ case 44100: ++ f_scale_count = f_scale_counts[6]; ++ f_scale_array = k_44100; ++ f_scale_reload = reload_44100; ++ break; ++ case 48000: ++ f_scale_count = f_scale_counts[7]; ++ f_scale_array = k_48000; ++ f_scale_reload = reload_48000; ++ break; ++ } ++ f_scale_idx = 0; ++ ++ return jz_audio_rate; ++} ++ ++static int record_fill_1x8_u(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned char data; ++ volatile unsigned char *s = (unsigned char *)(*(in_dma_buf + id)); ++ volatile unsigned char *dp = (unsigned char *)dst_start; ++ ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ if ((jz_audio_count++ % jz_audio_k) == 0) { ++ cnt++; ++ data = *(s++); ++ *(dp ++) = data + 0x80; ++ s++; /* skip the other channel */ ++ } else { ++ s += 2; /* skip the redundancy */ ++ } ++ if (jz_audio_count - last_jz_audio_count >= jz_audio_q) { ++ jz_audio_count++; ++ last_jz_audio_count = jz_audio_count; ++ count -= 2; ++ s += 2; ++ } ++ } ++ return cnt; ++} ++ ++static int record_fill_2x8_u(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned char d1, d2; ++ volatile unsigned char *s = (unsigned char *)(*(in_dma_buf + id)); ++ volatile unsigned char *dp = (unsigned char *)dst_start; ++ ++ while (count > 0) { ++ count -= 2; ++ if ((jz_audio_count++ % jz_audio_k) == 0) { ++ cnt += 2; ++ d1 = *(s++); ++ *(dp ++) = d1 + 0x80; ++ d2 = *(s++); ++ *(dp ++) = d2 + 0x80; ++ } else { ++ s += 2; /* skip the redundancy */ ++ } ++ if (jz_audio_count - last_jz_audio_count >= jz_audio_q * 2) { ++ jz_audio_count += 2; ++ last_jz_audio_count = jz_audio_count; ++ count -= 2; ++ s += 2; ++ } ++ } ++ return cnt; ++} ++ ++static int record_fill_1x16_s(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned short d1; ++ unsigned short *s = (unsigned short *)(*(in_dma_buf + id)); ++ unsigned short *dp = (unsigned short *)dst_start; ++ ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ if ((jz_audio_count++ % jz_audio_k) == 0) { ++ cnt += 2; /* count in byte */ ++ d1 = *(s++); ++ *(dp ++) = d1; ++ s++; /* skip the other channel */ ++ } else { ++ s += 2; /* skip the redundancy */ ++ } ++ if (jz_audio_count - last_jz_audio_count >= jz_audio_q * 2) { ++ jz_audio_count += 2; ++ last_jz_audio_count = jz_audio_count; ++ count -= 2; ++ s += 2; ++ } ++ } ++ return cnt; ++} ++ ++static int record_fill_2x16_s(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned short d1, d2; ++ unsigned short *s = (unsigned short *)(*(in_dma_buf + id)); ++ unsigned short *dp = (unsigned short *)dst_start; ++ ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ if ((jz_audio_count++ % jz_audio_k) == 0) { ++ cnt += 4; /* count in byte */ ++ d1 = *(s++); ++ *(dp ++) = d1; ++ d2 = *(s++); ++ *(dp ++) = d2; ++ } else ++ s += 2; /* skip the redundancy */ ++ ++ if (jz_audio_count - last_jz_audio_count >= jz_audio_q * 4) { ++ jz_audio_count += 4; ++ last_jz_audio_count = jz_audio_count; ++ count -= 2; ++ s += 2; ++ } ++ } ++ return cnt; ++} ++ ++ ++static void replay_fill_1x8_u(unsigned long src_start, int count, int id) ++{ ++ int i, cnt = 0; ++ unsigned char data; ++ unsigned char *s = (unsigned char *)src_start; ++ unsigned char *dp = (unsigned char *)(*(out_dma_buf + id)); ++ ++ while (count > 0) { ++ count--; ++ jz_audio_count++; ++ cnt += jz_audio_k; ++ data = *(s++) - 0x80; ++ for (i=0;i 0) { ++ count -= 2; ++ jz_audio_count += 2; ++ cnt += 2 * jz_audio_k; ++ d1 = *(s++) - 0x80; ++ d2 = *(s++) - 0x80; ++ for (i=0;i= jz_audio_q * 2) { ++ cnt += 2 * jz_audio_k; ++ last_jz_audio_count = jz_audio_count; ++ for (i=0;i= 0) { ++ if (f_scale_reload[f_scale_idx]) { ++ d1 = d2; ++ d2 = *s++; ++ if (!count) ++ break; ++ count -= 2; ++ } ++ d = d1 + (((d2 - d1) * f_scale_array[f_scale_idx]) >> 8); ++ *dp++ = d; ++ *dp++ = d; ++ cnt += 4; ++ f_scale_idx ++; ++ if (f_scale_idx >= f_scale_count) ++ f_scale_idx = 0; ++ } ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++static void replay_fill_2x16_s(unsigned long src_start, int count, int id) ++{ ++ int cnt = 0; ++ static short d11, d12, d21, d22, d1, d2; ++ short *s = (short *)src_start; ++ short *dp = (short *)(*(out_dma_buf + id)); ++ ++ d12 = *s++; ++ d22 = *s++; ++ count -= 4; ++ while (count >= 0) { ++ register unsigned int kvalue; ++ kvalue = f_scale_array[f_scale_idx]; ++ if (f_scale_reload[f_scale_idx]) { ++ d11 = d12; ++ d12 = *s++; ++ d21 = d22; ++ d22 = *s++; ++ if (!count) ++ break; ++ count -= 4; ++ } ++ d1 = d11 + (((d12 - d11)*kvalue) >> 8); ++ d2 = d21 + (((d22 - d21)*kvalue) >> 8); ++ *dp++ = d1; ++ *dp++ = d2; ++ cnt += 4; ++ f_scale_idx ++; ++ if (f_scale_idx >= f_scale_count) ++ f_scale_idx = 0; ++ } ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++static unsigned int jz_audio_set_format(int dev, unsigned int fmt) ++{ ++ switch (fmt) { ++ case AFMT_U8: ++ case AFMT_S16_LE: ++ jz_audio_format = fmt; ++ jz_update_filler(fmt, jz_audio_channels); ++ case AFMT_QUERY: ++ break; ++ } ++ return jz_audio_format; ++} ++ ++static short jz_audio_set_channels(int dev, short channels) ++{ ++ switch (channels) { ++ case 1: ++ __ac97_set_xs_stereo(); // always stereo when recording ++ __ac97_set_rs_stereo(); // always stereo when recording ++ jz_audio_channels = channels; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++ break; ++ case 2: ++ __ac97_set_xs_stereo(); ++ __ac97_set_rs_stereo(); ++ jz_audio_channels = channels; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++ break; ++ case 0: ++ break; ++ } ++ return jz_audio_channels; ++} ++ ++ ++static void jz_audio_reset(void) ++{ ++ __ac97_disable_replay(); ++ __ac97_disable_receive_dma(); ++ __ac97_disable_record(); ++ __ac97_disable_transmit_dma(); ++} ++ ++static int jz_audio_release(struct inode *inode, struct file *file); ++static int jz_audio_open(struct inode *inode, struct file *file); ++static int jz_audio_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg); ++static unsigned int jz_audio_poll(struct file *file, ++ struct poll_table_struct *wait); ++static ssize_t jz_audio_write(struct file *file, const char *buffer, ++ size_t count, loff_t *ppos); ++static ssize_t jz_audio_read(struct file *file, char *buffer, ++ size_t count, loff_t *ppos); ++ ++/* static struct file_operations jz_ac97_audio_fops */ ++static struct file_operations jz_ac97_audio_fops = ++{ ++ owner: THIS_MODULE, ++ open: jz_audio_open, ++ release: jz_audio_release, ++ write: jz_audio_write, ++ read: jz_audio_read, ++ poll: jz_audio_poll, ++ ioctl: jz_audio_ioctl ++}; ++ ++/* Read / Write AC97 codec registers */ ++static inline int jz_out_command_ready(void) ++{ ++ int t2 = 1000; ++ int done = 0; ++ ++ while (! done && t2-- > 0) { ++ if (REG32(AC97_ACSR) & AIC_ACSR_CADT) { ++ REG32(AC97_ACSR) &= ~AIC_ACSR_CADT; ++ done = 1; ++ } ++ else ++ udelay (1); ++ } ++ return done; ++} ++ ++static inline int jz_in_status_ready(void) ++{ ++ int t2 = 1000; ++ int done = 0; ++ ++ while (! done && t2-- > 0) { ++ if (REG32(AC97_ACSR) & AIC_ACSR_SADR) { ++ REG32(AC97_ACSR) &= ~AIC_ACSR_SADR; ++ done = 1; ++ } ++ else { ++ if (REG32(AC97_ACSR) & AIC_ACSR_RSTO) { ++ REG32(AC97_ACSR) &= ~AIC_ACSR_RSTO; ++ printk(KERN_DEBUG "%s: RSTO receive status timeout.\n", ++ __FUNCTION__); ++ done = 0; ++ break; ++ } ++ udelay (1); ++ } ++ } ++ return done; ++} ++ ++static int jz_readAC97Reg (struct ac97_codec *dev, u8 reg) ++{ ++ u16 value; ++ ++ if (reg < 128) { ++ u8 ret_reg; ++ __ac97_out_rcmd_addr(reg);//output read addr ++ if (jz_out_command_ready())//judge if send completely? ++ while (jz_in_status_ready()) {//judge if receive completely? ++ ret_reg = __ac97_in_status_addr();//slot1:send addr ++ value = __ac97_in_data(); ++ if (ret_reg == reg) ++ return value; ++ else { ++// printk(KERN_DEBUG "%s: index (0x%02x)->(0x%02x) 0x%x\n", __FUNCTION__, reg, ret_reg, value); ++ return -EINVAL; ++ } ++ } ++ } ++ value = __ac97_in_data(); ++ printk (KERN_DEBUG "timeout while reading AC97 codec (0x%x)\n", reg); ++ return -EINVAL; ++} ++ ++static u16 ac97_codec_read(struct ac97_codec *codec, u8 reg) ++{ ++ int res = jz_readAC97Reg(codec, reg); ++ int count = 0; ++ while (res == -EINVAL) { ++ udelay(1000); ++ __ac97_warm_reset_codec(); ++ udelay(1000); ++ res = jz_readAC97Reg(codec, reg); ++ count ++; ++ if (count > MAX_RETRY){ ++ printk(KERN_WARNING"After try %d when read AC97 codec 0x%x, can't success, give up operate!!\n", ++ MAX_RETRY, reg); ++ break; ++ } ++ } ++ return (u16)res; ++} ++ ++static int jz_writeAC97Reg (struct ac97_codec *dev, u8 reg, u16 value) ++{ ++ //unsigned long flags; ++ int done = 0; ++ ++ //save_and_cli(flags); ++ ++ __ac97_out_wcmd_addr(reg); ++ __ac97_out_data(value); ++ if (jz_out_command_ready()) ++ done = 1; ++ else ++ printk (KERN_DEBUG "Tiemout AC97 codec write (0x%X<==0x%X)\n", reg, value); ++ //restore_flags(flags); ++ return done; ++} ++static void ac97_codec_write(struct ac97_codec *codec, u8 reg, u16 data) ++{ ++ int done = jz_writeAC97Reg(codec, reg, data); ++ int count = 0; ++ while (done == 0) { ++ count ++; ++ udelay (2000); ++ __ac97_warm_reset_codec(); ++ udelay(2000); ++ done = jz_writeAC97Reg(codec, reg, data); ++ if ( count > MAX_RETRY ){ ++ printk (KERN_DEBUG " After try %d when write AC97 codec (0x%x), can't sucess, give up!! \n", ++ MAX_RETRY, reg); ++ break; ++ } ++ } ++} ++ ++/* OSS /dev/mixer file operation methods */ ++ ++static int jz_ac97_open_mixdev(struct inode *inode, struct file *file) ++{ ++ int i; ++ int minor = MINOR(inode->i_rdev); ++ struct jz_ac97_controller_info *controller = ac97_controller; ++ ++ for (i = 0; i < NR_AC97; i++) ++ if (controller->ac97_codec[i] != NULL && ++ controller->ac97_codec[i]->dev_mixer == minor) ++ goto match; ++ ++ if (!controller) ++ return -ENODEV; ++ ++ match: ++ file->private_data = controller->ac97_codec[i]; ++ ++ return 0; ++} ++ ++static int jz_ac97_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct ac97_codec *codec = (struct ac97_codec *)file->private_data; ++ ++ return codec->mixer_ioctl(codec, cmd, arg); ++} ++ ++static loff_t jz_ac97_llseek(struct file *file, loff_t offset, int origin) ++{ ++ return -ESPIPE; ++} ++ ++static /*const*/ struct file_operations jz_ac97_mixer_fops = { ++ owner: THIS_MODULE, ++ llseek: jz_ac97_llseek, ++ ioctl: jz_ac97_ioctl_mixdev, ++ open: jz_ac97_open_mixdev, ++}; ++ ++/* AC97 codec initialisation. */ ++static int __init jz_ac97_codec_init(struct jz_ac97_controller_info *controller) ++{ ++ int num_ac97 = 0; ++ int ready_2nd = 0; ++ struct ac97_codec *codec; ++ unsigned short eid; ++ int i = 0; ++ ++ if (__ac97_codec_is_low_power_mode()) { ++ printk(KERN_DEBUG "AC97 codec is low power mode, warm reset ...\n"); ++ __ac97_warm_reset_codec(); ++ udelay(10); ++ } ++ i = 0; ++ while (!__ac97_codec_is_ready()) { ++ i++; ++ if ( i > 100 ) { ++ printk(KERN_WARNING "AC97 codec not ready, failed init ..\n"); ++ return -ENODEV; ++ } ++ udelay(10); ++ } ++ i = 0; ++ ++ /* Reset the mixer. */ ++ for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { ++ if ((codec = kmalloc(sizeof(struct ac97_codec), ++ GFP_KERNEL)) == NULL) ++ return -ENOMEM; ++ memset(codec, 0, sizeof(struct ac97_codec)); ++ ++ /* initialize some basic codec information, ++ other fields will be filled ++ in ac97_probe_codec */ ++ codec->private_data = controller; ++ codec->id = num_ac97; ++ ++ codec->codec_read = ac97_codec_read; ++ codec->codec_write = ac97_codec_write; ++ ++ if (ac97_probe_codec(codec) == 0) ++ break; ++ ++ eid = ac97_codec_read(codec, AC97_EXTENDED_ID); ++ if (eid == 0xFFFF) { ++ printk(KERN_WARNING "Jz AC97: no codec attached?\n"); ++ kfree(codec); ++ break; ++ } ++ ++ controller->ac97_features = eid; ++ ++ if (!(eid & 0x0001)) ++ printk(KERN_WARNING "AC97 codec: only 48Khz playback available.\n"); ++ else { ++ printk(KERN_WARNING "AC97 codec: supports variable sample rate.\n"); ++ /* Enable HPEN: UCB1400 only */ ++ ac97_codec_write(codec, 0x6a, ++ ac97_codec_read(codec, 0x6a) | 0x40); ++ ++ /* Enable variable rate mode */ ++ ac97_codec_write(codec, AC97_EXTENDED_STATUS, 9); ++ ac97_codec_write(codec, ++ AC97_EXTENDED_STATUS, ++ ac97_codec_read(codec, AC97_EXTENDED_STATUS) | 0xE800); ++ /* power up everything, modify this ++ when implementing power saving */ ++ ac97_codec_write(codec, ++ AC97_POWER_CONTROL, ++ ac97_codec_read(codec, AC97_POWER_CONTROL) & ~0x7f00); ++ /* wait for analog ready */ ++ for (i=10; i && ((ac97_codec_read(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); i--) { ++// current->state = TASK_UNINTERRUPTIBLE; ++// schedule_timeout(HZ/20); ++ } ++ ++ if (!(ac97_codec_read(codec, AC97_EXTENDED_STATUS) & 1)) { ++ printk(KERN_WARNING "Jz AC97: Codec refused to allow VRA, using 48Khz only.\n"); ++ controller->ac97_features &= ~1; ++ } ++ } ++ ++ if ((codec->dev_mixer = ++ register_sound_mixer(&jz_ac97_mixer_fops, -1)) < 0) { ++ printk(KERN_ERR "Jz AC97: couldn't register mixer!\n"); ++ kfree(codec); ++ break; ++ } ++ ++ controller->ac97_codec[num_ac97] = codec; ++ ++ /* if there is no secondary codec at all, don't probe any more */ ++ if (!ready_2nd) ++ return num_ac97+1; ++ } ++ return num_ac97; ++} ++ ++static void jz_update_filler(int format, int channels) ++{ ++#define TYPE(fmt,ch) (((fmt)<<3) | ((ch)&7)) /* up to 8 chans supported. */ ++ ++ ++ switch (TYPE(format, channels)) { ++ default: ++ ++ case TYPE(AFMT_U8, 1): ++ if ((ac97_controller->patched) && ++ (ac97_controller->ac97_features & 1)) { ++ __aic_enable_mono2stereo(); ++ __aic_enable_unsignadj(); ++ jz_set_dma_block_size(ac97_controller->dma1, 16); ++ jz_set_dma_block_size(ac97_controller->dma2, 16); ++ } else { ++ __aic_disable_mono2stereo(); ++ __aic_disable_unsignadj(); ++ jz_set_dma_block_size(ac97_controller->dma1, 4); ++ jz_set_dma_block_size(ac97_controller->dma2, 4); ++ } ++ replay_filler = replay_fill_1x8_u; ++ record_filler = record_fill_1x8_u; ++ __ac97_set_oass(8); ++ __ac97_set_iass(8); ++ jz_set_dma_dest_width(ac97_controller->dma1, 8); ++ jz_set_dma_src_width(ac97_controller->dma2, 8); ++ break; ++ case TYPE(AFMT_U8, 2): ++ if ((ac97_controller->patched) && ++ (ac97_controller->ac97_features & 1)) { ++ __aic_enable_mono2stereo(); ++ __aic_enable_unsignadj(); ++ jz_set_dma_block_size(ac97_controller->dma1, 16); ++ jz_set_dma_block_size(ac97_controller->dma2, 16); ++ } else { ++ __aic_disable_mono2stereo(); ++ __aic_disable_unsignadj(); ++ jz_set_dma_block_size(ac97_controller->dma1, 4); ++ jz_set_dma_block_size(ac97_controller->dma2, 4); ++ } ++ replay_filler = replay_fill_2x8_u; ++ record_filler = record_fill_2x8_u; ++ __ac97_set_oass(8); ++ __ac97_set_iass(8); ++ jz_set_dma_dest_width(ac97_controller->dma1, 8); ++ jz_set_dma_src_width(ac97_controller->dma2, 8); ++ break; ++ case TYPE(AFMT_S16_LE, 1): ++ if ((ac97_controller->patched) && ++ (ac97_controller->ac97_features & 1)) { ++ __aic_enable_mono2stereo(); ++ jz_set_dma_block_size(ac97_controller->dma1, 16); ++ jz_set_dma_block_size(ac97_controller->dma2, 16); ++ } else { ++ __aic_disable_mono2stereo(); ++ jz_set_dma_block_size(ac97_controller->dma1, 4); ++ jz_set_dma_block_size(ac97_controller->dma2, 4); ++ } ++ __aic_disable_unsignadj(); ++ replay_filler = replay_fill_1x16_s; ++ record_filler = record_fill_1x16_s; ++ __ac97_set_oass(16); ++ __ac97_set_iass(16); ++ ++ jz_set_dma_dest_width(ac97_controller->dma1, 16); ++ jz_set_dma_src_width(ac97_controller->dma2, 16); ++ ++ break; ++ ++ case TYPE(AFMT_S16_LE, 2): ++ if ((ac97_controller->patched) && ++ (ac97_controller->ac97_features & 1)) { ++ jz_set_dma_block_size(ac97_controller->dma1, 16); ++ jz_set_dma_block_size(ac97_controller->dma2, 16); ++ } else { ++ jz_set_dma_block_size(ac97_controller->dma1, 4); ++ jz_set_dma_block_size(ac97_controller->dma2, 4); ++ } ++ __aic_disable_mono2stereo(); ++ __aic_disable_unsignadj(); ++ replay_filler = replay_fill_2x16_s; ++ record_filler = record_fill_2x16_s; ++ __ac97_set_oass(16); ++ __ac97_set_iass(16); ++ jz_set_dma_dest_width(ac97_controller->dma1, 16); ++ jz_set_dma_src_width(ac97_controller->dma2, 16); ++ break; ++ } ++} ++ ++#ifdef CONFIG_PROC_FS ++ ++extern struct proc_dir_entry *proc_jz_root; ++ ++static int jz_ac97_init_proc(struct jz_ac97_controller_info *controller) ++{ ++ if (!create_proc_read_entry ("ac97", 0, proc_jz_root, ++ ac97_read_proc, controller->ac97_codec[0])) ++ return -EIO; ++ ++ return 0; ++} ++ ++static void jz_ac97_cleanup_proc(struct jz_ac97_controller_info *controller) ++{ ++} ++ ++#endif ++ ++static void __init attach_jz_ac97(struct jz_ac97_controller_info *controller) ++{ ++ char *name; ++ int adev; ++ ++ name = controller->name; ++ jz_ac97_initHw(controller); ++ ++ /* register /dev/audio ? */ ++ adev = register_sound_dsp(&jz_ac97_audio_fops, -1); ++ if (adev < 0) ++ goto audio_failed; ++ ++ /* initialize AC97 codec and register /dev/mixer */ ++ if (jz_ac97_codec_init(controller) <= 0) ++ goto mixer_failed; ++ ++#ifdef CONFIG_PROC_FS ++ if (jz_ac97_init_proc(controller) < 0) { ++ printk(KERN_ERR "%s: can't create AC97 proc filesystem.\n", name); ++ goto proc_failed; ++ } ++#endif ++ ++ controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, 8);//4 ++ if (!controller->tmp1) { ++ printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); ++ goto tmp1_failed; ++ } ++ ++ controller->tmp2 = (void *)__get_free_pages(GFP_KERNEL, 8);//4 ++ if (!controller->tmp2) { ++ printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); ++ goto tmp2_failed; ++ } ++ ++ if ((controller->dma1 = jz_request_dma(DMA_ID_AC97_TX, "audio dac", ++ jz_ac97_replay_dma_irq, ++ IRQF_DISABLED, controller)) < 0) { ++ printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name); ++ goto dma1_failed; ++ } ++ if ((controller->dma2 = jz_request_dma(DMA_ID_AC97_RX, "audio adc", ++ jz_ac97_record_dma_irq, ++ IRQF_DISABLED, controller)) < 0) { ++ printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name); ++ goto dma2_failed; ++ } ++ ++ printk("Jz On-Chip AC97 controller registered (DAC: DMA%d/IRQ%d, ADC: DMA%d/IRQ%d)\n", ++ controller->dma1, get_dma_done_irq(controller->dma1), ++ controller->dma2, get_dma_done_irq(controller->dma2)); ++ ++ old_mksound = kd_mksound; /* see vt.c */ ++ kd_mksound = jz_ac97_mksound; ++ controller->dev_audio = adev; ++ return; ++ ++ dma2_failed: ++ jz_free_dma(controller->dma1); ++ dma1_failed: ++ free_pages((unsigned long)controller->tmp2, 8);//4 ++ tmp2_failed: ++ free_pages((unsigned long)controller->tmp1, 8);//4 ++ tmp1_failed: ++#ifdef CONFIG_PROC_FS ++ jz_ac97_cleanup_proc(controller); ++#endif ++ proc_failed: ++ /* unregister mixer dev */ ++ mixer_failed: ++ unregister_sound_dsp(adev); ++ audio_failed: ++ return; ++} ++ ++static int __init probe_jz_ac97(struct jz_ac97_controller_info **controller) ++{ ++ if ((*controller = kmalloc(sizeof(struct jz_ac97_controller_info), ++ GFP_KERNEL)) == NULL) { ++ printk(KERN_ERR "Jz AC97 Controller: out of memory.\n"); ++ return -ENOMEM; ++ } ++ memset( *controller, 0, sizeof(struct jz_ac97_controller_info) ); ++ (*controller)->name = "Jz AC97 controller"; ++ (*controller)->opened1 = 0; ++ (*controller)->opened2 = 0; ++ ++ init_waitqueue_head(&(*controller)->adc_wait); ++ init_waitqueue_head(&(*controller)->dac_wait); ++ spin_lock_init(&(*controller)->lock); ++ init_waitqueue_head(&rx_wait_queue); ++ init_waitqueue_head(&tx_wait_queue); ++ ++ return 0; ++} ++ ++static void __exit unload_jz_ac97(struct jz_ac97_controller_info *controller) ++{ ++ int adev = controller->dev_audio; ++ ++ jz_ac97_full_reset (controller); ++ ++ controller->dev_audio = -1; ++ ++ if (old_mksound) ++ kd_mksound = old_mksound; /* Our driver support bell for kb, see vt.c */ ++ ++#ifdef CONFIG_PROC_FS ++ jz_ac97_cleanup_proc(controller); ++#endif ++ ++ jz_free_dma(controller->dma1); ++ jz_free_dma(controller->dma2); ++ ++ free_pages((unsigned long)controller->tmp1, 8);//4 ++ free_pages((unsigned long)controller->tmp2, 8);//4 ++ ++ if (adev >= 0) { ++ //unregister_sound_mixer(audio_devs[adev]->mixer_dev); ++ unregister_sound_dsp(controller->dev_audio); ++ } ++} ++ ++#ifdef CONFIG_PM ++ ++static int reserve_mastervol, reserve_micvol; ++static int reserve_power1, reserve_power2; ++static int reserve_power3, reserve_power4; ++static int reserve_power5, reserve_power6; ++ ++static int jz_ac97_suspend(struct jz_ac97_controller_info *controller, int state) ++{ ++ struct ac97_codec *codec = controller->ac97_codec[0]; ++ ++ /* save codec states */ ++ reserve_mastervol = ac97_codec_read(codec, 0x0002); ++ reserve_micvol = ac97_codec_read(codec, 0x000e); ++ ++ reserve_power1 = ac97_codec_read(codec, 0x0026); ++ reserve_power2 = ac97_codec_read(codec, 0x006c); ++ reserve_power3 = ac97_codec_read(codec, 0x005c); ++ reserve_power4 = ac97_codec_read(codec, 0x0064); ++ reserve_power5 = ac97_codec_read(codec, 0x0066); ++ reserve_power6 = ac97_codec_read(codec, 0x006a); ++ ++ /* put codec into power-saving mode */ ++ ac97_codec_write(codec, 0x5c, 0xffff); ++ ac97_codec_write(codec, 0x64, 0x0000); ++ ac97_codec_write(codec, 0x66, 0x0000); ++ ac97_codec_write(codec, 0x6a, 0x0000); ++ ++ ac97_codec_write(codec, 0x6c, 0x0030); ++ ac97_codec_write(codec, 0x26, 0x3b00); ++ ++ ac97_save_state(codec); ++ __ac97_disable(); ++ ++ return 0; ++} ++ ++static int jz_ac97_resume(struct jz_ac97_controller_info *controller) ++{ ++ struct ac97_codec *codec = controller->ac97_codec[0]; ++ ++ jz_ac97_full_reset(controller); ++ ac97_probe_codec(codec); ++ ac97_restore_state(codec); ++ ++ ac97_codec_write(codec, 0x0026, reserve_power1); ++ ac97_codec_write(codec, 0x006c, reserve_power2); ++ ac97_codec_write(codec, 0x005c, reserve_power3); ++ ac97_codec_write(codec, 0x0064, reserve_power4); ++ ac97_codec_write(codec, 0x0066, reserve_power5); ++ ac97_codec_write(codec, 0x006a, reserve_power6); ++ ++ ac97_codec_write(codec, 0x0002, reserve_mastervol); ++ ac97_codec_write(codec, 0x000e, reserve_micvol); ++ ++ /* Enable variable rate mode */ ++ ac97_codec_write(codec, AC97_EXTENDED_STATUS, 9); ++ ac97_codec_write(codec, ++ AC97_EXTENDED_STATUS, ++ ac97_codec_read(codec, AC97_EXTENDED_STATUS) | 0xE800); ++ ++ return 0; ++} ++ ++static int jz_ac97_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) ++{ ++ int ret; ++ struct jz_ac97_controller_info *controller = pm_dev->data; ++ ++ if (!controller) return -EINVAL; ++ ++ switch (req) { ++ case PM_SUSPEND: ++ ret = jz_ac97_suspend(controller, (int)data); ++ break; ++ ++ case PM_RESUME: ++ ret = jz_ac97_resume(controller); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++#endif /* CONFIG_PM */ ++ ++static int __init init_jz_ac97(void) ++{ ++ int errno; ++ ++ if ((errno = probe_jz_ac97(&ac97_controller)) < 0) ++ return errno; ++ attach_jz_ac97(ac97_controller); ++ ++ out_empty_queue.id = NULL; ++ out_full_queue.id = NULL; ++ out_busy_queue.id = NULL; ++ ++ in_empty_queue.id = NULL; ++ in_full_queue.id = NULL; ++ in_busy_queue.id = NULL; ++ ++#ifdef CONFIG_PM ++ ac97_controller->pm = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ++ jz_ac97_pm_callback); ++ if (ac97_controller->pm) ++ ac97_controller->pm->data = ac97_controller; ++#endif ++ ++ return 0; ++} ++ ++static void __exit cleanup_jz_ac97(void) ++{ ++ unload_jz_ac97(ac97_controller); ++} ++ ++module_init(init_jz_ac97); ++module_exit(cleanup_jz_ac97); ++ ++ ++static int drain_adc(struct jz_ac97_controller_info *ctrl, int nonblock) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ unsigned long flags; ++ int count; ++ unsigned tmo; ++ ++ add_wait_queue(&ctrl->adc_wait, &wait); ++ for (;;) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma2); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ ++ if (signal_pending(current)) ++ break; ++ if (nonblock) { ++ remove_wait_queue(&ctrl->adc_wait, &wait); ++ current->state = TASK_RUNNING; ++ return -EBUSY; ++ } ++ tmo = (PAGE_SIZE * HZ) / STANDARD_SPEED; ++ tmo *= jz_audio_k * (jz_audio_format == AFMT_S16_LE) ? 2 : 4; ++ tmo /= jz_audio_channels; ++ } ++ remove_wait_queue(&ctrl->adc_wait, &wait); ++ current->state = TASK_RUNNING; ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ return 0; ++} ++ ++static int drain_dac(struct jz_ac97_controller_info *ctrl, int nonblock) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ unsigned long flags; ++ int count; ++ unsigned tmo; ++ ++ add_wait_queue(&(ctrl->dac_wait), &wait); ++ for (;;) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0 && elements_in_queue(&out_full_queue) <= 0) ++ break; ++ if (signal_pending(current)) ++ break; ++ if (nonblock) { ++ remove_wait_queue(&ctrl->dac_wait, &wait); ++ current->state = TASK_RUNNING; ++ return -EBUSY; ++ } ++ tmo = (PAGE_SIZE * HZ) / STANDARD_SPEED; ++ tmo *= jz_audio_k * (jz_audio_format == AFMT_S16_LE) ? 2 : 4; ++ tmo /= jz_audio_channels; ++ } ++ remove_wait_queue(&ctrl->dac_wait, &wait); ++ current->state = TASK_RUNNING; ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ return 0; ++} ++ ++/* ++ * Audio operation routines implementation ++ */ ++static int jz_audio_release(struct inode *inode, struct file *file) ++{ ++ struct jz_ac97_controller_info *controller = ++ (struct jz_ac97_controller_info *) file->private_data; ++ unsigned long flags; ++ ++ if (file->f_mode & FMODE_WRITE) { ++ controller->opened1 = 0; ++ drain_dac(controller, file->f_flags & O_NONBLOCK); ++ disable_dma(controller->dma1); ++ set_dma_count(controller->dma1, 0); ++ __ac97_disable_transmit_dma(); ++ __ac97_disable_replay(); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ } ++ if (file->f_mode & FMODE_READ) { ++ controller->opened2 = 0; ++ first_record_call = 1; ++ drain_adc(controller, file->f_flags & O_NONBLOCK); ++ disable_dma(controller->dma2); ++ set_dma_count(controller->dma2, 0); ++ __ac97_disable_receive_dma(); ++ __ac97_disable_record(); ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ } ++ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ return 0; ++} ++ ++static int jz_audio_open(struct inode *inode, struct file *file) ++{ ++ struct jz_ac97_controller_info *controller= ac97_controller; ++ ++ if (controller == NULL) ++ return -ENODEV; ++ ++ if (file->f_mode & FMODE_WRITE) { ++ if (controller->opened1 == 1) ++ return -EBUSY; ++ controller->opened1 = 1; ++ //for ioctl ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ jz_audio_set_channels(controller->dev_audio, 1); ++ jz_audio_set_format(controller->dev_audio, AFMT_U8); ++ jz_audio_set_speed(controller->dev_audio, 8000); ++ } ++ ++ if (file->f_mode & FMODE_READ) { ++ if (controller->opened2 == 1) ++ return -EBUSY; ++ controller->opened2 = 1; ++ first_record_call = 1; ++ printk(KERN_DEBUG "You'd better apply 48000Hz 16-bit Stereo for record.\n"); ++ //for ioctl ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ jz_audio_set_channels(controller->dev_audio, 2); ++ jz_audio_set_format(controller->dev_audio, AFMT_S16_LE); ++ jz_audio_set_speed(controller->dev_audio, 48000); ++ } ++ ++ last_jz_audio_count = jz_audio_count = 0; ++ file->private_data = controller; ++ ++ jz_audio_fragsize = 8 * PAGE_SIZE; ++ jz_audio_fragstotal = 4; ++ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ return 0; ++} ++ ++static int jz_audio_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct jz_ac97_controller_info *controller = ++ (struct jz_ac97_controller_info *) file->private_data; ++ int val=0,fullc,busyc,unfinish; ++ unsigned int flags; ++ count_info cinfo; ++ ++ switch (cmd) ++ { ++ case OSS_GETVERSION: ++ return put_user(SOUND_VERSION, (int *)arg); ++ ++ case SNDCTL_DSP_RESET: ++ jz_audio_reset(); ++ return 0; ++ ++ case SNDCTL_DSP_SYNC: ++ if (file->f_mode & FMODE_WRITE) ++ return drain_dac(controller, file->f_flags & O_NONBLOCK); ++ return 0; ++ ++ case SNDCTL_DSP_SPEED: /* set smaple rate */ ++ { ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ if (val >= 0) ++ jz_audio_set_speed(controller->dev_audio, val); ++ return put_user(val, (int *)arg); ++ } ++ ++ case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ jz_audio_set_channels(controller->dev_audio, val ? 2 : 1); ++ return 0; ++ ++ case SNDCTL_DSP_GETBLKSIZE: ++ //return put_user(4*PAGE_SIZE, (int *)arg); ++ return put_user(jz_audio_fragsize , (int *)arg); ++ ++ case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ ++ return put_user(AFMT_U8 | AFMT_S16_LE, (int *)arg); ++ ++ case SNDCTL_DSP_SETFMT: /* Select sample format */ ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ ++ if (val != AFMT_QUERY) { ++ jz_audio_set_format(controller->dev_audio,val); ++ } else { ++ if (file->f_mode & FMODE_READ) ++ val = (jz_audio_format == 16) ? ++ AFMT_S16_LE : AFMT_U8; ++ else ++ val = (jz_audio_format == 16) ? ++ AFMT_S16_LE : AFMT_U8; ++ } ++ return put_user(val, (int *)arg); ++ ++ case SNDCTL_DSP_CHANNELS: ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ jz_audio_set_channels(controller->dev_audio, val); ++ return put_user(val, (int *)arg); ++ ++ case SNDCTL_DSP_POST: ++ /* FIXME: the same as RESET ?? */ ++ return 0; ++ ++ case SNDCTL_DSP_SUBDIVIDE: ++ return 0; ++ ++ case SNDCTL_DSP_SETFRAGMENT: ++ if(out_dma_buf || in_dma_buf) ++ return -EBUSY; ++ get_user(val, (long *) arg); ++ jz_audio_fragsize = 1 << (val & 0xFFFF);//16 least bits ++ ++ if (jz_audio_fragsize < 4 * PAGE_SIZE) ++ jz_audio_fragsize = 4 * PAGE_SIZE; ++ if (jz_audio_fragsize > (16 * PAGE_SIZE)) //16 PAGE_SIZE ++ jz_audio_fragsize = 16 * PAGE_SIZE; ++ jz_audio_fragstotal = (val >> 16) & 0x7FFF; ++ if (jz_audio_fragstotal < 2) ++ jz_audio_fragstotal = 2; ++ if (jz_audio_fragstotal > 32) ++ jz_audio_fragstotal = 32; ++ ++ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ return 0; ++ ++ case SNDCTL_DSP_GETCAPS: ++ return put_user(DSP_CAP_REALTIME|DSP_CAP_BATCH, (int *)arg); ++ ++ case SNDCTL_DSP_NONBLOCK: ++ file->f_flags |= O_NONBLOCK; ++ return 0; ++ ++ case SNDCTL_DSP_SETDUPLEX: ++ return -EINVAL; ++ ++ case SNDCTL_DSP_GETOSPACE: ++ { ++ audio_buf_info abinfo; ++ int i, bytes = 0; ++ ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ //unused fragment amount ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ jz_audio_fragments = elements_in_queue(&out_empty_queue); ++ for (i = 0; i < jz_audio_fragments; i++) ++ bytes += jz_audio_fragsize; ++ bytes /= jz_audio_k; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ //bytes /= jz_audio_b; ++ abinfo.fragments = jz_audio_fragments; ++ abinfo.fragstotal = jz_audio_fragstotal; ++ abinfo.fragsize = jz_audio_fragsize; ++ abinfo.bytes = bytes; ++ return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; ++ } ++ ++ case SNDCTL_DSP_GETISPACE: ++ { ++ audio_buf_info abinfo; ++ int i, bytes = 0; ++ ++ if (!(file->f_mode & FMODE_READ)) ++ return -EINVAL; ++ //unused fragment amount ++ jz_audio_fragments = elements_in_queue(&in_empty_queue); ++ for (i = 0; i < jz_audio_fragments; i++) ++ bytes += jz_audio_fragsize; ++ ++ abinfo.fragments = jz_audio_fragments; ++ abinfo.fragstotal = jz_audio_fragstotal; ++ abinfo.fragsize = jz_audio_fragsize; ++ abinfo.bytes = bytes; ++ return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; ++ } ++ case SNDCTL_DSP_GETTRIGGER: ++ val = 0; ++ if (file->f_mode & FMODE_READ && in_dma_buf) //record is at working ++ val |= PCM_ENABLE_INPUT; ++ if (file->f_mode & FMODE_WRITE && out_dma_buf) //playback is at working ++ val |= PCM_ENABLE_OUTPUT; ++ return put_user(val, (int *)arg); ++ ++ case SNDCTL_DSP_SETTRIGGER: ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ /*if (file->f_mode & FMODE_READ) { ++ if (val & PCM_ENABLE_INPUT) { ++ if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) ++ return ret; ++ start_adc(state); ++ } else ++ stop_adc(state); ++ } ++ if (file->f_mode & FMODE_WRITE) { ++ if (val & PCM_ENABLE_OUTPUT) { ++ if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) ++ return ret; ++ start_dac(state); ++ } else ++ stop_dac(state); ++ }*/ ++ return 0; ++ ++ case SNDCTL_DSP_GETIPTR: ++ if (!(file->f_mode & FMODE_READ)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ //controller->total_bytes += get_dma_residue(controller->dma2); ++ cinfo.bytes = controller->total_bytes; ++ cinfo.blocks = controller->blocks; ++ cinfo.ptr = controller->nextIn; ++ controller->blocks = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); ++ ++ case SNDCTL_DSP_GETOPTR: ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ //controller->total_bytes += get_dma_residue(controller->dma1); ++ cinfo.bytes = controller->total_bytes; ++ cinfo.blocks = controller->blocks; ++ cinfo.ptr = controller->nextOut; ++ controller->blocks = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); ++ ++ case SNDCTL_DSP_GETODELAY: ++ { ++ int id, i; ++ ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ unfinish = 0; ++ fullc = elements_in_queue(&out_full_queue); ++ busyc = elements_in_queue(&out_busy_queue); ++ ++ for(i = 0;i < fullc ;i ++) ++ { ++ id = *(out_full_queue.id + i); ++ unfinish += *(out_dma_buf_data_count + id); ++ } ++ for(i = 0;i < busyc ;i ++) ++ { ++ id = *(out_busy_queue.id + i); ++ unfinish += get_dma_residue(controller->dma1); ++ } ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ unfinish /= jz_audio_k;//jz_audio_k is jz_audio_b ++ return put_user(val, (int *)arg); ++ } ++ case SOUND_PCM_READ_RATE: ++ return put_user(jz_audio_rate, (int *)arg); ++ case SOUND_PCM_READ_CHANNELS: ++ return put_user(jz_audio_channels, (int *)arg); ++ case SOUND_PCM_READ_BITS: ++ return put_user((jz_audio_format & (AFMT_S8 | AFMT_U8)) ? 8 : 16, (int *)arg); ++ case SNDCTL_DSP_MAPINBUF: ++ case SNDCTL_DSP_MAPOUTBUF: ++ case SNDCTL_DSP_SETSYNCRO: ++ case SOUND_PCM_WRITE_FILTER: ++ case SOUND_PCM_READ_FILTER: ++ return -EINVAL; ++ } ++ return -EINVAL; ++} ++ ++static unsigned int jz_audio_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ struct jz_ac97_controller_info *controller = ++ (struct jz_ac97_controller_info *) file->private_data; ++ unsigned long flags; ++ unsigned int mask = 0; ++ ++ if (file->f_mode & FMODE_WRITE) { ++ if (elements_in_queue(&out_empty_queue) > 0) ++ return POLLOUT | POLLWRNORM; ++ poll_wait(file, &controller->dac_wait, wait); ++ } ++ if (file->f_mode & FMODE_READ) { ++ if (elements_in_queue(&in_full_queue) > 0) ++ return POLLIN | POLLRDNORM; ++ poll_wait(file, &controller->adc_wait, wait); ++ } ++ spin_lock_irqsave(&controller->lock, flags); ++ if (file->f_mode & FMODE_WRITE) { ++ if (elements_in_queue(&out_empty_queue) > 0) ++ mask |= POLLOUT | POLLWRNORM; ++ } ++ else if (file->f_mode & FMODE_READ) { ++ if (elements_in_queue(&in_full_queue) > 0) ++ mask |= POLLIN | POLLRDNORM; ++ } ++ spin_unlock_irqrestore(&controller->lock, flags); ++ return mask; ++} ++ ++static ssize_t jz_audio_read(struct file *file, char *buffer, ++ size_t count, loff_t *ppos) ++{ ++ struct jz_ac97_controller_info *controller = ++ (struct jz_ac97_controller_info *) file->private_data; ++ int id, ret = 0, left_count, copy_count, cnt = 0; ++ unsigned long flags; ++ ++ if (count < 0) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextIn = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ ++ if (count < 2*PAGE_SIZE / jz_audio_k) { ++ copy_count = count * 16 / (jz_audio_channels * jz_audio_format); ++ } else ++ copy_count = ((2*PAGE_SIZE / jz_audio_k + 3) / 4) * 4; ++ left_count = count; ++ ++ if (first_record_call) { ++ first_record_call = 0; ++ if ((id = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id); ++ *(in_dma_buf_data_count + id) = copy_count * (jz_audio_format/8); ++ __ac97_enable_receive_dma(); ++ __ac97_enable_record(); ++ audio_start_dma(controller->dma2,file->private_data, ++ *(in_dma_pbuf + id), ++ *(in_dma_buf_data_count + id), ++ DMA_MODE_READ); ++ interruptible_sleep_on(&rx_wait_queue); ++ } else ++ BUG(); ++ } ++ ++ while (left_count > 0) { ++ if (elements_in_queue(&in_full_queue) <= 0) { ++ if (file->f_flags & O_NONBLOCK) ++ return ret ? ret : -EAGAIN; ++ else ++ interruptible_sleep_on(&rx_wait_queue); ++ } ++ if (signal_pending(current)) ++ return ret ? ret: -ERESTARTSYS; ++ ++ if ((id = get_buffer_id(&in_full_queue)) >= 0) { ++ cnt = record_filler((unsigned long)controller->tmp2+ret, copy_count, id); ++ put_buffer_id(&in_empty_queue, id); ++ } else ++ BUG(); ++ ++ if (elements_in_queue(&in_busy_queue) == 0) { ++ if ((id=get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id); ++ *(in_dma_buf_data_count + id) = copy_count * (jz_audio_format/8); ++ __ac97_enable_receive_dma(); ++ __ac97_enable_record(); ++ audio_start_dma(controller->dma2,file->private_data, ++ *(in_dma_pbuf + id), ++ *(in_dma_buf_data_count + id), ++ DMA_MODE_READ); ++ } ++ } ++ ++ if (ret + cnt > count) ++ cnt = count - ret; ++ ++ if (copy_to_user(buffer+ret, controller->tmp2+ret, cnt)) ++ return ret ? ret : -EFAULT; ++ ++ ret += cnt; ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextIn += ret; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ left_count -= cnt; ++ } ++ if (ret != count) ++ printk(KERN_DEBUG "%s: count=%d ret=%d jz_audio_count=%d\n", ++ __FUNCTION__, count, ret, jz_audio_count); ++ return ret; ++} ++ ++static ssize_t jz_audio_write(struct file *file, const char *buffer, ++ size_t count, loff_t *ppos) ++{ ++ struct jz_ac97_controller_info *controller = ++ (struct jz_ac97_controller_info *) file->private_data; ++ int id, ret = 0, left_count, copy_count; ++ unsigned int flags; ++ ++ if (count <= 0) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextOut = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ ++ /* The data buffer size of the user space is always a PAGE_SIZE ++ * scale, so the process can be simplified. ++ */ ++ ++ if ((ac97_controller->patched) && (ac97_controller->ac97_features & 1)) ++ copy_count = count; ++ else { ++ if (count < 2*PAGE_SIZE / jz_audio_k) ++ copy_count = count; ++ else ++ copy_count = ((2*PAGE_SIZE / jz_audio_k + 3) / 4) * 4; ++ } ++ left_count = count; ++ ++ if (!(ac97_controller->patched) || !(ac97_controller->ac97_features & 1)) { ++ if (copy_from_user(controller->tmp1, buffer, count)) { ++ printk(KERN_DEBUG "%s: copy_from_user failed.\n", __FUNCTION__); ++ return ret ? ret : -EFAULT; ++ } ++ } ++ ++ while (left_count > 0) { ++ if (elements_in_queue(&out_empty_queue) == 0) { ++ if (file->f_flags & O_NONBLOCK) ++ return ret; ++ else ++ interruptible_sleep_on(&tx_wait_queue); ++ } ++ ++ if (signal_pending(current)) ++ return ret ? ret : -ERESTARTSYS; ++ ++ /* the end fragment size in this write */ ++ if (ret + copy_count > count) ++ copy_count = count - ret; ++ ++ if ((id = get_buffer_id(&out_empty_queue)) >= 0) { ++ if ((ac97_controller->patched) && ++ (ac97_controller->ac97_features & 1)) { ++ if (copy_from_user((char *)(*(out_dma_buf + id)), buffer+ret, copy_count)) { ++ printk(KERN_DEBUG "%s: copy_from_user failed.\n", __FUNCTION__); ++ return ret ? ret : -EFAULT; ++ } ++ *(out_dma_buf_data_count + id) = copy_count; ++ } else ++ replay_filler( ++ (unsigned long)controller->tmp1 + ret, ++ copy_count, id); ++ //when 0,kernel will panic ++ if(*(out_dma_buf_data_count + id) > 0) { ++ put_buffer_id(&out_full_queue, id); ++ dma_cache_wback_inv(*(out_dma_buf + id), *(out_dma_buf_data_count + id)); ++ } else {//when 0,i need refill in empty queue ++ put_buffer_id(&out_empty_queue, id); ++ } ++ } else ++ BUG(); ++ left_count = left_count - copy_count; ++ ret += copy_count; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextOut += ret; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ if (elements_in_queue(&out_busy_queue) == 0) { ++ if ((id=get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_busy_queue, id); ++ ++ __ac97_enable_transmit_dma(); ++ __ac97_enable_replay(); ++ if(*(out_dma_buf_data_count + id) > 0) { ++ audio_start_dma(controller->dma1,file->private_data, ++ *(out_dma_pbuf + id), ++ *(out_dma_buf_data_count + id), ++ DMA_MODE_WRITE); ++ } ++ } ++ } ++ } ++ ++ if (ret != count) ++ printk(KERN_DEBUG "%s: count=%d ret=%d jz_audio_count=%d\n", ++ __FUNCTION__, count, ret, jz_audio_count); ++ return ret; ++} ++ ++/* this function for the other codec function */ ++struct ac97_codec * find_ac97_codec(void) ++{ ++ if ( ac97_controller ) ++ return ac97_controller->ac97_codec[0]; ++ return 0; ++ ++} ++ +diff -urN linux-2.6.24.7.old/sound/oss/jz_i2s.c linux-2.6.24.7/sound/oss/jz_i2s.c +--- linux-2.6.24.7.old/sound/oss/jz_i2s.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/oss/jz_i2s.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,2894 @@ ++/* ++ * linux/drivers/sound/Jz_i2s.c ++ * ++ * JzSOC On-Chip I2S audio driver. ++ * ++ * Copyright (C) 2005 by Junzheng Corp. ++ * Modified by cjfeng on Aug 9,2007,and not any bug on Jz4730 using ++ * dma channel 4&3,noah is tested. ++ * ++ * 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. ++ * ++ * Because the normal application of AUDIO devices are focused on Little_endian, ++ * then we only perform the little endian data format in driver. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "sound_config.h" ++ ++#if defined(CONFIG_I2S_DLV) ++#include "jzdlv.h" ++#endif ++ ++#define DPRINTK(args...) printk(args) ++#define DMA_ID_I2S_TX DMA_ID_AIC_TX ++#define DMA_ID_I2S_RX DMA_ID_AIC_RX ++#define NR_I2S 2 ++#define JZCODEC_RW_BUFFER_SIZE 5 ++#define JZCODEC_RW_BUFFER_TOTAL 4 ++ ++#define USE_NONE 1 ++#define USE_MIC 2 ++#define USE_LINEIN 3 ++ ++typedef struct hpvol_shift_s ++{ ++ int hpvol; ++ int shift; ++} hpvol_shift_t; ++ ++mixer_info info; ++_old_mixer_info old_info; ++int codec_volue_shift; ++hpvol_shift_t hpvol_shift_table[72]; ++int abnormal_data_count; ++unsigned long i2s_clk; ++ ++void (*set_codec_mode)(void) = NULL; ++void (*clear_codec_mode)(void) = NULL; ++void (*set_codec_gpio_pin)(void) = NULL; ++void (*each_time_init_codec)(void) = NULL; ++int (*set_codec_startup_param)(void) = NULL; ++void (*set_codec_volume_table)(void) = NULL; ++void (*set_codec_record)(int mode) = NULL; ++void (*set_codec_replay)(void) = NULL; ++void (*set_codec_replay_record)(int mode) = NULL; ++void (*turn_on_codec)(void) = NULL; ++void (*turn_off_codec)(void) = NULL; ++void (*set_codec_speed)(int rate) = NULL; ++void (*reset_codec)(void) = NULL; ++void (*codec_mixer_old_info_id_name)(void) = NULL; ++void (*codec_mixer_info_id_name)(void) = NULL; ++void (*set_codec_bass)(int val) = NULL; ++void (*set_codec_volume)(int val) = NULL; ++void (*set_codec_mic)(int val) = NULL; ++void (*set_codec_line)(int val) = NULL; ++void (*i2s_resume_codec)(void) = NULL; ++void (*i2s_suspend_codec)(int wr,int rd) = NULL; ++void (*init_codec_pin)(void) = NULL; ++void (*set_codec_some_func)(void) = NULL; ++void (*clear_codec_record)(void) = NULL; ++void (*clear_codec_replay)(void) = NULL; ++void (*set_replay_hp_or_speaker)(void) = NULL; ++void (*set_codec_direct_mode)(void) = NULL; ++void (*clear_codec_direct_mode)(void) = NULL; ++void (*set_codec_linein2hp)(void) = NULL; ++void (*clear_codec_linein2hp)(void) = NULL; ++ ++static int jz_audio_rate; ++static int jz_audio_format; ++static int jz_audio_volume; ++static int jz_audio_channels; ++static int jz_audio_b; /* bits expand multiple */ ++static int jz_audio_fragments; /* unused fragment amount */ ++static int jz_audio_fragstotal; ++static int jz_audio_fragsize; ++static int jz_audio_speed; ++ ++static int codec_bass_gain; ++static int audio_mix_modcnt; ++static int jz_audio_dma_tran_count; /* bytes count of one DMA transfer */ ++#if defined(CONFIG_I2S_DLV) ++int jz_mic_only = 1; ++static int jz_codec_config = 0; ++static unsigned long ramp_up_start; ++static unsigned long ramp_up_end; ++static unsigned long gain_up_start; ++static unsigned long gain_up_end; ++static unsigned long ramp_down_start; ++static unsigned long ramp_down_end; ++static unsigned long gain_down_start; ++static unsigned long gain_down_end; ++#endif ++ ++static int codec_mic_gain; ++static int pop_dma_flag; ++static int last_dma_buffer_id; ++static int drain_flag; ++static int use_mic_line_flag; ++ ++static void (*old_mksound)(unsigned int hz, unsigned int ticks); ++extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); ++static void jz_update_filler(int bits, int channels); ++ ++static int Init_In_Out_queue(int fragstotal,int fragsize); ++static int Free_In_Out_queue(int fragstotal,int fragsize); ++static irqreturn_t jz_i2s_replay_dma_irq(int irqnr, void *ref); ++static irqreturn_t jz_i2s_record_dma_irq(int irqnr, void *ref); ++static void (*replay_filler)(signed long src_start, int count, int id); ++static int (*record_filler)(unsigned long dst_start, int count, int id); ++#if defined(CONFIG_I2S_ICODEC) ++static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample); ++#endif ++static void jz_audio_reset(void); ++static struct file_operations jz_i2s_audio_fops; ++ ++static DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue); ++static DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue); ++static DECLARE_WAIT_QUEUE_HEAD (drain_wait_queue); ++static DECLARE_WAIT_QUEUE_HEAD (pop_wait_queue); ++ ++struct jz_i2s_controller_info ++{ ++ int io_base; ++ int dma1; /* for play */ ++ int dma2; /* for record */ ++ char *name; ++ int dev_audio; ++ struct i2s_codec *i2s_codec[NR_I2S]; ++ int opened1; ++ int opened2; ++ unsigned char *tmp1; /* tmp buffer for sample conversions */ ++ unsigned char *tmp2; ++ spinlock_t lock; ++ spinlock_t ioctllock; ++ ++ wait_queue_head_t dac_wait; ++ wait_queue_head_t adc_wait; ++ int nextIn; /* byte index to next-in to DMA buffer */ ++ int nextOut; /* byte index to next-out from DMA buffer */ ++ int count; /* current byte count in DMA buffer */ ++ int finish; /* current transfered byte count in DMA buffer */ ++ unsigned total_bytes; /* total bytes written or read */ ++ unsigned blocks; ++ unsigned error; /* over/underrun */ ++#ifdef CONFIG_PM ++ struct pm_dev *pm; ++#endif ++}; ++ ++ ++static struct jz_i2s_controller_info *i2s_controller = NULL; ++struct i2s_codec ++{ ++ /* I2S controller connected with */ ++ void *private_data; ++ char *name; ++ int id; ++ int dev_mixer; ++ /* controller specific lower leverl i2s accessing routines */ ++ u16 (*codec_read) (u8 reg); /* the function accessing Codec REGs */ ++ void (*codec_write) (u8 reg, u16 val); ++ /* Wait for codec-ready */ ++ void (*codec_wait) (struct i2s_codec *codec); ++ /* OSS mixer masks */ ++ int modcnt; ++ int supported_mixers; ++ int stereo_mixers; ++ int record_sources; ++ int bit_resolution; ++ /* OSS mixer interface */ ++ int (*read_mixer) (struct i2s_codec *codec, int oss_channel); ++ void (*write_mixer)(struct i2s_codec *codec, int oss_channel, ++ unsigned int left, unsigned int right); ++ int (*recmask_io) (struct i2s_codec *codec, int rw, int mask); ++ int (*mixer_ioctl)(struct i2s_codec *codec, unsigned int cmd, unsigned long arg); ++ /* saved OSS mixer states */ ++ unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; ++}; ++ ++ ++typedef struct buffer_queue_s ++{ ++ int count; ++ int *id; ++ int lock; ++} buffer_queue_t; ++ ++typedef struct left_right_sample_s ++{ ++ signed long left; ++ signed long right; ++} left_right_sample_t; ++ ++static unsigned long pop_turn_onoff_buf; ++static unsigned long pop_turn_onoff_pbuf; ++ ++static unsigned long *out_dma_buf = NULL; ++static unsigned long *out_dma_pbuf = NULL; ++static unsigned long *out_dma_buf_data_count = NULL; ++static unsigned long *in_dma_buf = NULL; ++static unsigned long *in_dma_pbuf = NULL; ++static unsigned long *in_dma_buf_data_count = NULL; ++ ++static buffer_queue_t out_empty_queue; ++static buffer_queue_t out_full_queue; ++static buffer_queue_t out_busy_queue; ++static buffer_queue_t in_empty_queue; ++static buffer_queue_t in_full_queue; ++static buffer_queue_t in_busy_queue; ++static int first_record_call = 0; ++ ++static left_right_sample_t save_last_samples[64]; ++ ++static inline int get_buffer_id(struct buffer_queue_s *q) ++{ ++ int r; ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&q->lock, flags); ++ if (q->count == 0) { ++ spin_unlock_irqrestore(&q->lock, flags); ++ return -1; ++ } ++ r = *(q->id + 0); ++ for (i=0;i < q->count-1;i++) ++ *(q->id + i) = *(q->id + (i+1)); ++ q->count --; ++ spin_unlock_irqrestore(&q->lock, flags); ++ ++ return r; ++} ++ ++static inline void put_buffer_id(struct buffer_queue_s *q, int id) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&q->lock, flags); ++ *(q->id + q->count) = id; ++ q->count ++; ++ spin_unlock_irqrestore(&q->lock, flags); ++} ++ ++static inline int elements_in_queue(struct buffer_queue_s *q) ++{ ++ int r; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&q->lock, flags); ++ r = q->count; ++ spin_unlock_irqrestore(&q->lock, flags); ++ ++ return r; ++} ++ ++static inline void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode) ++{ ++ unsigned long flags; ++ struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ jz_audio_dma_tran_count = count / jz_audio_b; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ flags = claim_dma_lock(); ++ disable_dma(chan); ++ clear_dma_ff(chan); ++#if 1 ++ jz_set_oss_dma(chan, mode, jz_audio_format); ++#else ++ set_dma_mode(chan, mode); ++#endif ++ set_dma_addr(chan, phyaddr); ++ if (count == 0) { ++ count++; ++ printk("JzSOC DMA controller can't set dma 0 count!\n"); ++ } ++ set_dma_count(chan, count); ++ enable_dma(chan); ++ release_dma_lock(flags); ++} ++ ++static irqreturn_t jz_i2s_record_dma_irq (int irq, void *dev_id) ++{ ++ int id1, id2; ++ unsigned long flags; ++ struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; ++ int dma = controller->dma2; ++ ++ disable_dma(dma); ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ ++ if(drain_flag == 1) ++ wake_up(&drain_wait_queue); ++ /* for DSP_GETIPTR */ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ id1 = get_buffer_id(&in_busy_queue); ++ put_buffer_id(&in_full_queue, id1); ++ ++ wake_up(&rx_wait_queue); ++ wake_up(&controller->adc_wait); ++ if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id2); ++ *(in_dma_buf_data_count + id2) = *(in_dma_buf_data_count + id1); ++ dma_cache_wback_inv(*(in_dma_buf + id2), *(in_dma_buf_data_count + id2)); ++ audio_start_dma(dma,dev_id, ++ *(in_dma_pbuf + id2), ++ *(in_dma_buf_data_count + id2), ++ DMA_MODE_READ); ++ } else ++ in_busy_queue.count = 0; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t jz_i2s_replay_dma_irq (int irq, void *dev_id) ++{ ++ int id; ++ unsigned long flags; ++ struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; ++ int dma = controller->dma1; ++ ++ disable_dma(dma); ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ ++ if(pop_dma_flag == 1) { ++ pop_dma_flag = 0; ++ wake_up(&pop_wait_queue); ++ } else { ++ if(drain_flag == 1) { ++ /* Is replay dma buffer over ? */ ++ if(elements_in_queue(&out_full_queue) <= 0) { ++ drain_flag = 0; ++ wake_up(&drain_wait_queue); ++ } ++ } ++ ++ /* for DSP_GETOPTR */ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ if ((id = get_buffer_id(&out_busy_queue)) < 0) ++ printk(KERN_DEBUG "Strange DMA finish interrupt for I2S module\n"); ++ put_buffer_id(&out_empty_queue, id); ++ if ((id = get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_busy_queue, id); ++ if(*(out_dma_buf_data_count + id) > 0) { ++ audio_start_dma(dma, dev_id, *(out_dma_pbuf + id), ++ *(out_dma_buf_data_count + id), ++ DMA_MODE_WRITE); ++ last_dma_buffer_id = id; ++ } ++ } else ++ out_busy_queue.count = 0; ++ ++ if (elements_in_queue(&out_empty_queue) > 0) { ++ wake_up(&tx_wait_queue); ++ wake_up(&controller->dac_wait); ++ } ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void jz_i2s_initHw(int set) ++{ ++#if defined(CONFIG_MIPS_JZ_URANUS) ++ i2s_clk = 48000000; ++#else ++ i2s_clk = __cpm_get_i2sclk(); ++#endif ++ __i2s_disable(); ++ if(set) ++ __i2s_reset(); ++ schedule_timeout(5); ++ if(each_time_init_codec) ++ each_time_init_codec(); ++ __i2s_disable_record(); ++ __i2s_disable_replay(); ++ __i2s_disable_loopback(); ++ __i2s_set_transmit_trigger(4); ++ __i2s_set_receive_trigger(3); ++} ++ ++static int Init_In_Out_queue(int fragstotal,int fragsize) ++{ ++ int i; ++ ++ /* recording */ ++ in_empty_queue.count = fragstotal; ++ in_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_buf) ++ goto all_mem_err; ++ in_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_pbuf) ++ goto all_mem_err; ++ in_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_buf_data_count) ++ goto all_mem_err; ++ in_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_empty_queue.id) ++ goto all_mem_err; ++ in_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_full_queue.id) ++ goto all_mem_err; ++ in_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_busy_queue.id) ++ goto all_mem_err; ++ ++ for (i=0;i < fragstotal;i++) ++ *(in_empty_queue.id + i) = i; ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ ++ for (i = 0; i < fragstotal; i++) { ++ *(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); ++ if (*(in_dma_buf + i) == 0) ++ goto mem_failed_in; ++ *(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i))); ++ dma_cache_wback_inv(*(in_dma_buf + i), fragsize); ++ } ++ ++ /* playing */ ++ out_empty_queue.count = fragstotal; ++ out_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!out_dma_buf) ++ goto all_mem_err; ++ out_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!out_dma_pbuf) ++ goto all_mem_err; ++ out_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ ++ if (!out_dma_buf_data_count) ++ goto all_mem_err; ++ out_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_empty_queue.id) ++ goto all_mem_err; ++ out_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_full_queue.id) ++ goto all_mem_err; ++ out_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_busy_queue.id) ++ goto all_mem_err; ++ for (i=0;i < fragstotal;i++) ++ *(out_empty_queue.id + i) = i; ++ ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ /* alloc DMA buffer */ ++ for (i = 0; i < fragstotal; i++) { ++ *(out_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); ++ if (*(out_dma_buf + i) == 0) { ++ printk(" can't allocate required DMA(OUT) buffers.\n"); ++ goto mem_failed_out; ++ } ++ *(out_dma_pbuf + i) = virt_to_phys((void *)(*(out_dma_buf + i))); ++ } ++ ++ return 1; ++all_mem_err: ++ printk("error:allocate memory occur error 1!\n"); ++ return 0; ++mem_failed_out: ++ printk("error:allocate memory occur error 2!\n"); ++ for (i = 0; i < fragstotal; i++) { ++ if(*(out_dma_buf + i)) ++ free_pages(*(out_dma_buf + i), get_order(fragsize)); ++ } ++ ++ return 0; ++mem_failed_in: ++ printk("error:allocate memory occur error 3!\n"); ++ for (i = 0; i < fragstotal; i++) { ++ if(*(in_dma_buf + i)) ++ free_pages(*(in_dma_buf + i), get_order(fragsize)); ++ } ++ return 0; ++} ++ ++static int Free_In_Out_queue(int fragstotal,int fragsize) ++{ ++ int i; ++ /* playing */ ++ if(out_dma_buf != NULL) { ++ for (i = 0; i < fragstotal; i++) { ++ if(*(out_dma_buf + i)) ++ free_pages(*(out_dma_buf + i), get_order(fragsize)); ++ *(out_dma_buf + i) = 0; ++ } ++ kfree(out_dma_buf); ++ out_dma_buf = NULL; ++ } ++ if(out_dma_pbuf) { ++ kfree(out_dma_pbuf); ++ out_dma_pbuf = NULL; ++ } ++ if(out_dma_buf_data_count) { ++ kfree(out_dma_buf_data_count); ++ out_dma_buf_data_count = NULL; ++ } ++ if(out_empty_queue.id) { ++ kfree(out_empty_queue.id); ++ out_empty_queue.id = NULL; ++ } ++ if(out_full_queue.id) { ++ kfree(out_full_queue.id); ++ out_full_queue.id = NULL; ++ } ++ if(out_busy_queue.id) { ++ kfree(out_busy_queue.id); ++ out_busy_queue.id = NULL; ++ } ++ out_empty_queue.count = fragstotal; ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ ++ /* recording */ ++ if(in_dma_buf) { ++ for (i = 0; i < fragstotal; i++) { ++ if(*(in_dma_buf + i)) { ++ dma_cache_wback_inv(*(in_dma_buf + i), fragsize); ++ free_pages(*(in_dma_buf + i), get_order(fragsize)); ++ } ++ *(in_dma_buf + i) = 0; ++ } ++ kfree(in_dma_buf); ++ in_dma_buf = NULL; ++ } ++ if(in_dma_pbuf) { ++ kfree(in_dma_pbuf); ++ in_dma_pbuf = NULL; ++ } ++ if(in_dma_buf_data_count) { ++ kfree(in_dma_buf_data_count); ++ in_dma_buf_data_count = NULL; ++ } ++ if(in_empty_queue.id) { ++ kfree(in_empty_queue.id); ++ in_empty_queue.id = NULL; ++ } ++ if(in_full_queue.id) { ++ kfree(in_full_queue.id); ++ in_full_queue.id = NULL; ++ } ++ if(in_busy_queue.id) { ++ kfree(in_busy_queue.id); ++ in_busy_queue.id = NULL; ++ } ++ ++ in_empty_queue.count = fragstotal; ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ ++ return 1; ++} ++ ++static void jz_i2s_full_reset(struct jz_i2s_controller_info *controller) ++{ ++ jz_i2s_initHw(0); ++} ++ ++static int jz_audio_set_speed(int dev, int rate) ++{ ++ /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 ? */ ++ jz_audio_speed = rate; ++#if defined(CONFIG_I2S_DLV) ++ if (rate > 96000) ++ rate = 96000; ++#else ++ if (rate > 48000) ++ rate = 48000; ++#endif ++ if (rate < 8000) ++ rate = 8000; ++ jz_audio_rate = rate; ++ ++ if(set_codec_speed) ++ set_codec_speed(rate); ++ ++ return jz_audio_rate; ++} ++ ++ ++static int record_fill_1x8_u(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long data; ++ volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ volatile unsigned char *dp = (unsigned char*)dst_start; ++ ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ cnt++; ++ data = *(s++); ++ *(dp ++) = ((data << 16) >> 24) + 0x80; ++ s++; /* skip the other channel */ ++ } ++ ++ return cnt; ++} ++ ++ ++static int record_fill_2x8_u(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long d1, d2; ++ volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ volatile unsigned char *dp = (unsigned char*)dst_start; ++ ++ while (count > 0) { ++ count -= 2; ++ cnt += 2; ++ d1 = *(s++); ++ *(dp ++) = ((d1 << 16) >> 24) + 0x80; ++ d2 = *(s++); ++ *(dp ++) = ((d2 << 16) >> 24) + 0x80; ++ } ++ ++ return cnt; ++} ++ ++ ++static int record_fill_1x16_s(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long d1; ++ unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ unsigned short *dp = (unsigned short *)dst_start; ++ ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ cnt += 2; /* count in byte */ ++ d1 = *(s++); ++ *(dp ++) = (d1 << 16) >> 16; ++ s++; /* skip the other channel */ ++ } ++ ++ return cnt; ++} ++ ++ ++static int record_fill_2x16_s(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long d1, d2; ++ unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ unsigned short *dp = (unsigned short *)dst_start; ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ cnt += 4; /* count in byte */ ++ d1 = *(s++); ++ d2 = *(s++); ++ if(abnormal_data_count > 0) { ++ d1 = d2 = 0; ++ abnormal_data_count --; ++ } ++ *(dp ++) = (d1 << 16) >> 16; ++ *(dp ++) = (d2 << 16) >> 16; ++ } ++ ++ return cnt; ++} ++ ++static void replay_fill_1x8_u(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned char data; ++ unsigned long ddata; ++ volatile unsigned char *s = (unsigned char *)src_start; ++ volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); ++ ++ while (count > 0) { ++ count--; ++ cnt += 1; ++ data = *(s++) - 0x80; ++ ddata = (unsigned long) data << 8; ++ *(dp ++) = ddata; ++ *(dp ++) = ddata; ++ ++ /* save last left and right */ ++ if(count == 1) { ++ save_last_samples[id].left = ddata; ++ save_last_samples[id].right = ddata; ++ } ++ } ++ cnt = cnt * 2 * jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++ ++static void replay_fill_2x8_u(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned char d1; ++ unsigned long dd1; ++ volatile unsigned char *s = (unsigned char *)src_start; ++ volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); ++ ++ while (count > 0) { ++ count -= 1; ++ cnt += 1 ; ++ d1 = *(s++) - 0x80; ++ dd1 = (unsigned long) d1 << 8; ++ *(dp ++) = dd1; ++ /* save last left */ ++ if(count == 2) ++ save_last_samples[id].left = dd1; ++ /* save last right */ ++ if(count == 1) ++ save_last_samples[id].right = dd1; ++ } ++ cnt *= jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++ ++static void replay_fill_1x16_s(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ signed short d1; ++ signed long l1; ++ volatile signed short *s = (signed short *)src_start; ++ volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); ++ ++ while (count > 0) { ++ count -= 2; ++ cnt += 2 ; ++ d1 = *(s++); ++ l1 = (signed long)d1; ++#if defined(CONFIG_I2S_ICODEC) ++ l1 >>= codec_volue_shift; ++#endif ++ *(dp ++) = l1; ++ *(dp ++) = l1; ++ ++ /* save last left and right */ ++ if(count == 1) { ++ save_last_samples[id].left = l1; ++ save_last_samples[id].right = l1; ++ } ++ } ++ cnt = cnt * 2 * jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++static void replay_fill_2x16_s(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ signed short d1; ++ signed long l1; ++ volatile signed short *s = (signed short *)src_start; ++ volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); ++#if defined(CONFIG_I2S_ICODEC) ++ int mute_cnt = 0; ++ signed long tmp1,tmp2; ++ volatile signed long *before_dp; ++ int sam_rate = jz_audio_rate / 20; ++ ++ tmp1 = tmp2 = 0; ++ while (count > 0) { ++ count -= 2; ++ cnt += 2; ++ d1 = *(s++); ++ ++ l1 = (signed long)d1; ++ l1 >>= codec_volue_shift; ++ ++ if(l1 == 0) { ++ mute_cnt ++; ++ if(mute_cnt >= sam_rate) { ++ before_dp = dp - 10; ++ *(before_dp) = (signed long)1; ++ before_dp = dp - 11; ++ *(before_dp) = (signed long)1; ++ mute_cnt = 0; ++ } ++ } else ++ mute_cnt = 0; ++ ++ *(dp ++) = l1; ++ ++ tmp1 = tmp2; ++ tmp2 = l1; ++ } ++ ++ /* save last left */ ++ save_last_samples[id].left = tmp1; ++ /* save last right */ ++ save_last_samples[id].right = tmp2; ++#endif ++#if defined(CONFIG_I2S_DLV) ++ while (count > 0) { ++ count -= 2; ++ cnt += 2; ++ d1 = *(s++); ++ ++ l1 = (signed long)d1; ++ ++ *(dp ++) = l1; ++ } ++#endif ++ cnt *= jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++static void replay_fill_2x18_s(unsigned long src_start, int count, int id) ++{ ++ int cnt = 0; ++ signed long d1; ++ signed long l1; ++ volatile signed long *s = (signed long *)src_start; ++ volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); ++ while (count > 0) { ++ count -= 4; ++ cnt += 4; ++ d1 = *(s++); ++ l1 = (signed long)d1; ++ *(dp ++) = l1; ++ } ++ ++ cnt *= jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++static unsigned int jz_audio_set_format(int dev, unsigned int fmt) ++{ ++ switch (fmt) { ++ case AFMT_U8: ++ __i2s_set_oss_sample_size(8); ++ __i2s_set_iss_sample_size(8); ++ jz_audio_format = fmt; ++ jz_update_filler(jz_audio_format,jz_audio_channels); ++ break; ++ case AFMT_S16_LE: ++#if defined(CONFIG_I2S_DLV) ++ /* DAC path and ADC path */ ++ write_codec_file(2, 0x00); ++ //write_codec_file(2, 0x60); ++#endif ++ jz_audio_format = fmt; ++ jz_update_filler(jz_audio_format,jz_audio_channels); ++ __i2s_set_oss_sample_size(16); ++ __i2s_set_iss_sample_size(16); ++ break; ++ case 18: ++ __i2s_set_oss_sample_size(18); ++ jz_audio_format = fmt; ++ jz_update_filler(jz_audio_format,jz_audio_channels); ++ break; ++ case AFMT_QUERY: ++ break; ++ } ++ ++ return jz_audio_format; ++} ++ ++ ++static short jz_audio_set_channels(int dev, short channels) ++{ ++ switch (channels) { ++ case 1: ++ if(set_codec_some_func) ++ set_codec_some_func(); ++ jz_audio_channels = channels; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(1, 1, 6);//CR1.MONO->1 for Mono ++#endif ++ break; ++ case 2: ++ jz_audio_channels = channels; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(1, 0, 6);//CR1.MONO->0 for Stereo ++#endif ++ break; ++ case 0: ++ break; ++ } ++ ++ return jz_audio_channels; ++} ++ ++static void init_codec(void) ++{ ++ /* inititalize internal I2S codec */ ++ if(init_codec_pin) ++ init_codec_pin(); ++ ++#if defined(CONFIG_I2S_ICDC) ++ /* initialize AIC but not reset it */ ++ jz_i2s_initHw(0); ++#endif ++ if(reset_codec) ++ reset_codec(); ++} ++ ++static void jz_audio_reset(void) ++{ ++ __i2s_disable_replay(); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_record(); ++ __i2s_disable_transmit_dma(); ++#if defined(CONFIG_I2S_DLV) ++ REG_AIC_I2SCR = 0x10; ++#endif ++ init_codec(); ++} ++ ++static int jz_audio_release(struct inode *inode, struct file *file); ++static int jz_audio_open(struct inode *inode, struct file *file); ++static int jz_audio_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg); ++static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait); ++static ssize_t jz_audio_write(struct file *file, const char *buffer,size_t count, loff_t *ppos); ++static ssize_t jz_audio_read(struct file *file, char *buffer,size_t count, loff_t *ppos); ++ ++/* static struct file_operations jz_i2s_audio_fops */ ++static struct file_operations jz_i2s_audio_fops = ++{ ++ owner: THIS_MODULE, ++ open: jz_audio_open, ++ release: jz_audio_release, ++ write: jz_audio_write, ++ read: jz_audio_read, ++ poll: jz_audio_poll, ++ ioctl: jz_audio_ioctl ++}; ++ ++static int jz_i2s_open_mixdev(struct inode *inode, struct file *file) ++{ ++ int i; ++ int minor = MINOR(inode->i_rdev); ++ struct jz_i2s_controller_info *controller = i2s_controller; ++ ++ for (i = 0; i < NR_I2S; i++) ++ if (controller->i2s_codec[i] != NULL && controller->i2s_codec[i]->dev_mixer == minor) ++ goto match; ++ ++ if (!controller) ++ return -ENODEV; ++match: ++ file->private_data = controller->i2s_codec[i]; ++ ++ return 0; ++} ++ ++static int jz_i2s_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct i2s_codec *codec = (struct i2s_codec *)file->private_data; ++ return codec->mixer_ioctl(codec, cmd, arg); ++} ++ ++static loff_t jz_i2s_llseek(struct file *file, loff_t offset, int origin) ++{ ++ return -ESPIPE; ++} ++ ++static struct file_operations jz_i2s_mixer_fops = ++{ ++ owner: THIS_MODULE, ++ llseek: jz_i2s_llseek, ++ ioctl: jz_i2s_ioctl_mixdev, ++ open: jz_i2s_open_mixdev, ++}; ++ ++static int i2s_mixer_ioctl(struct i2s_codec *codec, unsigned int cmd, unsigned long arg) ++{ ++ int ret; ++ long val = 0; ++ switch (cmd) { ++ case SOUND_MIXER_INFO: ++ ++ if(codec_mixer_info_id_name) ++ codec_mixer_info_id_name(); ++ info.modify_counter = audio_mix_modcnt; ++ ++ return copy_to_user((void *)arg, &info, sizeof(info)); ++ case SOUND_OLD_MIXER_INFO: ++ ++ if(codec_mixer_old_info_id_name) ++ codec_mixer_old_info_id_name(); ++ ++ return copy_to_user((void *)arg, &old_info, sizeof(info)); ++ case SOUND_MIXER_READ_STEREODEVS: ++ ++ return put_user(0, (long *) arg); ++ case SOUND_MIXER_READ_CAPS: ++ ++ val = SOUND_CAP_EXCL_INPUT; ++ return put_user(val, (long *) arg); ++ case SOUND_MIXER_READ_DEVMASK: ++ break; ++ case SOUND_MIXER_READ_RECMASK: ++ break; ++ case SOUND_MIXER_READ_RECSRC: ++ break; ++ case SOUND_MIXER_WRITE_SPEAKER: ++ ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ switch(val) { ++ case 100: ++ if(set_codec_direct_mode) ++ set_codec_direct_mode(); ++ break; ++ case 0: ++ if(clear_codec_direct_mode) ++ clear_codec_direct_mode(); ++ break; ++ } ++ break; ++ case SOUND_MIXER_WRITE_BASS: ++ ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ codec_bass_gain = val; ++ if(set_codec_bass) ++ set_codec_bass(val); ++ ++ return 0; ++ case SOUND_MIXER_READ_BASS: ++ ++ val = codec_bass_gain; ++ ret = val << 8; ++ val = val | ret; ++ ++ return put_user(val, (long *) arg); ++ case SOUND_MIXER_WRITE_VOLUME: ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ ++ jz_audio_volume = val; ++ if(set_codec_volume) ++ set_codec_volume(val); ++ ++ return 0; ++ case SOUND_MIXER_READ_VOLUME: ++ ++ val = jz_audio_volume; ++ ret = val << 8; ++ val = val | ret; ++ ++ return put_user(val, (long *) arg); ++ ++ case SOUND_MIXER_WRITE_MIC: ++ ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ codec_mic_gain = val; ++ use_mic_line_flag = USE_MIC; ++ if(set_codec_mic) ++ set_codec_mic(val); ++ ++ return 0; ++ case SOUND_MIXER_READ_MIC: ++ ++ val = codec_mic_gain; ++ ret = val << 8; ++ val = val | ret; ++ ++ return put_user(val, (long *) arg); ++ ++ case SOUND_MIXER_WRITE_LINE: ++ ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ use_mic_line_flag = USE_LINEIN; ++ codec_mic_gain = val; ++ if(set_codec_line) ++ set_codec_line(val); ++ ++ return 0; ++ case SOUND_MIXER_READ_LINE: ++ ++ val = codec_mic_gain; ++ ret = val << 8; ++ val = val | ret; ++ ++ return put_user(val, (long *) arg); ++ default: ++ return -ENOSYS; ++ } ++ audio_mix_modcnt ++; ++ return 0; ++} ++ ++ ++int i2s_probe_codec(struct i2s_codec *codec) ++{ ++ /* generic OSS to I2S wrapper */ ++ codec->mixer_ioctl = i2s_mixer_ioctl; ++ return 1; ++} ++ ++ ++/* I2S codec initialisation. */ ++static int __init jz_i2s_codec_init(struct jz_i2s_controller_info *controller) ++{ ++ int num_i2s = 0; ++ struct i2s_codec *codec; ++ ++ for (num_i2s = 0; num_i2s < NR_I2S; num_i2s++) { ++ if ((codec = kmalloc(sizeof(struct i2s_codec),GFP_KERNEL)) == NULL) ++ return -ENOMEM; ++ memset(codec, 0, sizeof(struct i2s_codec)); ++ codec->private_data = controller; ++ codec->id = num_i2s; ++ ++ if (i2s_probe_codec(codec) == 0) ++ break; ++ if ((codec->dev_mixer = register_sound_mixer(&jz_i2s_mixer_fops, -1)) < 0) { ++ printk(KERN_ERR "Jz I2S: couldn't register mixer!\n"); ++ kfree(codec); ++ break; ++ } ++ controller->i2s_codec[num_i2s] = codec; ++ } ++ return num_i2s; ++} ++ ++ ++static void jz_update_filler(int format, int channels) ++{ ++#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) ++ ++ switch (TYPE(format, channels)) ++ { ++ ++ case TYPE(AFMT_U8, 1): ++ jz_audio_b = 4; /* 4bytes * 8bits =32bits */ ++ replay_filler = replay_fill_1x8_u; ++ record_filler = record_fill_1x8_u; ++ break; ++ case TYPE(AFMT_U8, 2): ++ jz_audio_b = 4; ++ replay_filler = replay_fill_2x8_u; ++ record_filler = record_fill_2x8_u; ++ break; ++ case TYPE(AFMT_S16_LE, 1): ++ jz_audio_b = 2; /* 2bytes * 16bits =32bits */ ++ replay_filler = replay_fill_1x16_s; ++ record_filler = record_fill_1x16_s; ++ break; ++ case TYPE(AFMT_S16_LE, 2): ++ jz_audio_b = 2; ++ replay_filler = replay_fill_2x16_s; ++ record_filler = record_fill_2x16_s; ++ break; ++ case TYPE(18, 2): ++ jz_audio_b = 1; ++ replay_filler = replay_fill_2x18_s; ++ record_filler = record_fill_2x16_s; ++ break; ++ default: ++ ; ++ } ++} ++ ++ ++#ifdef CONFIG_PROC_FS ++extern struct proc_dir_entry *proc_jz_root; ++int i2s_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) ++{ ++ return 0; ++} ++ ++static int jz_i2s_init_proc(struct jz_i2s_controller_info *controller) ++{ ++ if (!create_proc_read_entry ("i2s", 0, proc_jz_root, i2s_read_proc, controller->i2s_codec[0])) ++ return -EIO; ++ return 0; ++} ++ ++static void jz_i2s_cleanup_proc(struct jz_i2s_controller_info *controller) ++{ ++} ++#endif ++ ++static void __init attach_jz_i2s(struct jz_i2s_controller_info *controller) ++{ ++ char *name; ++ int adev; /* No of Audio device. */ ++ ++ name = controller->name; ++ /* initialize AIC controller and reset it */ ++ jz_i2s_initHw(1); ++ adev = register_sound_dsp(&jz_i2s_audio_fops, -1); ++ if (adev < 0) ++ goto audio_failed; ++ /* initialize I2S codec and register /dev/mixer */ ++ if (jz_i2s_codec_init(controller) <= 0) ++ goto mixer_failed; ++ ++#ifdef CONFIG_PROC_FS ++ if (jz_i2s_init_proc(controller) < 0) { ++ printk(KERN_ERR "%s: can't create I2S proc filesystem.\n", name); ++ goto proc_failed; ++ } ++#endif ++ ++ controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, 8); ++ if (!controller->tmp1) { ++ printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); ++ goto tmp1_failed; ++ } ++ controller->tmp2 = (void *)__get_free_pages(GFP_KERNEL, 8); ++ if (!controller->tmp2) { ++ printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); ++ goto tmp2_failed; ++ } ++ if ((controller->dma2 = jz_request_dma(DMA_ID_I2S_RX, "audio adc", jz_i2s_record_dma_irq, IRQF_DISABLED, controller)) < 0) { ++ printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name); ++ goto dma2_failed; ++ } ++ if ((controller->dma1 = jz_request_dma(DMA_ID_I2S_TX, "audio dac", jz_i2s_replay_dma_irq, IRQF_DISABLED, controller)) < 0) { ++ printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name); ++ goto dma1_failed; ++ } ++ printk("JzSOC On-Chip I2S controller registered (DAC: DMA(play):%d/IRQ%d,\n ADC: DMA(record):%d/IRQ%d)\n", controller->dma1, get_dma_done_irq(controller->dma1), controller->dma2, get_dma_done_irq(controller->dma2)); ++ ++ controller->dev_audio = adev; ++ pop_turn_onoff_buf = __get_free_pages(GFP_KERNEL | GFP_DMA, 8); ++ if(!pop_turn_onoff_buf) ++ printk("pop_turn_onoff_buf alloc is wrong!\n"); ++ pop_turn_onoff_pbuf = virt_to_phys((void *)pop_turn_onoff_buf); ++ ++ return; ++dma2_failed: ++ jz_free_dma(controller->dma1); ++dma1_failed: ++ free_pages((unsigned long)controller->tmp2, 8); ++tmp2_failed: ++ free_pages((unsigned long)controller->tmp1, 8); ++tmp1_failed: ++ ++#ifdef CONFIG_PROC_FS ++ jz_i2s_cleanup_proc(controller); ++#endif ++proc_failed: ++ /* unregister mixer dev */ ++mixer_failed: ++ unregister_sound_dsp(adev); ++audio_failed: ++ return; ++} ++ ++static int __init probe_jz_i2s(struct jz_i2s_controller_info **controller) ++{ ++ if ((*controller = kmalloc(sizeof(struct jz_i2s_controller_info), ++ GFP_KERNEL)) == NULL) { ++ printk(KERN_ERR "Jz I2S Controller: out of memory.\n"); ++ return -ENOMEM; ++ } ++ (*controller)->name = "Jz I2S controller"; ++ (*controller)->opened1 = 0; ++ (*controller)->opened2 = 0; ++ init_waitqueue_head(&(*controller)->adc_wait); ++ init_waitqueue_head(&(*controller)->dac_wait); ++ spin_lock_init(&(*controller)->lock); ++ init_waitqueue_head(&rx_wait_queue); ++ init_waitqueue_head(&tx_wait_queue); ++ init_waitqueue_head(&pop_wait_queue); ++ init_waitqueue_head(&drain_wait_queue); ++ ++ return 0; ++} ++ ++static void __exit unload_jz_i2s(struct jz_i2s_controller_info *controller) ++{ ++ int adev = controller->dev_audio; ++ ++ jz_i2s_full_reset(controller); ++ controller->dev_audio = -1; ++ if (old_mksound) ++ kd_mksound = old_mksound;/* Our driver support bell for kb, see vt.c */ ++ ++#ifdef CONFIG_PROC_FS ++ jz_i2s_cleanup_proc(controller); ++#endif ++ ++ jz_free_dma(controller->dma1); ++ jz_free_dma(controller->dma2); ++ free_pages((unsigned long)controller->tmp1, 8); ++ free_pages((unsigned long)controller->tmp2, 8); ++ free_pages((unsigned long)pop_turn_onoff_buf, 8); ++ ++ if (adev >= 0) { ++ /* unregister_sound_mixer(audio_devs[adev]->mixer_dev); */ ++ unregister_sound_dsp(controller->dev_audio); ++ } ++} ++ ++#ifdef CONFIG_PM ++static int jz_i2s_suspend(struct jz_i2s_controller_info *controller, int state) ++{ ++ if(i2s_suspend_codec) ++ i2s_suspend_codec(controller->opened1,controller->opened2); ++ printk("Aic and codec are suspended!\n"); ++ return 0; ++} ++ ++static int jz_i2s_resume(struct jz_i2s_controller_info *controller) ++{ ++ if(i2s_resume_codec) ++ i2s_resume_codec(); ++ ++#if defined(CONFIG_I2S_AK4642EN) ++ jz_i2s_initHw(0); ++ jz_audio_reset(); ++ __i2s_enable(); ++ jz_audio_set_speed(controller->dev_audio,jz_audio_speed); ++ /* playing */ ++ if(controller->opened1) { ++ if(set_codec_replay) ++ set_codec_replay(); ++ int dma = controller->dma1; ++ int id; ++ unsigned long flags; ++ disable_dma(dma); ++ if(__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if(__dmac_channel_transmit_end_detected(dma)) ++ __dmac_channel_clear_transmit_end(dma); ++ ++ /* for DSP_GETOPTR */ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ while((id = get_buffer_id(&out_busy_queue)) >= 0) ++ put_buffer_id(&out_empty_queue, id); ++ ++ out_busy_queue.count=0; ++ if((id = get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_empty_queue, id); ++ } ++ if (elements_in_queue(&out_empty_queue) > 0) { ++ wake_up(&tx_wait_queue); ++ wake_up(&controller->dac_wait); ++ } else ++ printk("pm out_empty_queue empty"); ++ } ++ ++ /* recording */ ++ if(controller->opened2) { ++ if(set_codec_record) ++ set_codec_record(use_mic_line_flag); ++ int dma = controller->dma2; ++ int id1, id2; ++ unsigned long flags; ++ disable_dma(dma); ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ } ++ /* for DSP_GETIPTR */ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ id1 = get_buffer_id(&in_busy_queue); ++ put_buffer_id(&in_full_queue, id1); ++ wake_up(&rx_wait_queue); ++ wake_up(&controller->adc_wait); ++ if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_full_queue, id2); ++ } ++ in_busy_queue.count = 0; ++ } ++#endif ++ ++ return 0; ++} ++ ++static int jz_i2s_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) ++{ ++ int ret; ++ struct jz_i2s_controller_info *controller = pm_dev->data; ++ ++ if (!controller) return -EINVAL; ++ ++ switch (req) { ++ case PM_SUSPEND: ++ ret = jz_i2s_suspend(controller, (int)data); ++ break; ++ case PM_RESUME: ++ ret = jz_i2s_resume(controller); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++#endif /* CONFIG_PM */ ++ ++#if defined(CONFIG_I2S_DLV) ++static irqreturn_t aic_codec_irq(int irq, void *dev_id) ++{ ++ u8 file_9 = read_codec_file(9); ++ u8 file_8 = read_codec_file(8); ++ ++ //printk("--- 8:0x%x 9:0x%x ---\n",file_8,file_9); ++ if ((file_9 & 0x1f) == 0x10) { ++ ++ write_codec_file(8, 0x3f); ++ write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ mdelay(300); ++ while ((read_codec_file(9) & 0x4) != 0x4); ++ while ((read_codec_file(9) & 0x10) == 0x10) { ++ write_codec_file(9, 0x10); ++ } ++ write_codec_file_bit(5, 0, 6);//SB_OUT->0 ++ mdelay(300); ++ while ((read_codec_file(9) & 0x8) != 0x8); ++ write_codec_file(9, file_9); ++ write_codec_file(8, file_8); ++ ++ return IRQ_HANDLED; ++ } ++ ++ if (file_9 & 0x8) ++ ramp_up_end = jiffies; ++ else if (file_9 & 0x4) ++ ramp_down_end = jiffies; ++ else if (file_9 & 0x2) ++ gain_up_end = jiffies; ++ else if (file_9 & 0x1) ++ gain_down_end = jiffies; ++ ++ write_codec_file(9, file_9); ++ if (file_9 & 0xf) ++ wake_up(&pop_wait_queue); ++ while (REG_ICDC_RGDATA & 0x100); ++ ++ return IRQ_HANDLED; ++} ++#endif ++ ++static int __init init_jz_i2s(void) ++{ ++ int errno; ++#if defined(CONFIG_I2S_DLV) ++ int retval; ++ ramp_up_start = 0; ++ ramp_up_end = 0; ++ gain_up_start = 0; ++ gain_up_end = 0; ++ ramp_down_start = 0; ++ ramp_down_end = 0; ++ gain_down_start = 0; ++ gain_down_end = 0; ++#endif ++ use_mic_line_flag = USE_NONE; ++ abnormal_data_count = 0; ++ if(set_codec_mode) ++ set_codec_mode(); ++ ++ drain_flag = 0; ++ if ((errno = probe_jz_i2s(&i2s_controller)) < 0) ++ return errno; ++ if(set_codec_gpio_pin) ++ set_codec_gpio_pin(); ++ ++ attach_jz_i2s(i2s_controller); ++ if(set_codec_startup_param) ++ set_codec_startup_param(); ++ if(set_codec_volume_table) ++ set_codec_volume_table(); ++ ++#if defined(CONFIG_I2S_DLV) ++ jz_codec_config = 0; ++ retval = request_irq(IRQ_AIC, aic_codec_irq, IRQF_DISABLED, "aic_codec_irq", NULL); ++ if (retval) { ++ printk("Could not get aic codec irq %d\n", IRQ_AIC); ++ return retval; ++ } ++#endif ++ ++ out_empty_queue.id = NULL; ++ out_full_queue.id = NULL; ++ out_busy_queue.id = NULL; ++ in_empty_queue.id = NULL; ++ in_full_queue.id = NULL; ++ in_busy_queue.id = NULL; ++ ++ jz_audio_fragsize = JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE; ++ jz_audio_fragstotal = JZCODEC_RW_BUFFER_TOTAL ; ++ Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ ++#ifdef CONFIG_PM ++ i2s_controller->pm = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ++ jz_i2s_pm_callback); ++ if (i2s_controller->pm) ++ i2s_controller->pm->data = i2s_controller; ++#endif ++#if defined(CONFIG_I2S_DLV) ++ __cpm_start_idct(); ++ __cpm_start_db(); ++ __cpm_start_me(); ++ __cpm_start_mc(); ++ __cpm_start_ipu(); ++#endif ++ printk("JZ I2S OSS audio driver initialized\n"); ++ ++ return 0; ++} ++ ++static void __exit cleanup_jz_i2s(void) ++{ ++#ifdef CONFIG_PM ++ /* pm_unregister(i2s_controller->pm); */ ++#endif ++#if defined(CONFIG_I2S_DLV) ++ free_irq(IRQ_AIC, NULL); ++#endif ++ unload_jz_i2s(i2s_controller); ++ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ if(clear_codec_mode) ++ clear_codec_mode(); ++} ++ ++module_init(init_jz_i2s); ++module_exit(cleanup_jz_i2s); ++ ++#if defined(CONFIG_SOC_JZ4730) ++static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ unsigned long flags; ++ int count,con; ++ ++ if(elements_in_queue(&in_busy_queue) > 0) { ++ if (nonblock) ++ return -EBUSY; ++ drain_flag = 1; ++ sleep_on(&drain_wait_queue); ++ drain_flag = 0; ++ } else { ++ add_wait_queue(&ctrl->adc_wait, &wait); ++ for (con = 0; con < 1000; con ++) { ++ udelay(1); ++ set_current_state(TASK_INTERRUPTIBLE); ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma2); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ if (nonblock) { ++ remove_wait_queue(&ctrl->adc_wait, &wait); ++ current->state = TASK_RUNNING; ++ return -EBUSY; ++ } ++ } ++ remove_wait_queue(&ctrl->adc_wait, &wait); ++ current->state = TASK_RUNNING; ++ } ++ return 0; ++} ++ ++static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ unsigned long flags; ++ int count; ++ ++ if(elements_in_queue(&out_full_queue) > 0) { ++ if (nonblock) ++ return -EBUSY; ++ ++ drain_flag = 1; ++ sleep_on(&drain_wait_queue); ++ drain_flag = 0; ++ } else { ++ add_wait_queue(&(ctrl->dac_wait), &wait); ++ for (;;) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ if(elements_in_queue(&out_full_queue) <= 0) { ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if(count <= 0) ++ break; ++ } ++ if (nonblock) { ++ remove_wait_queue(&ctrl->dac_wait, &wait); ++ current->state = TASK_RUNNING; ++ return -EBUSY; ++ } ++ } ++ remove_wait_queue(&ctrl->dac_wait, &wait); ++ current->state = TASK_RUNNING; ++ } ++ ++ return 0; ++} ++#endif ++ ++#if defined(CONFIG_SOC_JZ4740) ++#define MAXDELAY 50000 ++static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ int count,ele,i=0; ++ int tfl; ++ ++ for (;;) { ++ if(!nonblock) {//blocked ++ if ( i < MAXDELAY ) { ++ udelay(10); ++ i++; ++ } else ++ break; ++ ++ ele = elements_in_queue(&out_full_queue); ++ if(ele <= 0) { ++ udelay(10); ++ spin_lock(&ctrl->lock); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock(&ctrl->lock); ++ if (count <= 0) ++ break; ++ } ++ } else {//non-blocked ++ mdelay(100); ++ ele = elements_in_queue(&out_full_queue); ++ ++ if(ele <= 0) { ++ mdelay(100); ++ ++ spin_lock(&ctrl->lock); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock(&ctrl->lock); ++ if (count <= 0) ++ break; ++ } ++ } ++ } ++ ++ /* wait for TX fifo */ ++ while (1) { ++ tfl = __aic_get_transmit_resident(); ++ if (tfl == 0) ++ break; ++ udelay(2); ++ } ++ ++ return 0; ++} ++ ++static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ int count,i=0; ++ ++ for (;;) { ++ if ( i < MAXDELAY ) ++ { ++ udelay(10); ++ i++; ++ } ++ else ++ break; ++ spin_lock(&ctrl->lock); ++ count = get_dma_residue(ctrl->dma2); ++ spin_unlock(&ctrl->lock); ++ if (count <= 0) ++ break; ++ ++ if (nonblock) { ++ return -EBUSY; ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++#if defined(CONFIG_SOC_JZ4750) ++#define MAXDELAY 50000 ++static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ //DECLARE_WAITQUEUE(wait, current); ++ unsigned long flags; ++ int count,i=0; ++ ++ //add_wait_queue(&ctrl->adc_wait, &wait); ++ for (;;) { ++ if (i < MAXDELAY) { ++ udelay(10); ++ i++; ++ } else ++ break; ++ //set_current_state(TASK_INTERRUPTIBLE); ++ spin_lock_irqsave(&ctrl->lock, flags); ++ //spin_lock(&ctrl->lock); ++ count = get_dma_residue(ctrl->dma2); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ //spin_unlock(&ctrl->lock); ++ if (count <= 0) ++ break; ++ ++ /*if (signal_pending(current)) ++ break;*/ ++ if (nonblock) { ++ //remove_wait_queue(&ctrl->adc_wait, &wait); ++ //current->state = TASK_RUNNING; ++ return -EBUSY; ++ } ++ } ++ //remove_wait_queue(&ctrl->adc_wait, &wait); ++ //current->state = TASK_RUNNING; ++ /*if (signal_pending(current)) ++ return -ERESTARTSYS;*/ ++ return 0; ++} ++static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ unsigned long flags; ++ int count,ele,busyele,emptyele,i=0; ++ ++ for (;;) { ++ if(!nonblock) {//blocked ++ if (i < MAXDELAY) { ++ udelay(10); ++ i++; ++ } else ++ break; ++ ++ ele = elements_in_queue(&out_full_queue); ++ if(ele <= 0) { ++ udelay(200); ++ ++ busyele = elements_in_queue(&out_busy_queue); ++ emptyele = elements_in_queue(&out_empty_queue); ++ if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ } ++ } ++ } else {//non-blocked ++ //mdelay(100); ++ ele = elements_in_queue(&out_full_queue); ++ ++ if(ele <= 0) { ++ //mdelay(100); ++ busyele = elements_in_queue(&out_busy_queue); ++ emptyele = elements_in_queue(&out_empty_queue); ++ ++ if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ } ++ } ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++static int jz_audio_release(struct inode *inode, struct file *file) ++{ ++ unsigned long flags; ++#if defined(CONFIG_I2S_DLV) ++ unsigned long tfl; ++#endif ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++#if defined(CONFIG_I2S_DLV) ++ jz_codec_config = 0; ++#endif ++ if (controller == NULL) ++ return -ENODEV; ++ ++ pop_dma_flag = 0; ++ ++ if (controller->opened1 == 1 && controller->opened2 == 1) { ++ controller->opened1 = 0; ++ __i2s_enable_transmit_dma(); ++ __i2s_enable_replay(); ++ drain_dac(controller, file->f_flags & O_NONBLOCK); ++#if defined(CONFIG_I2S_DLV) ++ /* wait for fifo empty */ ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ gain_down_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //gain_down_end = jiffies; ++ /*while (1) { ++ tfl = REG_AIC_SR & 0x00003f00; ++ if (tfl == 0) { ++ udelay(500); ++ break; ++ } ++ mdelay(2); ++ }*/ ++ mdelay(100); ++#endif ++ disable_dma(controller->dma1); ++ set_dma_count(controller->dma1, 0); ++ __i2s_disable_transmit_dma(); ++ __i2s_disable_replay(); ++ ++#if defined(CONFIG_I2S_ICODEC) ++ if(clear_codec_replay) ++ clear_codec_replay(); ++#endif ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ controller->opened2 = 0; ++ first_record_call = 1; ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ drain_adc(controller, file->f_flags & O_NONBLOCK); ++ disable_dma(controller->dma2); ++ set_dma_count(controller->dma2, 0); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_record(); ++#if defined(CONFIG_I2S_ICODEC) ++ if(clear_codec_record) ++ clear_codec_record(); ++#endif ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ ramp_down_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //ramp_down_end = jiffies; ++ if (use_mic_line_flag == USE_LINEIN) { ++ unset_record_line_input_audio_with_audio_data_replay(); ++ //printk("3 use_mic_line_flag=%d\n",use_mic_line_flag); ++ } ++ if (use_mic_line_flag == USE_MIC) { ++ unset_record_mic_input_audio_with_audio_data_replay(); ++ //printk("4 use_mic_line_flag=%d\n",use_mic_line_flag); ++ } ++ ++#if 0 ++ unset_record_playing_audio_mixed_with_mic_input_audio(); ++#endif ++#endif ++ __i2s_disable(); ++ if(turn_off_codec) ++ turn_off_codec(); ++ abnormal_data_count = 0; ++ } else if (controller->opened1 == 1) { ++ //controller->opened1 = 0; ++ __i2s_enable_transmit_dma(); ++ __i2s_enable_replay(); ++ drain_dac(controller, file->f_flags & O_NONBLOCK); ++ /* add some mute to anti-pop */ ++#if defined(CONFIG_I2S_ICODEC) ++ //write_mute_to_dma_buffer(save_last_samples[last_dma_buffer_id].left,save_last_samples[last_dma_buffer_id].right); ++#endif ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ gain_down_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //gain_down_end = jiffies; ++ while (1) { ++ tfl = REG_AIC_SR & 0x00003f00; ++ if (tfl == 0) { ++ udelay(500); ++ break; ++ } ++ mdelay(2); ++ } ++#endif ++ disable_dma(controller->dma1); ++ set_dma_count(controller->dma1, 0); ++ __i2s_disable_transmit_dma(); ++ __i2s_disable_replay(); ++#if defined(CONFIG_I2S_ICODEC) ++ if(clear_codec_replay) ++ clear_codec_replay(); ++#endif ++ __aic_flush_fifo(); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ ramp_down_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //ramp_down_end = jiffies; ++ unset_audio_data_replay(); ++#endif ++ __i2s_disable(); ++#if defined(CONFIG_I2S_ICODEC) ++ if(turn_off_codec) ++ turn_off_codec(); ++#endif ++ } else if (controller->opened2 == 1) { ++ controller->opened2 = 0; ++ first_record_call = 1; ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ drain_adc(controller, file->f_flags & O_NONBLOCK); ++ disable_dma(controller->dma2); ++ set_dma_count(controller->dma2, 0); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_record(); ++#if defined(CONFIG_I2S_ICODEC) ++ if(clear_codec_record) ++ clear_codec_record(); ++#endif ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++#if defined(CONFIG_I2S_DLV) ++#if 0 ++ /* unset Record MIC input audio with direct playback */ ++ unset_record_mic_input_audio_with_direct_playback(); ++#endif ++#if 1 ++ /* unset Record MIC input audio without playback */ ++ unset_record_mic_input_audio_without_playback(); ++#endif ++#if 0 ++ /* tested */ ++ /* unset Record LINE input audio without playback */ ++ unset_record_line_input_audio_without_playback(); ++#endif ++#endif ++ __i2s_disable(); ++#if defined(CONFIG_I2S_ICODEC) ++ if(turn_off_codec) ++ turn_off_codec(); ++#endif ++ abnormal_data_count = 0; ++ } ++ ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++/* __cpm_stop_idct(); ++ __cpm_stop_db(); ++ __cpm_stop_me(); ++ __cpm_stop_mc(); ++ __cpm_stop_ipu();*/ ++#endif ++ ++ if (controller->opened1 == 1 && controller->opened2 == 1) { ++ controller->opened1 = 0; ++ controller->opened2 = 0; ++ //print_pop_duration(); ++ //__dmac_disable_module(0); ++ } else if ( controller->opened1 == 1 ) { ++ controller->opened1 = 0; ++ //print_pop_duration(); ++ } else if ( controller->opened2 == 1 ) { ++ controller->opened2 = 0; ++ } ++ ++ return 0; ++} ++ ++static int jz_audio_open(struct inode *inode, struct file *file) ++{ ++ int i; ++ struct jz_i2s_controller_info *controller = i2s_controller; ++#if defined(CONFIG_I2S_DLV) ++ jz_codec_config = 0; ++#endif ++ if (controller == NULL) ++ return -ENODEV; ++ ++ mdelay(2); ++#if defined(CONFIG_I2S_DLV) ++ REG_DMAC_DMACKE(0) = 0x3f; ++#endif ++ pop_dma_flag = 0; ++ if (controller->opened1 == 1 || controller->opened2 == 1 ) { ++ printk("\naudio is busy!\n"); ++ return -EBUSY; ++ } ++ ++ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { ++ if (controller->opened1 == 1) ++ return -EBUSY; ++ controller->opened1 = 1; ++ /* for ioctl */ ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ ++ for(i=0;i < 64;i++) { ++ save_last_samples[i].left = 0; ++ save_last_samples[i].right = 0; ++ } ++ ++ out_empty_queue.count = jz_audio_fragstotal; ++ for (i=0;i < jz_audio_fragstotal;i++) ++ *(out_empty_queue.id + i) = i; ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ last_dma_buffer_id = 0; ++ ++ if (controller->opened2 == 1) ++ return -EBUSY; ++ ++ controller->opened2 = 1; ++ first_record_call = 1; ++ /* for ioctl */ ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ ++ in_empty_queue.count = jz_audio_fragstotal; ++ for (i=0;i < jz_audio_fragstotal;i++) ++ *(in_empty_queue.id + i) = i; ++ ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ } else if (file->f_mode & FMODE_WRITE) { ++ if (controller->opened1 == 1) ++ return -EBUSY; ++ ++ controller->opened1 = 1; ++ /* for ioctl */ ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ ++ for(i=0;i < 64;i++) { ++ save_last_samples[i].left = 0; ++ save_last_samples[i].right = 0; ++ } ++ ++ out_empty_queue.count = jz_audio_fragstotal; ++ for (i=0;i < jz_audio_fragstotal;i++) ++ *(out_empty_queue.id + i) = i; ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ last_dma_buffer_id = 0; ++ } else if (file->f_mode & FMODE_READ) { ++ if (controller->opened2 == 1) ++ return -EBUSY; ++ ++ controller->opened2 = 1; ++ first_record_call = 1; ++ /* for ioctl */ ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ ++ in_empty_queue.count = jz_audio_fragstotal; ++ for (i=0;i < jz_audio_fragstotal;i++) ++ *(in_empty_queue.id + i) = i; ++ ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ } ++ ++ file->private_data = controller; ++ jz_audio_reset(); ++ REG_AIC_FR |= (1 << 6); ++ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { ++#if defined(CONFIG_I2S_ICODEC) ++ if (set_codec_replay_record) ++ set_codec_replay_record(use_mic_line_flag); ++#endif ++#if defined(CONFIG_I2S_DLV) ++ if (use_mic_line_flag == USE_NONE) { ++ printk("you select mic or line recording please.or use mic recording!\n"); ++ use_mic_line_flag = USE_MIC; ++ } ++ if (use_mic_line_flag == USE_LINEIN) { ++ /* Record LINE input audio with Audio data replay (full duplex for linein) */ ++ /* codec_test_line */ ++ set_record_line_input_audio_with_audio_data_replay(); ++ ++ } ++ if (use_mic_line_flag == USE_MIC) { ++ /* Record MIC input audio with Audio data replay (full duplex) */ ++ /* codec_test_mic */ ++ set_record_mic_input_audio_with_audio_data_replay(); ++ } ++#if 0 ++ /* Record playing audio mixed with MIC input audio */ ++ set_record_playing_audio_mixed_with_mic_input_audio(); ++#endif ++ ++#endif ++ } else if (file->f_mode & FMODE_WRITE) { ++#if defined(CONFIG_I2S_ICODEC) ++ if(set_codec_replay) ++ set_codec_replay(); ++#endif ++#if defined(CONFIG_I2S_DLV) ++ //mdelay(10); ++ /* Audio data replay */ ++ set_audio_data_replay(); ++#endif ++ } else if (file->f_mode & FMODE_READ) { ++#if defined(CONFIG_I2S_ICODEC) ++ abnormal_data_count = 0; ++ if(set_codec_record) ++ set_codec_record(use_mic_line_flag); ++#endif ++#if defined(CONFIG_I2S_DLV) ++#if 0 ++ /* Record MIC input audio with direct playback */ ++ set_record_mic_input_audio_with_direct_playback(); ++#endif ++ ++#if 1 ++ /* set Record MIC input audio without playback */ ++ set_record_mic_input_audio_without_playback(); ++#endif ++#if 0 ++ /* tested */ ++ /* set Record LINE input audio without playback */ ++ set_record_line_input_audio_without_playback(); ++#endif ++ mdelay(1); ++#endif ++ } ++ ++#if defined(CONFIG_I2S_DLV) ++ __aic_reset(); ++ ++ mdelay(10); ++ REG_AIC_I2SCR = 0x10; ++ mdelay(20); ++ __aic_flush_fifo(); ++#endif ++ ++ __i2s_enable(); ++ ++#if defined(CONFIG_I2S_DLV) ++ ndelay(100); ++ ++ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { ++#if defined(CONFIG_I2S_DLV) ++ //set SB_ADC or SB_DAC ++ __dmac_enable_module(0); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ ramp_up_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //ramp_up_end = jiffies; ++#endif ++ } else if (file->f_mode & FMODE_WRITE) { ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ ramp_up_start = jiffies; ++ /*while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_RCR = 0x1; ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_RGR = 1;*/ ++ sleep_on(&pop_wait_queue); ++ //ramp_up_end = jiffies; ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++#endif ++ } else if (file->f_mode & FMODE_READ) { ++#if defined(CONFIG_I2S_DLV) ++ if (jz_mic_only) ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ else ++ write_codec_file_bit(5, 0, 7);//SB_DAC->0 ++ mdelay(500); ++#endif ++ } ++#endif ++ return 0; ++} ++ ++ ++static int jz_audio_ioctl(struct inode *inode, struct file *file, ++unsigned int cmd, unsigned long arg) ++{ ++ int val,fullc,busyc,unfinish,newfragstotal,newfragsize; ++ unsigned int flags; ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ count_info cinfo; ++ audio_buf_info abinfo; ++ int id, i; ++ ++ val = 0; ++ switch (cmd) { ++ case OSS_GETVERSION: ++ return put_user(SOUND_VERSION, (int *)arg); ++ case SNDCTL_DSP_RESET: ++#if 0 ++ jz_audio_reset(); ++ __i2s_disable_replay(); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_record(); ++ __i2s_disable_transmit_dma(); ++#endif ++ return 0; ++ case SNDCTL_DSP_SYNC: ++ if (file->f_mode & FMODE_WRITE) ++ return drain_dac(controller, file->f_flags & O_NONBLOCK); ++ return 0; ++ case SNDCTL_DSP_SPEED: ++ /* set smaple rate */ ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ if (val >= 0) ++ jz_audio_set_speed(controller->dev_audio, val); ++ ++ return put_user(val, (int *)arg); ++ case SNDCTL_DSP_STEREO: ++ /* set stereo or mono channel */ ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ jz_audio_set_channels(controller->dev_audio, val ? 2 : 1); ++ ++ return 0; ++ case SNDCTL_DSP_GETBLKSIZE: ++ //return put_user(jz_audio_fragsize / jz_audio_b, (int *)arg); ++ return put_user(jz_audio_fragsize, (int *)arg); ++ case SNDCTL_DSP_GETFMTS: ++ /* Returns a mask of supported sample format*/ ++ return put_user(AFMT_U8 | AFMT_S16_LE, (int *)arg); ++ case SNDCTL_DSP_SETFMT: ++ /* Select sample format */ ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ if (val != AFMT_QUERY) ++ jz_audio_set_format(controller->dev_audio,val); ++ else { ++ if (file->f_mode & FMODE_READ) ++ val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; ++ else ++ val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; ++ } ++ ++ return put_user(val, (int *)arg); ++ case SNDCTL_DSP_CHANNELS: ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ jz_audio_set_channels(controller->dev_audio, val); ++ ++ return put_user(val, (int *)arg); ++ case SNDCTL_DSP_POST: ++ /* FIXME: the same as RESET ?? */ ++ return 0; ++ case SNDCTL_DSP_SUBDIVIDE: ++ return 0; ++ case SNDCTL_DSP_SETFRAGMENT: ++ get_user(val, (long *) arg); ++ newfragsize = 1 << (val & 0xFFFF); ++ if (newfragsize < 4 * PAGE_SIZE) ++ newfragsize = 4 * PAGE_SIZE; ++ if (newfragsize > (16 * PAGE_SIZE)) ++ newfragsize = 16 * PAGE_SIZE; ++ ++ newfragstotal = (val >> 16) & 0x7FFF; ++ if (newfragstotal < 2) ++ newfragstotal = 2; ++ if (newfragstotal > 32) ++ newfragstotal = 32; ++ if((jz_audio_fragstotal == newfragstotal) && (jz_audio_fragsize == newfragsize)) ++ return 0; ++ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ mdelay(500); ++ jz_audio_fragstotal = newfragstotal; ++ jz_audio_fragsize = newfragsize; ++ ++ Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ mdelay(10); ++ ++ return 0; ++ case SNDCTL_DSP_GETCAPS: ++ return put_user(DSP_CAP_REALTIME|DSP_CAP_BATCH, (int *)arg); ++ case SNDCTL_DSP_NONBLOCK: ++ file->f_flags |= O_NONBLOCK; ++ return 0; ++ case SNDCTL_DSP_SETDUPLEX: ++ return -EINVAL; ++ case SNDCTL_DSP_GETOSPACE: ++ { ++ int i; ++ unsigned long bytes = 0; ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ jz_audio_fragments = elements_in_queue(&out_empty_queue); ++ for (i = 0; i < jz_audio_fragments; i++) ++ bytes += jz_audio_fragsize; ++ ++ if (jz_audio_channels == 2) ++ bytes /= jz_audio_b; ++ else if (jz_audio_channels == 1) ++ bytes /= 4; ++ else ++ printk("SNDCTL_DSP_GETOSPACE : channels is wrong 1!\n"); ++ ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ /* unused fragment amount */ ++ abinfo.fragments = jz_audio_fragments; ++ /* amount of fragments */ ++ abinfo.fragstotal = jz_audio_fragstotal; ++ /* fragment size in bytes */ ++ if (jz_audio_channels == 2) ++ abinfo.fragsize = jz_audio_fragsize / jz_audio_b; ++ else if (jz_audio_channels == 1) ++ abinfo.fragsize = jz_audio_fragsize / 4; ++ else ++ printk("SNDCTL_DSP_GETOSPACE : channels is wrong 2!\n"); ++ ++ /* write size count without blocking in bytes */ ++ abinfo.bytes = (int)bytes; ++ ++ return copy_to_user((void *)arg, &abinfo, ++ sizeof(abinfo)) ? -EFAULT : 0; ++ } ++ case SNDCTL_DSP_GETISPACE: ++ { ++ int i; ++ unsigned long bytes = 0; ++ if (!(file->f_mode & FMODE_READ)) ++ return -EINVAL; ++ jz_audio_fragments = elements_in_queue(&in_empty_queue); ++ for (i = 0; i < jz_audio_fragments; i++) ++ bytes += jz_audio_fragsize; ++ ++ if (jz_audio_channels == 2) ++ bytes /= jz_audio_b; ++ else if (jz_audio_channels == 1) ++ bytes /= 4; ++ else ++ printk("SNDCTL_DSP_GETISPACE : channels is wrong 1!\n"); ++ ++ abinfo.fragments = jz_audio_fragments; ++ abinfo.fragstotal = jz_audio_fragstotal; ++ ++ if (jz_audio_channels == 2) ++ abinfo.fragsize = jz_audio_fragsize / jz_audio_b; ++ else if (jz_audio_channels == 1) ++ abinfo.fragsize = jz_audio_fragsize / 4; ++ else ++ printk("SNDCTL_DSP_GETISPACE : channels is wrong 2!\n"); ++ ++ abinfo.bytes = (int)bytes; ++ ++ return copy_to_user((void *)arg, &abinfo, ++ sizeof(abinfo)) ? -EFAULT : 0; ++ } ++ case SNDCTL_DSP_GETTRIGGER: ++ val = 0; ++ if (file->f_mode & FMODE_READ && in_dma_buf) ++ val |= PCM_ENABLE_INPUT; ++ if (file->f_mode & FMODE_WRITE && out_dma_buf) ++ val |= PCM_ENABLE_OUTPUT; ++ ++ return put_user(val, (int *)arg); ++ case SNDCTL_DSP_SETTRIGGER: ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ return 0; ++ case SNDCTL_DSP_GETIPTR: ++ if (!(file->f_mode & FMODE_READ)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ cinfo.bytes = controller->total_bytes; ++ cinfo.blocks = controller->blocks; ++ cinfo.ptr = controller->nextIn; ++ controller->blocks = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); ++ case SNDCTL_DSP_GETOPTR: ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ cinfo.bytes = controller->total_bytes; ++ cinfo.blocks = controller->blocks; ++ cinfo.ptr = controller->nextOut; ++ controller->blocks = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); ++ case SNDCTL_DSP_GETODELAY: ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ unfinish = 0; ++ fullc = elements_in_queue(&out_full_queue); ++ busyc = elements_in_queue(&out_busy_queue); ++ for(i = 0;i < fullc ;i ++) { ++ id = *(out_full_queue.id + i); ++ unfinish += *(out_dma_buf_data_count + id); ++ } ++ for(i = 0;i < busyc ;i ++) { ++ id = *(out_busy_queue.id + i); ++ unfinish += get_dma_residue(controller->dma1); ++ } ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ if (jz_audio_channels == 2) ++ unfinish /= jz_audio_b; ++ else if (jz_audio_channels == 1) ++ unfinish /= 4; ++ else ++ printk("SNDCTL_DSP_GETODELAY : channels is wrong !\n"); ++ ++ return put_user(unfinish, (int *) arg); ++ case SOUND_PCM_READ_RATE: ++ return put_user(jz_audio_rate, (int *)arg); ++ case SOUND_PCM_READ_CHANNELS: ++ return put_user(jz_audio_channels, (int *)arg); ++ case SOUND_PCM_READ_BITS: ++ return put_user((jz_audio_format & (AFMT_S8 | AFMT_U8)) ? 8 : 16, (int *)arg); ++ case SNDCTL_DSP_MAPINBUF: ++ case SNDCTL_DSP_MAPOUTBUF: ++ case SNDCTL_DSP_SETSYNCRO: ++ case SOUND_PCM_WRITE_FILTER: ++ case SOUND_PCM_READ_FILTER: ++ return -EINVAL; ++ } ++ return -EINVAL; ++} ++ ++ ++static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait) ++{ ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ unsigned long flags; ++ unsigned int mask = 0; ++ ++ if (file->f_mode & FMODE_WRITE) { ++ if (elements_in_queue(&out_empty_queue) > 0) ++ return POLLOUT | POLLWRNORM; ++ ++ poll_wait(file, &controller->dac_wait, wait); ++ } ++ ++ if (file->f_mode & FMODE_READ) { ++ if (elements_in_queue(&in_full_queue) > 0) ++ return POLLIN | POLLRDNORM; ++ ++ poll_wait(file, &controller->adc_wait, wait); ++ } ++ ++ spin_lock_irqsave(&controller->lock, flags); ++ if (file->f_mode & FMODE_WRITE) { ++ if (elements_in_queue(&out_empty_queue) > 0) ++ mask |= POLLOUT | POLLWRNORM; ++ } else if (file->f_mode & FMODE_READ) { ++ if (elements_in_queue(&in_full_queue) > 0) ++ mask |= POLLIN | POLLRDNORM; ++ } ++ spin_unlock_irqrestore(&controller->lock, flags); ++ ++ return mask; ++} ++ ++static ssize_t jz_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos) ++{ ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ int id, ret = 0, left_count, copy_count, cnt = 0; ++ unsigned long flags; ++ ++ if (count < 0) ++ return -EINVAL; ++ ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextIn = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ copy_count = jz_audio_fragsize / 4; ++ ++ left_count = count; ++ if (first_record_call) { ++ first_record_call = 0; ++ audio_read_back_first: ++ if ((id = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id); ++ spin_lock(&controller->lock); ++ *(in_dma_buf_data_count + id) = copy_count * 4; ++ ++ spin_unlock(&controller->lock); ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); ++ audio_start_dma(controller->dma2,file->private_data, ++ *(in_dma_pbuf + id), ++ *(in_dma_buf_data_count + id), ++ DMA_MODE_READ); ++ sleep_on(&rx_wait_queue); ++ } else ++ goto audio_read_back_first; ++ } ++ ++ while (left_count > 0) { ++ audio_read_back_second: ++ if (elements_in_queue(&in_full_queue) <= 0) { ++ if (file->f_flags & O_NONBLOCK) ++ return ret ? ret : -EAGAIN; ++ else ++ sleep_on(&rx_wait_queue); ++ } ++ ++ if ((id = get_buffer_id(&in_full_queue)) >= 0) { ++ spin_lock(&controller->lock); ++ cnt = record_filler((unsigned long)controller->tmp2+ret, copy_count, id); ++ spin_unlock(&controller->lock); ++ put_buffer_id(&in_empty_queue, id); ++ } else ++ goto audio_read_back_second; ++ ++ if (elements_in_queue(&in_busy_queue) == 0) { ++ if ((id=get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id); ++ spin_lock(&controller->lock); ++ *(in_dma_buf_data_count + id) = copy_count * 4; ++ spin_unlock(&controller->lock); ++ ++ dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); ++ audio_start_dma(controller->dma2,file->private_data, ++ *(in_dma_pbuf + id), ++ *(in_dma_buf_data_count + id), ++ DMA_MODE_READ); ++ } ++ } ++ if (ret + cnt > count) { ++ spin_lock(&controller->lock); ++ cnt = count - ret; ++ spin_unlock(&controller->lock); ++ } ++ if (copy_to_user(buffer+ret, controller->tmp2+ret, cnt)) ++ return ret ? ret : -EFAULT; ++ ++ spin_lock(&controller->lock); ++ ret += cnt; ++ spin_unlock(&controller->lock); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextIn += ret; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ spin_lock(&controller->lock); ++ left_count -= cnt; ++ spin_unlock(&controller->lock); ++ } ++ return ret; ++} ++ ++static ssize_t jz_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) ++{ ++ int id, ret = 0, left_count, copy_count = 0; ++ unsigned int flags; ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ ++ if (count <= 0) ++ return -EINVAL; ++ ++ if(set_replay_hp_or_speaker) ++ set_replay_hp_or_speaker(); ++ ++ __i2s_enable_transmit_dma(); ++ __i2s_enable_replay(); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextOut = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ if (jz_audio_channels == 2) ++ copy_count = jz_audio_fragsize / jz_audio_b; ++ else if(jz_audio_channels == 1) ++ copy_count = jz_audio_fragsize / 4; ++ left_count = count; ++ if (copy_from_user(controller->tmp1, buffer, count)) { ++ printk("copy_from_user failed:%d",ret); ++ return ret ? ret : -EFAULT; ++ } ++ ++ while (left_count > 0) { ++ audio_write_back: ++ if (file->f_flags & O_NONBLOCK) ++ udelay(2); ++ if (elements_in_queue(&out_empty_queue) == 0) { ++ if (file->f_flags & O_NONBLOCK) ++ return ret; ++ else ++ sleep_on(&tx_wait_queue); ++ } ++ /* the end fragment size in this write */ ++ if (ret + copy_count > count) ++ copy_count = count - ret; ++ if ((id = get_buffer_id(&out_empty_queue)) >= 0) { ++ replay_filler((signed long)controller->tmp1 + ret, copy_count, id); ++ if(*(out_dma_buf_data_count + id) > 0) { ++ put_buffer_id(&out_full_queue, id); ++ dma_cache_wback_inv(*(out_dma_buf + id), *(out_dma_buf_data_count + id)); ++ } else ++ put_buffer_id(&out_empty_queue, id); ++ } else ++ goto audio_write_back; ++ ++ left_count = left_count - copy_count; ++ ret += copy_count; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextOut += ret; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ if (elements_in_queue(&out_busy_queue) == 0) { ++ if ((id=get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_busy_queue, id); ++ if(*(out_dma_buf_data_count + id) > 0) { ++ audio_start_dma(controller->dma1, ++ file->private_data, ++ *(out_dma_pbuf + id), ++ *(out_dma_buf_data_count + id), ++ DMA_MODE_WRITE); ++ last_dma_buffer_id = id; ++#if defined(CONFIG_I2S_DLV) ++ if (jz_codec_config == 0) { ++ write_codec_file_bit(1, 0, 5); ++ gain_up_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //gain_up_end = jiffies; ++ jz_codec_config = 1; ++ //SB_ADC->1 ++ //write_codec_file_bit(5, 1, 4); ++ //while(1); ++ } ++#endif ++ } ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++#if defined(CONFIG_I2S_ICODEC) ++static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample) ++{ ++ int i,step_len; ++ unsigned long *pop_buf = (unsigned long*)pop_turn_onoff_buf; ++ unsigned int sample_oss = (REG_AIC_CR & 0x00380000) >> 19; ++ unsigned long l_sample_count,r_sample_count,sample_count; ++ struct jz_i2s_controller_info *controller = i2s_controller; ++ signed int left_sam=0,right_sam=0,l_val,r_val; ++ ++ switch (sample_oss) { ++ case 0x0: ++ break; ++ case 0x1: ++ left_sam = (signed int)l_sample; ++ right_sam = (signed int)r_sample; ++ break; ++ case 0x2: ++ break; ++ case 0x3: ++ break; ++ case 0x4: ++ break; ++ } ++ ++ if(left_sam == 0 && right_sam == 0) ++ return; ++ ++ switch (sample_oss) { ++ case 0x0: ++ break; ++ case 0x1: ++ step_len = jz_audio_speed / 10 * 3; ++ step_len = step_len / 2; ++ step_len = 0x7fff / step_len + 1; ++ ++ l_sample_count = 0; ++ l_val = left_sam; ++ ++ while(1) { ++ if(l_val > 0) { ++ if(l_val >= step_len) { ++ l_val -= step_len; ++ l_sample_count ++; ++ } else ++ break; ++ } ++ ++ if(l_val < 0) { ++ if(l_val <= -step_len) { ++ l_val += step_len; ++ l_sample_count ++; ++ } else ++ break; ++ } ++ ++ if(l_val == 0) ++ break; ++ } ++ ++ r_sample_count = 0; ++ r_val = right_sam; ++ while(1) { ++ if(r_val > 0) { ++ if(r_val >= step_len) { ++ r_val -= step_len; ++ r_sample_count ++; ++ } else ++ break; ++ } ++ ++ if(r_val < 0) { ++ if(r_val <= -step_len) { ++ r_val += step_len; ++ r_sample_count ++; ++ } else ++ break; ++ } ++ ++ if(r_val == 0) ++ break; ++ } ++ /* fill up */ ++ if(l_sample_count > r_sample_count) ++ sample_count = l_sample_count; ++ else ++ sample_count = r_sample_count; ++ ++ l_val = left_sam; ++ r_val = right_sam; ++ for(i=0;i <= sample_count;i++) { ++ ++ *pop_buf = (unsigned long)l_val; ++ pop_buf ++; ++ ++ if(l_val > step_len) ++ l_val -= step_len; ++ else if(l_val < -step_len) ++ l_val += step_len; ++ else if(l_val >= -step_len && l_val <= step_len) ++ l_val = 0; ++ ++ *pop_buf = (unsigned long)r_val; ++ pop_buf ++; ++ if(r_val > step_len) ++ r_val -= step_len; ++ else if(r_val < -step_len) ++ r_val += step_len; ++ else if(r_val >= -step_len && r_val <= step_len) ++ r_val = 0; ++ } ++ ++ *pop_buf = 0; ++ pop_buf ++; ++ *pop_buf = 0; ++ ++ pop_buf ++; ++ sample_count += 2; ++ dma_cache_wback_inv(pop_turn_onoff_buf, sample_count*8); ++ ++ pop_dma_flag = 1; ++ audio_start_dma(controller->dma1,controller,pop_turn_onoff_pbuf,sample_count*8,DMA_MODE_WRITE); ++ sleep_on(&pop_wait_queue); ++ pop_dma_flag = 0; ++ break; ++ case 0x2: ++ break; ++ case 0x3: ++ break; ++ case 0x4: ++ break; ++ } ++} ++#endif +diff -urN linux-2.6.24.7.old/sound/oss/jz_i2s_4750.c linux-2.6.24.7/sound/oss/jz_i2s_4750.c +--- linux-2.6.24.7.old/sound/oss/jz_i2s_4750.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/oss/jz_i2s_4750.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,3010 @@ ++/* ++ * linux/drivers/sound/Jz_i2s.c ++ * ++ * JzSOC On-Chip I2S audio driver. ++ * ++ * Copyright (C) 2005 by Junzheng Corp. ++ * Modified by cjfeng on Aug 9,2007,and not any bug on Jz4730 using ++ * dma channel 4&3,noah is tested. ++ * ++ * 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. ++ * ++ * Because the normal application of AUDIO devices are focused on Little_endian, ++ * then we only perform the little endian data format in driver. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "sound_config.h" ++ ++#define DPRINTK(args...) printk(args) ++#define DMA_ID_I2S_TX DMA_ID_AIC_TX ++#define DMA_ID_I2S_RX DMA_ID_AIC_RX ++#define NR_I2S 2 ++#define MAXDELAY 50000 ++#define JZCODEC_RW_BUFFER_SIZE 2 ++#define JZCODEC_RW_BUFFER_TOTAL 6 ++ ++typedef struct hpvol_shift_s ++{ ++ int hpvol; ++ int shift; ++} hpvol_shift_t; ++ ++mixer_info info; ++_old_mixer_info old_info; ++int codec_volue_shift; ++hpvol_shift_t hpvol_shift_table[72]; ++int abnormal_data_count; ++unsigned long i2s_clk; ++ ++void (*set_codec_mode)(void) = NULL; ++void (*clear_codec_mode)(void) = NULL; ++void (*set_codec_gpio_pin)(void) = NULL; ++void (*each_time_init_codec)(void) = NULL; ++int (*set_codec_startup_param)(void) = NULL; ++void (*set_codec_volume_table)(void) = NULL; ++void (*set_codec_record)(void) = NULL; ++void (*set_codec_replay)(void) = NULL; ++void (*set_codec_replay_record)(void); ++void (*turn_on_codec)(void) = NULL; ++void (*turn_off_codec)(void) = NULL; ++void (*set_codec_speed)(int rate) = NULL; ++void (*reset_codec)(void) = NULL; ++void (*codec_mixer_old_info_id_name)(void) = NULL; ++void (*codec_mixer_info_id_name)(void) = NULL; ++void (*set_codec_bass)(int val) = NULL; ++void (*set_codec_volume)(int val) = NULL; ++void (*set_codec_mic)(int val) = NULL; ++void (*i2s_resume_codec)(void) = NULL; ++void (*i2s_suspend_codec)(int wr,int rd) = NULL; ++void (*init_codec_pin)(void) = NULL; ++void (*set_codec_some_func)(void) = NULL; ++void (*clear_codec_record)(void) = NULL; ++void (*clear_codec_replay)(void) = NULL; ++void (*set_replay_hp_or_speaker)(void) = NULL; ++void (*set_codec_direct_mode)(void) = NULL; ++void (*clear_codec_direct_mode)(void) = NULL; ++ ++static int jz_audio_rate; ++static int jz_audio_format; ++static int jz_audio_volume; ++static int jz_audio_channels; ++static int jz_audio_b; /* bits expand multiple */ ++static int jz_audio_fragments; /* unused fragment amount */ ++static int jz_audio_fragstotal; ++static int jz_audio_fragsize; ++static int jz_audio_speed; ++ ++static int codec_bass_gain; ++static int audio_mix_modcnt; ++static int jz_audio_dma_tran_count; /* bytes count of one DMA transfer */ ++static int jz_mic_only = 1; ++static int jz_codec_config = 0; ++static unsigned long ramp_up_start; ++static unsigned long ramp_up_end; ++static unsigned long gain_up_start; ++static unsigned long gain_up_end; ++static unsigned long ramp_down_start; ++static unsigned long ramp_down_end; ++static unsigned long gain_down_start; ++static unsigned long gain_down_end; ++ ++static int codec_mic_gain; ++static int pop_dma_flag; ++static int last_dma_buffer_id; ++static int drain_flag; ++ ++static void (*old_mksound)(unsigned int hz, unsigned int ticks); ++extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); ++static void jz_update_filler(int bits, int channels); ++ ++static int Init_In_Out_queue(int fragstotal,int fragsize); ++static int Free_In_Out_queue(int fragstotal,int fragsize); ++static irqreturn_t jz_i2s_replay_dma_irq(int irqnr, void *ref); ++static irqreturn_t jz_i2s_record_dma_irq(int irqnr, void *ref); ++static void (*replay_filler)(signed long src_start, int count, int id); ++static int (*record_filler)(unsigned long dst_start, int count, int id); ++#if defined(CONFIG_I2S_ICODEC) ++static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample); ++#endif ++static void jz_audio_reset(void); ++static struct file_operations jz_i2s_audio_fops; ++ ++static DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue); ++static DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue); ++static DECLARE_WAIT_QUEUE_HEAD (drain_wait_queue); ++static DECLARE_WAIT_QUEUE_HEAD (pop_wait_queue); ++ ++struct jz_i2s_controller_info ++{ ++ int io_base; ++ int dma1; /* for play */ ++ int dma2; /* for record */ ++ char *name; ++ int dev_audio; ++ struct i2s_codec *i2s_codec[NR_I2S]; ++ int opened1; ++ int opened2; ++ unsigned char *tmp1; /* tmp buffer for sample conversions */ ++ unsigned char *tmp2; ++ spinlock_t lock; ++ spinlock_t ioctllock; ++ ++ wait_queue_head_t dac_wait; ++ wait_queue_head_t adc_wait; ++ int nextIn; /* byte index to next-in to DMA buffer */ ++ int nextOut; /* byte index to next-out from DMA buffer */ ++ int count; /* current byte count in DMA buffer */ ++ int finish; /* current transfered byte count in DMA buffer */ ++ unsigned total_bytes; /* total bytes written or read */ ++ unsigned blocks; ++ unsigned error; /* over/underrun */ ++#ifdef CONFIG_PM ++ struct pm_dev *pm; ++#endif ++}; ++ ++ ++static struct jz_i2s_controller_info *i2s_controller = NULL; ++struct i2s_codec ++{ ++ /* I2S controller connected with */ ++ void *private_data; ++ char *name; ++ int id; ++ int dev_mixer; ++ /* controller specific lower leverl i2s accessing routines */ ++ u16 (*codec_read) (u8 reg); /* the function accessing Codec REGs */ ++ void (*codec_write) (u8 reg, u16 val); ++ /* Wait for codec-ready */ ++ void (*codec_wait) (struct i2s_codec *codec); ++ /* OSS mixer masks */ ++ int modcnt; ++ int supported_mixers; ++ int stereo_mixers; ++ int record_sources; ++ int bit_resolution; ++ /* OSS mixer interface */ ++ int (*read_mixer) (struct i2s_codec *codec, int oss_channel); ++ void (*write_mixer)(struct i2s_codec *codec, int oss_channel, ++ unsigned int left, unsigned int right); ++ int (*recmask_io) (struct i2s_codec *codec, int rw, int mask); ++ int (*mixer_ioctl)(struct i2s_codec *codec, unsigned int cmd, unsigned long arg); ++ /* saved OSS mixer states */ ++ unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; ++}; ++ ++ ++typedef struct buffer_queue_s ++{ ++ int count; ++ int *id; ++ int lock; ++} buffer_queue_t; ++ ++typedef struct left_right_sample_s ++{ ++ signed long left; ++ signed long right; ++} left_right_sample_t; ++ ++static unsigned long pop_turn_onoff_buf; ++static unsigned long pop_turn_onoff_pbuf; ++ ++static unsigned long *out_dma_buf = NULL; ++static unsigned long *out_dma_pbuf = NULL; ++static unsigned long *out_dma_buf_data_count = NULL; ++static unsigned long *in_dma_buf = NULL; ++static unsigned long *in_dma_pbuf = NULL; ++static unsigned long *in_dma_buf_data_count = NULL; ++ ++static buffer_queue_t out_empty_queue; ++static buffer_queue_t out_full_queue; ++static buffer_queue_t out_busy_queue; ++static buffer_queue_t in_empty_queue; ++static buffer_queue_t in_full_queue; ++static buffer_queue_t in_busy_queue; ++static int first_record_call = 0; ++ ++static left_right_sample_t save_last_samples[64]; ++static int read_codec_file(int addr) ++{ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ mdelay(1); ++ return(__icdc_get_value()); ++} ++ ++#if 0 /* mask warning */ ++static void printk_codec_files(void) ++{ ++ int cnt; ++ ++ printk("\n"); ++ ++ printk("REG_CPM_I2SCDR=0x%08x\n",REG_CPM_I2SCDR); ++ printk("REG_CPM_CLKGR=0x%08x\n",REG_CPM_CLKGR); ++ printk("REG_CPM_CPCCR=0x%08x\n",REG_CPM_CPCCR); ++ printk("REG_AIC_FR=0x%08x\n",REG_AIC_FR); ++ printk("REG_AIC_CR=0x%08x\n",REG_AIC_CR); ++ printk("REG_AIC_I2SCR=0x%08x\n",REG_AIC_I2SCR); ++ printk("REG_AIC_SR=0x%08x\n",REG_AIC_SR); ++ printk("REG_ICDC_RGDATA=0x%08x\n",REG_ICDC_RGDATA); ++ ++ for (cnt = 0; cnt <= 27 ; cnt++) { ++ printk(" ( %d : 0x%x ) ",cnt ,read_codec_file(cnt)); ++ } ++ printk("\n"); ++} ++#endif ++ ++static void write_codec_file(int addr, int val) ++{ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ __icdc_set_cmd(val); /* write */ ++ mdelay(1); ++ __icdc_set_rgwr(); ++ mdelay(1); ++} ++ ++static int write_codec_file_bit(int addr, int bitval, int mask_bit) ++{ ++ int val; ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ mdelay(1); ++ val = __icdc_get_value(); /* read */ ++ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ val &= ~(1 << mask_bit); ++ if (bitval == 1) ++ val |= 1 << mask_bit; ++ ++ __icdc_set_cmd(val); /* write */ ++ mdelay(1); ++ __icdc_set_rgwr(); ++ mdelay(1); ++ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ val = __icdc_get_value(); /* read */ ++ ++ if (((val >> mask_bit) & bitval) == bitval) ++ return 1; ++ else ++ return 0; ++} ++ ++#if 0 /* mask warning */ ++/* set Audio data replay */ ++static void set_audio_data_replay(void) ++{ ++ /* DAC path */ ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ mdelay(10); ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ //mdelay(100); ++ //write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //mdelay(300); ++} ++#endif ++ ++/* unset Audio data replay */ ++static void unset_audio_data_replay(void) ++{ ++ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ //mdelay(800); ++ //write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ //mdelay(800); ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 4);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++#if 0 /* mask warning */ ++/* set Record MIC input audio without playback */ ++static void set_record_mic_input_audio_without_playback(void) ++{ ++ /* ADC path for MIC IN */ ++ jz_mic_only = 1; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ mdelay(10); ++ write_codec_file_bit(1, 1, 2); ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ ++ write_codec_file(22, 0x40);//mic 1 ++ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ //write_codec_file_bit(6, 1, 3);// gain set ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ write_codec_file(1, 0x4); ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* unset Record MIC input audio without playback */ ++static void unset_record_mic_input_audio_without_playback(void) ++{ ++ /* ADC path for MIC IN */ ++ jz_mic_only = 0; ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* set Record LINE input audio without playback */ ++static void set_record_line_input_audio_without_playback(void) ++{ ++ /* ADC path for LINE IN */ ++ jz_mic_only = 1; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ mdelay(10); ++ write_codec_file(22, 0xf6);//line in 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1 ++ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ mdelay(10); ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ write_codec_file(1, 0x4); ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* unset Record LINE input audio without playback */ ++static void unset_record_line_input_audio_without_playback(void) ++{ ++ /* ADC path for LINE IN */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(5, 1, 3);//ONR1.SB_LIN->1 ++ ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* set Playback LINE input audio direct only */ ++static void set_playback_line_input_audio_direct_only(void) ++{ ++ jz_audio_reset();//or init_codec() ++ REG_AIC_I2SCR = 0x10; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ mdelay(10); ++ write_codec_file(22, 0xf6);//line in 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ mdelay(10); ++ write_codec_file_bit(1, 1, 2);//CR1.HP_BYPASS->1 ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ //write_codec_file_bit(5, 1, 7);//PMR1.SB_DAC->1 ++ //write_codec_file_bit(5, 1, 4);//PMR1.SB_ADC->1 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* unset Playback LINE input audio direct only */ ++static void unset_playback_line_input_audio_direct_only(void) ++{ ++ write_codec_file_bit(6, 0, 3);//GIM->0 ++ write_codec_file_bit(1, 0, 2);//PMR1.BYPASS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LINE->1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ mdelay(100); ++ write_codec_file_bit(5, 1, 5);//PMR1.SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* set Record MIC input audio with direct playback */ ++static void set_record_mic_input_audio_with_direct_playback(void) ++{ ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ jz_mic_only = 0; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ mdelay(10); ++ ++ write_codec_file(22, 0x60);//mic 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ write_codec_file_bit(6, 1, 3);// gain set ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ //write_codec_file(1, 0x4); ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* unset Record MIC input audio with direct playback */ ++static void unset_record_mic_input_audio_with_direct_playback(void) ++{ ++ /* ADC path for MIC IN */ ++ jz_mic_only = 0; ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* set Record playing audio mixed with MIC input audio */ ++static void set_record_playing_audio_mixed_with_mic_input_audio(void) ++{ ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ mdelay(10); ++ ++ write_codec_file(22, 0x63);//mic 1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(6, 1, 3);// gain set ++ ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 ++ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0 ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ write_codec_file_bit(5, 0, 4);//PMR1.SB_MIX->0 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* unset Record playing audio mixed with MIC input audio */ ++static void unset_record_playing_audio_mixed_with_mic_input_audio(void) ++{ ++ /* ADC path */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 ++ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ //write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 5);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* set Record MIC input audio with Audio data replay (full duplex) */ ++static void set_record_mic_input_audio_with_audio_data_replay(void) ++{ ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0 ++ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 ++ ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* unset Record MIC input audio with Audio data replay (full duplex) */ ++static void unset_record_mic_input_audio_with_audio_data_replay(void) ++{ ++ /* ADC path */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 5);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* set Record LINE input audio with Audio data replay (full duplex for linein) */ ++static void set_record_line_input_audio_with_audio_data_replay(void) ++{ ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(22, 1, 7);//CR3.SB_MIC->1 ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ ++ ++ //jz_mic_only = 1; ++ write_codec_file(22, 0xc6);//line in 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* unset Record LINE input audio with Audio data replay (full duplex for linein) */ ++static void unset_record_line_input_audio_with_audio_data_replay(void) ++{ ++ /* ADC path */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 5);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++static inline int get_buffer_id(struct buffer_queue_s *q) ++{ ++ int r; ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&q->lock, flags); ++ if (q->count == 0) { ++ spin_unlock_irqrestore(&q->lock, flags); ++ return -1; ++ } ++ r = *(q->id + 0); ++ for (i=0;i < q->count-1;i++) ++ *(q->id + i) = *(q->id + (i+1)); ++ q->count --; ++ spin_unlock_irqrestore(&q->lock, flags); ++ ++ return r; ++} ++ ++static inline void put_buffer_id(struct buffer_queue_s *q, int id) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&q->lock, flags); ++ *(q->id + q->count) = id; ++ q->count ++; ++ spin_unlock_irqrestore(&q->lock, flags); ++} ++ ++static inline int elements_in_queue(struct buffer_queue_s *q) ++{ ++ int r; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&q->lock, flags); ++ r = q->count; ++ spin_unlock_irqrestore(&q->lock, flags); ++ ++ return r; ++} ++ ++static inline void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode) ++{ ++ unsigned long flags; ++ struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ jz_audio_dma_tran_count = count / jz_audio_b; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ flags = claim_dma_lock(); ++ disable_dma(chan); ++ clear_dma_ff(chan); ++ //set_dma_mode(chan, mode); ++ jz_set_oss_dma(chan, mode, jz_audio_format); ++ set_dma_addr(chan, phyaddr); ++ if (count == 0) { ++ count++; ++ printk("JzSOC DMA controller can't set dma 0 count!\n"); ++ } ++ set_dma_count(chan, count); ++ enable_dma(chan); ++ release_dma_lock(flags); ++} ++ ++static irqreturn_t jz_i2s_record_dma_irq (int irq, void *dev_id) ++{ ++ int id1, id2; ++ unsigned long flags; ++ struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; ++ int dma = controller->dma2; ++ ++ disable_dma(dma); ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ ++ if(drain_flag == 1) ++ wake_up(&drain_wait_queue); ++ /* for DSP_GETIPTR */ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ id1 = get_buffer_id(&in_busy_queue); ++ put_buffer_id(&in_full_queue, id1); ++ ++ wake_up(&rx_wait_queue); ++ wake_up(&controller->adc_wait); ++ if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id2); ++ *(in_dma_buf_data_count + id2) = *(in_dma_buf_data_count + id1); ++ dma_cache_wback_inv(*(in_dma_buf + id2), *(in_dma_buf_data_count + id2)); ++ audio_start_dma(dma,dev_id, ++ *(in_dma_pbuf + id2), ++ *(in_dma_buf_data_count + id2), ++ DMA_MODE_READ); ++ } else ++ in_busy_queue.count = 0; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t jz_i2s_replay_dma_irq (int irq, void *dev_id) ++{ ++ int id; ++ unsigned long flags; ++ struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; ++ int dma = controller->dma1; ++ ++ disable_dma(dma); ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ ++ if(pop_dma_flag == 1) { ++ pop_dma_flag = 0; ++ wake_up(&pop_wait_queue); ++ } else { ++ if(drain_flag == 1) { ++ /* Is replay dma buffer over ? */ ++ if(elements_in_queue(&out_full_queue) <= 0) { ++ drain_flag = 0; ++ wake_up(&drain_wait_queue); ++ } ++ } ++ ++ /* for DSP_GETOPTR */ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ if ((id = get_buffer_id(&out_busy_queue)) < 0) ++ printk(KERN_DEBUG "Strange DMA finish interrupt for I2S module\n"); ++ put_buffer_id(&out_empty_queue, id); ++ if ((id = get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_busy_queue, id); ++ if(*(out_dma_buf_data_count + id) > 0) { ++ audio_start_dma(dma, dev_id, *(out_dma_pbuf + id), ++ *(out_dma_buf_data_count + id), ++ DMA_MODE_WRITE); ++ last_dma_buffer_id = id; ++ } ++ } else ++ out_busy_queue.count = 0; ++ ++ if (elements_in_queue(&out_empty_queue) > 0) { ++ wake_up(&tx_wait_queue); ++ wake_up(&controller->dac_wait); ++ } ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void jz_i2s_initHw(int set) ++{ ++#if defined(CONFIG_MIPS_JZ_URANUS) ++ i2s_clk = 48000000; ++#else ++ i2s_clk = __cpm_get_i2sclk(); ++#endif ++ __i2s_disable(); ++ if(set) ++ __i2s_reset(); ++ schedule_timeout(5); ++ if(each_time_init_codec) ++ each_time_init_codec(); ++ __i2s_disable_record(); ++ __i2s_disable_replay(); ++ __i2s_disable_loopback(); ++ __i2s_set_transmit_trigger(4); ++ __i2s_set_receive_trigger(3); ++} ++ ++static int Init_In_Out_queue(int fragstotal,int fragsize) ++{ ++ int i; ++ ++ /* recording */ ++ in_empty_queue.count = fragstotal; ++ in_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_buf) ++ goto all_mem_err; ++ in_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_pbuf) ++ goto all_mem_err; ++ in_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_buf_data_count) ++ goto all_mem_err; ++ in_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_empty_queue.id) ++ goto all_mem_err; ++ in_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_full_queue.id) ++ goto all_mem_err; ++ in_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_busy_queue.id) ++ goto all_mem_err; ++ ++ for (i=0;i < fragstotal;i++) ++ *(in_empty_queue.id + i) = i; ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ ++ for (i = 0; i < fragstotal; i++) { ++ *(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); ++ if (*(in_dma_buf + i) == 0) ++ goto mem_failed_in; ++ *(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i))); ++ dma_cache_wback_inv(*(in_dma_buf + i), fragsize); ++ } ++ ++ /* playing */ ++ out_empty_queue.count = fragstotal; ++ out_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!out_dma_buf) ++ goto all_mem_err; ++ out_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!out_dma_pbuf) ++ goto all_mem_err; ++ out_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ ++ if (!out_dma_buf_data_count) ++ goto all_mem_err; ++ out_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_empty_queue.id) ++ goto all_mem_err; ++ out_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_full_queue.id) ++ goto all_mem_err; ++ out_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_busy_queue.id) ++ goto all_mem_err; ++ for (i=0;i < fragstotal;i++) ++ *(out_empty_queue.id + i) = i; ++ ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ /* alloc DMA buffer */ ++ for (i = 0; i < fragstotal; i++) { ++ *(out_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); ++ if (*(out_dma_buf + i) == 0) { ++ printk(" can't allocate required DMA(OUT) buffers.\n"); ++ goto mem_failed_out; ++ } ++ *(out_dma_pbuf + i) = virt_to_phys((void *)(*(out_dma_buf + i))); ++ } ++ ++ return 1; ++all_mem_err: ++ printk("error:allocate memory occur error 1!\n"); ++ return 0; ++mem_failed_out: ++ printk("error:allocate memory occur error 2!\n"); ++ for (i = 0; i < fragstotal; i++) { ++ if(*(out_dma_buf + i)) ++ free_pages(*(out_dma_buf + i), get_order(fragsize)); ++ } ++ ++ return 0; ++mem_failed_in: ++ printk("error:allocate memory occur error 3!\n"); ++ for (i = 0; i < fragstotal; i++) { ++ if(*(in_dma_buf + i)) ++ free_pages(*(in_dma_buf + i), get_order(fragsize)); ++ } ++ return 0; ++} ++ ++static int Free_In_Out_queue(int fragstotal,int fragsize) ++{ ++ int i; ++ /* playing */ ++ if(out_dma_buf != NULL) { ++ for (i = 0; i < fragstotal; i++) { ++ if(*(out_dma_buf + i)) ++ free_pages(*(out_dma_buf + i), get_order(fragsize)); ++ *(out_dma_buf + i) = 0; ++ } ++ kfree(out_dma_buf); ++ out_dma_buf = NULL; ++ } ++ if(out_dma_pbuf) { ++ kfree(out_dma_pbuf); ++ out_dma_pbuf = NULL; ++ } ++ if(out_dma_buf_data_count) { ++ kfree(out_dma_buf_data_count); ++ out_dma_buf_data_count = NULL; ++ } ++ if(out_empty_queue.id) { ++ kfree(out_empty_queue.id); ++ out_empty_queue.id = NULL; ++ } ++ if(out_full_queue.id) { ++ kfree(out_full_queue.id); ++ out_full_queue.id = NULL; ++ } ++ if(out_busy_queue.id) { ++ kfree(out_busy_queue.id); ++ out_busy_queue.id = NULL; ++ } ++ out_empty_queue.count = fragstotal; ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ ++ /* recording */ ++ if(in_dma_buf) { ++ for (i = 0; i < fragstotal; i++) { ++ if(*(in_dma_buf + i)) { ++ dma_cache_wback_inv(*(in_dma_buf + i), fragsize); ++ free_pages(*(in_dma_buf + i), get_order(fragsize)); ++ } ++ *(in_dma_buf + i) = 0; ++ } ++ kfree(in_dma_buf); ++ in_dma_buf = NULL; ++ } ++ if(in_dma_pbuf) { ++ kfree(in_dma_pbuf); ++ in_dma_pbuf = NULL; ++ } ++ if(in_dma_buf_data_count) { ++ kfree(in_dma_buf_data_count); ++ in_dma_buf_data_count = NULL; ++ } ++ if(in_empty_queue.id) { ++ kfree(in_empty_queue.id); ++ in_empty_queue.id = NULL; ++ } ++ if(in_full_queue.id) { ++ kfree(in_full_queue.id); ++ in_full_queue.id = NULL; ++ } ++ if(in_busy_queue.id) { ++ kfree(in_busy_queue.id); ++ in_busy_queue.id = NULL; ++ } ++ ++ in_empty_queue.count = fragstotal; ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ ++ return 1; ++} ++ ++static void jz_i2s_full_reset(struct jz_i2s_controller_info *controller) ++{ ++ jz_i2s_initHw(0); ++} ++ ++static int jz_audio_set_speed(int dev, int rate) ++{ ++ /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 ? */ ++ jz_audio_speed = rate; ++ if (rate > 48000) ++ rate = 48000; ++ if (rate < 8000) ++ rate = 8000; ++ jz_audio_rate = rate; ++ ++ if(set_codec_speed) ++ set_codec_speed(rate); ++ ++ return jz_audio_rate; ++} ++ ++ ++static int record_fill_1x8_u(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long data; ++ volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ volatile unsigned char *dp = (unsigned char*)dst_start; ++ ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ cnt++; ++ data = *(s++); ++ *(dp ++) = ((data << 16) >> 24) + 0x80; ++ s++; /* skip the other channel */ ++ } ++ ++ return cnt; ++} ++ ++ ++static int record_fill_2x8_u(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long d1, d2; ++ volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ volatile unsigned char *dp = (unsigned char*)dst_start; ++ ++ while (count > 0) { ++ count -= 2; ++ cnt += 2; ++ d1 = *(s++); ++ *(dp ++) = ((d1 << 16) >> 24) + 0x80; ++ d2 = *(s++); ++ *(dp ++) = ((d2 << 16) >> 24) + 0x80; ++ } ++ ++ return cnt; ++} ++ ++ ++static int record_fill_1x16_s(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long d1; ++ unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ unsigned short *dp = (unsigned short *)dst_start; ++ ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ cnt += 2; /* count in byte */ ++ d1 = *(s++); ++ *(dp ++) = (d1 << 16) >> 16; ++ s++; /* skip the other channel */ ++ } ++ ++ return cnt; ++} ++ ++ ++static int record_fill_2x16_s(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long d1, d2; ++ unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ unsigned short *dp = (unsigned short *)dst_start; ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ cnt += 4; /* count in byte */ ++ d1 = *(s++); ++ d2 = *(s++); ++ if(abnormal_data_count > 0) { ++ d1 = d2 = 0; ++ abnormal_data_count --; ++ } ++ *(dp ++) = (d1 << 16) >> 16; ++ *(dp ++) = (d2 << 16) >> 16; ++ } ++ ++ return cnt; ++} ++ ++static void replay_fill_1x8_u(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned char data; ++ unsigned long ddata; ++ volatile unsigned char *s = (unsigned char *)src_start; ++ volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); ++ ++ while (count > 0) { ++ count--; ++ cnt += 1; ++ data = *(s++) - 0x80; ++ ddata = (unsigned long) data << 8; ++ *(dp ++) = ddata; ++ *(dp ++) = ddata; ++ ++ /* save last left and right */ ++ if(count == 1) { ++ save_last_samples[id].left = ddata; ++ save_last_samples[id].right = ddata; ++ } ++ } ++ cnt = cnt * 2 * jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++ ++static void replay_fill_2x8_u(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned char d1; ++ unsigned long dd1; ++ volatile unsigned char *s = (unsigned char *)src_start; ++ volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); ++ ++ while (count > 0) { ++ count -= 1; ++ cnt += 1 ; ++ d1 = *(s++) - 0x80; ++ dd1 = (unsigned long) d1 << 8; ++ *(dp ++) = dd1; ++ /* save last left */ ++ if(count == 2) ++ save_last_samples[id].left = dd1; ++ /* save last right */ ++ if(count == 1) ++ save_last_samples[id].right = dd1; ++ } ++ cnt *= jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++ ++static void replay_fill_1x16_s(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ signed short d1; ++ signed long l1; ++ volatile signed short *s = (signed short *)src_start; ++ volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); ++ ++ while (count > 0) { ++ count -= 2; ++ cnt += 2 ; ++ d1 = *(s++); ++ l1 = (signed long)d1; ++ *(dp ++) = l1; ++ *(dp ++) = l1; ++ ++ /* save last left and right */ ++ if(count == 1) { ++ save_last_samples[id].left = l1; ++ save_last_samples[id].right = l1; ++ } ++ } ++ cnt = cnt * 2 * jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++#if 0 ++static void replay_fill_2x16_s(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ signed short d1; ++ signed long l1; ++ int mute_cnt = 0; ++ signed long tmp1,tmp2; ++ volatile signed short *s = (signed short *)src_start; ++ volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); ++#if defined(CONFIG_I2S_ICDC) ++ volatile signed long *before_dp; ++ int sam_rate = jz_audio_rate / 20; ++ ++ tmp1 = tmp2 = 0; ++ while (count > 0) { ++ count -= 2; ++ cnt += 2; ++ d1 = *(s++); ++ ++ l1 = (signed long)d1; ++ l1 >>= codec_volue_shift; ++ ++ if(l1 == 0) { ++ mute_cnt ++; ++ if(mute_cnt >= sam_rate) { ++ before_dp = dp - 10; ++ *(before_dp) = (signed long)1; ++ before_dp = dp - 11; ++ *(before_dp) = (signed long)1; ++ mute_cnt = 0; ++ } ++ } else ++ mute_cnt = 0; ++ ++ *(dp ++) = l1; ++ ++ tmp1 = tmp2; ++ tmp2 = l1; ++ } ++ ++ /* save last left */ ++ save_last_samples[id].left = tmp1; ++ /* save last right */ ++ save_last_samples[id].right = tmp2; ++#endif ++#if defined(CONFIG_I2S_DLV) ++ while (count > 0) { ++ count -= 2; ++ cnt += 2; ++ d1 = *(s++); ++ ++ l1 = (signed long)d1; ++ ++ *(dp ++) = l1; ++ } ++#endif ++ cnt *= jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++#else ++static void replay_fill_2x16_s(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ signed short d1; ++ signed long l1; ++ ++#if 0 ++ volatile signed short *s = (signed short *)src_start; ++ volatile signed short *dp = (signed short*)(*(out_dma_buf + id)); ++ memcpy((char*)dp, (char*)s, count); ++ *(out_dma_buf_data_count + id) = count; ++#else ++ volatile signed short *s = (signed short *)src_start; ++ volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); ++ while (count > 0) { ++ count -= 2; ++ cnt += 2; ++ d1 = *(s++); ++ ++ l1 = (signed long)d1; ++ ++ *(dp ++) = l1; ++ } ++ cnt *= jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++#endif ++} ++#endif ++ ++ ++static unsigned int jz_audio_set_format(int dev, unsigned int fmt) ++{ ++ switch (fmt) { ++ case AFMT_U8: ++ __i2s_set_oss_sample_size(8); ++ __i2s_set_iss_sample_size(8); ++ jz_audio_format = fmt; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++ break; ++ case AFMT_S16_LE: ++#if defined(CONFIG_I2S_DLV) ++ /* DAC path and ADC path */ ++ write_codec_file(2, 0x00); ++ //write_codec_file(2, 0x60); ++#endif ++ jz_audio_format = fmt; ++ jz_update_filler(jz_audio_format,jz_audio_channels); ++ /* print all files */ ++ __i2s_set_oss_sample_size(16); ++ __i2s_set_iss_sample_size(16); ++ break; ++ ++ case AFMT_QUERY: ++ break; ++ } ++ ++ return jz_audio_format; ++} ++ ++ ++static short jz_audio_set_channels(int dev, short channels) ++{ ++ switch (channels) { ++ case 1: ++ if(set_codec_some_func) ++ set_codec_some_func(); ++ jz_audio_channels = channels; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(1, 1, 6);//CR1.MONO->1 for Mono ++#endif ++ break; ++ case 2: ++ jz_audio_channels = channels; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(1, 0, 6);//CR1.MONO->0 for Stereo ++#endif ++ break; ++ case 0: ++ break; ++ } ++ ++ return jz_audio_channels; ++} ++ ++static void init_codec(void) ++{ ++ /* inititalize internal I2S codec */ ++ if(init_codec_pin) ++ init_codec_pin(); ++ ++#if defined(CONFIG_I2S_ICDC) ++ /* initialize AIC but not reset it */ ++ jz_i2s_initHw(0); ++#endif ++ if(reset_codec) ++ reset_codec(); ++} ++ ++static void jz_audio_reset(void) ++{ ++ __i2s_disable_replay(); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_record(); ++ __i2s_disable_transmit_dma(); ++#if defined(CONFIG_I2S_DLV) ++ REG_AIC_I2SCR = 0x10; ++#endif ++ init_codec(); ++} ++ ++static int jz_audio_release(struct inode *inode, struct file *file); ++static int jz_audio_open(struct inode *inode, struct file *file); ++static int jz_audio_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg); ++static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait); ++static ssize_t jz_audio_write(struct file *file, const char *buffer,size_t count, loff_t *ppos); ++static ssize_t jz_audio_read(struct file *file, char *buffer,size_t count, loff_t *ppos); ++ ++/* static struct file_operations jz_i2s_audio_fops */ ++static struct file_operations jz_i2s_audio_fops = ++{ ++ owner: THIS_MODULE, ++ open: jz_audio_open, ++ release: jz_audio_release, ++ write: jz_audio_write, ++ read: jz_audio_read, ++ poll: jz_audio_poll, ++ ioctl: jz_audio_ioctl ++}; ++ ++static int jz_i2s_open_mixdev(struct inode *inode, struct file *file) ++{ ++ int i; ++ int minor = MINOR(inode->i_rdev); ++ struct jz_i2s_controller_info *controller = i2s_controller; ++ ++ for (i = 0; i < NR_I2S; i++) ++ if (controller->i2s_codec[i] != NULL && controller->i2s_codec[i]->dev_mixer == minor) ++ goto match; ++ ++ if (!controller) ++ return -ENODEV; ++match: ++ file->private_data = controller->i2s_codec[i]; ++ ++ return 0; ++} ++ ++static int jz_i2s_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct i2s_codec *codec = (struct i2s_codec *)file->private_data; ++ return codec->mixer_ioctl(codec, cmd, arg); ++} ++ ++static loff_t jz_i2s_llseek(struct file *file, loff_t offset, int origin) ++{ ++ return -ESPIPE; ++} ++ ++static struct file_operations jz_i2s_mixer_fops = ++{ ++ owner: THIS_MODULE, ++ llseek: jz_i2s_llseek, ++ ioctl: jz_i2s_ioctl_mixdev, ++ open: jz_i2s_open_mixdev, ++}; ++ ++static int i2s_mixer_ioctl(struct i2s_codec *codec, unsigned int cmd, unsigned long arg) ++{ ++ int ret; ++ long val = 0; ++ switch (cmd) { ++ case SOUND_MIXER_INFO: ++ ++ if(codec_mixer_info_id_name) ++ codec_mixer_info_id_name(); ++ info.modify_counter = audio_mix_modcnt; ++ ++ return copy_to_user((void *)arg, &info, sizeof(info)); ++ case SOUND_OLD_MIXER_INFO: ++ ++ if(codec_mixer_old_info_id_name) ++ codec_mixer_old_info_id_name(); ++ ++ return copy_to_user((void *)arg, &old_info, sizeof(info)); ++ case SOUND_MIXER_READ_STEREODEVS: ++ ++ return put_user(0, (long *) arg); ++ case SOUND_MIXER_READ_CAPS: ++ ++ val = SOUND_CAP_EXCL_INPUT; ++ return put_user(val, (long *) arg); ++ case SOUND_MIXER_READ_DEVMASK: ++ break; ++ case SOUND_MIXER_READ_RECMASK: ++ break; ++ case SOUND_MIXER_READ_RECSRC: ++ break; ++ case SOUND_MIXER_WRITE_SPEAKER: ++ ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ switch(val) { ++ case 100: ++ if(set_codec_direct_mode) ++ set_codec_direct_mode(); ++ break; ++ case 0: ++ if(clear_codec_direct_mode) ++ clear_codec_direct_mode(); ++ break; ++ } ++ break; ++ case SOUND_MIXER_WRITE_BASS: ++ ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ codec_bass_gain = val; ++ if(set_codec_bass) ++ set_codec_bass(val); ++ ++ return 0; ++ case SOUND_MIXER_READ_BASS: ++ ++ val = codec_bass_gain; ++ ret = val << 8; ++ val = val | ret; ++ ++ return put_user(val, (long *) arg); ++ case SOUND_MIXER_WRITE_VOLUME: ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ if (val > 31) ++ val = 31; ++ jz_audio_volume = val; ++ ++ if(set_codec_volume) ++ set_codec_volume(val); ++ return 0; ++ case SOUND_MIXER_READ_VOLUME: ++ ++ val = jz_audio_volume; ++ ret = val << 8; ++ val = val | ret; ++ ++ return put_user(val, (long *) arg); ++ case SOUND_MIXER_WRITE_MIC: ++ ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ codec_mic_gain = val; ++ if(set_codec_mic) ++ set_codec_mic(val); ++ ++ return 0; ++ case SOUND_MIXER_READ_MIC: ++ ++ val = codec_mic_gain; ++ ret = val << 8; ++ val = val | ret; ++ ++ return put_user(val, (long *) arg); ++ default: ++ return -ENOSYS; ++ } ++ audio_mix_modcnt ++; ++ return 0; ++} ++ ++ ++int i2s_probe_codec(struct i2s_codec *codec) ++{ ++ /* generic OSS to I2S wrapper */ ++ codec->mixer_ioctl = i2s_mixer_ioctl; ++ return 1; ++} ++ ++ ++/* I2S codec initialisation. */ ++static int __init jz_i2s_codec_init(struct jz_i2s_controller_info *controller) ++{ ++ int num_i2s = 0; ++ struct i2s_codec *codec; ++ ++ for (num_i2s = 0; num_i2s < NR_I2S; num_i2s++) { ++ if ((codec = kmalloc(sizeof(struct i2s_codec),GFP_KERNEL)) == NULL) ++ return -ENOMEM; ++ memset(codec, 0, sizeof(struct i2s_codec)); ++ codec->private_data = controller; ++ codec->id = num_i2s; ++ ++ if (i2s_probe_codec(codec) == 0) ++ break; ++ if ((codec->dev_mixer = register_sound_mixer(&jz_i2s_mixer_fops, -1)) < 0) { ++ printk(KERN_ERR "Jz I2S: couldn't register mixer!\n"); ++ kfree(codec); ++ break; ++ } ++ controller->i2s_codec[num_i2s] = codec; ++ } ++ return num_i2s; ++} ++ ++ ++static void jz_update_filler(int format, int channels) ++{ ++#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) ++ ++ switch (TYPE(format, channels)) ++ { ++ ++ case TYPE(AFMT_U8, 1): ++ jz_audio_b = 4; /* 4bytes * 8bits =32bits */ ++ replay_filler = replay_fill_1x8_u; ++ record_filler = record_fill_1x8_u; ++ break; ++ case TYPE(AFMT_U8, 2): ++ jz_audio_b = 4; ++ replay_filler = replay_fill_2x8_u; ++ record_filler = record_fill_2x8_u; ++ break; ++ case TYPE(AFMT_S16_LE, 1): ++ jz_audio_b = 2; /* 2bytes * 16bits =32bits */ ++ replay_filler = replay_fill_1x16_s; ++ record_filler = record_fill_1x16_s; ++ break; ++ case TYPE(AFMT_S16_LE, 2): ++ jz_audio_b = 2; ++ replay_filler = replay_fill_2x16_s; ++ record_filler = record_fill_2x16_s; ++ break; ++ default: ++ ; ++ } ++} ++ ++ ++#ifdef CONFIG_PROC_FS ++extern struct proc_dir_entry *proc_jz_root; ++int i2s_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) ++{ ++ return 0; ++} ++ ++static int jz_i2s_init_proc(struct jz_i2s_controller_info *controller) ++{ ++ if (!create_proc_read_entry ("i2s", 0, proc_jz_root, i2s_read_proc, controller->i2s_codec[0])) ++ return -EIO; ++ return 0; ++} ++ ++static void jz_i2s_cleanup_proc(struct jz_i2s_controller_info *controller) ++{ ++} ++#endif ++ ++static void __init attach_jz_i2s(struct jz_i2s_controller_info *controller) ++{ ++ char *name; ++ int adev; /* No of Audio device. */ ++ ++ name = controller->name; ++ /* initialize AIC controller and reset it */ ++ jz_i2s_initHw(1); ++ adev = register_sound_dsp(&jz_i2s_audio_fops, -1); ++ if (adev < 0) ++ goto audio_failed; ++ /* initialize I2S codec and register /dev/mixer */ ++ if (jz_i2s_codec_init(controller) <= 0) ++ goto mixer_failed; ++ ++#ifdef CONFIG_PROC_FS ++ if (jz_i2s_init_proc(controller) < 0) { ++ printk(KERN_ERR "%s: can't create I2S proc filesystem.\n", name); ++ goto proc_failed; ++ } ++#endif ++ ++ controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, 8); ++ if (!controller->tmp1) { ++ printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); ++ goto tmp1_failed; ++ } ++ controller->tmp2 = (void *)__get_free_pages(GFP_KERNEL, 8); ++ if (!controller->tmp2) { ++ printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); ++ goto tmp2_failed; ++ } ++ if ((controller->dma2 = jz_request_dma(DMA_ID_I2S_RX, "audio adc", jz_i2s_record_dma_irq, IRQF_DISABLED, controller)) < 0) { ++ printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name); ++ goto dma2_failed; ++ } ++ if ((controller->dma1 = jz_request_dma(DMA_ID_I2S_TX, "audio dac", jz_i2s_replay_dma_irq, IRQF_DISABLED, controller)) < 0) { ++ printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name); ++ goto dma1_failed; ++ } ++ printk("JzSOC On-Chip I2S controller registered (DAC: DMA(play):%d/IRQ%d,\n ADC: DMA(record):%d/IRQ%d)\n", controller->dma1, get_dma_done_irq(controller->dma1), controller->dma2, get_dma_done_irq(controller->dma2)); ++ ++ controller->dev_audio = adev; ++ pop_turn_onoff_buf = __get_free_pages(GFP_KERNEL | GFP_DMA, 8); ++ if(!pop_turn_onoff_buf) ++ printk("pop_turn_onoff_buf alloc is wrong!\n"); ++ pop_turn_onoff_pbuf = virt_to_phys((void *)pop_turn_onoff_buf); ++ ++ return; ++dma2_failed: ++ jz_free_dma(controller->dma1); ++dma1_failed: ++ free_pages((unsigned long)controller->tmp2, 8); ++tmp2_failed: ++ free_pages((unsigned long)controller->tmp1, 8); ++tmp1_failed: ++ ++#ifdef CONFIG_PROC_FS ++ jz_i2s_cleanup_proc(controller); ++#endif ++proc_failed: ++ /* unregister mixer dev */ ++mixer_failed: ++ unregister_sound_dsp(adev); ++audio_failed: ++ return; ++} ++ ++static int __init probe_jz_i2s(struct jz_i2s_controller_info **controller) ++{ ++ if ((*controller = kmalloc(sizeof(struct jz_i2s_controller_info), ++ GFP_KERNEL)) == NULL) { ++ printk(KERN_ERR "Jz I2S Controller: out of memory.\n"); ++ return -ENOMEM; ++ } ++ (*controller)->name = "Jz I2S controller"; ++ (*controller)->opened1 = 0; ++ (*controller)->opened2 = 0; ++ init_waitqueue_head(&(*controller)->adc_wait); ++ init_waitqueue_head(&(*controller)->dac_wait); ++ spin_lock_init(&(*controller)->lock); ++ init_waitqueue_head(&rx_wait_queue); ++ init_waitqueue_head(&tx_wait_queue); ++ init_waitqueue_head(&pop_wait_queue); ++ init_waitqueue_head(&drain_wait_queue); ++ ++ return 0; ++} ++ ++static void __exit unload_jz_i2s(struct jz_i2s_controller_info *controller) ++{ ++ int adev = controller->dev_audio; ++ ++ jz_i2s_full_reset(controller); ++ controller->dev_audio = -1; ++ if (old_mksound) ++ kd_mksound = old_mksound;/* Our driver support bell for kb, see vt.c */ ++ ++#ifdef CONFIG_PROC_FS ++ jz_i2s_cleanup_proc(controller); ++#endif ++ ++ jz_free_dma(controller->dma1); ++ jz_free_dma(controller->dma2); ++ free_pages((unsigned long)controller->tmp1, 8); ++ free_pages((unsigned long)controller->tmp2, 8); ++ free_pages((unsigned long)pop_turn_onoff_buf, 8); ++ ++ if (adev >= 0) { ++ /* unregister_sound_mixer(audio_devs[adev]->mixer_dev); */ ++ unregister_sound_dsp(controller->dev_audio); ++ } ++} ++ ++#ifdef CONFIG_PM ++static int jz_i2s_suspend(struct jz_i2s_controller_info *controller, int state) ++{ ++ if(i2s_suspend_codec) ++ i2s_suspend_codec(controller->opened1,controller->opened2); ++ printk("Aic and codec are suspended!\n"); ++ return 0; ++} ++ ++static int jz_i2s_resume(struct jz_i2s_controller_info *controller) ++{ ++ if(i2s_resume_codec) ++ i2s_resume_codec(); ++ ++#if defined(CONFIG_I2S_AK4642EN) ++ jz_i2s_initHw(0); ++ jz_audio_reset(); ++ __i2s_enable(); ++ jz_audio_set_speed(controller->dev_audio,jz_audio_speed); ++ /* playing */ ++ if(controller->opened1) { ++ if(set_codec_replay) ++ set_codec_replay(); ++ int dma = controller->dma1; ++ int id; ++ unsigned long flags; ++ disable_dma(dma); ++ if(__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if(__dmac_channel_transmit_end_detected(dma)) ++ __dmac_channel_clear_transmit_end(dma); ++ ++ /* for DSP_GETOPTR */ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ while((id = get_buffer_id(&out_busy_queue)) >= 0) ++ put_buffer_id(&out_empty_queue, id); ++ ++ out_busy_queue.count=0; ++ if((id = get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_empty_queue, id); ++ } ++ if (elements_in_queue(&out_empty_queue) > 0) { ++ wake_up(&tx_wait_queue); ++ wake_up(&controller->dac_wait); ++ } else ++ printk("pm out_empty_queue empty"); ++ } ++ ++ /* recording */ ++ if(controller->opened2) { ++ if(set_codec_record) ++ set_codec_record(); ++ int dma = controller->dma2; ++ int id1, id2; ++ unsigned long flags; ++ disable_dma(dma); ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ } ++ /* for DSP_GETIPTR */ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ id1 = get_buffer_id(&in_busy_queue); ++ put_buffer_id(&in_full_queue, id1); ++ wake_up(&rx_wait_queue); ++ wake_up(&controller->adc_wait); ++ if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_full_queue, id2); ++ } ++ in_busy_queue.count = 0; ++ } ++#endif ++ ++ return 0; ++} ++ ++static int jz_i2s_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) ++{ ++ int ret; ++ struct jz_i2s_controller_info *controller = pm_dev->data; ++ ++ if (!controller) return -EINVAL; ++ ++ switch (req) { ++ case PM_SUSPEND: ++ ret = jz_i2s_suspend(controller, (int)data); ++ break; ++ case PM_RESUME: ++ ret = jz_i2s_resume(controller); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++#endif /* CONFIG_PM */ ++static irqreturn_t aic_codec_irq(int irq, void *dev_id) ++{ ++ u8 file_9 = read_codec_file(9); ++ u8 file_8 = read_codec_file(8); ++ ++ //printk("--- 8:0x%x 9:0x%x ---\n",file_8,file_9); ++ if ((file_9 & 0x1f) == 0x10) { ++ ++ write_codec_file(8, 0x3f); ++ write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ mdelay(300); ++ while ((read_codec_file(9) & 0x4) != 0x4); ++ while ((read_codec_file(9) & 0x10) == 0x10) { ++ write_codec_file(9, 0x10); ++ } ++ write_codec_file_bit(5, 0, 6);//SB_OUT->0 ++ mdelay(300); ++ while ((read_codec_file(9) & 0x8) != 0x8); ++ write_codec_file(9, file_9); ++ write_codec_file(8, file_8); ++ ++ return IRQ_HANDLED; ++ } ++ ++ if (file_9 & 0x8) ++ ramp_up_end = jiffies; ++ else if (file_9 & 0x4) ++ ramp_down_end = jiffies; ++ else if (file_9 & 0x2) ++ gain_up_end = jiffies; ++ else if (file_9 & 0x1) ++ gain_down_end = jiffies; ++ ++ write_codec_file(9, file_9); ++ if (file_9 & 0xf) ++ wake_up(&pop_wait_queue); ++ while (REG_ICDC_RGDATA & 0x100); ++ ++ return IRQ_HANDLED; ++} ++ ++static int __init init_jz_i2s(void) ++{ ++ int errno, retval; ++#if defined(CONFIG_I2S_DLV) ++ ++ ramp_up_start = 0; ++ ramp_up_end = 0; ++ gain_up_start = 0; ++ gain_up_end = 0; ++ ramp_down_start = 0; ++ ramp_down_end = 0; ++ gain_down_start = 0; ++ gain_down_end = 0; ++#endif ++ ++ abnormal_data_count = 0; ++ if(set_codec_mode) ++ set_codec_mode(); ++ ++ drain_flag = 0; ++ if ((errno = probe_jz_i2s(&i2s_controller)) < 0) ++ return errno; ++ if(set_codec_gpio_pin) ++ set_codec_gpio_pin(); ++ ++ attach_jz_i2s(i2s_controller); ++ if(set_codec_startup_param) ++ set_codec_startup_param(); ++#if defined(CONFIG_I2S_DLV) ++ jz_codec_config = 0; ++ retval = request_irq(IRQ_AIC, aic_codec_irq, IRQF_DISABLED, "aic_codec_irq", NULL); ++ if (retval) { ++ printk("Could not get aic codec irq %d\n", IRQ_AIC); ++ return retval; ++ } ++#endif ++ if(set_codec_volume_table) ++ set_codec_volume_table(); ++ ++ out_empty_queue.id = NULL; ++ out_full_queue.id = NULL; ++ out_busy_queue.id = NULL; ++ in_empty_queue.id = NULL; ++ in_full_queue.id = NULL; ++ in_busy_queue.id = NULL; ++ ++ jz_audio_fragsize = JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE; ++ jz_audio_fragstotal = JZCODEC_RW_BUFFER_TOTAL ; ++ Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ ++#ifdef CONFIG_PM ++ i2s_controller->pm = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ++ jz_i2s_pm_callback); ++ if (i2s_controller->pm) ++ i2s_controller->pm->data = i2s_controller; ++#endif ++ ++#if defined(CONFIG_I2S_DLV) ++ __cpm_start_idct(); ++ __cpm_start_db(); ++ __cpm_start_me(); ++ __cpm_start_mc(); ++ __cpm_start_ipu(); ++#endif ++ ++ printk("JZ I2S OSS audio driver initialized\n"); ++ ++ return 0; ++} ++ ++static void __exit cleanup_jz_i2s(void) ++{ ++#ifdef CONFIG_PM ++ /* pm_unregister(i2s_controller->pm); */ ++#endif ++#if defined(CONFIG_I2S_DLV) ++ free_irq(IRQ_AIC, NULL); ++#endif ++ unload_jz_i2s(i2s_controller); ++ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ if(clear_codec_mode) ++ clear_codec_mode(); ++} ++ ++module_init(init_jz_i2s); ++module_exit(cleanup_jz_i2s); ++ ++#if defined(CONFIG_SOC_JZ4730) ++static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ unsigned long flags; ++ int count,con; ++ ++ if(elements_in_queue(&in_busy_queue) > 0) { ++ if (nonblock) ++ return -EBUSY; ++ drain_flag = 1; ++ sleep_on(&drain_wait_queue); ++ drain_flag = 0; ++ } else { ++ add_wait_queue(&ctrl->adc_wait, &wait); ++ for (con = 0; con < 1000; con ++) { ++ udelay(1); ++ set_current_state(TASK_INTERRUPTIBLE); ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma2); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ if (nonblock) { ++ remove_wait_queue(&ctrl->adc_wait, &wait); ++ current->state = TASK_RUNNING; ++ return -EBUSY; ++ } ++ } ++ remove_wait_queue(&ctrl->adc_wait, &wait); ++ current->state = TASK_RUNNING; ++ } ++ return 0; ++} ++ ++static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ unsigned long flags; ++ int count; ++ ++ if(elements_in_queue(&out_full_queue) > 0) { ++ if (nonblock) ++ return -EBUSY; ++ ++ drain_flag = 1; ++ sleep_on(&drain_wait_queue); ++ drain_flag = 0; ++ } else { ++ add_wait_queue(&(ctrl->dac_wait), &wait); ++ for (;;) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ if(elements_in_queue(&out_full_queue) <= 0) { ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if(count <= 0) ++ break; ++ } ++ if (nonblock) { ++ remove_wait_queue(&ctrl->dac_wait, &wait); ++ current->state = TASK_RUNNING; ++ return -EBUSY; ++ } ++ } ++ remove_wait_queue(&ctrl->dac_wait, &wait); ++ current->state = TASK_RUNNING; ++ } ++ ++ return 0; ++} ++#endif ++ ++#if defined(CONFIG_SOC_JZ4750) || defined(CONFIG_SOC_JZ4750D) ++static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ //DECLARE_WAITQUEUE(wait, current); ++ unsigned long flags; ++ int count,i=0; ++ ++ //add_wait_queue(&ctrl->adc_wait, &wait); ++ for (;;) { ++ if (i < MAXDELAY) { ++ udelay(10); ++ i++; ++ } else ++ break; ++ //set_current_state(TASK_INTERRUPTIBLE); ++ spin_lock_irqsave(&ctrl->lock, flags); ++ //spin_lock(&ctrl->lock); ++ count = get_dma_residue(ctrl->dma2); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ //spin_unlock(&ctrl->lock); ++ if (count <= 0) ++ break; ++ ++ /*if (signal_pending(current)) ++ break;*/ ++ if (nonblock) { ++ //remove_wait_queue(&ctrl->adc_wait, &wait); ++ //current->state = TASK_RUNNING; ++ return -EBUSY; ++ } ++ } ++ //remove_wait_queue(&ctrl->adc_wait, &wait); ++ //current->state = TASK_RUNNING; ++ /*if (signal_pending(current)) ++ return -ERESTARTSYS;*/ ++ return 0; ++} ++static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ unsigned long flags; ++ int count,ele,busyele,emptyele,i=0; ++ ++ for (;;) { ++ if(!nonblock) {//blocked ++ if (i < MAXDELAY) { ++ udelay(10); ++ i++; ++ } else ++ break; ++ ++ ele = elements_in_queue(&out_full_queue); ++ if(ele <= 0) { ++ udelay(200); ++ ++ busyele = elements_in_queue(&out_busy_queue); ++ emptyele = elements_in_queue(&out_empty_queue); ++ if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ } ++ } ++ } else {//non-blocked ++ //mdelay(100); ++ ele = elements_in_queue(&out_full_queue); ++ ++ if(ele <= 0) { ++ //mdelay(100); ++ busyele = elements_in_queue(&out_busy_queue); ++ emptyele = elements_in_queue(&out_empty_queue); ++ ++ if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ } ++ } ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++#if defined(CONFIG_SOC_JZ4740) ++#define MAXDELAY 50000 ++static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ int count,ele,i=0; ++ ++ for (;;) { ++ if(!nonblock) {//blocked ++ if ( i < MAXDELAY ) { ++ udelay(10); ++ i++; ++ } else ++ break; ++ ++ ele = elements_in_queue(&out_full_queue); ++ if(ele <= 0) { ++ udelay(10); ++ spin_lock(&ctrl->lock); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock(&ctrl->lock); ++ if (count <= 0) ++ break; ++ } ++ } else {//non-blocked ++ mdelay(100); ++ ele = elements_in_queue(&out_full_queue); ++ ++ if(ele <= 0) { ++ mdelay(100); ++ ++ spin_lock(&ctrl->lock); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock(&ctrl->lock); ++ if (count <= 0) ++ break; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ int count,i=0; ++ ++ for (;;) { ++ if ( i < MAXDELAY ) ++ { ++ udelay(10); ++ i++; ++ } ++ else ++ break; ++ spin_lock(&ctrl->lock); ++ count = get_dma_residue(ctrl->dma2); ++ spin_unlock(&ctrl->lock); ++ if (count <= 0) ++ break; ++ ++ if (nonblock) { ++ return -EBUSY; ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++static int jz_audio_release(struct inode *inode, struct file *file) ++{ ++ unsigned long flags; ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ unsigned long tfl; ++ ++ if (controller == NULL) ++ return -ENODEV; ++ ++ pop_dma_flag = 0; ++ if (controller->opened1 == 1) { ++ controller->opened1 = 0; ++ __i2s_enable_transmit_dma(); ++ __i2s_enable_replay(); ++ drain_dac(controller, file->f_flags & O_NONBLOCK); ++ /* add some mute to anti-pop */ ++#if defined(CONFIG_I2S_DLV) ++ /* wait for fifo empty */ ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ gain_down_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //gain_down_end = jiffies; ++ while (1) { ++ tfl = REG_AIC_SR & 0x00003f00; ++ if (tfl == 0) { ++ udelay(500); ++ break; ++ } ++ mdelay(2); ++ } ++#endif ++ disable_dma(controller->dma1); ++ set_dma_count(controller->dma1, 0); ++ __i2s_disable_transmit_dma(); ++ __i2s_disable_replay(); ++ __aic_flush_fifo(); ++ if(clear_codec_replay) ++ clear_codec_replay(); ++ __aic_flush_fifo(); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ ramp_down_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //ramp_down_end = jiffies; ++ unset_audio_data_replay(); ++#endif ++ __i2s_disable(); ++ if(turn_off_codec) ++ turn_off_codec(); ++ } ++ ++ if (controller->opened2 == 1) { ++ controller->opened2 = 0; ++ first_record_call = 1; ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ drain_adc(controller, file->f_flags & O_NONBLOCK); ++ disable_dma(controller->dma2); ++ set_dma_count(controller->dma2, 0); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_record(); ++ ++ if(clear_codec_record) ++ clear_codec_record(); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ __i2s_disable(); ++ if(turn_off_codec) ++ turn_off_codec(); ++ abnormal_data_count = 0; ++ } ++ ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++#endif ++ return 0; ++} ++ ++static int jz_audio_open(struct inode *inode, struct file *file) ++{ ++ int i; ++ struct jz_i2s_controller_info *controller = i2s_controller; ++ ++ if (controller == NULL) ++ return -ENODEV; ++ ++ mdelay(2); ++ REG_DMAC_DMACKE(0) = 0x3f; ++ pop_dma_flag = 0; ++ if (controller->opened1 == 1 || controller->opened2 == 1 ) { ++ printk("\naudio is busy!\n"); ++ return -EBUSY; ++ } ++ jz_codec_config = 0; ++ ++ ramp_up_start = 0; ++ ramp_up_end = 0; ++ gain_up_start = 0; ++ gain_up_end = 0; ++ ramp_down_start = 0; ++ ramp_down_end = 0; ++ gain_down_start = 0; ++ gain_down_end = 0; ++ if (file->f_mode & FMODE_WRITE) { ++ if (controller->opened1 == 1) ++ return -EBUSY; ++ ++ controller->opened1 = 1; ++ /* for ioctl */ ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ ++ for(i=0;i < 64;i++) { ++ save_last_samples[i].left = 0; ++ save_last_samples[i].right = 0; ++ } ++ ++ out_empty_queue.count = jz_audio_fragstotal; ++ for (i=0;i < jz_audio_fragstotal;i++) ++ *(out_empty_queue.id + i) = i; ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ last_dma_buffer_id = 0; ++ } ++ ++ if (file->f_mode & FMODE_READ) { ++ if (controller->opened2 == 1) ++ return -EBUSY; ++ ++ controller->opened2 = 1; ++ first_record_call = 1; ++ /* for ioctl */ ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ ++ in_empty_queue.count = jz_audio_fragstotal; ++ for (i=0;i < jz_audio_fragstotal;i++) ++ *(in_empty_queue.id + i) = i; ++ ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ } ++ ++ file->private_data = controller; ++ jz_audio_reset(); ++ REG_AIC_FR |= (1 << 6); ++ ++ if (file->f_mode & FMODE_WRITE) { ++ if(set_codec_replay) ++ set_codec_replay(); ++ } ++ ++ if (file->f_mode & FMODE_READ) { ++ abnormal_data_count = 0; ++ if(set_codec_record) ++ set_codec_record(); ++ } ++ ++#if defined(CONFIG_I2S_DLV) ++ __aic_reset(); ++ ++ mdelay(10); ++ REG_AIC_I2SCR = 0x10; ++ mdelay(20); ++ __aic_flush_fifo(); ++#endif ++ ++ __i2s_enable(); ++ ++#if defined(CONFIG_I2S_DLV) ++ if (file->f_mode & FMODE_WRITE) { ++ ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ ramp_up_start = jiffies; ++ /*while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_RCR = 0x1; ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_RGR = 1;*/ ++ sleep_on(&pop_wait_queue); ++ //ramp_up_end = jiffies; ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ } else if (file->f_mode & FMODE_READ) { ++ if (jz_mic_only) ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ else ++ write_codec_file_bit(5, 0, 7);//SB_DAC->0 ++ mdelay(500); ++ } ++ ++#endif ++ ++ return 0; ++} ++ ++ ++static int jz_audio_ioctl(struct inode *inode, struct file *file, ++unsigned int cmd, unsigned long arg) ++{ ++ int val,fullc,busyc,unfinish,newfragstotal,newfragsize; ++ unsigned int flags; ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ count_info cinfo; ++ audio_buf_info abinfo; ++ int id, i; ++ ++ val = 0; ++ switch (cmd) { ++ case OSS_GETVERSION: ++ return put_user(SOUND_VERSION, (int *)arg); ++ case SNDCTL_DSP_RESET: ++#if 0 ++ jz_audio_reset(); ++ __i2s_disable_replay(); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_record(); ++ __i2s_disable_transmit_dma(); ++#endif ++ return 0; ++ case SNDCTL_DSP_SYNC: ++ if (file->f_mode & FMODE_WRITE) ++ return drain_dac(controller, file->f_flags & O_NONBLOCK); ++ return 0; ++ case SNDCTL_DSP_SPEED: ++ /* set smaple rate */ ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ if (val >= 0) ++ jz_audio_set_speed(controller->dev_audio, val); ++ ++ return put_user(val, (int *)arg); ++ case SNDCTL_DSP_STEREO: ++ /* set stereo or mono channel */ ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ jz_audio_set_channels(controller->dev_audio, val ? 2 : 1); ++ ++ return 0; ++ case SNDCTL_DSP_GETBLKSIZE: ++ //return put_user(jz_audio_fragsize / jz_audio_b, (int *)arg); ++ return put_user(jz_audio_fragsize, (int *)arg); ++ case SNDCTL_DSP_GETFMTS: ++ /* Returns a mask of supported sample format*/ ++ return put_user(AFMT_U8 | AFMT_S16_LE, (int *)arg); ++ case SNDCTL_DSP_SETFMT: ++ /* Select sample format */ ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ if (val != AFMT_QUERY) ++ jz_audio_set_format(controller->dev_audio,val); ++ else { ++ if (file->f_mode & FMODE_READ) ++ val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; ++ else ++ val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; ++ } ++ ++ return put_user(val, (int *)arg); ++ case SNDCTL_DSP_CHANNELS: ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ jz_audio_set_channels(controller->dev_audio, val); ++ ++ return put_user(val, (int *)arg); ++ case SNDCTL_DSP_POST: ++ /* FIXME: the same as RESET ?? */ ++ return 0; ++ case SNDCTL_DSP_SUBDIVIDE: ++ return 0; ++ case SNDCTL_DSP_SETFRAGMENT: ++ get_user(val, (long *) arg); ++ newfragsize = 1 << (val & 0xFFFF); ++ if (newfragsize < 4 * PAGE_SIZE) ++ newfragsize = 4 * PAGE_SIZE; ++ if (newfragsize > (16 * PAGE_SIZE)) ++ newfragsize = 16 * PAGE_SIZE; ++ ++ newfragstotal = (val >> 16) & 0x7FFF; ++ if (newfragstotal < 2) ++ newfragstotal = 2; ++ if (newfragstotal > 32) ++ newfragstotal = 32; ++ if((jz_audio_fragstotal == newfragstotal) && (jz_audio_fragsize == newfragsize)) ++ return 0; ++ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ mdelay(500); ++ jz_audio_fragstotal = newfragstotal; ++ jz_audio_fragsize = newfragsize; ++ ++ Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ mdelay(10); ++ ++ return 0; ++ case SNDCTL_DSP_GETCAPS: ++ return put_user(DSP_CAP_REALTIME|DSP_CAP_BATCH, (int *)arg); ++ case SNDCTL_DSP_NONBLOCK: ++ file->f_flags |= O_NONBLOCK; ++ return 0; ++ case SNDCTL_DSP_SETDUPLEX: ++ return -EINVAL; ++ case SNDCTL_DSP_GETOSPACE: ++ { ++ int i, bytes = 0; ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ jz_audio_fragments = elements_in_queue(&out_empty_queue); ++ for (i = 0; i < jz_audio_fragments; i++) ++ bytes += jz_audio_fragsize; ++ ++ if (jz_audio_channels == 2) ++ bytes /= jz_audio_b; ++ else if (jz_audio_channels == 1) ++ bytes /= 4; ++ else ++ printk("SNDCTL_DSP_GETOSPACE : channels is wrong 1!\n"); ++ ++ ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ /* unused fragment amount */ ++ abinfo.fragments = jz_audio_fragments; ++ /* amount of fragments */ ++ abinfo.fragstotal = jz_audio_fragstotal; ++ /* fragment size in bytes */ ++ if (jz_audio_channels == 2) ++ abinfo.fragsize = jz_audio_fragsize / jz_audio_b; ++ else if (jz_audio_channels == 1) ++ abinfo.fragsize = jz_audio_fragsize / 4; ++ else ++ printk("SNDCTL_DSP_GETOSPACE : channels is wrong 2!\n"); ++ ++ /* write size count without blocking in bytes */ ++ abinfo.bytes = bytes; ++ ++ return copy_to_user((void *)arg, &abinfo, ++ sizeof(abinfo)) ? -EFAULT : 0; ++ } ++ case SNDCTL_DSP_GETISPACE: ++ { ++ int i, bytes = 0; ++ if (!(file->f_mode & FMODE_READ)) ++ return -EINVAL; ++ jz_audio_fragments = elements_in_queue(&in_empty_queue); ++ for (i = 0; i < jz_audio_fragments; i++) ++ bytes += jz_audio_fragsize; ++ ++ if (jz_audio_channels == 2) ++ bytes /= jz_audio_b; ++ else if (jz_audio_channels == 1) ++ bytes /= 4; ++ else ++ printk("SNDCTL_DSP_GETISPACE : channels is wrong 1!\n"); ++ ++ abinfo.fragments = jz_audio_fragments; ++ abinfo.fragstotal = jz_audio_fragstotal; ++ ++ if (jz_audio_channels == 2) ++ abinfo.fragsize = jz_audio_fragsize / jz_audio_b; ++ else if (jz_audio_channels == 1) ++ abinfo.fragsize = jz_audio_fragsize / 4; ++ else ++ printk("SNDCTL_DSP_GETISPACE : channels is wrong 2!\n"); ++ ++ abinfo.bytes = bytes; ++ ++ return copy_to_user((void *)arg, &abinfo, ++ sizeof(abinfo)) ? -EFAULT : 0; ++ } ++ case SNDCTL_DSP_GETTRIGGER: ++ val = 0; ++ if (file->f_mode & FMODE_READ && in_dma_buf) ++ val |= PCM_ENABLE_INPUT; ++ if (file->f_mode & FMODE_WRITE && out_dma_buf) ++ val |= PCM_ENABLE_OUTPUT; ++ ++ return put_user(val, (int *)arg); ++ case SNDCTL_DSP_SETTRIGGER: ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ return 0; ++ case SNDCTL_DSP_GETIPTR: ++ if (!(file->f_mode & FMODE_READ)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ cinfo.bytes = controller->total_bytes; ++ cinfo.blocks = controller->blocks; ++ cinfo.ptr = controller->nextIn; ++ controller->blocks = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); ++ case SNDCTL_DSP_GETOPTR: ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ cinfo.bytes = controller->total_bytes; ++ cinfo.blocks = controller->blocks; ++ cinfo.ptr = controller->nextOut; ++ controller->blocks = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); ++ case SNDCTL_DSP_GETODELAY: ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ unfinish = 0; ++ fullc = elements_in_queue(&out_full_queue); ++ busyc = elements_in_queue(&out_busy_queue); ++ for(i = 0;i < fullc ;i ++) { ++ id = *(out_full_queue.id + i); ++ unfinish += *(out_dma_buf_data_count + id); ++ } ++ for(i = 0;i < busyc ;i ++) { ++ id = *(out_busy_queue.id + i); ++ unfinish += get_dma_residue(controller->dma1); ++ } ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ if (jz_audio_channels == 2) ++ unfinish /= jz_audio_b; ++ else if (jz_audio_channels == 1) ++ unfinish /= 4; ++ else ++ printk("SNDCTL_DSP_GETODELAY : channels is wrong !\n"); ++ ++ return put_user(unfinish, (int *) arg); ++ case SOUND_PCM_READ_RATE: ++ return put_user(jz_audio_rate, (int *)arg); ++ case SOUND_PCM_READ_CHANNELS: ++ return put_user(jz_audio_channels, (int *)arg); ++ case SOUND_PCM_READ_BITS: ++ return put_user((jz_audio_format & (AFMT_S8 | AFMT_U8)) ? 8 : 16, (int *)arg); ++ case SNDCTL_DSP_MAPINBUF: ++ case SNDCTL_DSP_MAPOUTBUF: ++ case SNDCTL_DSP_SETSYNCRO: ++ case SOUND_PCM_WRITE_FILTER: ++ case SOUND_PCM_READ_FILTER: ++ return -EINVAL; ++ } ++ return -EINVAL; ++} ++ ++ ++static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait) ++{ ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ unsigned long flags; ++ unsigned int mask = 0; ++ ++ if (file->f_mode & FMODE_WRITE) { ++ if (elements_in_queue(&out_empty_queue) > 0) ++ return POLLOUT | POLLWRNORM; ++ ++ poll_wait(file, &controller->dac_wait, wait); ++ } ++ ++ if (file->f_mode & FMODE_READ) { ++ if (elements_in_queue(&in_full_queue) > 0) ++ return POLLIN | POLLRDNORM; ++ ++ poll_wait(file, &controller->adc_wait, wait); ++ } ++ ++ spin_lock_irqsave(&controller->lock, flags); ++ if (file->f_mode & FMODE_WRITE) { ++ if (elements_in_queue(&out_empty_queue) > 0) ++ mask |= POLLOUT | POLLWRNORM; ++ } else if (file->f_mode & FMODE_READ) { ++ if (elements_in_queue(&in_full_queue) > 0) ++ mask |= POLLIN | POLLRDNORM; ++ } ++ spin_unlock_irqrestore(&controller->lock, flags); ++ ++ return mask; ++} ++ ++static ssize_t jz_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos) ++{ ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ int id, ret = 0, left_count, copy_count, cnt = 0; ++ unsigned long flags; ++ ++ if (count < 0) ++ return -EINVAL; ++ ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextIn = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ copy_count = jz_audio_fragsize / 4; ++ ++ left_count = count; ++ if (first_record_call) { ++ first_record_call = 0; ++ audio_read_back_first: ++ if ((id = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id); ++ spin_lock(&controller->lock); ++ *(in_dma_buf_data_count + id) = copy_count * 4; ++ ++ spin_unlock(&controller->lock); ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); ++ audio_start_dma(controller->dma2,file->private_data, ++ *(in_dma_pbuf + id), ++ *(in_dma_buf_data_count + id), ++ DMA_MODE_READ); ++ sleep_on(&rx_wait_queue); ++ } else ++ goto audio_read_back_first; ++ } ++ ++ while (left_count > 0) { ++ audio_read_back_second: ++ if (elements_in_queue(&in_full_queue) <= 0) { ++ if (file->f_flags & O_NONBLOCK) ++ return ret ? ret : -EAGAIN; ++ else ++ sleep_on(&rx_wait_queue); ++ } ++ ++ if ((id = get_buffer_id(&in_full_queue)) >= 0) { ++ spin_lock(&controller->lock); ++ cnt = record_filler((unsigned long)controller->tmp2+ret, copy_count, id); ++ spin_unlock(&controller->lock); ++ put_buffer_id(&in_empty_queue, id); ++ } else ++ goto audio_read_back_second; ++ ++ if (elements_in_queue(&in_busy_queue) == 0) { ++ if ((id=get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id); ++ spin_lock(&controller->lock); ++ *(in_dma_buf_data_count + id) = copy_count * 4; ++ spin_unlock(&controller->lock); ++ ++ dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); ++ audio_start_dma(controller->dma2,file->private_data, ++ *(in_dma_pbuf + id), ++ *(in_dma_buf_data_count + id), ++ DMA_MODE_READ); ++ } ++ } ++ if (ret + cnt > count) { ++ spin_lock(&controller->lock); ++ cnt = count - ret; ++ spin_unlock(&controller->lock); ++ } ++ if (copy_to_user(buffer+ret, controller->tmp2+ret, cnt)) ++ return ret ? ret : -EFAULT; ++ ++ spin_lock(&controller->lock); ++ ret += cnt; ++ spin_unlock(&controller->lock); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextIn += ret; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ spin_lock(&controller->lock); ++ left_count -= cnt; ++ spin_unlock(&controller->lock); ++ } ++ return ret; ++} ++ ++static ssize_t jz_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) ++{ ++ int id, ret = 0, left_count, copy_count = 0; ++ unsigned int flags; ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ ++ if (count <= 0) ++ return -EINVAL; ++ ++ if(set_replay_hp_or_speaker) ++ set_replay_hp_or_speaker(); ++ ++ __i2s_enable_transmit_dma(); ++ __i2s_enable_replay(); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextOut = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ if (jz_audio_channels == 2) ++ copy_count = jz_audio_fragsize / jz_audio_b; ++ else if(jz_audio_channels == 1) ++ copy_count = jz_audio_fragsize / 4; ++ left_count = count; ++ if (copy_from_user(controller->tmp1, buffer, count)) { ++ printk("copy_from_user failed:%d",ret); ++ return ret ? ret : -EFAULT; ++ } ++ ++ while (left_count > 0) { ++ audio_write_back: ++ /*if (file->f_flags & O_NONBLOCK) ++ udelay(2);*/ ++ if (elements_in_queue(&out_empty_queue) == 0) { ++ if (file->f_flags & O_NONBLOCK) ++ return ret; ++ else ++ sleep_on(&tx_wait_queue); ++ } ++ /* the end fragment size in this write */ ++ if (ret + copy_count > count) ++ copy_count = count - ret; ++ if ((id = get_buffer_id(&out_empty_queue)) >= 0) { ++ replay_filler((signed long)controller->tmp1 + ret, copy_count, id); ++ if(*(out_dma_buf_data_count + id) > 0) { ++ put_buffer_id(&out_full_queue, id); ++ dma_cache_wback_inv(*(out_dma_buf + id), ++ *(out_dma_buf_data_count + id)); ++ } else ++ put_buffer_id(&out_empty_queue, id); ++ } else ++ goto audio_write_back; ++ ++ left_count = left_count - copy_count; ++ ret += copy_count; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextOut += ret; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ if (elements_in_queue(&out_busy_queue) == 0) { ++ if ((id=get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_busy_queue, id); ++ ++ if(*(out_dma_buf_data_count + id) > 0) { ++ audio_start_dma(controller->dma1, ++ file->private_data, ++ *(out_dma_pbuf + id), ++ *(out_dma_buf_data_count + id), ++ DMA_MODE_WRITE); ++ last_dma_buffer_id = id; ++ if (jz_codec_config == 0) { ++ write_codec_file_bit(1, 0, 5); ++ gain_up_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //gain_up_end = jiffies; ++ jz_codec_config = 1; ++ //SB_ADC->1 ++ //write_codec_file_bit(5, 1, 4); ++ //while(1); ++ } ++ } ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++#if defined(CONFIG_I2S_ICODEC) ++static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample) ++{ ++ int i,step_len; ++ unsigned long *pop_buf = (unsigned long*)pop_turn_onoff_buf; ++ unsigned int sample_oss = (REG_AIC_CR & 0x00380000) >> 19; ++ unsigned long l_sample_count,r_sample_count,sample_count; ++ struct jz_i2s_controller_info *controller = i2s_controller; ++ signed int left_sam=0,right_sam=0,l_val,r_val; ++ ++ switch (sample_oss) { ++ case 0x0: ++ break; ++ case 0x1: ++ left_sam = (signed int)l_sample; ++ right_sam = (signed int)r_sample; ++ break; ++ case 0x2: ++ break; ++ case 0x3: ++ break; ++ case 0x4: ++ break; ++ } ++ ++ if(left_sam == 0 && right_sam == 0) ++ return; ++ ++ switch (sample_oss) { ++ case 0x0: ++ break; ++ case 0x1: ++ step_len = jz_audio_speed / 10 * 3; ++ step_len = step_len / 2; ++ step_len = 0x7fff / step_len + 1; ++ ++ l_sample_count = 0; ++ l_val = left_sam; ++ ++ while(1) { ++ if(l_val > 0) { ++ if(l_val >= step_len) { ++ l_val -= step_len; ++ l_sample_count ++; ++ } else ++ break; ++ } ++ ++ if(l_val < 0) { ++ if(l_val <= -step_len) { ++ l_val += step_len; ++ l_sample_count ++; ++ } else ++ break; ++ } ++ ++ if(l_val == 0) ++ break; ++ } ++ ++ r_sample_count = 0; ++ r_val = right_sam; ++ while(1) { ++ if(r_val > 0) { ++ if(r_val >= step_len) { ++ r_val -= step_len; ++ r_sample_count ++; ++ } else ++ break; ++ } ++ ++ if(r_val < 0) { ++ if(r_val <= -step_len) { ++ r_val += step_len; ++ r_sample_count ++; ++ } else ++ break; ++ } ++ ++ if(r_val == 0) ++ break; ++ } ++ /* fill up */ ++ if(l_sample_count > r_sample_count) ++ sample_count = l_sample_count; ++ else ++ sample_count = r_sample_count; ++ ++ l_val = left_sam; ++ r_val = right_sam; ++ for(i=0;i <= sample_count;i++) { ++ ++ *pop_buf = (unsigned long)l_val; ++ pop_buf ++; ++ ++ if(l_val > step_len) ++ l_val -= step_len; ++ else if(l_val < -step_len) ++ l_val += step_len; ++ else if(l_val >= -step_len && l_val <= step_len) ++ l_val = 0; ++ ++ *pop_buf = (unsigned long)r_val; ++ pop_buf ++; ++ if(r_val > step_len) ++ r_val -= step_len; ++ else if(r_val < -step_len) ++ r_val += step_len; ++ else if(r_val >= -step_len && r_val <= step_len) ++ r_val = 0; ++ } ++ ++ *pop_buf = 0; ++ pop_buf ++; ++ *pop_buf = 0; ++ ++ pop_buf ++; ++ sample_count += 2; ++ dma_cache_wback_inv(pop_turn_onoff_buf, sample_count*8); ++ ++ pop_dma_flag = 1; ++ audio_start_dma(controller->dma1,controller,pop_turn_onoff_pbuf,sample_count*8,DMA_MODE_WRITE); ++ sleep_on(&pop_wait_queue); ++ pop_dma_flag = 0; ++ break; ++ case 0x2: ++ break; ++ case 0x3: ++ break; ++ case 0x4: ++ break; ++ } ++} ++#endif +diff -urN linux-2.6.24.7.old/sound/oss/jz_i2s_dlv_dma_test.c linux-2.6.24.7/sound/oss/jz_i2s_dlv_dma_test.c +--- linux-2.6.24.7.old/sound/oss/jz_i2s_dlv_dma_test.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/oss/jz_i2s_dlv_dma_test.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,2808 @@ ++/* ++ * linux/drivers/sound/jz_i2s_dlv.c ++ * ++ * JzSOC On-Chip I2S audio driver. ++ * ++ * Copyright (C) 2005 by Junzheng Corp. ++ * Modified by cjfeng on Aug 9,2007,and not any bug on Jz4730 using ++ * dma channel 4&3,noah is tested. ++ * ++ * 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. ++ * ++ * Because the normal application of AUDIO devices are focused on Little_endian, ++ * then we only perform the little endian data format in driver. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "sound_config.h" ++ ++#define DPRINTK(args...) printk(args) ++#define DMA_ID_I2S_TX DMA_ID_AIC_TX ++#define DMA_ID_I2S_RX DMA_ID_AIC_RX ++ ++#define NR_I2S 2 ++#define MAXDELAY 50000 ++#define JZCODEC_RW_BUFFER_SIZE 3 ++#define JZCODEC_RW_BUFFER_TOTAL 3 ++#define JZCODEC_USER_BUFFER 6 ++ ++#define USE_NONE 1 ++#define USE_MIC 2 ++#define USE_LINEIN 3 ++ ++static int jz_audio_rate; ++static char jz_audio_format; ++static char jz_audio_volume; ++static char jz_audio_channels; ++static int jz_audio_b; /* bits expand multiple */ ++static int jz_audio_fragments;//unused fragment amount ++static int jz_audio_fragstotal; ++static int jz_audio_fragsize; ++static int jz_audio_speed; ++static int audio_mix_modcnt; ++static int codec_volue_shift; ++static int jz_audio_dma_tran_count;//bytes count of one DMA transfer ++static int jz_mic_only = 1; ++static int jz_codec_config = 0; ++static int use_mic_line_flag; ++static unsigned long ramp_up_start; ++static unsigned long ramp_up_end; ++static unsigned long gain_up_start; ++static unsigned long gain_up_end; ++static unsigned long ramp_down_start; ++static unsigned long ramp_down_end; ++static unsigned long gain_down_start; ++static unsigned long gain_down_end; ++ ++ ++static void jz_update_filler(int bits, int channels); ++static int Init_In_Out_queue(int fragstotal,int fragsize); ++static int Free_In_Out_queue(int fragstotal,int fragsize); ++static irqreturn_t jz_i2s_replay_dma_irq(int irqnr, void *ref); ++static irqreturn_t jz_i2s_record_dma_irq(int irqnr, void *ref); ++static void (*replay_filler)(signed long src_start, int count, int id); ++static int (*record_filler)(unsigned long dst_start, int count, int id); ++static void jz_audio_reset(void); ++ ++static struct file_operations jz_i2s_audio_fops; ++ ++static DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue); ++static DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue); ++static DECLARE_WAIT_QUEUE_HEAD (pop_wait_queue); ++ ++struct jz_i2s_controller_info ++{ ++ int io_base; ++ int dma1; /* play */ ++ int dma2; /* record */ ++ char *name; ++ int dev_audio; ++ struct i2s_codec *i2s_codec[NR_I2S]; ++ int opened1; ++ int opened2; ++ unsigned char *tmp1; /* tmp buffer for sample conversions */ ++ unsigned char *tmp2; ++ spinlock_t lock; ++ spinlock_t ioctllock; ++ ++ wait_queue_head_t dac_wait; ++ wait_queue_head_t adc_wait; ++ int nextIn; // byte index to next-in to DMA buffer ++ int nextOut; // byte index to next-out from DMA buffer ++ int count; // current byte count in DMA buffer ++ int finish; // current transfered byte count in DMA buffer ++ unsigned total_bytes; // total bytes written or read ++ unsigned blocks; ++ unsigned error; // over/underrun ++#ifdef CONFIG_PM ++ struct pm_dev *pm; ++#endif ++}; ++ ++ ++static struct jz_i2s_controller_info *i2s_controller = NULL; ++struct i2s_codec ++{ ++ /* I2S controller connected with */ ++ void *private_data; ++ char *name; ++ int id; ++ int dev_mixer; ++ /* controller specific lower leverl i2s accessing routines */ ++ u16 (*codec_read) (u8 reg);//the function accessing Codec REGs fcj add ++ void (*codec_write) (u8 reg, u16 val);//to AK4642EN,val is 8bit ++ /* Wait for codec-ready. Ok to sleep here. */ ++ void (*codec_wait) (struct i2s_codec *codec); ++ /* OSS mixer masks */ ++ int modcnt; ++ int supported_mixers; ++ int stereo_mixers; ++ int record_sources; ++ int bit_resolution; ++ /* OSS mixer interface */ ++ int (*read_mixer) (struct i2s_codec *codec, int oss_channel); ++ void (*write_mixer)(struct i2s_codec *codec, int oss_channel, ++ unsigned int left, unsigned int right); ++ int (*recmask_io) (struct i2s_codec *codec, int rw, int mask); ++ int (*mixer_ioctl)(struct i2s_codec *codec, unsigned int cmd, unsigned long arg); ++ /* saved OSS mixer states */ ++ unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; ++}; ++ ++ ++typedef struct buffer_queue_s ++{ ++ int count; ++ int *id; ++ int lock; ++} buffer_queue_t; ++ ++typedef struct left_right_sample_s ++{ ++ signed long left; ++ signed long right; ++} left_right_sample_t; ++ ++typedef struct hpvol_shift_s ++{ ++ int hpvol; ++ int shift; ++} hpvol_shift_t; ++ ++static unsigned long *out_dma_buf = NULL; ++static unsigned long *out_dma_pbuf = NULL; ++static unsigned long *out_dma_buf_data_count = NULL; ++static unsigned long *in_dma_buf = NULL; ++static unsigned long *in_dma_pbuf = NULL; ++static unsigned long *in_dma_buf_data_count = NULL; ++ ++static buffer_queue_t out_empty_queue; ++static buffer_queue_t out_full_queue; ++static buffer_queue_t out_busy_queue; ++static buffer_queue_t in_empty_queue; ++static buffer_queue_t in_full_queue; ++static buffer_queue_t in_busy_queue; ++static int first_record_call = 0; ++ ++static int read_codec_file(int addr) ++{ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ mdelay(1); ++ return(__icdc_get_value()); ++} ++ ++static void printk_codec_files(void) ++{ ++ int cnt; ++ ++ printk("\n"); ++ ++ printk("REG_CPM_I2SCDR=0x%08x\n",REG_CPM_I2SCDR); ++ printk("REG_CPM_CLKGR=0x%08x\n",REG_CPM_CLKGR); ++ printk("REG_CPM_CPCCR=0x%08x\n",REG_CPM_CPCCR); ++ printk("REG_AIC_FR=0x%08x\n",REG_AIC_FR); ++ printk("REG_AIC_CR=0x%08x\n",REG_AIC_CR); ++ printk("REG_AIC_I2SCR=0x%08x\n",REG_AIC_I2SCR); ++ printk("REG_AIC_SR=0x%08x\n",REG_AIC_SR); ++ printk("REG_ICDC_RGDATA=0x%08x\n",REG_ICDC_RGDATA); ++ ++ for (cnt = 0; cnt <= 27 ; cnt++) { ++ printk(" ( %d : 0x%x ) ",cnt ,read_codec_file(cnt)); ++ } ++ printk("\n"); ++} ++ ++static void write_codec_file(int addr, int val) ++{ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ __icdc_set_cmd(val); /* write */ ++ mdelay(1); ++ __icdc_set_rgwr(); ++ mdelay(1); ++} ++ ++static int write_codec_file_bit(int addr, int bitval, int mask_bit) ++{ ++ int val; ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ mdelay(1); ++ val = __icdc_get_value(); /* read */ ++ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ val &= ~(1 << mask_bit); ++ if (bitval == 1) ++ val |= 1 << mask_bit; ++ ++ __icdc_set_cmd(val); /* write */ ++ mdelay(1); ++ __icdc_set_rgwr(); ++ mdelay(1); ++ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ val = __icdc_get_value(); /* read */ ++ ++ if (((val >> mask_bit) & bitval) == bitval) ++ return 1; ++ else ++ return 0; ++} ++ ++/* set Audio data replay */ ++static void set_audio_data_replay() ++{ ++ /* DAC path */ ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ mdelay(10); ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ //mdelay(100); ++ //write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //mdelay(300); ++} ++ ++/* unset Audio data replay */ ++static void unset_audio_data_replay(void) ++{ ++ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ //mdelay(800); ++ //write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ //mdelay(800); ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 4);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++/* set Record MIC input audio without playback */ ++static void set_record_mic_input_audio_without_playback(void) ++{ ++ /* ADC path for MIC IN */ ++ jz_mic_only = 1; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ mdelay(10); ++ write_codec_file_bit(1, 1, 2); ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ ++ write_codec_file(22, 0x40);//mic 1 ++ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ //write_codec_file_bit(6, 1, 3);// gain set ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ write_codec_file(1, 0x4); ++} ++ ++/* unset Record MIC input audio without playback */ ++static void unset_record_mic_input_audio_without_playback(void) ++{ ++ /* ADC path for MIC IN */ ++ jz_mic_only = 0; ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++/* set Record LINE input audio without playback */ ++static void set_record_line_input_audio_without_playback(void) ++{ ++ /* ADC path for LINE IN */ ++ jz_mic_only = 1; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ mdelay(10); ++ write_codec_file(22, 0xf6);//line in 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1 ++ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ mdelay(10); ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ write_codec_file(1, 0x4); ++} ++ ++/* unset Record LINE input audio without playback */ ++static void unset_record_line_input_audio_without_playback(void) ++{ ++ /* ADC path for LINE IN */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(5, 1, 3);//ONR1.SB_LIN->1 ++ ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++/* set Playback LINE input audio direct only */ ++static void set_playback_line_input_audio_direct_only() ++{ ++ jz_audio_reset();//or init_codec() ++ REG_AIC_I2SCR = 0x10; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ mdelay(10); ++ write_codec_file(22, 0xf6);//line in 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ mdelay(10); ++ write_codec_file_bit(1, 1, 2);//CR1.HP_BYPASS->1 ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ //write_codec_file_bit(5, 1, 7);//PMR1.SB_DAC->1 ++ //write_codec_file_bit(5, 1, 4);//PMR1.SB_ADC->1 ++} ++ ++/* unset Playback LINE input audio direct only */ ++static void unset_playback_line_input_audio_direct_only() ++{ ++ write_codec_file_bit(6, 0, 3);//GIM->0 ++ write_codec_file_bit(1, 0, 2);//PMR1.BYPASS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LINE->1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ mdelay(100); ++ write_codec_file_bit(5, 1, 5);//PMR1.SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++/* set Record MIC input audio with direct playback */ ++static void set_record_mic_input_audio_with_direct_playback(void) ++{ ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ jz_mic_only = 0; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ mdelay(10); ++ ++ write_codec_file(22, 0x60);//mic 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ write_codec_file_bit(6, 1, 3);// gain set ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ //write_codec_file(1, 0x4); ++} ++ ++/* unset Record MIC input audio with direct playback */ ++static void unset_record_mic_input_audio_with_direct_playback(void) ++{ ++ /* ADC path for MIC IN */ ++ jz_mic_only = 0; ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++/* set Record playing audio mixed with MIC input audio */ ++static void set_record_playing_audio_mixed_with_mic_input_audio(void) ++{ ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ mdelay(10); ++ ++ write_codec_file(22, 0x63);//mic 1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(6, 1, 3);// gain set ++ ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 ++ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0 ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ write_codec_file_bit(5, 0, 4);//PMR1.SB_MIX->0 ++} ++ ++/* unset Record playing audio mixed with MIC input audio */ ++static void unset_record_playing_audio_mixed_with_mic_input_audio(void) ++{ ++ /* ADC path */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 ++ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ //write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 5);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++/* set Record MIC input audio with Audio data replay (full duplex) */ ++static void set_record_mic_input_audio_with_audio_data_replay(void) ++{ ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0 ++ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 ++ ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++} ++ ++/* unset Record MIC input audio with Audio data replay (full duplex) */ ++static void unset_record_mic_input_audio_with_audio_data_replay(void) ++{ ++ /* ADC path */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 5);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++///////// ++/* set Record LINE input audio with Audio data replay (full duplex for linein) */ ++static void set_record_line_input_audio_with_audio_data_replay(void) ++{ ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(22, 1, 7);//CR3.SB_MIC->1 ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ ++ ++ //jz_mic_only = 1; ++ write_codec_file(22, 0xc6);//line in 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++} ++ ++/* unset Record LINE input audio with Audio data replay (full duplex for linein) */ ++static void unset_record_line_input_audio_with_audio_data_replay(void) ++{ ++ /* ADC path */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 5);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++///////// ++ ++static inline int get_buffer_id(struct buffer_queue_s *q) ++{ ++ int r; ++ unsigned long flags; ++ int i; ++ spin_lock_irqsave(&q->lock, flags); ++ //spin_lock(&q->lock); ++ if (q->count == 0) { ++ spin_unlock_irqrestore(&q->lock, flags); ++ //spin_unlock(&q->lock); ++ return -1; ++ } ++ r = *(q->id + 0); ++ for (i=0;i < q->count-1;i++) ++ *(q->id + i) = *(q->id + (i+1)); ++ q->count --; ++ spin_unlock_irqrestore(&q->lock, flags); ++ //spin_unlock(&q->lock); ++ return r; ++} ++ ++static inline void put_buffer_id(struct buffer_queue_s *q, int id) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&q->lock, flags); ++ //spin_lock(&q->lock); ++ *(q->id + q->count) = id; ++ q->count ++; ++ spin_unlock_irqrestore(&q->lock, flags); ++ //spin_unlock(&q->lock); ++} ++ ++static inline int elements_in_queue(struct buffer_queue_s *q) ++{ ++ int r; ++ unsigned long flags; ++ spin_lock_irqsave(&q->lock, flags); ++ //spin_lock(&q->lock); ++ r = q->count; ++ spin_unlock_irqrestore(&q->lock, flags); ++ //spin_unlock(&q->lock); ++ return r; ++} ++ ++static inline void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode) ++{ ++ unsigned long flags; ++ struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; ++ //for DSP_GETOPTR ++ //spin_lock_irqsave(&controller->ioctllock, flags); ++ spin_lock(&controller->ioctllock); ++ jz_audio_dma_tran_count = count; ++ //spin_unlock_irqrestore(&controller->ioctllock, flags); ++ spin_unlock(&controller->ioctllock); ++ flags = claim_dma_lock(); ++ //__dmac_disable_module(0); ++ disable_dma(chan); ++ clear_dma_ff(chan); ++ ++ set_dma_mode(chan, mode); ++ set_dma_addr(chan, phyaddr); ++ if (count == 0) ++ { ++ count++; ++ printk("JzSOC DMA controller can't set dma 0 count!\n"); ++ } ++ ++ set_dma_count(chan, count); ++ enable_dma(chan); ++ //__dmac_enable_module(0); ++ release_dma_lock(flags); ++ //dump_jz_dma_channel(chan); ++ ++ //printk_codec_files(); ++} ++ ++static irqreturn_t jz_i2s_record_dma_irq (int irq, void *dev_id) ++{ ++ int id1, id2; ++ ++ struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; ++ int dma = controller->dma2; ++ ++ disable_dma(dma); ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ //for DSP_GETIPTR ++ //spin_lock_irqsave(&controller->ioctllock, flags); ++ spin_lock(&controller->ioctllock); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ //spin_unlock_irqrestore(&controller->ioctllock, flags); ++ spin_unlock(&controller->ioctllock); ++ id1 = get_buffer_id(&in_busy_queue); ++ put_buffer_id(&in_full_queue, id1); ++ ++ wake_up(&rx_wait_queue); ++ wake_up(&controller->adc_wait); ++ if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id2); ++ *(in_dma_buf_data_count + id2) = *(in_dma_buf_data_count + id1); ++ //write back ++ dma_cache_wback_inv(*(in_dma_buf + id2), *(in_dma_buf_data_count + id2)); ++ audio_start_dma(dma,dev_id, ++ *(in_dma_pbuf + id2), ++ *(in_dma_buf_data_count + id2), ++ DMA_MODE_READ); ++ } ++ else ++ in_busy_queue.count = 0; ++ } ++ return IRQ_HANDLED; ++ ++} ++ ++static irqreturn_t jz_i2s_replay_dma_irq (int irq, void *dev_id) ++{ ++ int id; ++ unsigned long flags; ++ struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; ++ int dma = controller->dma1; ++ disable_dma(dma); ++ ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ ++ ++ //for DSP_GETOPTR ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ //spin_lock(&controller->ioctllock); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ //spin_unlock(&controller->ioctllock); ++ if ((id = get_buffer_id(&out_busy_queue)) < 0) ++ printk(KERN_DEBUG "Strange DMA finish interrupt for I2S module\n"); ++ put_buffer_id(&out_empty_queue, id); ++ if ((id = get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_busy_queue, id); //very busy ++ if(*(out_dma_buf_data_count + id) > 0) { ++ audio_start_dma(dma, dev_id, *(out_dma_pbuf + id), ++ *(out_dma_buf_data_count + id), ++ DMA_MODE_WRITE); ++ } ++ } else ++ out_busy_queue.count = 0; ++ ++ if (elements_in_queue(&out_empty_queue) > 0) { ++ wake_up(&tx_wait_queue); ++ wake_up(&controller->dac_wait); ++ } ++ } ++ return IRQ_HANDLED; ++} ++ ++static void jz_i2s_initHw(int set) ++{ ++ __i2s_disable(); ++ //schedule_timeout(5); ++ ++#if defined(CONFIG_I2S_DLV) ++ __i2s_disable(); ++ __i2s_as_slave(); ++ __aic_internal_codec(); ++ //__i2s_set_oss_sample_size(16); ++ //__i2s_set_iss_sample_size(16); ++ ++#endif ++ __i2s_disable_record(); ++ __i2s_disable_replay(); ++ __i2s_disable_loopback(); ++ __i2s_set_transmit_trigger(7); ++ __i2s_set_receive_trigger(8); ++} ++ ++static int Init_In_Out_queue(int fragstotal,int fragsize) ++{ ++ int i; ++ // recording ++ in_empty_queue.count = fragstotal; ++ in_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_buf) ++ goto all_mem_err; ++ in_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_pbuf) ++ goto all_mem_err; ++ in_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_buf_data_count) ++ goto all_mem_err; ++ in_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_empty_queue.id) ++ goto all_mem_err; ++ in_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_full_queue.id) ++ goto all_mem_err; ++ in_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_busy_queue.id) ++ goto all_mem_err; ++ ++ for (i=0;i < fragstotal;i++) ++ *(in_empty_queue.id + i) = i; ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ ++ for (i = 0; i < fragstotal; i++) { ++ *(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); ++ if (*(in_dma_buf + i) == 0) ++ goto mem_failed_in; ++ *(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i))); ++ dma_cache_wback_inv(*(in_dma_buf + i), fragsize); ++ } ++ ++ //playing ++ out_empty_queue.count = fragstotal; ++ out_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!out_dma_buf) ++ goto all_mem_err; ++ out_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!out_dma_pbuf) ++ goto all_mem_err; ++ out_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ ++ if (!out_dma_buf_data_count) ++ goto all_mem_err; ++ out_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_empty_queue.id) ++ goto all_mem_err; ++ out_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_full_queue.id) ++ goto all_mem_err; ++ out_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_busy_queue.id) ++ goto all_mem_err; ++ for (i=0;i < fragstotal;i++) ++ *(out_empty_queue.id + i) = i; ++ ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ /*alloc DMA buffer*/ ++ for (i = 0; i < fragstotal; i++) { ++ *(out_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); ++ if (*(out_dma_buf + i) == 0) { ++ printk(" can't allocate required DMA(OUT) buffers.\n"); ++ goto mem_failed_out; ++ } ++ *(out_dma_pbuf + i) = virt_to_phys((void *)(*(out_dma_buf + i))); ++ } ++ ++ return 1; ++all_mem_err: ++ printk("error:allocate memory occur error 1!\n"); ++ return 0; ++mem_failed_out: ++ printk("error:allocate memory occur error 2!\n"); ++ for (i = 0; i < fragstotal; i++) { ++ if(*(out_dma_buf + i)) ++ free_pages(*(out_dma_buf + i), get_order(fragsize)); ++ } ++ ++ return 0; ++mem_failed_in: ++ printk("error:allocate memory occur error 3!\n"); ++ for (i = 0; i < fragstotal; i++) { ++ if(*(in_dma_buf + i)) ++ free_pages(*(in_dma_buf + i), get_order(fragsize)); ++ } ++ return 0; ++} ++ ++static int Free_In_Out_queue(int fragstotal,int fragsize) ++{ ++ int i; ++ //playing ++ if(out_dma_buf != NULL) { ++ for (i = 0; i < fragstotal; i++) { ++ if(*(out_dma_buf + i)) ++ free_pages(*(out_dma_buf + i), get_order(fragsize)); ++ *(out_dma_buf + i) = 0; //release page error ++ } ++ kfree(out_dma_buf); ++ out_dma_buf = NULL; ++ } ++ if(out_dma_pbuf) { ++ kfree(out_dma_pbuf); ++ out_dma_pbuf = NULL; ++ } ++ if(out_dma_buf_data_count) { ++ kfree(out_dma_buf_data_count); ++ out_dma_buf_data_count = NULL; ++ } ++ if(out_empty_queue.id) { ++ kfree(out_empty_queue.id); ++ out_empty_queue.id = NULL; ++ } ++ if(out_full_queue.id) { ++ kfree(out_full_queue.id); ++ out_full_queue.id = NULL; ++ } ++ if(out_busy_queue.id) { ++ kfree(out_busy_queue.id); ++ out_busy_queue.id = NULL; ++ } ++ out_empty_queue.count = fragstotal; ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ ++ // recording ++ if(in_dma_buf) { ++ for (i = 0; i < fragstotal; i++) { ++ if(*(in_dma_buf + i)) { ++ dma_cache_wback_inv(*(in_dma_buf + i), fragsize); ++ free_pages(*(in_dma_buf + i), get_order(fragsize)); ++ } ++ *(in_dma_buf + i) = 0; //release page error ++ } ++ kfree(in_dma_buf); ++ in_dma_buf = NULL; ++ } ++ if(in_dma_pbuf) { ++ kfree(in_dma_pbuf); ++ in_dma_pbuf = NULL; ++ } ++ if(in_dma_buf_data_count) { ++ kfree(in_dma_buf_data_count); ++ in_dma_buf_data_count = NULL; ++ } ++ if(in_empty_queue.id) { ++ kfree(in_empty_queue.id); ++ in_empty_queue.id = NULL; ++ } ++ if(in_full_queue.id) { ++ kfree(in_full_queue.id); ++ in_full_queue.id = NULL; ++ } ++ if(in_busy_queue.id) { ++ kfree(in_busy_queue.id); ++ in_busy_queue.id = NULL; ++ } ++ ++ in_empty_queue.count = fragstotal; ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ ++ return 1; ++} ++ ++static void jz_i2s_full_reset(struct jz_i2s_controller_info *controller) ++{ ++ jz_i2s_initHw(0); ++} ++ ++static int jz_audio_set_speed(int dev, int rate) ++{ ++ /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 ? */ ++ int speed = 0, val; ++ ++ jz_audio_speed = rate; ++ if (rate > 96000) ++ rate = 96000; ++ if (rate < 8000) ++ rate = 8000; ++ jz_audio_rate = rate; ++ ++#if defined(CONFIG_I2S_DLV) ++ speed = 0; ++ switch (rate) { ++ case 8000: ++ speed = 10; ++ break; ++ case 9600: ++ speed = 9; ++ break; ++ case 11025: ++ speed = 8; ++ break; ++ case 12000: ++ speed = 7; ++ break; ++ case 16000: ++ speed = 6; ++ break; ++ case 22050: ++ speed = 5; ++ break; ++ case 24000: ++ speed = 4; ++ break; ++ case 32000: ++ speed = 3; ++ break; ++ case 44100: ++ speed = 2; ++ break; ++ case 48000: ++ speed = 1; ++ break; ++ case 96000: ++ speed = 0; ++ break; ++ default: ++ break; ++ } ++ ++ val = read_codec_file(4); ++ val = (speed << 4) | speed; ++ write_codec_file(4, val); ++ ++#if 0 ++ for (i = 0; i <= 27 ; i++) { ++ printk(" ( CCC %d : 0x%x ) ",i ,read_codec_file(i)); ++ } ++#endif ++ ++#endif ++ return jz_audio_rate; ++} ++ ++static int record_fill_1x8_u(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long data; ++ volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ volatile unsigned char *dp = (unsigned char*)dst_start; ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ cnt++; ++ data = *(s++); ++ *(dp ++) = ((data << 16) >> 24) + 0x80; ++ s++; /* skip the other channel */ ++ } ++ return cnt; ++} ++ ++static int record_fill_2x8_u(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long d1, d2; ++ volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ volatile unsigned char *dp = (unsigned char*)dst_start; ++ while (count > 0) { ++ count -= 2; ++ cnt += 2; ++ d1 = *(s++); ++ *(dp ++) = ((d1 << 16) >> 24) + 0x80; ++ d2 = *(s++); ++ *(dp ++) = ((d2 << 16) >> 24) + 0x80; ++ } ++ return cnt; ++} ++ ++static int record_fill_1x16_s(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long d1; ++ unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ unsigned short *dp = (unsigned short *)dst_start; ++ ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ cnt += 2; /* count in byte */ ++ d1 = *(s++); ++ *(dp ++) = (d1 << 16) >> 16; ++ s++; /* skip the other channel */ ++ } ++ return cnt; ++} ++ ++ ++static int record_fill_2x16_s(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long d1, d2; ++ unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ unsigned short *dp = (unsigned short *)dst_start; ++ ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ cnt += 4; /* count in byte */ ++ d1 = *(s++); ++ *(dp ++) = (d1 << 16) >> 16; ++ d2 = *(s++); ++ *(dp ++) = (d2 << 16) >> 16; ++ } ++ return cnt; ++} ++ ++static int record_fill_2x24_s(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long d1, d2; ++ unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ unsigned short *dp = (unsigned short *)dst_start; ++ ++ while (count > 0) { ++ count -= 4; /* count in dword */ ++ cnt += 4; /* count in byte */ ++ d1 = *(s++); ++ *(dp ++) = (d1 << 24) >> 24; ++ d2 = *(s++); ++ *(dp ++) = (d2 << 24) >> 24; ++ } ++ //printk("--- rec 24 ---\n"); ++ return cnt; ++} ++ ++static void replay_fill_1x8_u(unsigned long src_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned char data; ++ unsigned long ddata; ++ volatile unsigned char *s = (unsigned char *)src_start; ++ volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); ++ while (count > 0) { ++ count--; ++ cnt += 1; ++ data = *(s++) - 0x80; ++ ddata = (unsigned long) data << 8; ++ *(dp ++) = ddata; ++ *(dp ++) = ddata; ++ } ++ cnt = cnt * 2 * jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++static void replay_fill_2x8_u(unsigned long src_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned char d1; ++ unsigned long dd1; ++ volatile unsigned char *s = (unsigned char *)src_start; ++ volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); ++ while (count > 0) { ++ count -= 1; ++ cnt += 1 ; ++ d1 = *(s++) - 0x80; ++ dd1 = (unsigned long) d1 << 8; ++ *(dp ++) = dd1; ++ } ++ cnt *= jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++static void replay_fill_1x16_s(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ signed short d1; ++ signed long l1; ++ volatile signed short *s = (signed short *)src_start; ++ volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); ++ ++ while (count > 0) { ++ count -= 2; ++ cnt += 2 ; ++ d1 = *(s++); ++ l1 = (unsigned long)d1; ++ *(dp ++) = l1; ++ *(dp ++) = l1; ++ } ++ cnt = cnt * 2 * jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++static void replay_fill_2x16_s(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ signed short d1; ++ signed long l1; ++ volatile signed short *s = (signed short *)src_start; ++ volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); ++ ++ while (count > 0) { ++ count -= 2; ++ cnt += 2; ++ d1 = *(s++); ++ ++ l1 = (signed long)d1; ++ ++ *(dp ++) = l1; ++ } ++ ++ cnt *= jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++static unsigned int jz_audio_set_format(int dev, unsigned int fmt) ++{ ++ switch (fmt) { ++ case AFMT_U8: ++ __i2s_set_oss_sample_size(8); ++ __i2s_set_iss_sample_size(8); ++ jz_audio_format = fmt; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++ break; ++ case AFMT_S16_LE: ++ /* DAC path and ADC path */ ++ write_codec_file(2, 0x00); ++ //write_codec_file(2, 0x60); ++ ++ jz_audio_format = fmt; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++ ++ /* print all files */ ++ __i2s_set_oss_sample_size(16); ++ __i2s_set_iss_sample_size(16); ++ break; ++ case 18: ++ /* DAC path and ADC path */ ++ write_codec_file(2, 0x28); ++ //write_codec_file(2, 0x60); ++ __i2s_set_oss_sample_size(18); ++ __i2s_set_iss_sample_size(16); ++ jz_audio_format = fmt; ++ jz_update_filler(jz_audio_format,jz_audio_channels); ++ break; ++ case 24: ++ /* DAC path and ADC path */ ++ write_codec_file(2, 0x78); ++ //write_codec_file(2, 0x60); ++ __i2s_set_oss_sample_size(24); ++ __i2s_set_iss_sample_size(24); ++ jz_audio_format = fmt; ++ jz_update_filler(jz_audio_format,jz_audio_channels); ++ break; ++ case AFMT_QUERY: ++ break; ++ } ++ return jz_audio_format; ++} ++ ++static short jz_audio_set_channels(int dev, short channels) ++{ ++ switch (channels) { ++ case 1: ++ /* print all files */ ++ jz_audio_channels = channels; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++ write_codec_file_bit(1, 1, 6);//CR1.MONO->1 for Mono ++ break; ++ case 2: ++ /* print all files */ ++ jz_audio_channels = channels; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++ write_codec_file_bit(1, 0, 6);//CR1.MONO->0 for Stereo ++ break; ++ case 0: ++ break; ++ } ++ return jz_audio_channels; ++} ++ ++static void init_codec(void) ++{ ++#if defined(CONFIG_I2S_DLV) ++ /* reset DLV codec. from hibernate mode to sleep mode */ ++ write_codec_file(0, 0xf); ++ write_codec_file_bit(6, 0, 0); ++ write_codec_file_bit(6, 0, 1); ++ mdelay(200); ++ //write_codec_file(0, 0xf); ++ write_codec_file_bit(5, 0, 7);//PMR1.SB_DAC->0 ++ write_codec_file_bit(5, 0, 4);//PMR1.SB_ADC->0 ++ mdelay(10);//wait for stability ++#endif ++} ++ ++static void jz_audio_reset(void) ++{ ++ __i2s_disable_replay(); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_record(); ++ __i2s_disable_transmit_dma(); ++ REG_AIC_I2SCR = 0x10; ++ init_codec(); ++} ++ ++static int jz_audio_release(struct inode *inode, struct file *file); ++static int jz_audio_open(struct inode *inode, struct file *file); ++static int jz_audio_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg); ++static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait); ++static ssize_t jz_audio_write(struct file *file, const char *buffer,size_t count, loff_t *ppos); ++static ssize_t jz_audio_read(struct file *file, char *buffer,size_t count, loff_t *ppos); ++/* static struct file_operations jz_i2s_audio_fops */ ++static struct file_operations jz_i2s_audio_fops = ++{ ++ owner: THIS_MODULE, ++ open: jz_audio_open, ++ release: jz_audio_release, ++ write: jz_audio_write, ++ read: jz_audio_read, ++ poll: jz_audio_poll, ++ ioctl: jz_audio_ioctl ++}; ++ ++static int jz_i2s_open_mixdev(struct inode *inode, struct file *file) ++{ ++ int i; ++ int minor = MINOR(inode->i_rdev); ++ struct jz_i2s_controller_info *controller = i2s_controller; ++ ++ for (i = 0; i < NR_I2S; i++) ++ if (controller->i2s_codec[i] != NULL && controller->i2s_codec[i]->dev_mixer == minor) ++ goto match; ++ ++ if (!controller) ++ return -ENODEV; ++match: ++ file->private_data = controller->i2s_codec[i]; ++ return 0; ++} ++ ++static int jz_i2s_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct i2s_codec *codec = (struct i2s_codec *)file->private_data; ++ return codec->mixer_ioctl(codec, cmd, arg); ++} ++ ++static loff_t jz_i2s_llseek(struct file *file, loff_t offset, int origin) ++{ ++ return -ESPIPE; ++} ++ ++static struct file_operations jz_i2s_mixer_fops = ++{ ++ owner: THIS_MODULE, ++ llseek: jz_i2s_llseek, ++ ioctl: jz_i2s_ioctl_mixdev, ++ open: jz_i2s_open_mixdev, ++}; ++ ++static int i2s_mixer_ioctl(struct i2s_codec *codec, unsigned int cmd, unsigned long arg) ++{ ++ int ret; ++ u8 cur_vol; ++ long val = 0; ++ ++ switch (cmd) { ++ case SOUND_MIXER_INFO: ++ { ++ mixer_info info; ++#if defined(CONFIG_I2S_DLV) ++ strncpy(info.id, "DLV", sizeof(info.id)); ++ strncpy(info.name,"Jz4750 internal codec", sizeof(info.name)); ++#endif ++ ++ info.modify_counter = audio_mix_modcnt; ++ return copy_to_user((void *)arg, &info, sizeof(info)); ++ } ++ case SOUND_OLD_MIXER_INFO: ++ { ++ _old_mixer_info info; ++#if defined(CONFIG_I2S_DLV) ++ strncpy(info.id, "DLV", sizeof(info.id)); ++ strncpy(info.name,"Jz4750 internal codec", sizeof(info.name)); ++#endif ++ ++ return copy_to_user((void *)arg, &info, sizeof(info)); ++ } ++ case SOUND_MIXER_READ_STEREODEVS: ++ return put_user(0, (long *) arg); ++ case SOUND_MIXER_READ_CAPS: ++ val = SOUND_CAP_EXCL_INPUT; ++ return put_user(val, (long *) arg); ++ case SOUND_MIXER_READ_DEVMASK: ++ break; ++ case SOUND_MIXER_READ_RECMASK: ++ break; ++ case SOUND_MIXER_READ_RECSRC: ++ break; ++ case SOUND_MIXER_WRITE_SPEAKER: ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ ++ REG_CPM_I2SCDR = val; ++ printk_codec_files(); ++ break; ++ case SOUND_MIXER_WRITE_BASS: ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++#if defined(CONFIG_I2S_DLV) ++#endif ++ return 0; ++ ++ case SOUND_MIXER_READ_BASS: ++#if defined(CONFIG_I2S_DLV) ++#endif ++ return put_user(val, (long *) arg); ++ ++ case SOUND_MIXER_WRITE_VOLUME: ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val == 100) { ++ //REG_CPM_CLKGR |= 0x60; ++ printk("gate clock : 0x%08x\n", REG_CPM_CLKGR); ++ return 100; ++ } ++ if(val == 99) { ++ REG_CPM_CLKGR &= ~0x60; ++ return 99; ++ } ++ if(val == 98) { ++ REG_CPM_CPCCR |= 0x80400000; ++ return 98; ++ } ++ if(val == 97) { ++ REG_CPM_CPCCR &= ~0x8000000; ++ REG_CPM_CPCCR |= 0x0040000; ++ return 97; ++ } ++ if(val > 31) ++ val = 31; ++ ++ jz_audio_volume = val; ++ //printk("\njz_audio_volume=%d\n",jz_audio_volume); ++#if defined(CONFIG_I2S_DLV) ++ cur_vol = val; ++ write_codec_file(17, cur_vol | 0xc0); ++ write_codec_file(18, cur_vol); ++#endif ++ return 0; ++ case SOUND_MIXER_READ_VOLUME: ++#if defined(CONFIG_I2S_DLV) ++ val = jz_audio_volume | jz_audio_volume << 8; ++#endif ++ return put_user(val, (long *) arg); ++ case SOUND_MIXER_WRITE_LINE: ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if (val == 100) { ++ printk("Playback LINE input audio direct only is starting...\n"); ++ printk_codec_files(); ++ set_playback_line_input_audio_direct_only(); ++ printk_codec_files(); ++ return 100; ++ } ++ if (val == 99) { ++ printk("Playback LINE input audio direct only is end\n"); ++ unset_playback_line_input_audio_direct_only(); ++ printk_codec_files(); ++ return 99; ++ } ++ ++ if(val > 31) ++ val = 31; ++#if defined(CONFIG_I2S_DLV) ++ use_mic_line_flag = USE_LINEIN; ++ /* set gain */ ++ cur_vol = val; ++ cur_vol &= 0x1f; ++ write_codec_file(11, cur_vol);//GO1L ++ write_codec_file(12, cur_vol);//GO1R ++#endif ++ return 0; ++ break; ++ case SOUND_MIXER_WRITE_MIC: ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ ++ if(val > 0xf) ++ val = 0xf; ++#if defined(CONFIG_I2S_DLV) ++ use_mic_line_flag = USE_MIC; ++ /* set gain */ ++ //write_codec_file_bit(6, 1, 3);//GIM ++ cur_vol = val; ++ cur_vol |= cur_vol << 4; ++ write_codec_file(19, cur_vol);//GIL,GIR ++#endif ++ return 0; ++ ++ case SOUND_MIXER_READ_MIC: ++#if defined(CONFIG_I2S_DLV) ++#endif ++ ++ return put_user(val, (long *) arg); ++ default: ++ return -ENOSYS; ++ } ++ audio_mix_modcnt ++; ++ ++ return 0; ++} ++ ++int i2s_probe_codec(struct i2s_codec *codec) ++{ ++ /* generic OSS to I2S wrapper */ ++ codec->mixer_ioctl = i2s_mixer_ioctl; ++ return 1; ++} ++ ++ ++/* I2S codec initialisation. */ ++static int __init jz_i2s_codec_init(struct jz_i2s_controller_info *controller) ++{ ++ int num_i2s = 0; ++ struct i2s_codec *codec; ++ ++ for (num_i2s = 0; num_i2s < NR_I2S; num_i2s++) { ++ if ((codec = kmalloc(sizeof(struct i2s_codec),GFP_KERNEL)) == NULL) ++ return -ENOMEM; ++ memset(codec, 0, sizeof(struct i2s_codec)); ++ codec->private_data = controller; ++ codec->id = num_i2s; ++ ++ if (i2s_probe_codec(codec) == 0) ++ break; ++ if ((codec->dev_mixer = register_sound_mixer(&jz_i2s_mixer_fops, -1)) < 0) { ++ printk(KERN_ERR "Jz I2S: couldn't register mixer!\n"); ++ kfree(codec); ++ break; ++ } ++ controller->i2s_codec[num_i2s] = codec; ++ } ++ return num_i2s; ++} ++ ++static void replay_fill_2x18_s(unsigned long src_start, int count, int id) ++{ ++ int cnt = 0; ++ signed long d1; ++ signed long l1; ++ volatile signed long *s = (signed long *)src_start; ++ volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); ++ while (count > 0) { ++ count -= 4; ++ cnt += 4; ++ d1 = *(s++); ++ l1 = (signed long)d1; ++ *(dp ++) = l1; ++ } ++ ++ cnt *= jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++static void replay_fill_2x24_s(unsigned long src_start, int count, int id) ++{ ++ int cnt = 0; ++ signed long d1; ++ signed long l1; ++ volatile signed long *s = (signed long *)src_start; ++ volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); ++ while (count > 0) { ++ count -= 4; ++ cnt += 4; ++ d1 = *(s++); ++ l1 = (signed long)d1; ++ *(dp ++) = l1; ++ } ++ ++ cnt *= jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++ //printk("--- play 24 ---\n"); ++} ++ ++static void jz_update_filler(int format, int channels) ++{ ++ #define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) ++ ++ switch (TYPE(format, channels)) ++ { ++ default: ++ case TYPE(AFMT_U8, 1): ++ jz_audio_b = 4;//4bytes * 8bits =32bits ++ replay_filler = replay_fill_1x8_u; ++ record_filler = record_fill_1x8_u; ++ break; ++ case TYPE(AFMT_U8, 2): ++ jz_audio_b = 4; ++ replay_filler = replay_fill_2x8_u; ++ record_filler = record_fill_2x8_u; ++ break; ++ case TYPE(AFMT_S16_LE, 1): ++ //2bytes * 16bits =32bits ++ jz_audio_b = 2; ++ replay_filler = replay_fill_1x16_s; ++ record_filler = record_fill_1x16_s; ++ break; ++ case TYPE(AFMT_S16_LE, 2): ++ jz_audio_b = 2; ++ replay_filler = replay_fill_2x16_s; ++ record_filler = record_fill_2x16_s; ++ break; ++ case TYPE(18, 2): ++ jz_audio_b = 1; ++ replay_filler = replay_fill_2x18_s; ++ record_filler = record_fill_2x16_s; ++ break; ++ case TYPE(24, 2): ++ jz_audio_b = 1; ++ replay_filler = replay_fill_2x24_s; ++ record_filler = record_fill_2x24_s; ++ //printk("--- 24 ---\n"); ++ break; ++ } ++} ++ ++ ++#ifdef CONFIG_PROC_FS ++extern struct proc_dir_entry *proc_jz_root; ++ ++int i2s_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) ++{ ++ return 0; ++} ++ ++static int jz_i2s_init_proc(struct jz_i2s_controller_info *controller) ++{ ++ if (!create_proc_read_entry ("i2s", 0, proc_jz_root, i2s_read_proc, controller->i2s_codec[0])) ++ return -EIO; ++ return 0; ++} ++ ++static void jz_i2s_cleanup_proc(struct jz_i2s_controller_info *controller) ++{ ++ ; ++} ++#endif ++ ++static void __init attach_jz_i2s(struct jz_i2s_controller_info *controller) ++{ ++ char *name; ++ int adev;//No of Audio device. ++ name = controller->name; ++ jz_i2s_initHw(1);//initialize AIC controller and reset it ++ /* register /dev/audio */ ++ adev = register_sound_dsp(&jz_i2s_audio_fops, -1); ++ if (adev < 0) ++ goto audio_failed; ++ /* initialize I2S codec and register /dev/mixer */ ++ if (jz_i2s_codec_init(controller) <= 0) ++ goto mixer_failed; ++ ++#ifdef CONFIG_PROC_FS ++ if (jz_i2s_init_proc(controller) < 0) { ++ printk(KERN_ERR "%s: can't create I2S proc filesystem.\n", name); ++ goto proc_failed; ++ } ++#endif ++ ++ controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, JZCODEC_USER_BUFFER); ++ if (!controller->tmp1) { ++ printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); ++ goto tmp1_failed; ++ } ++ ++ controller->tmp2 = (void *)__get_free_pages(GFP_KERNEL, JZCODEC_USER_BUFFER); ++ if (!controller->tmp2) { ++ printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); ++ goto tmp2_failed; ++ } ++ ++ if ((controller->dma1 = jz_request_dma(DMA_ID_I2S_TX, "audio dac", jz_i2s_replay_dma_irq, IRQF_DISABLED, controller)) < 0) { ++ printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name); ++ goto dma1_failed; ++ } ++ if ((controller->dma2 = jz_request_dma(DMA_ID_I2S_RX, "audio adc", jz_i2s_record_dma_irq, IRQF_DISABLED, controller)) < 0) { ++ printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name); ++ goto dma2_failed; ++ } ++ printk("JzSOC On-Chip I2S controller registered (DAC: DMA(play):%d/IRQ%d,\n ADC: DMA(record):%d/IRQ%d)\n", controller->dma1, get_dma_done_irq(controller->dma1), controller->dma2, get_dma_done_irq(controller->dma2)); ++ ++ controller->dev_audio = adev; ++ ++ return; ++dma2_failed: ++ jz_free_dma(controller->dma2); ++dma1_failed: ++ jz_free_dma(controller->dma1); ++ free_pages((unsigned long)controller->tmp2, JZCODEC_USER_BUFFER); ++tmp2_failed: ++tmp1_failed: ++ free_pages((unsigned long)controller->tmp1, JZCODEC_USER_BUFFER); ++ ++#ifdef CONFIG_PROC_FS ++ jz_i2s_cleanup_proc(controller); ++#endif ++proc_failed: ++ /* unregister mixer dev */ ++mixer_failed: ++ unregister_sound_dsp(adev); ++audio_failed: ++ return; ++} ++ ++ ++static int __init probe_jz_i2s(struct jz_i2s_controller_info **controller) ++{ ++ if ((*controller = kmalloc(sizeof(struct jz_i2s_controller_info), ++ GFP_KERNEL)) == NULL) { ++ printk(KERN_ERR "Jz I2S Controller: out of memory.\n"); ++ return -ENOMEM; ++ } ++ (*controller)->name = "Jz I2S controller"; ++ (*controller)->opened1 = 0; ++ (*controller)->opened2 = 0; ++ init_waitqueue_head(&(*controller)->adc_wait); ++ init_waitqueue_head(&(*controller)->dac_wait); ++ spin_lock_init(&(*controller)->lock); ++ init_waitqueue_head(&rx_wait_queue); ++ init_waitqueue_head(&tx_wait_queue); ++ init_waitqueue_head(&pop_wait_queue); ++ ++ return 0; ++} ++ ++static void __exit unload_jz_i2s(struct jz_i2s_controller_info *controller) ++{ ++ int adev = controller->dev_audio; ++ jz_i2s_full_reset (controller); ++ controller->dev_audio = -1; ++ ++#ifdef CONFIG_PROC_FS ++ jz_i2s_cleanup_proc(controller); ++#endif ++ ++ jz_free_dma(controller->dma1); ++ jz_free_dma(controller->dma2); ++ free_pages((unsigned long)controller->tmp1, JZCODEC_USER_BUFFER); ++ free_pages((unsigned long)controller->tmp2, JZCODEC_USER_BUFFER); ++ if (adev >= 0) { ++ //unregister_sound_mixer(audio_devs[adev]->mixer_dev); ++ unregister_sound_dsp(controller->dev_audio); ++ } ++} ++ ++ ++#ifdef CONFIG_PM ++static int jz_i2s_suspend(struct jz_i2s_controller_info *controller, int state) ++{ ++ return 0; ++} ++ ++static int jz_i2s_resume(struct jz_i2s_controller_info *controller) ++{ ++ return 0; ++} ++ ++static int jz_i2s_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) ++{ ++ int ret; ++ struct jz_i2s_controller_info *controller = pm_dev->data; ++ ++ if (!controller) return -EINVAL; ++ ++ switch (req) { ++ case PM_SUSPEND: ++ ret = jz_i2s_suspend(controller, (int)data); ++ break; ++ ++ case PM_RESUME: ++ ret = jz_i2s_resume(controller); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++#endif /* CONFIG_PM */ ++ ++ ++static irqreturn_t aic_codec_irq(int irq, void *dev_id) ++{ ++ u8 file_9 = read_codec_file(9); ++ u8 file_8 = read_codec_file(8); ++ ++ //printk("--- 8:0x%x 9:0x%x ---\n",file_8,file_9); ++ if ((file_9 & 0x1f) == 0x10) { ++ ++ write_codec_file(8, 0x3f); ++ write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ mdelay(300); ++ while ((read_codec_file(9) & 0x4) != 0x4); ++ while ((read_codec_file(9) & 0x10) == 0x10) { ++ write_codec_file(9, 0x10); ++ } ++ write_codec_file_bit(5, 0, 6);//SB_OUT->0 ++ mdelay(300); ++ while ((read_codec_file(9) & 0x8) != 0x8); ++ write_codec_file(9, file_9); ++ write_codec_file(8, file_8); ++ ++ return IRQ_HANDLED; ++ } ++ ++ if (file_9 & 0x8) ++ ramp_up_end = jiffies; ++ else if (file_9 & 0x4) ++ ramp_down_end = jiffies; ++ else if (file_9 & 0x2) ++ gain_up_end = jiffies; ++ else if (file_9 & 0x1) ++ gain_down_end = jiffies; ++ ++ write_codec_file(9, file_9); ++ if (file_9 & 0xf) ++ wake_up(&pop_wait_queue); ++ while (REG_ICDC_RGDATA & 0x100); ++ ++ return IRQ_HANDLED; ++} ++ ++static int __init init_jz_i2s(void) ++{ ++ int errno, retval; ++ ++#if defined(CONFIG_I2S_DLV) ++ ramp_up_start = 0; ++ ramp_up_end = 0; ++ gain_up_start = 0; ++ gain_up_end = 0; ++ ramp_down_start = 0; ++ ramp_down_end = 0; ++ gain_down_start = 0; ++ gain_down_end = 0; ++ ++ //REG_CPM_CPCCR &= ~(1 << 31); ++ //REG_CPM_CPCCR &= ~(1 << 30); ++ use_mic_line_flag = USE_NONE; ++ write_codec_file(0, 0xf); ++ ++ REG_AIC_I2SCR = 0x10; ++ __i2s_internal_codec(); ++ __i2s_as_slave(); ++ __i2s_select_i2s(); ++ __aic_select_i2s(); ++ __aic_reset(); ++ mdelay(10); ++ REG_AIC_I2SCR = 0x10; ++ mdelay(20); ++ ++ /* power on DLV */ ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ mdelay(10); ++#endif ++ ++ if ((errno = probe_jz_i2s(&i2s_controller)) < 0) ++ return errno; ++ attach_jz_i2s(i2s_controller); ++#if defined(CONFIG_I2S_DLV) ++ __i2s_disable_transmit_intr(); ++ __i2s_disable_receive_intr(); ++ jz_codec_config = 0; ++ retval = request_irq(IRQ_AIC, aic_codec_irq, IRQF_DISABLED, "aic_codec_irq", NULL); ++ if (retval) { ++ printk("Could not get aic codec irq %d\n", IRQ_AIC); ++ return retval; ++ } ++#endif ++ ++ out_empty_queue.id = NULL; ++ out_full_queue.id = NULL; ++ out_busy_queue.id = NULL; ++ in_empty_queue.id = NULL; ++ in_full_queue.id = NULL; ++ in_busy_queue.id = NULL; ++ ++ jz_audio_fragsize = JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE; ++ jz_audio_fragstotal = JZCODEC_RW_BUFFER_TOTAL ; ++ Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ ++#ifdef CONFIG_PM ++ i2s_controller->pm = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ++ jz_i2s_pm_callback); ++ if (i2s_controller->pm) ++ i2s_controller->pm->data = i2s_controller; ++#endif ++ ++ __cpm_start_idct(); ++ __cpm_start_db(); ++ __cpm_start_me(); ++ __cpm_start_mc(); ++ __cpm_start_ipu(); ++ ++ return 0; ++} ++ ++ ++static void __exit cleanup_jz_i2s(void) ++{ ++ unload_jz_i2s(i2s_controller); ++#if defined(CONFIG_I2S_DLV) ++ free_irq(IRQ_AIC, NULL); ++#endif ++ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++} ++ ++module_init(init_jz_i2s); ++module_exit(cleanup_jz_i2s); ++ ++static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ //DECLARE_WAITQUEUE(wait, current); ++ unsigned long flags; ++ int count,i=0; ++ ++ //add_wait_queue(&ctrl->adc_wait, &wait); ++ for (;;) { ++ if (i < MAXDELAY) { ++ udelay(10); ++ i++; ++ } else ++ break; ++ //set_current_state(TASK_INTERRUPTIBLE); ++ spin_lock_irqsave(&ctrl->lock, flags); ++ //spin_lock(&ctrl->lock); ++ count = get_dma_residue(ctrl->dma2); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ //spin_unlock(&ctrl->lock); ++ if (count <= 0) ++ break; ++ ++ /*if (signal_pending(current)) ++ break;*/ ++ if (nonblock) { ++ //remove_wait_queue(&ctrl->adc_wait, &wait); ++ //current->state = TASK_RUNNING; ++ return -EBUSY; ++ } ++ } ++ //remove_wait_queue(&ctrl->adc_wait, &wait); ++ //current->state = TASK_RUNNING; ++ /*if (signal_pending(current)) ++ return -ERESTARTSYS;*/ ++ return 0; ++} ++static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ unsigned long flags; ++ int count,ele,busyele,emptyele,i=0; ++ ++ for (;;) { ++ if(!nonblock) {//blocked ++ if (i < MAXDELAY) { ++ udelay(10); ++ i++; ++ } else ++ break; ++ ++ ele = elements_in_queue(&out_full_queue); ++ if(ele <= 0) { ++ udelay(200); ++ ++ busyele = elements_in_queue(&out_busy_queue); ++ emptyele = elements_in_queue(&out_empty_queue); ++ if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ } ++ } ++ } else {//non-blocked ++ mdelay(100); ++ ele = elements_in_queue(&out_full_queue); ++ ++ if(ele <= 0) { ++ mdelay(100); ++ busyele = elements_in_queue(&out_busy_queue); ++ emptyele = elements_in_queue(&out_empty_queue); ++ ++ if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ } ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static void print_pop_duration(void) ++{ ++ long diff; ++#if 0 ++ printk(" ramp_up_start=0x%x\n",ramp_up_start); ++ printk(" ramp_up_end =0x%x\n",ramp_up_end); ++ printk(" gain_up_start=0x%x\n",gain_up_start); ++ printk(" gain_up_end =0x%x\n",gain_up_end); ++ ++ printk("ramp_down_start=0x%x\n",ramp_down_start); ++ printk("ramp_down_end =0x%x\n",ramp_down_end); ++ printk("gain_down_start=0x%x\n",gain_down_start); ++ printk("gain_down_end =0x%x\n",gain_down_end); ++#endif ++ ++ diff = (long)ramp_up_end - (long)ramp_up_start; ++ printk("ramp up duration: %d ms\n",diff * 1000 / HZ); ++ diff = (long)gain_up_end - (long)gain_up_start; ++ printk("gain up duration: %d ms\n",diff * 1000 / HZ); ++ diff = (long)gain_down_end - (long)gain_down_start; ++ printk("gain down duration: %d ms\n",diff); ++ diff = (long)ramp_down_end - (long)ramp_down_start; ++ printk("ramp down duration: %d ms\n",diff * 1000 / HZ); ++} ++ ++static int jz_audio_release(struct inode *inode, struct file *file) ++{ ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ unsigned long tfl; ++ ++ jz_codec_config = 0; ++ if (controller == NULL) ++ return -ENODEV; ++ ++ if (controller->opened1 == 1 && controller->opened2 == 1) { ++ __i2s_enable_transmit_dma(); ++ __i2s_enable_replay(); ++ ++ drain_dac(controller, file->f_flags & O_NONBLOCK); ++ /* wait for fifo empty */ ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ gain_down_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //gain_down_end = jiffies; ++ /*while (1) { ++ tfl = REG_AIC_SR & 0x00003f00; ++ if (tfl == 0) { ++ udelay(500); ++ break; ++ } ++ mdelay(2); ++ }*/ ++ mdelay(100); ++ disable_dma(controller->dma1); ++ set_dma_count(controller->dma1, 0); ++ ++ __i2s_disable_transmit_dma(); ++ __i2s_disable_replay(); ++ ++ //spin_lock_irqsave(&controller->ioctllock, flags); ++ spin_lock(&controller->ioctllock); ++ controller->total_bytes = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ //spin_unlock_irqrestore(&controller->ioctllock, flags); ++ spin_unlock(&controller->ioctllock); ++ ++ ///////////////////////// ++ first_record_call = 1; ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ drain_adc(controller, file->f_flags & O_NONBLOCK); ++ disable_dma(controller->dma2); ++ set_dma_count(controller->dma2, 0); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_record(); ++ //__aic_flush_fifo(); ++ //spin_lock_irqsave(&controller->ioctllock, flags); ++ spin_lock(&controller->ioctllock); ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ //spin_unlock_irqrestore(&controller->ioctllock, flags); ++ spin_unlock(&controller->ioctllock); ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ ramp_down_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //ramp_down_end = jiffies; ++ if (use_mic_line_flag == USE_LINEIN) { ++ unset_record_line_input_audio_with_audio_data_replay(); ++ //printk("3 use_mic_line_flag=%d\n",use_mic_line_flag); ++ } ++ if (use_mic_line_flag == USE_MIC) { ++ unset_record_mic_input_audio_with_audio_data_replay(); ++ //printk("4 use_mic_line_flag=%d\n",use_mic_line_flag); ++ } ++ ++#if 0 ++ unset_record_playing_audio_mixed_with_mic_input_audio(); ++#endif ++ __i2s_disable(); ++#endif ++ } else if ( controller->opened1 == 1 ) { ++ __i2s_enable_transmit_dma(); ++ __i2s_enable_replay(); ++ drain_dac(controller, file->f_flags & O_NONBLOCK); ++ /* wait for fifo empty */ ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ gain_down_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //gain_down_end = jiffies; ++ while (1) { ++ tfl = REG_AIC_SR & 0x00003f00; ++ if (tfl == 0) { ++ udelay(500); ++ break; ++ } ++ mdelay(2); ++ } ++ disable_dma(controller->dma1); ++ set_dma_count(controller->dma1, 0); ++ ++ __i2s_disable_transmit_dma(); ++ __i2s_disable_replay(); ++ ++ __aic_flush_fifo(); ++ ++ //spin_lock_irqsave(&controller->ioctllock, flags); ++ spin_lock(&controller->ioctllock); ++ controller->total_bytes = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ //spin_unlock_irqrestore(&controller->ioctllock, flags); ++ spin_unlock(&controller->ioctllock); ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ ramp_down_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //ramp_down_end = jiffies; ++ unset_audio_data_replay(); ++#endif ++ __i2s_disable(); ++ } else if ( controller->opened2 == 1 ) { ++ //controller->opened2 = 0; ++ first_record_call = 1; ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ drain_adc(controller, file->f_flags & O_NONBLOCK); ++ disable_dma(controller->dma2); ++ set_dma_count(controller->dma2, 0); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_record(); ++ ++ //spin_lock_irqsave(&controller->ioctllock, flags); ++ spin_lock(&controller->ioctllock); ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ //spin_unlock_irqrestore(&controller->ioctllock, flags); ++ spin_unlock(&controller->ioctllock); ++#if defined(CONFIG_I2S_DLV) ++#if 0 ++ /* unset Record MIC input audio with direct playback */ ++ unset_record_mic_input_audio_with_direct_playback(); ++#endif ++#if 1 ++ /* unset Record MIC input audio without playback */ ++ unset_record_mic_input_audio_without_playback(); ++#endif ++#if 0 ++ /* tested */ ++ /* unset Record LINE input audio without playback */ ++ unset_record_line_input_audio_without_playback(); ++#endif ++#endif ++ __i2s_disable(); ++ } ++ ++ if (controller->opened1 == 1 && controller->opened2 == 1) { ++ controller->opened1 = 0; ++ controller->opened2 = 0; ++ print_pop_duration(); ++ //__dmac_disable_module(0); ++ } else if ( controller->opened1 == 1 ) { ++ controller->opened1 = 0; ++ print_pop_duration(); ++ } else if ( controller->opened2 == 1 ) { ++ controller->opened2 = 0; ++ } ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ ++/* __cpm_stop_idct(); ++ __cpm_stop_db(); ++ __cpm_stop_me(); ++ __cpm_stop_mc(); ++ __cpm_stop_ipu();*/ ++ printk("close audio and clear ipu gate : 0x%08x\n", REG_CPM_CLKGR); ++ return 0; ++} ++ ++static int jz_audio_open(struct inode *inode, struct file *file) ++{ ++ int i; ++ struct jz_i2s_controller_info *controller = i2s_controller; ++ if (controller == NULL) ++ return -ENODEV; ++ mdelay(2); ++ REG_DMAC_DMACKE(0) = 0x3f; ++ if (controller->opened1 == 1 || controller->opened2 == 1 ) { ++ printk("\naudio is busy!\n"); ++ return -EBUSY; ++ } ++ jz_codec_config = 0; ++ ++ ramp_up_start = 0; ++ ramp_up_end = 0; ++ gain_up_start = 0; ++ gain_up_end = 0; ++ ramp_down_start = 0; ++ ramp_down_end = 0; ++ gain_down_start = 0; ++ gain_down_end = 0; ++ ++ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { ++ if (controller->opened1 == 1) ++ return -EBUSY; ++ controller->opened1 = 1; ++ /* for ioctl */ ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ ++ out_empty_queue.count = jz_audio_fragstotal; ++ for (i=0;i < jz_audio_fragstotal;i++) ++ *(out_empty_queue.id + i) = i; ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ ++ if (controller->opened2 == 1) ++ return -EBUSY; ++ ++ controller->opened2 = 1; ++ first_record_call = 1; ++ /* for ioctl */ ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ ++ in_empty_queue.count = jz_audio_fragstotal; ++ for (i=0;i < jz_audio_fragstotal;i++) ++ *(in_empty_queue.id + i) = i; ++ ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ } else if (file->f_mode & FMODE_WRITE) { ++ if (controller->opened1 == 1) ++ return -EBUSY; ++ controller->opened1 = 1; ++ //for ioctl ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ ++ out_empty_queue.count = jz_audio_fragstotal; ++ for (i=0;i < jz_audio_fragstotal;i++) ++ *(out_empty_queue.id + i) = i; ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ } else if (file->f_mode & FMODE_READ) { ++ if (controller->opened2 == 1) ++ return -EBUSY; ++ ++ controller->opened2 = 1; ++ first_record_call = 1; ++ //for ioctl ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ ++ in_empty_queue.count = jz_audio_fragstotal; ++ for (i=0;i < jz_audio_fragstotal;i++) ++ *(in_empty_queue.id + i) = i; ++ ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ } ++ ++ file->private_data = controller; ++ jz_audio_reset();//11.2 ++ ++ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { ++#if defined(CONFIG_I2S_DLV) ++ ++ if (use_mic_line_flag == USE_LINEIN) { ++ /* Record LINE input audio with Audio data replay (full duplex for linein) */ ++ /* codec_test_line */ ++ set_record_line_input_audio_with_audio_data_replay(); ++ ++ } ++ if (use_mic_line_flag == USE_MIC) { ++ /* Record MIC input audio with Audio data replay (full duplex) */ ++ /* codec_test_mic */ ++ set_record_mic_input_audio_with_audio_data_replay(); ++ } ++#if 0 ++ /* Record playing audio mixed with MIC input audio */ ++ set_record_playing_audio_mixed_with_mic_input_audio(); ++#endif ++ ++#endif ++ } else if (file->f_mode & FMODE_WRITE) { ++#if defined(CONFIG_I2S_DLV) ++ //mdelay(10); ++ /* Audio data replay */ ++ set_audio_data_replay(); ++#endif ++ } else if (file->f_mode & FMODE_READ) { ++#if defined(CONFIG_I2S_DLV) ++#if 0 ++ /* Record MIC input audio with direct playback */ ++ set_record_mic_input_audio_with_direct_playback(); ++#endif ++ ++#if 1 ++ /* set Record MIC input audio without playback */ ++ set_record_mic_input_audio_without_playback(); ++#endif ++#if 0 ++ /* tested */ ++ /* set Record LINE input audio without playback */ ++ set_record_line_input_audio_without_playback(); ++#endif ++ mdelay(1); ++#endif ++ } ++ ++ __aic_reset(); ++ ++ mdelay(10); ++ REG_AIC_I2SCR = 0x10; ++ mdelay(20); ++ __aic_flush_fifo(); ++ __i2s_enable(); ++ ndelay(100); ++ ++ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { ++#if defined(CONFIG_I2S_DLV) ++ //set SB_ADC or SB_DAC ++ __dmac_enable_module(0); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ ramp_up_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //ramp_up_end = jiffies; ++#endif ++ } else if (file->f_mode & FMODE_WRITE) { ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ ramp_up_start = jiffies; ++ /*while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_RCR = 0x1; ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_RGR = 1;*/ ++ sleep_on(&pop_wait_queue); ++ //ramp_up_end = jiffies; ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++#endif ++ } else if (file->f_mode & FMODE_READ) { ++#if defined(CONFIG_I2S_DLV) ++ if (jz_mic_only) ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ else ++ write_codec_file_bit(5, 0, 7);//SB_DAC->0 ++ mdelay(500); ++#endif ++ } ++ ++ ++ printk("open audio and set ipu gate : 0x%08x\n", REG_CPM_CLKGR); ++ return 0; ++} ++ ++ ++static int jz_audio_ioctl(struct inode *inode, struct file *file, ++unsigned int cmd, unsigned long arg) ++{ ++ int val,fullc,busyc,unfinish,newfragstotal,newfragsize; ++ unsigned int flags; ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ ++ audio_buf_info abinfo; ++ int i, bytes, id; ++ count_info cinfo; ++ val = 0; ++ bytes = 0; ++ switch (cmd) { ++ case OSS_GETVERSION: ++ return put_user(SOUND_VERSION, (int *)arg); ++ case SNDCTL_DSP_RESET: ++ return 0; ++ case SNDCTL_DSP_SYNC: ++ if (file->f_mode & FMODE_WRITE) ++ drain_dac(controller, file->f_flags & O_NONBLOCK); ++ return 0; ++ case SNDCTL_DSP_SPEED: ++ /* set smaple rate */ ++ { ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ if (val >= 0) ++ jz_audio_set_speed(controller->dev_audio, val); ++ return put_user(val, (int *)arg); ++ } ++ case SNDCTL_DSP_STEREO: ++ /* set stereo or mono channel */ ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ jz_audio_set_channels(controller->dev_audio, val ? 2 : 1); ++ return 0; ++ ++ case SNDCTL_DSP_GETBLKSIZE: ++ //return put_user(4*PAGE_SIZE, (int *)arg); ++ return put_user(jz_audio_fragsize , (int *)arg); ++ case SNDCTL_DSP_GETFMTS: ++ /* Returns a mask of supported sample format*/ ++ return put_user(AFMT_U8 | AFMT_S16_LE, (int *)arg); ++ ++ case SNDCTL_DSP_SETFMT: ++ /* Select sample format */ ++ { ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ if (val != AFMT_QUERY) { ++ jz_audio_set_format(controller->dev_audio,val); ++ } else { ++ if (file->f_mode & FMODE_READ) ++ val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; ++ else ++ val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; ++ ++ } ++ return put_user(val, (int *)arg); ++ } ++ ++ case SNDCTL_DSP_CHANNELS: ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ jz_audio_set_channels(controller->dev_audio, val); ++ return put_user(val, (int *)arg); ++ ++ case SNDCTL_DSP_POST: ++ /* FIXME: the same as RESET ?? */ ++ return 0; ++ ++ case SNDCTL_DSP_SUBDIVIDE: ++ return 0; ++ ++ case SNDCTL_DSP_SETFRAGMENT: ++ get_user(val, (long *) arg); ++ newfragsize = 1 << (val & 0xFFFF);//16 least bits ++ ++ if (newfragsize < 4 * PAGE_SIZE) ++ newfragsize = 4 * PAGE_SIZE; ++ if (newfragsize > (16 * PAGE_SIZE)) //16 PAGE_SIZE ++ newfragsize = 16 * PAGE_SIZE; ++ ++ newfragstotal = (val >> 16) & 0x7FFF; ++ if (newfragstotal < 2) ++ newfragstotal = 2; ++ if (newfragstotal > 32) ++ newfragstotal = 32; ++ if((jz_audio_fragstotal == newfragstotal) && (jz_audio_fragsize == newfragsize)) ++ return 0; ++ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ mdelay(500); ++ jz_audio_fragstotal = newfragstotal; ++ jz_audio_fragsize = newfragsize; ++ ++ Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ mdelay(10); ++ ++ return 0; ++ case SNDCTL_DSP_GETCAPS: ++ return put_user(DSP_CAP_REALTIME|DSP_CAP_BATCH, (int *)arg); ++ case SNDCTL_DSP_NONBLOCK: ++ file->f_flags |= O_NONBLOCK; ++ return 0; ++ case SNDCTL_DSP_SETDUPLEX: ++ return -EINVAL; ++ case SNDCTL_DSP_GETOSPACE: ++ { ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ ++ //unused fragment amount ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ jz_audio_fragments = elements_in_queue(&out_empty_queue); ++ for (i = 0; i < jz_audio_fragments; i++) ++ bytes += jz_audio_fragsize; ++ bytes /= jz_audio_b; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ abinfo.fragments = jz_audio_fragments; ++ abinfo.fragstotal = jz_audio_fragstotal; ++ abinfo.fragsize = jz_audio_fragsize; ++ abinfo.bytes = bytes; ++ return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; ++ } ++ case SNDCTL_DSP_GETISPACE: ++ if (!(file->f_mode & FMODE_READ)) ++ return -EINVAL; ++ ++ bytes = 0; ++ //unused fragment amount ++ jz_audio_fragments = elements_in_queue(&in_empty_queue); ++ for (i = 0; i < jz_audio_fragments; i++) ++ bytes += jz_audio_fragsize; ++ bytes /= jz_audio_b; ++ abinfo.fragments = jz_audio_fragments; ++ abinfo.fragstotal = jz_audio_fragstotal; ++ abinfo.fragsize = jz_audio_fragsize; ++ abinfo.bytes = bytes; ++ return copy_to_user((void *)arg, &abinfo, ++ sizeof(abinfo)) ? -EFAULT : 0; ++ case SNDCTL_DSP_GETTRIGGER: ++ val = 0; ++ if (file->f_mode & FMODE_READ && in_dma_buf) ++ val |= PCM_ENABLE_INPUT; ++ if (file->f_mode & FMODE_WRITE && out_dma_buf) ++ val |= PCM_ENABLE_OUTPUT; ++ return put_user(val, (int *)arg); ++ ++ case SNDCTL_DSP_SETTRIGGER: ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ return 0; ++ case SNDCTL_DSP_GETIPTR: ++ if (!(file->f_mode & FMODE_READ)) ++ return -EINVAL; ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ //controller->total_bytes += get_dma_residue(controller->dma2); ++ cinfo.bytes = controller->total_bytes; ++ cinfo.blocks = controller->blocks; ++ cinfo.ptr = controller->nextIn; ++ controller->blocks = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); ++ case SNDCTL_DSP_GETOPTR: ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ //controller->total_bytes += get_dma_residue(controller->dma1); ++ cinfo.bytes = controller->total_bytes; ++ cinfo.blocks = controller->blocks; ++ cinfo.ptr = controller->nextOut; ++ controller->blocks = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); ++ case SNDCTL_DSP_GETODELAY: ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ unfinish = 0; ++ fullc = elements_in_queue(&out_full_queue); ++ busyc = elements_in_queue(&out_busy_queue); ++ for(i = 0;i < fullc ;i ++) { ++ id = *(out_full_queue.id + i); ++ unfinish += *(out_dma_buf_data_count + id); ++ } ++ for(i = 0;i < busyc ;i ++) { ++ id = *(out_busy_queue.id + i); ++ unfinish += get_dma_residue(controller->dma1); ++ } ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ unfinish /= jz_audio_b; ++ return put_user(unfinish, (int *) arg); ++ case SOUND_PCM_READ_RATE: ++ return put_user(jz_audio_rate, (int *)arg); ++ case SOUND_PCM_READ_CHANNELS: ++ return put_user(jz_audio_channels, (int *)arg); ++ case SOUND_PCM_READ_BITS: ++ return put_user((jz_audio_format & (AFMT_S8 | AFMT_U8)) ? 8 : 16, ++ (int *)arg); ++ case SNDCTL_DSP_MAPINBUF: ++ case SNDCTL_DSP_MAPOUTBUF: ++ case SNDCTL_DSP_SETSYNCRO: ++ case SOUND_PCM_WRITE_FILTER: ++ case SOUND_PCM_READ_FILTER: ++ return -EINVAL; ++ } ++ return -EINVAL; ++} ++ ++ ++static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait) ++{ ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ unsigned long flags; ++ unsigned int mask = 0; ++ ++ if (file->f_mode & FMODE_WRITE) { ++ if (elements_in_queue(&out_empty_queue) > 0) ++ return POLLOUT | POLLWRNORM; ++ poll_wait(file, &controller->dac_wait, wait); ++ } ++ ++ if (file->f_mode & FMODE_READ) { ++ if (elements_in_queue(&in_full_queue) > 0) ++ return POLLIN | POLLRDNORM; ++ poll_wait(file, &controller->adc_wait, wait); ++ } ++ spin_lock_irqsave(&controller->lock, flags); ++ if (file->f_mode & FMODE_WRITE) { ++ if (elements_in_queue(&out_empty_queue) > 0) ++ mask |= POLLOUT | POLLWRNORM; ++ } else if (file->f_mode & FMODE_READ) { ++ if (elements_in_queue(&in_full_queue) > 0) ++ mask |= POLLIN | POLLRDNORM; ++ } ++ spin_unlock_irqrestore(&controller->lock, flags); ++ return mask; ++} ++ ++static ssize_t jz_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos) ++{ ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ int id, ret = 0, left_count, copy_count, cnt = 0; ++ ++ if (count < 0) ++ return -EINVAL; ++ ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ //spin_lock_irqsave(&controller->ioctllock, flags); ++ spin_lock(&controller->ioctllock); ++ controller->nextIn = 0; ++ //spin_unlock_irqrestore(&controller->ioctllock, flags); ++ spin_unlock(&controller->ioctllock); ++ ++ spin_lock(&controller->lock); ++ ++ copy_count = jz_audio_fragsize / 4; ++ ++ left_count = count; ++ spin_unlock(&controller->lock); ++ ++ if (first_record_call) { ++ first_record_call = 0; ++ audio_read_back_first: ++ if ((id = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id); ++ spin_lock(&controller->lock); ++ *(in_dma_buf_data_count + id) = copy_count * 4; ++ spin_unlock(&controller->lock); ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ ++ //write back ++ dma_cache_wback_inv(*(in_dma_buf + id), ++ *(in_dma_buf_data_count + id)); ++ audio_start_dma(controller->dma2,file->private_data, ++ *(in_dma_pbuf + id), ++ *(in_dma_buf_data_count + id), ++ DMA_MODE_READ); ++ //interruptible_sleep_on(&rx_wait_queue); ++ sleep_on(&rx_wait_queue); ++ } else ++ goto audio_read_back_first; ++ } ++ ++ while (left_count > 0) { ++ audio_read_back_second: ++ if (elements_in_queue(&in_full_queue) <= 0) { ++ if (file->f_flags & O_NONBLOCK) ++ return ret ? ret : -EAGAIN; ++ else ++ sleep_on(&rx_wait_queue); ++ } ++ /*if (signal_pending(current)) ++ return ret ? ret: -ERESTARTSYS; */ ++ if ((id = get_buffer_id(&in_full_queue)) >= 0) { ++ spin_lock(&controller->lock); ++ cnt = record_filler((unsigned long)controller->tmp2+ret, ++ copy_count, id); ++ spin_unlock(&controller->lock); ++ put_buffer_id(&in_empty_queue, id); ++ } else ++ goto audio_read_back_second; ++ ++ if (elements_in_queue(&in_busy_queue) == 0) { ++ if ((id=get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id); ++ spin_lock(&controller->lock); ++ *(in_dma_buf_data_count + id) = copy_count * 4; ++ spin_unlock(&controller->lock); ++ //write back ++ dma_cache_wback_inv(*(in_dma_buf + id), ++ *(in_dma_buf_data_count + id)); ++ audio_start_dma(controller->dma2,file->private_data, ++ *(in_dma_pbuf + id), ++ *(in_dma_buf_data_count + id), ++ DMA_MODE_READ); ++ } ++ } ++ if (ret + cnt > count) { ++ spin_lock(&controller->lock); ++ cnt = count - ret; ++ spin_unlock(&controller->lock); ++ } ++ if (copy_to_user(buffer+ret, controller->tmp2+ret, cnt)) ++ return ret ? ret : -EFAULT; ++ spin_lock(&controller->lock); ++ ret += cnt; ++ spin_unlock(&controller->lock); ++ //spin_lock_irqsave(&controller->ioctllock, flags); ++ spin_lock(&controller->ioctllock); ++ controller->nextIn += ret; ++ //spin_unlock_irqrestore(&controller->ioctllock, flags); ++ spin_unlock(&controller->ioctllock); ++ spin_lock(&controller->lock); ++ left_count -= cnt; ++ spin_unlock(&controller->lock); ++ }//while (left_count > 0) ++ return ret; ++} ++ ++static ssize_t jz_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) ++{ ++ int id, ret = 0, left_count, copy_count = 0, val; ++ unsigned int flags; ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ ++ if (count <= 0) ++ return -EINVAL; ++ ++ __i2s_enable_transmit_dma(); ++ __i2s_enable_replay(); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ //spin_lock(&controller->ioctllock); ++ controller->nextOut = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ //spin_unlock(&controller->ioctllock); ++ if (jz_audio_channels == 2) ++ copy_count = jz_audio_fragsize / jz_audio_b; ++ else if(jz_audio_channels == 1) ++ copy_count = jz_audio_fragsize / 4; ++ ++ left_count = count; ++ if (copy_from_user(controller->tmp1, buffer, count)) { ++ printk("copy_from_user failed:%d",ret); ++ return ret ? ret : -EFAULT; ++ } ++ ++ while (left_count > 0) { ++ audio_write_back: ++ if (file->f_flags & O_NONBLOCK) ++ udelay(2); ++ if (elements_in_queue(&out_empty_queue) == 0) { ++ // all are full ++ if (file->f_flags & O_NONBLOCK) ++ return ret;//no waiting,no block ++ else { ++ sleep_on(&tx_wait_queue);//blocked ++ } ++ } ++ /*if (signal_pending(current)) ++ return ret ? ret : -ERESTARTSYS;*/ ++ /* the end fragment size in this write */ ++ if (ret + copy_count > count) ++ copy_count = count - ret; ++ if ((id = get_buffer_id(&out_empty_queue)) >= 0) { ++ //replay_filler((unsigned long)controller->tmp1 + ret, copy_count, id); ++ replay_filler((signed long)controller->tmp1 + ret, copy_count, id); ++ if(*(out_dma_buf_data_count + id) > 0) { ++ put_buffer_id(&out_full_queue, id); //busy in ++ dma_cache_wback_inv(*(out_dma_buf + id), ++ *(out_dma_buf_data_count + id)); ++ } else ++ put_buffer_id(&out_empty_queue, id); //spare ++ } else ++ goto audio_write_back; ++ ++ left_count = left_count - copy_count; ++ ret += copy_count; ++ //spin_lock_irqsave(&controller->ioctllock, flags); ++ spin_lock(&controller->ioctllock); ++ controller->nextOut += ret; ++ //spin_unlock_irqrestore(&controller->ioctllock, flags); ++ spin_unlock(&controller->ioctllock); ++ if (elements_in_queue(&out_busy_queue) == 0) { ++ if ((id = get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_busy_queue, id); //first once,next spare ++ ++ if(*(out_dma_buf_data_count + id) > 0) { ++ audio_start_dma(controller->dma1, ++ file->private_data, ++ *(out_dma_pbuf + id), ++ *(out_dma_buf_data_count + id), ++ DMA_MODE_WRITE); ++ if (jz_codec_config == 0) { ++ write_codec_file_bit(1, 0, 5); ++ gain_up_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //gain_up_end = jiffies; ++ jz_codec_config = 1; ++ //SB_ADC->1 ++ //write_codec_file_bit(5, 1, 4); ++ //while(1); ++ } ++ } ++ } ++ } ++ } ++ return ret; ++} +diff -urN linux-2.6.24.7.old/sound/oss/jz_i2s_for_4750.c linux-2.6.24.7/sound/oss/jz_i2s_for_4750.c +--- linux-2.6.24.7.old/sound/oss/jz_i2s_for_4750.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/oss/jz_i2s_for_4750.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,2981 @@ ++/* ++ * linux/drivers/sound/Jz_i2s.c ++ * ++ * JzSOC On-Chip I2S audio driver. ++ * ++ * Copyright (C) 2005 by Junzheng Corp. ++ * Modified by cjfeng on Aug 9,2007,and not any bug on Jz4730 using ++ * dma channel 4&3,noah is tested. ++ * ++ * 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. ++ * ++ * Because the normal application of AUDIO devices are focused on Little_endian, ++ * then we only perform the little endian data format in driver. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "sound_config.h" ++ ++#define DPRINTK(args...) printk(args) ++#define DMA_ID_I2S_TX DMA_ID_AIC_TX ++#define DMA_ID_I2S_RX DMA_ID_AIC_RX ++#define NR_I2S 2 ++#define MAXDELAY 50000 ++#define JZCODEC_RW_BUFFER_SIZE 2 ++#define JZCODEC_RW_BUFFER_TOTAL 6 ++ ++typedef struct hpvol_shift_s ++{ ++ int hpvol; ++ int shift; ++} hpvol_shift_t; ++ ++mixer_info info; ++_old_mixer_info old_info; ++int codec_volue_shift; ++hpvol_shift_t hpvol_shift_table[72]; ++int abnormal_data_count; ++unsigned long i2s_clk; ++ ++void (*set_codec_mode)(void) = NULL; ++void (*clear_codec_mode)(void) = NULL; ++void (*set_codec_gpio_pin)(void) = NULL; ++void (*each_time_init_codec)(void) = NULL; ++int (*set_codec_startup_param)(void) = NULL; ++void (*set_codec_volume_table)(void) = NULL; ++void (*set_codec_record)(void) = NULL; ++void (*set_codec_replay)(void) = NULL; ++void (*set_codec_replay_record)(void); ++void (*turn_on_codec)(void) = NULL; ++void (*turn_off_codec)(void) = NULL; ++void (*set_codec_speed)(int rate) = NULL; ++void (*reset_codec)(void) = NULL; ++void (*codec_mixer_old_info_id_name)(void) = NULL; ++void (*codec_mixer_info_id_name)(void) = NULL; ++void (*set_codec_bass)(int val) = NULL; ++void (*set_codec_volume)(int val) = NULL; ++void (*set_codec_mic)(int val) = NULL; ++void (*i2s_resume_codec)(void) = NULL; ++void (*i2s_suspend_codec)(int wr,int rd) = NULL; ++void (*init_codec_pin)(void) = NULL; ++void (*set_codec_some_func)(void) = NULL; ++void (*clear_codec_record)(void) = NULL; ++void (*clear_codec_replay)(void) = NULL; ++void (*set_replay_hp_or_speaker)(void) = NULL; ++void (*set_codec_direct_mode)(void) = NULL; ++void (*clear_codec_direct_mode)(void) = NULL; ++ ++static int jz_audio_rate; ++static int jz_audio_format; ++static int jz_audio_volume; ++static int jz_audio_channels; ++static int jz_audio_b; /* bits expand multiple */ ++static int jz_audio_fragments; /* unused fragment amount */ ++static int jz_audio_fragstotal; ++static int jz_audio_fragsize; ++static int jz_audio_speed; ++ ++static int codec_bass_gain; ++static int audio_mix_modcnt; ++static int jz_audio_dma_tran_count; /* bytes count of one DMA transfer */ ++static int jz_mic_only = 1; ++static int jz_codec_config = 0; ++static unsigned long ramp_up_start; ++static unsigned long ramp_up_end; ++static unsigned long gain_up_start; ++static unsigned long gain_up_end; ++static unsigned long ramp_down_start; ++static unsigned long ramp_down_end; ++static unsigned long gain_down_start; ++static unsigned long gain_down_end; ++ ++static int codec_mic_gain; ++static int pop_dma_flag; ++static int last_dma_buffer_id; ++static int drain_flag; ++ ++static void (*old_mksound)(unsigned int hz, unsigned int ticks); ++extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); ++static void jz_update_filler(int bits, int channels); ++ ++static int Init_In_Out_queue(int fragstotal,int fragsize); ++static int Free_In_Out_queue(int fragstotal,int fragsize); ++static irqreturn_t jz_i2s_replay_dma_irq(int irqnr, void *ref); ++static irqreturn_t jz_i2s_record_dma_irq(int irqnr, void *ref); ++static void (*replay_filler)(signed long src_start, int count, int id); ++static int (*record_filler)(unsigned long dst_start, int count, int id); ++#if defined(CONFIG_I2S_ICODEC) ++static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample); ++#endif ++static void jz_audio_reset(void); ++static struct file_operations jz_i2s_audio_fops; ++ ++static DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue); ++static DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue); ++static DECLARE_WAIT_QUEUE_HEAD (drain_wait_queue); ++static DECLARE_WAIT_QUEUE_HEAD (pop_wait_queue); ++ ++struct jz_i2s_controller_info ++{ ++ int io_base; ++ int dma1; /* for play */ ++ int dma2; /* for record */ ++ char *name; ++ int dev_audio; ++ struct i2s_codec *i2s_codec[NR_I2S]; ++ int opened1; ++ int opened2; ++ unsigned char *tmp1; /* tmp buffer for sample conversions */ ++ unsigned char *tmp2; ++ spinlock_t lock; ++ spinlock_t ioctllock; ++ ++ wait_queue_head_t dac_wait; ++ wait_queue_head_t adc_wait; ++ int nextIn; /* byte index to next-in to DMA buffer */ ++ int nextOut; /* byte index to next-out from DMA buffer */ ++ int count; /* current byte count in DMA buffer */ ++ int finish; /* current transfered byte count in DMA buffer */ ++ unsigned total_bytes; /* total bytes written or read */ ++ unsigned blocks; ++ unsigned error; /* over/underrun */ ++#ifdef CONFIG_PM ++ struct pm_dev *pm; ++#endif ++}; ++ ++ ++static struct jz_i2s_controller_info *i2s_controller = NULL; ++struct i2s_codec ++{ ++ /* I2S controller connected with */ ++ void *private_data; ++ char *name; ++ int id; ++ int dev_mixer; ++ /* controller specific lower leverl i2s accessing routines */ ++ u16 (*codec_read) (u8 reg); /* the function accessing Codec REGs */ ++ void (*codec_write) (u8 reg, u16 val); ++ /* Wait for codec-ready */ ++ void (*codec_wait) (struct i2s_codec *codec); ++ /* OSS mixer masks */ ++ int modcnt; ++ int supported_mixers; ++ int stereo_mixers; ++ int record_sources; ++ int bit_resolution; ++ /* OSS mixer interface */ ++ int (*read_mixer) (struct i2s_codec *codec, int oss_channel); ++ void (*write_mixer)(struct i2s_codec *codec, int oss_channel, ++ unsigned int left, unsigned int right); ++ int (*recmask_io) (struct i2s_codec *codec, int rw, int mask); ++ int (*mixer_ioctl)(struct i2s_codec *codec, unsigned int cmd, unsigned long arg); ++ /* saved OSS mixer states */ ++ unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; ++}; ++ ++ ++typedef struct buffer_queue_s ++{ ++ int count; ++ int *id; ++ int lock; ++} buffer_queue_t; ++ ++typedef struct left_right_sample_s ++{ ++ signed long left; ++ signed long right; ++} left_right_sample_t; ++ ++static unsigned long pop_turn_onoff_buf; ++static unsigned long pop_turn_onoff_pbuf; ++ ++static unsigned long *out_dma_buf = NULL; ++static unsigned long *out_dma_pbuf = NULL; ++static unsigned long *out_dma_buf_data_count = NULL; ++static unsigned long *in_dma_buf = NULL; ++static unsigned long *in_dma_pbuf = NULL; ++static unsigned long *in_dma_buf_data_count = NULL; ++ ++static buffer_queue_t out_empty_queue; ++static buffer_queue_t out_full_queue; ++static buffer_queue_t out_busy_queue; ++static buffer_queue_t in_empty_queue; ++static buffer_queue_t in_full_queue; ++static buffer_queue_t in_busy_queue; ++static int first_record_call = 0; ++ ++static left_right_sample_t save_last_samples[64]; ++static int read_codec_file(int addr) ++{ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ mdelay(1); ++ return(__icdc_get_value()); ++} ++ ++static void printk_codec_files(void) ++{ ++ int cnt; ++ ++ printk("\n"); ++ ++ printk("REG_CPM_I2SCDR=0x%08x\n",REG_CPM_I2SCDR); ++ printk("REG_CPM_CLKGR=0x%08x\n",REG_CPM_CLKGR); ++ printk("REG_CPM_CPCCR=0x%08x\n",REG_CPM_CPCCR); ++ printk("REG_AIC_FR=0x%08x\n",REG_AIC_FR); ++ printk("REG_AIC_CR=0x%08x\n",REG_AIC_CR); ++ printk("REG_AIC_I2SCR=0x%08x\n",REG_AIC_I2SCR); ++ printk("REG_AIC_SR=0x%08x\n",REG_AIC_SR); ++ printk("REG_ICDC_RGDATA=0x%08x\n",REG_ICDC_RGDATA); ++ ++ for (cnt = 0; cnt <= 27 ; cnt++) { ++ printk(" ( %d : 0x%x ) ",cnt ,read_codec_file(cnt)); ++ } ++ printk("\n"); ++} ++ ++static void write_codec_file(int addr, int val) ++{ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ __icdc_set_cmd(val); /* write */ ++ mdelay(1); ++ __icdc_set_rgwr(); ++ mdelay(1); ++} ++ ++static int write_codec_file_bit(int addr, int bitval, int mask_bit) ++{ ++ int val; ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ mdelay(1); ++ val = __icdc_get_value(); /* read */ ++ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ val &= ~(1 << mask_bit); ++ if (bitval == 1) ++ val |= 1 << mask_bit; ++ ++ __icdc_set_cmd(val); /* write */ ++ mdelay(1); ++ __icdc_set_rgwr(); ++ mdelay(1); ++ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ val = __icdc_get_value(); /* read */ ++ ++ if (((val >> mask_bit) & bitval) == bitval) ++ return 1; ++ else ++ return 0; ++} ++ ++/* set Audio data replay */ ++static void set_audio_data_replay() ++{ ++ /* DAC path */ ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ mdelay(10); ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ //mdelay(100); ++ //write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //mdelay(300); ++} ++ ++/* unset Audio data replay */ ++static void unset_audio_data_replay(void) ++{ ++ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ //mdelay(800); ++ //write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ //mdelay(800); ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 4);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++/* set Record MIC input audio without playback */ ++static void set_record_mic_input_audio_without_playback(void) ++{ ++ /* ADC path for MIC IN */ ++ jz_mic_only = 1; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ mdelay(10); ++ write_codec_file_bit(1, 1, 2); ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ ++ write_codec_file(22, 0x40);//mic 1 ++ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ //write_codec_file_bit(6, 1, 3);// gain set ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ write_codec_file(1, 0x4); ++} ++ ++/* unset Record MIC input audio without playback */ ++static void unset_record_mic_input_audio_without_playback(void) ++{ ++ /* ADC path for MIC IN */ ++ jz_mic_only = 0; ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++/* set Record LINE input audio without playback */ ++static void set_record_line_input_audio_without_playback(void) ++{ ++ /* ADC path for LINE IN */ ++ jz_mic_only = 1; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ mdelay(10); ++ write_codec_file(22, 0xf6);//line in 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1 ++ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ mdelay(10); ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ write_codec_file(1, 0x4); ++} ++ ++/* unset Record LINE input audio without playback */ ++static void unset_record_line_input_audio_without_playback(void) ++{ ++ /* ADC path for LINE IN */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(5, 1, 3);//ONR1.SB_LIN->1 ++ ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++/* set Playback LINE input audio direct only */ ++static void set_playback_line_input_audio_direct_only() ++{ ++ jz_audio_reset();//or init_codec() ++ REG_AIC_I2SCR = 0x10; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ mdelay(10); ++ write_codec_file(22, 0xf6);//line in 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ mdelay(10); ++ write_codec_file_bit(1, 1, 2);//CR1.HP_BYPASS->1 ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ //write_codec_file_bit(5, 1, 7);//PMR1.SB_DAC->1 ++ //write_codec_file_bit(5, 1, 4);//PMR1.SB_ADC->1 ++} ++ ++/* unset Playback LINE input audio direct only */ ++static void unset_playback_line_input_audio_direct_only() ++{ ++ write_codec_file_bit(6, 0, 3);//GIM->0 ++ write_codec_file_bit(1, 0, 2);//PMR1.BYPASS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LINE->1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ mdelay(100); ++ write_codec_file_bit(5, 1, 5);//PMR1.SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++/* set Record MIC input audio with direct playback */ ++static void set_record_mic_input_audio_with_direct_playback(void) ++{ ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ jz_mic_only = 0; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ mdelay(10); ++ ++ write_codec_file(22, 0x60);//mic 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ write_codec_file_bit(6, 1, 3);// gain set ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ //write_codec_file(1, 0x4); ++} ++ ++/* unset Record MIC input audio with direct playback */ ++static void unset_record_mic_input_audio_with_direct_playback(void) ++{ ++ /* ADC path for MIC IN */ ++ jz_mic_only = 0; ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++/* set Record playing audio mixed with MIC input audio */ ++static void set_record_playing_audio_mixed_with_mic_input_audio(void) ++{ ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ mdelay(10); ++ ++ write_codec_file(22, 0x63);//mic 1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(6, 1, 3);// gain set ++ ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 ++ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0 ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ write_codec_file_bit(5, 0, 4);//PMR1.SB_MIX->0 ++} ++ ++/* unset Record playing audio mixed with MIC input audio */ ++static void unset_record_playing_audio_mixed_with_mic_input_audio(void) ++{ ++ /* ADC path */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 ++ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ //write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 5);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++/* set Record MIC input audio with Audio data replay (full duplex) */ ++static void set_record_mic_input_audio_with_audio_data_replay(void) ++{ ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0 ++ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 ++ ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++} ++ ++/* unset Record MIC input audio with Audio data replay (full duplex) */ ++static void unset_record_mic_input_audio_with_audio_data_replay(void) ++{ ++ /* ADC path */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 5);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++ ++///////// ++/* set Record LINE input audio with Audio data replay (full duplex for linein) */ ++static void set_record_line_input_audio_with_audio_data_replay(void) ++{ ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(22, 1, 7);//CR3.SB_MIC->1 ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ ++ ++ //jz_mic_only = 1; ++ write_codec_file(22, 0xc6);//line in 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++} ++ ++/* unset Record LINE input audio with Audio data replay (full duplex for linein) */ ++static void unset_record_line_input_audio_with_audio_data_replay(void) ++{ ++ /* ADC path */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 5);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++///////// ++static inline int get_buffer_id(struct buffer_queue_s *q) ++{ ++ int r; ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&q->lock, flags); ++ if (q->count == 0) { ++ spin_unlock_irqrestore(&q->lock, flags); ++ return -1; ++ } ++ r = *(q->id + 0); ++ for (i=0;i < q->count-1;i++) ++ *(q->id + i) = *(q->id + (i+1)); ++ q->count --; ++ spin_unlock_irqrestore(&q->lock, flags); ++ ++ return r; ++} ++ ++static inline void put_buffer_id(struct buffer_queue_s *q, int id) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&q->lock, flags); ++ *(q->id + q->count) = id; ++ q->count ++; ++ spin_unlock_irqrestore(&q->lock, flags); ++} ++ ++static inline int elements_in_queue(struct buffer_queue_s *q) ++{ ++ int r; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&q->lock, flags); ++ r = q->count; ++ spin_unlock_irqrestore(&q->lock, flags); ++ ++ return r; ++} ++ ++static inline void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode) ++{ ++ unsigned long flags; ++ struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ jz_audio_dma_tran_count = count / jz_audio_b; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ flags = claim_dma_lock(); ++ disable_dma(chan); ++ clear_dma_ff(chan); ++ //set_dma_mode(chan, mode); ++ jz_set_oss_dma(chan, mode, jz_audio_format); ++ set_dma_addr(chan, phyaddr); ++ if (count == 0) { ++ count++; ++ printk("JzSOC DMA controller can't set dma 0 count!\n"); ++ } ++ set_dma_count(chan, count); ++ enable_dma(chan); ++ release_dma_lock(flags); ++} ++ ++static irqreturn_t jz_i2s_record_dma_irq (int irq, void *dev_id) ++{ ++ int id1, id2; ++ unsigned long flags; ++ struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; ++ int dma = controller->dma2; ++ ++ disable_dma(dma); ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ ++ if(drain_flag == 1) ++ wake_up(&drain_wait_queue); ++ /* for DSP_GETIPTR */ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ id1 = get_buffer_id(&in_busy_queue); ++ put_buffer_id(&in_full_queue, id1); ++ ++ wake_up(&rx_wait_queue); ++ wake_up(&controller->adc_wait); ++ if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id2); ++ *(in_dma_buf_data_count + id2) = *(in_dma_buf_data_count + id1); ++ dma_cache_wback_inv(*(in_dma_buf + id2), *(in_dma_buf_data_count + id2)); ++ audio_start_dma(dma,dev_id, ++ *(in_dma_pbuf + id2), ++ *(in_dma_buf_data_count + id2), ++ DMA_MODE_READ); ++ } else ++ in_busy_queue.count = 0; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t jz_i2s_replay_dma_irq (int irq, void *dev_id) ++{ ++ int id; ++ unsigned long flags; ++ struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; ++ int dma = controller->dma1; ++ ++ disable_dma(dma); ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ ++ if(pop_dma_flag == 1) { ++ pop_dma_flag = 0; ++ wake_up(&pop_wait_queue); ++ } else { ++ if(drain_flag == 1) { ++ /* Is replay dma buffer over ? */ ++ if(elements_in_queue(&out_full_queue) <= 0) { ++ drain_flag = 0; ++ wake_up(&drain_wait_queue); ++ } ++ } ++ ++ /* for DSP_GETOPTR */ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ if ((id = get_buffer_id(&out_busy_queue)) < 0) ++ printk(KERN_DEBUG "Strange DMA finish interrupt for I2S module\n"); ++ put_buffer_id(&out_empty_queue, id); ++ if ((id = get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_busy_queue, id); ++ if(*(out_dma_buf_data_count + id) > 0) { ++ audio_start_dma(dma, dev_id, *(out_dma_pbuf + id), ++ *(out_dma_buf_data_count + id), ++ DMA_MODE_WRITE); ++ last_dma_buffer_id = id; ++ } ++ } else ++ out_busy_queue.count = 0; ++ ++ if (elements_in_queue(&out_empty_queue) > 0) { ++ wake_up(&tx_wait_queue); ++ wake_up(&controller->dac_wait); ++ } ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void jz_i2s_initHw(int set) ++{ ++#if defined(CONFIG_MIPS_JZ_URANUS) ++ i2s_clk = 48000000; ++#else ++ i2s_clk = __cpm_get_i2sclk(); ++#endif ++ __i2s_disable(); ++ if(set) ++ __i2s_reset(); ++ schedule_timeout(5); ++ if(each_time_init_codec) ++ each_time_init_codec(); ++ __i2s_disable_record(); ++ __i2s_disable_replay(); ++ __i2s_disable_loopback(); ++ __i2s_set_transmit_trigger(4); ++ __i2s_set_receive_trigger(3); ++} ++ ++static int Init_In_Out_queue(int fragstotal,int fragsize) ++{ ++ int i; ++ ++ /* recording */ ++ in_empty_queue.count = fragstotal; ++ in_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_buf) ++ goto all_mem_err; ++ in_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_pbuf) ++ goto all_mem_err; ++ in_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_buf_data_count) ++ goto all_mem_err; ++ in_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_empty_queue.id) ++ goto all_mem_err; ++ in_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_full_queue.id) ++ goto all_mem_err; ++ in_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_busy_queue.id) ++ goto all_mem_err; ++ ++ for (i=0;i < fragstotal;i++) ++ *(in_empty_queue.id + i) = i; ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ ++ for (i = 0; i < fragstotal; i++) { ++ *(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); ++ if (*(in_dma_buf + i) == 0) ++ goto mem_failed_in; ++ *(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i))); ++ dma_cache_wback_inv(*(in_dma_buf + i), fragsize); ++ } ++ ++ /* playing */ ++ out_empty_queue.count = fragstotal; ++ out_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!out_dma_buf) ++ goto all_mem_err; ++ out_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!out_dma_pbuf) ++ goto all_mem_err; ++ out_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ ++ if (!out_dma_buf_data_count) ++ goto all_mem_err; ++ out_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_empty_queue.id) ++ goto all_mem_err; ++ out_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_full_queue.id) ++ goto all_mem_err; ++ out_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_busy_queue.id) ++ goto all_mem_err; ++ for (i=0;i < fragstotal;i++) ++ *(out_empty_queue.id + i) = i; ++ ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ /* alloc DMA buffer */ ++ for (i = 0; i < fragstotal; i++) { ++ *(out_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); ++ if (*(out_dma_buf + i) == 0) { ++ printk(" can't allocate required DMA(OUT) buffers.\n"); ++ goto mem_failed_out; ++ } ++ *(out_dma_pbuf + i) = virt_to_phys((void *)(*(out_dma_buf + i))); ++ } ++ ++ return 1; ++all_mem_err: ++ printk("error:allocate memory occur error 1!\n"); ++ return 0; ++mem_failed_out: ++ printk("error:allocate memory occur error 2!\n"); ++ for (i = 0; i < fragstotal; i++) { ++ if(*(out_dma_buf + i)) ++ free_pages(*(out_dma_buf + i), get_order(fragsize)); ++ } ++ ++ return 0; ++mem_failed_in: ++ printk("error:allocate memory occur error 3!\n"); ++ for (i = 0; i < fragstotal; i++) { ++ if(*(in_dma_buf + i)) ++ free_pages(*(in_dma_buf + i), get_order(fragsize)); ++ } ++ return 0; ++} ++ ++static int Free_In_Out_queue(int fragstotal,int fragsize) ++{ ++ int i; ++ /* playing */ ++ if(out_dma_buf != NULL) { ++ for (i = 0; i < fragstotal; i++) { ++ if(*(out_dma_buf + i)) ++ free_pages(*(out_dma_buf + i), get_order(fragsize)); ++ *(out_dma_buf + i) = 0; ++ } ++ kfree(out_dma_buf); ++ out_dma_buf = NULL; ++ } ++ if(out_dma_pbuf) { ++ kfree(out_dma_pbuf); ++ out_dma_pbuf = NULL; ++ } ++ if(out_dma_buf_data_count) { ++ kfree(out_dma_buf_data_count); ++ out_dma_buf_data_count = NULL; ++ } ++ if(out_empty_queue.id) { ++ kfree(out_empty_queue.id); ++ out_empty_queue.id = NULL; ++ } ++ if(out_full_queue.id) { ++ kfree(out_full_queue.id); ++ out_full_queue.id = NULL; ++ } ++ if(out_busy_queue.id) { ++ kfree(out_busy_queue.id); ++ out_busy_queue.id = NULL; ++ } ++ out_empty_queue.count = fragstotal; ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ ++ /* recording */ ++ if(in_dma_buf) { ++ for (i = 0; i < fragstotal; i++) { ++ if(*(in_dma_buf + i)) { ++ dma_cache_wback_inv(*(in_dma_buf + i), fragsize); ++ free_pages(*(in_dma_buf + i), get_order(fragsize)); ++ } ++ *(in_dma_buf + i) = 0; ++ } ++ kfree(in_dma_buf); ++ in_dma_buf = NULL; ++ } ++ if(in_dma_pbuf) { ++ kfree(in_dma_pbuf); ++ in_dma_pbuf = NULL; ++ } ++ if(in_dma_buf_data_count) { ++ kfree(in_dma_buf_data_count); ++ in_dma_buf_data_count = NULL; ++ } ++ if(in_empty_queue.id) { ++ kfree(in_empty_queue.id); ++ in_empty_queue.id = NULL; ++ } ++ if(in_full_queue.id) { ++ kfree(in_full_queue.id); ++ in_full_queue.id = NULL; ++ } ++ if(in_busy_queue.id) { ++ kfree(in_busy_queue.id); ++ in_busy_queue.id = NULL; ++ } ++ ++ in_empty_queue.count = fragstotal; ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ ++ return 1; ++} ++ ++static void jz_i2s_full_reset(struct jz_i2s_controller_info *controller) ++{ ++ jz_i2s_initHw(0); ++} ++ ++static int jz_audio_set_speed(int dev, int rate) ++{ ++ /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 ? */ ++ jz_audio_speed = rate; ++ if (rate > 48000) ++ rate = 48000; ++ if (rate < 8000) ++ rate = 8000; ++ jz_audio_rate = rate; ++ ++ if(set_codec_speed) ++ set_codec_speed(rate); ++ ++ return jz_audio_rate; ++} ++ ++ ++static int record_fill_1x8_u(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long data; ++ volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ volatile unsigned char *dp = (unsigned char*)dst_start; ++ ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ cnt++; ++ data = *(s++); ++ *(dp ++) = ((data << 16) >> 24) + 0x80; ++ s++; /* skip the other channel */ ++ } ++ ++ return cnt; ++} ++ ++ ++static int record_fill_2x8_u(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long d1, d2; ++ volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ volatile unsigned char *dp = (unsigned char*)dst_start; ++ ++ while (count > 0) { ++ count -= 2; ++ cnt += 2; ++ d1 = *(s++); ++ *(dp ++) = ((d1 << 16) >> 24) + 0x80; ++ d2 = *(s++); ++ *(dp ++) = ((d2 << 16) >> 24) + 0x80; ++ } ++ ++ return cnt; ++} ++ ++ ++static int record_fill_1x16_s(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long d1; ++ unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ unsigned short *dp = (unsigned short *)dst_start; ++ ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ cnt += 2; /* count in byte */ ++ d1 = *(s++); ++ *(dp ++) = (d1 << 16) >> 16; ++ s++; /* skip the other channel */ ++ } ++ ++ return cnt; ++} ++ ++ ++static int record_fill_2x16_s(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned long d1, d2; ++ unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); ++ unsigned short *dp = (unsigned short *)dst_start; ++ while (count > 0) { ++ count -= 2; /* count in dword */ ++ cnt += 4; /* count in byte */ ++ d1 = *(s++); ++ d2 = *(s++); ++ if(abnormal_data_count > 0) { ++ d1 = d2 = 0; ++ abnormal_data_count --; ++ } ++ *(dp ++) = (d1 << 16) >> 16; ++ *(dp ++) = (d2 << 16) >> 16; ++ } ++ ++ return cnt; ++} ++ ++static void replay_fill_1x8_u(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned char data; ++ unsigned long ddata; ++ volatile unsigned char *s = (unsigned char *)src_start; ++ volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); ++ ++ while (count > 0) { ++ count--; ++ cnt += 1; ++ data = *(s++) - 0x80; ++ ddata = (unsigned long) data << 8; ++ *(dp ++) = ddata; ++ *(dp ++) = ddata; ++ ++ /* save last left and right */ ++ if(count == 1) { ++ save_last_samples[id].left = ddata; ++ save_last_samples[id].right = ddata; ++ } ++ } ++ cnt = cnt * 2 * jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++ ++static void replay_fill_2x8_u(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned char d1; ++ unsigned long dd1; ++ volatile unsigned char *s = (unsigned char *)src_start; ++ volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); ++ ++ while (count > 0) { ++ count -= 1; ++ cnt += 1 ; ++ d1 = *(s++) - 0x80; ++ dd1 = (unsigned long) d1 << 8; ++ *(dp ++) = dd1; ++ /* save last left */ ++ if(count == 2) ++ save_last_samples[id].left = dd1; ++ /* save last right */ ++ if(count == 1) ++ save_last_samples[id].right = dd1; ++ } ++ cnt *= jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++ ++static void replay_fill_1x16_s(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ signed short d1; ++ signed long l1; ++ volatile signed short *s = (signed short *)src_start; ++ volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); ++ ++ while (count > 0) { ++ count -= 2; ++ cnt += 2 ; ++ d1 = *(s++); ++ l1 = (signed long)d1; ++ *(dp ++) = l1; ++ *(dp ++) = l1; ++ ++ /* save last left and right */ ++ if(count == 1) { ++ save_last_samples[id].left = l1; ++ save_last_samples[id].right = l1; ++ } ++ } ++ cnt = cnt * 2 * jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++#if 0 ++static void replay_fill_2x16_s(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ signed short d1; ++ signed long l1; ++ int mute_cnt = 0; ++ signed long tmp1,tmp2; ++ volatile signed short *s = (signed short *)src_start; ++ volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); ++#if defined(CONFIG_I2S_ICDC) ++ volatile signed long *before_dp; ++ int sam_rate = jz_audio_rate / 20; ++ ++ tmp1 = tmp2 = 0; ++ while (count > 0) { ++ count -= 2; ++ cnt += 2; ++ d1 = *(s++); ++ ++ l1 = (signed long)d1; ++ l1 >>= codec_volue_shift; ++ ++ if(l1 == 0) { ++ mute_cnt ++; ++ if(mute_cnt >= sam_rate) { ++ before_dp = dp - 10; ++ *(before_dp) = (signed long)1; ++ before_dp = dp - 11; ++ *(before_dp) = (signed long)1; ++ mute_cnt = 0; ++ } ++ } else ++ mute_cnt = 0; ++ ++ *(dp ++) = l1; ++ ++ tmp1 = tmp2; ++ tmp2 = l1; ++ } ++ ++ /* save last left */ ++ save_last_samples[id].left = tmp1; ++ /* save last right */ ++ save_last_samples[id].right = tmp2; ++#endif ++#if defined(CONFIG_I2S_DLV) ++ while (count > 0) { ++ count -= 2; ++ cnt += 2; ++ d1 = *(s++); ++ ++ l1 = (signed long)d1; ++ ++ *(dp ++) = l1; ++ } ++#endif ++ cnt *= jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++} ++#else ++static void replay_fill_2x16_s(signed long src_start, int count, int id) ++{ ++ int cnt = 0; ++ signed short d1; ++ signed long l1; ++ int mute_cnt = 0; ++ signed long tmp1,tmp2; ++ ++#if 0 ++ volatile signed short *s = (signed short *)src_start; ++ volatile signed short *dp = (signed short*)(*(out_dma_buf + id)); ++ memcpy((char*)dp, (char*)s, count); ++ *(out_dma_buf_data_count + id) = count; ++#else ++ volatile signed short *s = (signed short *)src_start; ++ volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); ++ while (count > 0) { ++ count -= 2; ++ cnt += 2; ++ d1 = *(s++); ++ ++ l1 = (signed long)d1; ++ ++ *(dp ++) = l1; ++ } ++ cnt *= jz_audio_b; ++ *(out_dma_buf_data_count + id) = cnt; ++#endif ++} ++#endif ++ ++ ++static unsigned int jz_audio_set_format(int dev, unsigned int fmt) ++{ ++ switch (fmt) { ++ case AFMT_U8: ++ __i2s_set_oss_sample_size(8); ++ __i2s_set_iss_sample_size(8); ++ jz_audio_format = fmt; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++ break; ++ case AFMT_S16_LE: ++#if defined(CONFIG_I2S_DLV) ++ /* DAC path and ADC path */ ++ write_codec_file(2, 0x00); ++ //write_codec_file(2, 0x60); ++#endif ++ jz_audio_format = fmt; ++ jz_update_filler(jz_audio_format,jz_audio_channels); ++ /* print all files */ ++ __i2s_set_oss_sample_size(16); ++ __i2s_set_iss_sample_size(16); ++ break; ++ ++ case AFMT_QUERY: ++ break; ++ } ++ ++ return jz_audio_format; ++} ++ ++ ++static short jz_audio_set_channels(int dev, short channels) ++{ ++ switch (channels) { ++ case 1: ++ if(set_codec_some_func) ++ set_codec_some_func(); ++ jz_audio_channels = channels; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(1, 1, 6);//CR1.MONO->1 for Mono ++#endif ++ break; ++ case 2: ++ jz_audio_channels = channels; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(1, 0, 6);//CR1.MONO->0 for Stereo ++#endif ++ break; ++ case 0: ++ break; ++ } ++ ++ return jz_audio_channels; ++} ++ ++static void init_codec(void) ++{ ++ /* inititalize internal I2S codec */ ++ if(init_codec_pin) ++ init_codec_pin(); ++ ++#if defined(CONFIG_I2S_ICDC) ++ /* initialize AIC but not reset it */ ++ jz_i2s_initHw(0); ++#endif ++ if(reset_codec) ++ reset_codec(); ++} ++ ++static void jz_audio_reset(void) ++{ ++ __i2s_disable_replay(); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_record(); ++ __i2s_disable_transmit_dma(); ++#if defined(CONFIG_I2S_DLV) ++ REG_AIC_I2SCR = 0x10; ++#endif ++ init_codec(); ++} ++ ++static int jz_audio_release(struct inode *inode, struct file *file); ++static int jz_audio_open(struct inode *inode, struct file *file); ++static int jz_audio_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg); ++static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait); ++static ssize_t jz_audio_write(struct file *file, const char *buffer,size_t count, loff_t *ppos); ++static ssize_t jz_audio_read(struct file *file, char *buffer,size_t count, loff_t *ppos); ++ ++/* static struct file_operations jz_i2s_audio_fops */ ++static struct file_operations jz_i2s_audio_fops = ++{ ++ owner: THIS_MODULE, ++ open: jz_audio_open, ++ release: jz_audio_release, ++ write: jz_audio_write, ++ read: jz_audio_read, ++ poll: jz_audio_poll, ++ ioctl: jz_audio_ioctl ++}; ++ ++static int jz_i2s_open_mixdev(struct inode *inode, struct file *file) ++{ ++ int i; ++ int minor = MINOR(inode->i_rdev); ++ struct jz_i2s_controller_info *controller = i2s_controller; ++ ++ for (i = 0; i < NR_I2S; i++) ++ if (controller->i2s_codec[i] != NULL && controller->i2s_codec[i]->dev_mixer == minor) ++ goto match; ++ ++ if (!controller) ++ return -ENODEV; ++match: ++ file->private_data = controller->i2s_codec[i]; ++ ++ return 0; ++} ++ ++static int jz_i2s_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct i2s_codec *codec = (struct i2s_codec *)file->private_data; ++ return codec->mixer_ioctl(codec, cmd, arg); ++} ++ ++static loff_t jz_i2s_llseek(struct file *file, loff_t offset, int origin) ++{ ++ return -ESPIPE; ++} ++ ++static struct file_operations jz_i2s_mixer_fops = ++{ ++ owner: THIS_MODULE, ++ llseek: jz_i2s_llseek, ++ ioctl: jz_i2s_ioctl_mixdev, ++ open: jz_i2s_open_mixdev, ++}; ++ ++static int i2s_mixer_ioctl(struct i2s_codec *codec, unsigned int cmd, unsigned long arg) ++{ ++ int ret; ++ long val = 0; ++ switch (cmd) { ++ case SOUND_MIXER_INFO: ++ ++ if(codec_mixer_info_id_name) ++ codec_mixer_info_id_name(); ++ info.modify_counter = audio_mix_modcnt; ++ ++ return copy_to_user((void *)arg, &info, sizeof(info)); ++ case SOUND_OLD_MIXER_INFO: ++ ++ if(codec_mixer_old_info_id_name) ++ codec_mixer_old_info_id_name(); ++ ++ return copy_to_user((void *)arg, &old_info, sizeof(info)); ++ case SOUND_MIXER_READ_STEREODEVS: ++ ++ return put_user(0, (long *) arg); ++ case SOUND_MIXER_READ_CAPS: ++ ++ val = SOUND_CAP_EXCL_INPUT; ++ return put_user(val, (long *) arg); ++ case SOUND_MIXER_READ_DEVMASK: ++ break; ++ case SOUND_MIXER_READ_RECMASK: ++ break; ++ case SOUND_MIXER_READ_RECSRC: ++ break; ++ case SOUND_MIXER_WRITE_SPEAKER: ++ ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ switch(val) { ++ case 100: ++ if(set_codec_direct_mode) ++ set_codec_direct_mode(); ++ break; ++ case 0: ++ if(clear_codec_direct_mode) ++ clear_codec_direct_mode(); ++ break; ++ } ++ break; ++ case SOUND_MIXER_WRITE_BASS: ++ ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ codec_bass_gain = val; ++ if(set_codec_bass) ++ set_codec_bass(val); ++ ++ return 0; ++ case SOUND_MIXER_READ_BASS: ++ ++ val = codec_bass_gain; ++ ret = val << 8; ++ val = val | ret; ++ ++ return put_user(val, (long *) arg); ++ case SOUND_MIXER_WRITE_VOLUME: ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ if (val > 31) ++ val = 31; ++ jz_audio_volume = val; ++ ++ if(set_codec_volume) ++ set_codec_volume(val); ++ return 0; ++ case SOUND_MIXER_READ_VOLUME: ++ ++ val = jz_audio_volume; ++ ret = val << 8; ++ val = val | ret; ++ ++ return put_user(val, (long *) arg); ++ case SOUND_MIXER_WRITE_MIC: ++ ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ codec_mic_gain = val; ++ if(set_codec_mic) ++ set_codec_mic(val); ++ ++ return 0; ++ case SOUND_MIXER_READ_MIC: ++ ++ val = codec_mic_gain; ++ ret = val << 8; ++ val = val | ret; ++ ++ return put_user(val, (long *) arg); ++ default: ++ return -ENOSYS; ++ } ++ audio_mix_modcnt ++; ++ return 0; ++} ++ ++ ++int i2s_probe_codec(struct i2s_codec *codec) ++{ ++ /* generic OSS to I2S wrapper */ ++ codec->mixer_ioctl = i2s_mixer_ioctl; ++ return 1; ++} ++ ++ ++/* I2S codec initialisation. */ ++static int __init jz_i2s_codec_init(struct jz_i2s_controller_info *controller) ++{ ++ int num_i2s = 0; ++ struct i2s_codec *codec; ++ ++ for (num_i2s = 0; num_i2s < NR_I2S; num_i2s++) { ++ if ((codec = kmalloc(sizeof(struct i2s_codec),GFP_KERNEL)) == NULL) ++ return -ENOMEM; ++ memset(codec, 0, sizeof(struct i2s_codec)); ++ codec->private_data = controller; ++ codec->id = num_i2s; ++ ++ if (i2s_probe_codec(codec) == 0) ++ break; ++ if ((codec->dev_mixer = register_sound_mixer(&jz_i2s_mixer_fops, -1)) < 0) { ++ printk(KERN_ERR "Jz I2S: couldn't register mixer!\n"); ++ kfree(codec); ++ break; ++ } ++ controller->i2s_codec[num_i2s] = codec; ++ } ++ return num_i2s; ++} ++ ++ ++static void jz_update_filler(int format, int channels) ++{ ++#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) ++ ++ switch (TYPE(format, channels)) ++ { ++ ++ case TYPE(AFMT_U8, 1): ++ jz_audio_b = 4; /* 4bytes * 8bits =32bits */ ++ replay_filler = replay_fill_1x8_u; ++ record_filler = record_fill_1x8_u; ++ break; ++ case TYPE(AFMT_U8, 2): ++ jz_audio_b = 4; ++ replay_filler = replay_fill_2x8_u; ++ record_filler = record_fill_2x8_u; ++ break; ++ case TYPE(AFMT_S16_LE, 1): ++ jz_audio_b = 2; /* 2bytes * 16bits =32bits */ ++ replay_filler = replay_fill_1x16_s; ++ record_filler = record_fill_1x16_s; ++ break; ++ case TYPE(AFMT_S16_LE, 2): ++ jz_audio_b = 2; ++ replay_filler = replay_fill_2x16_s; ++ record_filler = record_fill_2x16_s; ++ break; ++ default: ++ ; ++ } ++} ++ ++ ++#ifdef CONFIG_PROC_FS ++extern struct proc_dir_entry *proc_jz_root; ++int i2s_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) ++{ ++ return 0; ++} ++ ++static int jz_i2s_init_proc(struct jz_i2s_controller_info *controller) ++{ ++ if (!create_proc_read_entry ("i2s", 0, proc_jz_root, i2s_read_proc, controller->i2s_codec[0])) ++ return -EIO; ++ return 0; ++} ++ ++static void jz_i2s_cleanup_proc(struct jz_i2s_controller_info *controller) ++{ ++} ++#endif ++ ++static void __init attach_jz_i2s(struct jz_i2s_controller_info *controller) ++{ ++ char *name; ++ int adev; /* No of Audio device. */ ++ ++ name = controller->name; ++ /* initialize AIC controller and reset it */ ++ jz_i2s_initHw(1); ++ adev = register_sound_dsp(&jz_i2s_audio_fops, -1); ++ if (adev < 0) ++ goto audio_failed; ++ /* initialize I2S codec and register /dev/mixer */ ++ if (jz_i2s_codec_init(controller) <= 0) ++ goto mixer_failed; ++ ++#ifdef CONFIG_PROC_FS ++ if (jz_i2s_init_proc(controller) < 0) { ++ printk(KERN_ERR "%s: can't create I2S proc filesystem.\n", name); ++ goto proc_failed; ++ } ++#endif ++ ++ controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, 8); ++ if (!controller->tmp1) { ++ printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); ++ goto tmp1_failed; ++ } ++ controller->tmp2 = (void *)__get_free_pages(GFP_KERNEL, 8); ++ if (!controller->tmp2) { ++ printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); ++ goto tmp2_failed; ++ } ++ if ((controller->dma2 = jz_request_dma(DMA_ID_I2S_RX, "audio adc", jz_i2s_record_dma_irq, IRQF_DISABLED, controller)) < 0) { ++ printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name); ++ goto dma2_failed; ++ } ++ if ((controller->dma1 = jz_request_dma(DMA_ID_I2S_TX, "audio dac", jz_i2s_replay_dma_irq, IRQF_DISABLED, controller)) < 0) { ++ printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name); ++ goto dma1_failed; ++ } ++ printk("JzSOC On-Chip I2S controller registered (DAC: DMA(play):%d/IRQ%d,\n ADC: DMA(record):%d/IRQ%d)\n", controller->dma1, get_dma_done_irq(controller->dma1), controller->dma2, get_dma_done_irq(controller->dma2)); ++ ++ controller->dev_audio = adev; ++ pop_turn_onoff_buf = __get_free_pages(GFP_KERNEL | GFP_DMA, 8); ++ if(!pop_turn_onoff_buf) ++ printk("pop_turn_onoff_buf alloc is wrong!\n"); ++ pop_turn_onoff_pbuf = virt_to_phys((void *)pop_turn_onoff_buf); ++ ++ return; ++dma2_failed: ++ jz_free_dma(controller->dma1); ++dma1_failed: ++ free_pages((unsigned long)controller->tmp2, 8); ++tmp2_failed: ++ free_pages((unsigned long)controller->tmp1, 8); ++tmp1_failed: ++ ++#ifdef CONFIG_PROC_FS ++ jz_i2s_cleanup_proc(controller); ++#endif ++proc_failed: ++ /* unregister mixer dev */ ++mixer_failed: ++ unregister_sound_dsp(adev); ++audio_failed: ++ return; ++} ++ ++static int __init probe_jz_i2s(struct jz_i2s_controller_info **controller) ++{ ++ if ((*controller = kmalloc(sizeof(struct jz_i2s_controller_info), ++ GFP_KERNEL)) == NULL) { ++ printk(KERN_ERR "Jz I2S Controller: out of memory.\n"); ++ return -ENOMEM; ++ } ++ (*controller)->name = "Jz I2S controller"; ++ (*controller)->opened1 = 0; ++ (*controller)->opened2 = 0; ++ init_waitqueue_head(&(*controller)->adc_wait); ++ init_waitqueue_head(&(*controller)->dac_wait); ++ spin_lock_init(&(*controller)->lock); ++ init_waitqueue_head(&rx_wait_queue); ++ init_waitqueue_head(&tx_wait_queue); ++ init_waitqueue_head(&pop_wait_queue); ++ init_waitqueue_head(&drain_wait_queue); ++ ++ return 0; ++} ++ ++static void __exit unload_jz_i2s(struct jz_i2s_controller_info *controller) ++{ ++ int adev = controller->dev_audio; ++ ++ jz_i2s_full_reset(controller); ++ controller->dev_audio = -1; ++ if (old_mksound) ++ kd_mksound = old_mksound;/* Our driver support bell for kb, see vt.c */ ++ ++#ifdef CONFIG_PROC_FS ++ jz_i2s_cleanup_proc(controller); ++#endif ++ ++ jz_free_dma(controller->dma1); ++ jz_free_dma(controller->dma2); ++ free_pages((unsigned long)controller->tmp1, 8); ++ free_pages((unsigned long)controller->tmp2, 8); ++ free_pages((unsigned long)pop_turn_onoff_buf, 8); ++ ++ if (adev >= 0) { ++ /* unregister_sound_mixer(audio_devs[adev]->mixer_dev); */ ++ unregister_sound_dsp(controller->dev_audio); ++ } ++} ++ ++#ifdef CONFIG_PM ++static int jz_i2s_suspend(struct jz_i2s_controller_info *controller, int state) ++{ ++ if(i2s_suspend_codec) ++ i2s_suspend_codec(controller->opened1,controller->opened2); ++ printk("Aic and codec are suspended!\n"); ++ return 0; ++} ++ ++static int jz_i2s_resume(struct jz_i2s_controller_info *controller) ++{ ++ if(i2s_resume_codec) ++ i2s_resume_codec(); ++ ++#if defined(CONFIG_I2S_AK4642EN) ++ jz_i2s_initHw(0); ++ jz_audio_reset(); ++ __i2s_enable(); ++ jz_audio_set_speed(controller->dev_audio,jz_audio_speed); ++ /* playing */ ++ if(controller->opened1) { ++ if(set_codec_replay) ++ set_codec_replay(); ++ int dma = controller->dma1; ++ int id; ++ unsigned long flags; ++ disable_dma(dma); ++ if(__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if(__dmac_channel_transmit_end_detected(dma)) ++ __dmac_channel_clear_transmit_end(dma); ++ ++ /* for DSP_GETOPTR */ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ while((id = get_buffer_id(&out_busy_queue)) >= 0) ++ put_buffer_id(&out_empty_queue, id); ++ ++ out_busy_queue.count=0; ++ if((id = get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_empty_queue, id); ++ } ++ if (elements_in_queue(&out_empty_queue) > 0) { ++ wake_up(&tx_wait_queue); ++ wake_up(&controller->dac_wait); ++ } else ++ printk("pm out_empty_queue empty"); ++ } ++ ++ /* recording */ ++ if(controller->opened2) { ++ if(set_codec_record) ++ set_codec_record(); ++ int dma = controller->dma2; ++ int id1, id2; ++ unsigned long flags; ++ disable_dma(dma); ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ } ++ /* for DSP_GETIPTR */ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ id1 = get_buffer_id(&in_busy_queue); ++ put_buffer_id(&in_full_queue, id1); ++ wake_up(&rx_wait_queue); ++ wake_up(&controller->adc_wait); ++ if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_full_queue, id2); ++ } ++ in_busy_queue.count = 0; ++ } ++#endif ++ ++ return 0; ++} ++ ++static int jz_i2s_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) ++{ ++ int ret; ++ struct jz_i2s_controller_info *controller = pm_dev->data; ++ ++ if (!controller) return -EINVAL; ++ ++ switch (req) { ++ case PM_SUSPEND: ++ ret = jz_i2s_suspend(controller, (int)data); ++ break; ++ case PM_RESUME: ++ ret = jz_i2s_resume(controller); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++#endif /* CONFIG_PM */ ++static irqreturn_t aic_codec_irq(int irq, void *dev_id) ++{ ++ u8 file_9 = read_codec_file(9); ++ u8 file_8 = read_codec_file(8); ++ ++ //printk("--- 8:0x%x 9:0x%x ---\n",file_8,file_9); ++ if ((file_9 & 0x1f) == 0x10) { ++ ++ write_codec_file(8, 0x3f); ++ write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ mdelay(300); ++ while ((read_codec_file(9) & 0x4) != 0x4); ++ while ((read_codec_file(9) & 0x10) == 0x10) { ++ write_codec_file(9, 0x10); ++ } ++ write_codec_file_bit(5, 0, 6);//SB_OUT->0 ++ mdelay(300); ++ while ((read_codec_file(9) & 0x8) != 0x8); ++ write_codec_file(9, file_9); ++ write_codec_file(8, file_8); ++ ++ return IRQ_HANDLED; ++ } ++ ++ if (file_9 & 0x8) ++ ramp_up_end = jiffies; ++ else if (file_9 & 0x4) ++ ramp_down_end = jiffies; ++ else if (file_9 & 0x2) ++ gain_up_end = jiffies; ++ else if (file_9 & 0x1) ++ gain_down_end = jiffies; ++ ++ write_codec_file(9, file_9); ++ if (file_9 & 0xf) ++ wake_up(&pop_wait_queue); ++ while (REG_ICDC_RGDATA & 0x100); ++ ++ return IRQ_HANDLED; ++} ++ ++static int __init init_jz_i2s(void) ++{ ++ int errno, retval; ++#if defined(CONFIG_I2S_DLV) ++ ++ ramp_up_start = 0; ++ ramp_up_end = 0; ++ gain_up_start = 0; ++ gain_up_end = 0; ++ ramp_down_start = 0; ++ ramp_down_end = 0; ++ gain_down_start = 0; ++ gain_down_end = 0; ++#endif ++ ++ abnormal_data_count = 0; ++ if(set_codec_mode) ++ set_codec_mode(); ++ ++ drain_flag = 0; ++ if ((errno = probe_jz_i2s(&i2s_controller)) < 0) ++ return errno; ++ if(set_codec_gpio_pin) ++ set_codec_gpio_pin(); ++ ++ attach_jz_i2s(i2s_controller); ++ if(set_codec_startup_param) ++ set_codec_startup_param(); ++#if defined(CONFIG_I2S_DLV) ++ jz_codec_config = 0; ++ retval = request_irq(IRQ_AIC, aic_codec_irq, IRQF_DISABLED, "aic_codec_irq", NULL); ++ if (retval) { ++ printk("Could not get aic codec irq %d\n", IRQ_AIC); ++ return retval; ++ } ++#endif ++ if(set_codec_volume_table) ++ set_codec_volume_table(); ++ ++ out_empty_queue.id = NULL; ++ out_full_queue.id = NULL; ++ out_busy_queue.id = NULL; ++ in_empty_queue.id = NULL; ++ in_full_queue.id = NULL; ++ in_busy_queue.id = NULL; ++ ++ jz_audio_fragsize = JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE; ++ jz_audio_fragstotal = JZCODEC_RW_BUFFER_TOTAL ; ++ Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ ++#ifdef CONFIG_PM ++ i2s_controller->pm = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ++ jz_i2s_pm_callback); ++ if (i2s_controller->pm) ++ i2s_controller->pm->data = i2s_controller; ++#endif ++ ++#if defined(CONFIG_I2S_DLV) ++ __cpm_start_idct(); ++ __cpm_start_db(); ++ __cpm_start_me(); ++ __cpm_start_mc(); ++ __cpm_start_ipu(); ++#endif ++ ++ printk("JZ I2S OSS audio driver initialized\n"); ++ ++ return 0; ++} ++ ++static void __exit cleanup_jz_i2s(void) ++{ ++#ifdef CONFIG_PM ++ /* pm_unregister(i2s_controller->pm); */ ++#endif ++#if defined(CONFIG_I2S_DLV) ++ free_irq(IRQ_AIC, NULL); ++#endif ++ unload_jz_i2s(i2s_controller); ++ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ if(clear_codec_mode) ++ clear_codec_mode(); ++} ++ ++module_init(init_jz_i2s); ++module_exit(cleanup_jz_i2s); ++ ++#if defined(CONFIG_SOC_JZ4730) ++static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ unsigned long flags; ++ int count,con; ++ ++ if(elements_in_queue(&in_busy_queue) > 0) { ++ if (nonblock) ++ return -EBUSY; ++ drain_flag = 1; ++ sleep_on(&drain_wait_queue); ++ drain_flag = 0; ++ } else { ++ add_wait_queue(&ctrl->adc_wait, &wait); ++ for (con = 0; con < 1000; con ++) { ++ udelay(1); ++ set_current_state(TASK_INTERRUPTIBLE); ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma2); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ if (nonblock) { ++ remove_wait_queue(&ctrl->adc_wait, &wait); ++ current->state = TASK_RUNNING; ++ return -EBUSY; ++ } ++ } ++ remove_wait_queue(&ctrl->adc_wait, &wait); ++ current->state = TASK_RUNNING; ++ } ++ return 0; ++} ++ ++static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ unsigned long flags; ++ int count; ++ ++ if(elements_in_queue(&out_full_queue) > 0) { ++ if (nonblock) ++ return -EBUSY; ++ ++ drain_flag = 1; ++ sleep_on(&drain_wait_queue); ++ drain_flag = 0; ++ } else { ++ add_wait_queue(&(ctrl->dac_wait), &wait); ++ for (;;) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ if(elements_in_queue(&out_full_queue) <= 0) { ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if(count <= 0) ++ break; ++ } ++ if (nonblock) { ++ remove_wait_queue(&ctrl->dac_wait, &wait); ++ current->state = TASK_RUNNING; ++ return -EBUSY; ++ } ++ } ++ remove_wait_queue(&ctrl->dac_wait, &wait); ++ current->state = TASK_RUNNING; ++ } ++ ++ return 0; ++} ++#endif ++ ++#if defined(CONFIG_SOC_JZ4750) ++static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ //DECLARE_WAITQUEUE(wait, current); ++ unsigned long flags; ++ int count,i=0; ++ ++ //add_wait_queue(&ctrl->adc_wait, &wait); ++ for (;;) { ++ if (i < MAXDELAY) { ++ udelay(10); ++ i++; ++ } else ++ break; ++ //set_current_state(TASK_INTERRUPTIBLE); ++ spin_lock_irqsave(&ctrl->lock, flags); ++ //spin_lock(&ctrl->lock); ++ count = get_dma_residue(ctrl->dma2); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ //spin_unlock(&ctrl->lock); ++ if (count <= 0) ++ break; ++ ++ /*if (signal_pending(current)) ++ break;*/ ++ if (nonblock) { ++ //remove_wait_queue(&ctrl->adc_wait, &wait); ++ //current->state = TASK_RUNNING; ++ return -EBUSY; ++ } ++ } ++ //remove_wait_queue(&ctrl->adc_wait, &wait); ++ //current->state = TASK_RUNNING; ++ /*if (signal_pending(current)) ++ return -ERESTARTSYS;*/ ++ return 0; ++} ++static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ unsigned long flags; ++ int count,ele,busyele,emptyele,i=0; ++ ++ for (;;) { ++ if(!nonblock) {//blocked ++ if (i < MAXDELAY) { ++ udelay(10); ++ i++; ++ } else ++ break; ++ ++ ele = elements_in_queue(&out_full_queue); ++ if(ele <= 0) { ++ udelay(200); ++ ++ busyele = elements_in_queue(&out_busy_queue); ++ emptyele = elements_in_queue(&out_empty_queue); ++ if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ } ++ } ++ } else {//non-blocked ++ //mdelay(100); ++ ele = elements_in_queue(&out_full_queue); ++ ++ if(ele <= 0) { ++ //mdelay(100); ++ busyele = elements_in_queue(&out_busy_queue); ++ emptyele = elements_in_queue(&out_empty_queue); ++ ++ if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ } ++ } ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++#if defined(CONFIG_SOC_JZ4740) ++#define MAXDELAY 50000 ++static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ int count,ele,i=0; ++ ++ for (;;) { ++ if(!nonblock) {//blocked ++ if ( i < MAXDELAY ) { ++ udelay(10); ++ i++; ++ } else ++ break; ++ ++ ele = elements_in_queue(&out_full_queue); ++ if(ele <= 0) { ++ udelay(10); ++ spin_lock(&ctrl->lock); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock(&ctrl->lock); ++ if (count <= 0) ++ break; ++ } ++ } else {//non-blocked ++ mdelay(100); ++ ele = elements_in_queue(&out_full_queue); ++ ++ if(ele <= 0) { ++ mdelay(100); ++ ++ spin_lock(&ctrl->lock); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock(&ctrl->lock); ++ if (count <= 0) ++ break; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) ++{ ++ int count,i=0; ++ ++ for (;;) { ++ if ( i < MAXDELAY ) ++ { ++ udelay(10); ++ i++; ++ } ++ else ++ break; ++ spin_lock(&ctrl->lock); ++ count = get_dma_residue(ctrl->dma2); ++ spin_unlock(&ctrl->lock); ++ if (count <= 0) ++ break; ++ ++ if (nonblock) { ++ return -EBUSY; ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++static int jz_audio_release(struct inode *inode, struct file *file) ++{ ++ unsigned long flags; ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ unsigned long tfl; ++ ++ if (controller == NULL) ++ return -ENODEV; ++ ++ pop_dma_flag = 0; ++ if (controller->opened1 == 1) { ++ controller->opened1 = 0; ++ __i2s_enable_transmit_dma(); ++ __i2s_enable_replay(); ++ drain_dac(controller, file->f_flags & O_NONBLOCK); ++ /* add some mute to anti-pop */ ++#if defined(CONFIG_I2S_DLV) ++ /* wait for fifo empty */ ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ gain_down_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //gain_down_end = jiffies; ++ while (1) { ++ tfl = REG_AIC_SR & 0x00003f00; ++ if (tfl == 0) { ++ udelay(500); ++ break; ++ } ++ mdelay(2); ++ } ++#endif ++ disable_dma(controller->dma1); ++ set_dma_count(controller->dma1, 0); ++ __i2s_disable_transmit_dma(); ++ __i2s_disable_replay(); ++ __aic_flush_fifo(); ++ if(clear_codec_replay) ++ clear_codec_replay(); ++ __aic_flush_fifo(); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ ramp_down_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //ramp_down_end = jiffies; ++ unset_audio_data_replay(); ++#endif ++ __i2s_disable(); ++ if(turn_off_codec) ++ turn_off_codec(); ++ } ++ ++ if (controller->opened2 == 1) { ++ controller->opened2 = 0; ++ first_record_call = 1; ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ drain_adc(controller, file->f_flags & O_NONBLOCK); ++ disable_dma(controller->dma2); ++ set_dma_count(controller->dma2, 0); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_record(); ++ ++ if(clear_codec_record) ++ clear_codec_record(); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ __i2s_disable(); ++ if(turn_off_codec) ++ turn_off_codec(); ++ abnormal_data_count = 0; ++ } ++ ++#if defined(CONFIG_I2S_DLV) ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++#endif ++ return 0; ++} ++ ++static int jz_audio_open(struct inode *inode, struct file *file) ++{ ++ int i; ++ struct jz_i2s_controller_info *controller = i2s_controller; ++ ++ if (controller == NULL) ++ return -ENODEV; ++ ++ mdelay(2); ++ REG_DMAC_DMACKE(0) = 0x3f; ++ pop_dma_flag = 0; ++ if (controller->opened1 == 1 || controller->opened2 == 1 ) { ++ printk("\naudio is busy!\n"); ++ return -EBUSY; ++ } ++ jz_codec_config = 0; ++ ++ ramp_up_start = 0; ++ ramp_up_end = 0; ++ gain_up_start = 0; ++ gain_up_end = 0; ++ ramp_down_start = 0; ++ ramp_down_end = 0; ++ gain_down_start = 0; ++ gain_down_end = 0; ++ if (file->f_mode & FMODE_WRITE) { ++ if (controller->opened1 == 1) ++ return -EBUSY; ++ ++ controller->opened1 = 1; ++ /* for ioctl */ ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ ++ for(i=0;i < 64;i++) { ++ save_last_samples[i].left = 0; ++ save_last_samples[i].right = 0; ++ } ++ ++ out_empty_queue.count = jz_audio_fragstotal; ++ for (i=0;i < jz_audio_fragstotal;i++) ++ *(out_empty_queue.id + i) = i; ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ last_dma_buffer_id = 0; ++ } ++ ++ if (file->f_mode & FMODE_READ) { ++ if (controller->opened2 == 1) ++ return -EBUSY; ++ ++ controller->opened2 = 1; ++ first_record_call = 1; ++ /* for ioctl */ ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ ++ in_empty_queue.count = jz_audio_fragstotal; ++ for (i=0;i < jz_audio_fragstotal;i++) ++ *(in_empty_queue.id + i) = i; ++ ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ } ++ ++ file->private_data = controller; ++ jz_audio_reset(); ++ REG_AIC_FR |= (1 << 6); ++ ++ if (file->f_mode & FMODE_WRITE) { ++ if(set_codec_replay) ++ set_codec_replay(); ++ } ++ ++ if (file->f_mode & FMODE_READ) { ++ abnormal_data_count = 0; ++ if(set_codec_record) ++ set_codec_record(); ++ } ++ ++#if defined(CONFIG_I2S_DLV) ++ __aic_reset(); ++ ++ mdelay(10); ++ REG_AIC_I2SCR = 0x10; ++ mdelay(20); ++ __aic_flush_fifo(); ++#endif ++ ++ __i2s_enable(); ++ ++#if defined(CONFIG_I2S_DLV) ++ if (file->f_mode & FMODE_WRITE) { ++ ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ ramp_up_start = jiffies; ++ /*while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_RCR = 0x1; ++ while (!(REG_RTC_RCR & RTC_RCR_WRDY)); ++ REG_RTC_RGR = 1;*/ ++ sleep_on(&pop_wait_queue); ++ //ramp_up_end = jiffies; ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ } else if (file->f_mode & FMODE_READ) { ++ if (jz_mic_only) ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ else ++ write_codec_file_bit(5, 0, 7);//SB_DAC->0 ++ mdelay(500); ++ } ++ ++#endif ++ ++ return 0; ++} ++ ++ ++static int jz_audio_ioctl(struct inode *inode, struct file *file, ++unsigned int cmd, unsigned long arg) ++{ ++ int val,fullc,busyc,unfinish,newfragstotal,newfragsize; ++ unsigned int flags; ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ count_info cinfo; ++ audio_buf_info abinfo; ++ int id, i; ++ ++ val = 0; ++ switch (cmd) { ++ case OSS_GETVERSION: ++ return put_user(SOUND_VERSION, (int *)arg); ++ case SNDCTL_DSP_RESET: ++#if 0 ++ jz_audio_reset(); ++ __i2s_disable_replay(); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_record(); ++ __i2s_disable_transmit_dma(); ++#endif ++ return 0; ++ case SNDCTL_DSP_SYNC: ++ if (file->f_mode & FMODE_WRITE) ++ return drain_dac(controller, file->f_flags & O_NONBLOCK); ++ return 0; ++ case SNDCTL_DSP_SPEED: ++ /* set smaple rate */ ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ if (val >= 0) ++ jz_audio_set_speed(controller->dev_audio, val); ++ ++ return put_user(val, (int *)arg); ++ case SNDCTL_DSP_STEREO: ++ /* set stereo or mono channel */ ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ jz_audio_set_channels(controller->dev_audio, val ? 2 : 1); ++ ++ return 0; ++ case SNDCTL_DSP_GETBLKSIZE: ++ //return put_user(jz_audio_fragsize / jz_audio_b, (int *)arg); ++ return put_user(jz_audio_fragsize, (int *)arg); ++ case SNDCTL_DSP_GETFMTS: ++ /* Returns a mask of supported sample format*/ ++ return put_user(AFMT_U8 | AFMT_S16_LE, (int *)arg); ++ case SNDCTL_DSP_SETFMT: ++ /* Select sample format */ ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ if (val != AFMT_QUERY) ++ jz_audio_set_format(controller->dev_audio,val); ++ else { ++ if (file->f_mode & FMODE_READ) ++ val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; ++ else ++ val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; ++ } ++ ++ return put_user(val, (int *)arg); ++ case SNDCTL_DSP_CHANNELS: ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ jz_audio_set_channels(controller->dev_audio, val); ++ ++ return put_user(val, (int *)arg); ++ case SNDCTL_DSP_POST: ++ /* FIXME: the same as RESET ?? */ ++ return 0; ++ case SNDCTL_DSP_SUBDIVIDE: ++ return 0; ++ case SNDCTL_DSP_SETFRAGMENT: ++ get_user(val, (long *) arg); ++ newfragsize = 1 << (val & 0xFFFF); ++ if (newfragsize < 4 * PAGE_SIZE) ++ newfragsize = 4 * PAGE_SIZE; ++ if (newfragsize > (16 * PAGE_SIZE)) ++ newfragsize = 16 * PAGE_SIZE; ++ ++ newfragstotal = (val >> 16) & 0x7FFF; ++ if (newfragstotal < 2) ++ newfragstotal = 2; ++ if (newfragstotal > 32) ++ newfragstotal = 32; ++ if((jz_audio_fragstotal == newfragstotal) && (jz_audio_fragsize == newfragsize)) ++ return 0; ++ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ mdelay(500); ++ jz_audio_fragstotal = newfragstotal; ++ jz_audio_fragsize = newfragsize; ++ ++ Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ mdelay(10); ++ ++ return 0; ++ case SNDCTL_DSP_GETCAPS: ++ return put_user(DSP_CAP_REALTIME|DSP_CAP_BATCH, (int *)arg); ++ case SNDCTL_DSP_NONBLOCK: ++ file->f_flags |= O_NONBLOCK; ++ return 0; ++ case SNDCTL_DSP_SETDUPLEX: ++ return -EINVAL; ++ case SNDCTL_DSP_GETOSPACE: ++ { ++ int i, bytes = 0; ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ jz_audio_fragments = elements_in_queue(&out_empty_queue); ++ for (i = 0; i < jz_audio_fragments; i++) ++ bytes += jz_audio_fragsize; ++ ++ if (jz_audio_channels == 2) ++ bytes /= jz_audio_b; ++ else if (jz_audio_channels == 1) ++ bytes /= 4; ++ else ++ printk("SNDCTL_DSP_GETOSPACE : channels is wrong 1!\n"); ++ ++ ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ /* unused fragment amount */ ++ abinfo.fragments = jz_audio_fragments; ++ /* amount of fragments */ ++ abinfo.fragstotal = jz_audio_fragstotal; ++ /* fragment size in bytes */ ++ if (jz_audio_channels == 2) ++ abinfo.fragsize = jz_audio_fragsize / jz_audio_b; ++ else if (jz_audio_channels == 1) ++ abinfo.fragsize = jz_audio_fragsize / 4; ++ else ++ printk("SNDCTL_DSP_GETOSPACE : channels is wrong 2!\n"); ++ ++ /* write size count without blocking in bytes */ ++ abinfo.bytes = bytes; ++ ++ return copy_to_user((void *)arg, &abinfo, ++ sizeof(abinfo)) ? -EFAULT : 0; ++ } ++ case SNDCTL_DSP_GETISPACE: ++ { ++ int i, bytes = 0; ++ if (!(file->f_mode & FMODE_READ)) ++ return -EINVAL; ++ jz_audio_fragments = elements_in_queue(&in_empty_queue); ++ for (i = 0; i < jz_audio_fragments; i++) ++ bytes += jz_audio_fragsize; ++ ++ if (jz_audio_channels == 2) ++ bytes /= jz_audio_b; ++ else if (jz_audio_channels == 1) ++ bytes /= 4; ++ else ++ printk("SNDCTL_DSP_GETISPACE : channels is wrong 1!\n"); ++ ++ abinfo.fragments = jz_audio_fragments; ++ abinfo.fragstotal = jz_audio_fragstotal; ++ ++ if (jz_audio_channels == 2) ++ abinfo.fragsize = jz_audio_fragsize / jz_audio_b; ++ else if (jz_audio_channels == 1) ++ abinfo.fragsize = jz_audio_fragsize / 4; ++ else ++ printk("SNDCTL_DSP_GETISPACE : channels is wrong 2!\n"); ++ ++ abinfo.bytes = bytes; ++ ++ return copy_to_user((void *)arg, &abinfo, ++ sizeof(abinfo)) ? -EFAULT : 0; ++ } ++ case SNDCTL_DSP_GETTRIGGER: ++ val = 0; ++ if (file->f_mode & FMODE_READ && in_dma_buf) ++ val |= PCM_ENABLE_INPUT; ++ if (file->f_mode & FMODE_WRITE && out_dma_buf) ++ val |= PCM_ENABLE_OUTPUT; ++ ++ return put_user(val, (int *)arg); ++ case SNDCTL_DSP_SETTRIGGER: ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ return 0; ++ case SNDCTL_DSP_GETIPTR: ++ if (!(file->f_mode & FMODE_READ)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ cinfo.bytes = controller->total_bytes; ++ cinfo.blocks = controller->blocks; ++ cinfo.ptr = controller->nextIn; ++ controller->blocks = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); ++ case SNDCTL_DSP_GETOPTR: ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ cinfo.bytes = controller->total_bytes; ++ cinfo.blocks = controller->blocks; ++ cinfo.ptr = controller->nextOut; ++ controller->blocks = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); ++ case SNDCTL_DSP_GETODELAY: ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ unfinish = 0; ++ fullc = elements_in_queue(&out_full_queue); ++ busyc = elements_in_queue(&out_busy_queue); ++ for(i = 0;i < fullc ;i ++) { ++ id = *(out_full_queue.id + i); ++ unfinish += *(out_dma_buf_data_count + id); ++ } ++ for(i = 0;i < busyc ;i ++) { ++ id = *(out_busy_queue.id + i); ++ unfinish += get_dma_residue(controller->dma1); ++ } ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ if (jz_audio_channels == 2) ++ unfinish /= jz_audio_b; ++ else if (jz_audio_channels == 1) ++ unfinish /= 4; ++ else ++ printk("SNDCTL_DSP_GETODELAY : channels is wrong !\n"); ++ ++ return put_user(unfinish, (int *) arg); ++ case SOUND_PCM_READ_RATE: ++ return put_user(jz_audio_rate, (int *)arg); ++ case SOUND_PCM_READ_CHANNELS: ++ return put_user(jz_audio_channels, (int *)arg); ++ case SOUND_PCM_READ_BITS: ++ return put_user((jz_audio_format & (AFMT_S8 | AFMT_U8)) ? 8 : 16, (int *)arg); ++ case SNDCTL_DSP_MAPINBUF: ++ case SNDCTL_DSP_MAPOUTBUF: ++ case SNDCTL_DSP_SETSYNCRO: ++ case SOUND_PCM_WRITE_FILTER: ++ case SOUND_PCM_READ_FILTER: ++ return -EINVAL; ++ } ++ return -EINVAL; ++} ++ ++ ++static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait) ++{ ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ unsigned long flags; ++ unsigned int mask = 0; ++ ++ if (file->f_mode & FMODE_WRITE) { ++ if (elements_in_queue(&out_empty_queue) > 0) ++ return POLLOUT | POLLWRNORM; ++ ++ poll_wait(file, &controller->dac_wait, wait); ++ } ++ ++ if (file->f_mode & FMODE_READ) { ++ if (elements_in_queue(&in_full_queue) > 0) ++ return POLLIN | POLLRDNORM; ++ ++ poll_wait(file, &controller->adc_wait, wait); ++ } ++ ++ spin_lock_irqsave(&controller->lock, flags); ++ if (file->f_mode & FMODE_WRITE) { ++ if (elements_in_queue(&out_empty_queue) > 0) ++ mask |= POLLOUT | POLLWRNORM; ++ } else if (file->f_mode & FMODE_READ) { ++ if (elements_in_queue(&in_full_queue) > 0) ++ mask |= POLLIN | POLLRDNORM; ++ } ++ spin_unlock_irqrestore(&controller->lock, flags); ++ ++ return mask; ++} ++ ++static ssize_t jz_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos) ++{ ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ int id, ret = 0, left_count, copy_count, cnt = 0; ++ unsigned long flags; ++ ++ if (count < 0) ++ return -EINVAL; ++ ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextIn = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ copy_count = jz_audio_fragsize / 4; ++ ++ left_count = count; ++ if (first_record_call) { ++ first_record_call = 0; ++ audio_read_back_first: ++ if ((id = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id); ++ spin_lock(&controller->lock); ++ *(in_dma_buf_data_count + id) = copy_count * 4; ++ ++ spin_unlock(&controller->lock); ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); ++ audio_start_dma(controller->dma2,file->private_data, ++ *(in_dma_pbuf + id), ++ *(in_dma_buf_data_count + id), ++ DMA_MODE_READ); ++ sleep_on(&rx_wait_queue); ++ } else ++ goto audio_read_back_first; ++ } ++ ++ while (left_count > 0) { ++ audio_read_back_second: ++ if (elements_in_queue(&in_full_queue) <= 0) { ++ if (file->f_flags & O_NONBLOCK) ++ return ret ? ret : -EAGAIN; ++ else ++ sleep_on(&rx_wait_queue); ++ } ++ ++ if ((id = get_buffer_id(&in_full_queue)) >= 0) { ++ spin_lock(&controller->lock); ++ cnt = record_filler((unsigned long)controller->tmp2+ret, copy_count, id); ++ spin_unlock(&controller->lock); ++ put_buffer_id(&in_empty_queue, id); ++ } else ++ goto audio_read_back_second; ++ ++ if (elements_in_queue(&in_busy_queue) == 0) { ++ if ((id=get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id); ++ spin_lock(&controller->lock); ++ *(in_dma_buf_data_count + id) = copy_count * 4; ++ spin_unlock(&controller->lock); ++ ++ dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); ++ audio_start_dma(controller->dma2,file->private_data, ++ *(in_dma_pbuf + id), ++ *(in_dma_buf_data_count + id), ++ DMA_MODE_READ); ++ } ++ } ++ if (ret + cnt > count) { ++ spin_lock(&controller->lock); ++ cnt = count - ret; ++ spin_unlock(&controller->lock); ++ } ++ if (copy_to_user(buffer+ret, controller->tmp2+ret, cnt)) ++ return ret ? ret : -EFAULT; ++ ++ spin_lock(&controller->lock); ++ ret += cnt; ++ spin_unlock(&controller->lock); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextIn += ret; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ spin_lock(&controller->lock); ++ left_count -= cnt; ++ spin_unlock(&controller->lock); ++ } ++ return ret; ++} ++ ++static ssize_t jz_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) ++{ ++ int id, ret = 0, left_count, copy_count = 0; ++ unsigned int flags; ++ struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; ++ ++ if (count <= 0) ++ return -EINVAL; ++ ++ if(set_replay_hp_or_speaker) ++ set_replay_hp_or_speaker(); ++ ++ __i2s_enable_transmit_dma(); ++ __i2s_enable_replay(); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextOut = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ if (jz_audio_channels == 2) ++ copy_count = jz_audio_fragsize / jz_audio_b; ++ else if(jz_audio_channels == 1) ++ copy_count = jz_audio_fragsize / 4; ++ left_count = count; ++ if (copy_from_user(controller->tmp1, buffer, count)) { ++ printk("copy_from_user failed:%d",ret); ++ return ret ? ret : -EFAULT; ++ } ++ ++ while (left_count > 0) { ++ audio_write_back: ++ /*if (file->f_flags & O_NONBLOCK) ++ udelay(2);*/ ++ if (elements_in_queue(&out_empty_queue) == 0) { ++ if (file->f_flags & O_NONBLOCK) ++ return ret; ++ else ++ sleep_on(&tx_wait_queue); ++ } ++ /* the end fragment size in this write */ ++ if (ret + copy_count > count) ++ copy_count = count - ret; ++ if ((id = get_buffer_id(&out_empty_queue)) >= 0) { ++ replay_filler((signed long)controller->tmp1 + ret, copy_count, id); ++ if(*(out_dma_buf_data_count + id) > 0) { ++ put_buffer_id(&out_full_queue, id); ++ dma_cache_wback_inv(*(out_dma_buf + id), ++ *(out_dma_buf_data_count + id)); ++ } else ++ put_buffer_id(&out_empty_queue, id); ++ } else ++ goto audio_write_back; ++ ++ left_count = left_count - copy_count; ++ ret += copy_count; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextOut += ret; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++ if (elements_in_queue(&out_busy_queue) == 0) { ++ if ((id=get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_busy_queue, id); ++ ++ if(*(out_dma_buf_data_count + id) > 0) { ++ audio_start_dma(controller->dma1, ++ file->private_data, ++ *(out_dma_pbuf + id), ++ *(out_dma_buf_data_count + id), ++ DMA_MODE_WRITE); ++ last_dma_buffer_id = id; ++ if (jz_codec_config == 0) { ++ write_codec_file_bit(1, 0, 5); ++ gain_up_start = jiffies; ++ sleep_on(&pop_wait_queue); ++ //gain_up_end = jiffies; ++ jz_codec_config = 1; ++ //SB_ADC->1 ++ //write_codec_file_bit(5, 1, 4); ++ //while(1); ++ } ++ } ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++#if defined(CONFIG_I2S_ICODEC) ++static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample) ++{ ++ int i,step_len; ++ unsigned long *pop_buf = (unsigned long*)pop_turn_onoff_buf; ++ unsigned int sample_oss = (REG_AIC_CR & 0x00380000) >> 19; ++ unsigned long l_sample_count,r_sample_count,sample_count; ++ struct jz_i2s_controller_info *controller = i2s_controller; ++ signed int left_sam=0,right_sam=0,l_val,r_val; ++ ++ switch (sample_oss) { ++ case 0x0: ++ break; ++ case 0x1: ++ left_sam = (signed int)l_sample; ++ right_sam = (signed int)r_sample; ++ break; ++ case 0x2: ++ break; ++ case 0x3: ++ break; ++ case 0x4: ++ break; ++ } ++ ++ if(left_sam == 0 && right_sam == 0) ++ return; ++ ++ switch (sample_oss) { ++ case 0x0: ++ break; ++ case 0x1: ++ step_len = jz_audio_speed / 10 * 3; ++ step_len = step_len / 2; ++ step_len = 0x7fff / step_len + 1; ++ ++ l_sample_count = 0; ++ l_val = left_sam; ++ ++ while(1) { ++ if(l_val > 0) { ++ if(l_val >= step_len) { ++ l_val -= step_len; ++ l_sample_count ++; ++ } else ++ break; ++ } ++ ++ if(l_val < 0) { ++ if(l_val <= -step_len) { ++ l_val += step_len; ++ l_sample_count ++; ++ } else ++ break; ++ } ++ ++ if(l_val == 0) ++ break; ++ } ++ ++ r_sample_count = 0; ++ r_val = right_sam; ++ while(1) { ++ if(r_val > 0) { ++ if(r_val >= step_len) { ++ r_val -= step_len; ++ r_sample_count ++; ++ } else ++ break; ++ } ++ ++ if(r_val < 0) { ++ if(r_val <= -step_len) { ++ r_val += step_len; ++ r_sample_count ++; ++ } else ++ break; ++ } ++ ++ if(r_val == 0) ++ break; ++ } ++ /* fill up */ ++ if(l_sample_count > r_sample_count) ++ sample_count = l_sample_count; ++ else ++ sample_count = r_sample_count; ++ ++ l_val = left_sam; ++ r_val = right_sam; ++ for(i=0;i <= sample_count;i++) { ++ ++ *pop_buf = (unsigned long)l_val; ++ pop_buf ++; ++ ++ if(l_val > step_len) ++ l_val -= step_len; ++ else if(l_val < -step_len) ++ l_val += step_len; ++ else if(l_val >= -step_len && l_val <= step_len) ++ l_val = 0; ++ ++ *pop_buf = (unsigned long)r_val; ++ pop_buf ++; ++ if(r_val > step_len) ++ r_val -= step_len; ++ else if(r_val < -step_len) ++ r_val += step_len; ++ else if(r_val >= -step_len && r_val <= step_len) ++ r_val = 0; ++ } ++ ++ *pop_buf = 0; ++ pop_buf ++; ++ *pop_buf = 0; ++ ++ pop_buf ++; ++ sample_count += 2; ++ dma_cache_wback_inv(pop_turn_onoff_buf, sample_count*8); ++ ++ pop_dma_flag = 1; ++ audio_start_dma(controller->dma1,controller,pop_turn_onoff_pbuf,sample_count*8,DMA_MODE_WRITE); ++ sleep_on(&pop_wait_queue); ++ pop_dma_flag = 0; ++ break; ++ case 0x2: ++ break; ++ case 0x3: ++ break; ++ case 0x4: ++ break; ++ } ++} ++#endif +diff -urN linux-2.6.24.7.old/sound/oss/jz_pcm_tlv320aic1106_dma.c linux-2.6.24.7/sound/oss/jz_pcm_tlv320aic1106_dma.c +--- linux-2.6.24.7.old/sound/oss/jz_pcm_tlv320aic1106_dma.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/oss/jz_pcm_tlv320aic1106_dma.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,1839 @@ ++/* ++ * linux/drivers/sound/jz_pcm_tlv320aic1106.c ++ * ++ * JzSOC On-Chip PCM audio driver. ++ * ++ * Copyright (C) 2005 by Ingenic Corp. ++ * ++ * 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. ++ * ++ * Because the normal application of AUDIO devices are focused on Little_endian, ++ * then we only perform the little endian data format in driver. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "sound_config.h" ++ ++#define NR_PCM 2 ++#define MAXDELAY 50000 ++#define JZCODEC_RW_BUFFER_SIZE 4 ++#define JZCODEC_RW_BUFFER_TOTAL 3 ++#define JZCODEC_USER_BUFFER 6 ++ ++#define MODE_is_8 0 // 1 is 8 bits, and 0 is 16 bits ++ ++static int jz_audio_rate; ++static char jz_audio_format; ++static char jz_audio_volume; ++static char jz_audio_channels; ++static int jz_audio_fragments;//unused fragment amount ++static int jz_audio_fragstotal; ++static int jz_audio_fragsize; ++static int jz_audio_speed; ++static int jz_audio_dma_tran_count;//bytes count of one DMA transfer ++ ++static void jz_update_filler(int bits, int channels); ++static int Init_In_Out_queue(int fragstotal,int fragsize); ++static int Free_In_Out_queue(int fragstotal,int fragsize); ++static irqreturn_t jz_pcm_irq(int irqnr, void *ref); ++static irqreturn_t jz_pcm_replay_dma_irq(int irqnr, void *ref); ++static irqreturn_t jz_pcm_record_dma_irq(int irqnr, void *ref); ++static void (*replay_filler)(unsigned long src_start, int count, int id); ++static int (*record_filler)(unsigned long dst_start, int count, int id); ++static void dump_pcmc_reg(void); ++ ++static struct file_operations jz_pcm_audio_fops; ++static DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue); ++static DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue); ++ ++struct jz_pcm_controller_info { ++ int io_base; ++ int dma1; /* play */ ++ int dma2; /* record */ ++ char *name; ++ int dev_audio; ++ struct pcm_codec *pcm_codec[NR_PCM]; ++ int opened1; ++ int opened2; ++ unsigned char *tmp1; /* tmp buffer for sample conversions */ ++ unsigned char *tmp2; ++ spinlock_t lock; ++ spinlock_t ioctllock; ++ ++ wait_queue_head_t dac_wait; ++ wait_queue_head_t adc_wait; ++ int nextIn; // byte index to next-in to DMA buffer ++ int nextOut; // byte index to next-out from DMA buffer ++ int count; // current byte count in DMA buffer ++ int finish; // current transfered byte count in DMA buffer ++ unsigned long total_bytes; // total bytes written or read ++ unsigned long blocks; ++ unsigned long error; // over/underrun ++}; ++static struct jz_pcm_controller_info *pcm_controller = NULL; ++ ++struct pcm_codec { ++ /* PCM controller connected with */ ++ void *private_data; ++ char *name; ++ int id; ++ int dev_mixer; ++ /* controller specific lower leverl pcm accessing routines */ ++ u16 (*codec_read) (u8 reg);//the function accessing Codec REGs ++ void (*codec_write) (u8 reg, u16 val); ++ /* Wait for codec-ready. Ok to sleep here. */ ++ void (*codec_wait) (struct pcm_codec *codec); ++ /* OSS mixer masks */ ++ int modcnt; ++ int supported_mixers; ++ int stereo_mixers; ++ int record_sources; ++ int bit_resolution; ++ /* OSS mixer interface */ ++ int (*read_mixer) (struct pcm_codec *codec, int oss_channel); ++ void (*write_mixer)(struct pcm_codec *codec, int oss_channel, ++ unsigned int left, unsigned int right); ++ int (*recmask_io) (struct pcm_codec *codec, int rw, int mask); ++ int (*mixer_ioctl)(struct pcm_codec *codec, unsigned int cmd, unsigned long arg); ++ /* saved OSS mixer states */ ++ unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; ++}; ++ ++typedef struct buffer_queue_s { ++ int count; ++ int *id; ++ int lock; ++} buffer_queue_t; ++ ++static unsigned long *out_dma_buf = NULL; ++static unsigned long *out_dma_pbuf = NULL; ++static unsigned long *out_dma_buf_data_count = NULL; ++static unsigned long *in_dma_buf = NULL; ++static unsigned long *in_dma_pbuf = NULL; ++static unsigned long *in_dma_buf_data_count = NULL; ++ ++static buffer_queue_t out_empty_queue; ++static buffer_queue_t out_full_queue; ++static buffer_queue_t out_busy_queue; ++static buffer_queue_t in_empty_queue; ++static buffer_queue_t in_full_queue; ++static buffer_queue_t in_busy_queue; ++static int first_record_call = 0; ++ ++static inline int get_buffer_id(struct buffer_queue_s *q) ++{ ++ int r, i; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&q->lock, flags); ++ if (q->count == 0) { ++ spin_unlock_irqrestore(&q->lock, flags); ++ return -1; ++ } ++ r = *(q->id + 0); ++ for (i=0;i < q->count-1;i++) ++ *(q->id + i) = *(q->id + (i+1)); ++ q->count --; ++ spin_unlock_irqrestore(&q->lock, flags); ++ ++ return r; ++} ++ ++static inline void put_buffer_id(struct buffer_queue_s *q, int id) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&q->lock, flags); ++ *(q->id + q->count) = id; ++ q->count ++; ++ spin_unlock_irqrestore(&q->lock, flags); ++} ++ ++static inline int elements_in_queue(struct buffer_queue_s *q) ++{ ++ int r; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&q->lock, flags); ++ r = q->count; ++ spin_unlock_irqrestore(&q->lock, flags); ++ ++ return r; ++} ++ ++static inline void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode) ++{ ++ unsigned long flags; ++ struct jz_pcm_controller_info * controller = (struct jz_pcm_controller_info *) dev_id; ++ ++ //for DSP_GETOPTR ++ //spin_lock_irqsave(&controller->ioctllock, flags); ++ spin_lock(&controller->ioctllock); ++ jz_audio_dma_tran_count = count; ++ //spin_unlock_irqrestore(&controller->ioctllock, flags); ++ spin_unlock(&controller->ioctllock); ++ flags = claim_dma_lock(); ++ __dmac_disable_module(0);//!!!!!!!!! ++ disable_dma(chan); ++ clear_dma_ff(chan); ++ set_dma_mode(chan, mode); ++#if MODE_is_8 ++ __dmac_channel_set_src_port_width(chan, 8); ++ __dmac_channel_set_dest_port_width(chan, 8); ++ __dmac_channel_set_transfer_unit_8bit(chan); ++#else ++ __dmac_channel_set_src_port_width(chan, 16); ++ __dmac_channel_set_dest_port_width(chan, 16); ++ __dmac_channel_set_transfer_unit_16bit(chan); ++#endif ++ ++ set_dma_addr(chan, phyaddr); ++ if (count == 0) { ++ count++; ++ printk("JzSOC DMA controller can't set dma 0 count!\n"); ++ } ++ ++ set_dma_count(chan, count); ++ enable_dma(chan); ++ __dmac_enable_module(0); ++ release_dma_lock(flags); ++#if 0 ++ //for DSP_GETOPTR on FPGA ++ struct jz_dma_chan *tmpchan = &jz_dma_table[1]; ++ ++ spin_lock(&controller->ioctllock); ++ jz_audio_dma_tran_count = count; ++ spin_unlock(&controller->ioctllock); ++ flags = claim_dma_lock(); ++ disable_dma(chan); ++ clear_dma_ff(chan); ++ set_dma_mode(chan, mode); ++#if MODE_is_8 ++ __dmac_channel_set_src_port_width(chan, 8); ++ __dmac_channel_set_dest_port_width(chan, 8); ++ __dmac_channel_set_transfer_unit_8bit(chan); ++#else ++ __dmac_channel_set_src_port_width(chan, 16); ++ __dmac_channel_set_dest_port_width(chan, 16); ++ __dmac_channel_set_transfer_unit_16bit(chan); ++#endif ++ set_dma_addr(chan, phyaddr); ++ if (count == 0) { ++ count++; ++ printk("JzSOC DMA controller can't set dma 0 count!\n"); ++ } ++ set_dma_count(chan, count); ++ enable_dma(chan); ++ release_dma_lock(flags); ++#if 0 ++ __pcm_enable_tfs_intr(); ++ __pcm_enable_tur_intr(); ++ __pcm_enable_rfs_intr(); ++ __pcm_enable_ror_intr(); ++#endif ++#endif ++} ++ ++static irqreturn_t jz_pcm_record_dma_irq(int irq, void *dev_id) ++{ ++ int id1, id2; ++ struct jz_pcm_controller_info * controller = (struct jz_pcm_controller_info *) dev_id; ++ int dma = controller->dma2; ++ ++ disable_dma(dma); ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ //for DSP_GETIPTR ++ spin_lock(&controller->ioctllock); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock(&controller->ioctllock); ++ id1 = get_buffer_id(&in_busy_queue); ++ put_buffer_id(&in_full_queue, id1); ++ ++ wake_up(&rx_wait_queue); ++ wake_up(&controller->adc_wait); ++ if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id2); ++ *(in_dma_buf_data_count + id2) = *(in_dma_buf_data_count + id1); ++ ++ dma_cache_wback_inv(*(in_dma_buf + id2), *(in_dma_buf_data_count + id2)); ++ audio_start_dma(dma,dev_id, ++ *(in_dma_pbuf + id2), ++ *(in_dma_buf_data_count + id2), ++ DMA_MODE_READ); ++ } else ++ in_busy_queue.count = 0; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t jz_pcm_irq(int irq, void *dev_id) ++{ ++ struct jz_pcm_controller_info * controller = (struct jz_pcm_controller_info *) dev_id; ++ printk("pcm interrupt REG_PCM_INTS : 0x%08x\n",REG_PCM_INTS); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t jz_pcm_replay_dma_irq(int irq, void *dev_id) ++{ ++ int id; ++ unsigned long flags; ++ struct jz_pcm_controller_info * controller = (struct jz_pcm_controller_info *) dev_id; ++ int dma = controller->dma1; ++ ++ disable_dma(dma); ++ if (__dmac_channel_address_error_detected(dma)) { ++ printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); ++ __dmac_channel_clear_address_error(dma); ++ } ++ if (__dmac_channel_transmit_end_detected(dma)) { ++ __dmac_channel_clear_transmit_end(dma); ++ //for DSP_GETOPTR ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->total_bytes += jz_audio_dma_tran_count; ++ controller->blocks ++; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ if ((id = get_buffer_id(&out_busy_queue)) < 0) ++ printk(KERN_DEBUG "Strange DMA finish interrupt for PCM module\n"); ++ put_buffer_id(&out_empty_queue, id); ++ if ((id = get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_busy_queue, id); //very busy ++ if(*(out_dma_buf_data_count + id) > 0) { ++ audio_start_dma(dma, dev_id, *(out_dma_pbuf + id), ++ *(out_dma_buf_data_count + id), ++ DMA_MODE_WRITE); ++ } ++ } else ++ out_busy_queue.count = 0; ++ ++ if (elements_in_queue(&out_empty_queue) > 0) { ++ wake_up(&tx_wait_queue); ++ wake_up(&controller->dac_wait); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int Init_In_Out_queue(int fragstotal,int fragsize) ++{ ++ int i; ++ // recording ++ in_empty_queue.count = fragstotal; ++ in_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_buf) ++ goto all_mem_err; ++ in_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_pbuf) ++ goto all_mem_err; ++ in_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!in_dma_buf_data_count) ++ goto all_mem_err; ++ in_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_empty_queue.id) ++ goto all_mem_err; ++ in_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_full_queue.id) ++ goto all_mem_err; ++ in_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!in_busy_queue.id) ++ goto all_mem_err; ++ ++ for (i=0;i < fragstotal;i++) ++ *(in_empty_queue.id + i) = i; ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ ++ for (i = 0; i < fragstotal; i++) { ++ *(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); ++ if (*(in_dma_buf + i) == 0) ++ goto mem_failed_in; ++ *(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i))); ++ dma_cache_wback_inv(*(in_dma_buf + i), fragsize); ++ } ++ ++ //playing ++ out_empty_queue.count = fragstotal; ++ out_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!out_dma_buf) ++ goto all_mem_err; ++ out_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ if (!out_dma_pbuf) ++ goto all_mem_err; ++ out_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); ++ ++ if (!out_dma_buf_data_count) ++ goto all_mem_err; ++ out_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_empty_queue.id) ++ goto all_mem_err; ++ out_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_full_queue.id) ++ goto all_mem_err; ++ out_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); ++ if (!out_busy_queue.id) ++ goto all_mem_err; ++ for (i=0;i < fragstotal;i++) ++ *(out_empty_queue.id + i) = i; ++ ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ /*alloc DMA buffer*/ ++ for (i = 0; i < fragstotal; i++) { ++ *(out_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); ++ if (*(out_dma_buf + i) == 0) { ++ printk(" can't allocate required DMA(OUT) buffers.\n"); ++ goto mem_failed_out; ++ } ++ *(out_dma_pbuf + i) = virt_to_phys((void *)(*(out_dma_buf + i))); ++ } ++ ++ return 1; ++all_mem_err: ++ printk("error:allocate memory occur error 1!\n"); ++ return 0; ++mem_failed_out: ++ printk("error:allocate memory occur error 2!\n"); ++ for (i = 0; i < fragstotal; i++) { ++ if(*(out_dma_buf + i)) ++ free_pages(*(out_dma_buf + i), get_order(fragsize)); ++ } ++ ++ return 0; ++mem_failed_in: ++ printk("error:allocate memory occur error 3!\n"); ++ for (i = 0; i < fragstotal; i++) { ++ if(*(in_dma_buf + i)) ++ free_pages(*(in_dma_buf + i), get_order(fragsize)); ++ } ++ return 0; ++} ++ ++static int Free_In_Out_queue(int fragstotal,int fragsize) ++{ ++ int i; ++ //playing ++ if(out_dma_buf != NULL) { ++ for (i = 0; i < fragstotal; i++) { ++ if(*(out_dma_buf + i)) ++ free_pages(*(out_dma_buf + i), get_order(fragsize)); ++ *(out_dma_buf + i) = 0; //release page error ++ } ++ kfree(out_dma_buf); ++ out_dma_buf = NULL; ++ } ++ if(out_dma_pbuf) { ++ kfree(out_dma_pbuf); ++ out_dma_pbuf = NULL; ++ } ++ if(out_dma_buf_data_count) { ++ kfree(out_dma_buf_data_count); ++ out_dma_buf_data_count = NULL; ++ } ++ if(out_empty_queue.id) { ++ kfree(out_empty_queue.id); ++ out_empty_queue.id = NULL; ++ } ++ if(out_full_queue.id) { ++ kfree(out_full_queue.id); ++ out_full_queue.id = NULL; ++ } ++ if(out_busy_queue.id) { ++ kfree(out_busy_queue.id); ++ out_busy_queue.id = NULL; ++ } ++ out_empty_queue.count = fragstotal; ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ ++ // recording ++ if(in_dma_buf) { ++ for (i = 0; i < fragstotal; i++) { ++ if(*(in_dma_buf + i)) { ++ dma_cache_wback_inv(*(in_dma_buf + i), fragsize); ++ free_pages(*(in_dma_buf + i), get_order(fragsize)); ++ } ++ *(in_dma_buf + i) = 0; //release page error ++ } ++ kfree(in_dma_buf); ++ in_dma_buf = NULL; ++ } ++ if(in_dma_pbuf) { ++ kfree(in_dma_pbuf); ++ in_dma_pbuf = NULL; ++ } ++ if(in_dma_buf_data_count) { ++ kfree(in_dma_buf_data_count); ++ in_dma_buf_data_count = NULL; ++ } ++ if(in_empty_queue.id) { ++ kfree(in_empty_queue.id); ++ in_empty_queue.id = NULL; ++ } ++ if(in_full_queue.id) { ++ kfree(in_full_queue.id); ++ in_full_queue.id = NULL; ++ } ++ if(in_busy_queue.id) { ++ kfree(in_busy_queue.id); ++ in_busy_queue.id = NULL; ++ } ++ ++ in_empty_queue.count = fragstotal; ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ ++ return 1; ++} ++ ++static int jz_audio_set_speed(int dev, int rate) ++{ ++ /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 */ ++ long codec_speed; ++ long speed = 0; ++ ++ jz_audio_speed = rate; ++ if (rate > 48000) ++ rate = 48000; ++ if (rate < 8000) ++ rate = 8000; ++ jz_audio_rate = rate; ++ ++ /*switch (rate) { ++ case 8000: ++ speed = 0; ++ break; ++ case 11025: ++ speed = 1; ++ break; ++ case 12000: ++ speed = 2; ++ break; ++ case 16000: ++ speed = 3; ++ break; ++ case 22050: ++ speed = 4; ++ break; ++ case 24000: ++ speed = 5; ++ break; ++ case 32000: ++ speed = 6; ++ break; ++ case 44100: ++ speed = 7; ++ break; ++ case 48000: ++ speed = 8; ++ break; ++ default: ++ break; ++ } ++ printk("set PCMIN or PCMOUT speed.\n"); ++ __pcm_set_clk_rate(speed); ++ __pcm_set_sync_rate(256);*/ ++ ++ return jz_audio_rate; ++} ++ ++ ++static int record_fill_1x8_u(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ volatile unsigned char *s = (unsigned char*)(*(in_dma_buf + id)); ++ volatile unsigned char *dp = (unsigned char*)dst_start; ++ ++ while (count > 0) { ++ *dp = *s; ++ dp ++; ++ s++; ++ count --; ++ cnt ++; ++ } ++ ++ return cnt; ++} ++ ++static int record_fill_2x8_u(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ volatile unsigned char *s = (unsigned char*)(*(in_dma_buf + id)); ++ volatile unsigned char *dp = (unsigned char*)dst_start; ++ ++#if 1 ++ while (count > 0) { ++ *dp = *s; ++ s ++; ++ dp ++; ++ count --; ++ cnt ++; ++ } ++#else ++ while (count > 0) { ++ *dp = *s; ++ s += 2; //skip right sample ++ dp ++; ++ count -= 2; ++ cnt ++; ++ } ++#endif ++ return cnt; ++} ++ ++static int record_fill_1x16_s(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned short d1; ++ unsigned short *s = (unsigned short*)(*(in_dma_buf + id)); ++ unsigned short *dp = (unsigned short *)dst_start; ++ ++ while (count > 0) { ++ *dp = *s; ++ s ++; ++ dp ++; ++ count -= 2; ++ cnt += 2; ++ } ++ ++ return cnt; ++} ++ ++static int record_fill_2x16_s(unsigned long dst_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned short *s = (unsigned short*)(*(in_dma_buf + id)); ++ unsigned short *dp = (unsigned short *)dst_start; ++ ++#if 1 ++ while (count > 0) { ++ *dp = *s; ++ s ++; ++ dp ++; ++ count -= 2; ++ cnt += 2; ++ } ++#else ++ while (count > 0) { ++ *dp = *s; ++ s += 2; //skip right sample ++ dp ++; ++ count -= 4; ++ cnt += 2;/* count in byte */ ++ } ++#endif ++ ++ return cnt; ++} ++ ++static void replay_fill_1x8_u(unsigned long src_start, int count, int id) ++{ ++#if 0 ++ volatile unsigned char *s = (unsigned char *)src_start; ++ volatile unsigned char *dp = (unsigned char *)(*(out_dma_buf + id)); ++ ++ *(out_dma_buf_data_count + id) = count; ++ while (count > 0) { ++ *dp = *s; ++ dp ++; ++ s ++; ++ count --; ++ } ++#else ++ volatile u8 *s = (u8 *)src_start; ++ volatile u8 *dp = (u8 *)(*(out_dma_buf + id)); ++ ++ *(out_dma_buf_data_count + id) = count; ++ while (count > 0) { ++ *dp = *s; ++ dp ++; ++ s ++; ++ count --; ++ } ++#endif ++} ++ ++static void replay_fill_2x8_u(unsigned long src_start, int count, int id) ++{ ++ volatile unsigned char *s = (unsigned char *)src_start; ++ volatile unsigned char *dp = (unsigned char *)(*(out_dma_buf + id)); ++ ++#if 1 ++ *(out_dma_buf_data_count + id) = count; ++ while (count > 0) { ++ *dp = *s; ++ dp ++; ++ s ++; ++ count --; ++ } ++#else ++ while (count > 0) { ++ *dp = *s; ++ s += 2; //skip right sample ++ dp ++; ++ count -= 2; ++ cnt ++; ++ } ++ *(out_dma_buf_data_count + id) = cnt; ++#endif ++} ++ ++ ++static void replay_fill_1x16_s(unsigned long src_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned short d1, l1; ++ signed short sam_to; ++ volatile unsigned short *s = (unsigned short *)src_start; ++ volatile unsigned short *dp = (unsigned short*)(*(out_dma_buf + id)); ++ ++ while (count > 0) { ++ d1 = *s; ++ ++ sam_to = (signed short)d1; ++ if (sam_to >= 0) { ++ sam_to = 0xfff * sam_to / 0x7fff; ++ } else { ++ sam_to = 0 - sam_to; ++ sam_to = 0xfff * sam_to / 0x7fff; ++ sam_to = 0 - sam_to; ++ } ++ d1 = (unsigned short)sam_to; ++ d1 = d1 << 3; ++ l1 = d1 | jz_audio_volume; ++ ++ *dp = l1; ++ s ++; ++ dp ++; ++ count -= 2; ++ cnt += 2 ; ++ } ++ *(out_dma_buf_data_count + id) = cnt; ++} ++ ++static void replay_fill_2x16_s(unsigned long src_start, int count, int id) ++{ ++ int cnt = 0; ++ unsigned short d1, l1; ++ volatile unsigned short *s = (unsigned short *)src_start; ++ volatile unsigned short *dp = (unsigned short*)(*(out_dma_buf + id)); ++ ++#if 1 ++ while (count > 0) { ++ d1 = *s; ++ l1 = (d1 & 0xfff8) | jz_audio_volume; ++ *dp = l1; ++ s ++; ++ dp ++; ++ count -= 2; ++ cnt += 2 ; ++ } ++ *(out_dma_buf_data_count + id) = cnt; ++#else ++ while (count > 0) { ++ d1 = *s; ++ l1 = (d1 & 0xfff8) | jz_audio_volume; ++ *dp = l1; ++ s += 2; //skip right sample ++ dp ++; ++ count -= 4; ++ cnt += 2; ++ } ++ *(out_dma_buf_data_count + id) = cnt; ++#endif ++} ++ ++static unsigned int jz_audio_set_format(int dev, unsigned int fmt) ++{ ++ switch (fmt) { ++ case AFMT_U8: ++ case AFMT_S16_LE: ++ jz_audio_format = fmt; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++ case AFMT_QUERY: ++ break; ++ } ++ ++ return jz_audio_format; ++} ++ ++static short jz_audio_set_channels(int dev, short channels) ++{ ++ switch (channels) { ++ case 1: ++ case 2: ++ jz_audio_channels = channels; ++ jz_update_filler(jz_audio_format, jz_audio_channels); ++ break; ++ default: ++ printk("channel number is wrong. %d\n",__LINE__); ++ } ++ ++ return jz_audio_channels; ++} ++ ++static void init_codec(void) ++{ ++ printk("set PCM codec RESET pin on LOW, while MCLK occur.\n"); ++ printk("set LINSEL on LOW(8bits) or HIGH(13bits+3bits for PCMOUT gain).\n"); ++} ++ ++static void jz_audio_reset(void) ++{ ++ __pcm_flush_fifo(); ++ __pcm_disable_txfifo(); ++ __pcm_disable_rxfifo(); ++ __pcm_set_transmit_trigger(7); ++ __pcm_set_receive_trigger(7); ++ __pcm_omsb_next_sync(); ++ __pcm_imsb_next_sync(); ++#if MODE_is_8 ++ __pcm_set_iss(8);//8bits decided by LINSEL ++ __pcm_set_oss(8);//8bits decided by LINSEL ++#else ++ __pcm_set_iss(16);//16bits decided by LINSEL ++ __pcm_set_oss(16); ++#endif ++ __pcm_disable_tfs_intr(); ++ __pcm_disable_tur_intr(); ++ __pcm_disable_rfs_intr(); ++ __pcm_disable_ror_intr(); ++ ++ __pcm_disable_receive_dma(); ++ __pcm_disable_transmit_dma(); ++ ++ //init_codec(); ++} ++ ++static int jz_audio_release(struct inode *inode, struct file *file); ++static int jz_audio_open(struct inode *inode, struct file *file); ++static int jz_audio_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg); ++static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait); ++static ssize_t jz_audio_write(struct file *file, const char *buffer,size_t count, loff_t *ppos); ++static ssize_t jz_audio_read(struct file *file, char *buffer,size_t count, loff_t *ppos); ++ ++/* static struct file_operations jz_pcm_audio_fops */ ++static struct file_operations jz_pcm_audio_fops = ++{ ++ owner: THIS_MODULE, ++ open: jz_audio_open, ++ release: jz_audio_release, ++ write: jz_audio_write, ++ read: jz_audio_read, ++ poll: jz_audio_poll, ++ ioctl: jz_audio_ioctl ++}; ++ ++static int jz_pcm_open_mixdev(struct inode *inode, struct file *file) ++{ ++ int i; ++ int minor = MINOR(inode->i_rdev); ++ struct jz_pcm_controller_info *controller = pcm_controller; ++ ++ for (i = 0; i < NR_PCM; i++) ++ if (controller->pcm_codec[i] != NULL && controller->pcm_codec[i]->dev_mixer == minor) ++ goto match; ++ ++ if (!controller) ++ return -ENODEV; ++match: ++ file->private_data = controller->pcm_codec[i]; ++ ++ return 0; ++} ++ ++static int jz_pcm_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct pcm_codec *codec = (struct pcm_codec *)file->private_data; ++ return codec->mixer_ioctl(codec, cmd, arg); ++} ++ ++static loff_t jz_pcm_llseek(struct file *file, loff_t offset, int origin) ++{ ++ return -ESPIPE; ++} ++ ++static struct file_operations jz_pcm_mixer_fops = ++{ ++ owner: THIS_MODULE, ++ llseek: jz_pcm_llseek, ++ ioctl: jz_pcm_ioctl_mixdev, ++ open: jz_pcm_open_mixdev, ++}; ++ ++static int pcm_mixer_ioctl(struct pcm_codec *codec, unsigned int cmd, unsigned long arg) ++{ ++ int ret; ++ long val = 0; ++ ++ switch (cmd) { ++ case SOUND_MIXER_READ_STEREODEVS: ++ return put_user(0, (long *) arg); ++ case SOUND_MIXER_READ_CAPS: ++ val = SOUND_CAP_EXCL_INPUT; ++ return put_user(val, (long *) arg); ++ case SOUND_MIXER_READ_DEVMASK: ++ break; ++ case SOUND_MIXER_READ_RECMASK: ++ break; ++ case SOUND_MIXER_READ_RECSRC: ++ break; ++ case SOUND_MIXER_WRITE_VOLUME: ++ ret = get_user(val, (long *) arg); ++ if (ret) ++ return ret; ++ val = val & 0xff; ++ if(val < 0) ++ val = 0; ++ if(val > 100) ++ val = 100; ++ if (val == 100) { ++ dump_pcmc_reg(); ++ return 100; ++ } ++ val = val / 10; ++ switch (val) { ++ case 0: ++ case 1: ++ jz_audio_volume = 7; ++ break; ++ case 2: ++ jz_audio_volume = 6; ++ break; ++ case 3: ++ jz_audio_volume = 5; ++ break; ++ case 4: ++ jz_audio_volume = 4; ++ break; ++ case 5: ++ jz_audio_volume = 3; ++ break; ++ case 6: ++ jz_audio_volume = 2; ++ break; ++ case 7: ++ case 8: ++ jz_audio_volume = 1; ++ break; ++ case 9: ++ case 10: ++ jz_audio_volume = 0; ++ break; ++ } ++ return 0; ++ case SOUND_MIXER_READ_VOLUME: ++ val = jz_audio_volume; ++ ret = val << 8; ++ val = val | ret; ++ return put_user(val, (long *) arg); ++ case SOUND_MIXER_WRITE_MIC: ++ printk("Can not justify Mic gain to the PCM codec.!\n"); ++ return -ENOSYS; ++ ++ case SOUND_MIXER_READ_MIC: ++ printk("Can not justify Mic gain to the PCM codec.!\n"); ++ return -ENOSYS; ++ default: ++ return -ENOSYS; ++ } ++ ++ return 0; ++} ++ ++ ++int pcm_probe_codec(struct pcm_codec *codec) ++{ ++ /* generic OSS to PCM wrapper */ ++ codec->mixer_ioctl = pcm_mixer_ioctl; ++ return 1; ++} ++ ++/* PCM codec initialisation. */ ++static int __init jz_pcm_codec_init(struct jz_pcm_controller_info *controller) ++{ ++ int num_pcm = 0; ++ struct pcm_codec *codec; ++ ++ for (num_pcm = 0; num_pcm < NR_PCM; num_pcm++) { ++ if ((codec = kmalloc(sizeof(struct pcm_codec),GFP_KERNEL)) == NULL) ++ return -ENOMEM; ++ memset(codec, 0, sizeof(struct pcm_codec)); ++ codec->private_data = controller; ++ codec->id = num_pcm; ++ ++ if (pcm_probe_codec(codec) == 0) ++ break; ++ if ((codec->dev_mixer = register_sound_mixer(&jz_pcm_mixer_fops, -1)) < 0) { ++ printk(KERN_ERR "Jz PCM: couldn't register mixer!\n"); ++ kfree(codec); ++ break; ++ } ++ controller->pcm_codec[num_pcm] = codec; ++ } ++ ++ return num_pcm; ++} ++ ++static void jz_update_filler(int format, int channels) ++{ ++#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) ++ ++ switch (TYPE(format, channels)) { ++ default: ++ case TYPE(AFMT_U8, 1): ++ replay_filler = replay_fill_1x8_u; ++ record_filler = record_fill_1x8_u; ++ break; ++ case TYPE(AFMT_U8, 2): ++ /*replay_filler = replay_fill_2x8_u; ++ record_filler = record_fill_2x8_u;*/ ++ printk("channel is 2. Line:%d\n",__LINE__); ++ break; ++ case TYPE(AFMT_S16_LE, 1): ++ replay_filler = replay_fill_1x16_s; ++ record_filler = record_fill_1x16_s; ++ break; ++ case TYPE(AFMT_S16_LE, 2): ++ /*replay_filler = replay_fill_2x16_s; ++ record_filler = record_fill_2x16_s;*/ ++ printk("channel is 2. Line:%d\n",__LINE__); ++ break; ++ } ++} ++ ++static void __init attach_jz_pcm(struct jz_pcm_controller_info *controller) ++{ ++ char *name; ++ int adev;//No of Audio device. ++ int err; ++ ++ name = controller->name; ++ /* register /dev/audio */ ++ adev = register_sound_dsp(&jz_pcm_audio_fops, -1); ++ if (adev < 0) ++ goto audio_failed; ++ /* initialize PCM codec and register /dev/mixer */ ++ if (jz_pcm_codec_init(controller) <= 0) ++ goto mixer_failed; ++ ++ controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, JZCODEC_USER_BUFFER); ++ if (!controller->tmp1) { ++ printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); ++ goto tmp1_failed; ++ } ++ printk("DMA_ID_PCM_TX=0x%08x DMA_ID_PCM_RX=0x%08x\n",DMA_ID_PCM_TX ,DMA_ID_PCM_RX); ++ if ((controller->dma1 = jz_request_dma(DMA_ID_PCM_TX, "audio dac", jz_pcm_replay_dma_irq, IRQF_DISABLED, controller)) < 0) { ++ printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name); ++ goto dma1_failed; ++ } ++ if ((controller->dma2 = jz_request_dma(DMA_ID_PCM_RX, "audio adc", jz_pcm_record_dma_irq, IRQF_DISABLED, controller)) < 0) { ++ printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name); ++ goto dma2_failed; ++ } ++ printk("JzSOC On-Chip PCM controller registered (DAC: DMA(play):%d/IRQ%d,\n ADC: DMA(record):%d/IRQ%d)\n", controller->dma1, get_dma_done_irq(controller->dma1), controller->dma2, get_dma_done_irq(controller->dma2)); ++ ++ err = request_irq(IRQ_PCM, jz_pcm_irq, IRQF_DISABLED, "pcm irq", controller); ++ if (err < 0) ++ printk("can't allocate pcm irq.\n"); ++ controller->dev_audio = adev; ++ ++ return; ++dma2_failed: ++ jz_free_dma(controller->dma2); ++dma1_failed: ++ jz_free_dma(controller->dma1); ++tmp1_failed: ++ free_pages((unsigned long)controller->tmp1, JZCODEC_USER_BUFFER); ++ ++mixer_failed: ++ unregister_sound_dsp(adev); ++audio_failed: ++ return; ++} ++ ++ ++static int __init probe_jz_pcm(struct jz_pcm_controller_info **controller) ++{ ++ if ((*controller = kmalloc(sizeof(struct jz_pcm_controller_info), ++ GFP_KERNEL)) == NULL) { ++ printk(KERN_ERR "Jz PCM Controller: out of memory.\n"); ++ return -ENOMEM; ++ } ++ ++ (*controller)->name = "Jz PCM controller"; ++ (*controller)->opened1 = 0; ++ (*controller)->opened2 = 0; ++ init_waitqueue_head(&(*controller)->adc_wait); ++ init_waitqueue_head(&(*controller)->dac_wait); ++ spin_lock_init(&(*controller)->lock); ++ init_waitqueue_head(&rx_wait_queue); ++ init_waitqueue_head(&tx_wait_queue); ++ ++ return 0; ++} ++ ++static void __exit unload_jz_pcm(struct jz_pcm_controller_info *controller) ++{ ++ int adev = controller->dev_audio; ++ ++ __pcm_reset(); ++ schedule_timeout(5); ++ __pcm_disable(); ++ __pcm_clk_disable(); ++ __pcm_flush_fifo(); ++ ++ controller->dev_audio = -1; ++ jz_free_dma(controller->dma1); ++ jz_free_dma(controller->dma2); ++ free_pages((unsigned long)controller->tmp1, JZCODEC_USER_BUFFER); ++ ++ if (adev >= 0) ++ unregister_sound_dsp(controller->dev_audio); ++} ++ ++static int __init init_jz_pcm(void) ++{ ++ int errno; ++ /* 24M OSC ---> CPCCR.ECS ---> PCMCDR.PCMS ---> cpm_pcm_sysclk(X) */ ++ long X = 12000000; //in Hz /* 6.144 MHz <= X <= 264.192 MHz */ ++ ++#if 0 ++ /* pcm_sys_clk is from PLL divsion */ ++ REG_CPM_PCMCDR = 0x8000001b; ++ REG_CPM_CPCCR |= 0x00400000; ++#endif ++ /* pcm_sys_clk is from external clock */ ++ /* reset codec GPF4 */ ++ __gpio_as_output(32 * 5 + 4); ++ __gpio_set_pin(32 * 5 + 4); ++ mdelay(1); ++ ++ __pcm_reset(); ++ schedule_timeout(5); ++ __pcm_clk_disable(); ++ ++ /* set CPM to output cpm-pcm-sysclk ,assume cpm-pcm-sysclk is X Hz */ ++ /* PCMCLK must be 2048000 Hz, it is 256 mutil of PCMSYNC */ ++ //__pcm_set_clk_rate(X, 2048000); ++ __pcm_set_clk_rate(X, 2000000); ++ /* PCMSYNC must be 8000 Hz. 2048000 / 256 = 8000 */ ++ __pcm_set_sync_rate(2048000, 8000); ++ //__pcm_set_sync_rate(2000000, 8000); ++ __pcm_set_sync_len(0); ++ ++ __pcm_flush_fifo(); ++ __pcm_disable_txfifo(); ++ __pcm_disable_rxfifo(); ++ __pcm_set_transmit_trigger(7); ++ __pcm_set_receive_trigger(7); ++ __pcm_omsb_next_sync(); ++ __pcm_imsb_next_sync(); ++ ++#if MODE_is_8 ++ __pcm_set_iss(8);//8bits decided by LINSEL ++ __pcm_set_oss(8); ++#else ++ __pcm_set_iss(16);//16bits decided by LINSEL ++ __pcm_set_oss(16); ++#endif ++ __pcm_set_valid_slot(1); ++ ++ __pcm_disable_tfs_intr(); ++ __pcm_disable_tur_intr(); ++ __pcm_disable_rfs_intr(); ++ __pcm_disable_ror_intr(); ++ ++ __pcm_disable_receive_dma(); ++ __pcm_disable_transmit_dma(); ++ ++ __pcm_last_sample(); ++ __pcm_as_master(); ++ ++ __pcm_enable(); ++ __pcm_clk_enable(); ++ ++ if ((errno = probe_jz_pcm(&pcm_controller)) < 0) ++ return errno; ++ attach_jz_pcm(pcm_controller); ++ ++ out_empty_queue.id = NULL; ++ out_full_queue.id = NULL; ++ out_busy_queue.id = NULL; ++ in_empty_queue.id = NULL; ++ in_full_queue.id = NULL; ++ in_busy_queue.id = NULL; ++ ++ jz_audio_fragsize = JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE; ++ jz_audio_fragstotal = JZCODEC_RW_BUFFER_TOTAL ; ++ Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ ++ __gpio_clear_pin(32 * 5 + 4); ++ udelay(100); ++ ++ __gpio_set_pin(32 * 5 + 4); ++ dump_pcmc_reg(); ++ ++ return 0; ++} ++ ++ ++static void __exit cleanup_jz_pcm(void) ++{ ++ unload_jz_pcm(pcm_controller); ++ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++} ++ ++module_init(init_jz_pcm); ++module_exit(cleanup_jz_pcm); ++ ++static int drain_adc(struct jz_pcm_controller_info *ctrl, int nonblock) ++{ ++ unsigned long flags; ++ int count, i=0; ++ ++ for (;;) { ++ if ( i < MAXDELAY ) { ++ udelay(10); ++ i++; ++ } else ++ break; ++ ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma2); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ ++ if (nonblock) ++ return -EBUSY; ++ } ++ ++ return 0; ++} ++static int drain_dac(struct jz_pcm_controller_info *ctrl, int nonblock) ++{ ++ unsigned long flags; ++ int count, ele, i=0; ++ ++ for (;;) { ++ if(!nonblock) {//blocked ++ if ( i < MAXDELAY ) { ++ udelay(10); ++ i++; ++ } else ++ break; ++ ++ ele = elements_in_queue(&out_full_queue); ++ if(ele <= 0) { ++ udelay(10); ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ } ++ } else {//non-blocked ++ mdelay(100); ++ ele = elements_in_queue(&out_full_queue); ++ ++ if(ele <= 0) { ++ mdelay(100); ++ ++ spin_lock_irqsave(&ctrl->lock, flags); ++ count = get_dma_residue(ctrl->dma1); ++ spin_unlock_irqrestore(&ctrl->lock, flags); ++ if (count <= 0) ++ break; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int jz_audio_release(struct inode *inode, struct file *file) ++{ ++ struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data; ++ ++ if (controller == NULL) ++ return -ENODEV; ++ ++ if ( controller->opened1 == 1 ) { ++ __pcm_enable_transmit_dma(); ++ __pcm_enable_txfifo(); ++ drain_dac(controller, file->f_flags & O_NONBLOCK); ++ disable_dma(controller->dma1); ++ set_dma_count(controller->dma1, 0); ++ ++ __pcm_disable_transmit_dma(); ++ __pcm_disable_txfifo(); ++ ++ spin_lock(&controller->ioctllock); ++ controller->total_bytes = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ spin_unlock(&controller->ioctllock); ++ //__pcm_disable(); ++ controller->opened1 = 0; ++ } ++ ++ if ( controller->opened2 == 1 ) { ++ first_record_call = 1; ++ __pcm_enable_receive_dma(); ++ __pcm_enable_rxfifo(); ++ drain_adc(controller, file->f_flags & O_NONBLOCK); ++ disable_dma(controller->dma2); ++ set_dma_count(controller->dma2, 0); ++ __pcm_disable_receive_dma(); ++ __pcm_disable_rxfifo(); ++ ++ spin_lock(&controller->ioctllock); ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ spin_unlock(&controller->ioctllock); ++ //__pcm_disable(); ++ controller->opened2 = 0; ++ } ++ __pcm_disable_tfs_intr(); ++ __pcm_disable_tur_intr(); ++ __pcm_disable_rfs_intr(); ++ __pcm_disable_ror_intr(); ++ ++ return 0; ++} ++ ++static int jz_audio_open(struct inode *inode, struct file *file) ++{ ++ int i; ++ struct jz_pcm_controller_info *controller = pcm_controller; ++ ++ if (controller == NULL) ++ return -ENODEV; ++ ++ if (controller->opened1 == 1 || controller->opened2 == 1 ) { ++ printk("\naudio is busy!\n"); ++ return -EBUSY; ++ } ++ REG_DMAC_DMACKE(0) = 0x3f; ++ REG_DMAC_DMACKE(1) = 0x3f; ++ if (file->f_mode & FMODE_WRITE) { ++ if (controller->opened1 == 1) ++ return -EBUSY; ++ controller->opened1 = 1; ++ //for ioctl ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextOut = 0; ++ ++ out_empty_queue.count = jz_audio_fragstotal; ++ for (i=0;i < jz_audio_fragstotal;i++) ++ *(out_empty_queue.id + i) = i; ++ out_busy_queue.count = 0; ++ out_full_queue.count = 0; ++ /* set PCMOUT params */ ++ } ++ ++ if (file->f_mode & FMODE_READ) { ++ if (controller->opened2 == 1) ++ return -EBUSY; ++ controller->opened2 = 1; ++ first_record_call = 1; ++ //for ioctl ++ controller->total_bytes = 0; ++ jz_audio_dma_tran_count = 0; ++ controller->count = 0; ++ controller->finish = 0; ++ controller->blocks = 0; ++ controller->nextIn = 0; ++ in_empty_queue.count = jz_audio_fragstotal; ++ for (i=0;i < jz_audio_fragstotal;i++) ++ *(in_empty_queue.id + i) = i; ++ in_full_queue.count = 0; ++ in_busy_queue.count = 0; ++ /* set PCMIN params */ ++ } ++ ++ file->private_data = controller; ++ jz_audio_reset(); ++ __pcm_enable(); ++ ++ return 0; ++} ++ ++static int jz_audio_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ int val,fullc,busyc,unfinish,newfragstotal,newfragsize; ++ unsigned int flags; ++ audio_buf_info abinfo; ++ int i, bytes, id; ++ count_info cinfo; ++ struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data; ++ ++ val = 0; ++ bytes = 0; ++ switch (cmd) { ++ case OSS_GETVERSION: ++ return put_user(SOUND_VERSION, (int *)arg); ++ case SNDCTL_DSP_RESET: ++ return 0; ++ case SNDCTL_DSP_SYNC: ++ if (file->f_mode & FMODE_WRITE) ++ drain_dac(controller, file->f_flags & O_NONBLOCK); ++ return 0; ++ case SNDCTL_DSP_SPEED: ++ { ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ /* set smaple rate */ ++ if (val >= 0) ++ jz_audio_set_speed(controller->dev_audio, val); ++ return put_user(val, (int *)arg); ++ } ++ case SNDCTL_DSP_STEREO: ++ /* set stereo or mono channel */ ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ ++ jz_audio_set_channels(controller->dev_audio, val ? 2 : 1); ++ return 0; ++ ++ case SNDCTL_DSP_GETBLKSIZE: ++ //return put_user(4*PAGE_SIZE, (int *)arg); ++ return put_user(jz_audio_fragsize , (int *)arg); ++ case SNDCTL_DSP_GETFMTS: ++ /* Returns a mask of supported sample format*/ ++ return put_user(AFMT_U8 | AFMT_S16_LE, (int *)arg); ++ ++ case SNDCTL_DSP_SETFMT: ++ { ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ /* Select sample format */ ++ if (val != AFMT_QUERY) ++ jz_audio_set_format(controller->dev_audio,val); ++ else ++ if (file->f_mode & FMODE_READ) ++ val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; ++ else ++ val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; ++ return put_user(val, (int *)arg); ++ } ++ ++ case SNDCTL_DSP_CHANNELS: ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ printk("%s:%s:%d\n",__FILE__,__FUNCTION__,__LINE__); ++ jz_audio_set_channels(controller->dev_audio, val); ++ return put_user(val, (int *)arg); ++ ++ case SNDCTL_DSP_POST: ++ /* FIXME: the same as RESET ?? */ ++ return 0; ++ ++ case SNDCTL_DSP_SUBDIVIDE: ++ return 0; ++ ++ case SNDCTL_DSP_SETFRAGMENT: ++ get_user(val, (long *) arg); ++ newfragsize = 1 << (val & 0xFFFF);//16 least bits ++ ++ if (newfragsize < 4 * PAGE_SIZE) ++ newfragsize = 4 * PAGE_SIZE; ++ if (newfragsize > (16 * PAGE_SIZE)) //16 PAGE_SIZE ++ newfragsize = 16 * PAGE_SIZE; ++ ++ newfragstotal = (val >> 16) & 0x7FFF; ++ if (newfragstotal < 2) ++ newfragstotal = 2; ++ if (newfragstotal > 32) ++ newfragstotal = 32; ++ if((jz_audio_fragstotal == newfragstotal) && (jz_audio_fragsize == newfragsize)) ++ return 0; ++ Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ mdelay(500); ++ jz_audio_fragstotal = newfragstotal; ++ jz_audio_fragsize = newfragsize; ++ ++ Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); ++ mdelay(10); ++ ++ return 0; ++ case SNDCTL_DSP_GETCAPS: ++ return put_user(DSP_CAP_REALTIME|DSP_CAP_BATCH, (int *)arg); ++ case SNDCTL_DSP_NONBLOCK: ++ file->f_flags |= O_NONBLOCK; ++ return 0; ++ case SNDCTL_DSP_SETDUPLEX: ++ return -EINVAL; ++ case SNDCTL_DSP_GETOSPACE: ++ { ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ //unused fragment amount ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ jz_audio_fragments = elements_in_queue(&out_empty_queue); ++ for (i = 0; i < jz_audio_fragments; i++) ++ bytes += jz_audio_fragsize; ++ ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ abinfo.fragments = jz_audio_fragments; ++ abinfo.fragstotal = jz_audio_fragstotal; ++ abinfo.fragsize = jz_audio_fragsize; ++ abinfo.bytes = bytes; ++ return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; ++ } ++ case SNDCTL_DSP_GETISPACE: ++ if (!(file->f_mode & FMODE_READ)) ++ return -EINVAL; ++ bytes = 0; ++ //unused fragment amount ++ jz_audio_fragments = elements_in_queue(&in_empty_queue); ++ for (i = 0; i < jz_audio_fragments; i++) ++ bytes += jz_audio_fragsize; ++ ++ abinfo.fragments = jz_audio_fragments; ++ abinfo.fragstotal = jz_audio_fragstotal; ++ abinfo.fragsize = jz_audio_fragsize; ++ abinfo.bytes = bytes; ++ return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; ++ case SNDCTL_DSP_GETTRIGGER: ++ val = 0; ++ if (file->f_mode & FMODE_READ && in_dma_buf) ++ val |= PCM_ENABLE_INPUT; ++ if (file->f_mode & FMODE_WRITE && out_dma_buf) ++ val |= PCM_ENABLE_OUTPUT; ++ return put_user(val, (int *)arg); ++ ++ case SNDCTL_DSP_SETTRIGGER: ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ return 0; ++ case SNDCTL_DSP_GETIPTR: ++ if (!(file->f_mode & FMODE_READ)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ cinfo.bytes = controller->total_bytes; ++ cinfo.blocks = controller->blocks; ++ cinfo.ptr = controller->nextIn; ++ controller->blocks = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); ++ case SNDCTL_DSP_GETOPTR: ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ cinfo.bytes = controller->total_bytes; ++ cinfo.blocks = controller->blocks; ++ cinfo.ptr = controller->nextOut; ++ controller->blocks = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); ++ case SNDCTL_DSP_GETODELAY: ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ unfinish = 0; ++ fullc = elements_in_queue(&out_full_queue); ++ busyc = elements_in_queue(&out_busy_queue); ++ for(i = 0;i < fullc ;i ++) { ++ id = *(out_full_queue.id + i); ++ unfinish += *(out_dma_buf_data_count + id); ++ } ++ for(i = 0;i < busyc ;i ++) { ++ id = *(out_busy_queue.id + i); ++ unfinish += get_dma_residue(controller->dma1); ++ } ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ return put_user(unfinish, (int *) arg); ++ case SOUND_PCM_READ_RATE: ++ return put_user(jz_audio_rate, (int *)arg); ++ case SOUND_PCM_READ_CHANNELS: ++ return put_user(jz_audio_channels, (int *)arg); ++ case SOUND_PCM_READ_BITS: ++ return put_user((jz_audio_format & (AFMT_S8 | AFMT_U8)) ? 8 : 16, (int *)arg); ++ case SNDCTL_DSP_MAPINBUF: ++ case SNDCTL_DSP_MAPOUTBUF: ++ case SNDCTL_DSP_SETSYNCRO: ++ case SOUND_PCM_WRITE_FILTER: ++ case SOUND_PCM_READ_FILTER: ++ return -EINVAL; ++ } ++ return -EINVAL; ++} ++ ++static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait) ++{ ++ unsigned long flags; ++ unsigned int mask = 0; ++ struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data; ++ ++ if (file->f_mode & FMODE_WRITE) { ++ if (elements_in_queue(&out_empty_queue) > 0) ++ return POLLOUT | POLLWRNORM; ++ poll_wait(file, &controller->dac_wait, wait); ++ } ++ ++ if (file->f_mode & FMODE_READ) { ++ if (elements_in_queue(&in_full_queue) > 0) ++ return POLLIN | POLLRDNORM; ++ poll_wait(file, &controller->adc_wait, wait); ++ } ++ ++ spin_lock_irqsave(&controller->lock, flags); ++ if (file->f_mode & FMODE_WRITE) { ++ if (elements_in_queue(&out_empty_queue) > 0) ++ mask |= POLLOUT | POLLWRNORM; ++ } else if (file->f_mode & FMODE_READ) { ++ if (elements_in_queue(&in_full_queue) > 0) ++ mask |= POLLIN | POLLRDNORM; ++ } ++ spin_unlock_irqrestore(&controller->lock, flags); ++ ++ return mask; ++} ++ ++static ssize_t jz_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos) ++{ ++ int id, ret = 0, left_count, copy_count, cnt = 0; ++ struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data; ++ ++ if (count < 0) ++ return -EINVAL; ++ ++ __pcm_enable_receive_dma(); ++ __pcm_enable_rxfifo(); ++ ++ spin_lock(&controller->ioctllock); ++ controller->nextIn = 0; ++ spin_unlock(&controller->ioctllock); ++ ++ spin_lock(&controller->lock); ++ ++#if 0 ++ if (count < 2 * PAGE_SIZE ) ++ copy_count = count * 16 / (jz_audio_channels * jz_audio_format); ++ else ++ copy_count = 2 * PAGE_SIZE ; ++#else ++ if (count <= jz_audio_fragsize) ++ copy_count = count; ++ else ++ copy_count = jz_audio_fragsize; ++#endif ++ ++ left_count = count; ++ spin_unlock(&controller->lock); ++ ++ if (first_record_call) { ++ first_record_call = 0; ++audio_read_back_first: ++ if ((id = get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id); ++ ++ spin_lock(&controller->lock); ++ *(in_dma_buf_data_count + id) = copy_count; ++ spin_unlock(&controller->lock); ++ ++ dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); ++ audio_start_dma(controller->dma2,file->private_data, ++ *(in_dma_pbuf + id), ++ *(in_dma_buf_data_count + id), ++ DMA_MODE_READ); ++ ++ sleep_on(&rx_wait_queue); ++ } else ++ goto audio_read_back_first; ++ } ++ ++ while (left_count > 0) { ++audio_read_back_second: ++ if (elements_in_queue(&in_full_queue) <= 0) { ++ if (file->f_flags & O_NONBLOCK) ++ return ret ? ret : -EAGAIN; ++ else ++ sleep_on(&rx_wait_queue); ++ } ++ ++ if ((id = get_buffer_id(&in_full_queue)) >= 0) { ++ spin_lock(&controller->lock); ++ cnt = record_filler((unsigned long)controller->tmp1+ret, copy_count, id); ++ spin_unlock(&controller->lock); ++ put_buffer_id(&in_empty_queue, id); ++ } else ++ goto audio_read_back_second; ++ ++ if (elements_in_queue(&in_busy_queue) == 0) { ++ if ((id=get_buffer_id(&in_empty_queue)) >= 0) { ++ put_buffer_id(&in_busy_queue, id); ++ spin_lock(&controller->lock); ++ *(in_dma_buf_data_count + id) = copy_count; ++ spin_unlock(&controller->lock); ++ ++ dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); ++ audio_start_dma(controller->dma2,file->private_data, ++ *(in_dma_pbuf + id), ++ *(in_dma_buf_data_count + id), ++ DMA_MODE_READ); ++ } ++ } ++ if (ret + cnt > count) { ++ spin_lock(&controller->lock); ++ cnt = count - ret; ++ spin_unlock(&controller->lock); ++ } ++ if (copy_to_user(buffer+ret, controller->tmp1+ret, cnt)) ++ return ret ? ret : -EFAULT; ++ spin_lock(&controller->lock); ++ ret += cnt; ++ spin_unlock(&controller->lock); ++ ++ spin_lock(&controller->ioctllock); ++ controller->nextIn += ret; ++ spin_unlock(&controller->ioctllock); ++ ++ spin_lock(&controller->lock); ++ left_count -= cnt; ++ spin_unlock(&controller->lock); ++ } ++ ++ return ret; ++} ++ ++static ssize_t jz_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) ++{ ++ int id, ret, left_count, copy_count; ++ unsigned int flags; ++ struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data; ++ ++ if (count <= 0) ++ return -EINVAL; ++ ++ __pcm_enable_transmit_dma(); ++ __pcm_enable_txfifo(); ++ ++ spin_lock_irqsave(&controller->ioctllock, flags); ++ controller->nextOut = 0; ++ spin_unlock_irqrestore(&controller->ioctllock, flags); ++ ++#if 0 ++ if (count < 2 * PAGE_SIZE ) ++ copy_count = count; ++ else ++ copy_count = 2 * PAGE_SIZE; ++#else ++ if (count <= jz_audio_fragsize) ++ copy_count = count; ++ else ++ copy_count = jz_audio_fragsize; ++#endif ++ ++ left_count = count; ++ ret = 0; ++ ++ if (copy_from_user(controller->tmp1, buffer, count)) { ++ printk("copy_from_user failed:%d",ret); ++ return ret ? ret : -EFAULT; ++ } ++ ++ while (left_count > 0) { ++audio_write_back: ++ if (elements_in_queue(&out_empty_queue) == 0) { ++ // all are full ++ if (file->f_flags & O_NONBLOCK) ++ return ret; ++ else ++ sleep_on(&tx_wait_queue); ++ } ++ /* the end fragment size in this write */ ++ if (ret + copy_count > count) ++ copy_count = count - ret; ++ if ((id = get_buffer_id(&out_empty_queue)) >= 0) { ++ replay_filler((unsigned long)controller->tmp1 + ret, copy_count, id); ++ if(*(out_dma_buf_data_count + id) > 0) { ++ put_buffer_id(&out_full_queue, id); //busy in ++ dma_cache_wback_inv(*(out_dma_buf + id), *(out_dma_buf_data_count + id)); ++ } else ++ put_buffer_id(&out_empty_queue, id); //spare ++ } else ++ goto audio_write_back; ++ ++ left_count = left_count - copy_count; ++ ret += copy_count;//all is in byte ++ ++ spin_lock(&controller->ioctllock); ++ controller->nextOut += ret; ++ spin_unlock(&controller->ioctllock); ++ ++ if (elements_in_queue(&out_busy_queue) == 0) { ++ if ((id=get_buffer_id(&out_full_queue)) >= 0) { ++ put_buffer_id(&out_busy_queue, id); ++ ++ if(*(out_dma_buf_data_count + id) > 0) { ++ audio_start_dma(controller->dma1, ++ file->private_data, ++ *(out_dma_pbuf + id), ++ *(out_dma_buf_data_count + id), ++ DMA_MODE_WRITE); ++ ++ } ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++static void dump_pcmc_reg(void) ++{ ++ printk("REG_DMAC_DMACKE(0) : 0x%08x\n",REG_DMAC_DMACKE(0)); ++ printk("REG_DMAC_DMACKE(1) : 0x%08x\n",REG_DMAC_DMACKE(1)); ++ printk("REG_CPM_CLKGR=0x%08x\n",REG_CPM_CLKGR); ++ printk("REG_CPM_CPCCR : 0x%08x\n",REG_CPM_CPCCR); ++ printk("REG_CPM_PCMCDR : 0x%08x\n",REG_CPM_PCMCDR); ++ printk("REG_PCM_CLT : 0x%08x\n",REG_PCM_CTL); ++ printk("REG_PCM_CFG : 0x%08x\n",REG_PCM_CFG); ++ printk("REG_PCM_INTC : 0x%08x\n",REG_PCM_INTC); ++ printk("REG_PCM_INTS : 0x%08x\n",REG_PCM_INTS); ++ printk("REG_PCM_DIV : 0x%08x\n",REG_PCM_DIV); ++} +diff -urN linux-2.6.24.7.old/sound/oss/jzcodec.c linux-2.6.24.7/sound/oss/jzcodec.c +--- linux-2.6.24.7.old/sound/oss/jzcodec.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/oss/jzcodec.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,443 @@ ++/* ++ * linux/drivers/sound/jzcodec.c ++ * ++ * JzSOC internal audio driver. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sound_config.h" ++ ++#define USE_NONE 1 ++#define USE_MIC 2 ++#define USE_LINEIN 3 ++ ++typedef struct hpvol_shift_s ++{ ++ int hpvol; ++ int shift; ++} hpvol_shift_t; ++ ++extern mixer_info info; ++extern _old_mixer_info old_info; ++extern int codec_volue_shift; ++extern hpvol_shift_t hpvol_shift_table[72]; ++extern int abnormal_data_count; ++ ++extern void (*set_codec_mode)(void); ++extern void (*each_time_init_codec)(void); ++extern int (*set_codec_startup_param)(void); ++extern void (*set_codec_volume_table)(void); ++extern void (*set_codec_record)(int mode); ++extern void (*set_codec_replay)(void); ++extern void (*set_codec_replay_record)(int mode); ++extern void (*turn_on_codec)(void); ++extern void (*turn_off_codec)(void); ++extern void (*set_codec_speed)(int rate); ++extern void (*reset_codec)(void); ++extern void (*codec_mixer_old_info_id_name)(void); ++extern void (*codec_mixer_info_id_name)(void); ++extern void (*set_codec_bass)(int val); ++extern void (*set_codec_volume)(int val); ++extern void (*set_codec_mic)(int val); ++extern void (*set_codec_line)(int val); ++extern void (*i2s_resume_codec)(void); ++extern void (*i2s_suspend_codec)(void); ++extern void (*set_codec_direct_mode)(void); ++extern void (*clear_codec_direct_mode)(void); ++ ++static int jzcodec_reg[2]; ++ ++void set_jzcodec_mode(void); ++void each_time_init_jzcodec(void); ++int set_jzcodec_startup_param(void); ++void set_jzcodec_volume_table(void); ++void set_jzcodec_replay(void); ++void set_jzcodec_record(int mode); ++void turn_on_jzcodec(void); ++void turn_off_jzcodec(void); ++void set_jzcodec_speed(int rate); ++void reset_jzcodec(void); ++void jzcodec_mixer_old_info_id_name(void); ++void jzcodec_mixer_info_id_name(void); ++void set_jzcodec_bass(int val); ++void set_jzcodec_volume(int val); ++void set_jzcodec_mic(int val); ++void set_jzcodec_line(int val); ++void in_codec_app1(void); ++void in_codec_app12(void); ++void HP_turn_on(void); ++void HP_turn_off(void); ++void resume_jzcodec(void); ++void suspend_jzcodec(void); ++void set_jzcodec_replay_record(int mode); ++ ++void set_jzcodec_mode(void) ++{ ++ __i2s_internal_codec(); ++ __i2s_as_slave(); ++ __i2s_select_i2s(); ++ __aic_select_i2s(); ++ ++ REG_ICDC_CDCCR1 = 0x001b2303; ++ schedule_timeout(1); ++ REG_ICDC_CDCCR1 = 0x001b2302; ++} ++ ++void each_time_init_jzcodec(void) ++{ ++ __i2s_disable(); ++ __i2s_as_slave(); ++ __i2s_set_oss_sample_size(16); ++ __i2s_set_iss_sample_size(16); ++} ++ ++int set_jzcodec_startup_param(void) ++{ ++ REG_ICDC_CDCCR1 = 0x001b2300; ++ schedule_timeout(50); ++ REG_ICDC_CDCCR1 = 0x00033300; ++ schedule_timeout(5); ++ REG_ICDC_CDCCR1 = 0x17026300; ++ ++ return 1; ++} ++void set_jzcodec_volume_table(void) ++{ ++ int errno,hpvol_step,sample_shift; ++ ++ codec_volue_shift = 0; ++ hpvol_step = 3; ++ sample_shift = 0; ++ for(errno = 0;errno < 72;errno++) { ++ hpvol_shift_table[errno].hpvol = hpvol_step; ++ hpvol_shift_table[errno].shift = sample_shift; ++ hpvol_step --; ++ if(hpvol_step <= 0) { ++ hpvol_step = 3; ++ sample_shift ++; ++ } ++ } ++} ++ ++void set_jzcodec_replay(void) ++{ ++ in_codec_app1(); ++} ++ ++void set_jzcodec_record(int mode) ++{ ++ switch (mode) { ++ case USE_NONE: ++ case USE_MIC: ++ in_codec_app12(); ++ abnormal_data_count = 0; ++ break; ++ case USE_LINEIN: ++ REG_ICDC_CDCCR1 = 0x27022000;//for linein ++ mdelay(300); ++ break; ++ } ++} ++ ++void set_jzcodec_replay_record(int mode) ++{ ++ long val = 0; ++ REG_ICDC_CDCCR1 = 0x00037302; ++ mdelay(2); ++ REG_ICDC_CDCCR1 = 0x03006000; ++ mdelay(2); ++ ++ switch (mode) { ++ case USE_NONE: ++ case USE_MIC: ++ REG_ICDC_CDCCR1 = 0x17022000;//for mic ++ break; ++ case USE_LINEIN: ++ REG_ICDC_CDCCR1 = 0x27022000;//for linein ++ break; ++ } ++ ++ val = REG_ICDC_CDCCR2; ++ val &= 0x0000ff00; ++ val |= 0x00170030; ++ REG_ICDC_CDCCR2 = val; ++} ++ ++void turn_on_jzcodec(void) ++{ ++ HP_turn_on(); ++} ++ ++void set_jzcodec_direct_mode(void) ++{ ++ long val = 0; ++ REG_ICDC_CDCCR1 = 0x14000000; ++ val &= 0x0000ff00; ++ val |= 0x001f0033; ++ REG_ICDC_CDCCR2 = val; ++ mdelay(300); ++} ++ ++void clear_jzcodec_direct_mode(void) ++{ ++ HP_turn_off(); ++} ++ ++void turn_off_jzcodec(void) ++{ ++ HP_turn_off(); ++} ++ ++void set_jzcodec_speed(int rate) ++{ ++ long codec_speed,speed = 0; ++ switch (rate) { ++ case 8000: ++ speed = 0; ++ break; ++ case 11025: ++ speed = 1; ++ break; ++ case 12000: ++ speed = 2; ++ break; ++ case 16000: ++ speed = 3; ++ break; ++ case 22050: ++ speed = 4; ++ break; ++ case 24000: ++ speed = 5; ++ break; ++ case 32000: ++ speed = 6; ++ break; ++ case 44100: ++ speed = 7; ++ break; ++ case 48000: ++ speed = 8; ++ break; ++ default: ++ break; ++ } ++ ++ codec_speed = REG_ICDC_CDCCR2; ++ codec_speed |= 0x00000f00; ++ ++ speed = speed << 8; ++ speed |= 0xfffff0ff; ++ codec_speed &= speed; ++ REG_ICDC_CDCCR2 = codec_speed; ++} ++ ++void reset_jzcodec(void) ++{ ++ REG_ICDC_CDCCR1 |= 1; ++ mdelay(1); ++ REG_ICDC_CDCCR1 &= 0xfffffffe; ++} ++ ++void jzcodec_mixer_old_info_id_name(void) ++{ ++ strncpy(info.id, "JZCODEC", sizeof(info.id)); ++ strncpy(info.name,"Jz internal codec", sizeof(info.name)); ++} ++ ++void jzcodec_mixer_info_id_name(void) ++{ ++ strncpy(old_info.id, "JZCODEC", sizeof(old_info.id)); ++ strncpy(old_info.name,"Jz internal codec", sizeof(old_info.name)); ++} ++ ++void set_jzcodec_bass(int val) ++{ ++ int bass_gain = 0; ++ if(val < 25) ++ bass_gain = 0; ++ if(val >= 25 && val < 50) ++ bass_gain = 1; ++ if(val >= 50 && val < 75) ++ bass_gain = 2; ++ if(val >= 75 && val <= 100 ) ++ bass_gain = 3; ++ ++ REG_ICDC_CDCCR2 = ((REG_ICDC_CDCCR2 & ~(0x3 << 4)) | (bass_gain << 4)); ++} ++ ++void set_jzcodec_volume(int val) ++{ ++ unsigned int sample_oss; ++ int index,shift_max,vol_scale,vol_step,codec_volume; ++ shift_max = 0; ++ sample_oss = (REG_AIC_CR & 0x00380000) >> 19; ++ ++ switch(sample_oss) ++ { ++ case 0x0: ++ shift_max = 4; /* 8 bits */ ++ break; ++ case 0x1: ++ shift_max = 10; /* 16 bits */ ++ break; ++ case 0x2: ++ shift_max = 12; /* 18 bits */ ++ break; ++ case 0x3: ++ shift_max = 15; /* 20 bits */ ++ break; ++ case 0x4: ++ shift_max = 19; /* 24 bits */ ++ break; ++ } ++ ++ vol_scale = 3 * (shift_max + 1); ++ vol_step = 100 / vol_scale; ++ ++ for(index = 0;index <= 100;index += vol_step) ++ if( val <= index ) ++ break; ++ ++ if(index == 0) ++ index = vol_step; ++ index = index / vol_step; ++ if(index > vol_scale) ++ index = vol_scale; ++ ++ index = vol_scale - index; ++ codec_volume = hpvol_shift_table[index].hpvol; ++ codec_volue_shift = hpvol_shift_table[index].shift; ++ codec_volume = 3; ++ REG_ICDC_CDCCR2 = ((REG_ICDC_CDCCR2 & ~(0x3)) | codec_volume); ++} ++ ++void set_jzcodec_mic(int val) ++{ ++ long mic_gain; ++ ++ mic_gain = 31 * val /100; ++ REG_ICDC_CDCCR2 = (REG_ICDC_CDCCR2 | (0x3 << 4)); ++ REG_ICDC_CDCCR2 = ((REG_ICDC_CDCCR2 & ~(0x1f << 16)) | (mic_gain << 16)); ++} ++ ++void set_jzcodec_line(int val) ++{ ++ long line_gain; ++ ++ line_gain = 31 * val /100; ++ REG_ICDC_CDCCR2 = ((REG_ICDC_CDCCR2 & ~(0x1f << 16)) | (line_gain << 16)); ++} ++ ++void HP_turn_on(void) ++{ ++ long val = 0; ++ /* simple and slow anti-pop */ ++ REG_ICDC_CDCCR1 = 0x00037302; ++ mdelay(2); ++ REG_ICDC_CDCCR1 = 0x03006000; ++ mdelay(2); ++ REG_ICDC_CDCCR1 = 0x03002000; ++ ++ val = REG_ICDC_CDCCR2; ++ val &= 0x0000ff00; ++ val |= 0x001f0033; ++ REG_ICDC_CDCCR2 = val; ++} ++ ++void HP_turn_off(void) ++{ ++ mdelay(20); ++ REG_ICDC_CDCCR1 = 0x00033300; ++} ++ ++void in_codec_app1(void) ++{ ++ /* test is OK */ ++ HP_turn_on(); ++} ++ ++void in_codec_app12(void) ++{ ++ REG_ICDC_CDCCR1 = 0x14024300; ++ mdelay(300); ++} ++ ++void resume_jzcodec(void) ++{ ++ REG_ICDC_CDCCR2 = 0x00170800; ++ mdelay(2); ++ REG_ICDC_CDCCR1 = 0x001f2102; ++ mdelay(5); ++ REG_ICDC_CDCCR1 = 0x00033302; ++ mdelay(550); ++ REG_ICDC_CDCCR1 = 0x00033300; ++ REG_ICDC_CDCCR1 = jzcodec_reg[0]; ++ REG_ICDC_CDCCR2 = jzcodec_reg[1]; ++} ++ ++void suspend_jzcodec(void) ++{ ++ ++ jzcodec_reg[0] = REG_ICDC_CDCCR1; ++ jzcodec_reg[1] = REG_ICDC_CDCCR2; ++ ++ REG_ICDC_CDCCR1 = 0x001b2302; ++ mdelay(1); ++ REG_ICDC_CDCCR1 = 0x001b2102; ++} ++ ++static int __init init_jzcodec(void) ++{ ++ set_codec_mode = set_jzcodec_mode; ++ each_time_init_codec = each_time_init_jzcodec; ++ ++ set_codec_startup_param = set_jzcodec_startup_param; ++ set_codec_volume_table = set_jzcodec_volume_table; ++ set_codec_record = set_jzcodec_record; ++ set_codec_replay = set_jzcodec_replay; ++ set_codec_replay_record = set_jzcodec_replay_record; ++ turn_on_codec = turn_on_jzcodec; ++ turn_off_codec = turn_off_jzcodec; ++ set_codec_speed = set_jzcodec_speed; ++ reset_codec = reset_jzcodec; ++ codec_mixer_old_info_id_name = jzcodec_mixer_old_info_id_name; ++ codec_mixer_info_id_name = jzcodec_mixer_info_id_name; ++ set_codec_bass = set_jzcodec_bass; ++ set_codec_volume = set_jzcodec_volume; ++ set_codec_mic = set_jzcodec_mic; ++ set_codec_line = set_jzcodec_line; ++ i2s_resume_codec = resume_jzcodec; ++ i2s_suspend_codec = suspend_jzcodec; ++ set_codec_direct_mode = set_jzcodec_direct_mode; ++ clear_codec_direct_mode = clear_jzcodec_direct_mode; ++ ++ return 0; ++} ++ ++ ++static void __exit cleanup_jzcodec(void) ++{ ++ REG_ICDC_CDCCR1 = 0x001b2302; ++ ++} ++ ++module_init(init_jzcodec); ++module_exit(cleanup_jzcodec); +diff -urN linux-2.6.24.7.old/sound/oss/jzdlv.c linux-2.6.24.7/sound/oss/jzdlv.c +--- linux-2.6.24.7.old/sound/oss/jzdlv.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/oss/jzdlv.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,644 @@ ++/* ++ * linux/drivers/sound/jzcodec.c ++ * ++ * JzSOC internal audio driver. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sound_config.h" ++#include "jzdlv.h" ++ ++#define USE_NONE 1 ++#define USE_MIC 2 ++#define USE_LINEIN 3 ++ ++extern mixer_info info; ++extern _old_mixer_info old_info; ++extern int codec_volue_shift; ++ ++extern void (*set_codec_mode)(void); ++extern void (*each_time_init_codec)(void); ++extern int (*set_codec_startup_param)(void); ++extern void (*set_codec_record)(void); ++extern void (*set_codec_replay)(void); ++extern void (*set_codec_replay_record)(void); ++extern void (*turn_on_codec)(void); ++extern void (*turn_off_codec)(void); ++extern void (*set_codec_speed)(int rate); ++extern void (*reset_codec)(void); ++extern void (*codec_mixer_old_info_id_name)(void); ++extern void (*codec_mixer_info_id_name)(void); ++extern void (*set_codec_bass)(int val); ++extern void (*set_codec_volume)(int val); ++extern void (*set_codec_mic)(int val); ++extern void (*set_codec_line)(int val); ++extern void (*i2s_resume_codec)(void); ++extern void (*i2s_suspend_codec)(void); ++extern void (*set_codec_direct_mode)(void); ++extern void (*clear_codec_direct_mode)(void); ++ ++ ++void set_dlv_mode(void); ++void each_time_init_jzcodec(void); ++int set_dlv_startup_param(void); ++void set_dlvjzcodec_volume_table(void); ++void set_dlv_replay(void); ++void set_dlv_record(void); ++void set_dlv_speed(int rate); ++void reset_dlv(void); ++void jzcodec_mixer_old_info_id_name(void); ++void jzcodec_mixer_info_id_name(void); ++void set_dlv_volume(int val); ++void set_dlv_mic(int val); ++ ++extern int jz_mic_only; ++int read_codec_file(int addr) ++{ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ mdelay(1); ++ return(__icdc_get_value()); ++} ++ ++#if 0 ++void printk_codec_files(void) ++{ ++ int cnt; ++ ++ printk("\n"); ++ ++ printk("REG_CPM_I2SCDR=0x%08x\n",REG_CPM_I2SCDR); ++ printk("REG_CPM_CLKGR=0x%08x\n",REG_CPM_CLKGR); ++ printk("REG_CPM_CPCCR=0x%08x\n",REG_CPM_CPCCR); ++ printk("REG_AIC_FR=0x%08x\n",REG_AIC_FR); ++ printk("REG_AIC_CR=0x%08x\n",REG_AIC_CR); ++ printk("REG_AIC_I2SCR=0x%08x\n",REG_AIC_I2SCR); ++ printk("REG_AIC_SR=0x%08x\n",REG_AIC_SR); ++ printk("REG_ICDC_RGDATA=0x%08x\n",REG_ICDC_RGDATA); ++ ++ for (cnt = 0; cnt <= 27 ; cnt++) { ++ printk(" ( %d : 0x%x ) ",cnt ,read_codec_file(cnt)); ++ } ++ printk("\n"); ++} ++#endif ++ ++void write_codec_file(int addr, int val) ++{ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ __icdc_set_cmd(val); /* write */ ++ mdelay(1); ++ __icdc_set_rgwr(); ++ mdelay(1); ++} ++ ++int write_codec_file_bit(int addr, int bitval, int mask_bit) ++{ ++ int val; ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ mdelay(1); ++ val = __icdc_get_value(); /* read */ ++ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ val &= ~(1 << mask_bit); ++ if (bitval == 1) ++ val |= 1 << mask_bit; ++ ++ __icdc_set_cmd(val); /* write */ ++ mdelay(1); ++ __icdc_set_rgwr(); ++ mdelay(1); ++ ++ while (__icdc_rgwr_ready()); ++ __icdc_set_addr(addr); ++ val = __icdc_get_value(); /* read */ ++ ++ if (((val >> mask_bit) & bitval) == bitval) ++ return 1; ++ else ++ return 0; ++} ++void set_dlv_mode(void) ++{ ++ /*REG_CPM_CPCCR &= ~(1 << 31); ++ REG_CPM_CPCCR &= ~(1 << 30);*/ ++ write_codec_file(0, 0xf); ++ ++ REG_AIC_I2SCR = 0x10; ++ __i2s_internal_codec(); ++ __i2s_as_slave(); ++ __i2s_select_i2s(); ++ __aic_select_i2s(); ++ __aic_reset(); ++ mdelay(10); ++ REG_AIC_I2SCR = 0x10; ++ mdelay(20); ++ ++ /* power on DLV */ ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ mdelay(10); ++} ++void reset_dlv_codec(void) ++{ ++ /* reset DLV codec. from hibernate mode to sleep mode */ ++ write_codec_file(0, 0xf); ++ write_codec_file_bit(6, 0, 0); ++ write_codec_file_bit(6, 0, 1); ++ mdelay(200); ++ //write_codec_file(0, 0xf); ++ write_codec_file_bit(5, 0, 7);//PMR1.SB_DAC->0 ++ write_codec_file_bit(5, 0, 4);//PMR1.SB_ADC->0 ++ mdelay(10);//wait for stability ++} ++ ++void each_time_init_dlv(void) ++{ ++ __i2s_disable(); ++ __i2s_as_slave(); ++ __aic_internal_codec(); ++ __i2s_set_oss_sample_size(16); ++ __i2s_set_iss_sample_size(16); ++} ++ ++int set_dlv_startup_param(void) ++{ ++ __i2s_disable_transmit_intr(); ++ __i2s_disable_receive_intr(); ++ ++ return 1; ++} ++/* set Audio data replay */ ++void set_audio_data_replay(void) ++{ ++ /* DAC path */ ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ mdelay(10); ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ //mdelay(100); ++ //write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //mdelay(300); ++} ++ ++#if 1 /* mask warning */ ++/* set Record MIC input audio without playback */ ++void set_record_mic_input_audio_without_playback(void) ++{ ++ /* ADC path for MIC IN */ ++ jz_mic_only = 1; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ mdelay(10); ++ write_codec_file_bit(1, 1, 2); ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ ++ write_codec_file(22, 0x40);//mic 1 ++ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ //write_codec_file_bit(6, 1, 3);// gain set ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ write_codec_file(1, 0x4); ++} ++#endif ++ ++#if 1 /* mask warning */ ++/* unset Record MIC input audio without playback */ ++void unset_record_mic_input_audio_without_playback(void) ++{ ++ /* ADC path for MIC IN */ ++ jz_mic_only = 0; ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* set Record LINE input audio without playback */ ++void set_record_line_input_audio_without_playback(void) ++{ ++ /* ADC path for LINE IN */ ++ jz_mic_only = 1; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ mdelay(10); ++ write_codec_file(22, 0xf6);//line in 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1 ++ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ mdelay(10); ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ write_codec_file(1, 0x4); ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* unset Record LINE input audio without playback */ ++void unset_record_line_input_audio_without_playback(void) ++{ ++ /* ADC path for LINE IN */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(5, 1, 3);//ONR1.SB_LIN->1 ++ ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* set Playback LINE input audio direct only */ ++void set_playback_line_input_audio_direct_only(void) ++{ ++ jz_audio_reset();//or init_codec() ++ REG_AIC_I2SCR = 0x10; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ mdelay(10); ++ write_codec_file(22, 0xf6);//line in 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ mdelay(10); ++ write_codec_file_bit(1, 1, 2);//CR1.HP_BYPASS->1 ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ //write_codec_file_bit(5, 1, 7);//PMR1.SB_DAC->1 ++ //write_codec_file_bit(5, 1, 4);//PMR1.SB_ADC->1 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* unset Playback LINE input audio direct only */ ++void unset_playback_line_input_audio_direct_only(void) ++{ ++ write_codec_file_bit(6, 0, 3);//GIM->0 ++ write_codec_file_bit(1, 0, 2);//PMR1.BYPASS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LINE->1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ mdelay(100); ++ write_codec_file_bit(5, 1, 5);//PMR1.SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* set Record MIC input audio with direct playback */ ++void set_record_mic_input_audio_with_direct_playback(void) ++{ ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ jz_mic_only = 0; ++ write_codec_file(9, 0xff); ++ write_codec_file(8, 0x3f); ++ mdelay(10); ++ ++ write_codec_file(22, 0x60);//mic 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 ++ write_codec_file_bit(6, 1, 3);// gain set ++ ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ mdelay(100); ++ write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 ++ //write_codec_file(1, 0x4); ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* unset Record MIC input audio with direct playback */ ++void unset_record_mic_input_audio_with_direct_playback(void) ++{ ++ /* ADC path for MIC IN */ ++ jz_mic_only = 0; ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1 ++ write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 ++ write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* set Record playing audio mixed with MIC input audio */ ++void set_record_playing_audio_mixed_with_mic_input_audio(void) ++{ ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ mdelay(10); ++ ++ write_codec_file(22, 0x63);//mic 1 ++ ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(6, 1, 3);// gain set ++ ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 ++ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0 ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ write_codec_file_bit(5, 0, 4);//PMR1.SB_MIX->0 ++} ++#endif ++ ++#if 0 /* mask warning */ ++/* unset Record playing audio mixed with MIC input audio */ ++void unset_record_playing_audio_mixed_with_mic_input_audio(void) ++{ ++ /* ADC path */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 ++ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ //write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 5);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++#if 1 /* mask warning */ ++/* set Record MIC input audio with Audio data replay (full duplex) */ ++void set_record_mic_input_audio_with_audio_data_replay(void) ++{ ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ ++ write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0 ++ write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 ++ ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++} ++#endif ++ ++#if 1 /* mask warning */ ++/* unset Record MIC input audio with Audio data replay (full duplex) */ ++void unset_record_mic_input_audio_with_audio_data_replay(void) ++{ ++ /* ADC path */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 5);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++#if 1 /* mask warning */ ++/* set Record LINE input audio with Audio data replay (full duplex for linein) */ ++void set_record_line_input_audio_with_audio_data_replay(void) ++{ ++ write_codec_file(9, 0xff); ++ //write_codec_file(8, 0x30); ++ write_codec_file(8, 0x20); ++ write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 ++ write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 ++ write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(22, 1, 7);//CR3.SB_MIC->1 ++ write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++ ++ ++ //jz_mic_only = 1; ++ write_codec_file(22, 0xc6);//line in 1 ++ write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 ++ write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 ++ write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 ++} ++#endif ++ ++#if 1 /* mask warning */ ++/* unset Record LINE input audio with Audio data replay (full duplex for linein) */ ++void unset_record_line_input_audio_with_audio_data_replay(void) ++{ ++ /* ADC path */ ++ write_codec_file_bit(5, 1, 4);//SB_ADC->1 ++ write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 ++ //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 ++ write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 5);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++#if 1 ++/* unset Audio data replay */ ++void unset_audio_data_replay(void) ++{ ++ //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 ++ //mdelay(800); ++ //write_codec_file_bit(5, 1, 6);//SB_OUT->1 ++ //mdelay(800); ++ write_codec_file_bit(5, 1, 7);//SB_DAC->1 ++ write_codec_file_bit(5, 1, 4);//SB_MIX->1 ++ write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 ++ write_codec_file_bit(6, 1, 1);//SB->1 ++} ++#endif ++ ++void set_dlv_replay(void) ++{ ++ set_audio_data_replay(); ++} ++ ++void set_dlv_speed(int rate) ++{ ++ int speed = 0, val; ++ speed = 0; ++ switch (rate) { ++ case 8000: ++ speed = 10; ++ break; ++ case 9600: ++ speed = 9; ++ break; ++ case 11025: ++ speed = 8; ++ break; ++ case 12000: ++ speed = 7; ++ break; ++ case 16000: ++ speed = 6; ++ break; ++ case 22050: ++ speed = 5; ++ break; ++ case 24000: ++ speed = 4; ++ break; ++ case 32000: ++ speed = 3; ++ break; ++ case 44100: ++ speed = 2; ++ break; ++ case 48000: ++ speed = 1; ++ break; ++ case 96000: ++ speed = 0; ++ break; ++ default: ++ break; ++ } ++ ++ val = read_codec_file(4); ++ val = (speed << 4) | speed; ++ write_codec_file(4, val); ++} ++ ++void reset_jzcodec(void) ++{ ++ ++} ++ ++void dlv_mixer_old_info_id_name(void) ++{ ++ strncpy(info.id, "JZDLV", sizeof(info.id)); ++ strncpy(info.name,"Jz internal codec dlv on jz4750", sizeof(info.name)); ++} ++ ++void dlv_mixer_info_id_name(void) ++{ ++ strncpy(old_info.id, "JZDLV", sizeof(old_info.id)); ++ strncpy(old_info.name,"Jz internal codec dlv on jz4750", sizeof(old_info.name)); ++} ++ ++void set_dlv_mic(int val) ++{ ++ int cur_vol ; ++ /* set gain */ ++ //write_codec_file_bit(6, 1, 3);//GIM ++ cur_vol = 31 * val / 100; ++ cur_vol |= cur_vol << 4; ++ write_codec_file(19, cur_vol);//GIL,GIR ++} ++ ++void set_dlv_line(int val) ++{ ++ int cur_vol; ++ /* set gain */ ++ cur_vol = 31 * val / 100; ++ cur_vol &= 0x1f; ++ write_codec_file(11, cur_vol);//GO1L ++ write_codec_file(12, cur_vol);//GO1R ++} ++ ++void set_dlv_volume(int val) ++{ ++ unsigned long cur_vol; ++ cur_vol = 31 * (100 - val) / 100; ++ write_codec_file(17, cur_vol | 0xc0); ++ write_codec_file(18, cur_vol); ++} ++ ++static int __init init_dlv(void) ++{ ++ set_codec_mode = set_dlv_mode; ++ each_time_init_codec = each_time_init_dlv; ++ reset_codec = reset_dlv_codec; ++ set_codec_startup_param = set_dlv_startup_param; ++ ++ set_codec_replay = set_dlv_replay; ++ ++ set_codec_speed = set_dlv_speed; ++ ++ codec_mixer_old_info_id_name = dlv_mixer_old_info_id_name; ++ codec_mixer_info_id_name = dlv_mixer_info_id_name; ++ ++ set_codec_volume = set_dlv_volume; ++ set_codec_mic = set_dlv_mic; ++ set_codec_line = set_dlv_line; ++ ++ return 0; ++} ++ ++ ++static void __exit cleanup_dlv(void) ++{ ++ ++} ++ ++module_init(init_dlv); ++module_exit(cleanup_dlv); +diff -urN linux-2.6.24.7.old/sound/oss/jzdlv.h linux-2.6.24.7/sound/oss/jzdlv.h +--- linux-2.6.24.7.old/sound/oss/jzdlv.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/oss/jzdlv.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,21 @@ ++/* header file for dlv */ ++void write_codec_file(int addr, int val); ++int read_codec_file(int addr); ++void printk_codec_files(void); ++int write_codec_file_bit(int addr, int bitval, int mask_bit); ++void set_audio_data_replay(void); ++void unset_audio_data_replay(void); ++void set_record_mic_input_audio_without_playback(void); ++void unset_record_mic_input_audio_without_playback(void); ++void set_record_line_input_audio_without_playback(void); ++void unset_record_line_input_audio_without_playback(void); ++void set_playback_line_input_audio_direct_only(void); ++void unset_playback_line_input_audio_direct_only(void); ++void set_record_mic_input_audio_with_direct_playback(void); ++void unset_record_mic_input_audio_with_direct_playback(void); ++void set_record_playing_audio_mixed_with_mic_input_audio(void); ++void unset_record_playing_audio_mixed_with_mic_input_audio(void); ++void set_record_mic_input_audio_with_audio_data_replay(void); ++void unset_record_mic_input_audio_with_audio_data_replay(void); ++void set_record_line_input_audio_with_audio_data_replay(void); ++void unset_record_line_input_audio_with_audio_data_replay(void); +diff -urN linux-2.6.24.7.old/sound/oss/os.h linux-2.6.24.7/sound/oss/os.h +--- linux-2.6.24.7.old/sound/oss/os.h 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/sound/oss/os.h 2009-04-12 18:13:57.000000000 +0200 +@@ -9,7 +9,6 @@ + #ifdef __KERNEL__ + #include + #include +-#include + #include + #include + #include +diff -urN linux-2.6.24.7.old/sound/soc/Kconfig linux-2.6.24.7/sound/soc/Kconfig +--- linux-2.6.24.7.old/sound/soc/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/sound/soc/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -28,6 +28,7 @@ + source "sound/soc/pxa/Kconfig" + source "sound/soc/s3c24xx/Kconfig" + source "sound/soc/sh/Kconfig" ++source "sound/soc/jz4740/Kconfig" + + # Supported codecs + source "sound/soc/codecs/Kconfig" +diff -urN linux-2.6.24.7.old/sound/soc/Makefile linux-2.6.24.7/sound/soc/Makefile +--- linux-2.6.24.7.old/sound/soc/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/sound/soc/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -1,4 +1,4 @@ + snd-soc-core-objs := soc-core.o soc-dapm.o + + obj-$(CONFIG_SND_SOC) += snd-soc-core.o +-obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ ++obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ jz4740/ +diff -urN linux-2.6.24.7.old/sound/soc/codecs/Kconfig linux-2.6.24.7/sound/soc/codecs/Kconfig +--- linux-2.6.24.7.old/sound/soc/codecs/Kconfig 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/sound/soc/codecs/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -37,3 +37,8 @@ + bool + depends on SND_SOC_CS4270 + ++config SND_SOC_ICODEC ++ tristate "Jz4740 internal codec" ++ depends on SND_SOC && SND_JZ4740_SOC_PAVO && SND_JZ4740_SOC_I2S ++ help ++ Say Y if you want to use internal codec on Ingenic Jz4740 PAVO board. +diff -urN linux-2.6.24.7.old/sound/soc/codecs/Makefile linux-2.6.24.7/sound/soc/codecs/Makefile +--- linux-2.6.24.7.old/sound/soc/codecs/Makefile 2008-05-07 01:22:34.000000000 +0200 ++++ linux-2.6.24.7/sound/soc/codecs/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -4,6 +4,7 @@ + snd-soc-wm8753-objs := wm8753.o + snd-soc-wm9712-objs := wm9712.o + snd-soc-cs4270-objs := cs4270.o ++snd-soc-jzcodec-objs := jzcodec.o + + obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o + obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o +@@ -11,3 +12,4 @@ + obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o + obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o + obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o ++obj-$(CONFIG_SND_SOC_ICODEC) += snd-soc-jzcodec.o +diff -urN linux-2.6.24.7.old/sound/soc/codecs/jzcodec.c linux-2.6.24.7/sound/soc/codecs/jzcodec.c +--- linux-2.6.24.7.old/sound/soc/codecs/jzcodec.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/soc/codecs/jzcodec.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,725 @@ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../jz4740/jz4740-pcm.h" ++#include "jzcodec.h" ++ ++#define AUDIO_NAME "jzcodec" ++#define JZCODEC_VERSION "1.0" ++ ++/* ++ * Debug ++ */ ++ ++#define JZCODEC_DEBUG 0 ++ ++#ifdef JZCODEC_DEBUG ++#define dbg(format, arg...) \ ++ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) do {} while (0) ++#endif ++#define err(format, arg...) \ ++ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) \ ++ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) \ ++ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg) ++ ++struct snd_soc_codec_device soc_codec_dev_jzcodec; ++ ++/* codec private data */ ++struct jzcodec_priv { ++ unsigned int sysclk; ++}; ++ ++/* ++ * jzcodec register cache ++ */ ++static u32 jzcodec_reg[JZCODEC_CACHEREGNUM / 2]; ++ ++/* ++ * codec register is 16 bits width in ALSA, so we define array to store 16 bits configure paras ++ */ ++static u16 jzcodec_reg_LH[JZCODEC_CACHEREGNUM]; ++ ++/* ++ * read jzcodec register cache ++ */ ++static inline unsigned int jzcodec_read_reg_cache(struct snd_soc_codec *codec, ++ unsigned int reg) ++{ ++ u16 *cache = codec->reg_cache; ++ ++ if (reg >= JZCODEC_CACHEREGNUM) ++ return -1; ++ return cache[reg]; ++} ++ ++/* ++ * write jzcodec register cache ++ */ ++static inline void jzcodec_write_reg_cache(struct snd_soc_codec *codec, ++ unsigned int reg, u16 value) ++{ ++ u16 *cache = codec->reg_cache; ++ u32 reg_val; ++ ++ if (reg >= JZCODEC_CACHEREGNUM) { ++ return; ++ } ++ ++ cache[reg] = value; ++ /* update internal codec register value */ ++ switch (reg) { ++ case 0: ++ case 1: ++ reg_val = cache[0] & 0xffff; ++ reg_val = reg_val | (cache[1] << 16); ++ jzcodec_reg[0] = reg_val; ++ break; ++ case 2: ++ case 3: ++ reg_val = cache[2] & 0xffff; ++ reg_val = reg_val | (cache[3] << 16); ++ jzcodec_reg[1] = reg_val; ++ break; ++ } ++} ++ ++/* ++ * write to the jzcodec register space ++ */ ++static int jzcodec_write(struct snd_soc_codec *codec, unsigned int reg, ++ unsigned int value) ++{ ++ jzcodec_write_reg_cache(codec, reg, value); ++ if(codec->hw_write) ++ codec->hw_write(&value, NULL, reg); ++ return 0; ++} ++ ++static int jzcodec_reset(struct snd_soc_codec *codec) ++{ ++ u16 val; ++ ++ val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW); ++ val = val | 0x1; ++ jzcodec_write(codec, ICODEC_1_LOW, val); ++ mdelay(1); ++ ++ val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW); ++ val = val & ~0x1; ++ jzcodec_write(codec, ICODEC_1_LOW, val); ++ mdelay(1); ++ ++ return 0; ++} ++ ++static const struct snd_kcontrol_new jzcodec_snd_controls[] = { ++ ++ //SOC_DOUBLE_R("Master Playback Volume", 1, 1, 0, 3, 0), ++ SOC_DOUBLE_R("Master Playback Volume", ICODEC_2_LOW, ICODEC_2_LOW, 0, 3, 0), ++ //SOC_DOUBLE_R("MICBG", ICODEC_2_LOW, ICODEC_2_LOW, 4, 3, 0), ++ //SOC_DOUBLE_R("Line", 2, 2, 0, 31, 0), ++ SOC_DOUBLE_R("Line", ICODEC_2_HIGH, ICODEC_2_HIGH, 0, 31, 0), ++}; ++ ++/* add non dapm controls */ ++static int jzcodec_add_controls(struct snd_soc_codec *codec) ++{ ++ int err, i; ++ ++ for (i = 0; i < ARRAY_SIZE(jzcodec_snd_controls); i++) { ++ if ((err = snd_ctl_add(codec->card, ++ snd_soc_cnew(&jzcodec_snd_controls[i], codec, NULL))) < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static const struct snd_soc_dapm_widget jzcodec_dapm_widgets[] = { ++ SND_SOC_DAPM_OUTPUT("LOUT"), ++ SND_SOC_DAPM_OUTPUT("LHPOUT"), ++ SND_SOC_DAPM_OUTPUT("ROUT"), ++ SND_SOC_DAPM_OUTPUT("RHPOUT"), ++ SND_SOC_DAPM_INPUT("MICIN"), ++ SND_SOC_DAPM_INPUT("RLINEIN"), ++ SND_SOC_DAPM_INPUT("LLINEIN"), ++}; ++ ++static const char *intercon[][3] = { ++ /* output mixer */ ++ {"Output Mixer", "Line Bypass Switch", "Line Input"}, ++ {"Output Mixer", "HiFi Playback Switch", "DAC"}, ++ {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, ++ ++ /* outputs */ ++ {"RHPOUT", NULL, "Output Mixer"}, ++ {"ROUT", NULL, "Output Mixer"}, ++ {"LHPOUT", NULL, "Output Mixer"}, ++ {"LOUT", NULL, "Output Mixer"}, ++ ++ /* input mux */ ++ {"Input Mux", "Line In", "Line Input"}, ++ {"Input Mux", "Mic", "Mic Bias"}, ++ {"ADC", NULL, "Input Mux"}, ++ ++ /* inputs */ ++ {"Line Input", NULL, "LLINEIN"}, ++ {"Line Input", NULL, "RLINEIN"}, ++ {"Mic Bias", NULL, "MICIN"}, ++ ++ /* terminator */ ++ {NULL, NULL, NULL}, ++}; ++ ++static int jzcodec_add_widgets(struct snd_soc_codec *codec) ++{ ++ int i,cnt; ++ ++ cnt = ARRAY_SIZE(jzcodec_dapm_widgets); ++ for(i = 0; i < ARRAY_SIZE(jzcodec_dapm_widgets); i++) { ++ snd_soc_dapm_new_control(codec, &jzcodec_dapm_widgets[i]); ++ } ++ ++ /* set up audio path interconnects */ ++ for(i = 0; intercon[i][0] != NULL; i++) { ++ snd_soc_dapm_connect_input(codec, intercon[i][0], ++ intercon[i][1], intercon[i][2]); ++ } ++ ++ snd_soc_dapm_new_widgets(codec); ++ return 0; ++} ++ ++static int jzcodec_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_device *socdev = rtd->socdev; ++ struct snd_soc_codec *codec = socdev->codec; ++ u16 reg_val = jzcodec_read_reg_cache(codec, ICODEC_2_LOW); ++ ++ /* bit size. codec side */ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S16_LE: ++ break; ++ } ++ /* sample rate */ ++ reg_val = reg_val & ~(0xf << 8); ++ ++ switch (params_rate(params)) { ++ case 8000: ++ reg_val |= (0x0 << 8); ++ break; ++ case 11025: ++ reg_val |= (0x1 << 8); ++ break; ++ case 12000: ++ reg_val |= (0x2 << 8); ++ break; ++ case 16000: ++ reg_val |= (0x3 << 8); ++ break; ++ case 22050: ++ reg_val |= (0x4 << 8); ++ break; ++ case 24000: ++ reg_val |= (0x5 << 8); ++ break; ++ case 32000: ++ reg_val |= (0x6 << 8); ++ break; ++ case 44100: ++ reg_val |= (0x7 << 8); ++ break; ++ case 48000: ++ reg_val |= (0x8 << 8); ++ break; ++ default: ++ printk(" invalid rate :0x%08x\n",params_rate(params)); ++ } ++ ++ jzcodec_write(codec, ICODEC_2_LOW, reg_val); ++ return 0; ++} ++ ++static int jzcodec_pcm_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ int ret = 0; ++ u16 val; ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_device *socdev = rtd->socdev; ++ struct snd_soc_codec *codec = socdev->codec; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ //case SNDRV_PCM_TRIGGER_RESUME: ++ //case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ val = 0x7302; ++ jzcodec_write(codec, ICODEC_1_LOW, val); ++ val = 0x0003; ++ jzcodec_write(codec, ICODEC_1_HIGH, val); ++ mdelay(2); ++ val = 0x6000; ++ jzcodec_write(codec, ICODEC_1_LOW, val); ++ val = 0x0300; ++ jzcodec_write(codec, ICODEC_1_HIGH, val); ++ mdelay(2); ++ val = 0x2000; ++ jzcodec_write(codec, ICODEC_1_LOW, val); ++ val = 0x0300; ++ jzcodec_write(codec, ICODEC_1_HIGH, val); ++ } else { ++ val = 0x4300; ++ jzcodec_write(codec, ICODEC_1_LOW, val); ++ val = 0x1402; ++ jzcodec_write(codec, ICODEC_1_HIGH, val); ++ } ++ break; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ //case SNDRV_PCM_TRIGGER_SUSPEND: ++ //case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { ++ val = 0x3300; ++ jzcodec_write(codec, ICODEC_1_LOW, val); ++ val = 0x0003; ++ jzcodec_write(codec, ICODEC_1_HIGH, val); ++ } else { ++ val = 0x3300; ++ jzcodec_write(codec, ICODEC_1_LOW, val); ++ val = 0x0003; ++ jzcodec_write(codec, ICODEC_1_HIGH, val); ++ } ++ break; ++ ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int jzcodec_pcm_prepare(struct snd_pcm_substream *substream) ++{ ++ /*struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_device *socdev = rtd->socdev; ++ struct snd_soc_codec *codec = socdev->codec; */ ++ ++ return 0; ++} ++ ++static void jzcodec_shutdown(struct snd_pcm_substream *substream) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_device *socdev = rtd->socdev; ++ struct snd_soc_codec *codec = socdev->codec; ++ ++ /* deactivate */ ++ if (!codec->active) { ++ udelay(50); ++ } ++} ++ ++static int jzcodec_mute(struct snd_soc_codec_dai *dai, int mute) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ u16 reg_val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW); ++ ++ if (mute != 0) ++ mute = 1; ++ if (mute) ++ reg_val = reg_val | (0x1 << 14); ++ else ++ reg_val = reg_val & ~(0x1 << 14); ++ ++ jzcodec_write(codec, ICODEC_1_LOW, reg_val); ++ return 0; ++} ++ ++static int jzcodec_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, ++ int clk_id, unsigned int freq, int dir) ++{ ++ struct snd_soc_codec *codec = codec_dai->codec; ++ struct jzcodec_priv *jzcodec = codec->private_data; ++ ++ jzcodec->sysclk = freq; ++ return 0; ++} ++/* ++ * Set's ADC and Voice DAC format. called by pavo_hw_params() in pavo.c ++ */ ++static int jzcodec_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, ++ unsigned int fmt) ++{ ++ /* struct snd_soc_codec *codec = codec_dai->codec; */ ++ ++ /* set master/slave audio interface. codec side */ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBM_CFM: ++ /* set master mode for codec */ ++ break; ++ case SND_SOC_DAIFMT_CBS_CFS: ++ /* set slave mode for codec */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* interface format . set some parameter for codec side */ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ /* set I2S mode for codec */ ++ break; ++ case SND_SOC_DAIFMT_RIGHT_J: ++ /* set right J mode */ ++ break; ++ case SND_SOC_DAIFMT_LEFT_J: ++ /* set left J mode */ ++ break; ++ case SND_SOC_DAIFMT_DSP_A: ++ /* set dsp A mode */ ++ break; ++ case SND_SOC_DAIFMT_DSP_B: ++ /* set dsp B mode */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* clock inversion. codec side */ ++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_NB_NF: ++ break; ++ case SND_SOC_DAIFMT_IB_IF: ++ break; ++ case SND_SOC_DAIFMT_IB_NF: ++ break; ++ case SND_SOC_DAIFMT_NB_IF: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* jzcodec_write(codec, 0, val); */ ++ return 0; ++} ++ ++static int jzcodec_dapm_event(struct snd_soc_codec *codec, int event) ++{ ++/* u16 reg_val; */ ++ ++ switch (event) { ++ case SNDRV_CTL_POWER_D0: /* full On */ ++ /* vref/mid, osc on, dac unmute */ ++ /* u16 reg_val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW); */ ++ /* jzcodec_write(codec, 0, val); */ ++ break; ++ case SNDRV_CTL_POWER_D1: /* partial On */ ++ case SNDRV_CTL_POWER_D2: /* partial On */ ++ break; ++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */ ++ /* everything off except vref/vmid, */ ++ /*reg_val = 0x0800; ++ jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val); ++ reg_val = 0x0017; ++ jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val); ++ REG_ICDC_CDCCR1 = jzcodec_reg[0]; ++ mdelay(2); ++ reg_val = 0x2102; ++ jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val); ++ reg_val = 0x001f; ++ jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val); ++ REG_ICDC_CDCCR1 = jzcodec_reg[0]; ++ mdelay(2); ++ reg_val = 0x3302; ++ jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val); ++ reg_val = 0x0003; ++ jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val); ++ REG_ICDC_CDCCR1 = jzcodec_reg[0];*/ ++ break; ++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */ ++ /* everything off, dac mute, inactive */ ++ /*reg_val = 0x2302; ++ jzcodec_write(codec, ICODEC_1_LOW, reg_val); ++ reg_val = 0x001b; ++ jzcodec_write(codec, ICODEC_1_HIGH, reg_val); ++ mdelay(1); ++ reg_val = 0x2102; ++ jzcodec_write(codec, ICODEC_1_LOW, reg_val); ++ reg_val = 0x001b; ++ jzcodec_write(codec, ICODEC_1_HIGH, reg_val);*/ ++ break; ++ } ++ codec->dapm_state = event; ++ return 0; ++} ++ ++#define JZCODEC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ ++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ ++ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ ++ SNDRV_PCM_RATE_48000) ++ ++#define JZCODEC_FORMATS (SNDRV_PCM_FORMAT_S8 | SNDRV_PCM_FMTBIT_S16_LE) ++ ++struct snd_soc_codec_dai jzcodec_dai = { ++ .name = "JZCODEC", ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = JZCODEC_RATES, ++ .formats = JZCODEC_FORMATS,}, ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = JZCODEC_RATES, ++ .formats = JZCODEC_FORMATS,}, ++ .ops = { ++ .trigger = jzcodec_pcm_trigger, ++ .prepare = jzcodec_pcm_prepare, ++ .hw_params = jzcodec_hw_params, ++ .shutdown = jzcodec_shutdown, ++ }, ++ .dai_ops = { ++ .digital_mute = jzcodec_mute, ++ .set_sysclk = jzcodec_set_dai_sysclk, ++ .set_fmt = jzcodec_set_dai_fmt, ++ } ++}; ++EXPORT_SYMBOL_GPL(jzcodec_dai); ++ ++#ifdef CONFIG_PM ++static u16 jzcodec_reg_pm[JZCODEC_CACHEREGNUM]; ++static int jzcodec_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct snd_soc_device *socdev = platform_get_drvdata(pdev); ++ struct snd_soc_codec *codec = socdev->codec; ++ ++ jzcodec_reg_pm[ICODEC_1_LOW] = jzcodec_read_reg_cache(codec, ICODEC_1_LOW); ++ jzcodec_reg_pm[ICODEC_1_HIGH] = jzcodec_read_reg_cache(codec, ICODEC_1_HIGH); ++ jzcodec_reg_pm[ICODEC_2_LOW] = jzcodec_read_reg_cache(codec, ICODEC_2_LOW); ++ jzcodec_reg_pm[ICODEC_2_HIGH] = jzcodec_read_reg_cache(codec, ICODEC_2_HIGH); ++ ++ jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3cold); ++ return 0; ++} ++ ++static int jzcodec_resume(struct platform_device *pdev) ++{ ++ struct snd_soc_device *socdev = platform_get_drvdata(pdev); ++ struct snd_soc_codec *codec = socdev->codec; ++ u16 reg_val; ++ ++ jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3hot); ++ reg_val = jzcodec_reg_pm[ICODEC_1_LOW]; ++ jzcodec_write(codec, ICODEC_1_LOW, reg_val); ++ reg_val = jzcodec_reg_pm[ICODEC_1_HIGH]; ++ jzcodec_write(codec, ICODEC_1_HIGH, reg_val); ++ reg_val = jzcodec_reg_pm[ICODEC_2_LOW]; ++ jzcodec_write(codec, ICODEC_2_LOW, reg_val); ++ reg_val = jzcodec_reg_pm[ICODEC_2_HIGH]; ++ jzcodec_write(codec, ICODEC_2_HIGH, reg_val); ++ ++ jzcodec_dapm_event(codec, codec->suspend_dapm_state); ++ return 0; ++} ++#else ++#define jzcodec_suspend NULL ++#define jzcodec_resume NULL ++#endif ++/* ++ * initialise the JZCODEC driver ++ * register the mixer and dsp interfaces with the kernel ++ */ ++static int jzcodec_init(struct snd_soc_device *socdev) ++{ ++ struct snd_soc_codec *codec = socdev->codec; ++ int reg, ret = 0; ++ u16 reg_val; ++ ++ for (reg = 0; reg < JZCODEC_CACHEREGNUM / 2; reg++) { ++ switch (reg) { ++ case 0: ++ jzcodec_reg[reg] = REG_ICDC_CDCCR1; ++ jzcodec_reg_LH[ICODEC_1_LOW] = jzcodec_reg[reg] & 0xffff; ++ jzcodec_reg_LH[ICODEC_1_HIGH] = (jzcodec_reg[reg] & 0xffff0000) >> 16; ++ break; ++ case 1: ++ jzcodec_reg[reg] = REG_ICDC_CDCCR2; ++ jzcodec_reg_LH[ICODEC_2_LOW] = jzcodec_reg[reg] & 0xffff; ++ jzcodec_reg_LH[ICODEC_2_HIGH] = (jzcodec_reg[reg] & 0xffff0000) >> 16; ++ break; ++ } ++ } ++ ++ codec->name = "JZCODEC"; ++ codec->owner = THIS_MODULE; ++ codec->read = jzcodec_read_reg_cache; ++ codec->write = jzcodec_write; ++ codec->dapm_event = jzcodec_dapm_event; ++ codec->dai = &jzcodec_dai; ++ codec->num_dai = 1; ++ codec->reg_cache_size = sizeof(jzcodec_reg_LH); ++ codec->reg_cache = kmemdup(jzcodec_reg_LH, sizeof(jzcodec_reg_LH), GFP_KERNEL); ++ if (codec->reg_cache == NULL) ++ return -ENOMEM; ++ ++ jzcodec_reset(codec); ++ /* register pcms */ ++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); ++ if (ret < 0) { ++ printk(KERN_ERR "jzcodec: failed to create pcms\n"); ++ goto pcm_err; ++ } ++ ++ /* power on device */ ++ jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3hot); ++ ++ /* clear suspend bit of jz4740 internal codec */ ++ reg_val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW); ++ reg_val = reg_val & ~(0x2); ++ jzcodec_write(codec, ICODEC_1_LOW, reg_val); ++ /* set vol bits */ ++ reg_val = jzcodec_read_reg_cache(codec, ICODEC_2_LOW); ++ reg_val = reg_val | 0x3; ++ jzcodec_write(codec, ICODEC_2_LOW, reg_val); ++ /* set line in capture gain bits */ ++ reg_val = jzcodec_read_reg_cache(codec, ICODEC_2_HIGH); ++ reg_val = reg_val | 0x1f; ++ jzcodec_write(codec, ICODEC_2_HIGH, reg_val); ++ /* set mic boost gain bits */ ++ reg_val = jzcodec_read_reg_cache(codec, ICODEC_2_LOW); ++ reg_val = reg_val | (0x3 << 4); ++ jzcodec_write(codec, ICODEC_2_LOW, reg_val); ++ mdelay(5); ++ reg_val = 0x3300; ++ jzcodec_write(codec, ICODEC_1_LOW, reg_val); ++ reg_val = 0x0003; ++ jzcodec_write(codec, ICODEC_1_HIGH, reg_val); ++ jzcodec_add_controls(codec); ++ jzcodec_add_widgets(codec); ++ ++ ret = snd_soc_register_card(socdev); ++ if (ret < 0) { ++ printk(KERN_ERR "jzcodec: failed to register card\n"); ++ goto card_err; ++ } ++ return ret; ++ ++card_err: ++ snd_soc_free_pcms(socdev); ++ snd_soc_dapm_free(socdev); ++pcm_err: ++ kfree(codec->reg_cache); ++ return ret; ++} ++ ++static struct snd_soc_device *jzcodec_socdev; ++ ++static int write_codec_reg(u16 * add, char * name, int reg) ++{ ++ switch (reg) { ++ case 0: ++ case 1: ++ REG_ICDC_CDCCR1 = jzcodec_reg[0]; ++ break; ++ case 2: ++ case 3: ++ REG_ICDC_CDCCR2 = jzcodec_reg[1]; ++ break; ++ } ++ return 0; ++} ++ ++static int jzcodec_probe(struct platform_device *pdev) ++{ ++ struct snd_soc_device *socdev = platform_get_drvdata(pdev); ++ struct snd_soc_codec *codec; ++ struct jzcodec_priv *jzcodec; ++ int ret = 0; ++ ++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); ++ if (codec == NULL) ++ return -ENOMEM; ++ ++ jzcodec = kzalloc(sizeof(struct jzcodec_priv), GFP_KERNEL); ++ if (jzcodec == NULL) { ++ kfree(codec); ++ return -ENOMEM; ++ } ++ ++ codec->private_data = jzcodec; ++ socdev->codec = codec; ++ mutex_init(&codec->mutex); ++ INIT_LIST_HEAD(&codec->dapm_widgets); ++ INIT_LIST_HEAD(&codec->dapm_paths); ++ ++ jzcodec_socdev = socdev; ++ ++ /* Add other interfaces here ,no I2C connection */ ++ codec->hw_write = (hw_write_t)write_codec_reg; ++ ret = jzcodec_init(jzcodec_socdev); ++ ++ if (ret < 0) { ++ codec = jzcodec_socdev->codec; ++ err("failed to initialise jzcodec\n"); ++ kfree(codec); ++ } ++ ++ return ret; ++} ++ ++/* power down chip */ ++static int jzcodec_remove(struct platform_device *pdev) ++{ ++ struct snd_soc_device *socdev = platform_get_drvdata(pdev); ++ struct snd_soc_codec *codec = socdev->codec; ++ ++ if (codec->control_data) ++ jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3cold); ++ ++ snd_soc_free_pcms(socdev); ++ snd_soc_dapm_free(socdev); ++ kfree(codec->private_data); ++ kfree(codec); ++ ++ return 0; ++} ++ ++struct snd_soc_codec_device soc_codec_dev_jzcodec = { ++ .probe = jzcodec_probe, ++ .remove = jzcodec_remove, ++ .suspend = jzcodec_suspend, ++ .resume = jzcodec_resume, ++}; ++ ++EXPORT_SYMBOL_GPL(soc_codec_dev_jzcodec); ++ ++MODULE_DESCRIPTION("ASoC JZCODEC driver"); ++MODULE_AUTHOR("Richard"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/sound/soc/codecs/jzcodec.h linux-2.6.24.7/sound/soc/codecs/jzcodec.h +--- linux-2.6.24.7.old/sound/soc/codecs/jzcodec.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/soc/codecs/jzcodec.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,22 @@ ++/* ++ * 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 _ICODEC_H ++#define _ICODEC_H ++ ++/* jzcodec register space */ ++#define ICODEC_1_LOW 0x00 /* bit0 -- bit15 in CDCCR1 */ ++#define ICODEC_1_HIGH 0x01 /* bit16 -- bit31 in CDCCR1 */ ++#define ICODEC_2_LOW 0x02 /* bit0 -- bit16 in CDCCR2 */ ++#define ICODEC_2_HIGH 0x03 /* bit16 -- bit31 in CDCCR2 */ ++ ++#define JZCODEC_CACHEREGNUM 4 ++#define JZCODEC_SYSCLK 0 ++ ++extern struct snd_soc_codec_dai jzcodec_dai; ++extern struct snd_soc_codec_device soc_codec_dev_jzcodec; ++ ++#endif +diff -urN linux-2.6.24.7.old/sound/soc/jz4740/Kconfig linux-2.6.24.7/sound/soc/jz4740/Kconfig +--- linux-2.6.24.7.old/sound/soc/jz4740/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/soc/jz4740/Kconfig 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,34 @@ ++config SND_JZ4740_SOC ++ tristate "SoC Audio for Ingenic jz4740 chip" ++ depends on (JZ4740_PAVO || JZ4725_DIPPER || JZ4720_VIRGO) && SND_SOC ++ help ++ Say Y or M if you want to add support for codecs attached to ++ the Jz4740 AC97, I2S or SSP interface. You will also need ++ to select the audio interfaces to support below. ++ ++config SND_JZ4740_SOC_PAVO ++ tristate "SoC Audio support for Ingenic Jz4740 PAVO board" ++ depends on SND_JZ4740_SOC ++ help ++ Say Y if you want to add support for SoC audio of internal codec on Ingenic Jz4740 PAVO board. ++ ++config SND_JZ4740_AC97 ++ tristate "select AC97 protocol and AC97 codec pcm core support" ++ depends on SND_JZ4740_SOC && SND_JZ4740_SOC_PAVO ++ select SND_AC97_CODEC ++ help ++ Say Y if you want to add AC97 protocol support for pcm core. ++ ++config SND_JZ4740_SOC_AC97 ++ tristate "SoC Audio (AC97 protocol) for Ingenic jz4740 chip" ++ depends on SND_JZ4740_SOC && SND_JZ4740_AC97 && SND_JZ4740_SOC_PAVO ++ select AC97_BUS ++ select SND_SOC_AC97_BUS ++ help ++ Say Y if you want to use AC97 protocol and ac97 codec on Ingenic Jz4740 PAVO board. ++ ++config SND_JZ4740_SOC_I2S ++ depends on SND_JZ4740_SOC && SND_JZ4740_SOC_PAVO ++ tristate "SoC Audio (I2S protocol) for Ingenic jz4740 chip" ++ help ++ Say Y if you want to use I2S protocol and I2S codec on Ingenic Jz4740 PAVO board. +diff -urN linux-2.6.24.7.old/sound/soc/jz4740/Makefile linux-2.6.24.7/sound/soc/jz4740/Makefile +--- linux-2.6.24.7.old/sound/soc/jz4740/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/soc/jz4740/Makefile 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,15 @@ ++# ++# Jz4740 Platform Support ++# ++snd-soc-jz4740-objs := jz4740-pcm.o ++snd-soc-jz4740-ac97-objs := jz4740-ac97.o ++snd-soc-jz4740-i2s-objs := jz4740-i2s.o ++ ++obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o ++obj-$(CONFIG_SND_JZ4740_SOC_AC97) += snd-soc-jz4740-ac97.o ++obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o ++ ++# Jz4740 Machine Support ++snd-soc-pavo-objs := pavo.o ++ ++obj-$(CONFIG_SND_JZ4740_SOC_PAVO) += snd-soc-pavo.o +diff -urN linux-2.6.24.7.old/sound/soc/jz4740/jz4740-ac97.c linux-2.6.24.7/sound/soc/jz4740/jz4740-ac97.c +--- linux-2.6.24.7.old/sound/soc/jz4740/jz4740-ac97.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/soc/jz4740/jz4740-ac97.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,261 @@ ++/* ++ * linux/sound/jz4740-ac97.c -- AC97 support for the Ingenic jz4740 chip. ++ * ++ * Author: Richard ++ * Created: Dec 02, 2007 ++ * Copyright: Ingenic Semiconductor Inc. ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "jz4740-pcm.h" ++#include "jz4740-ac97.h" ++ ++static DEFINE_MUTEX(car_mutex); ++static DECLARE_WAIT_QUEUE_HEAD(gsr_wq); ++static volatile long gsr_bits; ++ ++static unsigned short jz4740_ac97_read(struct snd_ac97 *ac97, ++ unsigned short reg) ++{ ++ unsigned short val = -1; ++ volatile u32 *reg_addr; ++ ++ mutex_lock(&car_mutex); ++ ++out: mutex_unlock(&car_mutex); ++ return val; ++} ++ ++static void jz4740_ac97_write(struct snd_ac97 *ac97, unsigned short reg, ++ unsigned short val) ++{ ++ volatile u32 *reg_addr; ++ ++ mutex_lock(&car_mutex); ++ ++ mutex_unlock(&car_mutex); ++} ++ ++static void jz4740_ac97_warm_reset(struct snd_ac97 *ac97) ++{ ++ gsr_bits = 0; ++} ++ ++static void jz4740_ac97_cold_reset(struct snd_ac97 *ac97) ++{ ++} ++ ++static irqreturn_t jz4740_ac97_irq(int irq, void *dev_id) ++{ ++ long status; ++ return IRQ_NONE; ++} ++ ++struct snd_ac97_bus_ops soc_ac97_ops = { ++ .read = jz4740_ac97_read, ++ .write = jz4740_ac97_write, ++ .warm_reset = jz4740_ac97_warm_reset, ++ .reset = jz4740_ac97_cold_reset, ++}; ++ ++static struct jz4740_pcm_dma_params jz4740_ac97_pcm_stereo_out = { ++ .name = "AC97 PCM Stereo out", ++ .dev_addr = __PREG(PCDR), ++ .drcmr = &DRCMRTXPCDR, ++ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | ++ DCMD_BURST32 | DCMD_WIDTH4, ++}; ++ ++static struct jz4740_pcm_dma_params jz4740_ac97_pcm_stereo_in = { ++ .name = "AC97 PCM Stereo in", ++ .dev_addr = __PREG(PCDR), ++ .drcmr = &DRCMRRXPCDR, ++ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | ++ DCMD_BURST32 | DCMD_WIDTH4, ++}; ++ ++static struct jz4740_pcm_dma_params jz4740_ac97_pcm_aux_mono_out = { ++ .name = "AC97 Aux PCM (Slot 5) Mono out", ++ .dev_addr = __PREG(MODR), ++ .drcmr = &DRCMRTXMODR, ++ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | ++ DCMD_BURST16 | DCMD_WIDTH2, ++}; ++ ++static struct jz4740_pcm_dma_params jz4740_ac97_pcm_aux_mono_in = { ++ .name = "AC97 Aux PCM (Slot 5) Mono in", ++ .dev_addr = __PREG(MODR), ++ .drcmr = &DRCMRRXMODR, ++ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | ++ DCMD_BURST16 | DCMD_WIDTH2, ++}; ++ ++static struct jz4740_pcm_dma_params jz4740_ac97_pcm_mic_mono_in = { ++ .name = "AC97 Mic PCM (Slot 6) Mono in", ++ .dev_addr = __PREG(MCDR), ++ .drcmr = &DRCMRRXMCDR, ++ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | ++ DCMD_BURST16 | DCMD_WIDTH2, ++}; ++ ++#ifdef CONFIG_PM ++static int jz4740_ac97_suspend(struct platform_device *pdev, ++ struct snd_soc_cpu_dai *dai) ++{ ++ return 0; ++} ++ ++static int jz4740_ac97_resume(struct platform_device *pdev, ++ struct snd_soc_cpu_dai *dai) ++{ ++ return 0; ++} ++ ++#else ++#define jz4740_ac97_suspend NULL ++#define jz4740_ac97_resume NULL ++#endif ++ ++static int jz4740_ac97_probe(struct platform_device *pdev) ++{ ++ int ret; ++ ++ return 0; ++} ++ ++static void jz4740_ac97_remove(struct platform_device *pdev) ++{ ++} ++ ++static int jz4740_ac97_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ cpu_dai->dma_data = &jz4740_ac97_pcm_stereo_out; ++ else ++ cpu_dai->dma_data = &jz4740_ac97_pcm_stereo_in; ++ ++ return 0; ++} ++ ++static int jz4740_ac97_hw_aux_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ cpu_dai->dma_data = &jz4740_ac97_pcm_aux_mono_out; ++ else ++ cpu_dai->dma_data = &jz4740_ac97_pcm_aux_mono_in; ++ ++ return 0; ++} ++ ++static int jz4740_ac97_hw_mic_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ return -ENODEV; ++ else ++ cpu_dai->dma_data = &jz4740_ac97_pcm_mic_mono_in; ++ ++ return 0; ++} ++ ++#define JZ4740_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ ++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ ++ SNDRV_PCM_RATE_48000) ++ ++struct snd_soc_cpu_dai jz4740_ac97_dai[] = { ++{ ++ .name = "jz4740-ac97", ++ .id = 0, ++ .type = SND_SOC_DAI_AC97, ++ .probe = jz4740_ac97_probe, ++ .remove = jz4740_ac97_remove, ++ .suspend = jz4740_ac97_suspend, ++ .resume = jz4740_ac97_resume, ++ .playback = { ++ .stream_name = "AC97 Playback", ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = JZ4740_AC97_RATES, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE,}, ++ .capture = { ++ .stream_name = "AC97 Capture", ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = JZ4740_AC97_RATES, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE,}, ++ .ops = { ++ .hw_params = jz4740_ac97_hw_params,}, ++}, ++{ ++ .name = "jz4740-ac97-aux", ++ .id = 1, ++ .type = SND_SOC_DAI_AC97, ++ .playback = { ++ .stream_name = "AC97 Aux Playback", ++ .channels_min = 1, ++ .channels_max = 1, ++ .rates = JZ4740_AC97_RATES, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE,}, ++ .capture = { ++ .stream_name = "AC97 Aux Capture", ++ .channels_min = 1, ++ .channels_max = 1, ++ .rates = JZ4740_AC97_RATES, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE,}, ++ .ops = { ++ .hw_params = jz4740_ac97_hw_aux_params,}, ++}, ++{ ++ .name = "jz4740-ac97-mic", ++ .id = 2, ++ .type = SND_SOC_DAI_AC97, ++ .capture = { ++ .stream_name = "AC97 Mic Capture", ++ .channels_min = 1, ++ .channels_max = 1, ++ .rates = JZ4740_AC97_RATES, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE,}, ++ .ops = { ++ .hw_params = jz4740_ac97_hw_mic_params,}, ++}, ++}; ++ ++EXPORT_SYMBOL_GPL(jz4740_ac97_dai); ++EXPORT_SYMBOL_GPL(soc_ac97_ops); ++ ++MODULE_AUTHOR("Richard"); ++MODULE_DESCRIPTION("AC97 driver for the Ingenic jz4740 chip"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/sound/soc/jz4740/jz4740-ac97.h linux-2.6.24.7/sound/soc/jz4740/jz4740-ac97.h +--- linux-2.6.24.7.old/sound/soc/jz4740/jz4740-ac97.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/soc/jz4740/jz4740-ac97.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,21 @@ ++/* ++ * linux/sound/soc/jz4740/jz4740-ac97.h ++ * ++ * 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 _JZ4740_AC97_H ++#define _JZ4740_AC97_H ++ ++#define JZ4740_DAI_AC97_HIFI 0 ++#define JZ4740_DAI_AC97_AUX 1 ++#define JZ4740_DAI_AC97_MIC 2 ++ ++extern struct snd_soc_cpu_dai jz4740_ac97_dai[3]; ++ ++/* platform data */ ++extern struct snd_ac97_bus_ops jz4740_ac97_ops; ++ ++#endif +diff -urN linux-2.6.24.7.old/sound/soc/jz4740/jz4740-i2s.c linux-2.6.24.7/sound/soc/jz4740/jz4740-i2s.c +--- linux-2.6.24.7.old/sound/soc/jz4740/jz4740-i2s.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/soc/jz4740/jz4740-i2s.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,296 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "jz4740-pcm.h" ++#include "jz4740-i2s.h" ++ ++static struct jz4740_dma_client jz4740_dma_client_out = { ++ .name = "I2S PCM Stereo out" ++}; ++ ++static struct jz4740_dma_client jz4740_dma_client_in = { ++ .name = "I2S PCM Stereo in" ++}; ++ ++static struct jz4740_pcm_dma_params jz4740_i2s_pcm_stereo_out = { ++ .client = &jz4740_dma_client_out, ++ .channel = DMA_ID_AIC_TX, ++ .dma_addr = AIC_DR, ++ .dma_size = 2, ++}; ++ ++static struct jz4740_pcm_dma_params jz4740_i2s_pcm_stereo_in = { ++ .client = &jz4740_dma_client_in, ++ .channel = DMA_ID_AIC_RX, ++ .dma_addr = AIC_DR, ++ .dma_size = 2, ++}; ++ ++static int jz4740_i2s_startup(struct snd_pcm_substream *substream) ++{ ++ /*struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;*/ ++ ++ return 0; ++} ++ ++static int jz4740_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai, ++ unsigned int fmt) ++{ ++ /* interface format */ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ /* 1 : ac97 , 0 : i2s */ ++ break; ++ case SND_SOC_DAIFMT_LEFT_J: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBS_CFS: ++ /* 0 : slave */ ++ break; ++ case SND_SOC_DAIFMT_CBM_CFS: ++ /* 1 : master */ ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++/* ++* Set Jz4740 Clock source ++*/ ++static int jz4740_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai, ++ int clk_id, unsigned int freq, int dir) ++{ ++ return 0; ++} ++ ++static void jz4740_snd_tx_ctrl(int on) ++{ ++ if (on) { ++ /* enable replay */ ++ __i2s_enable_transmit_dma(); ++ __i2s_enable_replay(); ++ __i2s_enable(); ++ ++ } else { ++ /* disable replay & capture */ ++ __i2s_disable_replay(); ++ __i2s_disable_record(); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_transmit_dma(); ++ __i2s_disable(); ++ } ++} ++ ++static void jz4740_snd_rx_ctrl(int on) ++{ ++ if (on) { ++ /* enable capture */ ++ __i2s_enable_receive_dma(); ++ __i2s_enable_record(); ++ __i2s_enable(); ++ ++ } else { ++ /* disable replay & capture */ ++ __i2s_disable_replay(); ++ __i2s_disable_record(); ++ __i2s_disable_receive_dma(); ++ __i2s_disable_transmit_dma(); ++ __i2s_disable(); ++ } ++} ++ ++static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; ++ int channels = params_channels(params); ++ ++ jz4740_snd_rx_ctrl(0); ++ jz4740_snd_rx_ctrl(0); ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ cpu_dai->dma_data = &jz4740_i2s_pcm_stereo_out; ++ if (channels == 1) ++ __aic_enable_mono2stereo(); ++ else ++ __aic_disable_mono2stereo(); ++ } else ++ cpu_dai->dma_data = &jz4740_i2s_pcm_stereo_in; ++ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S8: ++ __i2s_set_transmit_trigger(4); ++ __i2s_set_receive_trigger(3); ++ __i2s_set_oss_sample_size(8); ++ __i2s_set_iss_sample_size(8); ++ break; ++ case SNDRV_PCM_FORMAT_S16_LE: ++ /* playback sample:16 bits, burst:16 bytes */ ++ __i2s_set_transmit_trigger(4); ++ /* capture sample:16 bits, burst:16 bytes */ ++ __i2s_set_receive_trigger(3); ++ __i2s_set_oss_sample_size(16); ++ __i2s_set_iss_sample_size(16); ++ break; ++ } ++ ++ return 0; ++} ++ ++static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ int ret = 0; ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ jz4740_snd_rx_ctrl(1); ++ else ++ jz4740_snd_tx_ctrl(1); ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ jz4740_snd_rx_ctrl(0); ++ else ++ jz4740_snd_tx_ctrl(0); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream) ++{ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ } else { ++ } ++ ++ return; ++} ++ ++static int jz4740_i2s_probe(struct platform_device *pdev) ++{ ++ __i2s_internal_codec(); ++ __i2s_as_slave(); ++ __i2s_select_i2s(); ++ __aic_select_i2s(); ++ mdelay(2); ++ ++ __i2s_disable(); ++ __i2s_reset(); ++ mdelay(2); ++ ++ __i2s_disable(); ++ __i2s_internal_codec(); ++ __i2s_as_slave(); ++ __i2s_select_i2s(); ++ __aic_select_i2s(); ++ __i2s_set_oss_sample_size(16); ++ __i2s_set_iss_sample_size(16); ++ __aic_play_lastsample(); ++ ++ __i2s_disable_record(); ++ __i2s_disable_replay(); ++ __i2s_disable_loopback(); ++ __i2s_set_transmit_trigger(7); ++ __i2s_set_receive_trigger(7); ++ ++ jz4740_snd_tx_ctrl(0); ++ jz4740_snd_rx_ctrl(0); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int jz4740_i2s_suspend(struct platform_device *dev, ++ struct snd_soc_cpu_dai *dai) ++{ ++ if (!dai->active) ++ return 0; ++ ++ return 0; ++} ++ ++static int jz4740_i2s_resume(struct platform_device *pdev, ++ struct snd_soc_cpu_dai *dai) ++{ ++ if (!dai->active) ++ return 0; ++ ++ return 0; ++} ++ ++#else ++#define jz4740_i2s_suspend NULL ++#define jz4740_i2s_resume NULL ++#endif ++ ++#define JZ4740_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ ++ SNDRV_PCM_RATE_12000 | SNDRV_PCM_RATE_16000 |\ ++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_24000 |\ ++ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ ++ SNDRV_PCM_RATE_48000) ++ ++struct snd_soc_cpu_dai jz4740_i2s_dai = { ++ .name = "jz4740-i2s", ++ .id = 0, ++ .type = SND_SOC_DAI_I2S, ++ .probe = jz4740_i2s_probe, ++ .suspend = jz4740_i2s_suspend, ++ .resume = jz4740_i2s_resume, ++ .playback = { ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = JZ4740_I2S_RATES, ++ .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,}, ++ .capture = { ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = JZ4740_I2S_RATES, ++ .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,}, ++ .ops = { ++ .startup = jz4740_i2s_startup, ++ .shutdown = jz4740_i2s_shutdown, ++ .trigger = jz4740_i2s_trigger, ++ .hw_params = jz4740_i2s_hw_params,}, ++ .dai_ops = { ++ .set_fmt = jz4740_i2s_set_dai_fmt, ++ .set_sysclk = jz4740_i2s_set_dai_sysclk, ++ }, ++}; ++ ++EXPORT_SYMBOL_GPL(jz4740_i2s_dai); ++ ++/* Module information */ ++MODULE_AUTHOR("Richard, cjfeng@ingenic.cn, www.ingenic.cn"); ++MODULE_DESCRIPTION("jz4740 I2S SoC Interface"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/sound/soc/jz4740/jz4740-i2s.h linux-2.6.24.7/sound/soc/jz4740/jz4740-i2s.h +--- linux-2.6.24.7.old/sound/soc/jz4740/jz4740-i2s.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/soc/jz4740/jz4740-i2s.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,18 @@ ++/* ++ * 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 _JZ4740_I2S_H ++#define _JZ4740_I2S_H ++ ++/* jz4740 DAI ID's */ ++#define JZ4740_DAI_I2S 0 ++ ++/* I2S clock */ ++#define JZ4740_I2S_SYSCLK 0 ++ ++extern struct snd_soc_cpu_dai jz4740_i2s_dai; ++ ++#endif +diff -urN linux-2.6.24.7.old/sound/soc/jz4740/jz4740-pcm.c linux-2.6.24.7/sound/soc/jz4740/jz4740-pcm.c +--- linux-2.6.24.7.old/sound/soc/jz4740/jz4740-pcm.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/soc/jz4740/jz4740-pcm.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,689 @@ ++/* ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "jz4740-pcm.h" ++ ++static long sum_bytes = 0; ++static int first_transfer = 0; ++static int printk_flag = 0; ++static int tran_bit = 0; ++#ifdef CONFIG_SND_OSSEMUL ++static int hw_params_cnt = 0; ++#endif ++ ++struct jz4740_dma_buf_aic { ++ struct jz4740_dma_buf_aic *next; ++ int size; /* buffer size in bytes */ ++ dma_addr_t data; /* start of DMA data */ ++ dma_addr_t ptr; /* where the DMA got to [1] */ ++ void *id; /* client's id */ ++}; ++ ++struct jz4740_runtime_data { ++ spinlock_t lock; ++ int state; ++ int aic_dma_flag; /* start dma transfer or not */ ++ unsigned int dma_loaded; ++ unsigned int dma_limit; ++ unsigned int dma_period; ++ dma_addr_t dma_start; ++ dma_addr_t dma_pos; ++ dma_addr_t dma_end; ++ struct jz4740_pcm_dma_params *params; ++ ++ dma_addr_t user_cur_addr; /* user current write buffer start address */ ++ unsigned int user_cur_len; /* user current write buffer length */ ++ ++ /* buffer list and information */ ++ struct jz4740_dma_buf_aic *curr; /* current dma buffer */ ++ struct jz4740_dma_buf_aic *next; /* next buffer to load */ ++ struct jz4740_dma_buf_aic *end; /* end of queue */ ++ ++}; ++ ++/* identify hardware playback capabilities */ ++static const struct snd_pcm_hardware jz4740_pcm_hardware = { ++ .info = SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_MMAP_VALID | ++ SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_U16_LE | ++ SNDRV_PCM_FMTBIT_U8 | ++ SNDRV_PCM_FMTBIT_S8, ++ .rates = SNDRV_PCM_RATE_8000_48000/*0x3fe*/, ++ .rate_min = 8000, ++ .rate_min = 48000, ++ .channels_min = 1,//2 ++ .channels_max = 2, ++ .buffer_bytes_max = 128 * 1024,//16 * 1024 ++ .period_bytes_min = PAGE_SIZE, ++ .period_bytes_max = PAGE_SIZE * 2, ++ .periods_min = 2, ++ .periods_max = 128,//16, ++ .fifo_size = 32, ++}; ++ ++/* jz4740__dma_buf_enqueue ++ * ++ * queue an given buffer for dma transfer. ++ * ++ * data the physical address of the buffer data ++ * size the size of the buffer in bytes ++ * ++*/ ++static int jz4740_dma_buf_enqueue(struct jz4740_runtime_data *prtd, dma_addr_t data, int size) ++{ ++ struct jz4740_dma_buf_aic *aic_buf; ++ ++ aic_buf = kzalloc(sizeof(struct jz4740_dma_buf_aic), GFP_KERNEL); ++ if (aic_buf == NULL) { ++ printk("aic buffer allocate failed,no memory!\n"); ++ return -ENOMEM; ++ } ++ aic_buf->next = NULL; ++ aic_buf->data = aic_buf->ptr = data; ++ aic_buf->size = size; ++ if( prtd->curr == NULL) { ++ prtd->curr = aic_buf; ++ prtd->end = aic_buf; ++ prtd->next = NULL; ++ } else { ++ if (prtd->end == NULL) ++ printk("prtd->end is NULL\n"); ++ prtd->end->next = aic_buf; ++ prtd->end = aic_buf; ++ } ++ ++ /* if necessary, update the next buffer field */ ++ if (prtd->next == NULL) ++ prtd->next = aic_buf; ++ ++ return 0; ++} ++ ++ ++void audio_start_dma(struct jz4740_runtime_data *prtd, int mode) ++{ ++ unsigned long flags; ++ struct jz4740_dma_buf_aic *aic_buf; ++ int channel; ++ ++ switch (mode) { ++ case DMA_MODE_WRITE: ++ /* free cur aic_buf */ ++ if (first_transfer == 1) { ++ first_transfer = 0; ++ } else { ++ aic_buf = prtd->curr; ++ if (aic_buf != NULL) { ++ prtd->curr = aic_buf->next; ++ prtd->next = aic_buf->next; ++ aic_buf->next = NULL; ++ kfree(aic_buf); ++ aic_buf = NULL; ++ } ++ } ++ ++ aic_buf = prtd->next; ++ channel = prtd->params->channel; ++ if (aic_buf) { ++ flags = claim_dma_lock(); ++ disable_dma(channel); ++ jz_set_alsa_dma(channel, mode, tran_bit); ++ set_dma_addr(channel, aic_buf->data); ++ set_dma_count(channel, aic_buf->size); ++ enable_dma(channel); ++ release_dma_lock(flags); ++ prtd->aic_dma_flag |= AIC_START_DMA; ++ } else { ++ printk("next buffer is NULL for playback\n"); ++ prtd->aic_dma_flag &= ~AIC_START_DMA; ++ return; ++ } ++ break; ++ case DMA_MODE_READ: ++ /* free cur aic_buf */ ++ if (first_transfer == 1) { ++ first_transfer = 0; ++ } else { ++ aic_buf = prtd->curr; ++ if (aic_buf != NULL) { ++ prtd->curr = aic_buf->next; ++ prtd->next = aic_buf->next; ++ aic_buf->next = NULL; ++ kfree(aic_buf); ++ aic_buf = NULL; ++ } ++ } ++ ++ aic_buf = prtd->next; ++ channel = prtd->params->channel; ++ ++ if (aic_buf) { ++ flags = claim_dma_lock(); ++ disable_dma(channel); ++ jz_set_alsa_dma(channel, mode, tran_bit); ++ set_dma_addr(channel, aic_buf->data); ++ set_dma_count(channel, aic_buf->size); ++ enable_dma(channel); ++ release_dma_lock(flags); ++ prtd->aic_dma_flag |= AIC_START_DMA; ++ } else { ++ printk("next buffer is NULL for capture\n"); ++ prtd->aic_dma_flag &= ~AIC_START_DMA; ++ return; ++ } ++ break; ++ } ++ /* dump_jz_dma_channel(channel); */ ++} ++ ++/* ++ * place a dma buffer onto the queue for the dma system to handle. ++*/ ++static void jz4740_pcm_enqueue(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct jz4740_runtime_data *prtd = runtime->private_data; ++ /*struct snd_dma_buffer *buf = &substream->dma_buffer;*/ ++ dma_addr_t pos = prtd->dma_pos; ++ int ret; ++ ++ while (prtd->dma_loaded < prtd->dma_limit) { ++ unsigned long len = prtd->dma_period; ++ ++ if ((pos + len) > prtd->dma_end) { ++ len = prtd->dma_end - pos; ++ } ++ ret = jz4740_dma_buf_enqueue(prtd, pos, len); ++ if (ret == 0) { ++ prtd->dma_loaded++; ++ pos += prtd->dma_period; ++ if (pos >= prtd->dma_end) ++ pos = prtd->dma_start; ++ } else ++ break; ++ } ++ ++ prtd->dma_pos = pos; ++} ++ ++/* ++ * call the function:jz4740_pcm_dma_irq() after DMA has transfered the current buffer ++ */ ++static irqreturn_t jz4740_pcm_dma_irq(int dma_ch, void *dev_id) ++{ ++ struct snd_pcm_substream *substream = dev_id; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct jz4740_runtime_data *prtd = runtime->private_data; ++ /*struct jz4740_dma_buf_aic *aic_buf = prtd->curr;*/ ++ int channel = prtd->params->channel; ++ unsigned long flags; ++ ++ disable_dma(channel); ++ prtd->aic_dma_flag &= ~AIC_START_DMA; ++ /* must clear TT bit in DCCSR to avoid interrupt again */ ++ if (__dmac_channel_transmit_end_detected(channel)) { ++ __dmac_channel_clear_transmit_end(channel); ++ } ++ if (__dmac_channel_transmit_halt_detected(channel)) { ++ __dmac_channel_clear_transmit_halt(channel); ++ } ++ ++ if (__dmac_channel_address_error_detected(channel)) { ++ __dmac_channel_clear_address_error(channel); ++ } ++ ++ if (substream) ++ snd_pcm_period_elapsed(substream); ++ ++ spin_lock(&prtd->lock); ++ prtd->dma_loaded--; ++ if (prtd->state & ST_RUNNING) { ++ jz4740_pcm_enqueue(substream); ++ } ++ spin_unlock(&prtd->lock); ++ ++ local_irq_save(flags); ++ if (prtd->state & ST_RUNNING) { ++ if (prtd->dma_loaded) { ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ audio_start_dma(prtd, DMA_MODE_WRITE); ++ else ++ audio_start_dma(prtd, DMA_MODE_READ); ++ } ++ } ++ local_irq_restore(flags); ++ return IRQ_HANDLED; ++} ++ ++/* some parameter about DMA operation */ ++static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct jz4740_runtime_data *prtd = runtime->private_data; ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct jz4740_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data; ++ size_t totbytes = params_buffer_bytes(params); ++ int ret; ++ ++#ifdef CONFIG_SND_OSSEMUL ++ if (hw_params_cnt) ++ return 0; ++ else ++ hw_params_cnt++ ; ++#endif ++ ++ if (!dma) ++ return 0; ++ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S8: ++ tran_bit = 8; ++ break; ++ case SNDRV_PCM_FORMAT_S16_LE: ++ tran_bit = 16; ++ break; ++ } ++ ++ /* prepare DMA */ ++ prtd->params = dma; ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ ret = jz_request_dma(DMA_ID_AIC_TX, prtd->params->client->name, ++ jz4740_pcm_dma_irq, IRQF_DISABLED, substream); ++ if (ret < 0) ++ return ret; ++ prtd->params->channel = ret; ++ } else { ++ ret = jz_request_dma(DMA_ID_AIC_RX, prtd->params->client->name, ++ jz4740_pcm_dma_irq, IRQF_DISABLED, substream); ++ if (ret < 0) ++ return ret; ++ prtd->params->channel = ret; ++ } ++ ++ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); ++ runtime->dma_bytes = totbytes; ++ ++ spin_lock_irq(&prtd->lock); ++ prtd->dma_loaded = 0; ++ prtd->aic_dma_flag = 0; ++ prtd->dma_limit = runtime->hw.periods_min; ++ prtd->dma_period = params_period_bytes(params); ++ prtd->dma_start = runtime->dma_addr; ++ prtd->dma_pos = prtd->dma_start; ++ prtd->dma_end = prtd->dma_start + totbytes; ++ prtd->curr = NULL; ++ prtd->next = NULL; ++ prtd->end = NULL; ++ sum_bytes = 0; ++ first_transfer = 1; ++ printk_flag = 0; ++ ++ __dmac_disable_descriptor(prtd->params->channel); ++ __dmac_channel_disable_irq(prtd->params->channel); ++ spin_unlock_irq(&prtd->lock); ++ ++ return ret; ++} ++ ++static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream) ++{ ++ struct jz4740_runtime_data *prtd = substream->runtime->private_data; ++ ++ snd_pcm_set_runtime_buffer(substream, NULL); ++ if (prtd->params) { ++ jz_free_dma(prtd->params->channel); ++ prtd->params = NULL; ++ } ++ ++ return 0; ++} ++ ++/* set some dma para for playback/capture */ ++static int jz4740_dma_ctrl(int channel) ++{ ++ ++ disable_dma(channel); ++ ++ /* must clear TT bit in DCCSR to avoid interrupt again */ ++ if (__dmac_channel_transmit_end_detected(channel)) { ++ __dmac_channel_clear_transmit_end(channel); ++ } ++ if (__dmac_channel_transmit_halt_detected(channel)) { ++ __dmac_channel_clear_transmit_halt(channel); ++ } ++ ++ if (__dmac_channel_address_error_detected(channel)) { ++ __dmac_channel_clear_address_error(channel); ++ } ++ ++ return 0; ++ ++} ++ ++static int jz4740_pcm_prepare(struct snd_pcm_substream *substream) ++{ ++ struct jz4740_runtime_data *prtd = substream->runtime->private_data; ++ int ret = 0; ++ ++ /* return if this is a bufferless transfer e.g */ ++ if (!prtd->params) ++ return 0; ++ ++ /* flush the DMA channel and DMA channel bit check */ ++ jz4740_dma_ctrl(prtd->params->channel); ++ prtd->dma_loaded = 0; ++ prtd->dma_pos = prtd->dma_start; ++ ++ /* enqueue dma buffers */ ++ jz4740_pcm_enqueue(substream); ++ ++ return ret; ++ ++} ++ ++static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct jz4740_runtime_data *prtd = runtime->private_data; ++ ++ int ret = 0; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ prtd->state |= ST_RUNNING; ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ audio_start_dma(prtd, DMA_MODE_WRITE); ++ } else { ++ audio_start_dma(prtd, DMA_MODE_READ); ++ } ++ ++ break; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ prtd->state &= ~ST_RUNNING; ++ break; ++ ++ case SNDRV_PCM_TRIGGER_RESUME: ++ printk(" RESUME \n"); ++ break; ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ printk(" RESTART \n"); ++ break; ++ ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static snd_pcm_uframes_t ++jz4740_pcm_pointer(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct jz4740_runtime_data *prtd = runtime->private_data; ++ struct jz4740_dma_buf_aic *aic_buf = prtd->curr; ++ long count,res; ++ ++ dma_addr_t ptr; ++ snd_pcm_uframes_t x; ++ int channel = prtd->params->channel; ++ ++ spin_lock(&prtd->lock); ++#if 1 ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ count = get_dma_residue(channel); ++ count = aic_buf->size - count; ++ ptr = aic_buf->data + count; ++ res = ptr - prtd->dma_start; ++ } else { ++ count = get_dma_residue(channel); ++ count = aic_buf->size - count; ++ ptr = aic_buf->data + count; ++ res = ptr - prtd->dma_start; ++ } ++ ++# else ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ if ((prtd->aic_dma_flag & AIC_START_DMA) == 0) { ++ count = get_dma_residue(channel); ++ count = aic_buf->size - count; ++ ptr = aic_buf->data + count; ++ REG_DMAC_DSAR(channel) = ptr; ++ res = ptr - prtd->dma_start; ++ } else { ++ ptr = REG_DMAC_DSAR(channel); ++ if (ptr == 0x0) ++ printk("\ndma address is 00000000 in running!\n"); ++ res = ptr - prtd->dma_start; ++ } ++ } else { ++ if ((prtd->aic_dma_flag & AIC_START_DMA) == 0) { ++ count = get_dma_residue(channel); ++ count = aic_buf->size - count; ++ ptr = aic_buf->data + count; ++ REG_DMAC_DTAR(channel) = ptr; ++ res = ptr - prtd->dma_start; ++ } else { ++ ptr = REG_DMAC_DTAR(channel); ++ if (ptr == 0x0) ++ printk("\ndma address is 00000000 in running!\n"); ++ res = ptr - prtd->dma_start; ++ } ++ } ++#endif ++ spin_unlock(&prtd->lock); ++ x = bytes_to_frames(runtime, res); ++ if (x == runtime->buffer_size) ++ x = 0; ++ ++ return x; ++} ++ ++static int jz4740_pcm_open(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct jz4740_runtime_data *prtd; ++ ++#ifdef CONFIG_SND_OSSEMUL ++ hw_params_cnt = 0; ++#endif ++ snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware); ++ prtd = kzalloc(sizeof(struct jz4740_runtime_data), GFP_KERNEL); ++ if (prtd == NULL) ++ return -ENOMEM; ++ ++ spin_lock_init(&prtd->lock); ++ ++ runtime->private_data = prtd; ++ ++ return 0; ++} ++ ++static int jz4740_pcm_close(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct jz4740_runtime_data *prtd = runtime->private_data; ++ struct jz4740_dma_buf_aic *aic_buf = NULL; ++ ++#ifdef CONFIG_SND_OSSEMUL ++ hw_params_cnt = 0; ++#endif ++ ++ if (prtd) ++ aic_buf = prtd->curr; ++ ++ while (aic_buf != NULL) { ++ prtd->curr = aic_buf->next; ++ prtd->next = aic_buf->next; ++ aic_buf->next = NULL; ++ kfree(aic_buf); ++ aic_buf = NULL; ++ aic_buf = prtd->curr; ++ } ++ ++ if (prtd) { ++ prtd->curr = NULL; ++ prtd->next = NULL; ++ prtd->end = NULL; ++ kfree(prtd); ++ } ++ ++ return 0; ++} ++ ++static int jz4740_pcm_mmap(struct snd_pcm_substream *substream, ++ struct vm_area_struct *vma)//include/linux/mm.h ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ unsigned long start; ++ unsigned long off; ++ u32 len; ++ int ret = -ENXIO; ++ ++ off = vma->vm_pgoff << PAGE_SHIFT; ++ start = runtime->dma_addr; ++ ++ len = PAGE_ALIGN((start & ~PAGE_MASK) + runtime->dma_bytes); ++ start &= PAGE_MASK; ++ ++ if ((vma->vm_end - vma->vm_start + off) > len) { ++ return -EINVAL; ++ } ++ ++ off += start; ++ vma->vm_pgoff = off >> PAGE_SHIFT; ++ vma->vm_flags |= VM_IO; ++ ++#if defined(CONFIG_MIPS32) ++ pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; ++ pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; ++ /* pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; */ ++#endif ++ ret = io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, ++ vma->vm_end - vma->vm_start, ++ vma->vm_page_prot); ++ ++ return ret; ++} ++ ++struct snd_pcm_ops jz4740_pcm_ops = { ++ .open = jz4740_pcm_open, ++ .close = jz4740_pcm_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = jz4740_pcm_hw_params, ++ .hw_free = jz4740_pcm_hw_free, ++ .prepare = jz4740_pcm_prepare, ++ .trigger = jz4740_pcm_trigger, ++ .pointer = jz4740_pcm_pointer, ++ .mmap = jz4740_pcm_mmap, ++}; ++ ++static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) ++{ ++ struct snd_pcm_substream *substream = pcm->streams[stream].substream; ++ struct snd_dma_buffer *buf = &substream->dma_buffer; ++ size_t size = jz4740_pcm_hardware.buffer_bytes_max; ++ buf->dev.type = SNDRV_DMA_TYPE_DEV; ++ buf->dev.dev = pcm->card->dev; ++ buf->private_data = NULL; ++ ++ /*buf->area = dma_alloc_coherent(pcm->card->dev, size, ++ &buf->addr, GFP_KERNEL);*/ ++ buf->area = dma_alloc_noncoherent(pcm->card->dev, size, ++ &buf->addr, GFP_KERNEL); ++ if (!buf->area) ++ return -ENOMEM; ++ buf->bytes = size; ++ return 0; ++} ++ ++static void jz4740_pcm_free_dma_buffers(struct snd_pcm *pcm) ++{ ++ struct snd_pcm_substream *substream; ++ struct snd_dma_buffer *buf; ++ int stream; ++ ++ for (stream = 0; stream < 2; stream++) { ++ substream = pcm->streams[stream].substream; ++ if (!substream) ++ continue; ++ ++ buf = &substream->dma_buffer; ++ if (!buf->area) ++ continue; ++ ++ dma_free_noncoherent(pcm->card->dev, buf->bytes, ++ buf->area, buf->addr); ++ buf->area = NULL; ++ } ++} ++ ++static u64 jz4740_pcm_dmamask = DMA_32BIT_MASK; ++ ++int jz4740_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai, ++ struct snd_pcm *pcm) ++{ ++ int ret = 0; ++ ++ if (!card->dev->dma_mask) ++ card->dev->dma_mask = &jz4740_pcm_dmamask; ++ if (!card->dev->coherent_dma_mask) ++ card->dev->coherent_dma_mask = DMA_32BIT_MASK; ++ ++ if (dai->playback.channels_min) { ++ ret = jz4740_pcm_preallocate_dma_buffer(pcm, ++ SNDRV_PCM_STREAM_PLAYBACK); ++ if (ret) ++ goto out; ++ } ++ ++ if (dai->capture.channels_min) { ++ ret = jz4740_pcm_preallocate_dma_buffer(pcm, ++ SNDRV_PCM_STREAM_CAPTURE); ++ if (ret) ++ goto out; ++ } ++ out: ++ ++ return ret; ++} ++ ++struct snd_soc_platform jz4740_soc_platform = { ++ .name = "jz4740-audio", ++ .pcm_ops = &jz4740_pcm_ops, ++ .pcm_new = jz4740_pcm_new, ++ .pcm_free = jz4740_pcm_free_dma_buffers, ++}; ++ ++EXPORT_SYMBOL_GPL(jz4740_soc_platform); ++ ++MODULE_AUTHOR("Richard"); ++MODULE_DESCRIPTION("Ingenic Jz4740 PCM DMA module"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.6.24.7.old/sound/soc/jz4740/jz4740-pcm.h linux-2.6.24.7/sound/soc/jz4740/jz4740-pcm.h +--- linux-2.6.24.7.old/sound/soc/jz4740/jz4740-pcm.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/soc/jz4740/jz4740-pcm.h 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,33 @@ ++/* ++ * ++ * 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 _JZ4740_PCM_H ++#define _JZ4740_PCM_H ++ ++#include ++ ++#define ST_RUNNING (1<<0) ++#define ST_OPENED (1<<1) ++ ++#define AIC_START_DMA (1<<0) ++#define AIC_END_DMA (1<<1) ++ ++struct jz4740_dma_client { ++ char *name; ++}; ++ ++struct jz4740_pcm_dma_params { ++ struct jz4740_dma_client *client; /* stream identifier */ ++ int channel; /* Channel ID */ ++ dma_addr_t dma_addr; ++ int dma_size; /* Size of the DMA transfer */ ++}; ++ ++/* platform data */ ++extern struct snd_soc_platform jz4740_soc_platform; ++ ++#endif +diff -urN linux-2.6.24.7.old/sound/soc/jz4740/pavo.c linux-2.6.24.7/sound/soc/jz4740/pavo.c +--- linux-2.6.24.7.old/sound/soc/jz4740/pavo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7/sound/soc/jz4740/pavo.c 2009-04-12 18:13:57.000000000 +0200 +@@ -0,0 +1,360 @@ ++/* ++ * pavo.c -- SoC audio for PAVO ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../codecs/jzcodec.h" ++#include "jz4740-pcm.h" ++#include "jz4740-i2s.h" ++ ++#define PAVO_HP 0 ++#define PAVO_MIC 1 ++#define PAVO_LINE 2 ++#define PAVO_HEADSET 3 ++#define PAVO_HP_OFF 4 ++#define PAVO_SPK_ON 0 ++#define PAVO_SPK_OFF 1 ++ ++ /* audio clock in Hz - rounded from 12.235MHz */ ++#define PAVO_AUDIO_CLOCK 12288000 ++ ++static int pavo_jack_func; ++static int pavo_spk_func; ++ ++unsigned short set_scoop_gpio(struct device *dev, unsigned short bit) ++{ ++ unsigned short gpio_bit = 0; ++ ++ return gpio_bit; ++} ++ ++unsigned short reset_scoop_gpio(struct device *dev, unsigned short bit) ++{ ++ unsigned short gpio_bit = 0; ++ ++ return gpio_bit; ++} ++ ++static void pavo_ext_control(struct snd_soc_codec *codec) ++{ ++ int spk = 0, mic = 0, line = 0, hp = 0, hs = 0; ++ ++ /* set up jack connection */ ++ switch (pavo_jack_func) { ++ case PAVO_HP: ++ hp = 1; ++ /* set = unmute headphone */ ++ //set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); ++ //set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); ++ break; ++ case PAVO_MIC: ++ mic = 1; ++ /* reset = mute headphone */ ++ //reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); ++ //reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); ++ break; ++ case PAVO_LINE: ++ line = 1; ++ //reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); ++ //reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); ++ break; ++ case PAVO_HEADSET: ++ hs = 1; ++ mic = 1; ++ //reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); ++ //set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); ++ break; ++ } ++ ++ if (pavo_spk_func == PAVO_SPK_ON) ++ spk = 1; ++ ++ /* set the enpoints to their new connetion states */ ++ snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk); ++ snd_soc_dapm_set_endpoint(codec, "Mic Jack", mic); ++ snd_soc_dapm_set_endpoint(codec, "Line Jack", line); ++ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp); ++ snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs); ++ ++ /* signal a DAPM event */ ++ snd_soc_dapm_sync_endpoints(codec); ++} ++ ++static int pavo_startup(struct snd_pcm_substream *substream) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_codec *codec = rtd->socdev->codec; ++ ++ /* check the jack status at stream startup */ ++ pavo_ext_control(codec); ++ return 0; ++} ++ ++/* we need to unmute the HP at shutdown as the mute burns power on pavo */ ++static void pavo_shutdown(struct snd_pcm_substream *substream) ++{ ++ /*struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_codec *codec = rtd->socdev->codec;*/ ++ ++ return; ++} ++ ++static int pavo_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; ++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; ++ int ret = 0; ++ ++ /* set codec DAI configuration */ ++ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | ++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); ++ if (ret < 0) ++ return ret; ++ ++ /* set cpu DAI configuration */ ++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | ++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); ++ if (ret < 0) ++ return ret; ++ ++ /* set the codec system clock for DAC and ADC */ ++ ret = codec_dai->dai_ops.set_sysclk(codec_dai, JZCODEC_SYSCLK, 111, ++ SND_SOC_CLOCK_IN); ++ if (ret < 0) ++ return ret; ++ ++ /* set the I2S system clock as input (unused) */ ++ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, JZ4740_I2S_SYSCLK, 0, ++ SND_SOC_CLOCK_IN); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static struct snd_soc_ops pavo_ops = { ++ .startup = pavo_startup, ++ .hw_params = pavo_hw_params, ++ .shutdown = pavo_shutdown, ++}; ++ ++static int pavo_get_jack(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ ucontrol->value.integer.value[0] = pavo_jack_func; ++ return 0; ++} ++ ++static int pavo_set_jack(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ ++ if (pavo_jack_func == ucontrol->value.integer.value[0]) ++ return 0; ++ ++ pavo_jack_func = ucontrol->value.integer.value[0]; ++ pavo_ext_control(codec); ++ return 1; ++} ++ ++static int pavo_get_spk(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ ucontrol->value.integer.value[0] = pavo_spk_func; ++ return 0; ++} ++ ++static int pavo_set_spk(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ ++ if (pavo_spk_func == ucontrol->value.integer.value[0]) ++ return 0; ++ ++ pavo_spk_func = ucontrol->value.integer.value[0]; ++ pavo_ext_control(codec); ++ return 1; ++} ++ ++static int pavo_amp_event(struct snd_soc_dapm_widget *w, int event) ++{ ++ if (SND_SOC_DAPM_EVENT_ON(event)) ++ //set_scoop_gpio(&corgiscoop_device.dev, PAVO_SCP_APM_ON); ++ ; ++ else ++ //reset_scoop_gpio(&corgiscoop_device.dev, PAVO_SCP_APM_ON); ++ ; ++ ++ return 0; ++} ++ ++static int pavo_mic_event(struct snd_soc_dapm_widget *w, int event) ++{ ++ if (SND_SOC_DAPM_EVENT_ON(event)) ++ //set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS); ++ ; ++ else ++ //reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS); ++ ; ++ ++ return 0; ++} ++ ++/* pavo machine dapm widgets */ ++static const struct snd_soc_dapm_widget jzcodec_dapm_widgets[] = { ++SND_SOC_DAPM_HP("Headphone Jack", NULL), ++SND_SOC_DAPM_MIC("Mic Jack",pavo_mic_event), ++SND_SOC_DAPM_SPK("Ext Spk", pavo_amp_event), ++SND_SOC_DAPM_LINE("Line Jack", NULL), ++SND_SOC_DAPM_HP("Headset Jack", NULL), ++}; ++ ++/* pavo machine audio map (connections to the codec pins) */ ++static const char *audio_map[][3] = { ++ ++ /* headset Jack - in = micin, out = LHPOUT*/ ++ {"Headset Jack", NULL, "LHPOUT"}, ++ ++ /* headphone connected to LHPOUT1, RHPOUT1 */ ++ {"Headphone Jack", NULL, "LHPOUT"}, ++ {"Headphone Jack", NULL, "RHPOUT"}, ++ ++ /* speaker connected to LOUT, ROUT */ ++ {"Ext Spk", NULL, "ROUT"}, ++ {"Ext Spk", NULL, "LOUT"}, ++ ++ /* mic is connected to MICIN (via right channel of headphone jack) */ ++ {"MICIN", NULL, "Mic Jack"}, ++ ++ /* Same as the above but no mic bias for line signals */ ++ {"MICIN", NULL, "Line Jack"}, ++ ++ {NULL, NULL, NULL}, ++}; ++ ++static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", ++ "Off"}; ++static const char *spk_function[] = {"On", "Off"}; ++static const struct soc_enum pavo_enum[] = { ++ SOC_ENUM_SINGLE_EXT(5, jack_function), ++ SOC_ENUM_SINGLE_EXT(2, spk_function), ++}; ++ ++static const struct snd_kcontrol_new jzcodec_pavo_controls[] = { ++ SOC_ENUM_EXT("Jack Function", pavo_enum[0], pavo_get_jack, ++ pavo_set_jack), ++ SOC_ENUM_EXT("Speaker Function", pavo_enum[1], pavo_get_spk, ++ pavo_set_spk), ++}; ++ ++/* ++ * Pavo for a jzcodec as connected on jz4740 Device ++ */ ++static int pavo_jzcodec_init(struct snd_soc_codec *codec) ++{ ++ int i, err; ++ ++ snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0); ++ snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0); ++ ++ /* Add pavo specific controls */ ++ for (i = 0; i < ARRAY_SIZE(jzcodec_pavo_controls); i++) { ++ err = snd_ctl_add(codec->card, ++ snd_soc_cnew(&jzcodec_pavo_controls[i],codec, NULL)); ++ if (err < 0) ++ return err; ++ } ++ ++ /* Add pavo specific widgets */ ++ for(i = 0; i < ARRAY_SIZE(jzcodec_dapm_widgets); i++) { ++ snd_soc_dapm_new_control(codec, &jzcodec_dapm_widgets[i]); ++ } ++ ++ /* Set up pavo specific audio path audio_map */ ++ for(i = 0; audio_map[i][0] != NULL; i++) { ++ snd_soc_dapm_connect_input(codec, audio_map[i][0], ++ audio_map[i][1], audio_map[i][2]); ++ } ++ ++ snd_soc_dapm_sync_endpoints(codec); ++ return 0; ++} ++ ++/* pavo digital audio interface glue - connects codec <--> CPU */ ++static struct snd_soc_dai_link pavo_dai = { ++ .name = "JZCODEC", ++ .stream_name = "JZCODEC", ++ .cpu_dai = &jz4740_i2s_dai, ++ .codec_dai = &jzcodec_dai, ++ .init = pavo_jzcodec_init, ++ .ops = &pavo_ops, ++}; ++ ++/* pavo audio machine driver */ ++static struct snd_soc_machine snd_soc_machine_pavo = { ++ .name = "Pavo", ++ .dai_link = &pavo_dai, ++ .num_links = 1, ++}; ++ ++/* pavo audio subsystem */ ++static struct snd_soc_device pavo_snd_devdata = { ++ .machine = &snd_soc_machine_pavo, ++ .platform = &jz4740_soc_platform, ++ .codec_dev = &soc_codec_dev_jzcodec, ++ //.codec_data ++}; ++ ++static struct platform_device *pavo_snd_device; ++ ++static int __init pavo_init(void) ++{ ++ int ret; ++ ++ pavo_snd_device = platform_device_alloc("soc-audio", -1); ++ ++ if (!pavo_snd_device) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pavo_snd_device, &pavo_snd_devdata); ++ pavo_snd_devdata.dev = &pavo_snd_device->dev; ++ ret = platform_device_add(pavo_snd_device); ++ ++ if (ret) ++ platform_device_put(pavo_snd_device); ++ ++ return ret; ++} ++ ++static void __exit pavo_exit(void) ++{ ++ platform_device_unregister(pavo_snd_device); ++} ++ ++module_init(pavo_init); ++module_exit(pavo_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Richard"); ++MODULE_DESCRIPTION("ALSA SoC Pavo"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/x-burst/patches-2.6.24/002-yaffs2_dists.patch b/target/linux/x-burst/patches-2.6.24/002-yaffs2_dists.patch new file mode 100644 index 000000000..de1749e20 --- /dev/null +++ b/target/linux/x-burst/patches-2.6.24/002-yaffs2_dists.patch @@ -0,0 +1,8842 @@ +diff -urN linux-2.6.24.7/fs/yaffs2/Kconfig linux-2.6.24.7.new/fs/yaffs2/Kconfig +--- linux-2.6.24.7/fs/yaffs2/Kconfig 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/Kconfig 2009-04-21 15:01:02.000000000 +0200 +@@ -2,6 +2,8 @@ + # YAFFS file system configurations + # + ++menu "Yaffs2 Filesystems" ++ + config YAFFS_FS + tristate "YAFFS2 file system support" + default n +@@ -12,8 +14,8 @@ + YAFFS2, or Yet Another Flash Filing System, is a filing system + optimised for NAND Flash chips. + +- To compile the YAFFS2 file system support as a module, choose M +- here: the module will be called yaffs2. ++ To compile the YAFFS2 file system support as a module, choose M here: ++ the module will be called yaffs2. + + If unsure, say N. + +@@ -27,29 +29,11 @@ + help + Enable YAFFS1 support -- yaffs for 512 byte / page devices + +- Not needed for 2K-page devices. +- + If unsure, say Y. + +-config YAFFS_9BYTE_TAGS +- bool "Use older-style on-NAND data format with pageStatus byte" +- depends on YAFFS_YAFFS1 +- default n +- help +- +- Older-style on-NAND data format has a "pageStatus" byte to record +- chunk/page state. This byte is zero when the page is discarded. +- Choose this option if you have existing on-NAND data using this +- format that you need to continue to support. New data written +- also uses the older-style format. Note: Use of this option +- generally requires that MTD's oob layout be adjusted to use the +- older-style format. See notes on tags formats and MTD versions. +- +- If unsure, say N. +- + config YAFFS_DOES_ECC + bool "Lets Yaffs do its own ECC" +- depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS ++ depends on YAFFS_FS && YAFFS_YAFFS1 + default n + help + This enables Yaffs to use its own ECC functions instead of using +@@ -59,12 +43,12 @@ + + config YAFFS_ECC_WRONG_ORDER + bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" +- depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS ++ depends on YAFFS_FS && YAFFS_DOES_ECC + default n + help +- This makes yaffs_ecc.c use the same ecc byte order as Steven +- Hill's nand_ecc.c. If not set, then you get the same ecc byte +- order as SmartMedia. ++ This makes yaffs_ecc.c use the same ecc byte order as ++ Steven Hill's nand_ecc.c. If not set, then you get the ++ same ecc byte order as SmartMedia. + + If unsure, say N. + +@@ -73,10 +57,39 @@ + depends on YAFFS_FS + default y + help +- Enable YAFFS2 support -- yaffs for >= 2K bytes per page devices ++ Enable YAFFS2 support -- yaffs for >= 2048 byte / page larger devices + + If unsure, say Y. + ++if SOC_JZ4730 || SOC_JZ4740 ++ ++choice ++ prompt "ECC type for oob area" ++ depends on YAFFS_YAFFS2 ++ default CONFIG_YAFFS_ECC_RS ++ help ++ There are 16 bytes for yaffs2 information in oob which should be checked ++ using some type of ECC. ++ ++config YAFFS_ECC_RS ++ bool "Use soft reed solomon ECC for oob area" ++ select REED_SOLOMON ++ select REED_SOLOMON_ENC8 ++ select REED_SOLOMON_DEC8 ++ help ++ The reed solomon ECC could correct 2 5-bit symbols for 16 bytes in oob. ++ It should be selected for MLC nand. ++ ++config YAFFS_ECC_HAMMING ++ bool "Use hamming ECC for oob area" ++ help ++ The hamming ECC could only correct 1 bit for 16 bytes in oob, but it's ++ a bit faster than reed solomon ECC. It should be selected for SLC nand. ++ ++endchoice ++ ++endif ++ + config YAFFS_AUTO_YAFFS2 + bool "Autoselect yaffs2 format" + depends on YAFFS_YAFFS2 +@@ -84,8 +97,7 @@ + help + Without this, you need to explicitely use yaffs2 as the file + system type. With this, you can say "yaffs" and yaffs or yaffs2 +- will be used depending on the device page size (yaffs on +- 512-byte page devices, yaffs2 on 2K page devices). ++ will be used depending on the device page size. + + If unsure, say Y. + +@@ -109,57 +121,30 @@ + + If unsure, say N. + +-config YAFFS_CHECKPOINT_RESERVED_BLOCKS +- int "Reserved blocks for checkpointing" +- depends on YAFFS_YAFFS2 +- default 10 +- help +- Give the number of Blocks to reserve for checkpointing. +- Checkpointing saves the state at unmount so that mounting is +- much faster as a scan of all the flash to regenerate this state +- is not needed. These Blocks are reserved per partition, so if +- you have very small partitions the default (10) may be a mess +- for you. You can set this value to 0, but that does not mean +- checkpointing is disabled at all. There only won't be any +- specially reserved blocks for checkpointing, so if there is +- enough free space on the filesystem, it will be used for +- checkpointing. +- +- If unsure, leave at default (10), but don't wonder if there are +- always 2MB used on your large page device partition (10 x 2k +- pagesize). When using small partitions or when being very small +- on space, you probably want to set this to zero. +- + config YAFFS_DISABLE_WIDE_TNODES + bool "Turn off wide tnodes" + depends on YAFFS_FS + default n + help +- Wide tnodes are only used for NAND arrays >=32MB for 512-byte +- page devices and >=128MB for 2k page devices. They use slightly +- more RAM but are faster since they eliminate chunk group ++ Wide tnodes are only used for large NAND arrays (>=32MB for ++ 512-byte page devices and >=128MB for 2k page devices). They use ++ slightly more RAM but are faster since they eliminate chunk group + searching. + +- Setting this to 'y' will force tnode width to 16 bits and save +- memory but make large arrays slower. ++ Setting this to 'y' will force tnode width to 16 bits and make ++ large arrays slower. + + If unsure, say N. + +-config YAFFS_ALWAYS_CHECK_CHUNK_ERASED +- bool "Force chunk erase check" ++config YAFFS_DISABLE_CHUNK_ERASED_CHECK ++ bool "Turn off debug chunk erase check" + depends on YAFFS_FS +- default n ++ default y + help +- Normally YAFFS only checks chunks before writing until an erased +- chunk is found. This helps to detect any partially written +- chunks that might have happened due to power loss. +- +- Enabling this forces on the test that chunks are erased in flash +- before writing to them. This takes more time but is potentially +- a bit more secure. +- +- Suggest setting Y during development and ironing out driver +- issues etc. Suggest setting to N if you want faster writing. ++ Enabling this turns off the test that chunks are erased in flash ++ before writing to them. This is safe, since the write verification ++ will fail. Suggest enabling the test (ie. say N) ++ during development to help debug things. + + If unsure, say Y. + +@@ -173,3 +158,10 @@ + but makes look-ups faster. + + If unsure, say Y. ++ ++config YAFFS_CHECKPOINT_RESERVED_BLOCKS ++ int 'Reserved blocks for checkpointing' ++ depends on YAFFS_FS ++ default 10 ++ ++endmenu +diff -urN linux-2.6.24.7/fs/yaffs2/Makefile linux-2.6.24.7.new/fs/yaffs2/Makefile +--- linux-2.6.24.7/fs/yaffs2/Makefile 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/Makefile 2009-04-21 15:01:02.000000000 +0200 +@@ -1,11 +1,10 @@ + # +-# Makefile for the linux YAFFS filesystem routines. ++# Makefile for the linux YAFFS2 filesystem routines. + # + +-obj-$(CONFIG_YAFFS_FS) += yaffs.o +- +-yaffs-y := yaffs_ecc.o yaffs_fs.o yaffs_guts.o yaffs_checkptrw.o +-yaffs-y += yaffs_packedtags2.o yaffs_nand.o yaffs_qsort.o +-yaffs-y += yaffs_tagscompat.o yaffs_tagsvalidity.o +-yaffs-y += yaffs_mtdif1.o yaffs_packedtags1.o +-yaffs-y += yaffs_mtdif.o yaffs_mtdif2.o ++obj-y := yaffs2.o ++obj-$(CONFIG_YAFFS_FS) := yaffs_mtdif.o yaffs_mtdif2.o ++obj-$(CONFIG_YAFFS_FS) += yaffs_ecc.o yaffs_fs.o yaffs_guts.o ++obj-$(CONFIG_YAFFS_FS) += yaffs_packedtags2.o yaffs_qsort.o ++obj-$(CONFIG_YAFFS_FS) += yaffs_tagscompat.o yaffs_tagsvalidity.o ++obj-$(CONFIG_YAFFS_FS) += yaffs_checkptrw.o yaffs_nand.o +diff -urN linux-2.6.24.7/fs/yaffs2/devextras.h linux-2.6.24.7.new/fs/yaffs2/devextras.h +--- linux-2.6.24.7/fs/yaffs2/devextras.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/devextras.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +@@ -15,7 +15,7 @@ + + /* + * This file is just holds extra declarations used during development. +- * Most of these are from kernel includes placed here so we can use them in ++ * Most of these are from kernel includes placed here so we can use them in + * applications. + * + */ +diff -urN linux-2.6.24.7/fs/yaffs2/moduleconfig.h linux-2.6.24.7.new/fs/yaffs2/moduleconfig.h +--- linux-2.6.24.7/fs/yaffs2/moduleconfig.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/moduleconfig.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,10 +1,10 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * +- * Created by Martin Fouts ++ * Created by Martin Fouts + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as +@@ -44,21 +44,7 @@ + + /* Default: 10 */ + /* Meaning: set the count of blocks to reserve for checkpointing */ +-#define CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS 10 +- +-/* +-Older-style on-NAND data format has a "pageStatus" byte to record +-chunk/page state. This byte is zeroed when the page is discarded. +-Choose this option if you have existing on-NAND data in this format +-that you need to continue to support. New data written also uses the +-older-style format. +-Note: Use of this option generally requires that MTD's oob layout be +-adjusted to use the older-style format. See notes on tags formats and +-MTD versions. +-*/ +-/* Default: Not selected */ +-/* Meaning: Use older-style on-NAND data format with pageStatus byte */ +-#define CONFIG_YAFFS_9BYTE_TAGS ++#define YAFFS_CHECKPOINT_RESERVED_BLOCKS 10 + + #endif /* YAFFS_OUT_OF_TREE */ + +diff -urN linux-2.6.24.7/fs/yaffs2/utils/Makefile linux-2.6.24.7.new/fs/yaffs2/utils/Makefile +--- linux-2.6.24.7/fs/yaffs2/utils/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7.new/fs/yaffs2/utils/Makefile 2009-04-21 15:01:02.000000000 +0200 +@@ -0,0 +1,69 @@ ++#Makefile for mkyaffs ++# ++# NB this is not yet suitable for putting into the kernel tree. ++# YAFFS: Yet another Flash File System. A NAND-flash specific file system. ++# ++# Copyright (C) 2002 Aleph One Ltd. ++# for Toby Churchill Ltd and Brightstar Engineering ++# ++# Created by Charles Manning ++# ++# 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. ++ ++## Change or override KERNELDIR to your kernel ++#KERNELDIR = /usr/src/kernel-headers-2.4.18 ++#CFLAGS = -I$(KERNELDIR)/include -I.. -O2 -Wall -DCONFIG_YAFFS_UTIL ++ ++include ../../../.config ++ ++CFLAGS = -I../../../include -I.. -O2 -Wall -DCONFIG_YAFFS_UTIL ++CFLAGS+= -Wshadow -Wpointer-arith -Wwrite-strings -Wstrict-prototypes -Wmissing-declarations ++CFLAGS+= -Wmissing-prototypes -Wredundant-decls -Wnested-externs -Winline ++ ++ifeq ($(CONFIG_YAFFS_ECC_RS), y) ++CFLAGS+= -DCONFIG_YAFFS_ECC_RS ++endif ++ ++ifeq ($(CONFIG_YAFFS_ECC_HAMMING), y) ++CFLAGS+= -DCONFIG_YAFFS_ECC_HAMMING ++endif ++ ++ ++## Change if you are using a cross-compiler ++MAKETOOLS = #mipsel-linux- ++ ++CC=$(MAKETOOLS)gcc ++ ++COMMONLINKS = yaffs_ecc.c ++COMMONOBJS = $(COMMONLINKS:.c=.o) ++ ++ifeq ($(CONFIG_YAFFS_ECC_RS), y) ++COMMONOBJS += ssfdc_rs_ecc.o ++endif ++ ++MKYAFFSSOURCES = mkyaffsimage.c ++MKYAFFSIMAGEOBJS = $(MKYAFFSSOURCES:.c=.o) ++ ++MKYAFFS2SOURCES = mkyaffs2image.c ++MKYAFFS2LINKS = yaffs_packedtags2.c yaffs_tagsvalidity.c ++MKYAFFS2IMAGEOBJS = $(MKYAFFS2SOURCES:.c=.o) $(MKYAFFS2LINKS:.c=.o) ++ ++all: mkyaffsimage mkyaffs2image ++ ++$(COMMONLINKS) $(MKYAFFSLINKS) $(MKYAFFS2LINKS): ++ ln -s ../$@ $@ ++ ++$(COMMONOBJS) $(MKYAFFSIMAGEOBJS) $(MKYAFFS2IMAGEOBJS) : %.o: %.c ++ $(CC) -c $(CFLAGS) $< -o $@ ++ ++mkyaffsimage: $(COMMONOBJS) $(MKYAFFSIMAGEOBJS) ++ $(CC) -o $@ $(COMMONOBJS) $(MKYAFFSIMAGEOBJS) ++ ++mkyaffs2image: $(COMMONOBJS) $(MKYAFFS2IMAGEOBJS) ++ $(CC) -o $@ $(COMMONOBJS) $(MKYAFFS2IMAGEOBJS) ++ ++ ++clean: ++ rm -f $(COMMONOBJS) $(MKYAFFSIMAGEOBJS) $(MKYAFFS2IMAGEOBJS) $(COMMONLINKS) $(MKYAFFSLINKS) $(MKYAFFS2LINKS) mkyaffsimage mkyaffs2image core +diff -urN linux-2.6.24.7/fs/yaffs2/utils/README linux-2.6.24.7.new/fs/yaffs2/utils/README +--- linux-2.6.24.7/fs/yaffs2/utils/README 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7.new/fs/yaffs2/utils/README 2009-04-21 15:01:02.000000000 +0200 +@@ -0,0 +1,11 @@ ++This directory contains yaffs/yaffs2 tools to make the fs image. ++ ++To build the tools, type 'make'. ++ ++To make yaffs2 image, use next command: ++ ++ $ mkyaffs2image 1 /myroot myroot.yaffs2 ++ ++To burn the yaffs2 image to the NAND, use next command: ++ ++ $ nandwrite -a -o /dev/mtd0 myroot.yaffs2 +diff -urN linux-2.6.24.7/fs/yaffs2/utils/mkyaffs2image.c linux-2.6.24.7.new/fs/yaffs2/utils/mkyaffs2image.c +--- linux-2.6.24.7/fs/yaffs2/utils/mkyaffs2image.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7.new/fs/yaffs2/utils/mkyaffs2image.c 2009-04-21 15:01:02.000000000 +0200 +@@ -0,0 +1,738 @@ ++/* ++ * YAFFS: Yet another FFS. A NAND-flash specific file system. ++ * ++ * makeyaffsimage.c ++ * ++ * Makes a YAFFS file system image that can be used to load up a file system. ++ * ++ * Copyright (C) 2002 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * 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. ++ * ++ * ++ * Nick Bane modifications flagged NCB ++ * ++ * Endian handling patches by James Ng. ++ * ++ * mkyaffs2image hacks by NCB ++ * ++ * Changes by Sergey Kubushin flagged KSI ++ * ++ */ ++ ++/* KSI: ++ * All this nightmare should be rewritten from ground up. Why save return ++ * values if nobody checks them? The read/write function returns only one ++ * error, -1. Positive return value does NOT mean read/write operation has ++ * been completed successfully. If somebody opens files, he MUST close them ++ * when they are not longer needed. Only those brave enough can write 64 ++ * bytes from a yaffs_PackedTags2 structure. The list is too long, there is ++ * enough bugs here to write a couple of thick books on how NOT to write ++ * programs... ++ * ++ * And BTW, what was one supposed to do with that file that this horror ++ * occasionally managed to generate? ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "yaffs_ecc.h" ++#include "yaffs_guts.h" ++ ++#include "yaffs_packedtags2.h" ++ ++unsigned yaffs_traceMask=0; ++ ++#define MAX_OBJECTS 100000 ++#define MAX_CHUNKSIZE 8192 ++#define MAX_SPARESIZE 256 ++ ++#define PT2_BYTES 25 ++ ++const char * mkyaffsimage_c_version = "$Id: mkyaffs2image.c,v 1.1.1.1 2008-10-29 14:29:21 lhhuang Exp $"; ++ ++static int chunkSize = 2048; ++static int spareSize = 64; ++static int layout_no; ++ ++static struct nand_oobinfo oob_layout[] = { ++ /* KSI: ++ * Dummy "raw" layout - no ECC, all the bytes are free. Does NOT ++ * really work, only used for compatibility with CVS YAFFS2 that ++ * never ever worked with any stock MTD. ++ */ ++ { ++ .useecc = MTD_NANDECC_AUTOPLACE, ++ .eccbytes = 0, ++ .eccpos = {}, ++ .oobfree = { {0, 64} } ++ }, ++ /* KSI: ++ * Regular MTD AUTOPLACED ECC for large page NAND devices, the ++ * only one existing in stock MTD so far. It corresponds to layout# 1 ++ * in command line arguments. Any other layouts could be added to ++ * the list when they made their way in kernel's MTD. The structure ++ * is simply copied from kernel's drivers/mtd/nand/nand_base.c as-is. ++ */ ++ /* For 2KB pagesize NAND devices */ ++ { ++ .useecc = MTD_NANDECC_AUTOPLACE, ++ .eccbytes = 24, ++ .eccpos = { ++ 40, 41, 42, 43, 44, 45, 46, 47, ++ 48, 49, 50, 51, 52, 53, 54, 55, ++ 56, 57, 58, 59, 60, 61, 62, 63}, ++ .oobfree = { {2, 38} } ++ }, ++ /* For 4KB pagesize NAND devices */ ++ { ++ .useecc = MTD_NANDECC_AUTOPLACE, ++ .eccbytes = 72, ++ .eccpos = { ++ 56, 57, 58, 59, 60, 61, 62, 63, ++ 64, 65, 66, 67, 68, 69, 70, 71, ++ 72, 73, 74, 75, 76, 77, 78, 79, ++ 80, 81, 82, 83, 84, 85, 86, 87, ++ 88, 89, 90, 91, 92, 93, 94, 95, ++ 96, 97, 98, 99, 100, 101, 102, 103, ++ 104, 105, 106, 107, 108, 109, 110, 111, ++ 112, 113, 114, 115, 116, 117, 118, 119, ++ 120, 121, 122, 123, 124, 125, 126, 127}, ++ .oobfree = { {2, 54} } ++ }, ++ /* For 4KB pagesize with 2 planes NAND devices */ ++ { ++ .useecc = MTD_NANDECC_AUTOPLACE, ++ .eccbytes = 72, ++ .eccpos = { ++ 56, 57, 58, 59, 60, 61, 62, 63, ++ 64, 65, 66, 67, 68, 69, 70, 71, ++ 72, 73, 74, 75, 76, 77, 78, 79, ++ 80, 81, 82, 83, 84, 85, 86, 87, ++ 88, 89, 90, 91, 92, 93, 94, 95, ++ 96, 97, 98, 99, 100, 101, 102, 103, ++ 104, 105, 106, 107, 108, 109, 110, 111, ++ 112, 113, 114, 115, 116, 117, 118, 119, ++ 120, 121, 122, 123, 124, 125, 126, 127}, ++ .oobfree = { {2, 54} } ++ }, ++ /* End-of-list marker */ ++ { ++ .useecc = -1, ++ } ++}; ++ ++typedef struct ++{ ++ dev_t dev; ++ ino_t ino; ++ int obj; ++} objItem; ++ ++ ++static objItem obj_list[MAX_OBJECTS]; ++static int n_obj = 0; ++static int obj_id = YAFFS_NOBJECT_BUCKETS + 1; ++ ++static int nObjects = 0, nDirectories = 0, nPages = 0; ++ ++static int outFile; ++ ++static int error; ++ ++static int convert_endian = 0; ++ ++static int obj_compare(const void *a, const void * b) ++{ ++ objItem *oa, *ob; ++ ++ oa = (objItem *)a; ++ ob = (objItem *)b; ++ ++ if(oa->dev < ob->dev) return -1; ++ if(oa->dev > ob->dev) return 1; ++ if(oa->ino < ob->ino) return -1; ++ if(oa->ino > ob->ino) return 1; ++ ++ return 0; ++} ++ ++ ++static void add_obj_to_list(dev_t dev, ino_t ino, int obj) ++{ ++ if(n_obj < MAX_OBJECTS) ++ { ++ obj_list[n_obj].dev = dev; ++ obj_list[n_obj].ino = ino; ++ obj_list[n_obj].obj = obj; ++ n_obj++; ++ qsort(obj_list,n_obj,sizeof(objItem),obj_compare); ++ ++ } ++ else ++ { ++ // oops! not enough space in the object array ++ fprintf(stderr,"Not enough space in object array\n"); ++ exit(2); ++ } ++} ++ ++ ++static int find_obj_in_list(dev_t dev, ino_t ino) ++{ ++ objItem *i = NULL; ++ objItem test; ++ ++ test.dev = dev; ++ test.ino = ino; ++ ++ if(n_obj > 0) ++ { ++ i = bsearch(&test,obj_list,n_obj,sizeof(objItem),obj_compare); ++ } ++ ++ if(i) ++ { ++ return i->obj; ++ } ++ return -1; ++} ++ ++/* KSI: ++ * No big endian for now. This is left for a later time. The existing code ++ * is FUBAR. ++ */ ++#if 0 ++/* This little function converts a little endian tag to a big endian tag. ++ * NOTE: The tag is not usable after this other than calculating the CRC ++ * with. ++ */ ++static void little_to_big_endian(yaffs_Tags *tagsPtr) ++{ ++#if 0 // FIXME NCB ++ yaffs_TagsUnion * tags = (yaffs_TagsUnion* )tagsPtr; // Work in bytes. ++ yaffs_TagsUnion temp; ++ ++ memset(&temp, 0, sizeof(temp)); ++ // Ick, I hate magic numbers. ++ temp.asBytes[0] = ((tags->asBytes[2] & 0x0F) << 4) | ((tags->asBytes[1] & 0xF0) >> 4); ++ temp.asBytes[1] = ((tags->asBytes[1] & 0x0F) << 4) | ((tags->asBytes[0] & 0xF0) >> 4); ++ temp.asBytes[2] = ((tags->asBytes[0] & 0x0F) << 4) | ((tags->asBytes[2] & 0x30) >> 2) | ((tags->asBytes[3] & 0xC0) >> 6); ++ temp.asBytes[3] = ((tags->asBytes[3] & 0x3F) << 2) | ((tags->asBytes[2] & 0xC0) >> 6); ++ temp.asBytes[4] = ((tags->asBytes[6] & 0x03) << 6) | ((tags->asBytes[5] & 0xFC) >> 2); ++ temp.asBytes[5] = ((tags->asBytes[5] & 0x03) << 6) | ((tags->asBytes[4] & 0xFC) >> 2); ++ temp.asBytes[6] = ((tags->asBytes[4] & 0x03) << 6) | (tags->asBytes[7] & 0x3F); ++ temp.asBytes[7] = (tags->asBytes[6] & 0xFC) | ((tags->asBytes[7] & 0xC0) >> 6); ++ ++ // Now copy it back. ++ tags->asBytes[0] = temp.asBytes[0]; ++ tags->asBytes[1] = temp.asBytes[1]; ++ tags->asBytes[2] = temp.asBytes[2]; ++ tags->asBytes[3] = temp.asBytes[3]; ++ tags->asBytes[4] = temp.asBytes[4]; ++ tags->asBytes[5] = temp.asBytes[5]; ++ tags->asBytes[6] = temp.asBytes[6]; ++ tags->asBytes[7] = temp.asBytes[7]; ++#endif ++} ++#endif ++ ++void nandmtd2_pt2buf(unsigned char *buf, yaffs_PackedTags2 *pt) ++{ ++ int i, j = 0, k, n; ++ unsigned char pt2_byte_buf[PT2_BYTES]; ++ ++ *((unsigned int *) &pt2_byte_buf[0]) = pt->t.sequenceNumber; ++ *((unsigned int *) &pt2_byte_buf[4]) = pt->t.objectId; ++ *((unsigned int *) &pt2_byte_buf[8]) = pt->t.chunkId; ++ *((unsigned int *) &pt2_byte_buf[12]) = pt->t.byteCount; ++ pt2_byte_buf[16] = pt->ecc.colParity; ++ pt2_byte_buf[17] = pt->ecc.lineParity & 0xff; ++ pt2_byte_buf[18] = (pt->ecc.lineParity >> 8) & 0xff; ++ pt2_byte_buf[19] = (pt->ecc.lineParity >> 16) & 0xff; ++ pt2_byte_buf[20] = (pt->ecc.lineParity >> 24) & 0xff; ++ pt2_byte_buf[21] = pt->ecc.lineParityPrime & 0xff; ++ pt2_byte_buf[22] = (pt->ecc.lineParityPrime >> 8) & 0xff; ++ pt2_byte_buf[23] = (pt->ecc.lineParityPrime >> 16) & 0xff; ++ pt2_byte_buf[24] = (pt->ecc.lineParityPrime >> 24) & 0xff; ++ ++ k = oob_layout[layout_no].oobfree[j][0]; ++ n = oob_layout[layout_no].oobfree[j][1]; ++ ++ if (n == 0) { ++ fprintf(stderr, "No OOB space for tags"); ++ exit(-1); ++ } ++ ++ for (i = 0; i < PT2_BYTES; i++) { ++ if (n == 0) { ++ j++; ++ k = oob_layout[layout_no].oobfree[j][0]; ++ n = oob_layout[layout_no].oobfree[j][1]; ++ if (n == 0) { ++ fprintf(stderr, "No OOB space for tags"); ++ exit(-1); ++ } ++ } ++ buf[k++] = pt2_byte_buf[i]; ++ n--; ++ } ++} ++ ++static int write_chunk(__u8 *data, __u32 objId, __u32 chunkId, __u32 nBytes) ++{ ++ yaffs_ExtendedTags t; ++ yaffs_PackedTags2 pt; ++ unsigned char spare_buf[MAX_SPARESIZE]; ++ ++ ++ error = write(outFile,data,chunkSize); ++ if(error < 0) return error; ++ ++ yaffs_InitialiseTags(&t); ++ ++ t.chunkId = chunkId; ++// t.serialNumber = 0; ++ t.serialNumber = 1; // **CHECK** ++ t.byteCount = nBytes; ++ t.objectId = objId; ++ ++ t.sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++// added NCB **CHECK** ++ t.chunkUsed = 1; ++ ++/* KSI: Broken anyway -- e.g. &t is pointer to a wrong type... */ ++#if 0 ++ if (convert_endian) ++ { ++ little_to_big_endian(&t); ++ } ++#endif ++ ++ nPages++; ++ ++ yaffs_PackTags2(&pt,&t); ++ ++ memset(spare_buf, 0xff, spareSize); ++ ++ if (layout_no == 0) { ++ memcpy(spare_buf, &pt, sizeof(yaffs_PackedTags2)); ++ } else { ++ nandmtd2_pt2buf(spare_buf, &pt); ++ } ++ ++ return write(outFile,spare_buf,spareSize); ++} ++ ++#define SWAP32(x) ((((x) & 0x000000FF) << 24) | \ ++ (((x) & 0x0000FF00) << 8 ) | \ ++ (((x) & 0x00FF0000) >> 8 ) | \ ++ (((x) & 0xFF000000) >> 24)) ++ ++#define SWAP16(x) ((((x) & 0x00FF) << 8) | \ ++ (((x) & 0xFF00) >> 8)) ++ ++/* KSI: Removed for now. TBD later when the proper util (from scratch) is written */ ++#if 0 ++// This one is easier, since the types are more standard. No funky shifts here. ++static void object_header_little_to_big_endian(yaffs_ObjectHeader* oh) ++{ ++ oh->type = SWAP32(oh->type); // GCC makes enums 32 bits. ++ oh->parentObjectId = SWAP32(oh->parentObjectId); // int ++ oh->sum__NoLongerUsed = SWAP16(oh->sum__NoLongerUsed); // __u16 - Not used, but done for completeness. ++ // name = skip. Char array. Not swapped. ++ oh->yst_mode = SWAP32(oh->yst_mode); ++#ifdef CONFIG_YAFFS_WINCE // WinCE doesn't implement this, but we need to just in case. ++ // In fact, WinCE would be *THE* place where this would be an issue! ++ oh->notForWinCE[0] = SWAP32(oh->notForWinCE[0]); ++ oh->notForWinCE[1] = SWAP32(oh->notForWinCE[1]); ++ oh->notForWinCE[2] = SWAP32(oh->notForWinCE[2]); ++ oh->notForWinCE[3] = SWAP32(oh->notForWinCE[3]); ++ oh->notForWinCE[4] = SWAP32(oh->notForWinCE[4]); ++#else ++ // Regular POSIX. ++ oh->yst_uid = SWAP32(oh->yst_uid); ++ oh->yst_gid = SWAP32(oh->yst_gid); ++ oh->yst_atime = SWAP32(oh->yst_atime); ++ oh->yst_mtime = SWAP32(oh->yst_mtime); ++ oh->yst_ctime = SWAP32(oh->yst_ctime); ++#endif ++ ++ oh->fileSize = SWAP32(oh->fileSize); // Aiee. An int... signed, at that! ++ oh->equivalentObjectId = SWAP32(oh->equivalentObjectId); ++ // alias - char array. ++ oh->yst_rdev = SWAP32(oh->yst_rdev); ++ ++#ifdef CONFIG_YAFFS_WINCE ++ oh->win_ctime[0] = SWAP32(oh->win_ctime[0]); ++ oh->win_ctime[1] = SWAP32(oh->win_ctime[1]); ++ oh->win_atime[0] = SWAP32(oh->win_atime[0]); ++ oh->win_atime[1] = SWAP32(oh->win_atime[1]); ++ oh->win_mtime[0] = SWAP32(oh->win_mtime[0]); ++ oh->win_mtime[1] = SWAP32(oh->win_mtime[1]); ++ oh->roomToGrow[0] = SWAP32(oh->roomToGrow[0]); ++ oh->roomToGrow[1] = SWAP32(oh->roomToGrow[1]); ++ oh->roomToGrow[2] = SWAP32(oh->roomToGrow[2]); ++ oh->roomToGrow[3] = SWAP32(oh->roomToGrow[3]); ++ oh->roomToGrow[4] = SWAP32(oh->roomToGrow[4]); ++ oh->roomToGrow[5] = SWAP32(oh->roomToGrow[5]); ++#else ++ oh->roomToGrow[0] = SWAP32(oh->roomToGrow[0]); ++ oh->roomToGrow[1] = SWAP32(oh->roomToGrow[1]); ++ oh->roomToGrow[2] = SWAP32(oh->roomToGrow[2]); ++ oh->roomToGrow[3] = SWAP32(oh->roomToGrow[3]); ++ oh->roomToGrow[4] = SWAP32(oh->roomToGrow[4]); ++ oh->roomToGrow[5] = SWAP32(oh->roomToGrow[5]); ++ oh->roomToGrow[6] = SWAP32(oh->roomToGrow[6]); ++ oh->roomToGrow[7] = SWAP32(oh->roomToGrow[7]); ++ oh->roomToGrow[8] = SWAP32(oh->roomToGrow[8]); ++ oh->roomToGrow[9] = SWAP32(oh->roomToGrow[9]); ++ oh->roomToGrow[10] = SWAP32(oh->roomToGrow[10]); ++ oh->roomToGrow[11] = SWAP32(oh->roomToGrow[11]); ++#endif ++} ++#endif ++ ++static int write_object_header(int objId, yaffs_ObjectType t, struct stat *s, int parent, const char *name, int equivalentObj, const char * alias) ++{ ++ __u8 bytes[MAX_CHUNKSIZE]; ++ ++ ++ yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bytes; ++ ++ memset(bytes,0xff,chunkSize); ++ ++ oh->type = t; ++ ++ oh->parentObjectId = parent; ++ ++ strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH); ++ ++ ++ if(t != YAFFS_OBJECT_TYPE_HARDLINK) ++ { ++ oh->yst_mode = s->st_mode; ++ oh->yst_uid = s->st_uid; ++// NCB 12/9/02 oh->yst_gid = s->yst_uid; ++ oh->yst_gid = s->st_gid; ++ oh->yst_atime = s->st_atime; ++ oh->yst_mtime = s->st_mtime; ++ oh->yst_ctime = s->st_ctime; ++ oh->yst_rdev = s->st_rdev; ++ } ++ ++ if(t == YAFFS_OBJECT_TYPE_FILE) ++ { ++ oh->fileSize = s->st_size; ++ } ++ ++ if(t == YAFFS_OBJECT_TYPE_HARDLINK) ++ { ++ oh->equivalentObjectId = equivalentObj; ++ } ++ ++ if(t == YAFFS_OBJECT_TYPE_SYMLINK) ++ { ++ strncpy(oh->alias,alias,YAFFS_MAX_ALIAS_LENGTH); ++ } ++ ++/* KSI: FUBAR. Left for a leter time. */ ++#if 0 ++ if (convert_endian) ++ { ++ object_header_little_to_big_endian(oh); ++ } ++#endif ++ ++ return write_chunk(bytes,objId,0,0xffff); ++ ++} ++ ++ ++static int process_directory(int parent, const char *path) ++{ ++ ++ DIR *dir; ++ struct dirent *entry; ++ ++ nDirectories++; ++ ++ dir = opendir(path); ++ ++ if(dir) ++ { ++ while((entry = readdir(dir)) != NULL) ++ { ++ ++ /* Ignore . and .. */ ++ if(strcmp(entry->d_name,".") && ++ strcmp(entry->d_name,"..")) ++ { ++ char full_name[500]; ++ struct stat stats; ++ int equivalentObj; ++ int newObj; ++ ++ sprintf(full_name,"%s/%s",path,entry->d_name); ++ ++ lstat(full_name,&stats); ++ ++ if(S_ISLNK(stats.st_mode) || ++ S_ISREG(stats.st_mode) || ++ S_ISDIR(stats.st_mode) || ++ S_ISFIFO(stats.st_mode) || ++ S_ISBLK(stats.st_mode) || ++ S_ISCHR(stats.st_mode) || ++ S_ISSOCK(stats.st_mode)) ++ { ++ ++ newObj = obj_id++; ++ nObjects++; ++ ++ printf("Object %d, %s is a ",newObj,full_name); ++ ++ /* We're going to create an object for it */ ++ if((equivalentObj = find_obj_in_list(stats.st_dev, stats.st_ino)) > 0) ++ { ++ /* we need to make a hard link */ ++ printf("hard link to object %d\n",equivalentObj); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_HARDLINK, &stats, parent, entry->d_name, equivalentObj, NULL); ++ } ++ else ++ { ++ ++ add_obj_to_list(stats.st_dev,stats.st_ino,newObj); ++ ++ if(S_ISLNK(stats.st_mode)) ++ { ++ ++ char symname[500]; ++ ++ memset(symname,0, sizeof(symname)); ++ ++ readlink(full_name,symname,sizeof(symname) -1); ++ ++ printf("symlink to \"%s\"\n",symname); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SYMLINK, &stats, parent, entry->d_name, -1, symname); ++ ++ } ++ else if(S_ISREG(stats.st_mode)) ++ { ++ printf("file, "); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_FILE, &stats, parent, entry->d_name, -1, NULL); ++ ++ if(error >= 0) ++ { ++ int h; ++ __u8 bytes[MAX_CHUNKSIZE]; ++ int nBytes; ++ int chunk = 0; ++ ++ h = open(full_name,O_RDONLY); ++ if(h >= 0) ++ { ++ memset(bytes,0xff,chunkSize); ++ while((nBytes = read(h,bytes,chunkSize)) > 0) ++ { ++ chunk++; ++ write_chunk(bytes,newObj,chunk,nBytes); ++ memset(bytes,0xff,chunkSize); ++ } ++ if(nBytes < 0) ++ error = nBytes; ++ ++ printf("%d data chunks written\n",chunk); ++ close(h); ++ } ++ else ++ { ++ perror("Error opening file"); ++ } ++ ++ } ++ ++ } ++ else if(S_ISSOCK(stats.st_mode)) ++ { ++ printf("socket\n"); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL); ++ } ++ else if(S_ISFIFO(stats.st_mode)) ++ { ++ printf("fifo\n"); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL); ++ } ++ else if(S_ISCHR(stats.st_mode)) ++ { ++ printf("character device\n"); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL); ++ } ++ else if(S_ISBLK(stats.st_mode)) ++ { ++ printf("block device\n"); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL); ++ } ++ else if(S_ISDIR(stats.st_mode)) ++ { ++ printf("directory\n"); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, parent, entry->d_name, -1, NULL); ++// NCB modified 10/9/2001 process_directory(1,full_name); ++ process_directory(newObj,full_name); ++ } ++ } ++ } ++ else ++ { ++ printf(" we don't handle this type\n"); ++ } ++ } ++ } ++ /* KSI: ++ * Who is supposed to close those open directories in this ++ * recursive function, lord Byron? Stock "ulimit -n" is 1024 ++ * and e.g. stock Fedora /etc directory has more that 1024 ++ * directories... ++ */ ++ closedir(dir); ++ } ++ ++ return 0; ++ ++} ++ ++void usage(void) ++{ ++ /* ECC for oob should conform with CONFIG_YAFFS_ECC_XX when building linux kernel, but ecc ++ for oob isn't required when using BCH ECC, as oob will be corrected together with data ++ when using BCH ECC. */ ++#if defined(CONFIG_YAFFS_ECC_RS) ++ printf("Reed-solomn ECC will be used for checking 16 bytes for yaffs2 information in oob area.\n" ++ "so, CONFIG_YAFFS_ECC_RS should be selected when building linux kernel.\n"); ++#elif defined(CONFIG_YAFFS_ECC_HAMMING) ++ printf("Hamming ECC will be used for checking 16 bytes for yaffs2 information in oob area.\n" ++ "so, CONFIG_YAFFS_ECC_HAMMING should be selected when building linux kernel.\n"); ++#endif ++ ++ printf("usage: mkyaffs2image layout# dir image_file [convert]\n"); ++ printf("\n" ++ " layout# NAND OOB layout:\n" ++ " 0 - nand_oob_raw, no used, \n" ++ " 1 - nand_oob_64, for 2KB pagesize, \n" ++ " 2 - nand_oob_128, for 2KB pagesize using multiple planes or 4KB pagesize,\n" ++ " 3 - nand_oob_256, for 4KB pagesize using multiple planes\n"); ++ printf(" dir the directory tree to be converted\n"); ++ printf(" image_file the output file to hold the image\n"); ++ printf(" 'convert' make a big-endian img on a little-endian machine. BROKEN !\n"); ++ printf("\n Example:\n" ++ " mkyaffs2image 1 /nfsroot/root26 root26.yaffs2 \n" ++); ++ ++ exit(1); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ struct stat stats; ++ int i; ++ ++ printf("mkyaffs2image: image building tool for YAFFS2 built "__DATE__"\n"); ++ ++ if ((argc < 4) || (sscanf(argv[1], "%u", &layout_no) != 1)) ++ { ++ usage(); ++ } ++ ++ switch (layout_no) { ++ case 0: ++ printf("Warning: it isn't used by JZSOC!\n"); ++ break; ++ case 1: ++ chunkSize = 2048; ++ spareSize = 64; ++ break; ++ case 2: ++ chunkSize = 4096; ++ spareSize = 128; ++ break; ++ case 3: ++ chunkSize = 8192; ++ spareSize = 256; ++ break; ++ default: ++ usage(); ++ } ++ ++ i = 0; ++ ++ while (oob_layout[i].useecc != -1) ++ i++; ++ ++ if (layout_no >= i) ++ usage(); ++ ++ if ((argc == 5) && (!strncmp(argv[4], "convert", strlen("convert")))) ++ { ++ /* KSI: Broken as of now. TBD. Fail. */ ++ usage(); ++ convert_endian = 1; ++ } ++ ++ if(stat(argv[2],&stats) < 0) ++ { ++ printf("Could not stat %s\n",argv[2]); ++ exit(1); ++ } ++ ++ if(!S_ISDIR(stats.st_mode)) ++ { ++ printf(" %s is not a directory\n",argv[2]); ++ exit(1); ++ } ++ ++ outFile = open(argv[3],O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE); ++ ++ ++ if(outFile < 0) ++ { ++ printf("Could not open output file %s\n",argv[3]); ++ exit(1); ++ } ++ ++ printf("Processing directory %s into image file %s\n",argv[2],argv[3]); ++ error = write_object_header(1, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, 1,"", -1, NULL); ++ ++ if(error) ++ error = process_directory(YAFFS_OBJECTID_ROOT,argv[2]); ++ ++ close(outFile); ++ ++ if(error < 0) ++ { ++ perror("operation incomplete"); ++ exit(1); ++ } ++ else ++ { ++ printf("Operation complete.\n" ++ "%d objects in %d directories\n" ++ "%d NAND pages\n",nObjects, nDirectories, nPages); ++ } ++ ++ close(outFile); ++ ++ exit(0); ++} ++ +diff -urN linux-2.6.24.7/fs/yaffs2/utils/mkyaffsimage.c linux-2.6.24.7.new/fs/yaffs2/utils/mkyaffsimage.c +--- linux-2.6.24.7/fs/yaffs2/utils/mkyaffsimage.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7.new/fs/yaffs2/utils/mkyaffsimage.c 2009-04-21 15:01:02.000000000 +0200 +@@ -0,0 +1,593 @@ ++/* ++ * YAFFS: Yet another FFS. A NAND-flash specific file system. ++ * ++ * makeyaffsimage.c ++ * ++ * Makes a YAFFS file system image that can be used to load up a file system. ++ * ++ * Copyright (C) 2002 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * 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. ++ * ++ * ++ * Nick Bane modifications flagged NCB ++ * ++ * Endian handling patches by James Ng. ++ * ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "yaffs_ecc.h" ++#include "yaffs_guts.h" ++ ++ ++#define MAX_OBJECTS 10000 ++ ++const char * mkyaffsimage_c_version = "$Id: mkyaffsimage.c,v 1.1.1.1 2008-03-28 04:29:21 jlwei Exp $"; ++ ++ ++typedef struct ++{ ++ dev_t dev; ++ ino_t ino; ++ int obj; ++} objItem; ++ ++ ++static objItem obj_list[MAX_OBJECTS]; ++static int n_obj = 0; ++static int obj_id = YAFFS_NOBJECT_BUCKETS + 1; ++ ++static int nObjects, nDirectories, nPages; ++ ++static int outFile; ++ ++static int error; ++ ++static int convert_endian = 0; ++ ++static int obj_compare(const void *a, const void * b) ++{ ++ objItem *oa, *ob; ++ ++ oa = (objItem *)a; ++ ob = (objItem *)b; ++ ++ if(oa->dev < ob->dev) return -1; ++ if(oa->dev > ob->dev) return 1; ++ if(oa->ino < ob->ino) return -1; ++ if(oa->ino > ob->ino) return 1; ++ ++ return 0; ++} ++ ++ ++static void add_obj_to_list(dev_t dev, ino_t ino, int obj) ++{ ++ if(n_obj < MAX_OBJECTS) ++ { ++ obj_list[n_obj].dev = dev; ++ obj_list[n_obj].ino = ino; ++ obj_list[n_obj].obj = obj; ++ n_obj++; ++ qsort(obj_list,n_obj,sizeof(objItem),obj_compare); ++ ++ } ++ else ++ { ++ // oops! not enough space in the object array ++ fprintf(stderr,"Not enough space in object array\n"); ++ exit(2); ++ } ++} ++ ++ ++static int find_obj_in_list(dev_t dev, ino_t ino) ++{ ++ objItem *i = NULL; ++ objItem test; ++ ++ test.dev = dev; ++ test.ino = ino; ++ ++ if(n_obj > 0) ++ { ++ i = bsearch(&test,obj_list,n_obj,sizeof(objItem),obj_compare); ++ } ++ ++ if(i) ++ { ++ return i->obj; ++ } ++ return -1; ++} ++ ++// NCB added 10/9/2002 ++static __u16 yaffs_CalcNameSum(const char *name) ++{ ++ __u16 sum = 0; ++ __u16 i = 1; ++ ++ __u8 *bname = (__u8 *)name; ++ ++ while (*bname) ++ { ++ sum += (*bname) * i; ++ i++; ++ bname++; ++ } ++ return sum; ++} ++ ++ ++static void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare) ++{ ++ yaffs_ECCCalculate(data , spare->ecc1); ++ yaffs_ECCCalculate(&data[256] , spare->ecc2); ++} ++ ++static void yaffs_CalcTagsECC(yaffs_Tags *tags) ++{ ++ // Todo don't do anything yet. Need to calculate ecc ++ unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes; ++ unsigned i,j; ++ unsigned ecc = 0; ++ unsigned bit = 0; ++ ++ // Clear ECC fields ++ if (!convert_endian) ++ { ++ tags->ecc = 0; ++ } ++ else ++ { ++ // Because we're in "munged tag" mode, we have to clear it manually ++ b[6] &= 0xC0; ++ b[7] &= 0x03; ++ } ++ ++ for(i = 0; i < 8; i++) ++ { ++// NCB modified 20-9-02 for(j = 1; j &0x7f; j<<=1) ++ for(j = 1; j &0xff; j<<=1) ++ { ++ bit++; ++ if(b[i] & j) ++ { ++ ecc ^= bit; ++ } ++ } ++ } ++ ++ // Write out ECC ++ if (!convert_endian) ++ { ++ tags->ecc = ecc; ++ } ++ else ++ { ++ // We have to munge the ECC again. ++ b[6] |= ((ecc >> 6) & 0x3F); ++ b[7] |= ((ecc & 0x3F) << 2); ++ } ++} ++static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr) ++{ ++ yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr; ++ ++ //yaffs_CalcTagsECC(tagsPtr); ++ ++ sparePtr->tagByte0 = tu->asBytes[0]; ++ sparePtr->tagByte1 = tu->asBytes[1]; ++ sparePtr->tagByte2 = tu->asBytes[2]; ++ sparePtr->tagByte3 = tu->asBytes[3]; ++ sparePtr->tagByte4 = tu->asBytes[4]; ++ sparePtr->tagByte5 = tu->asBytes[5]; ++ sparePtr->tagByte6 = tu->asBytes[6]; ++ sparePtr->tagByte7 = tu->asBytes[7]; ++} ++ ++/* This little function converts a little endian tag to a big endian tag. ++ * NOTE: The tag is not usable after this other than calculating the CRC ++ * with. ++ */ ++static void little_to_big_endian(yaffs_Tags *tagsPtr) ++{ ++ yaffs_TagsUnion * tags = (yaffs_TagsUnion* )tagsPtr; // Work in bytes. ++ yaffs_TagsUnion temp; ++ ++ memset(&temp, 0, sizeof(temp)); ++ // Ick, I hate magic numbers. ++ temp.asBytes[0] = ((tags->asBytes[2] & 0x0F) << 4) | ((tags->asBytes[1] & 0xF0) >> 4); ++ temp.asBytes[1] = ((tags->asBytes[1] & 0x0F) << 4) | ((tags->asBytes[0] & 0xF0) >> 4); ++ temp.asBytes[2] = ((tags->asBytes[0] & 0x0F) << 4) | ((tags->asBytes[2] & 0x30) >> 2) | ((tags->asBytes[3] & 0xC0) >> 6); ++ temp.asBytes[3] = ((tags->asBytes[3] & 0x3F) << 2) | ((tags->asBytes[2] & 0xC0) >> 6); ++ temp.asBytes[4] = ((tags->asBytes[6] & 0x03) << 6) | ((tags->asBytes[5] & 0xFC) >> 2); ++ temp.asBytes[5] = ((tags->asBytes[5] & 0x03) << 6) | ((tags->asBytes[4] & 0xFC) >> 2); ++ temp.asBytes[6] = ((tags->asBytes[4] & 0x03) << 6) | (tags->asBytes[7] & 0x3F); ++ temp.asBytes[7] = (tags->asBytes[6] & 0xFC) | ((tags->asBytes[7] & 0xC0) >> 6); ++ ++ // Now copy it back. ++ tags->asBytes[0] = temp.asBytes[0]; ++ tags->asBytes[1] = temp.asBytes[1]; ++ tags->asBytes[2] = temp.asBytes[2]; ++ tags->asBytes[3] = temp.asBytes[3]; ++ tags->asBytes[4] = temp.asBytes[4]; ++ tags->asBytes[5] = temp.asBytes[5]; ++ tags->asBytes[6] = temp.asBytes[6]; ++ tags->asBytes[7] = temp.asBytes[7]; ++} ++ ++static int write_chunk(__u8 *data, __u32 objId, __u32 chunkId, __u32 nBytes) ++{ ++ yaffs_Tags t; ++ yaffs_Spare s; ++ ++ error = write(outFile,data,512); ++ if(error < 0) return error; ++ ++ memset(&t,0xff,sizeof (yaffs_Tags)); ++ memset(&s,0xff,sizeof (yaffs_Spare)); ++ ++ t.chunkId = chunkId; ++ t.serialNumber = 0; ++ t.byteCount = nBytes; ++ t.objectId = objId; ++ ++ if (convert_endian) ++ { ++ little_to_big_endian(&t); ++ } ++ ++ yaffs_CalcTagsECC(&t); ++ yaffs_LoadTagsIntoSpare(&s,&t); ++ yaffs_CalcECC(data,&s); ++ ++ nPages++; ++ ++ return write(outFile,&s,sizeof(yaffs_Spare)); ++ ++} ++ ++#define SWAP32(x) ((((x) & 0x000000FF) << 24) | \ ++ (((x) & 0x0000FF00) << 8 ) | \ ++ (((x) & 0x00FF0000) >> 8 ) | \ ++ (((x) & 0xFF000000) >> 24)) ++ ++#define SWAP16(x) ((((x) & 0x00FF) << 8) | \ ++ (((x) & 0xFF00) >> 8)) ++ ++// This one is easier, since the types are more standard. No funky shifts here. ++static void object_header_little_to_big_endian(yaffs_ObjectHeader* oh) ++{ ++ oh->type = SWAP32(oh->type); // GCC makes enums 32 bits. ++ oh->parentObjectId = SWAP32(oh->parentObjectId); // int ++ oh->sum__NoLongerUsed = SWAP16(oh->sum__NoLongerUsed); // __u16 - Not used, but done for completeness. ++ // name = skip. Char array. Not swapped. ++ oh->yst_mode = SWAP32(oh->yst_mode); ++#ifdef CONFIG_YAFFS_WINCE // WinCE doesn't implement this, but we need to just in case. ++ // In fact, WinCE would be *THE* place where this would be an issue! ++ oh->notForWinCE[0] = SWAP32(oh->notForWinCE[0]); ++ oh->notForWinCE[1] = SWAP32(oh->notForWinCE[1]); ++ oh->notForWinCE[2] = SWAP32(oh->notForWinCE[2]); ++ oh->notForWinCE[3] = SWAP32(oh->notForWinCE[3]); ++ oh->notForWinCE[4] = SWAP32(oh->notForWinCE[4]); ++#else ++ // Regular POSIX. ++ oh->yst_uid = SWAP32(oh->yst_uid); ++ oh->yst_gid = SWAP32(oh->yst_gid); ++ oh->yst_atime = SWAP32(oh->yst_atime); ++ oh->yst_mtime = SWAP32(oh->yst_mtime); ++ oh->yst_ctime = SWAP32(oh->yst_ctime); ++#endif ++ ++ oh->fileSize = SWAP32(oh->fileSize); // Aiee. An int... signed, at that! ++ oh->equivalentObjectId = SWAP32(oh->equivalentObjectId); ++ // alias - char array. ++ oh->yst_rdev = SWAP32(oh->yst_rdev); ++ ++#ifdef CONFIG_YAFFS_WINCE ++ oh->win_ctime[0] = SWAP32(oh->win_ctime[0]); ++ oh->win_ctime[1] = SWAP32(oh->win_ctime[1]); ++ oh->win_atime[0] = SWAP32(oh->win_atime[0]); ++ oh->win_atime[1] = SWAP32(oh->win_atime[1]); ++ oh->win_mtime[0] = SWAP32(oh->win_mtime[0]); ++ oh->win_mtime[1] = SWAP32(oh->win_mtime[1]); ++ oh->roomToGrow[0] = SWAP32(oh->roomToGrow[0]); ++ oh->roomToGrow[1] = SWAP32(oh->roomToGrow[1]); ++ oh->roomToGrow[2] = SWAP32(oh->roomToGrow[2]); ++ oh->roomToGrow[3] = SWAP32(oh->roomToGrow[3]); ++ oh->roomToGrow[4] = SWAP32(oh->roomToGrow[4]); ++ oh->roomToGrow[5] = SWAP32(oh->roomToGrow[5]); ++#else ++ oh->roomToGrow[0] = SWAP32(oh->roomToGrow[0]); ++ oh->roomToGrow[1] = SWAP32(oh->roomToGrow[1]); ++ oh->roomToGrow[2] = SWAP32(oh->roomToGrow[2]); ++ oh->roomToGrow[3] = SWAP32(oh->roomToGrow[3]); ++ oh->roomToGrow[4] = SWAP32(oh->roomToGrow[4]); ++ oh->roomToGrow[5] = SWAP32(oh->roomToGrow[5]); ++ oh->roomToGrow[6] = SWAP32(oh->roomToGrow[6]); ++ oh->roomToGrow[7] = SWAP32(oh->roomToGrow[7]); ++ oh->roomToGrow[8] = SWAP32(oh->roomToGrow[8]); ++ oh->roomToGrow[9] = SWAP32(oh->roomToGrow[9]); ++ oh->roomToGrow[10] = SWAP32(oh->roomToGrow[10]); ++ oh->roomToGrow[11] = SWAP32(oh->roomToGrow[11]); ++#endif ++} ++ ++static int write_object_header(int objId, yaffs_ObjectType t, struct stat *s, int parent, const char *name, int equivalentObj, const char * alias) ++{ ++ __u8 bytes[512]; ++ ++ ++ yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bytes; ++ ++ memset(bytes,0xff,512); ++ ++ oh->type = t; ++ ++ oh->parentObjectId = parent; ++ ++ strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH); ++ ++ ++ if(t != YAFFS_OBJECT_TYPE_HARDLINK) ++ { ++ oh->yst_mode = s->st_mode; ++ oh->yst_uid = s->st_uid; ++// NCB 12/9/02 oh->yst_gid = s->yst_uid; ++ oh->yst_gid = s->st_gid; ++ oh->yst_atime = s->st_atime; ++ oh->yst_mtime = s->st_mtime; ++ oh->yst_ctime = s->st_ctime; ++ oh->yst_rdev = s->st_rdev; ++ } ++ ++ if(t == YAFFS_OBJECT_TYPE_FILE) ++ { ++ oh->fileSize = s->st_size; ++ } ++ ++ if(t == YAFFS_OBJECT_TYPE_HARDLINK) ++ { ++ oh->equivalentObjectId = equivalentObj; ++ } ++ ++ if(t == YAFFS_OBJECT_TYPE_SYMLINK) ++ { ++ strncpy(oh->alias,alias,YAFFS_MAX_ALIAS_LENGTH); ++ } ++ ++ if (convert_endian) ++ { ++ object_header_little_to_big_endian(oh); ++ } ++ ++ return write_chunk(bytes,objId,0,0xffff); ++ ++} ++ ++ ++static int process_directory(int parent, const char *path) ++{ ++ ++ DIR *dir; ++ struct dirent *entry; ++ ++ nDirectories++; ++ ++ dir = opendir(path); ++ ++ if(dir) ++ { ++ while((entry = readdir(dir)) != NULL) ++ { ++ ++ /* Ignore . and .. */ ++ if(strcmp(entry->d_name,".") && ++ strcmp(entry->d_name,"..")) ++ { ++ char full_name[500]; ++ struct stat stats; ++ int equivalentObj; ++ int newObj; ++ ++ sprintf(full_name,"%s/%s",path,entry->d_name); ++ ++ lstat(full_name,&stats); ++ ++ if(S_ISLNK(stats.st_mode) || ++ S_ISREG(stats.st_mode) || ++ S_ISDIR(stats.st_mode) || ++ S_ISFIFO(stats.st_mode) || ++ S_ISBLK(stats.st_mode) || ++ S_ISCHR(stats.st_mode) || ++ S_ISSOCK(stats.st_mode)) ++ { ++ ++ newObj = obj_id++; ++ nObjects++; ++ ++ printf("Object %d, %s is a ",newObj,full_name); ++ ++ /* We're going to create an object for it */ ++ if((equivalentObj = find_obj_in_list(stats.st_dev, stats.st_ino)) > 0) ++ { ++ /* we need to make a hard link */ ++ printf("hard link to object %d\n",equivalentObj); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_HARDLINK, &stats, parent, entry->d_name, equivalentObj, NULL); ++ } ++ else ++ { ++ ++ add_obj_to_list(stats.st_dev,stats.st_ino,newObj); ++ ++ if(S_ISLNK(stats.st_mode)) ++ { ++ ++ char symname[500]; ++ ++ memset(symname,0, sizeof(symname)); ++ ++ readlink(full_name,symname,sizeof(symname) -1); ++ ++ printf("symlink to \"%s\"\n",symname); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SYMLINK, &stats, parent, entry->d_name, -1, symname); ++ ++ } ++ else if(S_ISREG(stats.st_mode)) ++ { ++ printf("file, "); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_FILE, &stats, parent, entry->d_name, -1, NULL); ++ ++ if(error >= 0) ++ { ++ int h; ++ __u8 bytes[512]; ++ int nBytes; ++ int chunk = 0; ++ ++ h = open(full_name,O_RDONLY); ++ if(h >= 0) ++ { ++ memset(bytes,0xff,512); ++ while((nBytes = read(h,bytes,512)) > 0) ++ { ++ chunk++; ++ write_chunk(bytes,newObj,chunk,nBytes); ++ memset(bytes,0xff,512); ++ } ++ if(nBytes < 0) ++ error = nBytes; ++ ++ printf("%d data chunks written\n",chunk); ++ } ++ else ++ { ++ perror("Error opening file"); ++ } ++ close(h); ++ ++ } ++ ++ } ++ else if(S_ISSOCK(stats.st_mode)) ++ { ++ printf("socket\n"); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL); ++ } ++ else if(S_ISFIFO(stats.st_mode)) ++ { ++ printf("fifo\n"); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL); ++ } ++ else if(S_ISCHR(stats.st_mode)) ++ { ++ printf("character device\n"); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL); ++ } ++ else if(S_ISBLK(stats.st_mode)) ++ { ++ printf("block device\n"); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL); ++ } ++ else if(S_ISDIR(stats.st_mode)) ++ { ++ printf("directory\n"); ++ error = write_object_header(newObj, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, parent, entry->d_name, -1, NULL); ++// NCB modified 10/9/2001 process_directory(1,full_name); ++ process_directory(newObj,full_name); ++ } ++ } ++ } ++ else ++ { ++ printf(" we don't handle this type\n"); ++ } ++ } ++ } ++ } ++ ++ return 0; ++ ++} ++ ++ ++int main(int argc, char *argv[]) ++{ ++ struct stat stats; ++ ++ printf("mkyaffsimage: image building tool for YAFFS built "__DATE__"\n"); ++ ++ if(argc < 3) ++ { ++ printf("usage: mkyaffsimage dir image_file [convert]\n"); ++ printf(" dir the directory tree to be converted\n"); ++ printf(" image_file the output file to hold the image\n"); ++ printf(" 'convert' produce a big-endian image from a little-endian machine\n"); ++ exit(1); ++ } ++ ++ if ((argc == 4) && (!strncmp(argv[3], "convert", strlen("convert")))) ++ { ++ convert_endian = 1; ++ } ++ ++ if(stat(argv[1],&stats) < 0) ++ { ++ printf("Could not stat %s\n",argv[1]); ++ exit(1); ++ } ++ ++ if(!S_ISDIR(stats.st_mode)) ++ { ++ printf(" %s is not a directory\n",argv[1]); ++ exit(1); ++ } ++ ++ outFile = open(argv[2],O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE); ++ ++ ++ if(outFile < 0) ++ { ++ printf("Could not open output file %s\n",argv[2]); ++ exit(1); ++ } ++ ++ printf("Processing directory %s into image file %s\n",argv[1],argv[2]); ++ error = write_object_header(1, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, 1,"", -1, NULL); ++ if(error) ++ error = process_directory(YAFFS_OBJECTID_ROOT,argv[1]); ++ ++ close(outFile); ++ ++ if(error < 0) ++ { ++ perror("operation incomplete"); ++ exit(1); ++ } ++ else ++ { ++ printf("Operation complete.\n" ++ "%d objects in %d directories\n" ++ "%d NAND pages\n",nObjects, nDirectories, nPages); ++ } ++ ++ close(outFile); ++ ++ exit(0); ++} ++ +diff -urN linux-2.6.24.7/fs/yaffs2/utils/ssfdc_rs_ecc.c linux-2.6.24.7.new/fs/yaffs2/utils/ssfdc_rs_ecc.c +--- linux-2.6.24.7/fs/yaffs2/utils/ssfdc_rs_ecc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7.new/fs/yaffs2/utils/ssfdc_rs_ecc.c 2009-04-21 15:01:02.000000000 +0200 +@@ -0,0 +1,180 @@ ++/* Reed-Solomon decoder ++ * Copyright 2002 Phil Karn, KA9Q ++ * May be used under the terms of the GNU Lesser General Public License (LGPL) ++ */ ++#include "ssfdc_rs_ecc.h" ++ ++#include ++ ++struct CONV_DATA { ++ unsigned char shift0; ++ unsigned char mask0; ++ unsigned char mask1; ++ unsigned char merge_shift; ++} ConvData [8]= { ++ {0x0, 0xff, 0x01, 0x8}, /* 0 */ ++ {0x1, 0x7f, 0x03, 0x7}, /* 1 */ ++ {0x2, 0x3f, 0x07, 0x6}, /* 2 */ ++ {0x3, 0x1f, 0x0f, 0x5}, /* 3 */ ++ {0x4, 0x0f, 0x1f, 0x4}, /* 4 */ ++ {0x5, 0x07, 0x3f, 0x3}, /* 5 */ ++ {0x6, 0x03, 0x7f, 0x2}, /* 6 */ ++ {0x7, 0x01, 0xff, 0x1}, /* 7 */ ++}; ++ ++static void kfree(void *ptr) ++{ ++// return 0; ++} ++ ++static int modnn(struct rs *rs,int x) ++{ ++ while (x >= rs->nn) { ++ x -= rs->nn; ++ x = (x >> rs->mm) + (x & rs->nn); ++ } ++ return x; ++} ++ ++void encode_rs(void *p,data_t *data, unsigned short *bb) ++{ ++ struct rs *rs = (struct rs *)p; ++ int i, j; ++ data_t feedback; ++ ++ memset(bb,0,(rs->nroots)*sizeof(unsigned short)); ++ ++ for(i=0;i<(rs->nn)-(rs->nroots)-(rs->pad);i++){ ++ ++ feedback = (rs->index_of)[data[i] ^ bb[0]]; ++ if(feedback != ((rs->nn))){ ++ for(j=1;j<(rs->nroots);j++) ++ bb[j] ^= (rs->alpha_to)[modnn(rs,feedback + (rs->genpoly)[(rs->nroots)-j])]; ++ } ++ ++ memmove(&bb[0],&bb[1],sizeof(unsigned short)*((rs->nroots)-1)); ++ ++ if(feedback != ((rs->nn))) { ++ bb[(rs->nroots)-1] = (rs->alpha_to)[modnn(rs,feedback + (rs->genpoly)[0])]; ++ } ++ else ++ bb[(rs->nroots)-1] = 0; ++ } ++ ++} ++ ++void free_rs_int(void *p) ++{ ++ struct rs *rs = (struct rs *)p; ++ ++ free(rs->alpha_to); ++ free(rs->index_of); ++ free(rs->genpoly); ++ free(rs); ++} ++ ++/* ++ * init_rs_int - Initialize a Reed-Solomon codec ++ * @symsize: symbol size ++ * @gfpoly: Field generator polynomial coefficients ++ * @fcr: first root of RS code generator polynomial, index form ++ * @prim: primitive element to generate polynomial roots ++ * @nroots: RS code generator polynomial degree (number of roots) ++ */ ++struct rs *init_rs_int(int symsize,int gfpoly,int fcr,int prim,int nroots,int pad) ++{ ++ struct rs *rs; ++#if 0 ++ static unsigned int rs0[64]={0}; ++ static data_t alpha0[512]={0}; ++ static data_t index0[512]={0}; ++ static data_t genepoly0[9]={0}; ++#endif ++ int i, j, sr,root,iprim; ++ ++ rs = ((void *)0); ++ if(symsize < 0 || symsize > 8*sizeof(data_t)){ ++ goto done; ++ } ++ ++ if(fcr < 0 || fcr >= (1<= (1<= (1<= ((1<mm = symsize; ++ rs->nn = (1<pad = pad; ++ rs->alpha_to = (data_t *)malloc(sizeof(data_t)*(rs->nn+1)); ++// rs->alpha_to = alpha0; ++ if(rs->alpha_to == ((void *)0)){ ++ kfree(rs); ++ rs = ((void *)0); ++ goto done; ++ } ++ rs->index_of = (data_t *)malloc(sizeof(data_t)*(rs->nn+1)); ++// rs->index_of = index0; ++ if(rs->index_of == ((void *)0)){ ++ kfree(rs->alpha_to); ++ kfree(rs); ++ rs = ((void *)0); ++ goto done; ++ } ++ rs->index_of[0] = ((rs->nn)); ++ rs->alpha_to[((rs->nn))] = 0; ++ sr = 1; ++ for(i=0;inn;i++){ ++ rs->index_of[sr] = i; ++ rs->alpha_to[i] = sr; ++ sr <<= 1; ++ if(sr & (1<nn; ++ } ++ if(sr != 1){ ++ kfree(rs->alpha_to); ++ kfree(rs->index_of); ++ kfree(rs); ++ rs = ((void *)0); ++ goto done; ++ } ++ rs->genpoly = (data_t *)malloc(sizeof(data_t)*(nroots+1)); ++// rs->genpoly = genepoly0; ++ if(rs->genpoly == ((void *)0)){ ++ kfree(rs->alpha_to); ++ kfree(rs->index_of); ++ kfree(rs); ++ rs = ((void *)0); ++ goto done; ++ } ++ rs->fcr = fcr; ++ rs->prim = prim; ++ rs->nroots = nroots; ++ for(iprim=1;(iprim % prim) != 0;iprim += rs->nn) ++ ; ++ rs->iprim = iprim / prim; ++ rs->genpoly[0] = 1; ++ for (i = 0,root=fcr*prim; i < nroots; i++,root += prim) { ++ rs->genpoly[i+1] = 1; ++ for (j = i; j > 0; j--){ ++ if (rs->genpoly[j] != 0) ++ rs->genpoly[j] = rs->genpoly[j-1] ^ rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[j]] + root)]; ++ else ++ rs->genpoly[j] = rs->genpoly[j-1]; ++ } ++ rs->genpoly[0] = rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[0]] + root)]; ++ } ++ for (i = 0; i <= nroots; i++) ++ rs->genpoly[i] = rs->index_of[rs->genpoly[i]]; ++ done:; ++ return rs; ++} ++ +diff -urN linux-2.6.24.7/fs/yaffs2/utils/ssfdc_rs_ecc.h linux-2.6.24.7.new/fs/yaffs2/utils/ssfdc_rs_ecc.h +--- linux-2.6.24.7/fs/yaffs2/utils/ssfdc_rs_ecc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24.7.new/fs/yaffs2/utils/ssfdc_rs_ecc.h 2009-04-21 15:01:02.000000000 +0200 +@@ -0,0 +1,29 @@ ++#ifndef __SSFDC_RS_ECC_H__ ++#define __SSFDC_RS_ECC_H__ ++ ++/* Stuff common to all the general-purpose Reed-Solomon codecs ++ * Copyright 2004 Phil Karn, KA9Q ++ * May be used under the terms of the GNU Lesser General Public License (LGPL) ++ */ ++ ++/* Reed-Solomon codec control block */ ++typedef unsigned char data_t; ++ ++struct rs { ++ int mm; /* Bits per symbol */ ++ int nn; /* Symbols per block (= (1<nErasedBlocks - dev->nReservedBlocks; +- ++ + T(YAFFS_TRACE_CHECKPOINT, + (TSTR("checkpt blocks available = %d" TENDSTR), + blocksAvailable)); +- +- ++ ++ + return (blocksAvailable <= 0) ? 0 : 1; + } + + ++ + static int yaffs_CheckpointErase(yaffs_Device *dev) + { +- ++ + int i; ++ + +- +- if(!dev->eraseBlockInNAND) ++ if(!dev->eraseBlockInNAND) + return 0; + T(YAFFS_TRACE_CHECKPOINT,(TSTR("checking blocks %d to %d"TENDSTR), + dev->internalStartBlock,dev->internalEndBlock)); +- ++ + for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); + if(bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT){ +@@ -58,9 +59,9 @@ + } + } + } +- ++ + dev->blocksInCheckpoint = 0; +- ++ + return 1; + } + +@@ -72,11 +73,11 @@ + T(YAFFS_TRACE_CHECKPOINT, + (TSTR("allocating checkpt block: erased %d reserved %d avail %d next %d "TENDSTR), + dev->nErasedBlocks,dev->nReservedBlocks,blocksAvailable,dev->checkpointNextBlock)); +- ++ + if(dev->checkpointNextBlock >= 0 && + dev->checkpointNextBlock <= dev->internalEndBlock && + blocksAvailable > 0){ +- ++ + for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); + if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY){ +@@ -88,7 +89,7 @@ + } + } + T(YAFFS_TRACE_CHECKPOINT,(TSTR("out of checkpt blocks"TENDSTR))); +- ++ + dev->checkpointNextBlock = -1; + dev->checkpointCurrentBlock = -1; + } +@@ -97,19 +98,19 @@ + { + int i; + yaffs_ExtendedTags tags; +- ++ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: start: blocks %d next %d" TENDSTR), + dev->blocksInCheckpoint, dev->checkpointNextBlock)); +- +- if(dev->blocksInCheckpoint < dev->checkpointMaxBlocks) ++ ++ if(dev->blocksInCheckpoint < dev->checkpointMaxBlocks) + for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){ + int chunk = i * dev->nChunksPerBlock; + int realignedChunk = chunk - dev->chunkOffset; + + dev->readChunkWithTagsFromNAND(dev,realignedChunk,NULL,&tags); +- T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: search: block %d oid %d seq %d eccr %d" TENDSTR), ++ T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: search: block %d oid %d seq %d eccr %d" TENDSTR), + i, tags.objectId,tags.sequenceNumber,tags.eccResult)); +- ++ + if(tags.sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA){ + /* Right kind of block */ + dev->checkpointNextBlock = tags.objectId; +@@ -130,7 +131,7 @@ + + int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting) + { +- ++ + /* Got the functions we need? */ + if (!dev->writeChunkWithTagsToNAND || + !dev->readChunkWithTagsFromNAND || +@@ -140,31 +141,29 @@ + + if(forWriting && !yaffs_CheckpointSpaceOk(dev)) + return 0; +- ++ + if(!dev->checkpointBuffer) + dev->checkpointBuffer = YMALLOC_DMA(dev->nDataBytesPerChunk); + if(!dev->checkpointBuffer) + return 0; + +- ++ + dev->checkpointPageSequence = 0; +- ++ + dev->checkpointOpenForWrite = forWriting; +- ++ + dev->checkpointByteCount = 0; +- dev->checkpointSum = 0; +- dev->checkpointXor = 0; + dev->checkpointCurrentBlock = -1; + dev->checkpointCurrentChunk = -1; + dev->checkpointNextBlock = dev->internalStartBlock; +- ++ + /* Erase all the blocks in the checkpoint area */ + if(forWriting){ + memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk); + dev->checkpointByteOffset = 0; + return yaffs_CheckpointErase(dev); +- +- ++ ++ + } else { + int i; + /* Set to a value that will kick off a read */ +@@ -177,15 +176,7 @@ + for(i = 0; i < dev->checkpointMaxBlocks; i++) + dev->checkpointBlockList[i] = -1; + } +- +- return 1; +-} +- +-int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum) +-{ +- __u32 compositeSum; +- compositeSum = (dev->checkpointSum << 8) | (dev->checkpointXor & 0xFF); +- *sum = compositeSum; ++ + return 1; + } + +@@ -196,15 +187,15 @@ + int realignedChunk; + + yaffs_ExtendedTags tags; +- ++ + if(dev->checkpointCurrentBlock < 0){ + yaffs_CheckpointFindNextErasedBlock(dev); + dev->checkpointCurrentChunk = 0; + } +- ++ + if(dev->checkpointCurrentBlock < 0) + return 0; +- ++ + tags.chunkDeleted = 0; + tags.objectId = dev->checkpointNextBlock; /* Hint to next place to look */ + tags.chunkId = dev->checkpointPageSequence + 1; +@@ -217,25 +208,25 @@ + bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT; + dev->blocksInCheckpoint++; + } +- ++ + chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + dev->checkpointCurrentChunk; + +- ++ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint wite buffer nand %d(%d:%d) objid %d chId %d" TENDSTR), +- chunk, dev->checkpointCurrentBlock, dev->checkpointCurrentChunk,tags.objectId,tags.chunkId)); +- ++ chunk, dev->checkpointCurrentBlock, dev->checkpointCurrentChunk,tags.objectId,tags.chunkId)); ++ + realignedChunk = chunk - dev->chunkOffset; +- ++ + dev->writeChunkWithTagsToNAND(dev,realignedChunk,dev->checkpointBuffer,&tags); + dev->checkpointByteOffset = 0; +- dev->checkpointPageSequence++; ++ dev->checkpointPageSequence++; + dev->checkpointCurrentChunk++; + if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock){ + dev->checkpointCurrentChunk = 0; + dev->checkpointCurrentBlock = -1; + } + memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk); +- ++ + return 1; + } + +@@ -245,37 +236,31 @@ + int i=0; + int ok = 1; + +- ++ + __u8 * dataBytes = (__u8 *)data; +- +- ++ ++ + + if(!dev->checkpointBuffer) + return 0; + +- if(!dev->checkpointOpenForWrite) +- return -1; +- + while(i < nBytes && ok) { ++ + +- +- +- dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ; +- dev->checkpointSum += *dataBytes; +- dev->checkpointXor ^= *dataBytes; +- ++ ++ dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ; + dev->checkpointByteOffset++; + i++; + dataBytes++; + dev->checkpointByteCount++; +- +- ++ ++ + if(dev->checkpointByteOffset < 0 || +- dev->checkpointByteOffset >= dev->nDataBytesPerChunk) ++ dev->checkpointByteOffset >= dev->nDataBytesPerChunk) + ok = yaffs_CheckpointFlushBuffer(dev); + + } +- ++ + return i; + } + +@@ -285,44 +270,41 @@ + int ok = 1; + yaffs_ExtendedTags tags; + +- ++ + int chunk; + int realignedChunk; + + __u8 *dataBytes = (__u8 *)data; +- ++ + if(!dev->checkpointBuffer) + return 0; + +- if(dev->checkpointOpenForWrite) +- return -1; +- + while(i < nBytes && ok) { +- +- ++ ++ + if(dev->checkpointByteOffset < 0 || + dev->checkpointByteOffset >= dev->nDataBytesPerChunk) { +- ++ + if(dev->checkpointCurrentBlock < 0){ + yaffs_CheckpointFindNextCheckpointBlock(dev); + dev->checkpointCurrentChunk = 0; + } +- ++ + if(dev->checkpointCurrentBlock < 0) + ok = 0; + else { +- +- chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + ++ ++ chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + + dev->checkpointCurrentChunk; + + realignedChunk = chunk - dev->chunkOffset; + + /* read in the next chunk */ + /* printf("read checkpoint page %d\n",dev->checkpointPage); */ +- dev->readChunkWithTagsFromNAND(dev, realignedChunk, ++ dev->readChunkWithTagsFromNAND(dev, realignedChunk, + dev->checkpointBuffer, + &tags); +- ++ + if(tags.chunkId != (dev->checkpointPageSequence + 1) || + tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA) + ok = 0; +@@ -330,30 +312,28 @@ + dev->checkpointByteOffset = 0; + dev->checkpointPageSequence++; + dev->checkpointCurrentChunk++; +- ++ + if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock) + dev->checkpointCurrentBlock = -1; + } + } +- ++ + if(ok){ + *dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset]; +- dev->checkpointSum += *dataBytes; +- dev->checkpointXor ^= *dataBytes; + dev->checkpointByteOffset++; + i++; + dataBytes++; + dev->checkpointByteCount++; + } + } +- ++ + return i; + } + + int yaffs_CheckpointClose(yaffs_Device *dev) + { + +- if(dev->checkpointOpenForWrite){ ++ if(dev->checkpointOpenForWrite){ + if(dev->checkpointByteOffset != 0) + yaffs_CheckpointFlushBuffer(dev); + } else { +@@ -373,19 +353,19 @@ + dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock; + dev->nErasedBlocks -= dev->blocksInCheckpoint; + +- ++ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint byte count %d" TENDSTR), + dev->checkpointByteCount)); +- ++ + if(dev->checkpointBuffer){ +- /* free the buffer */ ++ /* free the buffer */ + YFREE(dev->checkpointBuffer); + dev->checkpointBuffer = NULL; + return 1; + } + else + return 0; +- ++ + } + + int yaffs_CheckpointInvalidateStream(yaffs_Device *dev) +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_checkptrw.h linux-2.6.24.7.new/fs/yaffs2/yaffs_checkptrw.h +--- linux-2.6.24.7/fs/yaffs2/yaffs_checkptrw.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_checkptrw.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +@@ -24,8 +24,6 @@ + + int yaffs_CheckpointRead(yaffs_Device *dev,void *data, int nBytes); + +-int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum); +- + int yaffs_CheckpointClose(yaffs_Device *dev); + + int yaffs_CheckpointInvalidateStream(yaffs_Device *dev); +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_ecc.c linux-2.6.24.7.new/fs/yaffs2/yaffs_ecc.c +--- linux-2.6.24.7/fs/yaffs2/yaffs_ecc.c 2009-04-21 14:57:10.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_ecc.c 2009-04-21 15:01:02.000000000 +0200 +@@ -14,9 +14,9 @@ + /* + * This code implements the ECC algorithm used in SmartMedia. + * +- * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. + * The two unused bit are set to 1. +- * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC ++ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC + * blocks are used on a 512-byte NAND page. + * + */ +@@ -29,7 +29,7 @@ + */ + + const char *yaffs_ecc_c_version = +- "$Id: yaffs_ecc.c,v 1.9 2007-02-14 01:09:06 wookey Exp $"; ++ "$Id: yaffs_ecc.c,v 1.2 2008-07-17 23:07:00 lhhuang Exp $"; + + #include "yportenv.h" + +@@ -83,17 +83,6 @@ + return r; + } + +-static int yaffs_CountBits32(unsigned x) +-{ +- int r = 0; +- while (x) { +- if (x & 1) +- r++; +- x >>= 1; +- } +- return r; +-} +- + /* Calculate the ECC for a 256-byte block of data */ + void yaffs_ECCCalculate(const unsigned char *data, unsigned char *ecc) + { +@@ -228,8 +217,8 @@ + return 1; /* Corrected the error */ + } + +- if ((yaffs_CountBits(d0) + +- yaffs_CountBits(d1) + ++ if ((yaffs_CountBits(d0) + ++ yaffs_CountBits(d1) + + yaffs_CountBits(d2)) == 1) { + /* Reccoverable error in ecc */ + +@@ -239,13 +228,115 @@ + + return 1; /* Corrected the error */ + } +- ++ + /* Unrecoverable error */ + + return -1; + + } + ++#if defined(CONFIG_YAFFS_ECC_RS) ++#ifdef __KERNEL__ ++#include ++struct rs_control *rs_decoder; ++#else ++#include "ssfdc_rs_ecc.h" ++void *rs_init_user; ++#endif ++ ++/* Transfer 16 bytes to 26 5-bit symbols */ ++void Data2Sym(const unsigned char *in, unsigned char *out) ++{ ++ int i, j, shift; ++ ++ for (i = 0; i < 26; i++){ ++ j = (5 * i) >> 3; ++ shift = (5 * i) & 0x7; ++ if (shift > 3) ++ out[i] = ((in[j] >> shift) | (in[j+1] << (8 - shift))) & 0x1f; ++ else ++ out[i] = (in[j] >> shift) & 0x1f; ++ } ++ out[25] &= 0x7; /* the last symbol has only 3 bits */ ++} ++ ++/* ++ * It does reed solomon ECC calcs on 16 bytes of oob data ++ */ ++void yaffs_ECCCalculateOther(const unsigned char *data, unsigned nBytes, ++ yaffs_ECCOther * eccOther) ++{ ++ unsigned short *par = (unsigned short *)&eccOther->lineParity; ++ unsigned char data5[26]; ++ ++ ++ Data2Sym(data, data5); ++ memset(par, 0, 8); ++ ++#ifdef __KERNEL__ ++ /* Encode 26 symbols in data5. Store parities of 4 symbols in ++ * buffer par whose size is 8 bytes(2 bytes per symbol) */ ++ encode_rs8 (rs_decoder, data5, 26, par, 0); ++#else ++ /* init reed solomon ECC for nand oob area ++ * Symbolsize is 5 (bits) ++ * Primitive polynomial is x^5+x^2+1 ++ * first consecutive root is 0 ++ * primitive element to generate roots = 1 ++ * generator polynomial degree (number of roots) = 4 ++ * pad = (1<lineParity; ++ unsigned char data5[26]; ++ int numerr; ++ ++ Data2Sym(data, data5); ++ ++ /* Decode 26 symbols in data5. */ ++ numerr = decode_rs8 (rs_decoder, data5, par, 26, NULL, 0, NULL, 0, NULL); ++ ++ if (numerr == 0) ++ return 0; ++ else if (numerr > 0 && numerr < 3) ++ return 1; ++ else ++ return -1; ++} ++#else ++int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes, ++ yaffs_ECCOther * read_ecc, ++ const yaffs_ECCOther * test_ecc) ++{ ++ return 0; ++} ++#endif ++ ++#else ++ ++static int yaffs_CountBits32(unsigned x) ++{ ++ int r = 0; ++ while (x) { ++ if (x & 1) ++ r++; ++ x >>= 1; ++ } ++ return r; ++} + + /* + * ECCxxxOther does ECC calcs on arbitrary n bytes of data +@@ -293,7 +384,7 @@ + if ((cDelta | lDelta | lDeltaPrime) == 0) + return 0; /* no error */ + +- if (lDelta == ~lDeltaPrime && ++ if (lDelta == ~lDeltaPrime && + (((cDelta ^ (cDelta >> 1)) & 0x15) == 0x15)) + { + /* Single bit (recoverable) error in data */ +@@ -309,7 +400,7 @@ + + if(lDelta >= nBytes) + return -1; +- ++ + data[lDelta] ^= (1 << bit); + + return 1; /* corrected */ +@@ -328,4 +419,4 @@ + return -1; + + } +- ++#endif /* YAFFS_ECC_RS */ +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_ecc.h linux-2.6.24.7.new/fs/yaffs2/yaffs_ecc.h +--- linux-2.6.24.7/fs/yaffs2/yaffs_ecc.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_ecc.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +@@ -16,9 +16,9 @@ + /* + * This code implements the ECC algorithm used in SmartMedia. + * +- * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. + * The two unused bit are set to 1. +- * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC ++ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC + * blocks are used on a 512-byte NAND page. + * + */ +@@ -32,6 +32,10 @@ + unsigned lineParityPrime; + } yaffs_ECCOther; + ++#if defined(CONFIG_YAFFS_ECC_RS) ++extern struct rs_control *rs_decoder; ++#endif ++ + void yaffs_ECCCalculate(const unsigned char *data, unsigned char *ecc); + int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc, + const unsigned char *test_ecc); +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_fs.c linux-2.6.24.7.new/fs/yaffs2/yaffs_fs.c +--- linux-2.6.24.7/fs/yaffs2/yaffs_fs.c 2009-04-21 14:57:10.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_fs.c 2009-04-21 15:01:02.000000000 +0200 +@@ -23,7 +23,7 @@ + * This is the file system front-end to YAFFS that hooks it up to + * the VFS. + * +- * Special notes: ++ * Special notes: + * >> 2.4: sb->u.generic_sbp points to the yaffs_Device associated with + * this superblock + * >> 2.6: sb->s_fs_info points to the yaffs_Device associated with this +@@ -32,7 +32,7 @@ + */ + + const char *yaffs_fs_c_version = +- "$Id: yaffs_fs.c,v 1.63 2007-09-19 20:35:40 imcd Exp $"; ++ "$Id: yaffs_fs.c,v 1.2 2008-07-17 23:59:16 lhhuang Exp $"; + extern const char *yaffs_guts_c_version; + + #include +@@ -76,34 +76,23 @@ + + #endif + +-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +-#define WRITE_SIZE_STR "writesize" +-#define WRITE_SIZE(mtd) (mtd)->writesize +-#else +-#define WRITE_SIZE_STR "oobblock" +-#define WRITE_SIZE(mtd) (mtd)->oobblock +-#endif +- + #include + + #include "yportenv.h" + #include "yaffs_guts.h" + ++unsigned yaffs_traceMask = YAFFS_TRACE_ALWAYS | ++ YAFFS_TRACE_BAD_BLOCKS/* | ++ YAFFS_TRACE_CHECKPOINT*/ ++ /* | 0xFFFFFFFF */; ++ + #include + #include "yaffs_mtdif.h" +-#include "yaffs_mtdif1.h" + #include "yaffs_mtdif2.h" + +-unsigned int yaffs_traceMask = YAFFS_TRACE_BAD_BLOCKS; +-unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; +- +-/* Module Parameters */ +-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +-module_param(yaffs_traceMask,uint,0644); +-module_param(yaffs_wr_attempts,uint,0644); +-#else +-MODULE_PARM(yaffs_traceMask,"i"); +-MODULE_PARM(yaffs_wr_attempts,"i"); ++#if defined(CONFIG_YAFFS_ECC_RS) ++#include ++#include "yaffs_ecc.h" + #endif + + /*#define T(x) printk x */ +@@ -174,6 +163,8 @@ + static int yaffs_write_super(struct super_block *sb); + #endif + ++static int yaffs_remount_fs(struct super_block *, int *, char *); ++ + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf); + #elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +@@ -213,45 +204,24 @@ + .commit_write = yaffs_commit_write, + }; + +-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22)) +-static struct file_operations yaffs_file_operations = { +- .read = do_sync_read, +- .write = do_sync_write, +- .aio_read = generic_file_aio_read, +- .aio_write = generic_file_aio_write, +- .mmap = generic_file_mmap, +- .flush = yaffs_file_flush, +- .fsync = yaffs_sync_object, +- .splice_read = generic_file_splice_read, +- .splice_write = generic_file_splice_write, +-}; +- +-#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) +- + static struct file_operations yaffs_file_operations = { ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, +- .mmap = generic_file_mmap, +- .flush = yaffs_file_flush, +- .fsync = yaffs_sync_object, +- .sendfile = generic_file_sendfile, +-}; +- + #else +- +-static struct file_operations yaffs_file_operations = { + .read = generic_file_read, + .write = generic_file_write, ++#endif + .mmap = generic_file_mmap, + .flush = yaffs_file_flush, + .fsync = yaffs_sync_object, +-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)) + .sendfile = generic_file_sendfile, + #endif ++ + }; +-#endif + + static struct inode_operations yaffs_file_inode_operations = { + .setattr = yaffs_setattr, +@@ -287,6 +257,7 @@ + .read_inode = yaffs_read_inode, + .put_inode = yaffs_put_inode, + .put_super = yaffs_put_super, ++ .remount_fs = yaffs_remount_fs, + .delete_inode = yaffs_delete_inode, + .clear_inode = yaffs_clear_inode, + .sync_fs = yaffs_sync_fs, +@@ -391,7 +362,7 @@ + dentry->d_name.name); + + obj = yaffs_GetEquivalentObject(obj); /* in case it was a hardlink */ +- ++ + /* Can't hold gross lock when calling yaffs_get_inode() */ + yaffs_GrossUnlock(dev); + +@@ -429,6 +400,42 @@ + + } + ++static int yaffs_dump_dev1(yaffs_Device * dev) ++{ ++ printk("startBlock......... %d\n", dev->startBlock); ++ printk("endBlock........... %d\n", dev->endBlock); ++ printk("chunkGroupBits..... %d\n", dev->chunkGroupBits); ++ printk("chunkGroupSize..... %d\n", dev->chunkGroupSize); ++ printk("nErasedBlocks...... %d\n", dev->nErasedBlocks); ++ printk("nTnodesCreated..... %d\n", dev->nTnodesCreated); ++ printk("nFreeTnodes........ %d\n", dev->nFreeTnodes); ++ printk("nObjectsCreated.... %d\n", dev->nObjectsCreated); ++ printk("nFreeObjects....... %d\n", dev->nFreeObjects); ++ printk("nFreeChunks........ %d\n", dev->nFreeChunks); ++ printk("nPageWrites........ %d\n", dev->nPageWrites); ++ printk("nPageReads......... %d\n", dev->nPageReads); ++ printk("nBlockErasures..... %d\n", dev->nBlockErasures); ++ printk("nGCCopies.......... %d\n", dev->nGCCopies); ++ printk("garbageCollections. %d\n", dev->garbageCollections); ++ ++ printk("passiveGCs......... %d\n", dev->passiveGarbageCollections); ++ printk("nRetriedWrites..... %d\n", dev->nRetriedWrites); ++ printk("nRetireBlocks...... %d\n", dev->nRetiredBlocks); ++ printk("eccFixed........... %d\n", dev->eccFixed); ++ printk("eccUnfixed......... %d\n", dev->eccUnfixed); ++ printk("tagsEccFixed....... %d\n", dev->tagsEccFixed); ++ printk("tagsEccUnfixed..... %d\n", dev->tagsEccUnfixed); ++ printk("cacheHits.......... %d\n", dev->cacheHits); ++ printk("nDeletedFiles...... %d\n", dev->nDeletedFiles); ++ printk("nUnlinkedFiles..... %d\n", dev->nUnlinkedFiles); ++ printk("nBackgroudDeletions %d\n", dev->nBackgroundDeletions); ++ printk("useNANDECC......... %d\n", dev->useNANDECC); ++ printk("isYaffs2........... %d\n", dev->isYaffs2); ++ ++ return 1; ++} ++ ++ + /* For now put inode is just for debugging + * Put inode is called when the inode **structure** is put. + */ +@@ -729,21 +736,21 @@ + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFREG; + } +- ++ + break; + case YAFFS_OBJECT_TYPE_SYMLINK : + if( ! S_ISLNK(mode) ){ + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFLNK; + } +- ++ + break; + case YAFFS_OBJECT_TYPE_DIRECTORY : + if( ! S_ISDIR(mode) ){ + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFDIR; + } +- ++ + break; + case YAFFS_OBJECT_TYPE_UNKNOWN : + case YAFFS_OBJECT_TYPE_HARDLINK : +@@ -1020,7 +1027,7 @@ + int error = -ENOSPC; + uid_t uid = current->fsuid; + gid_t gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; +- ++ + if((dir->i_mode & S_ISGID) && S_ISDIR(mode)) + mode |= S_ISGID; + +@@ -1075,7 +1082,7 @@ + obj = NULL; /* Do we ever get here? */ + break; + } +- ++ + /* Can not call yaffs_get_inode() with gross lock held */ + yaffs_GrossUnlock(dev); + +@@ -1264,13 +1271,13 @@ + target = + yaffs_FindObjectByName(yaffs_InodeToObject(new_dir), + new_dentry->d_name.name); +- +- ++ ++ + + if (target && + target->variantType == YAFFS_OBJECT_TYPE_DIRECTORY && + !list_empty(&target->variant.directoryVariant.children)) { +- ++ + T(YAFFS_TRACE_OS, (KERN_DEBUG "target is non-empty dir\n")); + + retVal = YAFFS_FAIL; +@@ -1278,7 +1285,7 @@ + + /* Now does unlinking internally using shadowing mechanism */ + T(YAFFS_TRACE_OS, (KERN_DEBUG "calling yaffs_RenameObject\n")); +- ++ + retVal = + yaffs_RenameObject(yaffs_InodeToObject(old_dir), + old_dentry->d_name.name, +@@ -1390,7 +1397,7 @@ + + if(dev) + yaffs_CheckpointSave(dev); +- ++ + yaffs_GrossUnlock(dev); + + sb->s_dirt = 0; +@@ -1421,16 +1428,16 @@ + { + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_sync_fs\n")); +- ++ + return 0; /* yaffs_do_sync_fs(sb);*/ +- ++ + } + + + static void yaffs_read_inode(struct inode *inode) + { + /* NB This is called as a side effect of other functions, but +- * we had to release the lock to prevent deadlocks, so ++ * we had to release the lock to prevent deadlocks, so + * need to lock again. + */ + +@@ -1441,7 +1448,7 @@ + (KERN_DEBUG "yaffs_read_inode for %d\n", (int)inode->i_ino)); + + yaffs_GrossLock(dev); +- ++ + obj = yaffs_FindObjectByNumber(dev, inode->i_ino); + + yaffs_FillInodeFromObject(inode, obj); +@@ -1451,36 +1458,34 @@ + + static LIST_HEAD(yaffs_dev_list); + +-#if 0 // not used + static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data) + { + yaffs_Device *dev = yaffs_SuperToDevice(sb); + + if( *flags & MS_RDONLY ) { + struct mtd_info *mtd = yaffs_SuperToDevice(sb)->genericDevice; +- ++ + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_remount_fs: %s: RO\n", dev->name )); + + yaffs_GrossLock(dev); +- ++ + yaffs_FlushEntireDeviceCache(dev); +- ++ + yaffs_CheckpointSave(dev); +- ++ + if (mtd->sync) + mtd->sync(mtd); + + yaffs_GrossUnlock(dev); + } + else { +- T(YAFFS_TRACE_OS, ++ T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_remount_fs: %s: RW\n", dev->name )); + } +- ++ + return 0; + } +-#endif + + static void yaffs_put_super(struct super_block *sb) + { +@@ -1489,7 +1494,7 @@ + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_put_super\n")); + + yaffs_GrossLock(dev); +- ++ + yaffs_FlushEntireDeviceCache(dev); + + yaffs_CheckpointSave(dev); +@@ -1499,12 +1504,12 @@ + } + + yaffs_Deinitialise(dev); +- ++ + yaffs_GrossUnlock(dev); + + /* we assume this is protected by lock_kernel() in mount/umount */ + list_del(&dev->devList); +- ++ + if(dev->spareBuffer){ + YFREE(dev->spareBuffer); + dev->spareBuffer = NULL; +@@ -1530,61 +1535,12 @@ + static void yaffs_MarkSuperBlockDirty(void *vsb) + { + struct super_block *sb = (struct super_block *)vsb; +- ++ + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_MarkSuperBlockDirty() sb = %p\n",sb)); + // if(sb) + // sb->s_dirt = 1; + } + +-typedef struct { +- int inband_tags; +- int skip_checkpoint_read; +- int skip_checkpoint_write; +- int no_cache; +-} yaffs_options; +- +-#define MAX_OPT_LEN 20 +-static int yaffs_parse_options(yaffs_options *options, const char *options_str) +-{ +- char cur_opt[MAX_OPT_LEN+1]; +- int p; +- int error = 0; +- +- /* Parse through the options which is a comma seperated list */ +- +- while(options_str && *options_str && !error){ +- memset(cur_opt,0,MAX_OPT_LEN+1); +- p = 0; +- +- while(*options_str && *options_str != ','){ +- if(p < MAX_OPT_LEN){ +- cur_opt[p] = *options_str; +- p++; +- } +- options_str++; +- } +- +- if(!strcmp(cur_opt,"inband-tags")) +- options->inband_tags = 1; +- else if(!strcmp(cur_opt,"no-cache")) +- options->no_cache = 1; +- else if(!strcmp(cur_opt,"no-checkpoint-read")) +- options->skip_checkpoint_read = 1; +- else if(!strcmp(cur_opt,"no-checkpoint-write")) +- options->skip_checkpoint_write = 1; +- else if(!strcmp(cur_opt,"no-checkpoint")){ +- options->skip_checkpoint_read = 1; +- options->skip_checkpoint_write = 1; +- } else { +- printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n",cur_opt); +- error = 1; +- } +- +- } +- +- return error; +-} +- + static struct super_block *yaffs_internal_read_super(int yaffsVersion, + struct super_block *sb, + void *data, int silent) +@@ -1596,9 +1552,6 @@ + char devname_buf[BDEVNAME_SIZE + 1]; + struct mtd_info *mtd; + int err; +- char *data_str = (char *)data; +- +- yaffs_options options; + + sb->s_magic = YAFFS_MAGIC; + sb->s_op = &yaffs_super_ops; +@@ -1614,19 +1567,6 @@ + sb->s_dev, + yaffs_devname(sb, devname_buf)); + +- if(!data_str) +- data_str = ""; +- +- printk(KERN_INFO "yaffs: passed flags \"%s\"\n",data_str); +- +- memset(&options,0,sizeof(options)); +- +- if(yaffs_parse_options(&options,data_str)){ +- /* Option parsing failed */ +- return NULL; +- } +- +- + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + T(YAFFS_TRACE_OS, ("yaffs_read_super: Using yaffs%d\n", yaffsVersion)); +@@ -1670,14 +1610,18 @@ + T(YAFFS_TRACE_OS, (" writeoob %p\n", mtd->write_oob)); + T(YAFFS_TRACE_OS, (" block_isbad %p\n", mtd->block_isbad)); + T(YAFFS_TRACE_OS, (" block_markbad %p\n", mtd->block_markbad)); +- T(YAFFS_TRACE_OS, (" %s %d\n", WRITE_SIZE_STR, WRITE_SIZE(mtd))); ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ T(YAFFS_TRACE_OS, (" writesize %d\n", mtd->writesize)); ++#else ++ T(YAFFS_TRACE_OS, (" oobblock %d\n", mtd->oobblock)); ++#endif + T(YAFFS_TRACE_OS, (" oobsize %d\n", mtd->oobsize)); + T(YAFFS_TRACE_OS, (" erasesize %d\n", mtd->erasesize)); +- T(YAFFS_TRACE_OS, (" size %d\n", mtd->size)); +- ++ T(YAFFS_TRACE_OS, (" size %lld\n", mtd->size)); ++ + #ifdef CONFIG_YAFFS_AUTO_YAFFS2 + +- if (yaffsVersion == 1 && ++ if (yaffsVersion == 1 && + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + mtd->writesize >= 2048) { + #else +@@ -1685,10 +1629,10 @@ + #endif + T(YAFFS_TRACE_ALWAYS,("yaffs: auto selecting yaffs2\n")); + yaffsVersion = 2; +- } +- ++ } ++ + /* Added NCB 26/5/2006 for completeness */ +- if (yaffsVersion == 2 && ++ if (yaffsVersion == 2 && + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + mtd->writesize == 512) { + #else +@@ -1696,7 +1640,7 @@ + #endif + T(YAFFS_TRACE_ALWAYS,("yaffs: auto selecting yaffs1\n")); + yaffsVersion = 1; +- } ++ } + + #endif + +@@ -1747,7 +1691,11 @@ + return NULL; + } + +- if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ if (mtd->writesize < YAFFS_BYTES_PER_CHUNK || ++#else ++ if (mtd->oobblock < YAFFS_BYTES_PER_CHUNK || ++#endif + mtd->oobsize != YAFFS_BYTES_PER_SPARE) { + T(YAFFS_TRACE_ALWAYS, + ("yaffs: MTD device does not support have the " +@@ -1779,17 +1727,14 @@ + dev->name = mtd->name; + + /* Set up the memory size parameters.... */ +- +- nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); +- dev->startBlock = 0; +- dev->endBlock = nBlocks - 1; + dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK; + dev->nDataBytesPerChunk = YAFFS_BYTES_PER_CHUNK; + dev->nReservedBlocks = 5; +- dev->nShortOpCaches = (options.no_cache) ? 0 : 10; ++ dev->nShortOpCaches = 10; /* Enable short op caching */ + + /* ... and the functions. */ + if (yaffsVersion == 2) { ++ int block_shift; + dev->writeChunkWithTagsToNAND = + nandmtd2_WriteChunkWithTagsToNAND; + dev->readChunkWithTagsFromNAND = +@@ -1805,24 +1750,18 @@ + dev->nDataBytesPerChunk = mtd->oobblock; + dev->nChunksPerBlock = mtd->erasesize / mtd->oobblock; + #endif +- nBlocks = mtd->size / mtd->erasesize; +- ++ block_shift = ffs(mtd->erasesize) - 1; ++// nBlocks = mtd->size / mtd->erasesize; ++ nBlocks = mtd->size >> block_shift; + dev->nCheckpointReservedBlocks = CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS; + dev->startBlock = 0; + dev->endBlock = nBlocks - 1; + } else { +-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +- /* use the MTD interface in yaffs_mtdif1.c */ +- dev->writeChunkWithTagsToNAND = +- nandmtd1_WriteChunkWithTagsToNAND; +- dev->readChunkWithTagsFromNAND = +- nandmtd1_ReadChunkWithTagsFromNAND; +- dev->markNANDBlockBad = nandmtd1_MarkNANDBlockBad; +- dev->queryNANDBlock = nandmtd1_QueryNANDBlock; +-#else ++ nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); ++ dev->startBlock = 0; ++ dev->endBlock = nBlocks - 1; + dev->writeChunkToNAND = nandmtd_WriteChunkToNAND; + dev->readChunkFromNAND = nandmtd_ReadChunkFromNAND; +-#endif + dev->isYaffs2 = 0; + } + /* ... and common functions */ +@@ -1830,10 +1769,10 @@ + dev->initialiseNAND = nandmtd_InitialiseNAND; + + dev->putSuperFunc = yaffs_MTDPutSuper; +- ++ + dev->superBlock = (void *)sb; + dev->markSuperBlockDirty = yaffs_MarkSuperBlockDirty; +- ++ + + #ifndef CONFIG_YAFFS_DOES_ECC + dev->useNANDECC = 1; +@@ -1843,9 +1782,6 @@ + dev->wideTnodesDisabled = 1; + #endif + +- dev->skipCheckpointRead = options.skip_checkpoint_read; +- dev->skipCheckpointWrite = options.skip_checkpoint_write; +- + /* we assume this is protected by lock_kernel() in mount/umount */ + list_add_tail(&dev->devList, &yaffs_dev_list); + +@@ -1858,7 +1794,7 @@ + T(YAFFS_TRACE_OS, + ("yaffs_read_super: guts initialised %s\n", + (err == YAFFS_OK) ? "OK" : "FAILED")); +- ++ + /* Release lock before yaffs_get_inode() */ + yaffs_GrossUnlock(dev); + +@@ -1869,7 +1805,6 @@ + + if (!inode) + return NULL; +- + inode->i_op = &yaffs_dir_inode_operations; + inode->i_fop = &yaffs_dir_operations; + +@@ -1884,7 +1819,6 @@ + return NULL; + } + sb->s_root = root; +- + T(YAFFS_TRACE_OS, ("yaffs_read_super: done\n")); + return sb; + } +@@ -1990,13 +1924,9 @@ + { + buf += sprintf(buf, "startBlock......... %d\n", dev->startBlock); + buf += sprintf(buf, "endBlock........... %d\n", dev->endBlock); +- buf += sprintf(buf, "nDataBytesPerChunk. %d\n", dev->nDataBytesPerChunk); + buf += sprintf(buf, "chunkGroupBits..... %d\n", dev->chunkGroupBits); + buf += sprintf(buf, "chunkGroupSize..... %d\n", dev->chunkGroupSize); + buf += sprintf(buf, "nErasedBlocks...... %d\n", dev->nErasedBlocks); +- buf += sprintf(buf, "nReservedBlocks.... %d\n", dev->nReservedBlocks); +- buf += sprintf(buf, "nCheckptResBlocks.. %d\n", dev->nCheckpointReservedBlocks); +- buf += sprintf(buf, "blocksInCheckpoint. %d\n", dev->blocksInCheckpoint); + buf += sprintf(buf, "nTnodesCreated..... %d\n", dev->nTnodesCreated); + buf += sprintf(buf, "nFreeTnodes........ %d\n", dev->nFreeTnodes); + buf += sprintf(buf, "nObjectsCreated.... %d\n", dev->nObjectsCreated); +@@ -2012,7 +1942,6 @@ + sprintf(buf, "passiveGCs......... %d\n", + dev->passiveGarbageCollections); + buf += sprintf(buf, "nRetriedWrites..... %d\n", dev->nRetriedWrites); +- buf += sprintf(buf, "nShortOpCaches..... %d\n", dev->nShortOpCaches); + buf += sprintf(buf, "nRetireBlocks...... %d\n", dev->nRetiredBlocks); + buf += sprintf(buf, "eccFixed........... %d\n", dev->eccFixed); + buf += sprintf(buf, "eccUnfixed......... %d\n", dev->eccUnfixed); +@@ -2075,7 +2004,6 @@ + /** + * Set the verbosity of the warnings and error messages. + * +- * Note that the names can only be a..z or _ with the current code. + */ + + static struct { +@@ -2087,7 +2015,6 @@ + {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, + {"buffers", YAFFS_TRACE_BUFFERS}, + {"bug", YAFFS_TRACE_BUG}, +- {"checkpt", YAFFS_TRACE_CHECKPOINT}, + {"deletion", YAFFS_TRACE_DELETION}, + {"erase", YAFFS_TRACE_ERASE}, + {"error", YAFFS_TRACE_ERROR}, +@@ -2099,27 +2026,17 @@ + {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, + {"scan", YAFFS_TRACE_SCAN}, + {"tracing", YAFFS_TRACE_TRACING}, +- +- {"verify", YAFFS_TRACE_VERIFY}, +- {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, +- {"verify_full", YAFFS_TRACE_VERIFY_FULL}, +- {"verify_all", YAFFS_TRACE_VERIFY_ALL}, +- + {"write", YAFFS_TRACE_WRITE}, + {"all", 0xffffffff}, + {"none", 0}, + {NULL, 0}, + }; + +-#define MAX_MASK_NAME_LENGTH 40 + static int yaffs_proc_write(struct file *file, const char *buf, + unsigned long count, void *data) + { + unsigned rg = 0, mask_bitfield; +- char *end; +- char *mask_name; +- const char *x; +- char substring[MAX_MASK_NAME_LENGTH+1]; ++ char *end, *mask_name; + int i; + int done = 0; + int add, len = 0; +@@ -2146,22 +2063,16 @@ + break; + } + mask_name = NULL; +- + mask_bitfield = simple_strtoul(buf + pos, &end, 0); + if (end > buf + pos) { + mask_name = "numeral"; + len = end - (buf + pos); +- pos += len; + done = 0; + } else { +- for(x = buf + pos, i = 0; +- (*x == '_' || (*x >='a' && *x <= 'z')) && +- i 1){ + if(x & 1) extraBits++; + x>>=1; + nShifts++; + } + +- if(extraBits) ++ if(extraBits) + nShifts++; +- ++ + return nShifts; + } + + /* Function to return the number of shifts to get a 1 in bit 0 + */ +- ++ + static __u32 ShiftDiv(__u32 x) + { + int nShifts; +- ++ + nShifts = 0; +- ++ + if(!x) return 0; +- ++ + while( !(x&1)){ + x>>=1; + nShifts++; + } +- ++ + return nShifts; + } + + + +-/* ++/* + * Temporary buffer manipulations. + */ + +-static int yaffs_InitialiseTempBuffers(yaffs_Device *dev) +-{ +- int i; +- __u8 *buf = (__u8 *)1; +- +- memset(dev->tempBuffer,0,sizeof(dev->tempBuffer)); +- +- for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { +- dev->tempBuffer[i].line = 0; /* not in use */ +- dev->tempBuffer[i].buffer = buf = +- YMALLOC_DMA(dev->nDataBytesPerChunk); +- } +- +- return buf ? YAFFS_OK : YAFFS_FAIL; +- +-} +- + static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo) + { + int i, j; +@@ -290,8 +264,6 @@ + return 0; + } + +- +- + /* + * Chunk bitmap manipulations + */ +@@ -308,16 +280,6 @@ + (dev->chunkBitmapStride * (blk - dev->internalStartBlock)); + } + +-static Y_INLINE void yaffs_VerifyChunkBitId(yaffs_Device *dev, int blk, int chunk) +-{ +- if(blk < dev->internalStartBlock || blk > dev->internalEndBlock || +- chunk < 0 || chunk >= dev->nChunksPerBlock) { +- T(YAFFS_TRACE_ERROR, +- (TSTR("**>> yaffs: Chunk Id (%d:%d) invalid"TENDSTR),blk,chunk)); +- YBUG(); +- } +-} +- + static Y_INLINE void yaffs_ClearChunkBits(yaffs_Device * dev, int blk) + { + __u8 *blkBits = yaffs_BlockBits(dev, blk); +@@ -329,8 +291,6 @@ + { + __u8 *blkBits = yaffs_BlockBits(dev, blk); + +- yaffs_VerifyChunkBitId(dev,blk,chunk); +- + blkBits[chunk / 8] &= ~(1 << (chunk & 7)); + } + +@@ -338,16 +298,12 @@ + { + __u8 *blkBits = yaffs_BlockBits(dev, blk); + +- yaffs_VerifyChunkBitId(dev,blk,chunk); +- + blkBits[chunk / 8] |= (1 << (chunk & 7)); + } + + static Y_INLINE int yaffs_CheckChunkBit(yaffs_Device * dev, int blk, int chunk) + { + __u8 *blkBits = yaffs_BlockBits(dev, blk); +- yaffs_VerifyChunkBitId(dev,blk,chunk); +- + return (blkBits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; + } + +@@ -363,495 +319,10 @@ + return 0; + } + +-static int yaffs_CountChunkBits(yaffs_Device * dev, int blk) +-{ +- __u8 *blkBits = yaffs_BlockBits(dev, blk); +- int i; +- int n = 0; +- for (i = 0; i < dev->chunkBitmapStride; i++) { +- __u8 x = *blkBits; +- while(x){ +- if(x & 1) +- n++; +- x >>=1; +- } +- +- blkBits++; +- } +- return n; +-} +- +-/* +- * Verification code +- */ +- +-static int yaffs_SkipVerification(yaffs_Device *dev) +-{ +- return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); +-} +- +-static int yaffs_SkipFullVerification(yaffs_Device *dev) +-{ +- return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_FULL)); +-} +- +-static int yaffs_SkipNANDVerification(yaffs_Device *dev) +-{ +- return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_NAND)); +-} +- +-static const char * blockStateName[] = { +-"Unknown", +-"Needs scanning", +-"Scanning", +-"Empty", +-"Allocating", +-"Full", +-"Dirty", +-"Checkpoint", +-"Collecting", +-"Dead" +-}; +- +-static void yaffs_VerifyBlock(yaffs_Device *dev,yaffs_BlockInfo *bi,int n) +-{ +- int actuallyUsed; +- int inUse; +- +- if(yaffs_SkipVerification(dev)) +- return; +- +- /* Report illegal runtime states */ +- if(bi->blockState <0 || bi->blockState >= YAFFS_NUMBER_OF_BLOCK_STATES) +- T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has undefined state %d"TENDSTR),n,bi->blockState)); +- +- switch(bi->blockState){ +- case YAFFS_BLOCK_STATE_UNKNOWN: +- case YAFFS_BLOCK_STATE_SCANNING: +- case YAFFS_BLOCK_STATE_NEEDS_SCANNING: +- T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has bad run-state %s"TENDSTR), +- n,blockStateName[bi->blockState])); +- } +- +- /* Check pages in use and soft deletions are legal */ +- +- actuallyUsed = bi->pagesInUse - bi->softDeletions; +- +- if(bi->pagesInUse < 0 || bi->pagesInUse > dev->nChunksPerBlock || +- bi->softDeletions < 0 || bi->softDeletions > dev->nChunksPerBlock || +- actuallyUsed < 0 || actuallyUsed > dev->nChunksPerBlock) +- T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has illegal values pagesInUsed %d softDeletions %d"TENDSTR), +- n,bi->pagesInUse,bi->softDeletions)); +- +- +- /* Check chunk bitmap legal */ +- inUse = yaffs_CountChunkBits(dev,n); +- if(inUse != bi->pagesInUse) +- T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has inconsistent values pagesInUse %d counted chunk bits %d"TENDSTR), +- n,bi->pagesInUse,inUse)); +- +- /* Check that the sequence number is valid. +- * Ten million is legal, but is very unlikely +- */ +- if(dev->isYaffs2 && +- (bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING || bi->blockState == YAFFS_BLOCK_STATE_FULL) && +- (bi->sequenceNumber < YAFFS_LOWEST_SEQUENCE_NUMBER || bi->sequenceNumber > 10000000 )) +- T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has suspect sequence number of %d"TENDSTR), +- n,bi->sequenceNumber)); +- +-} +- +-static void yaffs_VerifyCollectedBlock(yaffs_Device *dev,yaffs_BlockInfo *bi,int n) +-{ +- yaffs_VerifyBlock(dev,bi,n); +- +- /* After collection the block should be in the erased state */ +- /* TODO: This will need to change if we do partial gc */ +- +- if(bi->blockState != YAFFS_BLOCK_STATE_EMPTY){ +- T(YAFFS_TRACE_ERROR,(TSTR("Block %d is in state %d after gc, should be erased"TENDSTR), +- n,bi->blockState)); +- } +-} +- +-static void yaffs_VerifyBlocks(yaffs_Device *dev) +-{ +- int i; +- int nBlocksPerState[YAFFS_NUMBER_OF_BLOCK_STATES]; +- int nIllegalBlockStates = 0; +- +- +- if(yaffs_SkipVerification(dev)) +- return; +- +- memset(nBlocksPerState,0,sizeof(nBlocksPerState)); +- +- +- for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++){ +- yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); +- yaffs_VerifyBlock(dev,bi,i); +- +- if(bi->blockState >=0 && bi->blockState < YAFFS_NUMBER_OF_BLOCK_STATES) +- nBlocksPerState[bi->blockState]++; +- else +- nIllegalBlockStates++; +- +- } +- +- T(YAFFS_TRACE_VERIFY,(TSTR(""TENDSTR))); +- T(YAFFS_TRACE_VERIFY,(TSTR("Block summary"TENDSTR))); +- +- T(YAFFS_TRACE_VERIFY,(TSTR("%d blocks have illegal states"TENDSTR),nIllegalBlockStates)); +- if(nBlocksPerState[YAFFS_BLOCK_STATE_ALLOCATING] > 1) +- T(YAFFS_TRACE_VERIFY,(TSTR("Too many allocating blocks"TENDSTR))); +- +- for(i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) +- T(YAFFS_TRACE_VERIFY, +- (TSTR("%s %d blocks"TENDSTR), +- blockStateName[i],nBlocksPerState[i])); +- +- if(dev->blocksInCheckpoint != nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT]) +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Checkpoint block count wrong dev %d count %d"TENDSTR), +- dev->blocksInCheckpoint, nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT])); +- +- if(dev->nErasedBlocks != nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY]) +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Erased block count wrong dev %d count %d"TENDSTR), +- dev->nErasedBlocks, nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY])); +- +- if(nBlocksPerState[YAFFS_BLOCK_STATE_COLLECTING] > 1) +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Too many collecting blocks %d (max is 1)"TENDSTR), +- nBlocksPerState[YAFFS_BLOCK_STATE_COLLECTING])); +- +- T(YAFFS_TRACE_VERIFY,(TSTR(""TENDSTR))); +- +-} +- +-/* +- * Verify the object header. oh must be valid, but obj and tags may be NULL in which +- * case those tests will not be performed. +- */ +-static void yaffs_VerifyObjectHeader(yaffs_Object *obj, yaffs_ObjectHeader *oh, yaffs_ExtendedTags *tags, int parentCheck) +-{ +- if(yaffs_SkipVerification(obj->myDev)) +- return; +- +- if(!(tags && obj && oh)){ +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Verifying object header tags %x obj %x oh %x"TENDSTR), +- (__u32)tags,(__u32)obj,(__u32)oh)); +- return; +- } +- +- if(oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || +- oh->type > YAFFS_OBJECT_TYPE_MAX) +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Obj %d header type is illegal value 0x%x"TENDSTR), +- tags->objectId, oh->type)); +- +- if(tags->objectId != obj->objectId) +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Obj %d header mismatch objectId %d"TENDSTR), +- tags->objectId, obj->objectId)); +- +- +- /* +- * Check that the object's parent ids match if parentCheck requested. +- * +- * Tests do not apply to the root object. +- */ +- +- if(parentCheck && tags->objectId > 1 && !obj->parent) +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Obj %d header mismatch parentId %d obj->parent is NULL"TENDSTR), +- tags->objectId, oh->parentObjectId)); +- +- +- if(parentCheck && obj->parent && +- oh->parentObjectId != obj->parent->objectId && +- (oh->parentObjectId != YAFFS_OBJECTID_UNLINKED || +- obj->parent->objectId != YAFFS_OBJECTID_DELETED)) +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Obj %d header mismatch parentId %d parentObjectId %d"TENDSTR), +- tags->objectId, oh->parentObjectId, obj->parent->objectId)); +- +- +- if(tags->objectId > 1 && oh->name[0] == 0) /* Null name */ +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Obj %d header name is NULL"TENDSTR), +- obj->objectId)); +- +- if(tags->objectId > 1 && ((__u8)(oh->name[0])) == 0xff) /* Trashed name */ +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Obj %d header name is 0xFF"TENDSTR), +- obj->objectId)); +-} +- +- +- +-static int yaffs_VerifyTnodeWorker(yaffs_Object * obj, yaffs_Tnode * tn, +- __u32 level, int chunkOffset) +-{ +- int i; +- yaffs_Device *dev = obj->myDev; +- int ok = 1; +- int nTnodeBytes = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8; +- +- if (tn) { +- if (level > 0) { +- +- for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++){ +- if (tn->internal[i]) { +- ok = yaffs_VerifyTnodeWorker(obj, +- tn->internal[i], +- level - 1, +- (chunkOffset<objectId; +- +- chunkOffset <<= YAFFS_TNODES_LEVEL0_BITS; +- +- for(i = 0; i < YAFFS_NTNODES_LEVEL0; i++){ +- __u32 theChunk = yaffs_GetChunkGroupBase(dev,tn,i); +- +- if(theChunk > 0){ +- /* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),tags.objectId,tags.chunkId,theChunk)); */ +- yaffs_ReadChunkWithTagsFromNAND(dev,theChunk,NULL, &tags); +- if(tags.objectId != objectId || tags.chunkId != chunkOffset){ +- T(~0,(TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), +- objectId, chunkOffset, theChunk, +- tags.objectId, tags.chunkId)); +- } +- } +- chunkOffset++; +- } +- } +- } +- +- return ok; +- +-} +- +- +-static void yaffs_VerifyFile(yaffs_Object *obj) +-{ +- int requiredTallness; +- int actualTallness; +- __u32 lastChunk; +- __u32 x; +- __u32 i; +- int ok; +- yaffs_Device *dev; +- yaffs_ExtendedTags tags; +- yaffs_Tnode *tn; +- __u32 objectId; +- +- if(obj && yaffs_SkipVerification(obj->myDev)) +- return; +- +- dev = obj->myDev; +- objectId = obj->objectId; +- +- /* Check file size is consistent with tnode depth */ +- lastChunk = obj->variant.fileVariant.fileSize / dev->nDataBytesPerChunk + 1; +- x = lastChunk >> YAFFS_TNODES_LEVEL0_BITS; +- requiredTallness = 0; +- while (x> 0) { +- x >>= YAFFS_TNODES_INTERNAL_BITS; +- requiredTallness++; +- } +- +- actualTallness = obj->variant.fileVariant.topLevel; +- +- if(requiredTallness > actualTallness ) +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Obj %d had tnode tallness %d, needs to be %d"TENDSTR), +- obj->objectId,actualTallness, requiredTallness)); +- +- +- /* Check that the chunks in the tnode tree are all correct. +- * We do this by scanning through the tnode tree and +- * checking the tags for every chunk match. +- */ +- +- if(yaffs_SkipNANDVerification(dev)) +- return; +- +- for(i = 1; i <= lastChunk; i++){ +- tn = yaffs_FindLevel0Tnode(dev, &obj->variant.fileVariant,i); +- +- if (tn) { +- __u32 theChunk = yaffs_GetChunkGroupBase(dev,tn,i); +- if(theChunk > 0){ +- /* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),objectId,i,theChunk)); */ +- yaffs_ReadChunkWithTagsFromNAND(dev,theChunk,NULL, &tags); +- if(tags.objectId != objectId || tags.chunkId != i){ +- T(~0,(TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), +- objectId, i, theChunk, +- tags.objectId, tags.chunkId)); +- } +- } +- } +- +- } +- +-} +- +-static void yaffs_VerifyDirectory(yaffs_Object *obj) +-{ +- if(obj && yaffs_SkipVerification(obj->myDev)) +- return; +- +-} +- +-static void yaffs_VerifyHardLink(yaffs_Object *obj) +-{ +- if(obj && yaffs_SkipVerification(obj->myDev)) +- return; +- +- /* Verify sane equivalent object */ +-} +- +-static void yaffs_VerifySymlink(yaffs_Object *obj) +-{ +- if(obj && yaffs_SkipVerification(obj->myDev)) +- return; +- +- /* Verify symlink string */ +-} +- +-static void yaffs_VerifySpecial(yaffs_Object *obj) +-{ +- if(obj && yaffs_SkipVerification(obj->myDev)) +- return; +-} +- +-static void yaffs_VerifyObject(yaffs_Object *obj) +-{ +- yaffs_Device *dev; +- +- __u32 chunkMin; +- __u32 chunkMax; +- +- __u32 chunkIdOk; +- __u32 chunkIsLive; +- +- if(!obj) +- return; +- +- dev = obj->myDev; +- +- if(yaffs_SkipVerification(dev)) +- return; +- +- /* Check sane object header chunk */ +- +- chunkMin = dev->internalStartBlock * dev->nChunksPerBlock; +- chunkMax = (dev->internalEndBlock+1) * dev->nChunksPerBlock - 1; +- +- chunkIdOk = (obj->chunkId >= chunkMin && obj->chunkId <= chunkMax); +- chunkIsLive = chunkIdOk && +- yaffs_CheckChunkBit(dev, +- obj->chunkId / dev->nChunksPerBlock, +- obj->chunkId % dev->nChunksPerBlock); +- if(!obj->fake && +- (!chunkIdOk || !chunkIsLive)) { +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Obj %d has chunkId %d %s %s"TENDSTR), +- obj->objectId,obj->chunkId, +- chunkIdOk ? "" : ",out of range", +- chunkIsLive || !chunkIdOk ? "" : ",marked as deleted")); +- } +- +- if(chunkIdOk && chunkIsLive &&!yaffs_SkipNANDVerification(dev)) { +- yaffs_ExtendedTags tags; +- yaffs_ObjectHeader *oh; +- __u8 *buffer = yaffs_GetTempBuffer(dev,__LINE__); +- +- oh = (yaffs_ObjectHeader *)buffer; +- +- yaffs_ReadChunkWithTagsFromNAND(dev, obj->chunkId,buffer, &tags); +- +- yaffs_VerifyObjectHeader(obj,oh,&tags,1); +- +- yaffs_ReleaseTempBuffer(dev,buffer,__LINE__); +- } +- +- /* Verify it has a parent */ +- if(obj && !obj->fake && +- (!obj->parent || obj->parent->myDev != dev)){ +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Obj %d has parent pointer %p which does not look like an object"TENDSTR), +- obj->objectId,obj->parent)); +- } +- +- /* Verify parent is a directory */ +- if(obj->parent && obj->parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY){ +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Obj %d's parent is not a directory (type %d)"TENDSTR), +- obj->objectId,obj->parent->variantType)); +- } +- +- switch(obj->variantType){ +- case YAFFS_OBJECT_TYPE_FILE: +- yaffs_VerifyFile(obj); +- break; +- case YAFFS_OBJECT_TYPE_SYMLINK: +- yaffs_VerifySymlink(obj); +- break; +- case YAFFS_OBJECT_TYPE_DIRECTORY: +- yaffs_VerifyDirectory(obj); +- break; +- case YAFFS_OBJECT_TYPE_HARDLINK: +- yaffs_VerifyHardLink(obj); +- break; +- case YAFFS_OBJECT_TYPE_SPECIAL: +- yaffs_VerifySpecial(obj); +- break; +- case YAFFS_OBJECT_TYPE_UNKNOWN: +- default: +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Obj %d has illegaltype %d"TENDSTR), +- obj->objectId,obj->variantType)); +- break; +- } +- +- +-} +- +-static void yaffs_VerifyObjects(yaffs_Device *dev) +-{ +- yaffs_Object *obj; +- int i; +- struct list_head *lh; +- +- if(yaffs_SkipVerification(dev)) +- return; +- +- /* Iterate through the objects in each hash entry */ +- +- for(i = 0; i < YAFFS_NOBJECT_BUCKETS; i++){ +- list_for_each(lh, &dev->objectBucket[i].list) { +- if (lh) { +- obj = list_entry(lh, yaffs_Object, hashLink); +- yaffs_VerifyObject(obj); +- } +- } +- } +- +-} +- +- + /* + * Simple hash function. Needs to have a reasonable spread + */ +- ++ + static Y_INLINE int yaffs_HashFunction(int n) + { + n = abs(n); +@@ -861,7 +332,7 @@ + /* + * Access functions to useful fake objects + */ +- ++ + yaffs_Object *yaffs_Root(yaffs_Device * dev) + { + return dev->rootDir; +@@ -876,7 +347,7 @@ + /* + * Erased NAND checking functions + */ +- ++ + int yaffs_CheckFF(__u8 * buffer, int nBytes) + { + /* Horrible, slow implementation */ +@@ -898,10 +369,10 @@ + int result; + + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunkInNAND, data, &tags); +- ++ + if(tags.eccResult > YAFFS_ECC_RESULT_NO_ERROR) + retval = YAFFS_FAIL; +- ++ + + if (!yaffs_CheckFF(data, dev->nDataBytesPerChunk) || tags.chunkUsed) { + T(YAFFS_TRACE_NANDACCESS, +@@ -915,90 +386,91 @@ + + } + +- + static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, + const __u8 * data, + yaffs_ExtendedTags * tags, + int useReserve) + { +- int attempts = 0; +- int writeOk = 0; + int chunk; + ++ int writeOk = 0; ++ int erasedOk = 1; ++ int attempts = 0; ++ yaffs_BlockInfo *bi; ++ + yaffs_InvalidateCheckpoint(dev); + + do { +- yaffs_BlockInfo *bi = 0; +- int erasedOk = 0; ++ chunk = yaffs_AllocateChunk(dev, useReserve,&bi); + +- chunk = yaffs_AllocateChunk(dev, useReserve, &bi); +- if (chunk < 0) { +- /* no space */ +- break; +- } +- +- /* First check this chunk is erased, if it needs +- * checking. The checking policy (unless forced +- * always on) is as follows: +- * +- * Check the first page we try to write in a block. +- * If the check passes then we don't need to check any +- * more. If the check fails, we check again... +- * If the block has been erased, we don't need to check. +- * +- * However, if the block has been prioritised for gc, +- * then we think there might be something odd about +- * this block and stop using it. +- * +- * Rationale: We should only ever see chunks that have +- * not been erased if there was a partially written +- * chunk due to power loss. This checking policy should +- * catch that case with very few checks and thus save a +- * lot of checks that are most likely not needed. +- */ +- if (bi->gcPrioritise) { +- yaffs_DeleteChunk(dev, chunk, 1, __LINE__); +- /* try another chunk */ +- continue; +- } ++ if (chunk >= 0) { ++ /* First check this chunk is erased, if it needs checking. ++ * The checking policy (unless forced always on) is as follows: ++ * Check the first page we try to write in a block. ++ * - If the check passes then we don't need to check any more. ++ * - If the check fails, we check again... ++ * If the block has been erased, we don't need to check. ++ * ++ * However, if the block has been prioritised for gc, then ++ * we think there might be something odd about this block ++ * and stop using it. ++ * ++ * Rationale: ++ * We should only ever see chunks that have not been erased ++ * if there was a partially written chunk due to power loss ++ * This checking policy should catch that case with very ++ * few checks and thus save a lot of checks that are most likely not ++ * needed. ++ */ ++ ++ if(bi->gcPrioritise){ ++ yaffs_DeleteChunk(dev, chunk, 1, __LINE__); ++ } else { ++#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED + +- /* let's give it a try */ +- attempts++; ++ bi->skipErasedCheck = 0; + +-#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED +- bi->skipErasedCheck = 0; + #endif +- if (!bi->skipErasedCheck) { +- erasedOk = yaffs_CheckChunkErased(dev, chunk); +- if (erasedOk != YAFFS_OK) { +- T(YAFFS_TRACE_ERROR, +- (TSTR ("**>> yaffs chunk %d was not erased" +- TENDSTR), chunk)); ++ if(!bi->skipErasedCheck){ ++ erasedOk = yaffs_CheckChunkErased(dev, chunk); ++ if(erasedOk && !bi->gcPrioritise) ++ bi->skipErasedCheck = 1; ++ } + +- /* try another chunk */ +- continue; ++ if (!erasedOk) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("**>> yaffs chunk %d was not erased" ++ TENDSTR), chunk)); ++ } else { ++ writeOk = ++ yaffs_WriteChunkWithTagsToNAND(dev, chunk, ++ data, tags); ++ } ++ ++ attempts++; ++ ++ if (writeOk) { ++ /* ++ * Copy the data into the robustification buffer. ++ * NB We do this at the end to prevent duplicates in the case of a write error. ++ * Todo ++ */ ++ yaffs_HandleWriteChunkOk(dev, chunk, data, tags); ++ ++ } else { ++ /* The erased check or write failed */ ++ yaffs_HandleWriteChunkError(dev, chunk, erasedOk); ++ } + } +- bi->skipErasedCheck = 1; + } + +- writeOk = yaffs_WriteChunkWithTagsToNAND(dev, chunk, +- data, tags); +- if (writeOk != YAFFS_OK) { +- yaffs_HandleWriteChunkError(dev, chunk, erasedOk); +- /* try another chunk */ +- continue; +- } +- +- /* Copy the data into the robustification buffer */ +- yaffs_HandleWriteChunkOk(dev, chunk, data, tags); +- +- } while (writeOk != YAFFS_OK && attempts < yaffs_wr_attempts); ++ } while (chunk >= 0 && !writeOk); + + if (attempts > 1) { + T(YAFFS_TRACE_ERROR, +- (TSTR("**>> yaffs write required %d attempts" TENDSTR), +- attempts)); +- ++ (TSTR("**>> yaffs write required %d attempts" TENDSTR), ++ attempts)); + dev->nRetriedWrites += (attempts - 1); + } + +@@ -1008,13 +480,13 @@ + /* + * Block retiring for handling a broken block. + */ +- ++ + static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND) + { + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND); + + yaffs_InvalidateCheckpoint(dev); +- ++ + yaffs_MarkBlockBad(dev, blockInNAND); + + bi->blockState = YAFFS_BLOCK_STATE_DEAD; +@@ -1028,7 +500,7 @@ + * Functions for robustisizing TODO + * + */ +- ++ + static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, + const yaffs_ExtendedTags * tags) +@@ -1046,13 +518,28 @@ + bi->gcPrioritise = 1; + dev->hasPendingPrioritisedGCs = 1; + bi->chunkErrorStrikes ++; +- ++ + if(bi->chunkErrorStrikes > 3){ + bi->needsRetiring = 1; /* Too many stikes, so retire this */ + T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Block struck out" TENDSTR))); + + } ++ ++ } ++} + ++static void yaffs_ReportOddballBlocks(yaffs_Device *dev) ++{ ++ int i; ++ ++ for(i = dev->internalStartBlock; i <= dev->internalEndBlock && (yaffs_traceMask & YAFFS_TRACE_BAD_BLOCKS); i++){ ++ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); ++ if(bi->needsRetiring || bi->gcPrioritise) ++ T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("yaffs block %d%s%s" TENDSTR), ++ i, ++ bi->needsRetiring ? " needs retiring" : "", ++ bi->gcPrioritise ? " gc prioritised" : "")); ++ + } + } + +@@ -1061,25 +548,22 @@ + + int blockInNAND = chunkInNAND / dev->nChunksPerBlock; + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND); +- ++ + yaffs_HandleChunkError(dev,bi); +- +- ++ + if(erasedOk ) { + /* Was an actual write failure, so mark the block for retirement */ + bi->needsRetiring = 1; + T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + (TSTR("**>> Block %d needs retiring" TENDSTR), blockInNAND)); +- +- + } +- ++ + /* Delete the chunk */ + yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__); + } + + +-/*---------------- Name handling functions ------------*/ ++/*---------------- Name handling functions ------------*/ + + static __u16 yaffs_CalcNameSum(const YCHAR * name) + { +@@ -1088,7 +572,7 @@ + + YUCHAR *bname = (YUCHAR *) name; + if (bname) { +- while ((*bname) && (i < (YAFFS_MAX_NAME_LENGTH/2))) { ++ while ((*bname) && (i <= YAFFS_MAX_NAME_LENGTH)) { + + #ifdef CONFIG_YAFFS_CASE_INSENSITIVE + sum += yaffs_toupper(*bname) * i; +@@ -1120,7 +604,7 @@ + * The list is hooked together using the first pointer + * in the tnode. + */ +- ++ + /* yaffs_CreateTnodes creates a bunch more tnodes and + * adds them to the tnode free list. + * Don't use this function directly +@@ -1138,7 +622,7 @@ + + if (nTnodes < 1) + return YAFFS_OK; +- ++ + /* Calculate the tnode size in bytes for variable width tnode support. + * Must be a multiple of 32-bits */ + tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8; +@@ -1175,7 +659,7 @@ + next = (yaffs_Tnode *) &mem[(i+1) * tnodeSize]; + curr->internal[0] = next; + } +- ++ + curr = (yaffs_Tnode *) &mem[(nTnodes - 1) * tnodeSize]; + curr->internal[0] = dev->freeTnodes; + dev->freeTnodes = (yaffs_Tnode *)mem; +@@ -1190,13 +674,12 @@ + * NB If we can't add this to the management list it isn't fatal + * but it just means we can't free this bunch of tnodes later. + */ +- ++ + tnl = YMALLOC(sizeof(yaffs_TnodeList)); + if (!tnl) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs: Could not add tnodes to management list" TENDSTR))); +- return YAFFS_FAIL; + + } else { + tnl->tnodes = newTnodes; +@@ -1239,11 +722,11 @@ + static yaffs_Tnode *yaffs_GetTnode(yaffs_Device * dev) + { + yaffs_Tnode *tn = yaffs_GetTnodeRaw(dev); +- ++ + if(tn) + memset(tn, 0, (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8); + +- return tn; ++ return tn; + } + + /* FreeTnode frees up a tnode and puts it back on the free list */ +@@ -1299,19 +782,19 @@ + __u32 bitInWord; + __u32 wordInMap; + __u32 mask; +- ++ + pos &= YAFFS_TNODES_LEVEL0_MASK; + val >>= dev->chunkGroupBits; +- ++ + bitInMap = pos * dev->tnodeWidth; + wordInMap = bitInMap /32; + bitInWord = bitInMap & (32 -1); +- ++ + mask = dev->tnodeMask << bitInWord; +- ++ + map[wordInMap] &= ~mask; + map[wordInMap] |= (mask & (val << bitInWord)); +- ++ + if(dev->tnodeWidth > (32-bitInWord)) { + bitInWord = (32 - bitInWord); + wordInMap++;; +@@ -1321,31 +804,31 @@ + } + } + +-static __u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos) ++__u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos) + { + __u32 *map = (__u32 *)tn; + __u32 bitInMap; + __u32 bitInWord; + __u32 wordInMap; + __u32 val; +- ++ + pos &= YAFFS_TNODES_LEVEL0_MASK; +- ++ + bitInMap = pos * dev->tnodeWidth; + wordInMap = bitInMap /32; + bitInWord = bitInMap & (32 -1); +- ++ + val = map[wordInMap] >> bitInWord; +- ++ + if(dev->tnodeWidth > (32-bitInWord)) { + bitInWord = (32 - bitInWord); + wordInMap++;; + val |= (map[wordInMap] << bitInWord); + } +- ++ + val &= dev->tnodeMask; + val <<= dev->chunkGroupBits; +- ++ + return val; + } + +@@ -1394,7 +877,7 @@ + while (level > 0 && tn) { + tn = tn-> + internal[(chunkId >> +- ( YAFFS_TNODES_LEVEL0_BITS + ++ ( YAFFS_TNODES_LEVEL0_BITS + + (level - 1) * + YAFFS_TNODES_INTERNAL_BITS) + ) & +@@ -1416,7 +899,7 @@ + * If the tn argument is NULL, then a fresh tnode will be added otherwise the specified tn will + * be plugged into the ttree. + */ +- ++ + static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device * dev, + yaffs_FileStructure * fStruct, + __u32 chunkId, +@@ -1453,7 +936,7 @@ + if (requiredTallness > fStruct->topLevel) { + /* Not tall enough,gotta make the tree taller */ + for (i = fStruct->topLevel; i < requiredTallness; i++) { +- ++ + tn = yaffs_GetTnode(dev); + + if (tn) { +@@ -1472,7 +955,7 @@ + + l = fStruct->topLevel; + tn = fStruct->top; +- ++ + if(l > 0) { + while (l > 0 && tn) { + x = (chunkId >> +@@ -1492,13 +975,13 @@ + if(tn->internal[x]) + yaffs_FreeTnode(dev,tn->internal[x]); + tn->internal[x] = passedTn; +- ++ + } else if(!tn->internal[x]) { + /* Don't have one, none passed in */ + tn->internal[x] = yaffs_GetTnode(dev); + } + } +- ++ + tn = tn->internal[x]; + l--; + } +@@ -1539,7 +1022,7 @@ + + /* DeleteWorker scans backwards through the tnode tree and deletes all the + * chunks and tnodes in the file +- * Returns 1 if the tree was deleted. ++ * Returns 1 if the tree was deleted. + * Returns 0 if it stopped early due to hitting the limit and the delete is incomplete. + */ + +@@ -1653,7 +1136,7 @@ + * of the tnode. + * Thus, essentially this is the same as DeleteWorker except that the chunks are soft deleted. + */ +- ++ + static int yaffs_SoftDeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, + __u32 level, int chunkOffset) + { +@@ -1694,7 +1177,7 @@ + theChunk = yaffs_GetChunkGroupBase(dev,tn,i); + if (theChunk) { + /* Note this does not find the real chunk, only the chunk group. +- * We make an assumption that a chunk group is not larger than ++ * We make an assumption that a chunk group is not larger than + * a block. + */ + yaffs_SoftDeleteChunk(dev, theChunk); +@@ -1796,7 +1279,7 @@ + /* Now we have a tree with all the non-zero branches NULL but the height + * is the same as it was. + * Let's see if we can trim internal tnodes to shorten the tree. +- * We can do this if only the 0th element in the tnode is in use ++ * We can do this if only the 0th element in the tnode is in use + * (ie all the non-zero are NULL) + */ + +@@ -1839,18 +1322,13 @@ + + /* make these things */ + newObjects = YMALLOC(nObjects * sizeof(yaffs_Object)); +- list = YMALLOC(sizeof(yaffs_ObjectList)); + +- if (!newObjects || !list) { +- if(newObjects) +- YFREE(newObjects); +- if(list) +- YFREE(list); ++ if (!newObjects) { + T(YAFFS_TRACE_ALLOCATE, + (TSTR("yaffs: Could not allocate more objects" TENDSTR))); + return YAFFS_FAIL; + } +- ++ + /* Hook them into the free list */ + for (i = 0; i < nObjects - 1; i++) { + newObjects[i].siblings.next = +@@ -1864,9 +1342,15 @@ + + /* Now add this bunch of Objects to a list for freeing up. */ + +- list->objects = newObjects; +- list->next = dev->allocatedObjectList; +- dev->allocatedObjectList = list; ++ list = YMALLOC(sizeof(yaffs_ObjectList)); ++ if (!list) { ++ T(YAFFS_TRACE_ALLOCATE, ++ (TSTR("Could not add objects to management list" TENDSTR))); ++ } else { ++ list->objects = newObjects; ++ list->next = dev->allocatedObjectList; ++ dev->allocatedObjectList = list; ++ } + + return YAFFS_OK; + } +@@ -2123,25 +1607,12 @@ + { + + yaffs_Object *theObject; +- yaffs_Tnode *tn; + + if (number < 0) { + number = yaffs_CreateNewObjectNumber(dev); + } + + theObject = yaffs_AllocateEmptyObject(dev); +- if(!theObject) +- return NULL; +- +- if(type == YAFFS_OBJECT_TYPE_FILE){ +- tn = yaffs_GetTnode(dev); +- if(!tn){ +- yaffs_FreeObject(theObject); +- return NULL; +- } +- } +- +- + + if (theObject) { + theObject->fake = 0; +@@ -2168,7 +1639,8 @@ + theObject->variant.fileVariant.scannedFileSize = 0; + theObject->variant.fileVariant.shrinkSize = 0xFFFFFFFF; /* max __u32 */ + theObject->variant.fileVariant.topLevel = 0; +- theObject->variant.fileVariant.top = tn; ++ theObject->variant.fileVariant.top = ++ yaffs_GetTnode(dev); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + INIT_LIST_HEAD(&theObject->variant.directoryVariant. +@@ -2205,7 +1677,7 @@ + return theObject; + + } +- ++ + + static YCHAR *yaffs_CloneString(const YCHAR * str) + { +@@ -2213,8 +1685,7 @@ + + if (str && *str) { + newStr = YMALLOC((yaffs_strlen(str) + 1) * sizeof(YCHAR)); +- if(newStr) +- yaffs_strcpy(newStr, str); ++ yaffs_strcpy(newStr, str); + } + + return newStr; +@@ -2227,7 +1698,7 @@ + * aliasString only has meaning for a sumlink. + * rdev only has meaning for devices (a subset of special objects) + */ +- ++ + static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type, + yaffs_Object * parent, + const YCHAR * name, +@@ -2238,7 +1709,6 @@ + const YCHAR * aliasString, __u32 rdev) + { + yaffs_Object *in; +- YCHAR *str; + + yaffs_Device *dev = parent->myDev; + +@@ -2249,16 +1719,6 @@ + + in = yaffs_CreateNewObject(dev, -1, type); + +- if(type == YAFFS_OBJECT_TYPE_SYMLINK){ +- str = yaffs_CloneString(aliasString); +- if(!str){ +- yaffs_FreeObject(in); +- return NULL; +- } +- } +- +- +- + if (in) { + in->chunkId = -1; + in->valid = 1; +@@ -2289,7 +1749,8 @@ + + switch (type) { + case YAFFS_OBJECT_TYPE_SYMLINK: +- in->variant.symLinkVariant.alias = str; ++ in->variant.symLinkVariant.alias = ++ yaffs_CloneString(aliasString); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + in->variant.hardLinkVariant.equivalentObject = +@@ -2298,7 +1759,7 @@ + equivalentObject->objectId; + list_add(&in->hardLinks, &equivalentObject->hardLinks); + break; +- case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_FILE: + case YAFFS_OBJECT_TYPE_DIRECTORY: + case YAFFS_OBJECT_TYPE_SPECIAL: + case YAFFS_OBJECT_TYPE_UNKNOWN: +@@ -2382,7 +1843,7 @@ + TENDSTR))); + YBUG(); + } +- ++ + /* TODO: Do we need this different handling for YAFFS2 and YAFFS1?? */ + if (obj->myDev->isYaffs2) { + unlinkOp = (newDir == obj->myDev->unlinkedDir); +@@ -2395,9 +1856,9 @@ + + existingTarget = yaffs_FindObjectByName(newDir, newName); + +- /* If the object is a file going into the unlinked directory, ++ /* If the object is a file going into the unlinked directory, + * then it is OK to just stuff it in since duplicate names are allowed. +- * else only proceed if the new name does not exist and if we're putting ++ * else only proceed if the new name does not exist and if we're putting + * it into a directory. + */ + if ((unlinkOp || +@@ -2461,7 +1922,7 @@ + /* There is a target that is a non-empty directory, so we fail */ + return YAFFS_FAIL; /* EEXIST or ENOTEMPTY */ + } else if (existingTarget && existingTarget != obj) { +- /* Nuke the target first, using shadowing, ++ /* Nuke the target first, using shadowing, + * but only if it isn't the same object + */ + yaffs_ChangeObjectName(obj, newDir, newName, force, +@@ -2479,13 +1940,10 @@ + static int yaffs_InitialiseBlocks(yaffs_Device * dev) + { + int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; +- +- dev->blockInfo = NULL; +- dev->chunkBits = NULL; +- ++ + dev->allocationBlock = -1; /* force it to get a new one */ + +- /* If the first allocation strategy fails, thry the alternate one */ ++ /* Todo we're assuming the malloc will pass. */ + dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo)); + if(!dev->blockInfo){ + dev->blockInfo = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockInfo)); +@@ -2493,20 +1951,17 @@ + } + else + dev->blockInfoAlt = 0; +- +- if(dev->blockInfo){ +- +- /* Set up dynamic blockinfo stuff. */ +- dev->chunkBitmapStride = (dev->nChunksPerBlock + 7) / 8; /* round up bytes */ +- dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks); +- if(!dev->chunkBits){ +- dev->chunkBits = YMALLOC_ALT(dev->chunkBitmapStride * nBlocks); +- dev->chunkBitsAlt = 1; +- } +- else +- dev->chunkBitsAlt = 0; ++ ++ /* Set up dynamic blockinfo stuff. */ ++ dev->chunkBitmapStride = (dev->nChunksPerBlock + 7) / 8; /* round up bytes */ ++ dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks); ++ if(!dev->chunkBits){ ++ dev->chunkBits = YMALLOC_ALT(dev->chunkBitmapStride * nBlocks); ++ dev->chunkBitsAlt = 1; + } +- ++ else ++ dev->chunkBitsAlt = 0; ++ + if (dev->blockInfo && dev->chunkBits) { + memset(dev->blockInfo, 0, nBlocks * sizeof(yaffs_BlockInfo)); + memset(dev->chunkBits, 0, dev->chunkBitmapStride * nBlocks); +@@ -2519,18 +1974,17 @@ + + static void yaffs_DeinitialiseBlocks(yaffs_Device * dev) + { +- if(dev->blockInfoAlt && dev->blockInfo) ++ if(dev->blockInfoAlt) + YFREE_ALT(dev->blockInfo); +- else if(dev->blockInfo) ++ else + YFREE(dev->blockInfo); +- + dev->blockInfoAlt = 0; + + dev->blockInfo = NULL; +- +- if(dev->chunkBitsAlt && dev->chunkBits) ++ ++ if(dev->chunkBitsAlt) + YFREE_ALT(dev->chunkBits); +- else if(dev->chunkBits) ++ else + YFREE(dev->chunkBits); + dev->chunkBitsAlt = 0; + dev->chunkBits = NULL; +@@ -2587,18 +2041,17 @@ + int i; + int iterations; + int dirtiest = -1; +- int pagesInUse = 0; ++ int pagesInUse; + int prioritised=0; + yaffs_BlockInfo *bi; ++ static int nonAggressiveSkip = 0; + int pendingPrioritisedExist = 0; +- ++ + /* First let's see if we need to grab a prioritised block */ + if(dev->hasPendingPrioritisedGCs){ + for(i = dev->internalStartBlock; i < dev->internalEndBlock && !prioritised; i++){ + + bi = yaffs_GetBlockInfo(dev, i); +- //yaffs_VerifyBlock(dev,bi,i); +- + if(bi->gcPrioritise) { + pendingPrioritisedExist = 1; + if(bi->blockState == YAFFS_BLOCK_STATE_FULL && +@@ -2610,7 +2063,7 @@ + } + } + } +- ++ + if(!pendingPrioritisedExist) /* None found, so we can clear this */ + dev->hasPendingPrioritisedGCs = 0; + } +@@ -2621,9 +2074,9 @@ + * block has only a few pages in use. + */ + +- dev->nonAggressiveSkip--; ++ nonAggressiveSkip--; + +- if (!aggressive && (dev->nonAggressiveSkip > 0)) { ++ if (!aggressive && (nonAggressiveSkip > 0)) { + return -1; + } + +@@ -2662,7 +2115,7 @@ + dirtiest = b; + pagesInUse = 0; + } +- else ++ else + #endif + + if (bi->blockState == YAFFS_BLOCK_STATE_FULL && +@@ -2684,7 +2137,7 @@ + dev->oldestDirtySequence = 0; + + if (dirtiest > 0) { +- dev->nonAggressiveSkip = 4; ++ nonAggressiveSkip = 4; + } + + return dirtiest; +@@ -2699,11 +2152,11 @@ + /* If the block is still healthy erase it and mark as clean. + * If the block has had a data failure, then retire it. + */ +- ++ + T(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, + (TSTR("yaffs_BlockBecameDirty block %d state %d %s"TENDSTR), + blockNo, bi->blockState, (bi->needsRetiring) ? "needs retiring" : "")); +- ++ + bi->blockState = YAFFS_BLOCK_STATE_DIRTY; + + if (!bi->needsRetiring) { +@@ -2716,8 +2169,7 @@ + } + } + +- if (erasedOk && +- ((yaffs_traceMask & YAFFS_TRACE_ERASE) || !yaffs_SkipVerification(dev))) { ++ if (erasedOk && (yaffs_traceMask & YAFFS_TRACE_ERASE)) { + int i; + for (i = 0; i < dev->nChunksPerBlock; i++) { + if (!yaffs_CheckChunkErased +@@ -2767,7 +2219,7 @@ + + return -1; + } +- ++ + /* Find an empty block. */ + + for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { +@@ -2808,13 +2260,13 @@ + int reservedChunks; + int reservedBlocks = dev->nReservedBlocks; + int checkpointBlocks; +- ++ + checkpointBlocks = dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint; + if(checkpointBlocks < 0) + checkpointBlocks = 0; +- ++ + reservedChunks = ((reservedBlocks + checkpointBlocks) * dev->nChunksPerBlock); +- ++ + return (dev->nFreeChunks > reservedChunks); + } + +@@ -2861,10 +2313,10 @@ + + if(blockUsedPtr) + *blockUsedPtr = bi; +- ++ + return retVal; + } +- ++ + T(YAFFS_TRACE_ERROR, + (TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR))); + +@@ -2895,7 +2347,6 @@ + int cleanups = 0; + int i; + int isCheckpointBlock; +- int matchingChunk; + + int chunksBefore = yaffs_GetErasedChunks(dev); + int chunksAfter; +@@ -2907,7 +2358,7 @@ + yaffs_Object *object; + + isCheckpointBlock = (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT); +- ++ + bi->blockState = YAFFS_BLOCK_STATE_COLLECTING; + + T(YAFFS_TRACE_TRACING, +@@ -2936,8 +2387,6 @@ + + __u8 *buffer = yaffs_GetTempBuffer(dev, __LINE__); + +- yaffs_VerifyBlock(dev,bi,block); +- + for (chunkInBlock = 0, oldChunk = block * dev->nChunksPerBlock; + chunkInBlock < dev->nChunksPerBlock + && yaffs_StillSomeChunkBits(dev, block); +@@ -2963,34 +2412,18 @@ + chunkInBlock, tags.objectId, tags.chunkId, + tags.byteCount)); + +- if(object && !yaffs_SkipVerification(dev)){ +- if(tags.chunkId == 0) +- matchingChunk = object->chunkId; +- else if(object->softDeleted) +- matchingChunk = oldChunk; /* Defeat the test */ +- else +- matchingChunk = yaffs_FindChunkInFile(object,tags.chunkId,NULL); +- +- if(oldChunk != matchingChunk) +- T(YAFFS_TRACE_ERROR, +- (TSTR("gc: page in gc mismatch: %d %d %d %d"TENDSTR), +- oldChunk,matchingChunk,tags.objectId, tags.chunkId)); +- +- } +- + if (!object) { + T(YAFFS_TRACE_ERROR, + (TSTR +- ("page %d in gc has no object: %d %d %d " +- TENDSTR), oldChunk, +- tags.objectId, tags.chunkId, tags.byteCount)); ++ ("page %d in gc has no object " ++ TENDSTR), oldChunk)); + } + + if (object && object->deleted + && tags.chunkId != 0) { + /* Data chunk in a deleted file, throw it away + * It's a soft deleted data chunk, +- * No need to copy this, just forget about it and ++ * No need to copy this, just forget about it and + * fix up the object. + */ + +@@ -3040,8 +2473,6 @@ + oh->shadowsObject = -1; + tags.extraShadows = 0; + tags.extraIsShrinkHeader = 0; +- +- yaffs_VerifyObjectHeader(object,oh,&tags,1); + } + + newChunk = +@@ -3098,8 +2529,6 @@ + + } + +- yaffs_VerifyCollectedBlock(dev,bi,block); +- + if (chunksBefore >= (chunksAfter = yaffs_GetErasedChunks(dev))) { + T(YAFFS_TRACE_GC, + (TSTR +@@ -3127,26 +2556,26 @@ + int aggressive; + int gcOk = YAFFS_OK; + int maxTries = 0; +- ++ + int checkpointBlockAdjust; + + if (dev->isDoingGC) { + /* Bail out so we don't get recursive gc */ + return YAFFS_OK; + } +- ++ + /* This loop should pass the first time. + * We'll only see looping here if the erase of the collected block fails. + */ + + do { + maxTries++; +- ++ + checkpointBlockAdjust = (dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint); + if(checkpointBlockAdjust < 0) + checkpointBlockAdjust = 0; + +- if (dev->nErasedBlocks < (dev->nReservedBlocks + checkpointBlockAdjust + 2)) { ++ if (dev->nErasedBlocks < (dev->nReservedBlocks + checkpointBlockAdjust)) { + /* We need a block soon...*/ + aggressive = 1; + } else { +@@ -3326,11 +2755,11 @@ + static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode, + int chunkInNAND, int inScan) + { +- /* NB inScan is zero unless scanning. +- * For forward scanning, inScan is > 0; ++ /* NB inScan is zero unless scanning. ++ * For forward scanning, inScan is > 0; + * for backward scanning inScan is < 0 + */ +- ++ + yaffs_Tnode *tn; + yaffs_Device *dev = in->myDev; + int existingChunk; +@@ -3354,7 +2783,7 @@ + return YAFFS_OK; + } + +- tn = yaffs_AddOrFindLevel0Tnode(dev, ++ tn = yaffs_AddOrFindLevel0Tnode(dev, + &in->variant.fileVariant, + chunkInInode, + NULL); +@@ -3366,7 +2795,7 @@ + + if (inScan != 0) { + /* If we're scanning then we need to test for duplicates +- * NB This does not need to be efficient since it should only ever ++ * NB This does not need to be efficient since it should only ever + * happen when the power fails during a write, then only one + * chunk should ever be affected. + * +@@ -3407,7 +2836,7 @@ + + } + +- /* NB The deleted flags should be false, otherwise the chunks will ++ /* NB The deleted flags should be false, otherwise the chunks will + * not be loaded during a scan + */ + +@@ -3418,7 +2847,7 @@ + (in->myDev->isYaffs2 || + existingChunk <= 0 || + ((existingSerial + 1) & 3) == newSerial)) { +- /* Forward scanning. ++ /* Forward scanning. + * Use new + * Delete the old one and drop through to update the tnode + */ +@@ -3459,7 +2888,7 @@ + (TSTR("Chunk %d not found zero instead" TENDSTR), + chunkInNAND)); + /* get sane (zero) data if you read a hole */ +- memset(buffer, 0, in->myDev->nDataBytesPerChunk); ++ memset(buffer, 0, in->myDev->nDataBytesPerChunk); + return 0; + } + +@@ -3475,17 +2904,10 @@ + if (chunkId <= 0) + return; + +- + dev->nDeletions++; + block = chunkId / dev->nChunksPerBlock; + page = chunkId % dev->nChunksPerBlock; + +- +- if(!yaffs_CheckChunkBit(dev,block,page)) +- T(YAFFS_TRACE_VERIFY, +- (TSTR("Deleting invalid chunk %d"TENDSTR), +- chunkId)); +- + bi = yaffs_GetBlockInfo(dev, block); + + T(YAFFS_TRACE_DELETION, +@@ -3596,19 +3018,15 @@ + + int newChunkId; + yaffs_ExtendedTags newTags; +- yaffs_ExtendedTags oldTags; + + __u8 *buffer = NULL; + YCHAR oldName[YAFFS_MAX_NAME_LENGTH + 1]; + + yaffs_ObjectHeader *oh = NULL; + +- yaffs_strcpy(oldName,"silly old name"); +- + if (!in->fake || force) { + + yaffs_CheckGarbageCollection(dev); +- yaffs_CheckObjectDetailsLoaded(in); + + buffer = yaffs_GetTempBuffer(in->myDev, __LINE__); + oh = (yaffs_ObjectHeader *) buffer; +@@ -3617,10 +3035,7 @@ + + if (prevChunkId >= 0) { + result = yaffs_ReadChunkWithTagsFromNAND(dev, prevChunkId, +- buffer, &oldTags); +- +- yaffs_VerifyObjectHeader(in,oh,&oldTags,0); +- ++ buffer, NULL); + memcpy(oldName, oh->name, sizeof(oh->name)); + } + +@@ -3654,7 +3069,7 @@ + if (name && *name) { + memset(oh->name, 0, sizeof(oh->name)); + yaffs_strncpy(oh->name, name, YAFFS_MAX_NAME_LENGTH); +- } else if (prevChunkId>=0) { ++ } else if (prevChunkId) { + memcpy(oh->name, oldName, sizeof(oh->name)); + } else { + memset(oh->name, 0, sizeof(oh->name)); +@@ -3708,8 +3123,6 @@ + newTags.extraShadows = (oh->shadowsObject > 0) ? 1 : 0; + newTags.extraObjectType = in->variantType; + +- yaffs_VerifyObjectHeader(in,oh,&newTags,1); +- + /* Create new chunk in NAND */ + newChunkId = + yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags, +@@ -3748,11 +3161,11 @@ + + /*------------------------ Short Operations Cache ---------------------------------------- + * In many situations where there is no high level buffering (eg WinCE) a lot of +- * reads might be short sequential reads, and a lot of writes may be short ++ * reads might be short sequential reads, and a lot of writes may be short + * sequential writes. eg. scanning/writing a jpeg file. +- * In these cases, a short read/write cache can provide a huge perfomance benefit ++ * In these cases, a short read/write cache can provide a huge perfomance benefit + * with dumb-as-a-rock code. +- * In Linux, the page cache provides read buffering aand the short op cache provides write ++ * In Linux, the page cache provides read buffering aand the short op cache provides write + * buffering. + * + * There are a limited number (~10) of cache chunks per device so that we don't +@@ -3765,14 +3178,14 @@ + int i; + yaffs_ChunkCache *cache; + int nCaches = obj->myDev->nShortOpCaches; +- ++ + for(i = 0; i < nCaches; i++){ + cache = &dev->srCache[i]; + if (cache->object == obj && + cache->dirty) + return 1; + } +- ++ + return 0; + } + +@@ -3838,7 +3251,7 @@ + yaffs_Object *obj; + int nCaches = dev->nShortOpCaches; + int i; +- ++ + /* Find a dirty object in the cache and flush it... + * until there are no further dirty objects. + */ +@@ -3848,18 +3261,18 @@ + if (dev->srCache[i].object && + dev->srCache[i].dirty) + obj = dev->srCache[i].object; +- ++ + } + if(obj) + yaffs_FlushFilesChunkCache(obj); +- ++ + } while(obj); +- ++ + } + + + /* Grab us a cache chunk for use. +- * First look for an empty one. ++ * First look for an empty one. + * Then look for the least recently used non-dirty one. + * Then look for the least recently used dirty one...., flush and look again. + */ +@@ -3871,7 +3284,7 @@ + + if (dev->nShortOpCaches > 0) { + for (i = 0; i < dev->nShortOpCaches; i++) { +- if (!dev->srCache[i].object) ++ if (!dev->srCache[i].object) + return &dev->srCache[i]; + } + +@@ -4032,14 +3445,11 @@ + static int yaffs_WriteCheckpointValidityMarker(yaffs_Device *dev,int head) + { + yaffs_CheckpointValidity cp; +- +- memset(&cp,0,sizeof(cp)); +- + cp.structType = sizeof(cp); + cp.magic = YAFFS_MAGIC; + cp.version = YAFFS_CHECKPOINT_VERSION; + cp.head = (head) ? 1 : 0; +- ++ + return (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp))? + 1 : 0; + } +@@ -4048,9 +3458,9 @@ + { + yaffs_CheckpointValidity cp; + int ok; +- ++ + ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp)); +- ++ + if(ok) + ok = (cp.structType == sizeof(cp)) && + (cp.magic == YAFFS_MAGIC) && +@@ -4059,20 +3469,20 @@ + return ok ? 1 : 0; + } + +-static void yaffs_DeviceToCheckpointDevice(yaffs_CheckpointDevice *cp, ++static void yaffs_DeviceToCheckpointDevice(yaffs_CheckpointDevice *cp, + yaffs_Device *dev) + { + cp->nErasedBlocks = dev->nErasedBlocks; + cp->allocationBlock = dev->allocationBlock; + cp->allocationPage = dev->allocationPage; + cp->nFreeChunks = dev->nFreeChunks; +- ++ + cp->nDeletedFiles = dev->nDeletedFiles; + cp->nUnlinkedFiles = dev->nUnlinkedFiles; + cp->nBackgroundDeletions = dev->nBackgroundDeletions; + cp->sequenceNumber = dev->sequenceNumber; + cp->oldestDirtySequence = dev->oldestDirtySequence; +- ++ + } + + static void yaffs_CheckpointDeviceToDevice(yaffs_Device *dev, +@@ -4082,7 +3492,7 @@ + dev->allocationBlock = cp->allocationBlock; + dev->allocationPage = cp->allocationPage; + dev->nFreeChunks = cp->nFreeChunks; +- ++ + dev->nDeletedFiles = cp->nDeletedFiles; + dev->nUnlinkedFiles = cp->nUnlinkedFiles; + dev->nBackgroundDeletions = cp->nBackgroundDeletions; +@@ -4098,20 +3508,20 @@ + __u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1); + + int ok; +- ++ + /* Write device runtime values*/ + yaffs_DeviceToCheckpointDevice(&cp,dev); + cp.structType = sizeof(cp); +- ++ + ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp)); +- ++ + /* Write block info */ + if(ok) { + nBytes = nBlocks * sizeof(yaffs_BlockInfo); + ok = (yaffs_CheckpointWrite(dev,dev->blockInfo,nBytes) == nBytes); + } +- +- /* Write chunk bits */ ++ ++ /* Write chunk bits */ + if(ok) { + nBytes = nBlocks * dev->chunkBitmapStride; + ok = (yaffs_CheckpointWrite(dev,dev->chunkBits,nBytes) == nBytes); +@@ -4126,28 +3536,28 @@ + __u32 nBytes; + __u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1); + +- int ok; +- ++ int ok; ++ + ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp)); + if(!ok) + return 0; +- ++ + if(cp.structType != sizeof(cp)) + return 0; +- +- ++ ++ + yaffs_CheckpointDeviceToDevice(dev,&cp); +- ++ + nBytes = nBlocks * sizeof(yaffs_BlockInfo); +- ++ + ok = (yaffs_CheckpointRead(dev,dev->blockInfo,nBytes) == nBytes); +- ++ + if(!ok) + return 0; + nBytes = nBlocks * dev->chunkBitmapStride; +- ++ + ok = (yaffs_CheckpointRead(dev,dev->chunkBits,nBytes) == nBytes); +- ++ + return ok ? 1 : 0; + } + +@@ -4158,7 +3568,7 @@ + cp->objectId = obj->objectId; + cp->parentId = (obj->parent) ? obj->parent->objectId : 0; + cp->chunkId = obj->chunkId; +- cp->variantType = obj->variantType; ++ cp->variantType = obj->variantType; + cp->deleted = obj->deleted; + cp->softDeleted = obj->softDeleted; + cp->unlinked = obj->unlinked; +@@ -4167,7 +3577,7 @@ + cp->unlinkAllowed = obj->unlinkAllowed; + cp->serial = obj->serial; + cp->nDataChunks = obj->nDataChunks; +- ++ + if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) + cp->fileSizeOrEquivalentObjectId = obj->variant.fileVariant.fileSize; + else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) +@@ -4178,9 +3588,9 @@ + { + + yaffs_Object *parent; +- ++ + obj->objectId = cp->objectId; +- ++ + if(cp->parentId) + parent = yaffs_FindOrCreateObjectByNumber( + obj->myDev, +@@ -4188,12 +3598,12 @@ + YAFFS_OBJECT_TYPE_DIRECTORY); + else + parent = NULL; +- ++ + if(parent) + yaffs_AddObjectToDirectory(parent, obj); +- ++ + obj->chunkId = cp->chunkId; +- obj->variantType = cp->variantType; ++ obj->variantType = cp->variantType; + obj->deleted = cp->deleted; + obj->softDeleted = cp->softDeleted; + obj->unlinked = cp->unlinked; +@@ -4202,12 +3612,12 @@ + obj->unlinkAllowed = cp->unlinkAllowed; + obj->serial = cp->serial; + obj->nDataChunks = cp->nDataChunks; +- ++ + if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) + obj->variant.fileVariant.fileSize = cp->fileSizeOrEquivalentObjectId; + else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) + obj->variant.hardLinkVariant.equivalentObjectId = cp->fileSizeOrEquivalentObjectId; +- ++ + if(obj->objectId >= YAFFS_NOBJECT_BUCKETS) + obj->lazyLoaded = 1; + } +@@ -4250,17 +3660,17 @@ + { + __u32 endMarker = ~0; + int ok = 1; +- ++ + if(obj->variantType == YAFFS_OBJECT_TYPE_FILE){ + ok = yaffs_CheckpointTnodeWorker(obj, + obj->variant.fileVariant.top, + obj->variant.fileVariant.topLevel, + 0); + if(ok) +- ok = (yaffs_CheckpointWrite(obj->myDev,&endMarker,sizeof(endMarker)) == ++ ok = (yaffs_CheckpointWrite(obj->myDev,&endMarker,sizeof(endMarker)) == + sizeof(endMarker)); + } +- ++ + return ok ? 1 : 0; + } + +@@ -4271,15 +3681,12 @@ + yaffs_Device *dev = obj->myDev; + yaffs_FileStructure *fileStructPtr = &obj->variant.fileVariant; + yaffs_Tnode *tn; +- int nread = 0; +- ++ + ok = (yaffs_CheckpointRead(dev,&baseChunk,sizeof(baseChunk)) == sizeof(baseChunk)); +- ++ + while(ok && (~baseChunk)){ +- nread++; + /* Read level 0 tnode */ +- +- ++ + /* printf("read tnode at %d\n",baseChunk); */ + tn = yaffs_GetTnodeRaw(dev); + if(tn) +@@ -4287,27 +3694,22 @@ + (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8); + else + ok = 0; +- ++ + if(tn && ok){ + ok = yaffs_AddOrFindLevel0Tnode(dev, + fileStructPtr, + baseChunk, + tn) ? 1 : 0; +- + } +- ++ + if(ok) + ok = (yaffs_CheckpointRead(dev,&baseChunk,sizeof(baseChunk)) == sizeof(baseChunk)); +- ++ + } + +- T(YAFFS_TRACE_CHECKPOINT,( +- TSTR("Checkpoint read tnodes %d records, last %d. ok %d" TENDSTR), +- nread,baseChunk,ok)); +- +- return ok ? 1 : 0; ++ return ok ? 1 : 0; + } +- ++ + + static int yaffs_WriteCheckpointObjects(yaffs_Device *dev) + { +@@ -4317,11 +3719,11 @@ + int ok = 1; + struct list_head *lh; + +- ++ + /* Iterate through the objects in each hash entry, + * dumping them to the checkpointing stream. + */ +- ++ + for(i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++){ + list_for_each(lh, &dev->objectBucket[i].list) { + if (lh) { +@@ -4333,9 +3735,9 @@ + T(YAFFS_TRACE_CHECKPOINT,( + TSTR("Checkpoint write object %d parent %d type %d chunk %d obj addr %x" TENDSTR), + cp.objectId,cp.parentId,cp.variantType,cp.chunkId,(unsigned) obj)); +- ++ + ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp)); +- ++ + if(ok && obj->variantType == YAFFS_OBJECT_TYPE_FILE){ + ok = yaffs_WriteCheckpointTnodes(obj); + } +@@ -4343,14 +3745,14 @@ + } + } + } +- ++ + /* Dump end of list */ + memset(&cp,0xFF,sizeof(yaffs_CheckpointObject)); + cp.structType = sizeof(cp); +- ++ + if(ok) + ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp)); +- ++ + return ok ? 1 : 0; + } + +@@ -4361,22 +3763,20 @@ + int ok = 1; + int done = 0; + yaffs_Object *hardList = NULL; +- ++ + while(ok && !done) { + ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp)); + if(cp.structType != sizeof(cp)) { +- T(YAFFS_TRACE_CHECKPOINT,(TSTR("struct size %d instead of %d ok %d"TENDSTR), +- cp.structType,sizeof(cp),ok)); ++ /* printf("structure parsing failed\n"); */ + ok = 0; + } +- +- T(YAFFS_TRACE_CHECKPOINT,(TSTR("Checkpoint read object %d parent %d type %d chunk %d " TENDSTR), +- cp.objectId,cp.parentId,cp.variantType,cp.chunkId)); +- ++ + if(ok && cp.objectId == ~0) + done = 1; + else if(ok){ + obj = yaffs_FindOrCreateObjectByNumber(dev,cp.objectId, cp.variantType); ++ T(YAFFS_TRACE_CHECKPOINT,(TSTR("Checkpoint read object %d parent %d type %d chunk %d obj addr %x" TENDSTR), ++ cp.objectId,cp.parentId,cp.variantType,cp.chunkId,(unsigned) obj)); + if(obj) { + yaffs_CheckpointObjectToObject(obj,&cp); + if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) { +@@ -4387,93 +3787,39 @@ + hardList; + hardList = obj; + } +- ++ + } + } + } +- ++ + if(ok) + yaffs_HardlinkFixup(dev,hardList); +- ++ + return ok ? 1 : 0; + } + +-static int yaffs_WriteCheckpointSum(yaffs_Device *dev) +-{ +- __u32 checkpointSum; +- int ok; +- +- yaffs_GetCheckpointSum(dev,&checkpointSum); +- +- ok = (yaffs_CheckpointWrite(dev,&checkpointSum,sizeof(checkpointSum)) == sizeof(checkpointSum)); +- +- if(!ok) +- return 0; +- +- return 1; +-} +- +-static int yaffs_ReadCheckpointSum(yaffs_Device *dev) +-{ +- __u32 checkpointSum0; +- __u32 checkpointSum1; +- int ok; +- +- yaffs_GetCheckpointSum(dev,&checkpointSum0); +- +- ok = (yaffs_CheckpointRead(dev,&checkpointSum1,sizeof(checkpointSum1)) == sizeof(checkpointSum1)); +- +- if(!ok) +- return 0; +- +- if(checkpointSum0 != checkpointSum1) +- return 0; +- +- return 1; +-} +- +- + static int yaffs_WriteCheckpointData(yaffs_Device *dev) + { + +- int ok = 1; +- +- if(dev->skipCheckpointWrite || !dev->isYaffs2){ +- T(YAFFS_TRACE_CHECKPOINT,(TSTR("skipping checkpoint write" TENDSTR))); +- ok = 0; +- } +- ++ int ok; ++ ++ ok = yaffs_CheckpointOpen(dev,1); ++ + if(ok) +- ok = yaffs_CheckpointOpen(dev,1); +- +- if(ok){ +- T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint validity" TENDSTR))); + ok = yaffs_WriteCheckpointValidityMarker(dev,1); +- } +- if(ok){ +- T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint device" TENDSTR))); ++ if(ok) + ok = yaffs_WriteCheckpointDevice(dev); +- } +- if(ok){ +- T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint objects" TENDSTR))); ++ if(ok) + ok = yaffs_WriteCheckpointObjects(dev); +- } +- if(ok){ +- T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint validity" TENDSTR))); ++ if(ok) + ok = yaffs_WriteCheckpointValidityMarker(dev,0); +- } +- +- if(ok){ +- ok = yaffs_WriteCheckpointSum(dev); +- } +- +- ++ + if(!yaffs_CheckpointClose(dev)) + ok = 0; +- ++ + if(ok) + dev->isCheckpointed = 1; +- else ++ else + dev->isCheckpointed = 0; + + return dev->isCheckpointed; +@@ -4481,44 +3827,27 @@ + + static int yaffs_ReadCheckpointData(yaffs_Device *dev) + { +- int ok = 1; +- +- if(dev->skipCheckpointRead || !dev->isYaffs2){ +- T(YAFFS_TRACE_CHECKPOINT,(TSTR("skipping checkpoint read" TENDSTR))); +- ok = 0; +- } +- ++ int ok; ++ ++ ok = yaffs_CheckpointOpen(dev,0); /* open for read */ ++ + if(ok) +- ok = yaffs_CheckpointOpen(dev,0); /* open for read */ +- +- if(ok){ +- T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint validity" TENDSTR))); + ok = yaffs_ReadCheckpointValidityMarker(dev,1); +- } +- if(ok){ +- T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint device" TENDSTR))); ++ if(ok) + ok = yaffs_ReadCheckpointDevice(dev); +- } +- if(ok){ +- T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint objects" TENDSTR))); ++ if(ok) + ok = yaffs_ReadCheckpointObjects(dev); +- } +- if(ok){ +- T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint validity" TENDSTR))); ++ if(ok) + ok = yaffs_ReadCheckpointValidityMarker(dev,0); +- } ++ + +- if(ok){ +- ok = yaffs_ReadCheckpointSum(dev); +- T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint checksum %d" TENDSTR),ok)); +- } + + if(!yaffs_CheckpointClose(dev)) + ok = 0; + + if(ok) + dev->isCheckpointed = 1; +- else ++ else + dev->isCheckpointed = 0; + + return ok ? 1 : 0; +@@ -4527,7 +3856,7 @@ + + static void yaffs_InvalidateCheckpoint(yaffs_Device *dev) + { +- if(dev->isCheckpointed || ++ if(dev->isCheckpointed || + dev->blocksInCheckpoint > 0){ + dev->isCheckpointed = 0; + yaffs_CheckpointInvalidateStream(dev); +@@ -4539,19 +3868,13 @@ + + int yaffs_CheckpointSave(yaffs_Device *dev) + { +- ++ yaffs_ReportOddballBlocks(dev); + T(YAFFS_TRACE_CHECKPOINT,(TSTR("save entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); + +- yaffs_VerifyObjects(dev); +- yaffs_VerifyBlocks(dev); +- yaffs_VerifyFreeChunks(dev); +- +- if(!dev->isCheckpointed) { +- yaffs_InvalidateCheckpoint(dev); ++ if(!dev->isCheckpointed) + yaffs_WriteCheckpointData(dev); +- } +- +- T(YAFFS_TRACE_ALWAYS,(TSTR("save exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); ++ ++ T(YAFFS_TRACE_CHECKPOINT,(TSTR("save exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); + + return dev->isCheckpointed; + } +@@ -4560,17 +3883,13 @@ + { + int retval; + T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); +- ++ + retval = yaffs_ReadCheckpointData(dev); + +- if(dev->isCheckpointed){ +- yaffs_VerifyObjects(dev); +- yaffs_VerifyBlocks(dev); +- yaffs_VerifyFreeChunks(dev); +- } +- + T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); +- ++ ++ yaffs_ReportOddballBlocks(dev); ++ + return retval; + } + +@@ -4606,7 +3925,7 @@ + chunk++; + + /* OK now check for the curveball where the start and end are in +- * the same chunk. ++ * the same chunk. + */ + if ((start + n) < dev->nDataBytesPerChunk) { + nToCopy = n; +@@ -4763,7 +4082,7 @@ + yaffs_ChunkCache *cache; + /* If we can't find the data in the cache, then load the cache */ + cache = yaffs_FindChunkCache(in, chunk); +- ++ + if (!cache + && yaffs_CheckSpaceForAllocation(in-> + myDev)) { +@@ -4776,12 +4095,12 @@ + cache-> + data); + } +- else if(cache && ++ else if(cache && + !cache->dirty && + !yaffs_CheckSpaceForAllocation(in->myDev)){ + /* Drop the cache if it was a read cache item and + * no space check has been made for it. +- */ ++ */ + cache = NULL; + } + +@@ -4945,9 +4264,9 @@ + int oldFileSize = in->variant.fileVariant.fileSize; + int newSizeOfPartialChunk; + int newFullChunks; +- ++ + yaffs_Device *dev = in->myDev; +- ++ + yaffs_AddrToChunk(dev, newSize, &newFullChunks, &newSizeOfPartialChunk); + + yaffs_FlushFilesChunkCache(in); +@@ -4969,7 +4288,7 @@ + + if (newSizeOfPartialChunk != 0) { + int lastChunk = 1 + newFullChunks; +- ++ + __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__); + + /* Got to read and rewrite the last chunk with its new size and zero pad */ +@@ -4993,8 +4312,8 @@ + in->variant.fileVariant.fileSize = newSize; + } + +- +- ++ ++ + /* Write a new object header. + * show we've shrunk the file, if need be + * Do this only if the file is not in the deleted directories. +@@ -5058,7 +4377,7 @@ + + if (in->myDev->isYaffs2 && (in->parent != in->myDev->deletedDir)) { + /* Move to the unlinked directory so we have a record that it was deleted. */ +- yaffs_ChangeObjectName(in, in->myDev->deletedDir,"deleted", 0, 0); ++ yaffs_ChangeObjectName(in, in->myDev->deletedDir, NULL, 0, 0); + + } + +@@ -5096,7 +4415,7 @@ + if (immediateDeletion) { + retVal = + yaffs_ChangeObjectName(in, in->myDev->deletedDir, +- "deleted", 0, 0); ++ NULL, 0, 0); + T(YAFFS_TRACE_TRACING, + (TSTR("yaffs: immediate deletion of file %d" TENDSTR), + in->objectId)); +@@ -5109,7 +4428,7 @@ + } else { + retVal = + yaffs_ChangeObjectName(in, in->myDev->unlinkedDir, +- "unlinked", 0, 0); ++ NULL, 0, 0); + } + + } +@@ -5313,7 +4632,7 @@ + { + yaffs_Object *hl; + yaffs_Object *in; +- ++ + while (hardList) { + hl = hardList; + hardList = (yaffs_Object *) (hardList->hardLinks.next); +@@ -5370,6 +4689,7 @@ + int deleted; + yaffs_BlockState state; + yaffs_Object *hardList = NULL; ++ yaffs_Object *hl; + yaffs_BlockInfo *bi; + int sequenceNumber; + yaffs_ObjectHeader *oh; +@@ -5377,9 +4697,6 @@ + yaffs_Object *parent; + int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; + +- int alloc_failed = 0; +- +- + __u8 *chunkData; + + yaffs_BlockIndex *blockIndex = NULL; +@@ -5389,9 +4706,9 @@ + (TSTR("yaffs_Scan is not for YAFFS2!" TENDSTR))); + return YAFFS_FAIL; + } +- ++ + //TODO Throw all the yaffs2 stuuf out of yaffs_Scan since it is only for yaffs1 format. +- ++ + T(YAFFS_TRACE_SCAN, + (TSTR("yaffs_Scan starts intstartblk %d intendblk %d..." TENDSTR), + dev->internalStartBlock, dev->internalEndBlock)); +@@ -5402,8 +4719,6 @@ + + if (dev->isYaffs2) { + blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex)); +- if(!blockIndex) +- return YAFFS_FAIL; + } + + /* Scan all the blocks to determine their state */ +@@ -5485,7 +4800,7 @@ + } + + /* For each block.... */ +- for (blockIterator = startIterator; !alloc_failed && blockIterator <= endIterator; ++ for (blockIterator = startIterator; blockIterator <= endIterator; + blockIterator++) { + + if (dev->isYaffs2) { +@@ -5501,7 +4816,7 @@ + deleted = 0; + + /* For each chunk in each block that needs scanning....*/ +- for (c = 0; !alloc_failed && c < dev->nChunksPerBlock && ++ for (c = 0; c < dev->nChunksPerBlock && + state == YAFFS_BLOCK_STATE_NEEDS_SCANNING; c++) { + /* Read the tags and decide what to do */ + chunk = blk * dev->nChunksPerBlock + c; +@@ -5520,7 +4835,7 @@ + /*T((" %d %d deleted\n",blk,c)); */ + } else if (!tags.chunkUsed) { + /* An unassigned chunk in the block +- * This means that either the block is empty or ++ * This means that either the block is empty or + * this is the one being allocated from + */ + +@@ -5537,9 +4852,9 @@ + state = YAFFS_BLOCK_STATE_ALLOCATING; + dev->allocationBlock = blk; + dev->allocationPage = c; +- dev->allocationBlockFinder = blk; ++ dev->allocationBlockFinder = blk; + /* Set it to here to encourage the allocator to go forth from here. */ +- ++ + /* Yaffs2 sanity check: + * This should be the one with the highest sequence number + */ +@@ -5569,20 +4884,12 @@ + /* PutChunkIntoFile checks for a clash (two data chunks with + * the same chunkId). + */ +- +- if(!in) +- alloc_failed = 1; +- +- if(in){ +- if(!yaffs_PutChunkIntoFile(in, tags.chunkId, chunk,1)) +- alloc_failed = 1; +- } +- ++ yaffs_PutChunkIntoFile(in, tags.chunkId, chunk, ++ 1); + endpos = + (tags.chunkId - 1) * dev->nDataBytesPerChunk + + tags.byteCount; +- if (in && +- in->variantType == YAFFS_OBJECT_TYPE_FILE ++ if (in->variantType == YAFFS_OBJECT_TYPE_FILE + && in->variant.fileVariant.scannedFileSize < + endpos) { + in->variant.fileVariant. +@@ -5613,7 +4920,7 @@ + tags.objectId); + if (in && in->variantType != oh->type) { + /* This should not happen, but somehow +- * Wev'e ended up with an objectId that has been reused but not yet ++ * Wev'e ended up with an objectId that has been reused but not yet + * deleted, and worse still it has changed type. Delete the old object. + */ + +@@ -5627,17 +4934,14 @@ + objectId, + oh->type); + +- if(!in) +- alloc_failed = 1; +- +- if (in && oh->shadowsObject > 0) { ++ if (oh->shadowsObject > 0) { + yaffs_HandleShadowedObject(dev, + oh-> + shadowsObject, + 0); + } + +- if (in && in->valid) { ++ if (in->valid) { + /* We have already filled this one. We have a duplicate and need to resolve it. */ + + unsigned existingSerial = in->serial; +@@ -5658,7 +4962,7 @@ + } + } + +- if (in && !in->valid && ++ if (!in->valid && + (tags.objectId == YAFFS_OBJECTID_ROOT || + tags.objectId == YAFFS_OBJECTID_LOSTNFOUND)) { + /* We only load some info, don't fiddle with directory structure */ +@@ -5683,7 +4987,7 @@ + #endif + in->chunkId = chunk; + +- } else if (in && !in->valid) { ++ } else if (!in->valid) { + /* we need to load this info */ + + in->valid = 1; +@@ -5752,11 +5056,11 @@ + * Since we might scan a hardlink before its equivalent object is scanned + * we put them all in a list. + * After scanning is complete, we should have all the objects, so we run through this +- * list and fix up all the chains. ++ * list and fix up all the chains. + */ + + switch (in->variantType) { +- case YAFFS_OBJECT_TYPE_UNKNOWN: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: + /* Todo got a problem */ + break; + case YAFFS_OBJECT_TYPE_FILE: +@@ -5791,11 +5095,10 @@ + case YAFFS_OBJECT_TYPE_SPECIAL: + /* Do nothing */ + break; +- case YAFFS_OBJECT_TYPE_SYMLINK: +- in->variant.symLinkVariant.alias = ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symLinkVariant. ++ alias = + yaffs_CloneString(oh->alias); +- if(!in->variant.symLinkVariant.alias) +- alloc_failed = 1; + break; + } + +@@ -5826,11 +5129,11 @@ + if (blockIndex) { + YFREE(blockIndex); + } +- +- ++ ++ + /* Ok, we've done all the scanning. + * Fix up the hard link chains. +- * We should now have scanned all the objects, now it's time to add these ++ * We should now have scanned all the objects, now it's time to add these + * hardlinks. + */ + +@@ -5857,13 +5160,8 @@ + + yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); + +- if(alloc_failed){ +- return YAFFS_FAIL; +- } +- + T(YAFFS_TRACE_SCAN, (TSTR("yaffs_Scan ends" TENDSTR))); + +- + return YAFFS_OK; + } + +@@ -5874,23 +5172,19 @@ + yaffs_Device *dev = in->myDev; + yaffs_ExtendedTags tags; + int result; +- int alloc_failed = 0; +- +- if(!in) +- return; +- ++ + #if 0 + T(YAFFS_TRACE_SCAN,(TSTR("details for object %d %s loaded" TENDSTR), + in->objectId, + in->lazyLoaded ? "not yet" : "already")); + #endif +- ++ + if(in->lazyLoaded){ + in->lazyLoaded = 0; + chunkData = yaffs_GetTempBuffer(dev, __LINE__); + + result = yaffs_ReadChunkWithTagsFromNAND(dev,in->chunkId,chunkData,&tags); +- oh = (yaffs_ObjectHeader *) chunkData; ++ oh = (yaffs_ObjectHeader *) chunkData; + + in->yst_mode = oh->yst_mode; + #ifdef CONFIG_YAFFS_WINCE +@@ -5907,17 +5201,14 @@ + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; +- ++ + #endif + yaffs_SetObjectName(in, oh->name); +- +- if(in->variantType == YAFFS_OBJECT_TYPE_SYMLINK){ ++ ++ if(in->variantType == YAFFS_OBJECT_TYPE_SYMLINK) + in->variant.symLinkVariant.alias = + yaffs_CloneString(oh->alias); +- if(!in->variant.symLinkVariant.alias) +- alloc_failed = 1; /* Not returned to caller */ +- } +- ++ + yaffs_ReleaseTempBuffer(dev,chunkData, __LINE__); + } + } +@@ -5945,13 +5236,12 @@ + int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; + int itsUnlinked; + __u8 *chunkData; +- ++ + int fileSize; + int isShrink; + int foundChunksInBlock; + int equivalentObjectId; +- int alloc_failed = 0; +- ++ + + yaffs_BlockIndex *blockIndex = NULL; + int altBlockIndex = 0; +@@ -5971,20 +5261,18 @@ + dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER; + + blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex)); +- ++ + if(!blockIndex) { + blockIndex = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockIndex)); + altBlockIndex = 1; + } +- ++ + if(!blockIndex) { + T(YAFFS_TRACE_SCAN, + (TSTR("yaffs_Scan() could not allocate block index!" TENDSTR))); + return YAFFS_FAIL; + } +- +- dev->blocksInCheckpoint = 0; +- ++ + chunkData = yaffs_GetTempBuffer(dev, __LINE__); + + /* Scan all the blocks to determine their state */ +@@ -6001,15 +5289,15 @@ + + if(bi->sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA) + bi->blockState = state = YAFFS_BLOCK_STATE_CHECKPOINT; +- ++ + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk, + state, sequenceNumber)); + +- ++ + if(state == YAFFS_BLOCK_STATE_CHECKPOINT){ +- dev->blocksInCheckpoint++; +- ++ /* todo .. fix free space ? */ ++ + } else if (state == YAFFS_BLOCK_STATE_DEAD) { + T(YAFFS_TRACE_BAD_BLOCKS, + (TSTR("block %d is bad" TENDSTR), blk)); +@@ -6053,12 +5341,14 @@ + + /* Sort the blocks */ + #ifndef CONFIG_YAFFS_USE_OWN_SORT +- yaffs_qsort(blockIndex, nBlocksToScan, +- sizeof(yaffs_BlockIndex), ybicmp); ++ { ++ /* Use qsort now. */ ++ qsort(blockIndex, nBlocksToScan, sizeof(yaffs_BlockIndex), ybicmp); ++ } + #else + { + /* Dungy old bubble sort... */ +- ++ + yaffs_BlockIndex temp; + int i; + int j; +@@ -6084,7 +5374,7 @@ + (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan)); + + /* For each block.... backwards */ +- for (blockIterator = endIterator; !alloc_failed && blockIterator >= startIterator; ++ for (blockIterator = endIterator; blockIterator >= startIterator; + blockIterator--) { + /* Cooperative multitasking! This loop can run for so + long that watchdog timers expire. */ +@@ -6094,22 +5384,18 @@ + blk = blockIndex[blockIterator].block; + + bi = yaffs_GetBlockInfo(dev, blk); +- +- + state = bi->blockState; + + deleted = 0; + + /* For each chunk in each block that needs scanning.... */ + foundChunksInBlock = 0; +- for (c = dev->nChunksPerBlock - 1; +- !alloc_failed && c >= 0 && ++ for (c = dev->nChunksPerBlock - 1; c >= 0 && + (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || + state == YAFFS_BLOCK_STATE_ALLOCATING); c--) { +- /* Scan backwards... ++ /* Scan backwards... + * Read the tags and decide what to do + */ +- + chunk = blk * dev->nChunksPerBlock + c; + + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, +@@ -6123,14 +5409,14 @@ + * it is a chunk that was skipped due to failing the erased + * check. Just skip it so that it can be deleted. + * But, more typically, We get here when this is an unallocated +- * chunk and his means that either the block is empty or ++ * chunk and his means that either the block is empty or + * this is the one being allocated from + */ + + if(foundChunksInBlock) + { + /* This is a chunk that was skipped due to failing the erased check */ +- ++ + } else if (c == 0) { + /* We're looking at the first chunk in the block so the block is unused */ + state = YAFFS_BLOCK_STATE_EMPTY; +@@ -6140,7 +5426,7 @@ + state == YAFFS_BLOCK_STATE_ALLOCATING) { + if(dev->sequenceNumber == bi->sequenceNumber) { + /* this is the block being allocated from */ +- ++ + T(YAFFS_TRACE_SCAN, + (TSTR + (" Allocating from %d %d" +@@ -6149,34 +5435,34 @@ + state = YAFFS_BLOCK_STATE_ALLOCATING; + dev->allocationBlock = blk; + dev->allocationPage = c; +- dev->allocationBlockFinder = blk; ++ dev->allocationBlockFinder = blk; + } + else { + /* This is a partially written block that is not + * the current allocation block. This block must have + * had a write failure, so set up for retirement. + */ +- ++ + bi->needsRetiring = 1; + bi->gcPrioritise = 1; +- ++ + T(YAFFS_TRACE_ALWAYS, + (TSTR("Partially written block %d being set for retirement" TENDSTR), + blk)); + } + + } +- ++ + } + + dev->nFreeChunks++; +- ++ + } else if (tags.chunkId > 0) { + /* chunkId > 0 so it is a data chunk... */ + unsigned int endpos; + __u32 chunkBase = + (tags.chunkId - 1) * dev->nDataBytesPerChunk; +- ++ + foundChunksInBlock = 1; + + +@@ -6187,29 +5473,21 @@ + tags. + objectId, + YAFFS_OBJECT_TYPE_FILE); +- if(!in){ +- /* Out of memory */ +- alloc_failed = 1; +- } +- +- if (in && +- in->variantType == YAFFS_OBJECT_TYPE_FILE ++ if (in->variantType == YAFFS_OBJECT_TYPE_FILE + && chunkBase < + in->variant.fileVariant.shrinkSize) { + /* This has not been invalidated by a resize */ +- if(!yaffs_PutChunkIntoFile(in, tags.chunkId, +- chunk, -1)){ +- alloc_failed = 1; +- } ++ yaffs_PutChunkIntoFile(in, tags.chunkId, ++ chunk, -1); + +- /* File size is calculated by looking at the data chunks if we have not ++ /* File size is calculated by looking at the data chunks if we have not + * seen an object header yet. Stop this practice once we find an object header. + */ + endpos = + (tags.chunkId - + 1) * dev->nDataBytesPerChunk + + tags.byteCount; +- ++ + if (!in->valid && /* have not got an object header yet */ + in->variant.fileVariant. + scannedFileSize < endpos) { +@@ -6221,7 +5499,7 @@ + scannedFileSize; + } + +- } else if(in) { ++ } else { + /* This chunk has been invalidated by a resize, so delete */ + yaffs_DeleteChunk(dev, chunk, 1, __LINE__); + +@@ -6255,7 +5533,7 @@ + ) { + + /* If we don't have valid info then we need to read the chunk +- * TODO In future we can probably defer reading the chunk and ++ * TODO In future we can probably defer reading the chunk and + * living with invalid data until needed. + */ + +@@ -6283,12 +5561,12 @@ + + if (in->valid) { + /* We have already filled this one. +- * We have a duplicate that will be discarded, but ++ * We have a duplicate that will be discarded, but + * we first have to suck out resize info if it is a file. + */ + +- if ((in->variantType == YAFFS_OBJECT_TYPE_FILE) && +- ((oh && ++ if ((in->variantType == YAFFS_OBJECT_TYPE_FILE) && ++ ((oh && + oh-> type == YAFFS_OBJECT_TYPE_FILE)|| + (tags.extraHeaderInfoAvailable && + tags.extraObjectType == YAFFS_OBJECT_TYPE_FILE)) +@@ -6339,7 +5617,7 @@ + YAFFS_OBJECTID_LOSTNFOUND)) { + /* We only load some info, don't fiddle with directory structure */ + in->valid = 1; +- ++ + if(oh) { + in->variantType = oh->type; + +@@ -6358,13 +5636,13 @@ + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; +- ++ + #endif + } else { + in->variantType = tags.extraObjectType; + in->lazyLoaded = 1; + } +- ++ + in->chunkId = chunk; + + } else if (!in->valid) { +@@ -6372,7 +5650,7 @@ + + in->valid = 1; + in->chunkId = chunk; +- ++ + if(oh) { + in->variantType = oh->type; + +@@ -6393,12 +5671,12 @@ + in->yst_rdev = oh->yst_rdev; + #endif + +- if (oh->shadowsObject > 0) ++ if (oh->shadowsObject > 0) + yaffs_HandleShadowedObject(dev, + oh-> + shadowsObject, + 1); +- ++ + + yaffs_SetObjectName(in, oh->name); + parent = +@@ -6466,11 +5744,11 @@ + * Since we might scan a hardlink before its equivalent object is scanned + * we put them all in a list. + * After scanning is complete, we should have all the objects, so we run +- * through this list and fix up all the chains. ++ * through this list and fix up all the chains. + */ + + switch (in->variantType) { +- case YAFFS_OBJECT_TYPE_UNKNOWN: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: + /* Todo got a problem */ + break; + case YAFFS_OBJECT_TYPE_FILE: +@@ -6479,7 +5757,7 @@ + scannedFileSize < fileSize) { + /* This covers the case where the file size is greater + * than where the data is +- * This will happen if the file is resized to be larger ++ * This will happen if the file is resized to be larger + * than its current data extents. + */ + in->variant.fileVariant.fileSize = fileSize; +@@ -6509,21 +5787,16 @@ + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SYMLINK: +- if(oh){ ++ if(oh) + in->variant.symLinkVariant.alias = + yaffs_CloneString(oh-> + alias); +- if(!in->variant.symLinkVariant.alias) +- alloc_failed = 1; +- } + break; + } + + } +- + } +- +- } /* End of scanning for each chunk */ ++ } + + if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + /* If we got this far while scanning, then the block is fully allocated. */ +@@ -6541,19 +5814,19 @@ + + } + +- if (altBlockIndex) ++ if (altBlockIndex) + YFREE_ALT(blockIndex); + else + YFREE(blockIndex); +- ++ + /* Ok, we've done all the scanning. + * Fix up the hard link chains. +- * We should now have scanned all the objects, now it's time to add these ++ * We should now have scanned all the objects, now it's time to add these + * hardlinks. + */ + yaffs_HardlinkFixup(dev,hardList); +- +- ++ ++ + /* + * Sort out state of unlinked and deleted objects. + */ +@@ -6587,10 +5860,6 @@ + + yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); + +- if(alloc_failed){ +- return YAFFS_FAIL; +- } +- + T(YAFFS_TRACE_SCAN, (TSTR("yaffs_ScanBackwards ends" TENDSTR))); + + return YAFFS_OK; +@@ -6601,10 +5870,10 @@ + static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj) + { + yaffs_Device *dev = obj->myDev; +- ++ + if(dev && dev->removeObjectCallback) + dev->removeObjectCallback(obj); +- ++ + list_del_init(&obj->siblings); + obj->parent = NULL; + } +@@ -6682,7 +5951,7 @@ + list_for_each(i, &directory->variant.directoryVariant.children) { + if (i) { + l = list_entry(i, yaffs_Object, siblings); +- ++ + yaffs_CheckObjectDetailsLoaded(l); + + /* Special case for lost-n-found */ +@@ -6690,14 +5959,14 @@ + if (yaffs_strcmp(name, YAFFS_LOSTNFOUND_NAME) == 0) { + return l; + } +- } else if (yaffs_SumCompare(l->sum, sum) || l->chunkId <= 0) ++ } else if (yaffs_SumCompare(l->sum, sum) || l->chunkId <= 0) + { + /* LostnFound cunk called Objxxx + * Do a real check + */ + yaffs_GetObjectName(l, buffer, + YAFFS_MAX_NAME_LENGTH); +- if (yaffs_strncmp(name, buffer,YAFFS_MAX_NAME_LENGTH) == 0) { ++ if (yaffs_strcmp(name, buffer) == 0) { + return l; + } + +@@ -6753,7 +6022,6 @@ + if (obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { + /* We want the object id of the equivalent object, not this one */ + obj = obj->variant.hardLinkVariant.equivalentObject; +- yaffs_CheckObjectDetailsLoaded(obj); + } + return obj; + +@@ -6762,7 +6030,7 @@ + int yaffs_GetObjectName(yaffs_Object * obj, YCHAR * name, int buffSize) + { + memset(name, 0, buffSize * sizeof(YCHAR)); +- ++ + yaffs_CheckObjectDetailsLoaded(obj); + + if (obj->objectId == YAFFS_OBJECTID_LOSTNFOUND) { +@@ -6991,16 +6259,15 @@ + } + + +-static int yaffs_CreateInitialDirectories(yaffs_Device *dev) ++static void yaffs_CreateInitialDirectories(yaffs_Device *dev) + { + /* Initialise the unlinked, deleted, root and lost and found directories */ +- ++ + dev->lostNFoundDir = dev->rootDir = NULL; + dev->unlinkedDir = dev->deletedDir = NULL; + + dev->unlinkedDir = + yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); +- + dev->deletedDir = + yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); + +@@ -7010,18 +6277,11 @@ + dev->lostNFoundDir = + yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_LOSTNFOUND, + YAFFS_LOSTNFOUND_MODE | S_IFDIR); +- +- if(dev->lostNFoundDir && dev->rootDir && dev->unlinkedDir && dev->deletedDir){ +- yaffs_AddObjectToDirectory(dev->rootDir, dev->lostNFoundDir); +- return YAFFS_OK; +- } +- +- return YAFFS_FAIL; ++ yaffs_AddObjectToDirectory(dev->rootDir, dev->lostNFoundDir); + } + + int yaffs_GutsInitialise(yaffs_Device * dev) + { +- int init_failed = 0; + unsigned x; + int bits; + +@@ -7049,12 +6309,12 @@ + + /* Check geometry parameters. */ + +- if ((dev->isYaffs2 && dev->nDataBytesPerChunk < 1024) || +- (!dev->isYaffs2 && dev->nDataBytesPerChunk != 512) || +- dev->nChunksPerBlock < 2 || +- dev->nReservedBlocks < 2 || +- dev->internalStartBlock <= 0 || +- dev->internalEndBlock <= 0 || ++ if ((dev->isYaffs2 && dev->nDataBytesPerChunk < 1024) || ++ (!dev->isYaffs2 && dev->nDataBytesPerChunk != 512) || ++ dev->nChunksPerBlock < 2 || ++ dev->nReservedBlocks < 2 || ++ dev->internalStartBlock <= 0 || ++ dev->internalEndBlock <= 0 || + dev->internalEndBlock <= (dev->internalStartBlock + dev->nReservedBlocks + 2) // otherwise it is too small + ) { + T(YAFFS_TRACE_ALWAYS, +@@ -7100,9 +6360,9 @@ + + + /* OK now calculate a few things for the device */ +- ++ + /* +- * Calculate all the chunk size manipulation numbers: ++ * Calculate all the chunk size manipulation numbers: + */ + /* Start off assuming it is a power of 2 */ + dev->chunkShift = ShiftDiv(dev->nDataBytesPerChunk); +@@ -7121,7 +6381,7 @@ + dev->chunkShift = 0; + dev->chunkMask = 0; + } +- ++ + + /* + * Calculate chunkGroupBits. +@@ -7129,9 +6389,9 @@ + */ + + x = dev->nChunksPerBlock * (dev->internalEndBlock + 1); +- ++ + bits = ShiftsGE(x); +- ++ + /* Set up tnode width if wide tnodes are enabled. */ + if(!dev->wideTnodesDisabled){ + /* bits must be even so that we end up with 32-bit words */ +@@ -7144,20 +6404,20 @@ + } + else + dev->tnodeWidth = 16; +- ++ + dev->tnodeMask = (1<tnodeWidth)-1; +- ++ + /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), + * so if the bitwidth of the + * chunk range we're using is greater than 16 we need + * to figure out chunk shift and chunkGroupSize + */ +- ++ + if (bits <= dev->tnodeWidth) + dev->chunkGroupBits = 0; + else + dev->chunkGroupBits = bits - dev->tnodeWidth; +- ++ + + dev->chunkGroupSize = 1 << dev->chunkGroupBits; + +@@ -7193,111 +6453,71 @@ + dev->hasPendingPrioritisedGCs = 1; /* Assume the worst for now, will get fixed on first GC */ + + /* Initialise temporary buffers and caches. */ +- if(!yaffs_InitialiseTempBuffers(dev)) +- init_failed = 1; +- +- dev->srCache = NULL; +- dev->gcCleanupList = NULL; +- +- +- if (!init_failed && +- dev->nShortOpCaches > 0) { ++ { ++ int i; ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ dev->tempBuffer[i].line = 0; /* not in use */ ++ dev->tempBuffer[i].buffer = ++ YMALLOC_DMA(dev->nDataBytesPerChunk); ++ } ++ } ++ ++ if (dev->nShortOpCaches > 0) { + int i; +- __u8 *buf; +- int srCacheBytes = dev->nShortOpCaches * sizeof(yaffs_ChunkCache); + + if (dev->nShortOpCaches > YAFFS_MAX_SHORT_OP_CACHES) { + dev->nShortOpCaches = YAFFS_MAX_SHORT_OP_CACHES; + } + +- buf = dev->srCache = YMALLOC(srCacheBytes); ++ dev->srCache = ++ YMALLOC(dev->nShortOpCaches * sizeof(yaffs_ChunkCache)); + +- if(dev->srCache) +- memset(dev->srCache,0,srCacheBytes); +- +- for (i = 0; i < dev->nShortOpCaches && buf; i++) { ++ for (i = 0; i < dev->nShortOpCaches; i++) { + dev->srCache[i].object = NULL; + dev->srCache[i].lastUse = 0; + dev->srCache[i].dirty = 0; +- dev->srCache[i].data = buf = YMALLOC_DMA(dev->nDataBytesPerChunk); ++ dev->srCache[i].data = YMALLOC_DMA(dev->nDataBytesPerChunk); + } +- if(!buf) +- init_failed = 1; +- + dev->srLastUse = 0; + } + + dev->cacheHits = 0; +- +- if(!init_failed){ +- dev->gcCleanupList = YMALLOC(dev->nChunksPerBlock * sizeof(__u32)); +- if(!dev->gcCleanupList) +- init_failed = 1; +- } ++ ++ dev->gcCleanupList = YMALLOC(dev->nChunksPerBlock * sizeof(__u32)); + + if (dev->isYaffs2) { + dev->useHeaderFileSize = 1; + } +- if(!init_failed && !yaffs_InitialiseBlocks(dev)) +- init_failed = 1; + ++ yaffs_InitialiseBlocks(dev); + yaffs_InitialiseTnodes(dev); + yaffs_InitialiseObjects(dev); + +- if(!init_failed && !yaffs_CreateInitialDirectories(dev)) +- init_failed = 1; +- +- +- if(!init_failed){ +- /* Now scan the flash. */ +- if (dev->isYaffs2) { +- if(yaffs_CheckpointRestore(dev)) { +- T(YAFFS_TRACE_ALWAYS, +- (TSTR("yaffs: restored from checkpoint" TENDSTR))); +- } else { +- +- /* Clean up the mess caused by an aborted checkpoint load +- * and scan backwards. +- */ +- yaffs_DeinitialiseBlocks(dev); +- yaffs_DeinitialiseTnodes(dev); +- yaffs_DeinitialiseObjects(dev); +- +- +- dev->nErasedBlocks = 0; +- dev->nFreeChunks = 0; +- dev->allocationBlock = -1; +- dev->allocationPage = -1; +- dev->nDeletedFiles = 0; +- dev->nUnlinkedFiles = 0; +- dev->nBackgroundDeletions = 0; +- dev->oldestDirtySequence = 0; ++ yaffs_CreateInitialDirectories(dev); + +- if(!init_failed && !yaffs_InitialiseBlocks(dev)) +- init_failed = 1; + +- yaffs_InitialiseTnodes(dev); +- yaffs_InitialiseObjects(dev); +- +- if(!init_failed && !yaffs_CreateInitialDirectories(dev)) +- init_failed = 1; +- +- if(!init_failed && !yaffs_ScanBackwards(dev)) +- init_failed = 1; +- } +- }else +- if(!yaffs_Scan(dev)) +- init_failed = 1; +- } ++ /* Now scan the flash. */ ++ if (dev->isYaffs2) { ++ if(yaffs_CheckpointRestore(dev)) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR("yaffs: restored from checkpoint" TENDSTR))); ++ } else { + +- if(init_failed){ +- /* Clean up the mess */ +- T(YAFFS_TRACE_TRACING, +- (TSTR("yaffs: yaffs_GutsInitialise() aborted.\n" TENDSTR))); ++ /* Clean up the mess caused by an aborted checkpoint load ++ * and scan backwards. ++ */ ++ yaffs_DeinitialiseBlocks(dev); ++ yaffs_DeinitialiseTnodes(dev); ++ yaffs_DeinitialiseObjects(dev); ++ yaffs_InitialiseBlocks(dev); ++ yaffs_InitialiseTnodes(dev); ++ yaffs_InitialiseObjects(dev); ++ yaffs_CreateInitialDirectories(dev); + +- yaffs_Deinitialise(dev); +- return YAFFS_FAIL; +- } ++ yaffs_ScanBackwards(dev); ++ } ++ }else ++ yaffs_Scan(dev); + + /* Zero out stats */ + dev->nPageReads = 0; +@@ -7309,9 +6529,6 @@ + dev->nRetiredBlocks = 0; + + yaffs_VerifyFreeChunks(dev); +- yaffs_VerifyBlocks(dev); +- +- + T(YAFFS_TRACE_TRACING, + (TSTR("yaffs: yaffs_GutsInitialise() done.\n" TENDSTR))); + return YAFFS_OK; +@@ -7326,17 +6543,13 @@ + yaffs_DeinitialiseBlocks(dev); + yaffs_DeinitialiseTnodes(dev); + yaffs_DeinitialiseObjects(dev); +- if (dev->nShortOpCaches > 0 && +- dev->srCache) { ++ if (dev->nShortOpCaches > 0) { + + for (i = 0; i < dev->nShortOpCaches; i++) { +- if(dev->srCache[i].data) +- YFREE(dev->srCache[i].data); +- dev->srCache[i].data = NULL; ++ YFREE(dev->srCache[i].data); + } + + YFREE(dev->srCache); +- dev->srCache = NULL; + } + + YFREE(dev->gcCleanupList); +@@ -7394,7 +6607,7 @@ + #endif + + nFree += dev->nDeletedFiles; +- ++ + /* Now count the number of dirty chunks in the cache and subtract those */ + + { +@@ -7408,12 +6621,12 @@ + nFree -= nDirtyCacheChunks; + + nFree -= ((dev->nReservedBlocks + 1) * dev->nChunksPerBlock); +- ++ + /* Now we figure out how much to reserve for the checkpoint and report that... */ + blocksForCheckpoint = dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint; + if(blocksForCheckpoint < 0) + blocksForCheckpoint = 0; +- ++ + nFree -= (blocksForCheckpoint * dev->nChunksPerBlock); + + if (nFree < 0) +@@ -7427,15 +6640,9 @@ + + static void yaffs_VerifyFreeChunks(yaffs_Device * dev) + { +- int counted; +- int difference; +- +- if(yaffs_SkipVerification(dev)) +- return; +- +- counted = yaffs_CountFreeChunks(dev); ++ int counted = yaffs_CountFreeChunks(dev); + +- difference = dev->nFreeChunks - counted; ++ int difference = dev->nFreeChunks - counted; + + if (difference) { + T(YAFFS_TRACE_ALWAYS, +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_guts.h linux-2.6.24.7.new/fs/yaffs2/yaffs_guts.h +--- linux-2.6.24.7/fs/yaffs2/yaffs_guts.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_guts.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +@@ -22,11 +22,11 @@ + #define YAFFS_OK 1 + #define YAFFS_FAIL 0 + +-/* Give us a Y=0x59, +- * Give us an A=0x41, +- * Give us an FF=0xFF ++/* Give us a Y=0x59, ++ * Give us an A=0x41, ++ * Give us an FF=0xFF + * Give us an S=0x53 +- * And what have we got... ++ * And what have we got... + */ + #define YAFFS_MAGIC 0x5941FF53 + +@@ -63,7 +63,9 @@ + + #define YAFFS_OBJECT_SPACE 0x40000 + +-#define YAFFS_CHECKPOINT_VERSION 3 ++#define YAFFS_NCHECKPOINT_OBJECTS 5000 ++ ++#define YAFFS_CHECKPOINT_VERSION 2 + + #ifdef CONFIG_YAFFS_UNICODE + #define YAFFS_MAX_NAME_LENGTH 127 +@@ -92,17 +94,11 @@ + + #define YAFFS_N_TEMP_BUFFERS 4 + +-/* We limit the number attempts at sucessfully saving a chunk of data. +- * Small-page devices have 32 pages per block; large-page devices have 64. +- * Default to something in the order of 5 to 10 blocks worth of chunks. +- */ +-#define YAFFS_WR_ATTEMPTS (5*64) +- + /* Sequence numbers are used in YAFFS2 to determine block allocation order. + * The range is limited slightly to help distinguish bad numbers from good. + * This also allows us to perhaps in the future use special numbers for + * special purposes. +- * EFFFFF00 allows the allocation of 8 blocks per second (~1Mbytes) for 15 years, ++ * EFFFFF00 allows the allocation of 8 blocks per second (~1Mbytes) for 15 years, + * and is a larger number than the lifetime of a 2GB device. + */ + #define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 +@@ -166,8 +162,6 @@ + YAFFS_OBJECT_TYPE_SPECIAL + } yaffs_ObjectType; + +-#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL +- + typedef struct { + + unsigned validMarker0; +@@ -178,7 +172,7 @@ + + /* The following stuff only has meaning when we read */ + yaffs_ECCResult eccResult; +- unsigned blockBad; ++ unsigned blockBad; + + /* YAFFS 1 stuff */ + unsigned chunkDeleted; /* The chunk is marked deleted */ +@@ -244,35 +238,32 @@ + /* This block is empty */ + + YAFFS_BLOCK_STATE_ALLOCATING, +- /* This block is partially allocated. ++ /* This block is partially allocated. + * At least one page holds valid data. + * This is the one currently being used for page + * allocation. Should never be more than one of these + */ + +- YAFFS_BLOCK_STATE_FULL, ++ YAFFS_BLOCK_STATE_FULL, + /* All the pages in this block have been allocated. + */ + + YAFFS_BLOCK_STATE_DIRTY, +- /* All pages have been allocated and deleted. ++ /* All pages have been allocated and deleted. + * Erase me, reuse me. + */ + +- YAFFS_BLOCK_STATE_CHECKPOINT, ++ YAFFS_BLOCK_STATE_CHECKPOINT, + /* This block is assigned to holding checkpoint data. + */ + +- YAFFS_BLOCK_STATE_COLLECTING, ++ YAFFS_BLOCK_STATE_COLLECTING, + /* This block is being garbage collected */ + +- YAFFS_BLOCK_STATE_DEAD ++ YAFFS_BLOCK_STATE_DEAD + /* This block has failed and is not in use */ + } yaffs_BlockState; + +-#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) +- +- + typedef struct { + + int softDeletions:10; /* number of soft deleted pages */ +@@ -281,7 +272,7 @@ + __u32 needsRetiring:1; /* Data has failed on this block, need to get valid data off */ + /* and retire the block. */ + __u32 skipErasedCheck: 1; /* If this is set we can skip the erased check on this block */ +- __u32 gcPrioritise: 1; /* An ECC check or blank check has failed on this block. ++ __u32 gcPrioritise: 1; /* An ECC check or bank check has failed on this block. + It should be prioritised for GC */ + __u32 chunkErrorStrikes:3; /* How many times we've had ecc etc failures on this block and tried to reuse it */ + +@@ -408,7 +399,7 @@ + __u8 renameAllowed:1; /* Some objects are not allowed to be renamed. */ + __u8 unlinkAllowed:1; + __u8 dirty:1; /* the object needs to be written to flash */ +- __u8 valid:1; /* When the file system is being loaded up, this ++ __u8 valid:1; /* When the file system is being loaded up, this + * object might be created before the data + * is available (ie. file data records appear before the header). + */ +@@ -430,11 +421,11 @@ + + /* directory structure stuff */ + /* also used for linking up the free list */ +- struct yaffs_ObjectStruct *parent; ++ struct yaffs_ObjectStruct *parent; + struct list_head siblings; + + /* Where's my object header in NAND? */ +- int chunkId; ++ int chunkId; + + int nDataChunks; /* Number of data chunks attached to the file. */ + +@@ -490,26 +481,26 @@ + } yaffs_ObjectBucket; + + +-/* yaffs_CheckpointObject holds the definition of an object as dumped ++/* yaffs_CheckpointObject holds the definition of an object as dumped + * by checkpointing. + */ + + typedef struct { + int structType; +- __u32 objectId; ++ __u32 objectId; + __u32 parentId; + int chunkId; +- ++ + yaffs_ObjectType variantType:3; +- __u8 deleted:1; +- __u8 softDeleted:1; +- __u8 unlinked:1; +- __u8 fake:1; ++ __u8 deleted:1; ++ __u8 softDeleted:1; ++ __u8 unlinked:1; ++ __u8 fake:1; + __u8 renameAllowed:1; + __u8 unlinkAllowed:1; +- __u8 serial; +- +- int nDataChunks; ++ __u8 serial; ++ ++ int nDataChunks; + __u32 fileSizeOrEquivalentObjectId; + + }yaffs_CheckpointObject; +@@ -539,14 +530,17 @@ + int endBlock; /* End block we're allowed to use */ + int nReservedBlocks; /* We want this tuneable so that we can reduce */ + /* reserved blocks on NOR and RAM. */ +- +- ++ ++ /* Stuff used by the partitioned checkpointing mechanism */ ++ int checkpointStartBlock; ++ int checkpointEndBlock; ++ + /* Stuff used by the shared space checkpointing mechanism */ + /* If this value is zero, then this mechanism is disabled */ +- ++ + int nCheckpointReservedBlocks; /* Blocks to reserve for checkpoint data */ + +- ++ + + + int nShortOpCaches; /* If <= 0, then short op caching is disabled, else +@@ -561,7 +555,7 @@ + * On an mtd this holds the mtd pointer. + */ + void *superBlock; +- ++ + /* NAND access functions (Must be set before calling YAFFS)*/ + + int (*writeChunkToNAND) (struct yaffs_DeviceStruct * dev, +@@ -587,57 +581,53 @@ + #endif + + int isYaffs2; +- +- /* The removeObjectCallback function must be supplied by OS flavours that ++ ++ /* The removeObjectCallback function must be supplied by OS flavours that + * need it. The Linux kernel does not use this, but yaffs direct does use + * it to implement the faster readdir + */ + void (*removeObjectCallback)(struct yaffs_ObjectStruct *obj); +- ++ + /* Callback to mark the superblock dirsty */ + void (*markSuperBlockDirty)(void * superblock); +- ++ + int wideTnodesDisabled; /* Set to disable wide tnodes */ +- ++ + + /* End of stuff that must be set before initialisation. */ + +- /* Checkpoint control. Can be set before or after initialisation */ +- __u8 skipCheckpointRead; +- __u8 skipCheckpointWrite; +- + /* Runtime parameters. Set up by YAFFS. */ + + __u16 chunkGroupBits; /* 0 for devices <= 32MB. else log2(nchunks) - 16 */ + __u16 chunkGroupSize; /* == 2^^chunkGroupBits */ +- ++ + /* Stuff to support wide tnodes */ + __u32 tnodeWidth; + __u32 tnodeMask; +- ++ + /* Stuff to support various file offses to chunk/offset translations */ + /* "Crumbs" for nDataBytesPerChunk not being a power of 2 */ + __u32 crumbMask; + __u32 crumbShift; + __u32 crumbsPerChunk; +- ++ + /* Straight shifting for nDataBytesPerChunk being a power of 2 */ + __u32 chunkShift; + __u32 chunkMask; +- ++ + + #ifdef __KERNEL__ + + struct semaphore sem; /* Semaphore for waiting on erasure.*/ + struct semaphore grossLock; /* Gross locking semaphore */ +- __u8 *spareBuffer; /* For mtdif2 use. Don't know the size of the buffer ++ __u8 *spareBuffer; /* For mtdif2 use. Don't know the size of the buffer + * at compile time so we have to allocate it. + */ + void (*putSuperFunc) (struct super_block * sb); + #endif + + int isMounted; +- ++ + int isCheckpointed; + + +@@ -646,7 +636,7 @@ + int internalEndBlock; + int blockOffset; + int chunkOffset; +- ++ + + /* Runtime checkpointing stuff */ + int checkpointPageSequence; /* running sequence number of checkpoint pages */ +@@ -660,15 +650,13 @@ + int checkpointNextBlock; + int *checkpointBlockList; + int checkpointMaxBlocks; +- __u32 checkpointSum; +- __u32 checkpointXor; +- ++ + /* Block Info */ + yaffs_BlockInfo *blockInfo; + __u8 *chunkBits; /* bitmap of chunks in use */ + unsigned blockInfoAlt:1; /* was allocated using alternative strategy */ + unsigned chunkBitsAlt:1; /* was allocated using alternative strategy */ +- int chunkBitmapStride; /* Number of bytes of chunkBits per block. ++ int chunkBitmapStride; /* Number of bytes of chunkBits per block. + * Must be consistent with nChunksPerBlock. + */ + +@@ -698,7 +686,6 @@ + int currentDirtyChecker; /* Used to find current dirtiest block */ + + __u32 *gcCleanupList; /* objects to delete at the end of a GC. */ +- int nonAggressiveSkip; /* GC state/mode */ + + /* Statistcs */ + int nPageWrites; +@@ -716,7 +703,7 @@ + int tagsEccUnfixed; + int nDeletions; + int nUnmarkedDeletions; +- ++ + int hasPendingPrioritisedGCs; /* We think this device might have pending prioritised gcs */ + + /* Special directories */ +@@ -727,7 +714,7 @@ + * __u8 bufferedData[YAFFS_CHUNKS_PER_BLOCK][YAFFS_BYTES_PER_CHUNK]; + * yaffs_Spare bufferedSpare[YAFFS_CHUNKS_PER_BLOCK]; + */ +- ++ + int bufferedBlock; /* Which block is buffered here? */ + int doingBufferedBlockRewrite; + +@@ -768,7 +755,7 @@ + int endBlock; + int rfu[100]; + } yaffs_SuperBlockHeader; +- ++ + /* The CheckpointDevice structure holds the device information that changes at runtime and + * must be preserved over unmount/mount cycles. + */ +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_mtdif.c linux-2.6.24.7.new/fs/yaffs2/yaffs_mtdif.c +--- linux-2.6.24.7/fs/yaffs2/yaffs_mtdif.c 2009-04-21 14:57:10.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_mtdif.c 2009-04-21 15:01:02.000000000 +0200 +@@ -12,13 +12,10 @@ + */ + + const char *yaffs_mtdif_c_version = +- "$Id: yaffs_mtdif.c,v 1.19 2007-02-14 01:09:06 wookey Exp $"; ++ "$Id: yaffs_mtdif.c,v 1.1.1.1 2008-03-28 04:29:21 jlwei Exp $"; + + #include "yportenv.h" +- +- + #include "yaffs_mtdif.h" +- + #include "linux/mtd/mtd.h" + #include "linux/types.h" + #include "linux/time.h" +@@ -54,6 +51,7 @@ + static inline void translate_oob2spare(yaffs_Spare *spare, __u8 *oob) + { + struct yaffs_NANDSpare *nspare = (struct yaffs_NANDSpare *)spare; ++ + spare->tagByte0 = oob[0]; + spare->tagByte1 = oob[1]; + spare->tagByte2 = oob[2]; +@@ -68,6 +66,11 @@ + spare->ecc2[0] = spare->ecc2[1] = spare->ecc2[2] = 0xff; + + nspare->eccres1 = nspare->eccres2 = 0; /* FIXME */ ++#if 0 ++ char i; ++ for (i=0;i<8;i++) ++ printk("oob %d :%x",i,oob[i]); ++#endif + } + #endif + +@@ -78,10 +81,11 @@ + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + struct mtd_oob_ops ops; + #endif +- size_t dummy; ++ size_mtd_t dummy; + int retval = 0; + +- loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; ++ loff_mtd_t addr = ((loff_mtd_t) chunkInNAND) * dev->nDataBytesPerChunk; ++ + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + __u8 spareAsBytes[8]; /* OOB */ + +@@ -142,10 +146,11 @@ + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + struct mtd_oob_ops ops; + #endif +- size_t dummy; ++ size_mtd_t dummy; + int retval = 0; + +- loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; ++ loff_mtd_t addr = ((loff_mtd_t) chunkInNAND) * dev->nDataBytesPerChunk; ++ + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + __u8 spareAsBytes[8]; /* OOB */ + +@@ -164,7 +169,7 @@ + ops.datbuf = data; + ops.ooboffs = 0; + ops.oobbuf = spareAsBytes; +- retval = mtd->read_oob(mtd, addr, &ops); ++ retval = mtd->read_oob(mtd, addr, &ops); + if (dev->useNANDECC) + translate_oob2spare(spare, spareAsBytes); + } +@@ -172,7 +177,7 @@ + __u8 *spareAsBytes = (__u8 *) spare; + + if (data && spare) { +- if (dev->useNANDECC) { ++ if (dev->useNANDECC) { + /* Careful, this call adds 2 ints */ + /* to the end of the spare data. Calling function */ + /* should allocate enough memory for spare, */ +@@ -208,8 +213,8 @@ + int nandmtd_EraseBlockInNAND(yaffs_Device * dev, int blockNumber) + { + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); +- __u32 addr = +- ((loff_t) blockNumber) * dev->nDataBytesPerChunk ++ __u64 addr = ++ ((loff_mtd_t) blockNumber) * dev->nDataBytesPerChunk + * dev->nChunksPerBlock; + struct erase_info ei; + int retval = 0; +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_mtdif.h linux-2.6.24.7.new/fs/yaffs2/yaffs_mtdif.h +--- linux-2.6.24.7/fs/yaffs2/yaffs_mtdif.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_mtdif.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_mtdif1-compat.c linux-2.6.24.7.new/fs/yaffs2/yaffs_mtdif1-compat.c +--- linux-2.6.24.7/fs/yaffs2/yaffs_mtdif1-compat.c 2009-04-21 14:57:10.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_mtdif1-compat.c 1970-01-01 01:00:00.000000000 +0100 +@@ -1,434 +0,0 @@ +-From ian@brightstareng.com Fri May 18 15:06:49 2007 +-From ian@brightstareng.com Fri May 18 15:08:21 2007 +-Received: from 206.173.66.57.ptr.us.xo.net ([206.173.66.57] helo=zebra.brightstareng.com) +- by apollo.linkchoose.co.uk with esmtp (Exim 4.60) +- (envelope-from ) +- id 1Hp380-00011e-T6 +- for david.goodenough@linkchoose.co.uk; Fri, 18 May 2007 15:08:21 +0100 +-Received: from localhost (localhost.localdomain [127.0.0.1]) +- by zebra.brightstareng.com (Postfix) with ESMTP +- id 4819F28C004; Fri, 18 May 2007 10:07:49 -0400 (EDT) +-Received: from zebra.brightstareng.com ([127.0.0.1]) +- by localhost (zebra [127.0.0.1]) (amavisd-new, port 10024) with ESMTP +- id 05328-06; Fri, 18 May 2007 10:07:16 -0400 (EDT) +-Received: from pippin (unknown [192.168.1.25]) +- by zebra.brightstareng.com (Postfix) with ESMTP +- id 8BEF528C1BC; Fri, 18 May 2007 10:06:53 -0400 (EDT) +-From: Ian McDonnell +-To: David Goodenough +-Subject: Re: something tested this time -- yaffs_mtdif1-compat.c +-Date: Fri, 18 May 2007 10:06:49 -0400 +-User-Agent: KMail/1.9.1 +-References: <200705142207.06909.ian@brightstareng.com> <200705171131.53536.ian@brightstareng.com> <200705181334.32166.david.goodenough@linkchoose.co.uk> +-In-Reply-To: <200705181334.32166.david.goodenough@linkchoose.co.uk> +-Cc: Andrea Conti , +- Charles Manning +-MIME-Version: 1.0 +-Content-Type: Multipart/Mixed; +- boundary="Boundary-00=_5LbTGmt62YoutxM" +-Message-Id: <200705181006.49860.ian@brightstareng.com> +-X-Virus-Scanned: by amavisd-new at brightstareng.com +-Status: R +-X-Status: NT +-X-KMail-EncryptionState: +-X-KMail-SignatureState: +-X-KMail-MDN-Sent: +- +---Boundary-00=_5LbTGmt62YoutxM +-Content-Type: text/plain; +- charset="iso-8859-15" +-Content-Transfer-Encoding: 7bit +-Content-Disposition: inline +- +-David, Andrea, +- +-On Friday 18 May 2007 08:34, you wrote: +-> Yea team. With this fix in place (I put it in the wrong place +-> at first) I can now mount and ls the Yaffs partition without +-> an error messages! +- +-Good news! +- +-Attached is a newer yaffs_mtdif1.c with a bandaid to help the +-2.6.18 and 2.6.19 versions of MTD not trip on the oob read. +-See the LINUX_VERSION_CODE conditional in +-nandmtd1_ReadChunkWithTagsFromNAND. +- +--imcd +- +---Boundary-00=_5LbTGmt62YoutxM +-Content-Type: text/x-csrc; +- charset="iso-8859-15"; +- name="yaffs_mtdif1.c" +-Content-Transfer-Encoding: 7bit +-Content-Disposition: attachment; +- filename="yaffs_mtdif1.c" +- +-/* +- * YAFFS: Yet another FFS. A NAND-flash specific file system. +- * yaffs_mtdif1.c NAND mtd interface functions for small-page NAND. +- * +- * Copyright (C) 2002 Aleph One Ltd. +- * for Toby Churchill Ltd and Brightstar Engineering +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-/* +- * This module provides the interface between yaffs_nand.c and the +- * MTD API. This version is used when the MTD interface supports the +- * 'mtd_oob_ops' style calls to read_oob and write_oob, circa 2.6.17, +- * and we have small-page NAND device. +- * +- * These functions are invoked via function pointers in yaffs_nand.c. +- * This replaces functionality provided by functions in yaffs_mtdif.c +- * and the yaffs_TagsCompatability functions in yaffs_tagscompat.c that are +- * called in yaffs_mtdif.c when the function pointers are NULL. +- * We assume the MTD layer is performing ECC (useNANDECC is true). +- */ +- +-#include "yportenv.h" +-#include "yaffs_guts.h" +-#include "yaffs_packedtags1.h" +-#include "yaffs_tagscompat.h" // for yaffs_CalcTagsECC +- +-#include "linux/kernel.h" +-#include "linux/version.h" +-#include "linux/types.h" +-#include "linux/mtd/mtd.h" +- +-/* Don't compile this module if we don't have MTD's mtd_oob_ops interface */ +-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +- +-const char *yaffs_mtdif1_c_version = "$Id$"; +- +-#ifndef CONFIG_YAFFS_9BYTE_TAGS +-# define YTAG1_SIZE 8 +-#else +-# define YTAG1_SIZE 9 +-#endif +- +-#if 0 +-/* Use the following nand_ecclayout with MTD when using +- * CONFIG_YAFFS_9BYTE_TAGS and the older on-NAND tags layout. +- * If you have existing Yaffs images and the byte order differs from this, +- * adjust 'oobfree' to match your existing Yaffs data. +- * +- * This nand_ecclayout scatters/gathers to/from the old-yaffs layout with the +- * pageStatus byte (at NAND spare offset 4) scattered/gathered from/to +- * the 9th byte. +- * +- * Old-style on-NAND format: T0,T1,T2,T3,P,B,T4,T5,E0,E1,E2,T6,T7,E3,E4,E5 +- * We have/need PackedTags1 plus pageStatus: T0,T1,T2,T3,T4,T5,T6,T7,P +- * where Tn are the tag bytes, En are MTD's ECC bytes, P is the pageStatus +- * byte and B is the small-page bad-block indicator byte. +- */ +-static struct nand_ecclayout nand_oob_16 = { +- .eccbytes = 6, +- .eccpos = { 8, 9, 10, 13, 14, 15 }, +- .oobavail = 9, +- .oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } } +-}; +-#endif +- +-/* Write a chunk (page) of data to NAND. +- * +- * Caller always provides ExtendedTags data which are converted to a more +- * compact (packed) form for storage in NAND. A mini-ECC runs over the +- * contents of the tags meta-data; used to valid the tags when read. +- * +- * - Pack ExtendedTags to PackedTags1 form +- * - Compute mini-ECC for PackedTags1 +- * - Write data and packed tags to NAND. +- * +- * Note: Due to the use of the PackedTags1 meta-data which does not include +- * a full sequence number (as found in the larger PackedTags2 form) it is +- * necessary for Yaffs to re-write a chunk/page (just once) to mark it as +- * discarded and dirty. This is not ideal: newer NAND parts are supposed +- * to be written just once. When Yaffs performs this operation, this +- * function is called with a NULL data pointer -- calling MTD write_oob +- * without data is valid usage (2.6.17). +- * +- * Any underlying MTD error results in YAFFS_FAIL. +- * Returns YAFFS_OK or YAFFS_FAIL. +- */ +-int nandmtd1_WriteChunkWithTagsToNAND(yaffs_Device *dev, +- int chunkInNAND, const __u8 * data, const yaffs_ExtendedTags * etags) +-{ +- struct mtd_info * mtd = dev->genericDevice; +- int chunkBytes = dev->nDataBytesPerChunk; +- loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; +- struct mtd_oob_ops ops; +- yaffs_PackedTags1 pt1; +- int retval; +- +- /* we assume that PackedTags1 and yaffs_Tags are compatible */ +- compile_time_assertion(sizeof(yaffs_PackedTags1) == 12); +- compile_time_assertion(sizeof(yaffs_Tags) == 8); +- +- yaffs_PackTags1(&pt1, etags); +- yaffs_CalcTagsECC((yaffs_Tags *)&pt1); +- +- /* When deleting a chunk, the upper layer provides only skeletal +- * etags, one with chunkDeleted set. However, we need to update the +- * tags, not erase them completely. So we use the NAND write property +- * that only zeroed-bits stick and set tag bytes to all-ones and +- * zero just the (not) deleted bit. +- */ +-#ifndef CONFIG_YAFFS_9BYTE_TAGS +- if (etags->chunkDeleted) { +- memset(&pt1, 0xff, 8); +- /* clear delete status bit to indicate deleted */ +- pt1.deleted = 0; +- } +-#else +- ((__u8 *)&pt1)[8] = 0xff; +- if (etags->chunkDeleted) { +- memset(&pt1, 0xff, 8); +- /* zero pageStatus byte to indicate deleted */ +- ((__u8 *)&pt1)[8] = 0; +- } +-#endif +- +- memset(&ops, 0, sizeof(ops)); +- ops.mode = MTD_OOB_AUTO; +- ops.len = (data) ? chunkBytes : 0; +- ops.ooblen = YTAG1_SIZE; +- ops.datbuf = (__u8 *)data; +- ops.oobbuf = (__u8 *)&pt1; +- +- retval = mtd->write_oob(mtd, addr, &ops); +- if (retval) { +- yaffs_trace(YAFFS_TRACE_MTD, +- "write_oob failed, chunk %d, mtd error %d\n", +- chunkInNAND, retval); +- } +- return retval ? YAFFS_FAIL : YAFFS_OK; +-} +- +-/* Return with empty ExtendedTags but add eccResult. +- */ +-static int rettags(yaffs_ExtendedTags * etags, int eccResult, int retval) +-{ +- if (etags) { +- memset(etags, 0, sizeof(*etags)); +- etags->eccResult = eccResult; +- } +- return retval; +-} +- +-/* Read a chunk (page) from NAND. +- * +- * Caller expects ExtendedTags data to be usable even on error; that is, +- * all members except eccResult and blockBad are zeroed. +- * +- * - Check ECC results for data (if applicable) +- * - Check for blank/erased block (return empty ExtendedTags if blank) +- * - Check the PackedTags1 mini-ECC (correct if necessary/possible) +- * - Convert PackedTags1 to ExtendedTags +- * - Update eccResult and blockBad members to refect state. +- * +- * Returns YAFFS_OK or YAFFS_FAIL. +- */ +-int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device *dev, +- int chunkInNAND, __u8 * data, yaffs_ExtendedTags * etags) +-{ +- struct mtd_info * mtd = dev->genericDevice; +- int chunkBytes = dev->nDataBytesPerChunk; +- loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; +- int eccres = YAFFS_ECC_RESULT_NO_ERROR; +- struct mtd_oob_ops ops; +- yaffs_PackedTags1 pt1; +- int retval; +- int deleted; +- +- memset(&ops, 0, sizeof(ops)); +- ops.mode = MTD_OOB_AUTO; +- ops.len = (data) ? chunkBytes : 0; +- ops.ooblen = YTAG1_SIZE; +- ops.datbuf = data; +- ops.oobbuf = (__u8 *)&pt1; +- +-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) +- /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; +- * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. +- */ +- ops.len = (ops.datbuf) ? ops.len : ops.ooblen; +-#endif +- /* Read page and oob using MTD. +- * Check status and determine ECC result. +- */ +- retval = mtd->read_oob(mtd, addr, &ops); +- if (retval) { +- yaffs_trace(YAFFS_TRACE_MTD, +- "read_oob failed, chunk %d, mtd error %d\n", +- chunkInNAND, retval); +- } +- +- switch (retval) { +- case 0: +- /* no error */ +- break; +- +- case -EUCLEAN: +- /* MTD's ECC fixed the data */ +- eccres = YAFFS_ECC_RESULT_FIXED; +- dev->eccFixed++; +- break; +- +- case -EBADMSG: +- /* MTD's ECC could not fix the data */ +- dev->eccUnfixed++; +- /* fall into... */ +- default: +- rettags(etags, YAFFS_ECC_RESULT_UNFIXED, 0); +- etags->blockBad = (mtd->block_isbad)(mtd, addr); +- return YAFFS_FAIL; +- } +- +- /* Check for a blank/erased chunk. +- */ +- if (yaffs_CheckFF((__u8 *)&pt1, 8)) { +- /* when blank, upper layers want eccResult to be <= NO_ERROR */ +- return rettags(etags, YAFFS_ECC_RESULT_NO_ERROR, YAFFS_OK); +- } +- +-#ifndef CONFIG_YAFFS_9BYTE_TAGS +- /* Read deleted status (bit) then return it to it's non-deleted +- * state before performing tags mini-ECC check. pt1.deleted is +- * inverted. +- */ +- deleted = !pt1.deleted; +- pt1.deleted = 1; +-#else +- (void) deleted; /* not used */ +-#endif +- +- /* Check the packed tags mini-ECC and correct if necessary/possible. +- */ +- retval = yaffs_CheckECCOnTags((yaffs_Tags *)&pt1); +- switch (retval) { +- case 0: +- /* no tags error, use MTD result */ +- break; +- case 1: +- /* recovered tags-ECC error */ +- dev->tagsEccFixed++; +- eccres = YAFFS_ECC_RESULT_FIXED; +- break; +- default: +- /* unrecovered tags-ECC error */ +- dev->tagsEccUnfixed++; +- return rettags(etags, YAFFS_ECC_RESULT_UNFIXED, YAFFS_FAIL); +- } +- +- /* Unpack the tags to extended form and set ECC result. +- * [set shouldBeFF just to keep yaffs_UnpackTags1 happy] +- */ +- pt1.shouldBeFF = 0xFFFFFFFF; +- yaffs_UnpackTags1(etags, &pt1); +- etags->eccResult = eccres; +- +- /* Set deleted state. +- */ +-#ifndef CONFIG_YAFFS_9BYTE_TAGS +- etags->chunkDeleted = deleted; +-#else +- etags->chunkDeleted = (yaffs_CountBits(((__u8 *)&pt1)[8]) < 7); +-#endif +- return YAFFS_OK; +-} +- +-/* Mark a block bad. +- * +- * This is a persistant state. +- * Use of this function should be rare. +- * +- * Returns YAFFS_OK or YAFFS_FAIL. +- */ +-int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) +-{ +- struct mtd_info * mtd = dev->genericDevice; +- int blocksize = dev->nChunksPerBlock * dev->nDataBytesPerChunk; +- int retval; +- +- yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", blockNo); +- +- retval = mtd->block_markbad(mtd, (loff_t)blocksize * blockNo); +- return (retval) ? YAFFS_FAIL : YAFFS_OK; +-} +- +-/* Check any MTD prerequists. +- * +- * Returns YAFFS_OK or YAFFS_FAIL. +- */ +-static int nandmtd1_TestPrerequists(struct mtd_info * mtd) +-{ +- /* 2.6.18 has mtd->ecclayout->oobavail */ +- /* 2.6.21 has mtd->ecclayout->oobavail and mtd->oobavail */ +- int oobavail = mtd->ecclayout->oobavail; +- +- if (oobavail < YTAG1_SIZE) { +- yaffs_trace(YAFFS_TRACE_ERROR, +- "mtd device has only %d bytes for tags, need %d", +- oobavail, YTAG1_SIZE); +- return YAFFS_FAIL; +- } +- return YAFFS_OK; +-} +- +-/* Query for the current state of a specific block. +- * +- * Examine the tags of the first chunk of the block and return the state: +- * - YAFFS_BLOCK_STATE_DEAD, the block is marked bad +- * - YAFFS_BLOCK_STATE_NEEDS_SCANNING, the block is in use +- * - YAFFS_BLOCK_STATE_EMPTY, the block is clean +- * +- * Always returns YAFFS_OK. +- */ +-int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, +- yaffs_BlockState * pState, int *pSequenceNumber) +-{ +- struct mtd_info * mtd = dev->genericDevice; +- int chunkNo = blockNo * dev->nChunksPerBlock; +- yaffs_ExtendedTags etags; +- int state = YAFFS_BLOCK_STATE_DEAD; +- int seqnum = 0; +- int retval; +- +- /* We don't yet have a good place to test for MTD config prerequists. +- * Do it here as we are called during the initial scan. +- */ +- if (nandmtd1_TestPrerequists(mtd) != YAFFS_OK) { +- return YAFFS_FAIL; +- } +- +- retval = nandmtd1_ReadChunkWithTagsFromNAND(dev, chunkNo, NULL, &etags); +- if (etags.blockBad) { +- yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, +- "block %d is marked bad", blockNo); +- state = YAFFS_BLOCK_STATE_DEAD; +- } +- else if (etags.chunkUsed) { +- state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; +- seqnum = etags.sequenceNumber; +- } +- else { +- state = YAFFS_BLOCK_STATE_EMPTY; +- } +- +- *pState = state; +- *pSequenceNumber = seqnum; +- +- /* query always succeeds */ +- return YAFFS_OK; +-} +- +-#endif /*KERNEL_VERSION*/ +- +---Boundary-00=_5LbTGmt62YoutxM-- +- +- +- +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_mtdif1.c linux-2.6.24.7.new/fs/yaffs2/yaffs_mtdif1.c +--- linux-2.6.24.7/fs/yaffs2/yaffs_mtdif1.c 2009-04-21 14:57:10.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_mtdif1.c 1970-01-01 01:00:00.000000000 +0100 +@@ -1,363 +0,0 @@ +-/* +- * YAFFS: Yet another FFS. A NAND-flash specific file system. +- * yaffs_mtdif1.c NAND mtd interface functions for small-page NAND. +- * +- * Copyright (C) 2002 Aleph One Ltd. +- * for Toby Churchill Ltd and Brightstar Engineering +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-/* +- * This module provides the interface between yaffs_nand.c and the +- * MTD API. This version is used when the MTD interface supports the +- * 'mtd_oob_ops' style calls to read_oob and write_oob, circa 2.6.17, +- * and we have small-page NAND device. +- * +- * These functions are invoked via function pointers in yaffs_nand.c. +- * This replaces functionality provided by functions in yaffs_mtdif.c +- * and the yaffs_TagsCompatability functions in yaffs_tagscompat.c that are +- * called in yaffs_mtdif.c when the function pointers are NULL. +- * We assume the MTD layer is performing ECC (useNANDECC is true). +- */ +- +-#include "yportenv.h" +-#include "yaffs_guts.h" +-#include "yaffs_packedtags1.h" +-#include "yaffs_tagscompat.h" // for yaffs_CalcTagsECC +- +-#include "linux/kernel.h" +-#include "linux/version.h" +-#include "linux/types.h" +-#include "linux/mtd/mtd.h" +- +-/* Don't compile this module if we don't have MTD's mtd_oob_ops interface */ +-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +- +-const char *yaffs_mtdif1_c_version = "$Id: yaffs_mtdif1.c,v 1.3 2007/05/15 20:16:11 ian Exp $"; +- +-#ifndef CONFIG_YAFFS_9BYTE_TAGS +-# define YTAG1_SIZE 8 +-#else +-# define YTAG1_SIZE 9 +-#endif +- +-#if 0 +-/* Use the following nand_ecclayout with MTD when using +- * CONFIG_YAFFS_9BYTE_TAGS and the older on-NAND tags layout. +- * If you have existing Yaffs images and the byte order differs from this, +- * adjust 'oobfree' to match your existing Yaffs data. +- * +- * This nand_ecclayout scatters/gathers to/from the old-yaffs layout with the +- * pageStatus byte (at NAND spare offset 4) scattered/gathered from/to +- * the 9th byte. +- * +- * Old-style on-NAND format: T0,T1,T2,T3,P,B,T4,T5,E0,E1,E2,T6,T7,E3,E4,E5 +- * We have/need PackedTags1 plus pageStatus: T0,T1,T2,T3,T4,T5,T6,T7,P +- * where Tn are the tag bytes, En are MTD's ECC bytes, P is the pageStatus +- * byte and B is the small-page bad-block indicator byte. +- */ +-static struct nand_ecclayout nand_oob_16 = { +- .eccbytes = 6, +- .eccpos = { 8, 9, 10, 13, 14, 15 }, +- .oobavail = 9, +- .oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } } +-}; +-#endif +- +-/* Write a chunk (page) of data to NAND. +- * +- * Caller always provides ExtendedTags data which are converted to a more +- * compact (packed) form for storage in NAND. A mini-ECC runs over the +- * contents of the tags meta-data; used to valid the tags when read. +- * +- * - Pack ExtendedTags to PackedTags1 form +- * - Compute mini-ECC for PackedTags1 +- * - Write data and packed tags to NAND. +- * +- * Note: Due to the use of the PackedTags1 meta-data which does not include +- * a full sequence number (as found in the larger PackedTags2 form) it is +- * necessary for Yaffs to re-write a chunk/page (just once) to mark it as +- * discarded and dirty. This is not ideal: newer NAND parts are supposed +- * to be written just once. When Yaffs performs this operation, this +- * function is called with a NULL data pointer -- calling MTD write_oob +- * without data is valid usage (2.6.17). +- * +- * Any underlying MTD error results in YAFFS_FAIL. +- * Returns YAFFS_OK or YAFFS_FAIL. +- */ +-int nandmtd1_WriteChunkWithTagsToNAND(yaffs_Device *dev, +- int chunkInNAND, const __u8 * data, const yaffs_ExtendedTags * etags) +-{ +- struct mtd_info * mtd = dev->genericDevice; +- int chunkBytes = dev->nDataBytesPerChunk; +- loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; +- struct mtd_oob_ops ops; +- yaffs_PackedTags1 pt1; +- int retval; +- +- /* we assume that PackedTags1 and yaffs_Tags are compatible */ +- compile_time_assertion(sizeof(yaffs_PackedTags1) == 12); +- compile_time_assertion(sizeof(yaffs_Tags) == 8); +- +- dev->nPageWrites++; +- +- yaffs_PackTags1(&pt1, etags); +- yaffs_CalcTagsECC((yaffs_Tags *)&pt1); +- +- /* When deleting a chunk, the upper layer provides only skeletal +- * etags, one with chunkDeleted set. However, we need to update the +- * tags, not erase them completely. So we use the NAND write property +- * that only zeroed-bits stick and set tag bytes to all-ones and +- * zero just the (not) deleted bit. +- */ +-#ifndef CONFIG_YAFFS_9BYTE_TAGS +- if (etags->chunkDeleted) { +- memset(&pt1, 0xff, 8); +- /* clear delete status bit to indicate deleted */ +- pt1.deleted = 0; +- } +-#else +- ((__u8 *)&pt1)[8] = 0xff; +- if (etags->chunkDeleted) { +- memset(&pt1, 0xff, 8); +- /* zero pageStatus byte to indicate deleted */ +- ((__u8 *)&pt1)[8] = 0; +- } +-#endif +- +- memset(&ops, 0, sizeof(ops)); +- ops.mode = MTD_OOB_AUTO; +- ops.len = (data) ? chunkBytes : 0; +- ops.ooblen = YTAG1_SIZE; +- ops.datbuf = (__u8 *)data; +- ops.oobbuf = (__u8 *)&pt1; +- +- retval = mtd->write_oob(mtd, addr, &ops); +- if (retval) { +- yaffs_trace(YAFFS_TRACE_MTD, +- "write_oob failed, chunk %d, mtd error %d\n", +- chunkInNAND, retval); +- } +- return retval ? YAFFS_FAIL : YAFFS_OK; +-} +- +-/* Return with empty ExtendedTags but add eccResult. +- */ +-static int rettags(yaffs_ExtendedTags * etags, int eccResult, int retval) +-{ +- if (etags) { +- memset(etags, 0, sizeof(*etags)); +- etags->eccResult = eccResult; +- } +- return retval; +-} +- +-/* Read a chunk (page) from NAND. +- * +- * Caller expects ExtendedTags data to be usable even on error; that is, +- * all members except eccResult and blockBad are zeroed. +- * +- * - Check ECC results for data (if applicable) +- * - Check for blank/erased block (return empty ExtendedTags if blank) +- * - Check the PackedTags1 mini-ECC (correct if necessary/possible) +- * - Convert PackedTags1 to ExtendedTags +- * - Update eccResult and blockBad members to refect state. +- * +- * Returns YAFFS_OK or YAFFS_FAIL. +- */ +-int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device *dev, +- int chunkInNAND, __u8 * data, yaffs_ExtendedTags * etags) +-{ +- struct mtd_info * mtd = dev->genericDevice; +- int chunkBytes = dev->nDataBytesPerChunk; +- loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; +- int eccres = YAFFS_ECC_RESULT_NO_ERROR; +- struct mtd_oob_ops ops; +- yaffs_PackedTags1 pt1; +- int retval; +- int deleted; +- +- dev->nPageReads++; +- +- memset(&ops, 0, sizeof(ops)); +- ops.mode = MTD_OOB_AUTO; +- ops.len = (data) ? chunkBytes : 0; +- ops.ooblen = YTAG1_SIZE; +- ops.datbuf = data; +- ops.oobbuf = (__u8 *)&pt1; +- +-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) +- /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; +- * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. +- */ +- ops.len = (ops.datbuf) ? ops.len : ops.ooblen; +-#endif +- /* Read page and oob using MTD. +- * Check status and determine ECC result. +- */ +- retval = mtd->read_oob(mtd, addr, &ops); +- if (retval) { +- yaffs_trace(YAFFS_TRACE_MTD, +- "read_oob failed, chunk %d, mtd error %d\n", +- chunkInNAND, retval); +- } +- +- switch (retval) { +- case 0: +- /* no error */ +- break; +- +- case -EUCLEAN: +- /* MTD's ECC fixed the data */ +- eccres = YAFFS_ECC_RESULT_FIXED; +- dev->eccFixed++; +- break; +- +- case -EBADMSG: +- /* MTD's ECC could not fix the data */ +- dev->eccUnfixed++; +- /* fall into... */ +- default: +- rettags(etags, YAFFS_ECC_RESULT_UNFIXED, 0); +- etags->blockBad = (mtd->block_isbad)(mtd, addr); +- return YAFFS_FAIL; +- } +- +- /* Check for a blank/erased chunk. +- */ +- if (yaffs_CheckFF((__u8 *)&pt1, 8)) { +- /* when blank, upper layers want eccResult to be <= NO_ERROR */ +- return rettags(etags, YAFFS_ECC_RESULT_NO_ERROR, YAFFS_OK); +- } +- +-#ifndef CONFIG_YAFFS_9BYTE_TAGS +- /* Read deleted status (bit) then return it to it's non-deleted +- * state before performing tags mini-ECC check. pt1.deleted is +- * inverted. +- */ +- deleted = !pt1.deleted; +- pt1.deleted = 1; +-#else +- deleted = (yaffs_CountBits(((__u8 *)&pt1)[8]) < 7); +-#endif +- +- /* Check the packed tags mini-ECC and correct if necessary/possible. +- */ +- retval = yaffs_CheckECCOnTags((yaffs_Tags *)&pt1); +- switch (retval) { +- case 0: +- /* no tags error, use MTD result */ +- break; +- case 1: +- /* recovered tags-ECC error */ +- dev->tagsEccFixed++; +- if (eccres == YAFFS_ECC_RESULT_NO_ERROR) +- eccres = YAFFS_ECC_RESULT_FIXED; +- break; +- default: +- /* unrecovered tags-ECC error */ +- dev->tagsEccUnfixed++; +- return rettags(etags, YAFFS_ECC_RESULT_UNFIXED, YAFFS_FAIL); +- } +- +- /* Unpack the tags to extended form and set ECC result. +- * [set shouldBeFF just to keep yaffs_UnpackTags1 happy] +- */ +- pt1.shouldBeFF = 0xFFFFFFFF; +- yaffs_UnpackTags1(etags, &pt1); +- etags->eccResult = eccres; +- +- /* Set deleted state */ +- etags->chunkDeleted = deleted; +- return YAFFS_OK; +-} +- +-/* Mark a block bad. +- * +- * This is a persistant state. +- * Use of this function should be rare. +- * +- * Returns YAFFS_OK or YAFFS_FAIL. +- */ +-int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) +-{ +- struct mtd_info * mtd = dev->genericDevice; +- int blocksize = dev->nChunksPerBlock * dev->nDataBytesPerChunk; +- int retval; +- +- yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", blockNo); +- +- retval = mtd->block_markbad(mtd, (loff_t)blocksize * blockNo); +- return (retval) ? YAFFS_FAIL : YAFFS_OK; +-} +- +-/* Check any MTD prerequists. +- * +- * Returns YAFFS_OK or YAFFS_FAIL. +- */ +-static int nandmtd1_TestPrerequists(struct mtd_info * mtd) +-{ +- /* 2.6.18 has mtd->ecclayout->oobavail */ +- /* 2.6.21 has mtd->ecclayout->oobavail and mtd->oobavail */ +- int oobavail = mtd->ecclayout->oobavail; +- +- if (oobavail < YTAG1_SIZE) { +- yaffs_trace(YAFFS_TRACE_ERROR, +- "mtd device has only %d bytes for tags, need %d\n", +- oobavail, YTAG1_SIZE); +- return YAFFS_FAIL; +- } +- return YAFFS_OK; +-} +- +-/* Query for the current state of a specific block. +- * +- * Examine the tags of the first chunk of the block and return the state: +- * - YAFFS_BLOCK_STATE_DEAD, the block is marked bad +- * - YAFFS_BLOCK_STATE_NEEDS_SCANNING, the block is in use +- * - YAFFS_BLOCK_STATE_EMPTY, the block is clean +- * +- * Always returns YAFFS_OK. +- */ +-int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, +- yaffs_BlockState * pState, int *pSequenceNumber) +-{ +- struct mtd_info * mtd = dev->genericDevice; +- int chunkNo = blockNo * dev->nChunksPerBlock; +- yaffs_ExtendedTags etags; +- int state = YAFFS_BLOCK_STATE_DEAD; +- int seqnum = 0; +- int retval; +- +- /* We don't yet have a good place to test for MTD config prerequists. +- * Do it here as we are called during the initial scan. +- */ +- if (nandmtd1_TestPrerequists(mtd) != YAFFS_OK) { +- return YAFFS_FAIL; +- } +- +- retval = nandmtd1_ReadChunkWithTagsFromNAND(dev, chunkNo, NULL, &etags); +- if (etags.blockBad) { +- yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, +- "block %d is marked bad", blockNo); +- state = YAFFS_BLOCK_STATE_DEAD; +- } +- else if (etags.chunkUsed) { +- state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; +- seqnum = etags.sequenceNumber; +- } +- else { +- state = YAFFS_BLOCK_STATE_EMPTY; +- } +- +- *pState = state; +- *pSequenceNumber = seqnum; +- +- /* query always succeeds */ +- return YAFFS_OK; +-} +- +-#endif /*KERNEL_VERSION*/ +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_mtdif1.h linux-2.6.24.7.new/fs/yaffs2/yaffs_mtdif1.h +--- linux-2.6.24.7/fs/yaffs2/yaffs_mtdif1.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_mtdif1.h 1970-01-01 01:00:00.000000000 +0100 +@@ -1,28 +0,0 @@ +-/* +- * YAFFS: Yet another Flash File System. A NAND-flash specific file system. +- * +- * Copyright (C) 2002-2007 Aleph One Ltd. +- * for Toby Churchill Ltd and Brightstar Engineering +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU Lesser General Public License version 2.1 as +- * published by the Free Software Foundation. +- * +- * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. +- */ +- +-#ifndef __YAFFS_MTDIF1_H__ +-#define __YAFFS_MTDIF1_H__ +- +-int nandmtd1_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND, +- const __u8 * data, const yaffs_ExtendedTags * tags); +- +-int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, +- __u8 * data, yaffs_ExtendedTags * tags); +- +-int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); +- +-int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, +- yaffs_BlockState * state, int *sequenceNumber); +- +-#endif +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_mtdif2.c linux-2.6.24.7.new/fs/yaffs2/yaffs_mtdif2.c +--- linux-2.6.24.7/fs/yaffs2/yaffs_mtdif2.c 2009-04-21 14:57:10.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_mtdif2.c 2009-04-21 15:01:02.000000000 +0200 +@@ -14,19 +14,152 @@ + /* mtd interface for YAFFS2 */ + + const char *yaffs_mtdif2_c_version = +- "$Id: yaffs_mtdif2.c,v 1.17 2007-02-14 01:09:06 wookey Exp $"; ++ "$Id: yaffs_mtdif2.c,v 1.1.1.1 2008-03-28 04:29:21 jlwei Exp $"; + + #include "yportenv.h" +- +- + #include "yaffs_mtdif2.h" +- + #include "linux/mtd/mtd.h" + #include "linux/types.h" + #include "linux/time.h" + + #include "yaffs_packedtags2.h" + ++#define PT2_BYTES 25 ++ ++void nandmtd2_pt2buf(yaffs_Device *dev, yaffs_PackedTags2 *pt, int is_raw) ++{ ++ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); ++ int i, j = 0, k, n; ++ __u8 pt2_byte_buf[PT2_BYTES]; ++ ++ /* Pack buffer with 0xff */ ++ for (i = 0; i < mtd->oobsize; i++) ++ dev->spareBuffer[i] = 0xff; ++ ++ if (!is_raw) { ++ *((unsigned int *) &dev->spareBuffer[0]) = pt->t.sequenceNumber; ++ *((unsigned int *) &dev->spareBuffer[4]) = pt->t.objectId; ++ *((unsigned int *) &dev->spareBuffer[8]) = pt->t.chunkId; ++ *((unsigned int *) &dev->spareBuffer[12]) = pt->t.byteCount; ++ dev->spareBuffer[16] = pt->ecc.colParity; ++ dev->spareBuffer[17] = pt->ecc.lineParity & 0xff; ++ dev->spareBuffer[18] = (pt->ecc.lineParity >> 8) & 0xff; ++ dev->spareBuffer[19] = (pt->ecc.lineParity >> 16) & 0xff; ++ dev->spareBuffer[20] = (pt->ecc.lineParity >> 24) & 0xff; ++ dev->spareBuffer[21] = pt->ecc.lineParityPrime & 0xff; ++ dev->spareBuffer[22] = (pt->ecc.lineParityPrime >> 8) & 0xff; ++ dev->spareBuffer[23] = (pt->ecc.lineParityPrime >> 16) & 0xff; ++ dev->spareBuffer[24] = (pt->ecc.lineParityPrime >> 24) & 0xff; ++ } else { ++ *((unsigned int *) &pt2_byte_buf[0]) = pt->t.sequenceNumber; ++ *((unsigned int *) &pt2_byte_buf[4]) = pt->t.objectId; ++ *((unsigned int *) &pt2_byte_buf[8]) = pt->t.chunkId; ++ *((unsigned int *) &pt2_byte_buf[12]) = pt->t.byteCount; ++ pt2_byte_buf[16] = pt->ecc.colParity; ++ pt2_byte_buf[17] = pt->ecc.lineParity & 0xff; ++ pt2_byte_buf[18] = (pt->ecc.lineParity >> 8) & 0xff; ++ pt2_byte_buf[19] = (pt->ecc.lineParity >> 16) & 0xff; ++ pt2_byte_buf[20] = (pt->ecc.lineParity >> 24) & 0xff; ++ pt2_byte_buf[21] = pt->ecc.lineParityPrime & 0xff; ++ pt2_byte_buf[22] = (pt->ecc.lineParityPrime >> 8) & 0xff; ++ pt2_byte_buf[23] = (pt->ecc.lineParityPrime >> 16) & 0xff; ++ pt2_byte_buf[24] = (pt->ecc.lineParityPrime >> 24) & 0xff; ++ ++// k = mtd->oobinfo.oobfree[j][0]; ++// n = mtd->oobinfo.oobfree[j][1]; ++ ++ k = mtd->ecclayout->oobfree[j].offset; ++ n = mtd->ecclayout->oobfree[j].length; ++ if (n == 0) { ++ T(YAFFS_TRACE_ERROR, (TSTR("No OOB space for tags" TENDSTR))); ++ YBUG(); ++ } ++ ++ for (i = 0; i < PT2_BYTES; i++) { ++ if (n == 0) { ++ j++; ++// k = mtd->oobinfo.oobfree[j][0]; ++// n = mtd->oobinfo.oobfree[j][1]; ++ k = mtd->ecclayout->oobfree[j].offset; ++ n = mtd->ecclayout->oobfree[j].length; ++ ++ if (n == 0) { ++ T(YAFFS_TRACE_ERROR, (TSTR("No OOB space for tags" TENDSTR))); ++ YBUG(); ++ } ++ } ++ dev->spareBuffer[k++] = pt2_byte_buf[i]; ++ n--; ++ } ++ } ++ ++} ++ ++void nandmtd2_buf2pt(yaffs_Device *dev, yaffs_PackedTags2 *pt, int is_raw) ++{ ++ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); ++ int i, j = 0, k, n; ++ __u8 pt2_byte_buf[PT2_BYTES]; ++ ++ ++ if (!is_raw) { ++ pt->t.sequenceNumber = *((unsigned int *) &dev->spareBuffer[0]); ++ pt->t.objectId = *((unsigned int *) &dev->spareBuffer[4]); ++ pt->t.chunkId = *((unsigned int *) &dev->spareBuffer[8]); ++ pt->t.byteCount = *((unsigned int *) &dev->spareBuffer[12]); ++ pt->ecc.colParity = dev->spareBuffer[16]; ++ pt->ecc.lineParity = (dev->spareBuffer[17] & 0x000000ff) | ++ ((dev->spareBuffer[18] << 8) & 0x0000ff00) | ++ ((dev->spareBuffer[19] << 16) & 0x00ff0000) | ++ ((dev->spareBuffer[20] << 24) & 0xff000000); ++ pt->ecc.lineParityPrime = (dev->spareBuffer[21] & 0x000000ff) | ++ ((dev->spareBuffer[22] << 8) & 0x0000ff00) | ++ ((dev->spareBuffer[23] << 16) & 0x00ff0000) | ++ ((dev->spareBuffer[24] << 24) & 0xff000000); ++ } else { ++// k = mtd->oobinfo.oobfree[j][0]; ++// n = mtd->oobinfo.oobfree[j][1]; ++ ++ k = mtd->ecclayout->oobfree[j].offset; ++ n = mtd->ecclayout->oobfree[j].length; ++ ++ if (n == 0) { ++ T(YAFFS_TRACE_ERROR, (TSTR("No space in OOB for tags" TENDSTR))); ++ YBUG(); ++ } ++ ++ for (i = 0; i < PT2_BYTES; i++) { ++ if (n == 0) { ++ j++; ++// k = mtd->oobinfo.oobfree[j][0]; ++// n = mtd->oobinfo.oobfree[j][1]; ++ k = mtd->ecclayout->oobfree[j].offset; ++ n = mtd->ecclayout->oobfree[j].length; ++ ++ if (n == 0) { ++ T(YAFFS_TRACE_ERROR, (TSTR("No space in OOB for tags" TENDSTR))); ++ YBUG(); ++ } ++ } ++ pt2_byte_buf[i] = dev->spareBuffer[k++]; ++ n--; ++ } ++ pt->t.sequenceNumber = *((unsigned int *) &pt2_byte_buf[0]); ++ pt->t.objectId = *((unsigned int *) &pt2_byte_buf[4]); ++ pt->t.chunkId = *((unsigned int *) &pt2_byte_buf[8]); ++ pt->t.byteCount = *((unsigned int *) &pt2_byte_buf[12]); ++ pt->ecc.colParity = pt2_byte_buf[16]; ++ pt->ecc.lineParity = (pt2_byte_buf[17] & 0x000000ff) | ++ ((pt2_byte_buf[18] << 8) & 0x0000ff00) | ++ ((pt2_byte_buf[19] << 16) & 0x00ff0000) | ++ ((pt2_byte_buf[20] << 24) & 0xff000000); ++ pt->ecc.lineParityPrime = (pt2_byte_buf[21] & 0x000000ff) | ++ ((pt2_byte_buf[22] << 8) & 0x0000ff00) | ++ ((pt2_byte_buf[23] << 16) & 0x00ff0000) | ++ ((pt2_byte_buf[24] << 24) & 0xff000000); ++ } ++} ++ + int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, + const yaffs_ExtendedTags * tags) +@@ -38,9 +171,7 @@ + size_t dummy; + #endif + int retval = 0; +- +- loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; +- ++ loff_mtd_t addr = ((loff_mtd_t) chunkInNAND) * dev->nDataBytesPerChunk; + yaffs_PackedTags2 pt; + + T(YAFFS_TRACE_MTD, +@@ -55,12 +186,14 @@ + BUG(); /* both tags and data should always be present */ + + if (data) { ++ nandmtd2_pt2buf(dev, &pt, 0); //modify + ops.mode = MTD_OOB_AUTO; + ops.ooblen = sizeof(pt); + ops.len = dev->nDataBytesPerChunk; + ops.ooboffs = 0; + ops.datbuf = (__u8 *)data; +- ops.oobbuf = (void *)&pt; ++// ops.oobbuf = (void *)&pt; //modify ++ ops.oobbuf = (void *)dev->spareBuffer; //modify + retval = mtd->write_oob(mtd, addr, &ops); + } else + BUG(); /* both tags and data should always be present */ +@@ -70,24 +203,27 @@ + } + + if (data && tags) { ++ nandmtd2_pt2buf(dev, &pt, 0); + if (dev->useNANDECC) + retval = + mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, +- &dummy, data, (__u8 *) & pt, NULL); ++ &dummy, data, dev->spareBuffer, NULL); + else + retval = + mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, +- &dummy, data, (__u8 *) & pt, NULL); ++ &dummy, data, dev->spareBuffer, NULL); + } else { +- if (data) ++ if (data) { + retval = + mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy, + data); +- if (tags) ++ } ++ if (tags) { ++ nandmtd2_pt2buf(dev, &pt, 1); + retval = + mtd->write_oob(mtd, addr, mtd->oobsize, &dummy, +- (__u8 *) & pt); +- ++ dev->spareBuffer); ++ } + } + #endif + +@@ -104,11 +240,9 @@ + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + struct mtd_oob_ops ops; + #endif +- size_t dummy; ++ size_mtd_t dummy; + int retval = 0; +- +- loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; +- ++ loff_mtd_t addr = ((loff_mtd_t) chunkInNAND) * dev->nDataBytesPerChunk; + yaffs_PackedTags2 pt; + + T(YAFFS_TRACE_MTD, +@@ -128,6 +262,7 @@ + ops.datbuf = data; + ops.oobbuf = dev->spareBuffer; + retval = mtd->read_oob(mtd, addr, &ops); ++ nandmtd2_buf2pt(dev, &pt, 0); //modify by yliu + } + #else + if (data && tags) { +@@ -142,23 +277,25 @@ + &dummy, data, dev->spareBuffer, + NULL); + } ++ nandmtd2_buf2pt(dev, &pt, 0); + } else { +- if (data) ++ if (data) { + retval = + mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy, + data); +- if (tags) ++ } ++ if (tags) { + retval = + mtd->read_oob(mtd, addr, mtd->oobsize, &dummy, + dev->spareBuffer); ++ nandmtd2_buf2pt(dev, &pt, 1); ++ } + } + #endif + +- memcpy(&pt, dev->spareBuffer, sizeof(pt)); +- + if (tags) + yaffs_UnpackTags2(tags, &pt); +- ++ + if(tags && retval == -EBADMSG && tags->eccResult == YAFFS_ECC_RESULT_NO_ERROR) + tags->eccResult = YAFFS_ECC_RESULT_UNFIXED; + +@@ -177,7 +314,7 @@ + + retval = + mtd->block_markbad(mtd, +- blockNo * dev->nChunksPerBlock * ++ (u64)blockNo * dev->nChunksPerBlock * + dev->nDataBytesPerChunk); + + if (retval == 0) +@@ -195,11 +332,11 @@ + + T(YAFFS_TRACE_MTD, + (TSTR("nandmtd2_QueryNANDBlock %d" TENDSTR), blockNo)); ++ + retval = + mtd->block_isbad(mtd, +- blockNo * dev->nChunksPerBlock * ++ (u64)blockNo * dev->nChunksPerBlock * + dev->nDataBytesPerChunk); +- + if (retval) { + T(YAFFS_TRACE_MTD, (TSTR("block is bad" TENDSTR))); + +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_mtdif2.h linux-2.6.24.7.new/fs/yaffs2/yaffs_mtdif2.h +--- linux-2.6.24.7/fs/yaffs2/yaffs_mtdif2.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_mtdif2.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_nand.c linux-2.6.24.7.new/fs/yaffs2/yaffs_nand.c +--- linux-2.6.24.7/fs/yaffs2/yaffs_nand.c 2009-04-21 14:57:10.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_nand.c 2009-04-21 15:01:02.000000000 +0200 +@@ -10,9 +10,9 @@ + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +- ++ + const char *yaffs_nand_c_version = +- "$Id: yaffs_nand.c,v 1.7 2007-02-14 01:09:06 wookey Exp $"; ++ "$Id: yaffs_nand.c,v 1.1.1.1 2008-03-28 04:29:21 jlwei Exp $"; + + #include "yaffs_nand.h" + #include "yaffs_tagscompat.h" +@@ -25,9 +25,9 @@ + { + int result; + yaffs_ExtendedTags localTags; +- ++ + int realignedChunkInNAND = chunkInNAND - dev->chunkOffset; +- ++ + /* If there are no tags provided, use local tags to get prioritised gc working */ + if(!tags) + tags = &localTags; +@@ -39,14 +39,14 @@ + result = yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(dev, + realignedChunkInNAND, + buffer, +- tags); +- if(tags && +- tags->eccResult > YAFFS_ECC_RESULT_NO_ERROR){ ++ tags); + ++ if(tags && ++ tags->eccResult > YAFFS_ECC_RESULT_NO_ERROR){ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, chunkInNAND/dev->nChunksPerBlock); + yaffs_HandleChunkError(dev,bi); + } +- ++ + return result; + } + +@@ -57,7 +57,7 @@ + { + chunkInNAND -= dev->chunkOffset; + +- ++ + if (tags) { + tags->sequenceNumber = dev->sequenceNumber; + tags->chunkUsed = 1; +@@ -131,4 +131,4 @@ + } + + +- ++ +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_nand.h linux-2.6.24.7.new/fs/yaffs2/yaffs_nand.h +--- linux-2.6.24.7/fs/yaffs2/yaffs_nand.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_nand.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_nandemul2k.h linux-2.6.24.7.new/fs/yaffs2/yaffs_nandemul2k.h +--- linux-2.6.24.7/fs/yaffs2/yaffs_nandemul2k.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_nandemul2k.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_packedtags1.h linux-2.6.24.7.new/fs/yaffs2/yaffs_packedtags1.h +--- linux-2.6.24.7/fs/yaffs2/yaffs_packedtags1.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_packedtags1.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_packedtags2.c linux-2.6.24.7.new/fs/yaffs2/yaffs_packedtags2.c +--- linux-2.6.24.7/fs/yaffs2/yaffs_packedtags2.c 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_packedtags2.c 2009-04-21 15:01:02.000000000 +0200 +@@ -40,7 +40,8 @@ + static void yaffs_DumpPackedTags2(const yaffs_PackedTags2 * pt) + { + T(YAFFS_TRACE_MTD, +- (TSTR("packed tags obj %d chunk %d byte %d seq %d" TENDSTR), ++// T(YAFFS_TRACE_ALWAYS, ++ (TSTR("packed tags obj %x chunk %x byte %x seq %x" TENDSTR), + pt->t.objectId, pt->t.chunkId, pt->t.byteCount, + pt->t.sequenceNumber)); + } +@@ -48,9 +49,10 @@ + static void yaffs_DumpTags2(const yaffs_ExtendedTags * t) + { + T(YAFFS_TRACE_MTD, ++// T(YAFFS_TRACE_ALWAYS, + (TSTR +- ("ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte " +- "%d del %d ser %d seq %d" ++ ("ext.tags eccres %x blkbad %x chused %x obj %x chunk%x byte " ++ "%x del %x ser %x seq %x" + TENDSTR), t->eccResult, t->blockBad, t->chunkUsed, t->objectId, + t->chunkId, t->byteCount, t->chunkDeleted, t->serialNumber, + t->sequenceNumber)); +@@ -89,10 +91,10 @@ + } + } + +- yaffs_DumpPackedTags2(pt); +- yaffs_DumpTags2(t); ++// yaffs_DumpPackedTags2(pt); ++// yaffs_DumpTags2(t); + +-#ifndef YAFFS_IGNORE_TAGS_ECC ++#if !defined(YAFFS_IGNORE_TAGS_ECC) && !defined(CONFIG_MTD_HW_BCH_ECC) + { + yaffs_ECCCalculateOther((unsigned char *)&pt->t, + sizeof(yaffs_PackedTags2TagsPart), +@@ -103,14 +105,11 @@ + + void yaffs_UnpackTags2(yaffs_ExtendedTags * t, yaffs_PackedTags2 * pt) + { +- +- memset(t, 0, sizeof(yaffs_ExtendedTags)); +- + yaffs_InitialiseTags(t); + + if (pt->t.sequenceNumber != 0xFFFFFFFF) { + /* Page is in use */ +-#ifdef YAFFS_IGNORE_TAGS_ECC ++#if defined(YAFFS_IGNORE_TAGS_ECC) || defined(CONFIG_MTD_HW_BCH_ECC) + { + t->eccResult = YAFFS_ECC_RESULT_NO_ERROR; + } +@@ -118,20 +117,23 @@ + { + yaffs_ECCOther ecc; + int result; ++#if defined(CONFIG_YAFFS_ECC_HAMMING) + yaffs_ECCCalculateOther((unsigned char *)&pt->t, + sizeof + (yaffs_PackedTags2TagsPart), + &ecc); ++#endif + result = + yaffs_ECCCorrectOther((unsigned char *)&pt->t, + sizeof + (yaffs_PackedTags2TagsPart), + &pt->ecc, &ecc); ++ + switch(result){ +- case 0: +- t->eccResult = YAFFS_ECC_RESULT_NO_ERROR; ++ case 0: ++ t->eccResult = YAFFS_ECC_RESULT_NO_ERROR; + break; +- case 1: ++ case 1: + t->eccResult = YAFFS_ECC_RESULT_FIXED; + break; + case -1: +@@ -175,8 +177,8 @@ + } + } + } +- ++// printk("\n TAG %d",t->chunkUsed); + yaffs_DumpPackedTags2(pt); + yaffs_DumpTags2(t); +- ++// printk("\n TAG %d",t->chunkUsed); + } +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_packedtags2.h linux-2.6.24.7.new/fs/yaffs2/yaffs_packedtags2.h +--- linux-2.6.24.7/fs/yaffs2/yaffs_packedtags2.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_packedtags2.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_qsort.c linux-2.6.24.7.new/fs/yaffs2/yaffs_qsort.c +--- linux-2.6.24.7/fs/yaffs2/yaffs_qsort.c 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_qsort.c 2009-04-21 15:01:02.000000000 +0200 +@@ -50,7 +50,7 @@ + static __inline void + swapfunc(char *a, char *b, int n, int swaptype) + { +- if (swaptype <= 1) ++ if (swaptype <= 1) + swapcode(long, a, b, n) + else + swapcode(char, a, b, n) +@@ -74,13 +74,8 @@ + :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c )); + } + +-#ifndef min +-#define min(a,b) (((a) < (b)) ? (a) : (b)) +-#endif +- + void +-yaffs_qsort(void *aa, size_t n, size_t es, +- int (*cmp)(const void *, const void *)) ++qsort(void *aa, size_t n, size_t es, int (*cmp)(const void *, const void *)) + { + char *pa, *pb, *pc, *pd, *pl, *pm, *pn; + int d, r, swaptype, swap_cnt; +@@ -137,7 +132,7 @@ + } + if (swap_cnt == 0) { /* Switch to insertion sort */ + for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es) +- for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; ++ for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; +@@ -149,12 +144,12 @@ + r = min((long)(pd - pc), (long)(pn - pd - es)); + vecswap(pb, pn - r, r); + if ((r = pb - pa) > es) +- yaffs_qsort(a, r / es, es, cmp); +- if ((r = pd - pc) > es) { ++ qsort(a, r / es, es, cmp); ++ if ((r = pd - pc) > es) { + /* Iterate rather than recurse to save stack space */ + a = pn - r; + n = r / es; + goto loop; + } +-/* yaffs_qsort(pn - r, r / es, es, cmp);*/ ++/* qsort(pn - r, r / es, es, cmp);*/ + } +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_qsort.h linux-2.6.24.7.new/fs/yaffs2/yaffs_qsort.h +--- linux-2.6.24.7/fs/yaffs2/yaffs_qsort.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_qsort.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +@@ -17,7 +17,7 @@ + #ifndef __YAFFS_QSORT_H__ + #define __YAFFS_QSORT_H__ + +-extern void yaffs_qsort (void *const base, size_t total_elems, size_t size, ++extern void qsort (void *const base, size_t total_elems, size_t size, + int (*cmp)(const void *, const void *)); + + #endif +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_tagscompat.c linux-2.6.24.7.new/fs/yaffs2/yaffs_tagscompat.c +--- linux-2.6.24.7/fs/yaffs2/yaffs_tagscompat.c 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_tagscompat.c 2009-04-21 15:01:02.000000000 +0200 +@@ -45,7 +45,7 @@ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 + }; + +-int yaffs_CountBits(__u8 x) ++static int yaffs_CountBits(__u8 x) + { + int retVal; + retVal = yaffs_countBitsTable[x]; +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_tagscompat.h linux-2.6.24.7.new/fs/yaffs2/yaffs_tagscompat.h +--- linux-2.6.24.7/fs/yaffs2/yaffs_tagscompat.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_tagscompat.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +@@ -33,8 +33,4 @@ + int blockNo, yaffs_BlockState * + state, int *sequenceNumber); + +-void yaffs_CalcTagsECC(yaffs_Tags * tags); +-int yaffs_CheckECCOnTags(yaffs_Tags * tags); +-int yaffs_CountBits(__u8 byte); +- + #endif +diff -urN linux-2.6.24.7/fs/yaffs2/yaffs_tagsvalidity.h linux-2.6.24.7.new/fs/yaffs2/yaffs_tagsvalidity.h +--- linux-2.6.24.7/fs/yaffs2/yaffs_tagsvalidity.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffs_tagsvalidity.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +diff -urN linux-2.6.24.7/fs/yaffs2/yaffsinterface.h linux-2.6.24.7.new/fs/yaffs2/yaffsinterface.h +--- linux-2.6.24.7/fs/yaffs2/yaffsinterface.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yaffsinterface.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +diff -urN linux-2.6.24.7/fs/yaffs2/yportenv.h linux-2.6.24.7.new/fs/yaffs2/yportenv.h +--- linux-2.6.24.7/fs/yaffs2/yportenv.h 2009-04-09 17:39:09.000000000 +0200 ++++ linux-2.6.24.7.new/fs/yaffs2/yportenv.h 2009-04-21 15:01:02.000000000 +0200 +@@ -1,5 +1,5 @@ + /* +- * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering +@@ -32,7 +32,6 @@ + #endif + #include + #include +-#include + #include + #include + #include +@@ -42,7 +41,6 @@ + #define _Y(x) x + #define yaffs_strcpy(a,b) strcpy(a,b) + #define yaffs_strncpy(a,b,c) strncpy(a,b,c) +-#define yaffs_strncmp(a,b,c) strncmp(a,b,c) + #define yaffs_strlen(s) strlen(s) + #define yaffs_sprintf sprintf + #define yaffs_toupper(a) toupper(a) +@@ -62,7 +60,7 @@ + // KR - added for use in scan so processes aren't blocked indefinitely. + #define YYIELD() schedule() + +-#define YAFFS_ROOT_MODE 0666 ++#define YAFFS_ROOT_MODE 0666 + #define YAFFS_LOSTNFOUND_MODE 0666 + + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +@@ -80,14 +78,6 @@ + #define TSTR(x) KERN_WARNING x + #define TOUT(p) printk p + +-#define yaffs_trace(mask, fmt, args...) \ +- do { if ((mask) & (yaffs_traceMask|YAFFS_TRACE_ERROR)) \ +- printk(KERN_WARNING "yaffs: " fmt, ## args); \ +- } while (0) +- +-#define compile_time_assertion(assertion) \ +- ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) +- + #elif defined CONFIG_YAFFS_DIRECT + + /* Direct interface */ +@@ -142,15 +132,9 @@ + + #endif + +-/* see yaffs_fs.c */ +-extern unsigned int yaffs_traceMask; +-extern unsigned int yaffs_wr_attempts; +- +-/* +- * Tracing flags. +- * The flags masked in YAFFS_TRACE_ALWAYS are always traced. +- */ ++extern unsigned yaffs_traceMask; + ++#define YAFFS_TRACE_ERROR 0x00000001 + #define YAFFS_TRACE_OS 0x00000002 + #define YAFFS_TRACE_ALLOCATE 0x00000004 + #define YAFFS_TRACE_SCAN 0x00000008 +@@ -166,19 +150,10 @@ + #define YAFFS_TRACE_SCAN_DEBUG 0x00002000 + #define YAFFS_TRACE_MTD 0x00004000 + #define YAFFS_TRACE_CHECKPOINT 0x00008000 +- +-#define YAFFS_TRACE_VERIFY 0x00010000 +-#define YAFFS_TRACE_VERIFY_NAND 0x00020000 +-#define YAFFS_TRACE_VERIFY_FULL 0x00040000 +-#define YAFFS_TRACE_VERIFY_ALL 0x000F0000 +- +- +-#define YAFFS_TRACE_ERROR 0x40000000 ++#define YAFFS_TRACE_ALWAYS 0x40000000 + #define YAFFS_TRACE_BUG 0x80000000 +-#define YAFFS_TRACE_ALWAYS 0xF0000000 +- + +-#define T(mask,p) do{ if((mask) & (yaffs_traceMask | YAFFS_TRACE_ALWAYS)) TOUT(p);} while(0) ++#define T(mask,p) do{ if((mask) & (yaffs_traceMask | YAFFS_TRACE_ERROR)) TOUT(p);} while(0) + + #ifndef CONFIG_YAFFS_WINCE + #define YBUG() T(YAFFS_TRACE_BUG,(TSTR("==>> yaffs bug: " __FILE__ " %d" TENDSTR),__LINE__))